New silcconfig library and server parser. Merged silc-newconfig-final.patch.
authorGiovanni Giacobbi <johnny@silcnet.org>
Wed, 13 Feb 2002 11:56:01 +0000 (11:56 +0000)
committerGiovanni Giacobbi <johnny@silcnet.org>
Wed, 13 Feb 2002 11:56:01 +0000 (11:56 +0000)
995 files changed:
.cvsignore [new file with mode: 0644]
CHANGES [new file with mode: 0644]
CREDITS [new file with mode: 0644]
INSTALL
Makefile.am.pre [new file with mode: 0644]
Makefile.defines.pre [new file with mode: 0644]
Makefile.defines_int.pre [new file with mode: 0644]
README
README.CVS [new file with mode: 0644]
README.WIN32 [new file with mode: 0644]
TODO
acconfig.h [deleted file]
acconfig.h.pre [new file with mode: 0644]
apps/irssi/AUTHORS [new file with mode: 0644]
apps/irssi/COPYING [new file with mode: 0644]
apps/irssi/Makefile.am
apps/irssi/acconfig.h
apps/irssi/autogen.sh
apps/irssi/config [deleted file]
apps/irssi/config.h.in [deleted file]
apps/irssi/configure.in
apps/irssi/curses.m4
apps/irssi/default-config.h [deleted file]
apps/irssi/default.theme
apps/irssi/docs/.cvsignore [new file with mode: 0644]
apps/irssi/docs/Makefile.am
apps/irssi/docs/botnet.txt [new file with mode: 0644]
apps/irssi/docs/crash.txt [new file with mode: 0644]
apps/irssi/docs/design.txt [new file with mode: 0644]
apps/irssi/docs/formats.txt
apps/irssi/docs/help/Makefile.am.gen
apps/irssi/docs/help/in/Makefile.am.gen
apps/irssi/docs/help/in/action.in
apps/irssi/docs/help/in/admin.in
apps/irssi/docs/help/in/away.in
apps/irssi/docs/help/in/ban.in
apps/irssi/docs/help/in/channel.in
apps/irssi/docs/help/in/clear.in
apps/irssi/docs/help/in/close.in [new file with mode: 0644]
apps/irssi/docs/help/in/cmode.in [new file with mode: 0644]
apps/irssi/docs/help/in/connect.in
apps/irssi/docs/help/in/ctcp.in [deleted file]
apps/irssi/docs/help/in/cumode.in [new file with mode: 0644]
apps/irssi/docs/help/in/date.in
apps/irssi/docs/help/in/dcc.in [deleted file]
apps/irssi/docs/help/in/deop.in [deleted file]
apps/irssi/docs/help/in/devoice.in [deleted file]
apps/irssi/docs/help/in/die.in [deleted file]
apps/irssi/docs/help/in/disconnect.in
apps/irssi/docs/help/in/file.in [new file with mode: 0644]
apps/irssi/docs/help/in/getkey.in [new file with mode: 0644]
apps/irssi/docs/help/in/hash.in [deleted file]
apps/irssi/docs/help/in/info.in
apps/irssi/docs/help/in/invite.in
apps/irssi/docs/help/in/ircnet.in [deleted file]
apps/irssi/docs/help/in/ison.in [deleted file]
apps/irssi/docs/help/in/join.in
apps/irssi/docs/help/in/key.in [new file with mode: 0644]
apps/irssi/docs/help/in/kick.in
apps/irssi/docs/help/in/kill.in
apps/irssi/docs/help/in/layout.in
apps/irssi/docs/help/in/links.in [deleted file]
apps/irssi/docs/help/in/load.in
apps/irssi/docs/help/in/lusers.in [deleted file]
apps/irssi/docs/help/in/map.in [deleted file]
apps/irssi/docs/help/in/me.in
apps/irssi/docs/help/in/mircdcc.in [deleted file]
apps/irssi/docs/help/in/mode.in [deleted file]
apps/irssi/docs/help/in/msg.in
apps/irssi/docs/help/in/names.in
apps/irssi/docs/help/in/nctcp.in [deleted file]
apps/irssi/docs/help/in/netsplit.in [deleted file]
apps/irssi/docs/help/in/nick.in
apps/irssi/docs/help/in/note.in [deleted file]
apps/irssi/docs/help/in/notice.in
apps/irssi/docs/help/in/notify.in [deleted file]
apps/irssi/docs/help/in/op.in [deleted file]
apps/irssi/docs/help/in/oper.in
apps/irssi/docs/help/in/ping.in
apps/irssi/docs/help/in/query.in
apps/irssi/docs/help/in/quit.in
apps/irssi/docs/help/in/quote.in [deleted file]
apps/irssi/docs/help/in/rawlog.in [deleted file]
apps/irssi/docs/help/in/rehash.in [deleted file]
apps/irssi/docs/help/in/restart.in [deleted file]
apps/irssi/docs/help/in/rmrejoins.in
apps/irssi/docs/help/in/rping.in [deleted file]
apps/irssi/docs/help/in/run.in [deleted file]
apps/irssi/docs/help/in/sconnect.in
apps/irssi/docs/help/in/server.in
apps/irssi/docs/help/in/servlist.in [deleted file]
apps/irssi/docs/help/in/shutdown.in [new file with mode: 0644]
apps/irssi/docs/help/in/silcoper.in [new file with mode: 0644]
apps/irssi/docs/help/in/silence.in [deleted file]
apps/irssi/docs/help/in/squery.in [deleted file]
apps/irssi/docs/help/in/squit.in [deleted file]
apps/irssi/docs/help/in/stats.in [deleted file]
apps/irssi/docs/help/in/time.in [deleted file]
apps/irssi/docs/help/in/topic.in
apps/irssi/docs/help/in/trace.in [deleted file]
apps/irssi/docs/help/in/ts.in [deleted file]
apps/irssi/docs/help/in/umode.in [new file with mode: 0644]
apps/irssi/docs/help/in/unban.in [deleted file]
apps/irssi/docs/help/in/unload.in
apps/irssi/docs/help/in/unnotify.in [deleted file]
apps/irssi/docs/help/in/unsilence.in [deleted file]
apps/irssi/docs/help/in/uping.in [deleted file]
apps/irssi/docs/help/in/userhost.in [deleted file]
apps/irssi/docs/help/in/users.in [new file with mode: 0644]
apps/irssi/docs/help/in/ver.in [deleted file]
apps/irssi/docs/help/in/version.in
apps/irssi/docs/help/in/voice.in [deleted file]
apps/irssi/docs/help/in/wait.in [deleted file]
apps/irssi/docs/help/in/wall.in [deleted file]
apps/irssi/docs/help/in/wallchops.in [deleted file]
apps/irssi/docs/help/in/wallops.in [deleted file]
apps/irssi/docs/help/in/who.in [deleted file]
apps/irssi/docs/help/in/whois.in
apps/irssi/docs/help/in/whowas.in
apps/irssi/docs/help/in/wjoin.in [deleted file]
apps/irssi/docs/help/in/wquery.in [deleted file]
apps/irssi/docs/manual.txt
apps/irssi/docs/perl.txt [new file with mode: 0644]
apps/irssi/docs/proxy.txt [new file with mode: 0644]
apps/irssi/docs/signals.txt [new file with mode: 0644]
apps/irssi/docs/special_vars.txt [new file with mode: 0644]
apps/irssi/docs/startup-HOWTO.html
apps/irssi/docs/startup-HOWTO.txt [new file with mode: 0644]
apps/irssi/irssi-config.in
apps/irssi/irssi-icon.png [new file with mode: 0644]
apps/irssi/irssi-version.h.in [deleted file]
apps/irssi/irssi.spec.in
apps/irssi/scripts/.cvsignore [new file with mode: 0644]
apps/irssi/scripts/Makefile.am [new file with mode: 0644]
apps/irssi/scripts/autoop.pl [new file with mode: 0644]
apps/irssi/scripts/autorejoin.pl [new file with mode: 0644]
apps/irssi/scripts/clones.pl [new file with mode: 0644]
apps/irssi/scripts/hello.pl [new file with mode: 0644]
apps/irssi/scripts/mail.pl [new file with mode: 0644]
apps/irssi/scripts/mlock.pl [new file with mode: 0644]
apps/irssi/scripts/privmsg.pl [new file with mode: 0644]
apps/irssi/scripts/quitmsg.pl [new file with mode: 0644]
apps/irssi/scripts/realname.pl [new file with mode: 0644]
apps/irssi/silc.conf [new file with mode: 0644]
apps/irssi/src/Makefile.am
apps/irssi/src/common.h
apps/irssi/src/core/.cvsignore [new file with mode: 0644]
apps/irssi/src/core/Makefile.am [new file with mode: 0644]
apps/irssi/src/core/args.c [new file with mode: 0644]
apps/irssi/src/core/args.h [new file with mode: 0644]
apps/irssi/src/core/channel-rec.h [new file with mode: 0644]
apps/irssi/src/core/channel-setup-rec.h [new file with mode: 0644]
apps/irssi/src/core/channels-setup.c [new file with mode: 0644]
apps/irssi/src/core/channels-setup.h [new file with mode: 0644]
apps/irssi/src/core/channels.c [new file with mode: 0644]
apps/irssi/src/core/channels.h [new file with mode: 0644]
apps/irssi/src/core/chat-commands.c [new file with mode: 0644]
apps/irssi/src/core/chat-protocols.c [new file with mode: 0644]
apps/irssi/src/core/chat-protocols.h [new file with mode: 0644]
apps/irssi/src/core/chatnet-rec.h [new file with mode: 0644]
apps/irssi/src/core/chatnets.c [new file with mode: 0644]
apps/irssi/src/core/chatnets.h [new file with mode: 0644]
apps/irssi/src/core/commands.c [new file with mode: 0644]
apps/irssi/src/core/commands.h [new file with mode: 0644]
apps/irssi/src/core/core.c [new file with mode: 0644]
apps/irssi/src/core/core.h [new file with mode: 0644]
apps/irssi/src/core/expandos.c [new file with mode: 0644]
apps/irssi/src/core/expandos.h [new file with mode: 0644]
apps/irssi/src/core/ignore.c [new file with mode: 0644]
apps/irssi/src/core/ignore.h [new file with mode: 0644]
apps/irssi/src/core/levels.c [new file with mode: 0644]
apps/irssi/src/core/levels.h [new file with mode: 0644]
apps/irssi/src/core/line-split.c [new file with mode: 0644]
apps/irssi/src/core/line-split.h [new file with mode: 0644]
apps/irssi/src/core/log-away.c [new file with mode: 0644]
apps/irssi/src/core/log.c [new file with mode: 0644]
apps/irssi/src/core/log.h [new file with mode: 0644]
apps/irssi/src/core/masks.c [new file with mode: 0644]
apps/irssi/src/core/masks.h [new file with mode: 0644]
apps/irssi/src/core/misc.c [new file with mode: 0644]
apps/irssi/src/core/misc.h [new file with mode: 0644]
apps/irssi/src/core/module.h [new file with mode: 0644]
apps/irssi/src/core/modules-load.c [new file with mode: 0644]
apps/irssi/src/core/modules-load.h [new file with mode: 0644]
apps/irssi/src/core/modules.c [new file with mode: 0644]
apps/irssi/src/core/modules.h [new file with mode: 0644]
apps/irssi/src/core/net-disconnect.c [new file with mode: 0644]
apps/irssi/src/core/net-disconnect.h [new file with mode: 0644]
apps/irssi/src/core/net-nonblock.c [new file with mode: 0644]
apps/irssi/src/core/net-nonblock.h [new file with mode: 0644]
apps/irssi/src/core/net-sendbuffer.c [new file with mode: 0644]
apps/irssi/src/core/net-sendbuffer.h [new file with mode: 0644]
apps/irssi/src/core/network.c [new file with mode: 0644]
apps/irssi/src/core/network.h [new file with mode: 0644]
apps/irssi/src/core/nick-rec.h [new file with mode: 0644]
apps/irssi/src/core/nicklist.c [new file with mode: 0644]
apps/irssi/src/core/nicklist.h [new file with mode: 0644]
apps/irssi/src/core/nickmatch-cache.c [new file with mode: 0644]
apps/irssi/src/core/nickmatch-cache.h [new file with mode: 0644]
apps/irssi/src/core/pidwait.c [new file with mode: 0644]
apps/irssi/src/core/pidwait.h [new file with mode: 0644]
apps/irssi/src/core/queries.c [new file with mode: 0644]
apps/irssi/src/core/queries.h [new file with mode: 0644]
apps/irssi/src/core/query-rec.h [new file with mode: 0644]
apps/irssi/src/core/rawlog.c [new file with mode: 0644]
apps/irssi/src/core/rawlog.h [new file with mode: 0644]
apps/irssi/src/core/server-connect-rec.h [new file with mode: 0644]
apps/irssi/src/core/server-rec.h [new file with mode: 0644]
apps/irssi/src/core/server-setup-rec.h [new file with mode: 0644]
apps/irssi/src/core/servers-reconnect.c [new file with mode: 0644]
apps/irssi/src/core/servers-reconnect.h [new file with mode: 0644]
apps/irssi/src/core/servers-setup.c [new file with mode: 0644]
apps/irssi/src/core/servers-setup.h [new file with mode: 0644]
apps/irssi/src/core/servers.c [new file with mode: 0644]
apps/irssi/src/core/servers.h [new file with mode: 0644]
apps/irssi/src/core/session.c [new file with mode: 0644]
apps/irssi/src/core/session.h [new file with mode: 0644]
apps/irssi/src/core/settings.c [new file with mode: 0644]
apps/irssi/src/core/settings.h [new file with mode: 0644]
apps/irssi/src/core/signals.c [new file with mode: 0644]
apps/irssi/src/core/signals.h [new file with mode: 0644]
apps/irssi/src/core/special-vars.c [new file with mode: 0644]
apps/irssi/src/core/special-vars.h [new file with mode: 0644]
apps/irssi/src/core/window-item-def.h [new file with mode: 0644]
apps/irssi/src/core/window-item-rec.h [new file with mode: 0644]
apps/irssi/src/core/write-buffer.c [new file with mode: 0644]
apps/irssi/src/core/write-buffer.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/.cvsignore [new file with mode: 0644]
apps/irssi/src/fe-common/core/Makefile.am [new file with mode: 0644]
apps/irssi/src/fe-common/core/autorun.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/autorun.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/chat-completion.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/chat-completion.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/command-history.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/command-history.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/completion.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/completion.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-channels.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-channels.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-common-core.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-common-core.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-core-commands.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-exec.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-exec.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-expandos.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-help.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-ignore-messages.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-ignore.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-log.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-messages.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-messages.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-modules.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-queries.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-queries.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-server.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-settings.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-windows.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-windows.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/formats.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/formats.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/hilight-text.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/hilight-text.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/keyboard.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/keyboard.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/module-formats.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/module-formats.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/module.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/printtext.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/printtext.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/themes.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/themes.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/translation.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/translation.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/window-activity.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/window-commands.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/window-items.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/window-items.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/windows-layout.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/windows-layout.h [new file with mode: 0644]
apps/irssi/src/fe-common/silc/Makefile.am
apps/irssi/src/fe-common/silc/fe-common-silc.c
apps/irssi/src/fe-common/silc/module-formats.c
apps/irssi/src/fe-common/silc/module-formats.h
apps/irssi/src/fe-common/silc/silc-modules.c [deleted file]
apps/irssi/src/fe-text/.cvsignore [new file with mode: 0644]
apps/irssi/src/fe-text/Makefile.am
apps/irssi/src/fe-text/gui-entry.c
apps/irssi/src/fe-text/gui-entry.h
apps/irssi/src/fe-text/gui-expandos.c
apps/irssi/src/fe-text/gui-printtext.c
apps/irssi/src/fe-text/gui-printtext.h
apps/irssi/src/fe-text/gui-readline.c
apps/irssi/src/fe-text/gui-readline.h
apps/irssi/src/fe-text/gui-windows.c
apps/irssi/src/fe-text/gui-windows.h
apps/irssi/src/fe-text/lastlog.c
apps/irssi/src/fe-text/mainwindows-layout.c [new file with mode: 0644]
apps/irssi/src/fe-text/mainwindows.c
apps/irssi/src/fe-text/mainwindows.h
apps/irssi/src/fe-text/module-formats.c
apps/irssi/src/fe-text/module-formats.h
apps/irssi/src/fe-text/module.h
apps/irssi/src/fe-text/screen.c
apps/irssi/src/fe-text/screen.h
apps/irssi/src/fe-text/silc.c
apps/irssi/src/fe-text/statusbar-config.c [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar-config.h [new file with mode: 0644]
apps/irssi/src/fe-text/statusbar-items.c
apps/irssi/src/fe-text/statusbar.c
apps/irssi/src/fe-text/statusbar.h
apps/irssi/src/fe-text/term-curses.c [new file with mode: 0644]
apps/irssi/src/fe-text/term-dummy.c [new file with mode: 0644]
apps/irssi/src/fe-text/term-terminfo.c [new file with mode: 0644]
apps/irssi/src/fe-text/term.c [new file with mode: 0644]
apps/irssi/src/fe-text/term.h [new file with mode: 0644]
apps/irssi/src/fe-text/terminfo-core.c [new file with mode: 0644]
apps/irssi/src/fe-text/terminfo-core.h [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-commands.c
apps/irssi/src/fe-text/textbuffer-reformat.c [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-reformat.h [new file with mode: 0644]
apps/irssi/src/fe-text/textbuffer-view.c
apps/irssi/src/fe-text/textbuffer-view.h
apps/irssi/src/fe-text/textbuffer.c
apps/irssi/src/fe-text/textbuffer.h
apps/irssi/src/fe-text/tparm.c [new file with mode: 0644]
apps/irssi/src/fe-text/utf8.c [new file with mode: 0644]
apps/irssi/src/fe-text/utf8.h [new file with mode: 0644]
apps/irssi/src/lib-config/.cvsignore [new file with mode: 0644]
apps/irssi/src/lib-config/get.c
apps/irssi/src/lib-config/iconfig.h
apps/irssi/src/lib-config/parse.c
apps/irssi/src/lib-config/write.c
apps/irssi/src/lib-popt/popt.c
apps/irssi/src/lib-popt/poptconfig.c
apps/irssi/src/lib-popt/popthelp.c
apps/irssi/src/lib-popt/poptparse.c
apps/irssi/src/perl/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/Makefile.am [new file with mode: 0644]
apps/irssi/src/perl/common/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/common/Channel.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Core.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Ignore.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Irssi.bs [new file with mode: 0644]
apps/irssi/src/perl/common/Irssi.pm [new file with mode: 0644]
apps/irssi/src/perl/common/Irssi.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Log.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Makefile.PL.in [new file with mode: 0644]
apps/irssi/src/perl/common/Masks.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Query.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Rawlog.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Server.xs [new file with mode: 0644]
apps/irssi/src/perl/common/Settings.xs [new file with mode: 0644]
apps/irssi/src/perl/common/module.h [new file with mode: 0644]
apps/irssi/src/perl/common/typemap [new file with mode: 0644]
apps/irssi/src/perl/get-signals.pl [new file with mode: 0755]
apps/irssi/src/perl/irssi-core.pl [new file with mode: 0644]
apps/irssi/src/perl/irssi-core.pl.h [new file with mode: 0644]
apps/irssi/src/perl/libperl_dynaloader.la [new file with mode: 0644]
apps/irssi/src/perl/libperl_orig.la [new file with mode: 0644]
apps/irssi/src/perl/module-fe.h [new file with mode: 0644]
apps/irssi/src/perl/module-formats.c [new file with mode: 0644]
apps/irssi/src/perl/module-formats.h [new file with mode: 0644]
apps/irssi/src/perl/module.h [new file with mode: 0644]
apps/irssi/src/perl/perl-common.c [new file with mode: 0644]
apps/irssi/src/perl/perl-common.h [new file with mode: 0644]
apps/irssi/src/perl/perl-core.c [new file with mode: 0644]
apps/irssi/src/perl/perl-core.h [new file with mode: 0644]
apps/irssi/src/perl/perl-fe.c [new file with mode: 0644]
apps/irssi/src/perl/perl-signals-list.h [new file with mode: 0644]
apps/irssi/src/perl/perl-signals.c [new file with mode: 0644]
apps/irssi/src/perl/perl-signals.h [new file with mode: 0644]
apps/irssi/src/perl/perl-sources.c [new file with mode: 0644]
apps/irssi/src/perl/perl-sources.h [new file with mode: 0644]
apps/irssi/src/perl/textui/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/textui/Makefile.PL.in [new file with mode: 0644]
apps/irssi/src/perl/textui/Statusbar.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/TextBuffer.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/TextBufferView.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/TextUI.pm [new file with mode: 0644]
apps/irssi/src/perl/textui/TextUI.xs [new file with mode: 0644]
apps/irssi/src/perl/textui/module.h [new file with mode: 0644]
apps/irssi/src/perl/textui/typemap [new file with mode: 0644]
apps/irssi/src/perl/ui/.cvsignore [new file with mode: 0644]
apps/irssi/src/perl/ui/Formats.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/Makefile.PL.in [new file with mode: 0644]
apps/irssi/src/perl/ui/Themes.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/UI.pm [new file with mode: 0644]
apps/irssi/src/perl/ui/UI.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/Window.xs [new file with mode: 0644]
apps/irssi/src/perl/ui/module.h [new file with mode: 0644]
apps/irssi/src/perl/ui/typemap [new file with mode: 0644]
apps/irssi/src/silc/core/Makefile.am [new file with mode: 0644]
apps/irssi/src/silc/core/client_ops.c [new file with mode: 0644]
apps/irssi/src/silc/core/client_ops.h [new file with mode: 0644]
apps/irssi/src/silc/core/clientutil.c [new file with mode: 0644]
apps/irssi/src/silc/core/clientutil.h [moved from lib/silccrypt/loki_internal.h with 51% similarity]
apps/irssi/src/silc/core/module.h [new file with mode: 0644]
apps/irssi/src/silc/core/silc-channels.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-channels.h [new file with mode: 0644]
apps/irssi/src/silc/core/silc-core.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-core.h [new file with mode: 0644]
apps/irssi/src/silc/core/silc-nicklist.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-nicklist.h [new file with mode: 0644]
apps/irssi/src/silc/core/silc-queries.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-queries.h [new file with mode: 0644]
apps/irssi/src/silc/core/silc-servers-reconnect.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-servers.c [new file with mode: 0644]
apps/irssi/src/silc/core/silc-servers.h [new file with mode: 0644]
apps/irssi/src/silc/silc.c [deleted file]
apps/silc/Makefile.am
apps/silc/README [new file with mode: 0644]
apps/silc/client.c [deleted file]
apps/silc/client.h [deleted file]
apps/silc/client_ops.c [new file with mode: 0644]
apps/silc/client_ops.h [new file with mode: 0644]
apps/silc/clientconfig.c
apps/silc/clientconfig.h
apps/silc/clientincludes.h [moved from includes/clientincludes.h with 80% similarity]
apps/silc/clientutil.c
apps/silc/clientutil.h
apps/silc/command.c [deleted file]
apps/silc/command.h [deleted file]
apps/silc/command_reply.c [deleted file]
apps/silc/idlist.h [deleted file]
apps/silc/local_command.c [new file with mode: 0644]
apps/silc/local_command.h [new file with mode: 0644]
apps/silc/protocol.c [deleted file]
apps/silc/protocol.h [deleted file]
apps/silc/pubkey.pub [deleted file]
apps/silc/screen.c
apps/silc/screen.h
apps/silc/silc.c
apps/silc/silc.h
apps/silc/testi.conf [deleted file]
apps/silc/testi2.conf [deleted file]
apps/silcd/Makefile.am
apps/silcd/command.c
apps/silcd/command.h
apps/silcd/command_reply.c
apps/silcd/command_reply.h
apps/silcd/idlist.c
apps/silcd/idlist.h
apps/silcd/leevi.conf [deleted file]
apps/silcd/leevi2.conf [deleted file]
apps/silcd/packet_receive.c [new file with mode: 0644]
apps/silcd/packet_receive.h [new file with mode: 0644]
apps/silcd/packet_send.c [new file with mode: 0644]
apps/silcd/packet_send.h [new file with mode: 0644]
apps/silcd/protocol.c
apps/silcd/protocol.h
apps/silcd/pubkey.pub [deleted file]
apps/silcd/route.c
apps/silcd/route.h
apps/silcd/server.c
apps/silcd/server.h
apps/silcd/server_backup.c [new file with mode: 0644]
apps/silcd/server_backup.h [new file with mode: 0644]
apps/silcd/server_internal.h
apps/silcd/server_util.c [new file with mode: 0644]
apps/silcd/server_util.h [new file with mode: 0644]
apps/silcd/server_version.c
apps/silcd/serverconfig.c
apps/silcd/serverconfig.h
apps/silcd/serverid.c
apps/silcd/serverid.h
apps/silcd/serverincludes.h [moved from includes/serverincludes.h with 90% similarity]
apps/silcd/silc.conf [deleted file]
apps/silcd/silcd.c
apps/silcd/silcd.h
apps/silcd/testi.conf [deleted file]
apps/silcd/testi2.conf [deleted file]
apps/silcer/ABOUT-NLS [new file with mode: 0644]
apps/silcer/AUTHORS [new file with mode: 0644]
apps/silcer/COPYING [new file with mode: 0644]
apps/silcer/ChangeLog [new file with mode: 0644]
apps/silcer/INSTALL [new file with mode: 0644]
apps/silcer/Makefile.am [new file with mode: 0644]
apps/silcer/NEWS [new file with mode: 0644]
apps/silcer/README [new file with mode: 0644]
apps/silcer/acconfig.h [new file with mode: 0644]
apps/silcer/aclocal.m4 [new file with mode: 0644]
apps/silcer/autogen.sh [new file with mode: 0755]
apps/silcer/config.guess [new file with mode: 0755]
apps/silcer/config.h.in [new file with mode: 0644]
apps/silcer/config.sub [new file with mode: 0755]
apps/silcer/configure.in [new file with mode: 0644]
apps/silcer/depcomp [new file with mode: 0755]
apps/silcer/install-sh [new file with mode: 0755]
apps/silcer/intl/ChangeLog [new file with mode: 0644]
apps/silcer/intl/Makefile.in.in [new file with mode: 0644]
apps/silcer/intl/VERSION [new file with mode: 0644]
apps/silcer/intl/bindtextdom.c [new file with mode: 0644]
apps/silcer/intl/config.charset [new file with mode: 0755]
apps/silcer/intl/dcgettext.c [new file with mode: 0644]
apps/silcer/intl/dcigettext.c [new file with mode: 0644]
apps/silcer/intl/dcngettext.c [new file with mode: 0644]
apps/silcer/intl/dgettext.c [new file with mode: 0644]
apps/silcer/intl/dngettext.c [new file with mode: 0644]
apps/silcer/intl/explodename.c [new file with mode: 0644]
apps/silcer/intl/finddomain.c [new file with mode: 0644]
apps/silcer/intl/gettext.c [new file with mode: 0644]
apps/silcer/intl/gettext.h [new file with mode: 0644]
apps/silcer/intl/gettextP.h [new file with mode: 0644]
apps/silcer/intl/hash-string.h [new file with mode: 0644]
apps/silcer/intl/intl-compat.c [new file with mode: 0644]
apps/silcer/intl/l10nflist.c [new file with mode: 0644]
apps/silcer/intl/libgettext.h [new file with mode: 0644]
apps/silcer/intl/libgnuintl.h [new file with mode: 0644]
apps/silcer/intl/loadinfo.h [new file with mode: 0644]
apps/silcer/intl/loadmsgcat.c [new file with mode: 0644]
apps/silcer/intl/localcharset.c [new file with mode: 0644]
apps/silcer/intl/locale.alias [new file with mode: 0644]
apps/silcer/intl/localealias.c [new file with mode: 0644]
apps/silcer/intl/ngettext.c [new file with mode: 0644]
apps/silcer/intl/plural.c [new file with mode: 0644]
apps/silcer/intl/plural.y [new file with mode: 0644]
apps/silcer/intl/ref-add.sin [new file with mode: 0644]
apps/silcer/intl/ref-del.sin [new file with mode: 0644]
apps/silcer/intl/textdomain.c [new file with mode: 0644]
apps/silcer/macros/Makefile.am [new file with mode: 0644]
apps/silcer/macros/aclocal-include.m4 [new file with mode: 0644]
apps/silcer/macros/autogen.sh [new file with mode: 0644]
apps/silcer/macros/compiler-flags.m4 [new file with mode: 0644]
apps/silcer/macros/curses.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-bonobo-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-common.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-fileutils.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-ghttp-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-gnorba-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-guile-checks.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-libgtop-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-objc-checks.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-orbit-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-print-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-pthread-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-support.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-undelfs.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-vfs.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-x-checks.m4 [new file with mode: 0644]
apps/silcer/macros/gnome-xml-check.m4 [new file with mode: 0644]
apps/silcer/macros/gnome.m4 [new file with mode: 0644]
apps/silcer/macros/gperf-check.m4 [new file with mode: 0644]
apps/silcer/macros/linger.m4 [new file with mode: 0644]
apps/silcer/macros/need-declaration.m4 [new file with mode: 0644]
apps/silcer/missing [new file with mode: 0755]
apps/silcer/mkinstalldirs [new file with mode: 0755]
apps/silcer/pixmaps/folder.xpm [new file with mode: 0644]
apps/silcer/pixmaps/glade-group.xpm [new file with mode: 0644]
apps/silcer/po/Makefile.in.in [new file with mode: 0644]
apps/silcer/po/POTFILES.in [new file with mode: 0644]
apps/silcer/silcer.png [new file with mode: 0644]
apps/silcer/src/Makefile.am [new file with mode: 0644]
apps/silcer/src/SilcerMainDlg.cc [new file with mode: 0644]
apps/silcer/src/SilcerMainDlg.hh [new file with mode: 0644]
apps/silcer/src/gtkspell.c [new file with mode: 0644]
apps/silcer/src/gtkspell.h [new file with mode: 0644]
apps/silcer/src/gtkurl.c [new file with mode: 0644]
apps/silcer/src/gtkurl.h [new file with mode: 0644]
apps/silcer/src/silcer.cc [new file with mode: 0644]
apps/silcer/src/silcer_gladehelper.hh [new file with mode: 0644]
apps/silcer/src/silcerapp.cc [new file with mode: 0644]
apps/silcer/src/silcerapp.hh [new file with mode: 0644]
apps/silcer/src/silcerbasewin.cc [new file with mode: 0644]
apps/silcer/src/silcerbasewin.hh [new file with mode: 0644]
apps/silcer/src/silcerchatview.cc [new file with mode: 0644]
apps/silcer/src/silcerchatview.hh [new file with mode: 0644]
apps/silcer/src/xtext.c [new file with mode: 0644]
apps/silcer/src/xtext.h [new file with mode: 0644]
apps/silcer/stamp-h.in [new file with mode: 0644]
apps/silcer/ui/SilcerConfirmPkDlg.glade [new file with mode: 0644]
apps/silcer/ui/SilcerFirstsetupDlg.glade [new file with mode: 0644]
apps/silcer/ui/SilcerMainDlg.glade [new file with mode: 0644]
apps/silcer/xml-i18n-extract.in [new file with mode: 0644]
apps/silcer/xml-i18n-merge.in [new file with mode: 0644]
apps/silcer/xml-i18n-update.in [new file with mode: 0644]
config.guess
config.sub
configure.in [deleted file]
configure.in.pre [new file with mode: 0644]
distributions [new file with mode: 0644]
doc/CodingStyle
doc/FAQ
doc/Makefile.am.pre [new file with mode: 0644]
doc/Makefile.in [deleted file]
doc/draft-riikonen-silc-commands-00.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-commands-01.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-commands-02.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-commands-03.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-ke-auth-00.nroff
doc/draft-riikonen-silc-ke-auth-01.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-ke-auth-02.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-ke-auth-03.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-ke-auth-04.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-ke-auth-05.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-pp-00.nroff
doc/draft-riikonen-silc-pp-01.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-pp-02.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-pp-03.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-pp-04.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-pp-05.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-spec-00.nroff
doc/draft-riikonen-silc-spec-01.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-spec-02.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-spec-03.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-spec-04.nroff [new file with mode: 0644]
doc/draft-riikonen-silc-spec-05.nroff [new file with mode: 0644]
doc/example_silc.conf [deleted file]
doc/example_silc.conf.in [new file with mode: 0644]
doc/example_silcd.conf [deleted file]
doc/example_silcd.conf.in [new file with mode: 0644]
doc/examples/README [new file with mode: 0644]
doc/examples/cell1_backup.conf [new file with mode: 0644]
doc/examples/cell1_router.conf [new file with mode: 0644]
doc/examples/cell1_server1.conf [new file with mode: 0644]
doc/examples/cell1_server2.conf [new file with mode: 0644]
doc/examples/cell2_router.conf [new file with mode: 0644]
doc/examples/cell2_server1.conf [new file with mode: 0644]
doc/examples/cell2_server2.conf [new file with mode: 0644]
doc/examples/cell3_router.conf [new file with mode: 0644]
doc/examples/cell3_server1.conf [new file with mode: 0644]
doc/examples/cell3_server2.conf [new file with mode: 0644]
doc/examples/silcd.prv [new file with mode: 0644]
doc/examples/silcd.pub [new file with mode: 0644]
doc/whitepaper/Makefile.am [new file with mode: 0644]
doc/whitepaper/silc_channel.edg [new file with mode: 0644]
doc/whitepaper/silc_network.edg [new file with mode: 0644]
doc/whitepaper/silc_packet.edg [new file with mode: 0644]
doc/whitepaper/silc_priv1.edg [new file with mode: 0644]
doc/whitepaper/silc_priv2.edg [new file with mode: 0644]
doc/whitepaper/silc_priv3.edg [new file with mode: 0644]
doc/whitepaper/silc_protocol.html [new file with mode: 0644]
doc/whitepaper/silc_routers.edg [new file with mode: 0644]
doc/whitepaper/silc_template.edg [new file with mode: 0644]
includes/Makefile.am
includes/Makefile.in [deleted file]
includes/bitmove.h
includes/clientlibincludes.h [new file with mode: 0644]
includes/silcdefs.h.in [deleted file]
includes/silcepoc.h [new file with mode: 0644]
includes/silcincludes.h
includes/silcwin32.h [new file with mode: 0644]
includes/version.h
lib/Makefile.am.pre [new file with mode: 0644]
lib/contrib/Makefile.am [moved from lib/Makefile.am with 74% similarity]
lib/contrib/getopt.c [new file with mode: 0644]
lib/contrib/getopt.h [new file with mode: 0644]
lib/contrib/getopt1.c [new file with mode: 0644]
lib/contrib/regex.c [new file with mode: 0644]
lib/contrib/regex.h [new file with mode: 0644]
lib/doc/INDEX.tmpl [new file with mode: 0644]
lib/doc/LIBINDEX [new file with mode: 0644]
lib/doc/arch.gif [new file with mode: 0644]
lib/doc/box.gif [new file with mode: 0644]
lib/doc/box2.gif [new file with mode: 0644]
lib/doc/dot.gif [new file with mode: 0644]
lib/doc/index_pic.gif [new file with mode: 0644]
lib/doc/intro_reference.html [new file with mode: 0644]
lib/doc/silcclient_using.html [new file with mode: 0644]
lib/doc/silcexample.h [new file with mode: 0644]
lib/doc/space.gif [new file with mode: 0644]
lib/silcclient/DIRECTORY [new file with mode: 0644]
lib/silcclient/Makefile.am [moved from doc/Makefile.am with 56% similarity]
lib/silcclient/client.c [new file with mode: 0644]
lib/silcclient/client.h [new file with mode: 0644]
lib/silcclient/client_channel.c [new file with mode: 0644]
lib/silcclient/client_ftp.c [new file with mode: 0644]
lib/silcclient/client_internal.h [new file with mode: 0644]
lib/silcclient/client_keyagr.c [new file with mode: 0644]
lib/silcclient/client_notify.c [new file with mode: 0644]
lib/silcclient/client_ops_example.c [new file with mode: 0644]
lib/silcclient/client_prvmsg.c [new file with mode: 0644]
lib/silcclient/command.c [new file with mode: 0644]
lib/silcclient/command.h [new file with mode: 0644]
lib/silcclient/command_reply.c [new file with mode: 0644]
lib/silcclient/command_reply.h [moved from apps/silc/command_reply.h with 60% similarity]
lib/silcclient/idlist.c [new file with mode: 0644]
lib/silcclient/idlist.h [new file with mode: 0644]
lib/silcclient/protocol.c [new file with mode: 0644]
lib/silcclient/protocol.h [new file with mode: 0644]
lib/silcclient/silcapi.h [new file with mode: 0644]
lib/silccore/DIRECTORY [new file with mode: 0644]
lib/silccore/Makefile.am
lib/silccore/id.c [deleted file]
lib/silccore/id.h [deleted file]
lib/silccore/idcache.c [deleted file]
lib/silccore/idcache.h [deleted file]
lib/silccore/silcargument.c [new file with mode: 0644]
lib/silccore/silcargument.h [new file with mode: 0644]
lib/silccore/silcauth.c [new file with mode: 0644]
lib/silccore/silcauth.h [new file with mode: 0644]
lib/silccore/silcbuffer.c [deleted file]
lib/silccore/silcbufutil.c [deleted file]
lib/silccore/silcchannel.c
lib/silccore/silcchannel.h
lib/silccore/silccommand.c
lib/silccore/silccommand.h
lib/silccore/silcconfig.c [deleted file]
lib/silccore/silcid.c [new file with mode: 0644]
lib/silccore/silcid.h [new file with mode: 0644]
lib/silccore/silcidcache.c [new file with mode: 0644]
lib/silccore/silcidcache.h [new file with mode: 0644]
lib/silccore/silclog.c [deleted file]
lib/silccore/silclog.h [deleted file]
lib/silccore/silcmode.h [new file with mode: 0644]
lib/silccore/silcnet.c [deleted file]
lib/silccore/silcnet.h [deleted file]
lib/silccore/silcnotify.c [new file with mode: 0644]
lib/silccore/silcnotify.h [new file with mode: 0644]
lib/silccore/silcpacket.c
lib/silccore/silcpacket.h
lib/silccore/silcprivate.c [new file with mode: 0644]
lib/silccore/silcprivate.h [new file with mode: 0644]
lib/silccore/silcprotocol.c [deleted file]
lib/silccore/silcprotocol.h [deleted file]
lib/silccore/silcschedule.c [deleted file]
lib/silccore/silcschedule.h [deleted file]
lib/silccore/silcsockconn.c [deleted file]
lib/silccore/silcsockconn.h [deleted file]
lib/silccore/silctask.c [deleted file]
lib/silccore/silctask.h [deleted file]
lib/silccore/silcutil.c [deleted file]
lib/silccore/silcutil.h [deleted file]
lib/silccrypt/DIRECTORY [new file with mode: 0644]
lib/silccrypt/Makefile.am
lib/silccrypt/aes.c [moved from lib/silccrypt/rijndael.c with 86% similarity]
lib/silccrypt/aes.h [moved from lib/silccore/silcconfig.h with 69% similarity]
lib/silccrypt/blowfish.c
lib/silccrypt/blowfish.h
lib/silccrypt/cast.c
lib/silccrypt/cast.h
lib/silccrypt/ciphers.h
lib/silccrypt/ciphers_def.h
lib/silccrypt/crypton.c [deleted file]
lib/silccrypt/crypton.h [deleted file]
lib/silccrypt/crypton_internal.h [deleted file]
lib/silccrypt/dfc.c [deleted file]
lib/silccrypt/dfc.h [deleted file]
lib/silccrypt/dfc_internal.h [deleted file]
lib/silccrypt/e2.c [deleted file]
lib/silccrypt/e2.h [deleted file]
lib/silccrypt/loki.c [deleted file]
lib/silccrypt/loki.h [deleted file]
lib/silccrypt/mars.c
lib/silccrypt/mars.h
lib/silccrypt/md5.c
lib/silccrypt/md5.h
lib/silccrypt/md5_internal.h
lib/silccrypt/none.c
lib/silccrypt/none.h
lib/silccrypt/pkcs1.c [new file with mode: 0644]
lib/silccrypt/pkcs1.h [new file with mode: 0644]
lib/silccrypt/rc5.c
lib/silccrypt/rc5.h
lib/silccrypt/rc5_internal.h
lib/silccrypt/rc6.c
lib/silccrypt/rc6.h
lib/silccrypt/rijndael.h [deleted file]
lib/silccrypt/rsa.c
lib/silccrypt/rsa.h
lib/silccrypt/rsa_internal.h
lib/silccrypt/safer.c [deleted file]
lib/silccrypt/safer.h [deleted file]
lib/silccrypt/safer_internal.h [deleted file]
lib/silccrypt/serpent.c [deleted file]
lib/silccrypt/serpent.h [deleted file]
lib/silccrypt/serpent_internal.h [deleted file]
lib/silccrypt/sha1.c
lib/silccrypt/sha1.h
lib/silccrypt/sha1_internal.h
lib/silccrypt/silccipher.c
lib/silccrypt/silccipher.h
lib/silccrypt/silcdh.h [new file with mode: 0644]
lib/silccrypt/silchash.c
lib/silccrypt/silchash.h
lib/silccrypt/silchmac.c
lib/silccrypt/silchmac.h
lib/silccrypt/silcpkcs.c
lib/silccrypt/silcpkcs.h
lib/silccrypt/silcrng.c
lib/silccrypt/silcrng.h
lib/silccrypt/tests/inst [deleted file]
lib/silccrypt/tests/inst_aes [new file with mode: 0644]
lib/silccrypt/tests/inst_rsa [deleted file]
lib/silccrypt/tests/inst_twofish
lib/silccrypt/tests/insth [deleted file]
lib/silccrypt/tests/test_aes.c [new file with mode: 0644]
lib/silccrypt/tests/test_rijndael.c [deleted file]
lib/silccrypt/tests/test_rsa.c [deleted file]
lib/silccrypt/tests/test_twofish.c
lib/silccrypt/twofish.c
lib/silccrypt/twofish.h
lib/silcmath/DIRECTORY [new file with mode: 0644]
lib/silcmath/Makefile.am
lib/silcmath/modinv.c
lib/silcmath/mp_gmp.c [new file with mode: 0644]
lib/silcmath/mp_gmp.h [moved from lib/silcmath/modinv.h with 79% similarity]
lib/silcmath/mp_mpi.c [new file with mode: 0644]
lib/silcmath/mp_mpi.h [moved from lib/silcmath/silcprimegen.h with 66% similarity]
lib/silcmath/mpbin.c [new file with mode: 0644]
lib/silcmath/silcmath.h [new file with mode: 0644]
lib/silcmath/silcmp.h
lib/silcmath/silcprimegen.c
lib/silcsftp/DIRECTORY [new file with mode: 0644]
lib/silcsftp/Makefile.am [new file with mode: 0644]
lib/silcsftp/sftp_client.c [new file with mode: 0644]
lib/silcsftp/sftp_fs_memory.c [new file with mode: 0644]
lib/silcsftp/sftp_server.c [new file with mode: 0644]
lib/silcsftp/sftp_util.c [new file with mode: 0644]
lib/silcsftp/sftp_util.h [new file with mode: 0644]
lib/silcsftp/silcsftp.h [new file with mode: 0644]
lib/silcsftp/silcsftp_fs.h [new file with mode: 0644]
lib/silcsftp/tests/Makefile.am [new file with mode: 0644]
lib/silcsftp/tests/sftp_client.c [new file with mode: 0644]
lib/silcsftp/tests/sftp_server.c [new file with mode: 0644]
lib/silcsim/Makefile.am
lib/silcsim/modules/Makefile.in [deleted file]
lib/silcsim/silcsim.c
lib/silcske/DIRECTORY [new file with mode: 0644]
lib/silcske/Makefile.am
lib/silcske/groups.c
lib/silcske/groups.h
lib/silcske/groups_internal.h
lib/silcske/payload.c
lib/silcske/payload.h
lib/silcske/payload_internal.h [deleted file]
lib/silcske/silcske.c
lib/silcske/silcske.h
lib/silcske/silcske_status.h
lib/silcutil/DIRECTORY [new file with mode: 0644]
lib/silcutil/Makefile.am [new file with mode: 0644]
lib/silcutil/silcbuffer.h [moved from lib/silccore/silcbuffer.h with 74% similarity]
lib/silcutil/silcbuffmt.c [moved from lib/silccore/silcbuffmt.c with 58% similarity]
lib/silcutil/silcbuffmt.h [moved from lib/silccore/silcbuffmt.h with 60% similarity]
lib/silcutil/silcbufutil.h [moved from lib/silccore/silcbufutil.h with 66% similarity]
lib/silcutil/silcconfig.c [new file with mode: 0644]
lib/silcutil/silcconfig.h [new file with mode: 0644]
lib/silcutil/silcdlist.h [new file with mode: 0644]
lib/silcutil/silchashtable.c [new file with mode: 0644]
lib/silcutil/silchashtable.h [new file with mode: 0644]
lib/silcutil/silclist.h [new file with mode: 0644]
lib/silcutil/silclog.c [new file with mode: 0644]
lib/silcutil/silclog.h [new file with mode: 0644]
lib/silcutil/silcmemory.c [moved from lib/silccore/silcmemory.c with 64% similarity]
lib/silcutil/silcmemory.h [moved from lib/silccore/silcmemory.h with 99% similarity]
lib/silcutil/silcmutex.h [new file with mode: 0644]
lib/silcutil/silcnet.c [new file with mode: 0644]
lib/silcutil/silcnet.h [new file with mode: 0644]
lib/silcutil/silcprotocol.c [new file with mode: 0644]
lib/silcutil/silcprotocol.h [new file with mode: 0644]
lib/silcutil/silcschedule.c [new file with mode: 0644]
lib/silcutil/silcschedule.h [new file with mode: 0644]
lib/silcutil/silcschedule_i.h [new file with mode: 0644]
lib/silcutil/silcsockconn.c [new file with mode: 0644]
lib/silcutil/silcsockconn.h [new file with mode: 0644]
lib/silcutil/silcthread.h [new file with mode: 0644]
lib/silcutil/silcutil.c [new file with mode: 0644]
lib/silcutil/silcutil.h [new file with mode: 0644]
lib/silcutil/symbian/silcepocsockconn.cpp [new file with mode: 0644]
lib/silcutil/symbian/silcepocthread.cpp [new file with mode: 0644]
lib/silcutil/symbian/silcepocutil.cpp [new file with mode: 0644]
lib/silcutil/unix/Makefile.am [new file with mode: 0644]
lib/silcutil/unix/silcunixmutex.c [new file with mode: 0644]
lib/silcutil/unix/silcunixnet.c [new file with mode: 0644]
lib/silcutil/unix/silcunixschedule.c [new file with mode: 0644]
lib/silcutil/unix/silcunixsockconn.c [new file with mode: 0644]
lib/silcutil/unix/silcunixthread.c [new file with mode: 0644]
lib/silcutil/unix/silcunixutil.c [new file with mode: 0644]
lib/silcutil/win32/Makefile.am [new file with mode: 0644]
lib/silcutil/win32/silcwin32mutex.c [new file with mode: 0644]
lib/silcutil/win32/silcwin32net.c [new file with mode: 0644]
lib/silcutil/win32/silcwin32schedule.c [new file with mode: 0644]
lib/silcutil/win32/silcwin32sockconn.c [new file with mode: 0644]
lib/silcutil/win32/silcwin32thread.c [new file with mode: 0644]
lib/silcutil/win32/silcwin32util.c [new file with mode: 0644]
ltmain.sh [new file with mode: 0644]
missing
prepare
prepare-clean
public_html/about.html [deleted file]
public_html/features.html [deleted file]
public_html/history.html [deleted file]
public_html/html/about.php [new file with mode: 0644]
public_html/html/contact.php [new file with mode: 0644]
public_html/html/contribute.php [new file with mode: 0644]
public_html/html/copying.php [moved from public_html/copying.html with 91% similarity]
public_html/html/counter.php [new file with mode: 0644]
public_html/html/cvs.php [new file with mode: 0644]
public_html/html/docs.php [new file with mode: 0644]
public_html/html/download.php [new file with mode: 0644]
public_html/html/faq.php [new file with mode: 0644]
public_html/html/features.php [new file with mode: 0644]
public_html/html/help.php [new file with mode: 0644]
public_html/html/history.php [new file with mode: 0644]
public_html/html/install.php [new file with mode: 0644]
public_html/html/links.php [new file with mode: 0644]
public_html/html/lists.php [new file with mode: 0644]
public_html/html/mirrors.php [new file with mode: 0644]
public_html/html/news.php [new file with mode: 0644]
public_html/html/servers.php [new file with mode: 0644]
public_html/html/todo.php [new file with mode: 0644]
public_html/html/whitepaper.php [new file with mode: 0644]
public_html/img/silc.gif [new file with mode: 0755]
public_html/index.html [deleted file]
public_html/index.php [new file with mode: 0644]
public_html/silc.css [new file with mode: 0644]
public_html/silc.jpg [deleted file]
public_html/silc2.jpg [deleted file]
scripts/fix.pl [moved from doc/fix.pl with 100% similarity]
scripts/html2ps [new file with mode: 0755]
scripts/html2psrc [new file with mode: 0755]
scripts/makerfc [moved from doc/makerfc with 75% similarity]
scripts/silcdoc/gen.sh [new file with mode: 0755]
scripts/silcdoc/gen_detail.php [new file with mode: 0644]
scripts/silcdoc/gen_html_detail.php [moved from lib/silccrypt/e2_internal.h with 51% similarity]
scripts/silcdoc/gen_index.php [new file with mode: 0644]
scripts/silcdoc/gen_toc.php [new file with mode: 0644]
scripts/silcdoc/index.php [new file with mode: 0644]
scripts/silcdoc/silcdoc [new file with mode: 0755]
util/robodoc/AUTHORS [new file with mode: 0644]
util/robodoc/COPYING [new file with mode: 0644]
util/robodoc/ChangeLog [new file with mode: 0644]
util/robodoc/Docs/example.c [new file with mode: 0644]
util/robodoc/Docs/example_makefile [new file with mode: 0644]
util/robodoc/Docs/general.m4 [new file with mode: 0644]
util/robodoc/Docs/main.css [new file with mode: 0644]
util/robodoc/Docs/makefile.am [new file with mode: 0644]
util/robodoc/Docs/makefile.in [new file with mode: 0644]
util/robodoc/Docs/robodoc.1 [new file with mode: 0644]
util/robodoc/Docs/robodoc.html [new file with mode: 0644]
util/robodoc/Docs/robodoc.m4 [new file with mode: 0644]
util/robodoc/Docs/tocgen.m4 [new file with mode: 0644]
util/robodoc/Examples/C/makefile [new file with mode: 0644]
util/robodoc/Examples/C/prog1.c [new file with mode: 0644]
util/robodoc/Examples/C/prog1.c.html [new file with mode: 0644]
util/robodoc/Examples/C/prog2.c [new file with mode: 0644]
util/robodoc/Examples/C/prog2.c.html [new file with mode: 0644]
util/robodoc/Examples/CPP/makefile [new file with mode: 0644]
util/robodoc/Examples/CPP/masterindex.html [new file with mode: 0644]
util/robodoc/Examples/CPP/muppets.cpp [new file with mode: 0644]
util/robodoc/Examples/CPP/muppets.cpp.html [new file with mode: 0644]
util/robodoc/Examples/CPP/muppets.h [new file with mode: 0644]
util/robodoc/Examples/CPP/muppets.h.html [new file with mode: 0644]
util/robodoc/Headers/assembler.sample [new file with mode: 0644]
util/robodoc/Headers/basic.sample [new file with mode: 0644]
util/robodoc/Headers/c.sample [new file with mode: 0644]
util/robodoc/Headers/cpp.sample [new file with mode: 0644]
util/robodoc/Headers/fortan.sample [new file with mode: 0644]
util/robodoc/Headers/html.sample [new file with mode: 0644]
util/robodoc/Headers/tcl.sample [new file with mode: 0644]
util/robodoc/INSTALL [new file with mode: 0644]
util/robodoc/NEWS [new file with mode: 0644]
util/robodoc/README [new file with mode: 0644]
util/robodoc/Source/analyser.c [new file with mode: 0644]
util/robodoc/Source/analyser.h [new file with mode: 0644]
util/robodoc/Source/config.h [new file with mode: 0644]
util/robodoc/Source/config.h.in [new file with mode: 0644]
util/robodoc/Source/folds.c [new file with mode: 0644]
util/robodoc/Source/folds.h [new file with mode: 0644]
util/robodoc/Source/generator.c [new file with mode: 0644]
util/robodoc/Source/generator.h [new file with mode: 0644]
util/robodoc/Source/headers.c [new file with mode: 0644]
util/robodoc/Source/headers.h [new file with mode: 0644]
util/robodoc/Source/items.c [new file with mode: 0644]
util/robodoc/Source/items.h [new file with mode: 0644]
util/robodoc/Source/links.c [new file with mode: 0644]
util/robodoc/Source/links.h [new file with mode: 0644]
util/robodoc/Source/makefile.am [new file with mode: 0644]
util/robodoc/Source/makefile.in [new file with mode: 0644]
util/robodoc/Source/makefile.plain [new file with mode: 0644]
util/robodoc/Source/robodoc.c [new file with mode: 0644]
util/robodoc/Source/robodoc.h [new file with mode: 0644]
util/robodoc/Source/stamp-h.in [new file with mode: 0644]
util/robodoc/Source/util.c [new file with mode: 0644]
util/robodoc/Source/util.h [new file with mode: 0644]
util/robodoc/TODO [new file with mode: 0644]
util/robodoc/aclocal.m4 [new file with mode: 0644]
util/robodoc/configure [new file with mode: 0755]
util/robodoc/configure.in [new file with mode: 0644]
util/robodoc/install-sh [new file with mode: 0755]
util/robodoc/makefile.am [new file with mode: 0644]
util/robodoc/makefile.in [new file with mode: 0644]
util/robodoc/missing [new file with mode: 0755]
util/robodoc/mkinstalldirs [new file with mode: 0755]
win32/Makefile.am [new file with mode: 0644]
win32/copy_dll [new file with mode: 0644]
win32/libsilc/Makefile.am [moved from Makefile.am with 90% similarity]
win32/libsilc/libsilc.def [new file with mode: 0644]
win32/libsilc/libsilc.dsp [new file with mode: 0644]
win32/libsilcclient/Makefile.am [moved from lib/silcsim/modules/Makefile.am with 90% similarity]
win32/libsilcclient/libsilcclient.def [new file with mode: 0644]
win32/libsilcclient/libsilcclient.dsp [new file with mode: 0644]
win32/silc.dsw [new file with mode: 0644]
win32/silcdefs.h [new file with mode: 0644]
win32/tests/testi2.cpp [new file with mode: 0644]
win32/tests/testi2.h [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..204db8f
--- /dev/null
@@ -0,0 +1,19 @@
+Makefile
+Makefile.in
+Makefile.am
+Makefile.defines.in
+Makefile.defines_int.in
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+configure.in
+libtool
+libtool-shared
+ltconfig
+stamp-h
+stamp-h.in
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..f3617b1
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,7018 @@
+Wed Feb 13 12:46:25 CET 2002  Johnny Mnemonic <johnny@themnemonic.org>
+
+       * Merged the new SILC Config library, with the server parsing
+         support.  Read the header file silcconfig.h or the toolkit
+         documentation for the news.  Affected files are
+         doc/example_silcd.conf.in lib/silcutil/silcconfig.[ch]
+         silcd/command.c silcd/packet_receive.c silcd/packet_send.c
+         silcd/protocol.c silcd/server.c silcd/server_backup.c
+         silcd/serverconfig.[ch] silcd/silcd.c.
+
+       * Fixed some silclog documentation.  Affected file is
+         lib/silcutil/silclog.h.
+
+Sun Feb 10 18:11:30 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * The silc_cipher_register, silc_hash_register and
+         silc_hmac_register now checks if the object to be registered
+         is registered already.  Affected files are
+         lib/silccrypt/silccipher.c, silchash.c and silchmac.c.
+
+Sun Feb 10 15:48:38 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Merged new irssi from irssi.org's CVS, the version 0.7.99.
+
+Sat Feb  9 14:54:33 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Allow zero length channel messages inside the Channel Message
+         Payload.  Affected file lib/silccore/silcchannel.c.
+
+       * Fixed scripts/silcdoc/silcdoc to support all kinds of filenames
+         as header filenames.
+
+       * Removed lib/silcclient/README and created HTML file
+         lib/silcclient/silcclient_using.html, which is now included
+         as part of Toolkit documentation.
+
+Thu Feb  7 10:12:25 CET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed CUMODE_CHANGE notify handling to change the mode of
+         correct client.  Affected file lib/silcclient/client_notify.c.
+
+       * Make silc_rng_alloc fail if it cannot allocate the sha1
+         hash algorithm.  Affected file lib/silccrypt/silcrng.c.
+
+Sun Feb  3 17:20:52 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the file transfer's key agreement payload to include
+         zero port also if the hostname is NULL because it could not
+         be bound.  
+
+         Call file transfer monitor callback now also if error occurs
+         during key agreement protocol.
+
+         Changed the silc_client_file_send interface to return the
+         SilcClientFileError instead of session id.  The session ID
+         is returned into pointer provided as argument.
+
+         Check that the file exists locally before sending the
+         file transfer request at all.
+
+         Affected file lib/silcclient/client_ftp.c, silcapi.h.
+
+       * Added SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED file transfer
+         error than can occur while key agreement protocol.  Affected
+         file lib/silcclient/silcapi.h.
+
+       * Fixed the event_mode CMODE handler to not crash when mode
+         is changed and +k mode is set in the channel.  Affected file
+         irssi/src/silc/core/silc-channels.c.
+
+       * Fixed SILC_LOG_ERROR to give out Error and not Warning, and
+         SILC_LOG_WARNING to give out Warning and not Error.  Affected
+         file lib/silcutil/silclog.c.
+
+       * Fixed the channel message payload decryption in the function
+         silc_channel_message_payload_decrypt to not modify the original
+         buffer before it is verified that the message decrypted
+         correctly.  Otherwise, next time it is called with correct
+         channel key it won't encrypt since the payload is corrupted.
+         Affected file lib/silccore/silcchannel.c.
+
+Sun Feb  3 11:46:12 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not constantly resize the window.  A fix patch by cras.
+         Affected file irssi/src/fe-text/screen.c.
+
+Sat Feb  2 16:54:18 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Applied IPv6 fix patch from Jun-ichiro itojun Hagino.
+         Affected file lib/silcutil/silcnet.c.
+
+Fri Feb  1 22:33:11 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a bug in hash table internal routine for traversing
+         the table with foreach callback.  The current entry may
+         become invalid in the callback but it was referenced after
+         the callback returned.
+
+         Do not allow auto rehashing of hash table during the
+         silc_hash_table_foreach operation, for same reasons as it is
+         not allowed for SilcHashTableList.  Affected files are
+         lib/silcutil/silchashtable.[ch].
+
+Fri Feb  1 14:55:00 CET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Defined DLLAPI into silcincludes.h and silcwin32.h for
+         Win32 DLL.  extern's in header files are now declared with
+         DLLAPI.
+
+Thu Jan 31 23:34:33 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed private message handling.  It used some old code that
+         caused the client to crash.  Affecte file is
+         lib/silcclient/client_prvmsg.c.
+
+Thu Jan 31 19:06:22 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added function silc_client_add_channel, 
+         silc_client_replace_channel_id, and removed functions
+         silc_client_new_channel_id and silc_idlist_get_channel_by_id
+         from client library.
+
+       * Added cross reference of the joined channels to the 
+         SilcClientEntry, and changed the SilcChannelEntry's
+         users list to SilcHashTable.  The affected files are
+         lib/silcclient/idlist.[ch].
+
+       * Fixed a bug in hash table tarversing.  While the hash table
+         is traversed with SilcHashTableList the table must not be
+         rehashed.  It is now guaranteed that auto rehashable tables
+         are not rehashed while tarversing the list.  Also defined that
+         silc_hash_table_rehash must not be called while tarversing
+         the table.  Added function silc_hash_table_list_reset that must
+         be called after the tarversing is over.  The affected files are
+         lib/silcutil/silchashtable.[ch].
+
+       * Changed all hash table traversing to call the new
+         silc_hash_table_list_reset in server and in client library.
+
+       * Added function silc_client_on_channel to return the 
+         SilcChannelUser entry if the specified client entry is joined
+         on the specified channel.  This is exported to application as
+         well.  Affected files lib/silcclient/client_channel.c, silcapi.h.
+
+Wed Jan 30 19:14:31 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed founder regaining problem with JOIN command on normal
+         server.  The notify for mode change must be sent always and
+         not only if !cmd->pending.  Affected file silcd/command.c.
+
+       * Fixed the WHOWAS command's reply sending to support the
+         lists correctly.  Affected file silcd/command.c.
+
+Wed Jan 30 11:11:47 CET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * When sending JOIN command to router for processing the
+         sender's old command identifier was not saved back to the
+         sender's command context, fixed now.  The affected file is
+         silcd/command.c.
+
+       * Create the key in JOIN command of the router did not return
+         the channel key, added check for this.  Affected file is
+         silcd/command.c.
+
+       * Fixed a channel ID update bug in JOIN command reply.  Do
+         not directly upgrade the ID but call the function
+         silc_idlist_replace_channel_id if the ID was changed.
+         Affected file silcd/command_reply.c.
+
+       * Fixed memory leaks from command calling if it would fail.
+         Affected file silcd/command.c.
+
+Tue Jan 29 19:49:31 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Applied patches from cras:
+
+         Memory leak fixes around libaries, irssi window resize fix,
+         new silclist.h and silcdlist.h, all extern inline changed to
+         static inline.
+
+       * Removed dotconf from lib/dotconf, not needed anymore.
+
+       * Removed TRQ from lib/trq, not needed anymore.
+
+       * Do more frequent heartbeats (5 minutes instead of 10 minutes)
+         with server connections.  Later this will be configurable
+         in config file after new config file is done.  Affected file
+         silcd/server.c.
+
+Tue Jan 29 10:35:03 CET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a crash in server related to channel announcements.
+         Affected file silcd/server.c.
+
+Mon Jan 28 17:49:42 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed memory leaks in silc_server_create_new_channel*
+         functions.  Affected file silcd/server.c.
+
+       * Fixed the CHANNEL_CHANGE notify to re-announce the channel
+         which ID was changed.  This way the router will send the
+         user list for the channel again, and server won't be in 
+         desync in some rare circumstances.  Affected file is
+         silcd/packet_receive.c.
+
+Sun Jan 27 21:04:19 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Check for NULL socket pointer in the function
+         silc_server_packet_send_to_channel_real.  Affected file
+         silcd/packet_send.c.
+
+       * Fixed the BAN notify handling to correctly remove ban
+         list.  Affected file silcd/packet_receive.c.
+
+Sat Jan 26 23:01:03 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed some header addition to Toolkit distribution in
+         lib/silcutil/Makefile.am and lib/trq/Makefile.am.
+
+       * Added lib/silcclient/client_ops_example.h as an template
+         file for application programmers to quickly start using
+         the SilcClientOperation functions in their application.
+         Updated the lib/silcclient/README as well to tell about this
+         nice file made available.
+
+Sat Jan 26 10:45:41 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Call silc_server_remove_from_channels when removing client
+         entry when NO_SUCH_CLIENT_ID was received.  Affected file
+         is silcd/command_reply.c.
+
+Fri Jan 25 19:12:36 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added server & router operator statistics updating.  Affected
+         file silcd/packet_receive.c and silcd/command.c.
+
+       * Fixed the SERVER_SIGNOFF notify handling on normal server
+         not to save the history information for clients.  Same was
+         fixed earlier in remove_clients_by_server function, but not
+         here.  Affected file silcd/packet_receive.c.
+
+       * Raised the default connection-retry count from 4 to 7 in
+         server.  Affected file silcd/server.h.
+
+       * Cancel any possible reconnect timeouts when we start the
+         key exchange.  Affected file silcd/server.c.
+
+       * Do not reconnect on connection failure when SCONNECT was
+         given.  Affected files silcd/server.[ch].
+
+Tue Jan 22 18:19:36 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Removed assert()'s from the lib/silcclient/client_keyagr.c.
+
+       * Fixed the NICK command to always give the unformatted
+         nickname to the one giving the NICK command.  If unformatted
+         nickname is cached already it will be formatted and the
+         local entry will always get the unformatted nickname.
+         Affected file lib/silcclient/idlist.c.
+
+       * Fixed some double frees from client library commands.
+         Affected file is lib/silcclient/command.c.
+
+       * Fixed CUMODE command in server to assure that no one can
+         change founder's mode than the founder itself, there was a
+         little bug.  Affected file silcd/command.c.
+
+Mon Jan 21 19:07:53 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Removed the SilcClientCommandDestructor from the client
+         libary, it is not needed anymore.  Affected files are
+         lib/silcclient/silcapi.h, command[_reply].[ch],
+         client_notify, idlist.c.
+
+       * Fixed GETKEY command to first resolve client, and then
+         resolve the server only if the client was not found, instead
+         of resolving both at the same time.  Affected file is
+         lib/silcclient/command.c.
+
+       * Added silc_client_start_key_exchange_cb and lookup the
+         remote hostname and IP address before starting the key
+         exchange with server.  The affected file is 
+         lib/silcclient/client.c.
+
+       * The server's public key is now saved using the IP address
+         of the server and not the servername for the filename.
+         The hostname public key filename is checked as an fall back
+         method if the IP address based filename is not found.
+
+         Fixed the GETKEY command to save the fetched server key
+         in correct filename.
+
+         Print the remote server's hostname now when new key is
+         received during connection process.  Affected file is
+         irssi/src/silc/core/client_ops.c.
+
+       * Return always our own public key to the client if it asks
+         for it with GETKEY command.  Affected file silcd/command.c.
+
+       * Removed the use_auto_addr variable from default config
+         file since it was in wrong section.  Affected file is
+         irssi/src/config.
+
+       * Fixed TOPIC_CHANGE notification to not route it when it
+         was sent using silc_server_send_notify_to_channel function.
+         Affected file silcd/command.c.
+
+       * Fixed silc_server_send_notify_kicked to send the kicker's
+         Client ID also, it was missing.  Affected files are
+         silcd/command.c, silcd/packet_send.[ch].
+
+Thu Jan 17 18:59:11 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not save client history information in SERVER_SIGNOFF.
+         Fixes the bug in normal server that it does not detect
+         the client becoming valid after the server becomes back
+         online.  Affected file silcd/server_util.c.
+
+       * Added `sock_error' field  into the SilcSocketConnection
+         context.  When error occurs during socket operation (read
+         or write) the error is saved.  Added also new function
+         silc_socket_get_error to return human readable socket error
+         message.  Affected files are lib/silcutil/silcsockconn.[ch], 
+         lib/silcutil/unix/silcunixsockconn.c, and
+         lib/silcutil/win32/silcwin32sockconn.c.
+
+       * The server now prints the socket error message in the
+         signoff for client.  Affected file silcd/server.c.
+
+       * Fixed the `created' channel information sending from router
+         to server in JOIN command.  Checks now whether the channel
+         really was created or not and set it according that. 
+
+         Fixed the JOIN command to use the client entry's current
+         ID during the joining procedure instead of the one it sent
+         in the command (it is checked though), since it can change
+         between the packet processing and command processing, and 
+         would just case unnecessary pain in the client end.  Affected
+         file silcd/command.c.
+
+       * Fixed a channel key payload sending to use correct channel
+         ID when the server was forced to change the channel's ID by
+         router.  Router sent the key payload with the old Channel ID.
+         Affected file silcd/packet_receive.c.
+
+Wed Jan 16 22:26:30 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Call silc_server_save_channel_key only if the key payload
+         was provided in the JOIN command's command reply.  Affected
+         file silcd/command_reply.c.
+
+Tue Jan 15 18:49:41 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed silc_mp_sizeinbase to return the value correctly with
+         MPI.  Affected file lib/silcmath/mp_mpi.c.
+
+       * Fixed the stop_server signal to correctly stop the scheduler
+         and gracefully stop the server when SIGTERM or SIGINT signals
+         are received.  Affected file silcd/silcd.c.
+
+Mon Jan  7 23:38:19 CET 2002  Johnny Mnemonic <johnny@themnemonic.org>
+
+       * Simple handling of TERM and HUP signals. Also added some log
+         flushing call around.  Affected file is
+         silcd/silcd.c.
+
+       * Fixed small bugs in silclog.c. Now buffering output will take
+         effect after 10 seconds since startup: This will ensure that
+         no important startup messages are lost. Also output redirection
+         will preserve original format ([Date] [Type] message).
+         Affected file is lib/silcutil/silclog.c.
+
+       * Added two options to the config file, in the logging section:
+         quicklogs:<yes/no>: and flushdelay:<seconds>:.  Affected files
+         lib/silcutil/silclog.[ch], silcd/serverconfig.[ch].
+
+Sun Jan  6 12:49:40 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not print the warning about log files not being initialized
+         more than once to avoid excess logging.  Affected file is
+         lib/silcutil/silclog.c.
+
+       * Fixed the SIM compilation in lib/silcsim/Makefile.am.  Fixed
+         the SIM copying in make install in Makefile.am.pre.
+
+Sun Jan  6 01:10:21 CET 2001  Johnny Mnemonic <johnny@themnemonic.org>
+
+       * Rewritten silclog APIs. Globally interesting changes follows:
+         silc_log_set_files() changed to silc_log_set_file().
+         silc_log_set_callbacks() changed to silc_log_set_callback().
+         ROBOdoc documented silclog header file.
+         SilcLogCb now returns bool to wether inihibit the default
+         handler or not (to keep the old behaviour return always TRUE).
+         The new APIs should also fix the problem of the
+         silcd_error.log file that was written in the current directory.
+
+         New features:
+         Log files streams will remain opened after silc_log_set_file()
+         call, means less CPU usage notably on high traffic servers.
+         File streams are now full buffered, and flushed to the disk
+         every 5 minutes, lesses HD activity and CPU usage.
+         Messages can be redirected, allowing admins to configure
+         one single logfile for all server messages.
+         the silc_log_quick global variable to activate fast-logging.
+         Affected files lib/silcutil/silclog.[ch]
+
+       * Changed some code to conform new silclog APIs. Affected
+         files are doc/example_silcd.conf.in, silcd/server.c
+         irssi/src/silc/core/silc-core.c, silcd/serverconfig.[ch],
+         silcd/silcd.c.
+
+       * Fixed a memory leak that could occur in some situations.
+         Affected file silcd/serverconfig.c.
+
+Sat Jan  5 13:37:29 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added the silc_client_del_client to remove the client from
+         all channels as well.  Affected file lib/silcclient/idlist.c.
+
+       * Fixed the client library to correctly remove the client
+         from all channels when the client entry is being destroyed.
+         Affected file lib/silcclient/client_notify.c, command.c.
+
+       * Added auto-nicking support to the client library.  If the
+         applicatio now sets client->nickname it will be sent to the
+         server after connecting by the library.  This way for example
+         SILCNICK (or IRCNICK) environment variables will have effect
+         and always change the nickname automatically to whatever
+         it is wanted.  Affected file lib/silcclient/client.[ch].
+
+       * Renamed silc_server_command_bad_chars to the
+         silc_server_name_bad_chars and moved it to the
+         silcd/server_util.[ch].  Added also new function
+         silc_server_name_modify_bad to return nickname that
+         includes bad characters as new nickname without those
+         bad characters.  This check and modify is now used in
+         silc_server_new_client when the username is initially set
+         as nickname, so it must be checked to be valid nickname.
+         Affected file silcd/packet_receive.c.
+
+       * The nickname length is now taken from the packet for real
+         and not trusted to strlen() since it clearly can return
+         wrong length for nickname including bad characters.  This
+         also applies to channel names.  Affected file silcd/command.c.
+
+       * Removed the lib/silcsilm/modules directory.  Modules are now
+         compiled into the lib/silcsim.  Fixed the copying of the
+         modules to follow symbolic links in Makefile.am.pre.
+
+Wed Jan  2 18:56:21 EET 2002  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed silc_string_regexify list creation.  Fixes bugs with
+         BAN and INVITE commands in server.  The affected file is
+         lib/silcutil/unix/silcunixutil.c.
+
+Sun Dec 30 13:41:34 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Removed the command destructor entirely from the server's
+         command and command reply routines.  It is not needed, and
+         its usage was buggy and caused crashes.  Affected files are
+         silcd/command[_reply].[ch].
+
+Fri Dec 28 12:43:22 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Cancel protocol and NULL sock->protocol if timeout
+         occurred during protocol.  Affected file silcd/server.c.
+
+       * Cancel protocol timeouts always before calling the final
+         callback, to assure that after final callback is called
+         no other state will be called for the protocol anymore.
+         Affected file silcd/protocol.c.
+
+       * Print error log if incoming connection configuration could
+         not be found.  Affected file silcd/server.c.
+
+       * Fixed JOIN command to correctly save the founder mode
+         to the client on normal SILC server, when the channel
+         was created by the router.  Affected file silcd/command.c.
+
+       * Fixed LIST command (hopefully) to send correct reply
+         packets.  Affected file silcd/command.c.
+
+Thu Dec 20 16:14:52 CET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * The silc_packet_receive_process now returns FALSE if the
+         read data was invalid packet, and TRUE if it was ok.
+
+         The server now checks that if unauthenticated connection
+         sends data and its processing fails the server will close
+         the connection since it could be a malicious flooder. 
+
+         Affected files lib/silccore/silcpacket.[ch], silcd/server.c.
+
+Wed Dec 19 21:31:25 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Make sure the warning about error opening a log file is
+         printed only once and not everytime it fails (produces
+         too much useless log).  Affected file lib/silcutil/silclog.c.
+
+Wed Dec 19 18:21:51 CET 2001  Johnny Mnemonic <johnny@themnemonic.org>
+       * Made the silc_server_daemonise() function more readable.
+         Affected file silcd/server.c.
+       * Pid file is now optional, the user may comment it out from
+         the config file. Removed define SILC_SERVER_PID_FILE, we
+         don't need a default any longer.  Affected file
+         configure.in.pre, lib/Makefile.am.pre.
+       * Make some use of the pid file. The server now dies at startup
+         if it detects a valid pid file on his path. The server would
+         die anyway in this circumstance, because of the bind() failure.
+         Affected file silcd/silcd.c.
+       * No longer compiling lib/dotconf.
+
+Mon Dec 17 18:24:27 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed JOIN command parsing not to crash.  Affected file
+         lib/silcclient/command.c.
+
+       * Fied the NICK_CHANGE notify to add the new client entry
+         even it is resolved.  This removes an <[unknown]> nick
+         thingy bug in the client.  Affected file is 
+         lib/silcclient/client_notify.c.
+
+       * Do not try to allocate 0 bytes (efence does not like it)
+         in lib/silccore/silccomand.c when encoding payload.
+
+       * Do not take IRCNICK as nickname in Irssi SILC client since
+         it is not possible to set nickname before hand connecting
+         the server (TODO has an entry about adding auto-nicking
+         support).
+
+       * Changed the silc_server_command_pending to check whether
+         there already exists an pending entry with the specified
+         command, command identifier and pending callback.  This is
+         to fix IDENTIFY and WHOIS related crashes that may register
+         multiple pending commands with same identifier.  Affected
+         file silcd/command.c.
+
+       * Fixed the server to reconnect to the router even if it
+         was already reconnecting and EOF was received.  This to
+         fix a possibility that the server wouldn't ever try to
+         auto-reconnect to the router.  Affected file silcd/server.c.
+
+Sat Dec 15 20:31:50 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the server's password authentication to use the
+         length of the locally saved password, and not the one
+         sent in the packet.  Affected file silcd/protocol.c.
+
+       * Fixed same password authentication problem in the
+         Authentication Payload handling routines in
+         lib/silccore/silcauth.c.
+
+       * Yet another password authentication problem fixed with
+         channel password handling in silcd/command.c.
+
+Mon Dec 10 19:57:40 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * If first character of string in silc_parse_userfqdn is '@'
+         then do not parse it.  Affected file is
+         lib/silcutil/silcutil.c.
+
+Sun Dec  9 22:18:50 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed minor bug in IDENTIFY command reply sending, which
+         caused various weird problems during JOIN when it was
+         resolving names for users.  Affected file silcd/command.c.
+
+Sun Dec  9 19:18:41 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the IDENTIFY command reply sending to chech better valid
+         clients.  It was possible to send incomplete list of replies.
+         Affected file silcd/command.c.
+
+Sat Dec  8 15:58:31 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_client_command[s]_[un]register functions now to
+         dynamically register the commands in client library.  Removed
+         the static table of commands.  This allows the client library
+         to call commands without causing the application to know about
+         what commands library has called.
+
+         Removed the INFO command reply kludge to detect when the command
+         was called by library.  Now library use its own command reply
+         function for INFO command.
+
+         Added function silc_client_command_call to call a command.
+         Application can use it to call command, not access the structure
+         directly.
+
+         Now all commands that are sent by the client library (not
+         explicitly sent by application) use own command reply functions.
+
+         Affected files around lib/silcclient/ and in
+         irssi/src/silc/core/.
+
+       * Fixed the WHOIS command reply sending to chech better valid
+         clients.  It was possible to send incomplete list of replies.
+
+         Fixed the WHOIS and IDENTIFY to send the request to router
+         if normal server did not do it and did not find any results.
+
+         Affected file silcd/command.c.
+
+Thu Dec  6 17:21:06 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Moved the internal data from SilcClient context into its
+         own file, not accesible to application.  Affected files
+         lib/silcclient/client.h and lib/silcclient/client_internal.h,
+         and other files in client library.
+
+Thu Dec  6 10:37:55 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added doc/examples installation target in Makefile.am.pre.
+         A patch by salo.
+
+Tue Dec  4 17:43:19 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * If NO_SUCH_CLIENT_ID notify is received for WHOIS or IDENTIFY
+         commands the found client entry will be removed from the
+         cache, after notifying application about the error.  Affected
+         file lib/silcclient/command_reply.c.
+
+       * Changed the /MSG to check for exact nickname user gave, and
+         not let `nick' match `nick@host' if it is only one found.  Now,
+         user must type the exact nickname (like nick@host2) even if
+         there are no more than one same nicks found.  This is to avoid
+         a possibility of sending nickname to wrong nickname since
+         `nick' could match `nick@host'.  Affected file is
+         irssi/src/core/silc-servers.c.
+
+Mon Dec  3 18:49:45 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not print "you are now server operator" or similar when
+         giving /away command.  Affected files are
+         irssi/src/silc/core/client_ops.c, silc-servers.h.
+
+       * Made the silc_server_command_pending_error_check to send
+         the same command reply payload it received back to the
+         original sender of the command.  This way all arguments
+         that was received by the server will be received by the
+         client too.  Affected file silcd/command.c.
+
+       * Added the silc_idcache_add to return the created cache entry
+         to a pointer.  Affected file lib/silccore/silcidcache.[ch].
+
+       * Add global clients to expire if they are not on any channel.
+         This is because normal server will never know if they signoff
+         if they are not on any channel.  The cache expiry will take
+         case of these entries.  This is done by normal servers only.
+         The affected files are silcd/command_reply.c,
+         silcd/idlist.[ch], silcd/server and silcd/packet_receive.c.
+
+       * If server receives invalid ID notification for WHOIS or
+         IDENTIFY and the ID exists in the lists, it is removed.
+         Affected file silcd/command_reply.c.
+
+       * If NO_SUCH_CLIENT_ID is received for WHOIS or IDENTIFY command
+         in client then client entry that it matches is searched and
+         the nickname is printed on the screen for user.  Affected
+         file irssi/src/silc/core/client_ops.c.
+
+Mon Dec  3 11:56:59 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Use cache entry expire time in the LIST command reply to
+         purge old entries from the cache after the LIST command
+         reply has been received.  This way we don't have non-existent
+         entries in the cache for too long.  Affected file is
+         silcd/command_reply.c.
+
+Sun Dec  2 23:29:07 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * If we are normal server, and we've not resolved client info
+         in WHOIS or IDENTIFY from router, and it is global client,
+         we'll check whether it is on some channel.  If it is not
+         then we cannot be sure about its validity and will resolve it
+         from router.  Fixes a bug in WHOIS and IDENTIFY.  Affected 
+         file silcd/command.c.
+
+       * Search channel by name (if possible) rather than by ID
+         in IDENTIFY command's command reply.  Affected file is
+         silcd/command_reply.c.
+
+Sun Dec  2 13:48:46 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Distribute to the channel passphrase in CMODE_CHANGE notify.
+         Updated specs and implemented it.  Affected file silcd/command.c,
+         silcd/packet_send.c and silcd/packet_receive.c.
+
+       * Implemented the <founder auth> payload handling in the JOIN
+         command.  If provided all conditions for channel joining
+         except requirement to provide correct passphrase can be 
+         overrided by the channel founder.  Updated the protocol specs.
+         Affected file silcd/command.c.
+
+         Added support for founder auth in JOIN command in client
+         library.  Fixed the parsing of the JOIN command now to support
+         all options as they should be.  The affected file is
+         lib/silcclient/command.c.
+
+       * Optimized the WHOIS and IDENTIFY commands to send the request
+         to router only if it includes nicknames or other names.  If
+         they include only IDs then check the local cache first before
+         routing.  Affected file is silcd/command.c.
+
+       * Added channels topic announcements.  Affected file is
+         silcd/packet_receive.c and silcd/server.c.
+
+       * Fixed the silc_server_send_notify_topic_set to really destine
+         the packet to channel.  Affected file silcd/packet_send.c.
+
+       * Fixed a crash in CHANNEL_CHANGE notify handling in the client
+         library.  Affected file lib/silcclient/client_notify.c.
+
+       * Added UMODE announcements.  Affected file silcd/server.c.
+
+Sat Dec  1 12:52:39 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Memory leak fixes in:
+
+         lib/silcutil/silcsockconn.c
+         lib/silcske/silcske.c
+         lib/silcske/groups.c
+         lib/silccrypt/rsa.c
+         lib/silccrypt/silcpkcs.c
+         lib/silccore/silccommand.c
+         lib/silccore/silcidcache.c
+         silcd/idlist.c
+         silcd/packet_send.c
+         silcd/command.c
+
+       * ROBOdoc documented the lib/silcske/groups.h file and a
+         bit changed the interface for better.
+
+Thu Nov 29 22:12:50 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
+
+       * Update the client entry context in the ID cache after
+         nick change.  Affected file lib/silcclient/command.c.
+         Fixes the CUMODE command when regaining founder privileges,
+         and a little WHOIS problem.
+
+       * Fixed silc_net_gethostbyname to correctly call the
+         inet_ntop.  Affected file lib/silcutil/silcnet.c.
+
+Thu Nov 29 19:31:23 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
+
+       * Added IPv6 support checking to the configure.in.pre, added
+         also --enable-ipv6 option to override the check.  Affected
+         file configure.in.pre.
+
+       * The silc_thread_create now calls the start function
+         directly if threads support is not compiled in.  Removes
+         ugly #ifdef's from generic code.  Affected files are
+         lib/silcutil/unix/silcunixthread, win32/silcwin32thread.c.
+
+       * Added silc_net_gethostby[name/addr]_async to asynchronously
+         resolve.  Affected files are lib/silcutil/silcnet.[ch].
+
+       * Added support for rendering IPv6 based server, client and
+         channel IDs.  Affected file lib/silcutil/silcutil.c.
+
+       * Added support for creating IPv6 based server IDs.  Affected
+         file is silcd/serverid.c.
+
+Wed Nov 28 23:46:09 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
+
+       * Added silc_net_gethostby[addr/name] into the
+         lib/silcutil/silcnet.[ch].  Added IPv6 support to Unix network
+         routines.  Added silc_net_is_ip[4/6].  Affected file is
+         lib/silcutil/unix/silcunixnet.c.  All routines that take
+         address as argument now supports both IPv4 and IPv6 addresses.
+
+Mon Nov 26 18:09:48 EET 2001  Pekka Riikonen <priikone@silcnet.org>'
+
+       * Fixed LIST command reply sending in server.  Affected file
+         silcd/command.c.
+
+       * Server now sends the kicker's client ID in the KICK notify
+         to the kicked client.  Affected file silcd/command.c.
+
+       * The client library now parses the kickers client ID and
+         UI displays it.  Affected files lib/silcclient/client_notify.c
+         and irssi/src/silc/core/silc-channels.c, module-formats.c.
+
+       * Made all payload parsing function prototypes consistent.
+         They all take now const unsigned char * and uint32 pair as
+         the payload data instead of SilcBuffer.  Changes all around
+         the source tree.  Other unsigned char* -> const unsigned char*
+         changes around the tree as well.
+
+       * Optimized SFTP client and server packet sending not to
+         allocate new buffer for each packet but to recycle the
+         first allocated buffer.  Affected files are
+         lib/silcsftp/sftp_client.c, sftp_server.c, sftp_util.[ch].
+
+       * Optimized the SFTP client to use SilcList instead of
+         SilcDList for requests, because it is faster.  Affected file
+         is lib/silcsftp/sftp_client.c.
+
+       * Moved the ID Payload routines from lib/silccore/silcpayload.[ch]
+         into lib/silccore/silcid.[ch].
+
+         Renamed silcpayload.[ch] into silcargument.[ch].
+
+Mon Nov 26 15:01:53 CET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * If client entry is deleted with active key agreement
+         session, abort the session.
+
+         The silc_client_abort_key_agreement now calls the completion
+         callback with new SILC_KEY_AGREEMENT_ABORTED status.
+
+         Affected file lib/silcclient/silcapi.h, client_keyagr.c and
+         idlist.c.
+
+Sun Nov 25 18:01:45 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Don't use __restrict in older GCC's.  Affected file is
+         lib/silcmath/mpi/mpi-priv.h.  A patch by salo.
+
+       * silc_net_localhost now attempts to reverse lookup the
+         IP/hostname.  Affected file lib/silcutil/silcnet.c.
+
+       * Defined <founder auth> argument to the SILC_COMMAND_JOIN
+         command.  It can be used to gain founder privileges at 
+         the same time when joining the channel.
+
+         Defined that the SILC_NOTIFY_TYPE_KICKED send the 
+         kicker's client ID as well.  Updated protocol specs.
+
+         Defined that the server must send SILC_COMMAND_IDENTIFY
+         command reply with error status to client who sent
+         private message with invalid client ID.
+
+         Updated the protocol specification.
+
+       * Added silc_server_send_command_reply to send any
+         command reply.  Affected file silcd/packet_send.[ch].
+
+       * Added silc_id_payload_encode_data to encode ID payload
+         from raw ID data.  Affected file lib/silccore/silcpayload.[ch].
+
+       * The server now send IDENTIFY command reply with error
+         status if client ID in private message is invalid.  Affected
+         file silcd/packet_receive.c.
+
+       * Save the server key file with server's IP address in
+         the filename instead of hostname.  The affected file is
+         irssi/src/silc/core/client_ops.c.
+
+Sat Nov 24 20:08:22 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Typo fixes in irssi/src/fe-common/silc/module-formats.c.
+         A patch by Sunfall.
+
+       * Added libtool support for compiling shared objects in
+         lib/silcsim.  Affected file configure.in.pre and
+         lib/silcsim/Makefile.am.  Original patch by cras.
+
+Fri Nov 23 23:30:59 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Pid file configuration, and server's config file fixes
+         patch by toma.  Updated CREDITS file. 
+
+Sun Nov 18 01:34:41 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed silc_client_channel_message to not try to decrypt
+         the message twice if it resolved the destination client
+         information.  This could cause of dropping one channel
+         message.  Affected file lib/silcclient/client_channel.c.
+
+Wed Nov 14 23:44:56 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_client_run_one into lib/silcclient/silcapi.h and
+         lib/silcclient/client.c. This function is used when the SILC
+         Client is run under some other scheduler, or event loop or
+         main loop.  On GUI applications, for example this may be
+         desired to used to run the client under the GUI application's
+         main loop.  Typically the GUI application would register an
+         idle task that calls this function multiple times in a second
+         to quickly process the SILC specific data.
+
+Wed Nov 14 19:16:52 CET 2001  Johnny Mnemonic <johnny@themnemonic.org>
+
+        * Fixed silc_server_drop() for dropping the supplementary
+          groups as well, this could cause a security hole on some
+          systems.
+
+Wed Nov 14 16:22:25 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * __pid_t -> pid_t in lib/silccrypt/silcrng.c.  A patch by
+         johnny.
+
+       * Write PID file after dropping privileges.  Added -F option
+         to run server on foreground.  A patch by debolaz.
+         Affected files silcd/server.c, silcd/silcd.c.
+
+       * Fixed MOTD to return the MOTD file server name.  Affected
+         file silcd/command.c.
+
+       * Added INFO command reply handling to the Irssi SILC Client.
+         Affected file irssi/src/silc/core/client_ops.c.
+
+Wed Nov 14 00:18:08 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the silc_idcache_list_* routines to really support
+         the dynamic list.  Fixes a crash.  Affected file is
+         lib/silccore/silcidcache.c.
+
+       * Fixed the LIST command reply to really call LIST command's
+         pending callbacks.  Affected file silcd/command_reply.c.
+
+Tue Nov 13 00:49:17 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Update conn->local_entry->nickname after giving NICK
+         command.  Affected file lib/silcclient/command.c.
+
+Sun Nov 11 23:43:02 PST 2001  Brian Costello <bc@wpfr.org>
+
+       * Added the [pid] option to the silcd configuration file
+
+         Affected files: serverconfig.[ch] and silcd.c
+
+Sun Nov 11 23:56:39 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Save fingerprint in WHOIS command reply in server.
+         Affected file silcd/command_reply.c.
+
+       * Fixed NICK commands pending callback registration.
+         Affected file lib/silcclient/command.c.
+
+Sun Nov 11 10:49:10 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Use ++server->cmd_ident when sending commands in server,
+         instead of random number.  Affected file silcd/command.c.
+
+       * Fixed GETKEY command reply to call actually GETKEY pending
+         command callbacks.  Affected file silcd/command_reply.c.
+
+       * A bit stricter check for nicknames.  Check for same nickname
+         in NICK command also.  Affected file silcd/command.c.
+
+       * Do not call INFO command everytime client ID changes, only
+         during first connecting.  Affected file lib/silcclient/client.c.
+
+       * Set the new nickname only after successful command reply for
+         NICK command is returned by server.  Affected file
+         lib/silcclient/command.c.
+
+       * Remove nicknames from nicklist during server_signoff notify.
+         Should fix /NAMES bit more.  The affected file is
+         irssi/src/silc/core/silc-channels.c.
+
+       * Added `fingerprint' field to the SilcIDListData in the 
+         silcd/idlist.h to hold the fingerprint of the client's
+         public key.
+
+         Send the fingerprint of the client's public key in WHOIS
+         command reply.
+
+         Affected files silcd/command.c, and silcd/idlist.[ch].
+
+       * Added silc_fingerprint into lib/silcutil/silcutil.[ch] to
+         create fingerprint from given data.
+
+       * Show the fingerprint of the client's public key in WHOIS.
+         Affected files irssi/src/module-formats.[ch] and
+         irssi/src/silc/core/client_ops.c.
+
+       * Format the multiple same nicknames also during JOIN and
+         NICK_CHANGE notifys.  Affected file is
+         lib/silcclient/client_notify.c.
+
+       * Do not print error on screen for invalid private message
+         payload since it can come if someone is sending private
+         messages with wrong key.  Affected file
+         lib/silccore/silcprivate.c.
+
+       * Fixed multiple concurrent /PING crash.  Affected file
+         lib/silcclient/command.c.
+
+       * Changed the wrong ID encoding.  All IP addresses must be
+         in MSB first order in encoded format.  They were encoded
+         wrong and was in LSB format.  Affected files are
+         silcd/serverid.c, lib/silcutil/silcutil.c.
+
+       * Remove silc_net_addr2bin_ne from lib/silcutil/silcnet.[ch].
+
+       * Call the `connect' client operation through the scheduler
+         in case of error.  Affected file lib/silcclient/client.c.
+
+       * Call the `failure' client operation even if the error
+         occurred locally during a protocol.  Affected file is
+         lib/silcclient/protocol.c.
+
+       * Added support of sending LIST command to router from normal
+         server.  This way normal server can get list of all channels
+         in the network too.  Fixed the channel list sending in the
+         server too.  Affected files are silcd/command.c, and
+         silcd/command_reply.[ch].
+
+       * Added silc_server_update_channels_by_server and
+         silc_server_remove_channels_by_server.  They are used during
+         disconnection of primary router and in backup router protocol.
+         Affected file silcd/server_util.[ch], silcd/server.c and
+         silcd/server_backup.c.
+
+       * Fixed channel adding to global list in IDENTIFY command
+         reply in server.  Affected file silcd/command_reply.c.
+
+Sat Nov 10 21:39:22 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * If the incoming packet type is REKEY or REKEY_DONE process
+         that packet always synchronously.  Fixes yet another MAC
+         failed error on slow (dialup) connections.  Affected file
+         lib/silcclient/client.c and silcd/server.c.
+
+Thu Nov  8 22:21:09 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Call check_version SKE callback for initiator too.  Affected
+         file lib/silcske/silcske.c.
+
+       * Implemented fix for security hole found in the SKE that was
+         fixed in the specification few days back; the initiator's
+         public key is now added to the HASH value computation.
+         Added backwards support for the old way of doing it too, for
+         old clients and old servers.  Affected file is
+         lib/silcske/silcske.c.
+
+       * Enabled mutual authentication by default in SKE.  If initiator
+         is not providing mutual authentication the responder will
+         force it.  This will provide the proof of posession of the
+         private key for responder.  The affected files are
+         lib/silcclient/protocol.c and silcd/protocol.c.
+
+       * Do not cache anymore the server's public key during SKE.
+         We do mutual authentication so the proof of posession of
+         private key is done, and if the server is authenticated in
+         conn auth protocol with public key we must have the public
+         key already.  Affected file silcd/protocol.c.
+
+       * Added new global debug variable: silc_debug_hexdump.  If
+         it is set to TRUE SILC_LOG_HEXDUMP will be printed.  Affected
+         file lib/silcutil/silclog.[ch].
+
+       * Fixed compilation warning due to char * -> const char *.
+         Affected files lib/silcutil/silcnet.h, and
+         lib/silccore/silcauth.[ch].
+
+Wed Nov  7 20:43:03 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed CMODE command when new channel key was created.  If
+         the creation failed the old key was removed.  Next time giving
+         same command would crash the server since the old key was
+         freed already.  Affected file silcd/command.c.
+
+       * Fixed the silc_server_announce_get_channels to not crash
+         on reconnect.  Affected file silcd/server.c.
+
+Wed Nov  7 17:15:07 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_log_set_debug_string function to set a regex
+         string to match for debug output.  Only the function names,
+         or filenames matching the given debug string is actually
+         printed.  This way it is possible to filter out those debug
+         strings that user is not interested in.
+
+         Fixed a bug in silc_string_regexify.
+
+         Affected files lib/silcutil/silclog.[ch], and
+         lib/silcutil/unix/silcunixutil.c.
+
+       * Changed the -d options in both server and Irssi SILC client
+         to take the debug string as argument.  Affected files
+         silcd/silcd.c and irssi/src/silc/core/silc-core.c.
+
+Tue Nov  6 21:31:54 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_hash_babbleprint to create a Bubble Babble
+         Encoded fingerprint.  The encoding is developed by Antti
+         Huima (draft-huima-babble-01.txt), and it creates human
+         readable strings out of binary data.  Affected file
+         lib/silccrypt/silchash.[ch].
+
+       * Print the babble print now in addition of fingerprint as well
+         in Irssi SILC client.  Affected files are
+         irssi/src/fe-common/silc/module-formats.[ch],
+         irssi/src/fe-common/silc/core/client_ops.c.
+
+Sun Nov  4 23:37:28 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a security problem found in SKE.  The initiator's
+         public key too is now added to the HASH hash value creation
+         which is signed by the responder to create the SIGN value.
+         This will prevent anyone in the middle to lie to the responder
+         about the initiator's public key.  If this is done now, the
+         man in the middle will get caught.  Updated the protocol
+         specification.
+
+Sun Nov  4 11:43:53 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Better installation directory handling.  Configure module
+         paths and other paths automatically to example_silc* files
+         in doc/.  A patch by toma.
+
+       * Fixed compiler warning from MPI library, and from SILC RNG.
+         A patch by johnny.
+
+       * Added SILC_SERVER_PID_FILE to define the pid file for server.
+         It can be configured with ./configure.  A patch by toma.
+
+Sat Nov  3 23:48:23 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Find correct make to use in prepare-clean.  A patch by
+         toma.  Affected file prepare-clean.
+
+Sat Nov  3 22:04:00 PST 2001  Brian Costello <bc@mksecure.com>
+
+       * Added irssi variables use_auto_addr, auto_bind_ip,
+          auto_bind_port and auto_public_ip.
+
+       * Changed the interface for silc_client_send_key_agreement
+          in lib/silcclient/silcapi.h
+
+       Affected files:
+
+         irssi/src/silc/core/silc-core.c
+         irssi/config
+         lib/silcclient/silcapi.h
+         irssi/src/silc/core/silc-channels.c
+         lib/silcclient/client_keyagr.c
+         irssi/docs/help/key
+
+Sat Nov  3 17:48:55 EET 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_pkcs_public_key_compare to compare two 
+         public keys.  Affected file lib/silccrypt/silcpkcs.[ch].
+
+       * Check that the client who set the founder mode on the
+         channel is the same client that is giving the founder
+         mode to itself.  It is done by comparing the saved public
+         key (it is saved even in the authentication is passphrase).
+         Affected file silcd/command.c.
+
+Fri Nov  2 18:52:08 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not process packet for disconnected socket connection.
+         Affected file lib/silccore/silcpacket.c.
+
+       * Process the DISCONNECT packet through scheduler in the
+         client library.  Affected file lib/silcclient/client.c.
+
+       * Fixed the silc_client_packet_parse to not to increase
+         the packet sequence number if the conn->sock and the 
+         current socket connection is not same.  This can happen
+         for example during key agreement when the conn includes
+         multiple socket connections (listeners).  Affected file
+         lib/silcclient/client.c.
+
+       * The sender of the file transfer request now provides also
+         the pointer (listener) for the key exchange protocol.  If
+         the listener cannot be created then it sends empty key
+         agreement and lets the receiver provide the listener.
+
+         Added `local_ip' and `local_port' arguments to the
+         silc_client_file_send.  If they are provided they are used,
+         if not then it will attempt to find local IP address, if
+         not found or bind fails then the remote client will provide
+         the listener.
+
+         Affected files are lib/silcclient/client_ftp.c and
+         lib/silcclient/silcapi.h.
+
+       * Extended the FILE SEND command to support defining the
+         local IP and port for key exchange listener.  They are
+         optional.  Affected file irssi/src/silc/core/silc-servers.c.
+
+Thu Nov  1 22:10:07 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Defined to WHOIS command reply the sending of fingerprint
+         of the client's public key (if the proof of posession of the
+         corresponding private key is verified by the server).
+         Updated to the protocol specification.
+
+       * Added support of receiving the client's public key's 
+         fingerprint in command reply in client library.  Affected
+         file is lib/silcclient/command_reply.c, and
+         lib/silcclient/idlist.[ch].
+
+Thu Nov  1 18:06:12 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not send over 128 chars long nickname to the server
+         in NICK command.  Affected file lib/silcclient/command.c.
+
+       * Do not send over 256 chars long channel names to the server
+         in JOIN command.  Affected file lib/silcclient/command.c.
+
+Tue Oct 30 22:48:59 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Assure that silc_server_close_connection cannot be called
+         twice for same socket context.  Affected file is
+         silcd/server.c.
+
+Tue Oct 30 16:58:14 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Send error message to application if opening file for
+         writing during file transfer fails.  Affected file is
+         lib/silcclient/client_ftp.c.
+
+         Remove all file transfer sessions for a client that we're
+         removing from ID cache.
+
+         Affected file is lib/silcclient/client_ftp.c.
+
+       * Fixed silc_net_addr2bin to return correct address.  Affected
+         file lib/silcutil/[unix/win32]/silc[unix/win32]net.c.
+
+       * Fixed file transfer session removing on signoff notify.
+         Affected file irssi/src/silc/core/silc-servers.c.
+
+       * Added the SilcClientFileError to be returned in the monitor
+         callback.  Added NO_SUCH_FILE and PERMISSION_DENIED errors.
+         Affected file lib/silcclient/silcapi.h.
+
+Mon Oct 29 17:43:04 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a crash in silc_client_ftp_free_sessions and
+         silc_client_ftp_session_free_client.  Affected file
+         lib/silcclient/client_ftp.c.
+
+       * Added `disabled' field in the SilcChannelEntry in the server
+         to indicate if the server entry is disabled.  Affected file
+         silcd/idlist.h, silcd/command[_reply].c.
+
+       * SILC server adds now /var/run/silcd.pid everytime it is
+         started.  Affected file silcd/silcd.c.
+
+       * Added silc_server_packet_send_clients to send a packet to
+         the provided table of client entries.  Affected file
+         silcd/packet_send.[ch].
+
+       * Fixed a crash in client resolving in client_prvmsg.c in 
+         client library.  Affected file lib/silcclient/client_prvmsg.c.
+
+       * Do not actually remove the client directly from ID cache
+         during SERVER_SIGNOFF, but invalidate it.  This way we
+         preserve the WHOWAS info for the client.  Affected file
+         silcd/server_util.c.
+
+       * Fixed SERVER_SIGNOFF notify handling in the server.  The
+         server is now able to process incoming SERVER_SIGNOFF notify
+         for a server that it doesn't even know about.  It will remove
+         the clients provided in the notify.  Affected file
+         silcd/packet_receive.c.
+
+       * Check for partial packet in data queue after every packet that
+         was found from the queue.  Return and wait for more data if 
+         there is partial data in queue.  Affected file is
+         lib/silccore/silcpacket.c.
+
+Sun Oct 28 18:46:27 EST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added SilcClietFileError enum to indicate error in
+         file transfer.  Added SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT
+         and SILC_CLIENT_FILE_MONITOR_ERROR new monitor statuses.
+         Affected files lib/silcclient/silcapi.h and
+         lib/silcclient/client_ftp.c.
+
+       * Check that newsize in silc_buffer_realloc is larger than
+         the old buffer's size.  Affected file lib/silcutil/silcbufutil.h.
+
+       * Added better monitor of file transfers.  It now monitors
+         key agreement protocol during the file transfer too.  Added
+         error reporting too.  Affected files
+         irssi/src/silc/core/silc-servers.c,
+         irssi/src/fe-common/silc/module-formats.[ch].
+
+       * Wrote a help file for FILE command.
+
+       * Added silc_rng_global_get_byte_fast to get not-so-secure
+         random data as fast as possible.  Random data is read from
+         /dev/urandom if available and from the SILC RNG if not
+         available.  It is used in padding generation.  Affected file
+         lib/silccrypt/silcrng.[ch].
+
+       * All packets in client library are now processed synchronously.
+         Optimized packet processing a lot.  Affected file
+         lib/silcclient/client.c.
+
+       * All server connection packets are processing synchronously
+         now in server, to optimize packet processing.  Affected file
+         silcd/server.c.
+
+       * Include files are installed now only in Toolkit distribution
+         if make install is given.  Affected files: all Makefile.am's.
+
+Thu Oct 25 22:44:06 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Assure that silc_client_notify_by_server_resolve does not
+         resolve the client information multiple times.  If it cannot
+         be found by the first it cannot be found at all.  Affected
+         file lib/silcclient/client_notify.c.
+
+       * Fixed WHOWAS command reply calling.  Affected file
+         lib/silcclient/command_reply.c.
+
+       * Removed all references to silc_idlist_get_client from the
+         Irssi SILC client since that call is internal call used by
+         the library.  The Irssi SILC client will use now client
+         retrieval functions found in silcapi.h.
+
+       * Fixed a bug in resolving nickname info before sending
+         private message.  It used freed memory.  Affected file
+         irssi/src/silc/core/silc-servers.c.
+
+Thu Oct 25 19:04:49 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Assure my_channels statistics cannot go negative in server.
+         Affected files silcd/server.c, silcd/server_util.c.
+
+Wed Oct 24 19:53:05 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Upgraded dotconf 1.0.2 to 1.0.6 in lib/dotconf.
+
+Tue Oct 23 13:51:19 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Win32 Toolkit changes.  Affected files
+         win32/silc.dsw, win32/libsilc/libsilc.def,
+         win32/libsilcclient/libsilc.def,
+         lib/silcutil/silcutil.c, and
+         lib/sftp/sftp_fs_memory.c.
+
+Mon Oct 22 16:35:05 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_net_localip to return local host's IP address.
+         Affected file lib/silcutil/silcnet.[ch].
+
+       * If key exchange or rekey protocol is active for a connection
+         parse all packets syncronously since there might be packets
+         in packet queue that we are not able to process without first
+         processing packets before them.  Affected file silcd/server,
+         lib/silcclient/client.c.
+
+       * SilcPacketParserCallback now returns TRUE or FALSE to indicate
+         whether library should continue processing the packet. 
+         Affected file lib/silccore/silcpacket.h.
+
+       * Added SilcSFTPMonitor callback, SilcSFTPMonitors and
+         SilcSFTPMonitorData to SFTP server to monitor various
+         SFTP client requests.  Affected file lib/silcsftp/silcsftp.h,
+         lib/silcsftp/sftp_server.c.
+
+       * Added silc_file_size to return file size.  Affected file
+         lib/silcutil/silcutil.[ch].
+
+       * Implemented the file transfer support for the client library.
+         Added preliminary support for simple client to client one-file
+         transmission.  Affected file lib/silcclient/client_ftp.c,
+         lib/silccilent/client.[ch].
+
+       * Added new local command FILE to the Irssi SILC Client.
+         It is used to perform the file transfer.  It has subcommands
+         SEND, RECEIVE, SHOW and CLOSE.  Affected files
+         irssi/src/silc/core/client_ops.c, 
+         irssi/src/silc/core/silc-server.[ch].
+
+Mon Oct 22 12:50:08 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Relay the SILC_PACKET_FTP in the server.  Affected files
+         silcd/server.c and silcd/packet_receive.c.
+
+Sun Oct 21 20:21:02 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Renamed silc_file_read and silc_file_write to functions
+         silc_file_readfile and silc_file_writefile.  Added function
+         silc_file_open and silc_file_close.  Affected files 
+         lib/silcutil/silcutil.[ch].
+
+Thu Oct 18 20:58:13 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Resolve the client info when received private message or
+         channel message for a client which nickname we don't know.
+         Affected files lib/silcclient/client_prvmsg.c and
+         lib/silcclient/client_channel.c.
+
+       * Do not crash in /KEY if client is not connected.  Affected
+         file irssi/src/silc/core/silc-channels.c.
+
+       * Added SilcClientStatus field to the SilcClientEntry in the
+         lib/silcclient/idlist.h.
+
+         Added SILC_CLIENT_STATUS_RESOLVING to mark that the entry
+         is incomplete and is being resolved, it won't be resolved
+         twice.
+
+         Make sure also that USERS command reply does not resolve
+         twice information.  Affected file is
+         lib/silcclient/command_reply.c.
+
+         Make sure that silc_client_get_clients_by_list does not
+         resolve twice same information.
+
+       * Check for valid client->id in the silc_server_free_client_data.
+         Affected file silcd/server.c.
+
+       * Fixed /GETKEY nick@server not to crash if the server entry
+         is not found.  Affected file lib/silcclient/command.c.
+
+       * Fixed the silc_server_check_cmode_rights to check the
+         requested modes correctly.  Affected file silcd/command.c.
+
+Thu Oct 18 12:10:22 CEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Better checks for non-printable chars in nick added.
+         Affected file silcd/command.c.
+
+Thu Oct 18 09:18:58 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Call the silc_server_udpate_servers_by_server in the
+         primary router that comes back online in the backup resuming
+         protocol.  Otherwise it routes packets wrong.  Affected file
+         silcd/server_util.[ch], silcd/server_backup.c.
+
+Wed Oct 17 16:51:18 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added SILC_STR_UI8_[N]STRING[_ALLOC] formats to the
+         lib/silcutil/silcbuffmt.[ch].
+
+       * Redefined the SILC packet header to include the padding
+         length.  Affected file lib/silccore/silcpacket.[ch].
+
+       * Added SILC_PACKET_PADLEN_MAX macro to return the padding
+         length for maximum padding up to 128 bytes).  Affected
+         file lib/silccore/silcpacket.h.
+
+       * Removed all backwards support for old 0.5.x MAC thingies.
+         The SILC packet header change makes it impossible to be
+         backwards compatible.
+
+       * Send the ENDING packet with timeout in the backup resuming
+         protocol.  This is to assure that all routers has connected
+         to the primary router.  Affected file silcd/server_backup.c.
+
+       * Changed the RNG to take the first IV from random data.  It
+         used to take it from zero actually.  Changed the RNG also
+         to use /dev/urandom during session.  /dev/random is used
+         in initialization.  Affected file lib/silccrypt/silcrng.[ch].
+
+Tue Oct 16 20:45:49 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the SILC packet header to have the first two bytes
+         (the packet length) encrypted.  Affected files aroung the
+         code tree, lib/silccore/silcpacket.[ch].  Removed the
+         SilcPacketCheckDecrypt callback.  It is not needed anymore
+         since the silc_packet_receive_process will determine now
+         whether the packet is normal or special.
+
+       * Implemented the unidirectional MAC keys.  Affected files
+         lib/silcske/silcske.c, silcd/protocol.c and
+         lib/silcclient/protocol.c.
+
+       * Implemented the packet sequence number to the MAC computation.
+         Affected files lib/silccore/silcpacket.c, silcd/protocol.c,
+         silcd/packet_send.c, silcd/server.c, lib/silcclient/client.c,
+         lib/silcclient/protocol.c.
+
+Mon Oct 15 17:42:55 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Allow backup router to announce servers.  All servers
+         announced by backup router are added to the global list
+         automatically.  Update hte server's socket to our primary
+         router also when backup router announces a server.
+         Affected file silcd/packet_receive.c.
+
+       * Do not update the client->router in the function
+         silc_server_udpate_clients_by_server if the client is on
+         global list.  We might fail to find any specific server
+         for locally connected clients and local cell clients.  They
+         should still use the `from' and not `to' as client->router.
+         This fixes backup router resuming protocol.  Affected file
+         silcd/server_util.c.
+
+       * Decrease channel statistics count only if the channel
+         deletion worked.  Affected files are silcd/server.c and
+         silcd/server_util.c.
+
+       * Added silc_server_update_servers_by_server to update origin
+         of all server entries.  Used during backup router protocol.
+         Affected files silcd/server_util.[ch], silcd/server.c. and
+         silcd/backup_router.c.
+
+       * ROBODoc documented the lib/silccrypt/silchmac.h.  Added new
+         function silc_hmac_init, silc_hmac_update, silc_hmac_final,
+         silc_hmac_get_hash and silc_hmac_get_name.  Affected file
+         lib/silccrypt/silchmac.c.
+
+Sun Oct 14 18:28:22 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Assure that router cannot reroute the same channel message
+         to the sender.  Affected file silcd/packet_receive.c.
+
+Sat Oct 13 12:46:18 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Made better checks that the channel message is not sent
+         to the router it came from.  Affected file is
+         silcd/packet_send.c.  Fixed memory leak too.
+
+       * Announce informations for incoming router connection, but
+         only after checking if it is replaced by backup router.
+         Affected file silcd/packet_receive.c.
+
+Fri Oct 12 18:37:24 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the backup resuming protocol to work in multiple
+         router environment.  Affected file silcd/server_backup.c.
+
+       * Route packet only to one router in the function
+         silc_server_packet_send_to_channel.  Affected file is
+         silcd/packet_send.c.
+
+       * Fixed silc_server_send_notify_dest to set the broadcast
+         flag.  Fixed the silc_server_send_notify_topic to actually
+         send the TOPIC_CHANGE notify and not SERVER_SIGNOFF notify.
+         Affected file silcd/packet_send.c.
+
+       * Changed the SFTP Filesystem interface.  Changed the
+         SilcSFTPFilesystemStruct to SilcSFTPFilesystemOps to include
+         the filesystem operation function.  The SilcSFTPFilesystem
+         is now a context that is allocated by all filesystem allocation
+         functions and it already includes the operations structure
+         and filesystem specific context.  It is given as argument
+         now to the silc_sftp_server_start.  This made the interface
+         a bit cleaner.  Affected file lib/silcsftp/silcsftp[_fs].h,
+         lib/silcsftp/sftp_fs_memory.c and sftp_server.c.
+
+Thu Oct 11 22:19:26 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the backup router adding and getting interfaces
+         in the server.  The router that will be replaced by the
+         specified backup router is now sent as argument.  Affected
+         files silcd/serverconfig.[ch], silcd/backup_router.[ch], and
+         silcd/server.c.
+
+       * Added silc_net_addr2bin_ne to return the binary form of
+         the IP address in network byte order.  Affected files
+         lib/silcutil/[unix/win32].silc[unix/win32]net.[ch].
+
+Thu Oct 11 12:14:19 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Check for existing server ID in silc_server_new_server
+         and in silc_server_connect_to_router_final and remove the
+         old entry if it exists.  Affected file silcd/packet_receive.c,
+         silcd/server.c.
+
+       * Send the channel message always to only one router, either
+         in upstream or downstream.  Affected file is
+         silcd/packet_send.c.
+
+Tue Oct  9 17:45:43 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Wrote the definition of the backup resuming protocol to the
+         protocol specification.
+
+       * Removed one redundant channel key generation from normal
+         server during joining procedure.  Removed one redundant
+         channel key sending from server to router during joining
+         procedure.  Affected file silcd/command.c.
+
+       * Made minor bugfixes to the backup router resuming protocol.
+         Affected file silcd/server_backup.c, server.c.
+
+Mon Oct  8 16:47:42 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added --disable-asm configuration option.  Affected files
+         configure.in.pre, lib/silcmath/mpi/configure.in.  A patch
+         by salo.
+
+       * Implemented the backup resuming protocol that is used to
+         resume the primary router position in the cell after the
+         primary router comes back online.  Affected files
+         silcd/server_backup.[ch], silcd/server, silcd/packet_receive.c,
+         and silcd/server_util.[ch].
+
+Sun Oct  7 12:29:25 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Sleep two (2) seconds after sending QUIT command to server.
+         Affected file lib/silcclient/command.c.
+
+       * Assure that if outgoing data buffer is pending do not force
+         send any data.  Affected file silcd/packet_send.c.
+
+       * Assure that if outgoing data buffer is pending do not force
+         send any data.  Affected file lib/silcclient/client.c.
+
+       * Implemented the backup router support when the primary router
+         goes down.  The servers and routers can now use the backup
+         router as new primary router without loosing connectivity.
+
+Sat Oct  6 21:18:54 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added new SILC_IDLIST_STATUS_DISABLED flag for entries
+         in the server to indicate disabled entry.  All data read
+         from the connection will be ignored and no data is sent
+         for entry that is disabled.  Affected files are
+         silcd/idlist.h, silcd/server.c.
+
+Fri Oct  5 00:03:29 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Created SFTP client and server test programs in the
+         lib/silcsftp/tests directory.
+
+Wed Oct  3 23:31:42 EDT 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Implemented memory filesystem (virtual filesystem) for
+         SFTP server.  Affected file lib/silcsftp/silcsftp_fs.h,
+         sftp_fs_memory.c.
+
+Sun Sep 30 22:10:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Implemented the SFTP (SSH File Transfer Protocol) to the
+         lib/silcsftp.  It includes SFTP client and SFTP server
+         implementations.
+
+Sun Sep 30 10:35:44 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Moved lib/silccore/silcprotocol.[ch] to the
+         lib/silcutil library.
+
+       * Added silc_buffer_format_vp and silc_buffer_unformat_vp to
+         take variable argument list pointer as argument.  Affected
+         file lib/silcutil/silcbuffmt.[ch].
+
+       * Added silc_buffer_set function that is used to set data
+         to a SilcBuffer that is not allocated at all (SilcBufferStruct).
+         Affected file lib/silcutil/silcbuffer.h.
+
+       * Changed various routines in the core library to use the new
+         silc_buffer_set instead of allocating new buffer only for
+         temporary purposes.
+
+       * Added 64-bit value formatting and unformatting support to the
+         silc_buffer_[un]format routines.  Affected file is
+         lib/silcutil/silcbuffmt.[ch].
+
+         Added also 64-bit macros: SILC_GET64_MSB and SILC_PUT64_MSB,
+         to includes/bitmove.h.
+
+Fri Sep 28 21:30:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed channel user mode saving in client library.  Affected
+         file lib/silcclient/command[_reply].c.
+
+Thu Sep 27 22:52:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Defined the file transfer to the SILC Protocol.  Added
+         new packet type SILC_PACKET_FTP and defined File Transfer
+         Payload.  The mandatory file transfer protocol is SFTP
+         (SSH File Transfer Protocol).  Affected file in addition
+         of the internet draft is lib/silccore/silcpacket.h.
+
+       * Deprecated the SILC_PACKET_CELL_ROUTERS and defined new 
+         packet SILC_PACKET_RESUME_ROUTER instead.  The new packet
+         is used as part of backup router protocol when the primary
+         router of the cell is back online and wishes to resume
+         the position as primary router.
+
+       * Redefined the MAC generation keys in the protocol.  The
+         same key is not used anymore in both direction.  Both
+         direction will now use different keys for sending and
+         receiving.  This fixes a potential security flaw.  This
+         change causes incompatibilities in the protocol.
+
+       * Redefined also the MAC computation from the packet.
+         An packet sequence number is now added to the MAC 
+         computation.  This prevents possible replay attacks against
+         the protocol.  This change too causes incompatibilities
+         in the protocol.
+
+         Added `sequence' field to the SilcPacketContext to hold
+         the current sequence number for the packet.
+
+Wed Sep 26 20:15:22 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added `created' field to the SilcIDListData in the file
+         silcd/idlist.h to indicate the time when the entry was
+         created.
+
+       * Added `created' field to the SilcChannelEntry too.  Affected
+         file silcd/idlist.h.
+
+       * Added `creation_time' aguments to all the announcement functions
+         in the server.  If it is provided then only the entries that
+         was created after the provided time frame are actually
+         announced.  Affected file silcd/server.[ch].
+
+       * The protocol says that the Channel ID's IP address must be
+         based on the router's IP address.  Added check for this in
+         the silc_server_new_channel when processing incoming New Channel
+         Payload.  Affected file silcd/packet_receive.c.
+
+       * Print out the correct version with --version in SILC client.
+         Affected file irssi/src/silc/core/silc-core.c.
+
+Mon Sep 24 17:19:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed WHOWAS command to check for completnes of the client
+         entry always, not just when the command is coming from client.
+         Affected file silcd/command.c.
+
+       * Added new function silc_server_packet_queue_purge to purge the
+         outgoing data queue to the network.  After the function returns
+         it is guaranteed that the outgoing packet queue is empty.
+         Affected file silcd/packet_send.[ch].
+
+       * Purge the outgoing packet queue in the rekey protocol's final
+         callback to assure that all rekey packets go to the network
+         before quitting the protocol.  Affected file silcd/server.c.
+
+       * Added silc_client_packet_queue_parse as similar function as
+         in server to the client library.  The affected file is
+         lib/silcclient/client.c.
+
+Sun Sep 23 15:15:53 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Splitted silcd/server.c and created silcd/server_util.[ch]
+         for utility functions.
+
+       * Added new socket flag SILC_SF_DISABLED to indicate that the
+         connection is open but nothing can be sent to or received from
+         the connection.  Affected file lib/silcutil/silsockconn.[ch].
+         The checking for disabled socket is checked in the low level
+         silc_socket_write and silc_socket_read functions.
+
+Thu Sep 20 23:11:28 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Allow only nicknames and channel names that fits into the
+         7-bit unsigned char ASCII set.  Affected file silcd/command.c.
+
+Thu Sep 20 18:04:12 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * When processing JOIN command reply in server check that if
+         the channel exists in our global list we'll move it the local
+         list.  Affected file silcd/command_reply.c.
+
+       * Fixed the check whether client is joined on the channel already
+         in JOIN command.  Affected file lib/silcclient/command.c.
+
+       * Fixed the JOIN command reply to check whether the channel
+         already exists.  Affected file lib/silcclient/command_reply.c.
+
+Wed Sep 19 22:58:32 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added silc_ske_status_string to map the SKE error numbers
+         to readable strings.  The affected files are
+         lib/silcske/silcske[_status].[ch].
+
+Tue Sep 18 22:50:41 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not show the private channels on the WHOIS channel list
+         as it is not allowed by the protocol.  The affected file is
+         silcd/server.c.
+
+Sun Sep 16 12:32:58 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Assure that the packet length digged from the actual packet
+         is something sensible in the silc_packet_decrypt_rest_special
+         in lib/silccrypt/silcpacket.c.
+
+       * Free and NULL the allocated pointer in silc_hmac_alloc if
+         the HMAC allocation fails.  The affected file is
+         lib/silccrypt/silchmac.c.
+
+       * Print the selected security properties to the log files in
+         the server.  Affected file silcd/protocol.c.
+
+       * Add SKE's reference counter even if calling the completion
+         callback manually.  Otherwise it goes negative, although it
+         does not cause any problems.  The affected file is
+         lib/silcske/silcske.c.
+
+       * Remove the client entry with short timeout after giving the
+         KILL command.  Affected file lib/silcclient/command.c.
+
+       * Fixed to send error reply in WHOIS and IDENTIFY commands in
+         case all found clients are already disconnected (WHOWAS would
+         found them) in the server.  Affected file silcd/command.c.
+
+       * Update the last_receive (time of last data received) to be 
+         updated only when received private or channel message so that
+         the idle time showed in WHOIS makes more sense.
+
+       * Added boolean field `valid' in to the SilcClientEntry in the
+         client library to indicate whether the entry is valid or not.
+         This fixes the nickname change bug on channel when changing
+         the nickname to be same than the old (like nick to Nick) the
+         nickname formatter doesn't set the new nick anymore to Nick@host.
+         Affected file lib/silcclient/idlist.[ch].
+
+       * Now actually fixed the nickname changing on disconnection.
+         Added new function silc_change_nick to the Irssi SILC Client.
+         Affected file irssi/src/silc/core/client_ops.c,
+         irssi/src/silc/core/silc-nicklist.[ch].
+
+Sat Sep 15 13:29:17 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Check that the public key exists in the GETKEY command before
+         trying to encode it.  Affected file silcd/command.c.
+
+       * Print some notifications on received public keys with GETKEY
+         command in the Irssi SILC Client.  Affected files are
+         irssi/src/fe-common/silc/module-formats.[ch],
+         irssi/src/silc/core/client_ops.c.
+
+       * Use IDENTIFY command to resolve the server information in the
+         GETKEY command instead of INFO command.  Affected file
+         lib/silcclient/command.c.
+
+       * All command reply functions in the client library now calls
+         the pending command reply callbacks even if an error has
+         occurred.  The server has done this a long time and now it was
+         time to move the client library to this as well.  Now all
+         errors can be delivered back to the pending command reply
+         callbacks if necessary.  Affected files are
+         lib/silcclient/command[_reply].[ch].
+
+       * Change the nickname on disconnection back to the username
+         because in reconnect the server will enforce it to it anyway.
+         Affected file irssi/src/silc/core/silc-servers.c.
+
+       * Fixed a config file parsing bug in the Irssi SILC client.
+         Affected file irssi/src/silc/core/clientconfig.c.
+
+Thu Sep 13 23:11:18 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * When printing the channel mode on JOIN, verify that the
+         channel key and channel's HMAC are valid.  Affected file
+         irssi/src/silc/core/client_ops.c.
+
+Thu Sep 13 20:24:52 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added defines SILC_DEFAULT_CIPHER, SILC_DEFAULT_HMAC,
+         SILC_DEFAULT_HASH and SILC_DEFAULT_PKCS in the file
+         lib/silccrypt/[silccipher.h|silchmac.h|silchash.h|silcpkcs.h].
+
+       * Removed channel key rekey task deleting from the function
+         silc_server_save_channel_key.  Affected file silcd/server.c.
+         Added explicit timeout task context instead that is used to   
+         delete the task if we are registering a new task before the
+         new task has elapsed.
+
+       * When channel key rekey occurs the client library now saves
+         the old channel key for a short period of time (10 seconds) and
+         is able to use it in case some is still sending channel
+         messages encrypted with the old key after the rekey.  Affected
+         file lib/silcclient/[idlist.h|client_channel.c].
+
+Sun Sep  9 15:49:16 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added check to the silc_server_new_id_real to not accept
+         new ID if it is the sender's own ID.  Affected file is
+         silcd/packet_receive.c.
+
+       * Assure that we do not announce ourself or the one we've
+         sending our announcements when we're router and are announcing
+         servers to our primary router.  Affected file silcd/server.c.
+
+       * Fixed silc_server_command_identify_check_client to assemble
+         correct WHOIS packet.  It send corrupted WHOIS packet and
+         caused problem with router to router connections.  Affected
+         file silcd/command.c.
+
+         Fixed also silc_server_command_whois_check the same way
+         as for the IDENTIFY command.
+
+       * Added new SilcIDListStatus to the server in the SilcIDListData
+         structure.   The status now includes the current status of
+         the entry (like registered, resolved etc.).  Affected file
+         silcd/idlist.[ch].  Defined a bunch of different status types
+         as well.  This replaced the old boolean registered field as well.
+
+         Added resolve_cmd_ident field to the SilcClientEntry structure
+         too so that if the entry is for example being resolved so 
+         another command may attach to the same pending command reply
+         without requiring to resolve the same entry again.  This concept
+         should optimize the WHOIS and the IDENTIFY resolving under
+         heavy load by taking away unnecessary resolving for entries
+         that are being resolved already.
+
+         Added support for adding multiple pending commands for one
+         command idenfier.  Affected file silcd/command[_reply].[ch].
+
+       * Fixed WHOIS and IDENTIFY save to remove the cache entry
+         before deleting the data.  Otherwise the hash table will have
+         freed data in comparison functions.  Affected file is
+         silcd/command_reply.c.
+
+       * Fixed silc_idlist_replace_client_id to add the new entry to
+         the cache with NULL nickname.  Otherwise there will be invalid
+         memory as the nickname after the nickname is freed.  Affected
+         file silcd/packet_receive.c.
+
+       * Fixed the silc_idlist_get_clients_by_hash.  The entries was
+         saved into wrong slots because the previous number of entries
+         was not taken into account.  Affected file silcd/idlist.c.
+         Fixed same thing in silc_idlist_get_clients_by_nickname too.
+
+       * If we are router and we receive JOIN notify to a channel that
+         does not have any users then notified client is marked as the
+         channel founder, as it is it.  The affected file is
+         silcd/packet_receive.c
+
+       * Added to the extended hash table API's table_del_*ext functions
+         the destructor as argument too, so that the caller can decide
+         which destructor to use or whether to use destructor at all.
+         Affected file lib/silcutil/silchashtable.[ch].
+
+       * Fixed ID Cache purging.  It actually deleted the entries from
+         the hash table after the data was freed.  The hash table ended
+         up comparing freed memory.  The affected file is
+         lib/silccore/silcidcache.c.
+
+Sat Sep  8 10:22:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed Irssi SILC client's KILL command's HELP syntax.
+
+       * The USERS command now resolves the detailed user information
+         if the userinfo field is missing.  Affected file is
+         lib/silcclient/command_reply.c.
+
+       * Do not print error in silc_file_read if the read file does
+         not exist.  Just silently return NULL.  Affected file is
+         lib/silcutil/silcutil.c.
+
+       * Fixed the silc_log_output to not wine about NULL filename
+         and to not create some bogus " " filename.  Affected file is
+         lib/silcutil/silclog.c.
+
+Fri Sep  7 22:16:38 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed various printing bugs on the user interface in the
+         Irssi SILC Client.  Minor changes that were forgotten from
+         the release.
+
+Fri Sep  7 17:28:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the configure.in.pre and the compilation and distribution
+         environment to support the new autoconf 2.52.  That version is
+         now required to compile the CVS trunk.
+
+Thu Sep  6 12:47:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Renamed function silc_parse_nickname to silc_parse_userfqdn
+         to generally parse user@fqdn format strings.  Affected file
+         lib/silcutil/silcutil.c.
+
+       * Added nickname_format and nickname_force_format fields to the
+         SilcClientParams structure.  The first one defines the format
+         for the nicknames that the library will enforce if the receives
+         multiple same nicknames.  The second one is boolean value and
+         can be used to force the library to always enforce the format
+         to the nicknames regardles whether there are multiple nicknames
+         or not.  This configurable formatting was employed to flexibly
+         support accessing multiple nicknames from the user interface.
+         The userinterface can now set the nicknames to what ever format
+         they prefer.  Affected file lib/silcclient/silcapi.h.
+
+         Added function silc_client_nickname_format to the file
+         lib/silcclient/idlist.c.  It performs the nickname formatting.
+
+         Added new field `hostname´ to the SilcClientEntry context.
+         It holds the hostname of the client.  Affected file is
+         lib/silcclient/idlist.h.
+
+       * Irssi SILC Client sets the nicknames in nick@hostn format.
+         Fe. priikone@otaku, priikone@otaku2 etc.  Affected file
+         irssi/src/silc/core/silc-core.c.
+
+         The WHOIS printing now also shows both the real nickname and
+         the formatted nickname so that user knows how to access the
+         user if there are multiple same nicknames cached.  Affected
+         file irssi/src/silc/core/client_ops.c.  Changed the WHOIS
+         printing formatting too to take the hostname now as a separate
+         argument.  The Affected file is
+         irssi/src/fe-common/silc/modules-formats.[ch].
+
+       * Changed the silc_client_get_clients_local to accept the formatted
+         nickname as argument.  It accepts the real nickname too but the
+         formatted nickname can be used to find the true entry from 
+         multiple entries.  Affected file lib/silcclient/silcapi.h and
+         lib/silcclient/idlist.c.
+
+       * Added nickname_format_parse field to the SilcClientParams.
+         It is a callback function provided by the application to parse
+         the nickname out of the formatted nickname string. The library
+         calls it to get the nickname from the formatted string. Since
+         the application generally knows better the format of the nickname
+         string it parses it instead of the library, even though library
+         encodes the formatted string.  If the callback function is not
+         provided then the library will use the string as is.  The
+         affected file is lib/silcclient/silcapi.h.
+
+       * All the nickname strings passed to the client library in 
+         commands are now expected to be formatted nickname strings.
+         If the command does not support the formatted nickname string
+         it will assume that the sent string is the actual nickname.
+         Affected file lib/silcclient/command.c.
+
+Tue Sep  4 22:31:28 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added public key authentication support to OPER and SILCOPER
+         commands in the client library.  Affected file is
+         lib/silcclient/command.c.
+
+Tue Sep  4 12:39:17 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the get_auth_methdod client operation to be asynchronous.
+         It can be async if the application resolves the authentication
+         method from the server during the negotiation.  Added new
+         SilcGetAuthMeth completion callback that the application will
+         call after resolving the authentication method.
+
+         Added function silc_client_request_authentication_method that
+         the application can use to resolve the authentication method
+         from the server.  Added also SilcConnectionAuthRequest callback
+         that the library will call after the server has replied.  The
+         application can call this function if it does not know the
+         current authentication method.
+
+         Affected files are lib/silcclient/client.c and 
+         lib/silcclient/silcapi.h.
+
+       * The Irssi SILC client now automatically resolves the authentication
+         method incase any configuration information is not present (and
+         currently there never is).  The affected file is
+         irssi/src/silc/core/client_ops.c.
+
+       * Fixed public key authentication from the client library.
+         Affected file lib/silcclient/protocol.c.  Changed also the
+         protocol specification about the public key authentication in
+         the connection authentication protocol.  The actual data to be
+         signed is now computed with a hash function before signing.
+
+       * Fixed the public key authentication from the server as well.
+         Affected file silcd/protocol.c.
+
+       * Removed the mlock()'s from the memory allocation routines.
+         Affected file lib/silcutil/silcmemory.c.  The ./configure does
+         not check anymore for the mlock().  Affected file is
+         configure.in.pre.
+
+       * Fixed USERS command in server to allow the execution of the
+         command for private and secret channels if the client sending
+         the command is on the channel.  Affected file silcd/command.c.
+
+       * Fixed silc_client_get_clients_local to return the clients
+         count correctly.  It could return wrong value.  Affected file
+         lib/silcclient/idlist.c.
+
+Mon Sep  3 20:09:59 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the lib/silcmath/mpi/mpi.h to always use 32-bit data
+         types.  The assembler optimizations seemed not to like 64-bit
+         data types.  The assmebler optimizations thus are now enabled
+         also for BSD systems as opposed to only enable them for Linux.
+
+       * Do not check for threads at all on BSD systems.  Affected
+         file configure.in.pre.
+
+       * Removed -n and -h options from the Irssi SILC Client since
+         they are not used in silc.
+
+       * Fixed the prime generation to assure that the first digit
+         of the generated random number is not zero since our conversion
+         routines does not like number strings that starts with zero
+         digit.  If zero digit is seen the random number is regenerated.
+         This caused some corrupted RSA keys when the zero first digit
+         was met.  Affected file lib/silcmath/silcprimegen.c.
+
+Sun Sep  2 17:17:24 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed WIN32 configuration in the ./configure script.
+         Fixed to include xti.h on environments that has it.
+         Patches by Carsten Ilchmann and andrew.
+
+Sat Sep  1 00:29:33 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the silc_id_create_client_id to be collision
+         resistant.  It is now assured that there cannot be created
+         two same client ID's.  I suspect that some weird bugs in 
+         the server were actually caused by duplicate Client IDs.
+         Affected file silcd/serverid.[ch].  A router receiving
+         new ID now also assures and informs the sending server
+         if the ID caused collision.
+
+       * Changed the silc_id_create_channel_id to also assure that
+         there are no collisions.
+
+Wed Aug 29 17:55:01 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Statement about ignoring the Mutual Authentication flag when
+         performing rekey with PFS was a bit misleading.  It is ignored
+         if it was set in the initial negotiation, it cannot be even
+         set in the rekey.  Fixed in the ke-auth draft.  Started the
+         new versions of the protocol drafts in the doc/.
+
+Sun Aug 26 14:59:15 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a bug in silc_client_command_identify_save when saving
+         new channel information.  The channel name was no duplicated
+         and caused crash on exit.  Affected file is
+         lib/silcclient/command_reply.c.
+
+Fri Aug 17 23:07:45 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the getkey command handling in the server.  Send just
+         empty OK reply to the sender if the key could not be fetched
+         (but everything else was ok, like the key just was not available).
+         Changed the public key parameter to optional in the protocol
+         specs so that empty OK reply can be sent.  Affected file
+         silcd/command.c.
+
+         Added a message to Irssi SILC client to tell to user if the
+         server did not return a public key.
+
+Tue Aug 14 07:29:27 CEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a channel key regeneration bug.  It registered new
+         timeout tasks exponentially until all system resources were
+         used.  Affected file silcd/server.c.
+
+Sun Aug 12 20:48:14 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added the SILC Document generator to the scripts/silcdoc.
+         It can be used to generate the Toolkit Reference Manual out
+         of the source tree.  Internally it will also use the RoboDoc
+         generator now imported in util/robodoc.
+
+Sun Aug 12 12:28:17 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added couple of return's in rekey protocol if error orccurred
+         during the protocol.  The execution must be terminated.
+         Affected file silcd/protocol.c.  Also, terminate the protocol
+         always with timeout.
+
+Sat Aug 11 12:36:02 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * The client's Client ID was created initally from the wrong
+         nickname (it could have been in format nick@host) in the
+         silc_server_new_client.  Affected file silcd/packet_receive.c
+
+Sat Aug 11 00:29:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added some SILC_LOG_ERROR's to various error conditions
+         if client could not be added to ID cache.  Affected files
+         silcd/packet_receive.c and silcd/server.c.
+
+       * When client's sock->user_data is freed, NULL also the 
+         client->router and client->connection pointers.  Added check
+         for these pointers being NULL to various places around the
+         code.  Affected file silcd/server.c.
+
+       * Added client->data.registered == TRUE checks to various
+         places around the code to assure that unregistered client's
+         are not handled when it is not allowed.  Affected file
+         silcd/server.c.
+
+       * Added `bool registered' fields to all 
+         silc_idlist_[server|client]_get_* routines to indicate whether
+         the fetched client needs to be registered or not.  Affected
+         file silcd/idlist.[ch].
+
+       * Add your own entry as registered to the ID cache in the
+         server.  Affected file server.c.
+
+       * Fixed a bug in silc_server_new_server.  The SilcServer was
+         set as the new server's context instead of SilcServerEntry.
+         This naturally caused some weird bugs.
+
+Thu Aug  9 18:28:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not delete the channel rekey task when adding it
+         for in silc_server_create_channel_key.
+
+       * Changed the silc_server_create_channel_key to return
+         TRUE or FALSE to indicate the success of the channel key
+         creation.
+
+Thu Jul 26 11:32:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed MSVC++ project files and added missing files to
+         Makefiles under win32/.
+
+Wed Jul 25 18:43:54 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not add TCP_NODELAY flag if the operating system
+         does not have it defined.  Affected files are
+         lib/silcutil/[unix/win32]/silc[unix/win32]net.c.
+
+       * Fixed buffer overflow from Irssi SILC Client.  Affected
+         file irssi/src/fe-common/core/themes.c.
+
+       * Fixed double free in client library in the file
+         lib/silcclient/client.c when disconnecting from server.
+
+       * Applied double free patch from cras to Irssi SILC client.
+         Affected files irssi/src/core/[modules/expandos].c
+
+       * Fixed the disconnection handling to Irssi SILC Client.
+         The application must call silc_client_close_connection
+         in ops->connect client operation in case of failure of
+         the connection.  Affected file is
+         irssi/src/silc/core/client_ops.c.
+
+       * Do not set sock->protocol to NULL in the function
+         silc_client_close_connection after executing the protocol's
+         final callback since the sock might not be valid anymore.
+         Affected file lib/silcclient/client.c.
+
+Wed Jul 25 16:04:35 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not enable SILC_THREADS if the linking with libpthread
+         did not happen.  Affected file configure.in.pre.
+
+       * Added notion to protocol specification that server must
+         verify the sent authentication payload with CMODE when
+         setting the channel founder key.  Implemented it to the
+         server.  Affected file silcd/command.c.
+
+Mon Jul 23 18:31:43 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added _EXTRA_DIST SILC distribution variable to the
+         distributions file.  It is used to conditionally add extra
+         files or directories to the specific distribution.  Affected
+         files ./prepare, Makefile.am.pre and distributions.
+
+         Removed the `_' from the start of the distribution names.
+         It is redundant.
+
+       * Added README.WIN32 for instructions to compile the Toolkit
+         under WIN32.
+
+Mon Jul 23 10:12:37 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a double free in disconnection in the server.  Affected
+         file is silcd/server.c.
+
+       * Fixed the lib/silcske/groups.c to work now also with GMP
+         MP library.  The string conversion did not work when using
+         specific base and the base is indicated in the string as well.
+
+       * Created win32/ directory which now includes MSVC++ specific
+         stuff so that toolkit (DLLs) may be compiled with MSVC++.
+         It will appear only in the toolkit distribution
+
+Sun Jul 22 19:40:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the key material distribution function in case when
+         the hash output is too short.  The data is now concatenated
+         a bit differently than it used to.  Made the change to the
+         SKE protocol specification.
+
+       * Added better GMP detection to configure.in.pre.  A patch
+         by salo.
+
+Fri Jul 20 13:16:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a minor bug in SKE that might cause some problem on
+         some platforms.  Affected file lib/silcske/silcske.c.
+
+       * Added the cookie checking for initiator in the SKE.  It checks
+         that the responder returns the sent cookie unmodified.  The
+         affected file is lib/silcske/silcske.c.  Added new SKE
+         error type INVALID_COOKIE that can be sent during the
+         negotiation.  Fixed some memory leaks as well.
+
+       * Added the "invalid cookie" error message to Irssi SILC client's
+         message formats.
+
+Thu Jul 19 21:44:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added `task_max' field to the SilcClientParams to indicate
+         the maximum tasks the scheduler can handle.  If set to zero,
+         default values are used.  Affected file lib/silcclient/silcapi.h.
+
+       * Fixed memory leaks in silc_client_close_connection.  Affected
+         file lib/silcclient/client.c.
+
+       * Added silc_client_del_client_entry to client library to free
+         all memory of given client entry.  Affected file is
+         lib/silcclient/idlist.[ch].
+
+       * Added new functions silc_client_del_channel and
+         silc_client_del_server to delete channel and server entries.
+         Affected file lib/silcclient/[silcapi.h/idlist.c].
+
+       * Removed silc_client_del_client_by_id from silcapi.h.
+
+       * Fixed the INFO command to return the server's own info
+         correctly when querying by Server ID.  Affected file is
+         silcd/command.c.
+
+Thu Jul 19 14:47:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Removed the non-blocking settings in WIN32 code in the
+         silc_sock_[read/write] and added SleepEx instead.  Affected
+         file lib/silcutil/win32/silcwin32sockconn.c.  The availability
+         of input data is now checked with FIONREAD and ioctlsocket.
+
+Wed Jul 18 18:34:01 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Call silc_schedule_task_del_by_context in the 
+         silc_protocol_cancel instead of silc_schedule_task_del_by_callback.
+         Affected file lib/silccore/silcprotocol.c.
+
+       * Call silc_protocol_cancel for active protocols in the
+         silc_server_close_connection if the funtion
+         silc_server_free_sock_user_data has not been called.
+         Affected file silcd/server.c.
+
+       * Generic tasks cannot be deleted using the del_by_fd
+         task deleting function since generic tasks does not match
+         any specific fd.  Affected file lib/silcutil/silcschedule.[ch].
+
+       * Added a notion to SILCOPER help file that the SILCOPER works
+         only on router server, not on normal server.
+
+Wed Jul 18 09:40:04 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added for WIN32 support for the new scheduler as well.
+         Affected file lib/silcutil/win32/silcwin32schedule.c.
+
+       * Fixed the SHA1 implementation to work on various platforms.
+
+Tue Jul 17 23:04:10 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Rewrote the SILC Scheduler entirely.  Removed the old SILC Task
+         API.  It is part of the scheduler now.  Everything else is
+         as previously but some functions has changed their names.
+         Checkout the lib/silcutil/silcschedule.h for the interface.
+         Updated all applications to use the new interface.  Affected
+         files are lib/silcutil/silcschedule.[ch].
+
+Tue Jul 17 16:53:30 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Found a bug in the SKE implementation.  The HASH value,
+         specified by the protocol, was not computed correctly.  The
+         public key of the responder was not added to the computation
+         even though it is mandatory.  Affected file lib/silcske/silcske.c.
+         This unfortunately causes incompatibilities with older
+         clients and servers.
+
+       * Added WIN32 specific network init and uninit functions:
+         silc_net_win32_init and silc_net_win32_uninit to init and uninit
+         the Winsock2.  Affected file lib/silcutil/silcnet.h and
+         lib/silcutil/win32/silcwin32net.c.
+
+       * Set the socket always to nonblocking mode on WIN32 after
+         reading data or writing data.  Affected file is
+         lib/silcutil/win32/silcwin32sockconn.c.
+
+Mon Jul 16 22:55:26 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed various compilation problems under WIN32.  Affected
+         files lib/silcutil/win32/silcwin32thread.c and
+         lib/silcutil/win32/silcwin32schedule.c.
+
+       * Removed all _internal.h #includes from public header
+         files.  Internal headers must never be included from
+         public headers.
+
+         Removed also the lib/silcske/payload_internal.h file.
+
+       * All include files that may be needed (public and some others
+         included by the public headers) by application developers are
+         now copied to the ./includes directory.  It does not copy any
+         internal headers.  Affected file Makefile.defines.pre and all
+         Makefile.am's under lib/ and subdirs.
+
+Thu Jul 12 17:49:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not change the ~/.silc directory's permissions automatically.
+         Affected file irssi/src/silc/core/clientutil.c.
+
+Thu Jul 12 10:18:40 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not cancel the protocol in silc_server_close_connection
+         it might cause recursion.  Now cancelled in the function
+         silc_server_free_sock_user_data.  Affected file silcd/server.c.
+
+       * Fixed the silc_server_remove_clients_by_server to regenerate
+         the channel keys correctly finally.  Added also new function
+         silc_server_remove_clients_channels to actually do it.
+         Affected file silcd/server.c.
+
+       * Fixed the silc_server_new_channel to not crash by giving
+         wrong router to the new channel.  Affected file is
+         silcd/packet_receive.c.
+
+Wed Jul 11 18:31:57 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added SilcClientParams structure to the lib/silcclient/silcapi.h
+         which is given as argument to the silc_client_alloc now.
+         It can be used to configure the client and set various parameters
+         that affect the function of the client.
+
+       * The USERS command in server did not check whether the channel
+         is private or secret.  Affected file silcd/command.c.
+
+       * Added new argument to the USERS command in protocol specification.
+         The USERS command now can take the channel name as argument
+         as well.  Added support for this in client and server and
+         updated the protocol specs.
+
+       * Completed the GETKEY command in client. It can be now used
+         to fetch also servers public key not only some clients. 
+         Affected files lib/silcclient/command[_reply].c.
+
+       * Added silc_client_get_server to return server entry by the
+         server name.  Affected files lib/silcclient/silcapi.h and
+         idlist.c.
+
+       * Redefined the IDENTIFY command in protocol specification to be
+         more generic.  It now can be used to query information about
+         any entity in the SILC Network, including clients, servers and
+         channels.  The query may be based either the entity's name
+         or the ID.  Added support for this in both client and server.
+
+         Affected files silcd/command.c and lib/silcclient/command.c
+         and command_reply.c.
+
+       * Optimized the WHOIS and WHOWAS commands in the server. Removed
+         the _from_client and _from_server functions.  Affected file
+         silcd/command.c.
+
+       * Added silc_client_get_channel_by_id_resolve to the file
+         lib/silcclient/silcapi.h to resolve channel information by
+         its ID.  Added also silc_client_get_channel_by_id that
+         does not resolve it from the server.
+
+Tue Jul 10 18:05:38 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added SilcServerEntry context into the client library
+         to represent one server.  The INFO command now allocates
+         these to save the resolved server info.  For now on the
+         client library will also keep information about servers,
+         connected and resolved with INFO.
+
+         The INFO command now allocates the SilcServerEntry context
+         and saves the server info there.  The COMMAND_REPLY in
+         the INFO now returns the parameters to application in 
+         same order as defined in the protocol specification.
+
+         The entries are cached in the client->server_cache.
+
+       * The INFO command is now issued after received the Client ID
+         from the server.  Affected file lib/silcclient/client.c.
+
+       * The CMODE_CHANGE notify may now return also an SilcServerEntry
+         to the application as the mode changer might be server.
+         It is guaranteed that NULL is not returned anymore to the
+         application.  Affected file lib/silcclient/client_notify.c.
+
+         The ID Type is now also passed to the application so that
+         it can check whether the returned entry is SilcClientEntry
+         or SilcServerEntry.
+
+         Added new function silc_client_get_server_by_id to return
+         the server entry by ID.  Affected files are the
+         lib/silcclient/silcapi.h and lib/silcclient/idlist.c.
+
+       * Do not create the channel in the Irssi SILC Client when issuing
+         the JOIN command but when received the sucessful JOIN command
+         reply.  Otherwise the channel might get created even though we
+         could not join it.  The Affected file is
+         irssi/src/silc/core/[silc-channels.c/client_ops.c].
+
+       * Fixed a channel joining bug in router.  The router must also
+         check the channel modes, invite and ban lists etc. when serving
+         the JOIN command sent by normal server.  Affected file is
+         silcd/command.c.  The router now resolves the client's 
+         information from the server who sent the JOIN command if it
+         does not know it, and processes the JOIN command only after
+         that.
+
+       * Changed the SilcCommandCb to take new argument; void *context2.
+         Affected file lib/silccore/silccommand.h
+
+         The second argument in the command callbacks in the server now
+         includes the SilcServerCommandReplyContext if the command was
+         called as pending command callback from the command reply.
+         Otherwise it is NULL. When called as pending the status of the
+         command reply will be checked and if it was erronous the
+         error will be sent to the original sender of the command.
+         This way the client always receives the error messages even
+         though the server was actually the one who received the error
+         when it resent the command to router, for example.  Affected
+         files silcd/command[_reply].[ch].
+
+       * Fixed sending WHOWAS command's error message to client if
+         the requested client could not be found.  It was missing.
+         silcd/command.c.
+
+       * Changed the CMODE and CUMODE commands reply arguments in the
+         protocol specification.  The Channel ID is now sent in both
+         of the commands to identify the channel.  Implemented this
+         new feature to the client and server.  Affected files
+         lib/silcclient/command_reply.c and silcd/command.c.
+
+       * Made better checks for invite and ban lists in the JOIN
+         command in server.  Affected file silcd/command.c.
+
+Mon Jul  9 18:28:34 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * The server now performs the incoming host IP/DNS lookup
+         using the silc_socket_host_lookup and thus does not block
+         the server anymore.  Affected file silcd/server.c.
+
+       * Completed the multi-thread support for SILC Scheduler in
+         the lib/silcutil/silcschedule.c.
+
+       * Fixed the configure.in.pre to detect the pthread correctly
+         on various systems.
+
+       * Fixed a deadlock in silc_task_queue_wakeup in the file
+         lib/silcutil/silctask.c.
+
+Mon Jul  9 13:40:03 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added new function silc_schedule_wakeup that is used in
+         multi-threaded environment to wakeup the main thread's
+         schduler. It needs to be used when a thread adds a new task
+         or removes a task from task queues. After waking up, the
+         scheduler will detect the task queue changes. If threads
+         support is not compiled in this function has no effect.
+         Implemented the wakeup mechanism to both Unix and WIN32
+         systems.  Affected files are lib/silcutil/silcschedule.[ch],
+         lib/silcutil/unix/silcunixschedule.c and the
+         lib/silcutil/win32/silcwin32schedule.c.
+
+       * Added new function silc_task_queue_wakeup to wakeup the
+         scheduler by the specified task queue.  Affected file
+         lib/silcutil/silctask.[ch].
+
+       * The silc_socket_host_lookup_start now wakes up the scheduler
+         after adding the timeout task.  Affected file is
+         lib/silcutil/silcsockconn.c.
+
+       * The silc_socket_host_lookup is synchronous now if the threads
+         support is not compiled in.  However, the callback is still
+         called asyncronously through the scheduler, anyway.  Affected
+         file lib/silcutil/silcsockconn.c.
+
+Mon Jul  9 00:24:45 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added new function silc_socket_host_lookup to perform
+         asynchronous IP and FQDN lookups for the socket connection.
+         Affected files lib/silcutil/silcsockconn.[ch].
+
+Sun Jul  8 18:44:53 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added SILC_MUTEX_DEFINE to define the mutex on environments
+         that may or may not compile the mutex support in.
+       
+         Changed the silc_mutex_alloc interface. It allocates the
+         mutex now to the sent pointer and returns TRUE or FALSE.
+
+         Affected file lib/silcutil/silcmutex.h.
+
+       * Wrote the SILC Task Queue interface to support multi-threads.
+         Affected file lib/silcutil/silctask.[ch].
+
+       * Wrote the SILC Scheduler to support multi-threads.  Affected
+         file lib/silcutil/silcschedule.c.
+
+Sun Jul  8 11:16:01 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Implemented the SILC Mutex API and SILC Thread API for WIN32
+         in lib/silcutil/win32/.
+
+Sun Jul  8 00:18:15 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Defined SILC Mutex API and SILC Thread API and implemented
+         them for Unix.  Affected files are
+         lib/silcutil/silcmutex.h, lib/silcutil/silcthread.h,
+         lib/silcutil/unix/silcunixmutex.c and
+         lib/silcutil/unix/silcunixthread.c.
+
+Sat Jul  7 14:40:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed the silc_server_remove_clients_by_server's channel
+         key re-generation.  The hash table handling was incorrect
+         and would not work with many channels.  Affected file is
+         silcd/server.c.
+
+       * Fixed some memory leaks around the server code.
+
+       * Rewrote the silc_server_get_users_on_channel to support IPv6
+         based Client ID's.  Affected file silcd/server.c.
+
+       * Defined the SILC_MESSAGE_FLAG_SIGNED to the protocol
+         specification.  However, a separate document must be written
+         to define the detailed signing procedure and the payload
+         associated with the flag.  Defined the flag to the
+         lib/silccore/silcchannel.h as well.
+
+Fri Jul  6 18:26:31 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the dynamic tables to static size tables in the
+         lib/silccrypt/silchmac.c.
+
+       * Removed GCC dependencies from the code.  A patch by cras.
+
+Fri Jul  6 09:39:35 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not show the error "Error receiving packet bla bla"
+         in server if it really was not an error (-2 means that reading
+         is pending).  Affected file silcd/server.c.
+
+Thu Jul  5 21:22:32 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a possible crash in silc_server_remove_clients_by_server
+         in silcd/server.c.  Fixed there also some memory leaks.
+
+       * Fixed the silc_idlist_replace_client_id.  It could replace
+         wrong key in the hash table.  Affected file silcd/idlist.c.
+
+       * Do not check whether there are global users on the channel
+         if the channel->global_users is FALSE.  Affected functions
+         silc_server_remove_from_one_channel and
+         silc_server_remove_from_channels in silcd/server.c.  Also,
+         do not check if the removed client is local as we can be
+         sure that global client was not removed from the channel
+         and checking for global users is not needed.
+
+       * The silc_server_remove_clients_by_server now re-generates
+         the channel keys correctly for those channels that had
+         clients removed from them.  Affected file silcd/server.c.
+
+Tue Jul  3 11:39:20 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Found the reason of random crashes in the server.  We weren't
+         ignoring the SIGPIPE signal (which can be sent in write())
+         and it crashed the server.  Affected file silcd/silcd.c.
+
+Fri Jun 29 20:05:25 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Assure that sock->user_data is not NULL in the function
+         silc_server_packet_send in silcd/packet_send.c.
+
+       * Disconnect the remote connection if it could not be added
+         to any ID lists in the server.  The affected file is
+         silcd/server.c.
+
+       * Check in silc_server_packet_send[_real/dest] that the
+         socket is not disconnecting and ignore the data if it is.
+         Affected file silcd/packet_send.c.
+
+       * Define inline to __inline on native WIN32 compilation.
+         Affected file includes/silcwin32.h.
+
+       * Added some explicit type casts for inline code since MSVC
+         require them.  Affected files lib/silcutil/silcbuffer.h,
+         lib/trq/silcdlist.h and lib/trq/silclist.h.
+
+       * Print warning in log files from now on if the packet
+         decryption fails.  Affected file silcd/server.c.
+
+Thu Jun 28 21:30:39 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Changed the `say' client operation's interface to accept
+         new `type' argument to indicate the type of the message sent
+         by the library.  The application may filter the library's
+         messages according the type.  The affected file is the
+         lib/silcclient/silcapi.h.
+
+       * Added two new functions to lib/silcclient/silcapi.h:
+         silc_client_del_client and silc_client_del_client_by_id.
+         Affected file lib/silcclient/idlist.c.
+
+       * Moved the clientincludes.h from includes/ to silc/ and
+         serverincludes.h from includes/ to silcd/.
+
+       * The modes for the CMODE and CUMODE are now passed as
+         uint32 for application with COMMAND_REPLY.  The affected
+         file is lib/silcclient/command_reply.c.
+
+Wed Jun 27 22:24:47 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * /WHOIS without arguments shows client's own information.
+         Affected file lib/silcclient/command.c.
+
+       * Changed PING to not accept any arguments.  The specs
+         says that client can ping only the connected server so
+         requiring an argument is not needed.  Affected file is
+         lib/silcclient/command.c.
+
+Wed Jun 27 00:10:33 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Fixed a fatal bug in private message sending and reception
+         encryption and decryption when using private message keys.
+         The implementation was incorrect and did not follow the
+         specification.  It causd that some of the message were
+         lost since it did not use the sending and receiving keys
+         as the protocol suggests.  This has been fixed and will cause
+         incompatibilities with older clients when sending private
+         message encrypted with private message keys.  Affected files
+         lib/silcclient/client_prvmsg.c, lib/silcclient/client_keyagr.c
+         and various other in Irssi SILC Client.
+
+         Added `responder' boolean argument to the functions
+         silc_client_add_private_message_key[_ske] to indicate when
+         the key is added as responder or initiator of the key
+         negotiation.
+
+Tue Jun 26 19:23:07 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Removed the silc_ske_check_version function and created
+         a SilcSKECheckVersion callback.  Added also a function
+         silc_ske_set_callbacks that is now used to set all SKE
+         callbacks.  The callback functions are not given to
+         the SKE functions anymore, but this function is used to
+         set the callbacks.
+
+       * Fixed the WIN32 DLL generation in lib/Makefile.am.pre.
+
+       * Added `silc_version' argument to the silc_client_alloc
+         to define the version of the application for the library.
+         The library will use the version string to compare it
+         against the remote host's (usually a server) version
+         string.  Affected file lib/silcclient/silcapi.h
+
+       * Added the KE protocol context to Key Agreement context
+         in client library so that we can abort the SKE if it
+         is in process when we get timeout.  Affected file is
+         lib/silcclient/client_keyagr.c.
+
+       * Do not resolve the client ID forever if it returns in the
+         first time that such client does not exist.  This was done
+         for example with private message.  Affected file is
+         lib/silcclient/client_prvmsg.c.
+
+Mon Jun 25 21:42:51 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not add regex.h for WIN32.  The affected file
+         includes/silcincludes.h.
+
+       * Added WIN32 DLL generation to lib/Makefile.am.pre.  It might
+         not work yet 100%.  It generates the DLL's automatically
+         when compiling with --with-win32 under cygwin.
+
+Sun Jun 24 19:49:23 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * lib/contrib/regex.c is not compiled on WIN32.
+
+       * Added silc_net_get_socket_opt function to the
+         lib/silcutil/silcnet.h.
+
+       * Added includes/silcwin32.h for WIN32 specific includes
+         and definitions.
+
+       * Do not use ptime structure or any of the posix process
+         functions on WIN32 in lib/silccrypt/silrng.c.
+
+       * Added silc_gettimeofday to provide generic function
+         for struct timeval on all platforms.  Added the function
+         to lib/silcutil/silcutil.h.
+
+Sun Jun 24 12:19:52 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Moved the lib/silccore/silcsockconn.[ch] to the utility
+         library as they clearly belong there.  As a plus side we
+         can make the actual socket connection routines platform
+         specific.
+
+         Added also new generic function silc_socket_read and
+         silc_socket_write (that used to be silc_packet_[read/write].
+         The implementation of these are platform specific.
+
+       * Added WIN32 specific routines of silc_socket_[read/write]
+         to lib/silcutil/win32/silcwin32sockconn.c.
+
+Sat Jun 23 16:01:00 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Added preliminary support for native WIN32 compilation under
+         cygwin (using the -mno-cygwin option for GCC) to the
+         ./configure.in.pre.  The --with-win32 now prepares the
+         compilation for native WIN32.
+
+       * Rewrote the SILC Scheduler interface in the file
+         lib/silcutil/silcschedule.h.  The scheduler is now context
+         based and does not have anymore any global static scheduler.
+         Moved the Unix scheduler to the lib/silcutil/unix/ directory
+         and created lib/silcutil/win32 directory for WIN32 based
+         scheduler.
+
+       * Added Unix specific network routines to the
+         lib/silcutil/unix/silcunixnet.c and the old
+         lib/silcutil/silcnet.c includes now only generic routines.
+
+         Added WIN32 specific network routines to the
+         lib/silcutil/win32/silcwin32net.c.
+
+       * Added Unix specific utility functions from the
+         lib/silcutil/silcutil.c to lib/silcutil/unix/silcunixutil.c.
+
+       * Added WIN32 SILC Scheduler to the file
+         lib/silcutil/win32/silcwin32schedule.c. The code is of course
+          untested.
+
+Fri Jun 22 10:44:14 EEST 2001  Pekka Riikonen <priikone@silcnet.org>
+
+       * Do not handle JOIN notify in the server if the target client
+         is not registered (idata->registered == FALSE).  The affected
+         file is silcd/packet_receive.c.
+
+       * Update the nickrec->founder in event_cumode in the Irssi SILC
+         client.  Affected file irssi/src/silc/core/silc-channels.c.
+
+       * Fixed the CUMODE_CHANGE notify handling in the server when
+         server and router are announcing their clients on channels.
+         Now the mode changes are saved and notified correctly.  The
+         affected file is /silcd/packet_receive.c.
+
+       * Fixed silc_idlit_replace_[server/client/channel]_id functions.
+         They really did not replace the cache entry in the ID Cache.
+         Now they do that.  Affected file silcd/idlist.c.
+
+       * Fixed the KICK notify handling in the Irssi SILC client to
+         update the channel records so that the kicked client does not
+         appear to be on the channel.  The affected file is 
+         irssi/src/silc/core/silc-channels.c.
+
+       * Always update the conn->current_channel when executing command
+         on a channel.  Affected file irssi/src/silc/core/silc-servers.c.
+
+       * Fixed the KILL notify handling in Irssi SILC client to remove
+         the killed client on all channels.
+
+Thu Jun 21 17:10:08 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the silc_parse_command_line to remove extra spaces
+         from the start and end of the arguments.  Affected file is
+         lib/silcutil/silcutil.c.
+
+       * Cancel and free any active protocol in the function
+         silc_server_close_connection.  Affected file silcd/server.c.
+
+       * Cancel and free any active protocol in the function
+         silc_client_close_connction.  Affected file is
+         lib/silcclient/client.c.
+
+       * Do not execute the KILL command for clients that are in
+         history (ie. they are not in the network).  Affected file is
+         silcd/command.c.
+
+       * Fixed KILL notify handling, client does not crash anymore.
+         Affected file irssi/src/silc/core/silc-channels.c.
+
+       * Reduced the default packet buffer size from 2048 to 1024 in   
+         lib/silccore/silcpacket.c.
+
+       * Added SILC_SKE_STATUS_FREED SKE status type and a reference
+         counter to the SKE context that is incresed when the SKE library
+         performs async operation outside the library.  If the outside
+         process frees the SKE context and FREED status will be set 
+         and the library will detect after the sync operation that the
+         libary is freed.  The affected files are
+         lib/silcske/silcske[_status].[ch].
+
+       * Resolve the client entry information in the function
+         silc_client_channel_message to assure that NULL pointer is not
+         passed as client entry to the application. */
+
+       * Fixed the task timeout calculation to assure that there is
+         never negative timeouts.  The affected file is 
+         lib/silcutil/silcschedule.c.
+
+       * Fixed the channel user mode notification sending in server.
+         It was sent point-to-point to the router (or to server by router)
+         but it needs to be destined to a channel.  The routines now
+         supports sending the channel user mode notifys to the channels
+         when announcing clients and channels.  Affected files are
+         silcd/server.c and silcd/packet_receive.c.
+
+       * Fixed the CHANNEL_CHANGE notify handling in the client libary.
+         It did not actually replace the old channel entry in the cache.
+         Affected file lib/silcclient/client_notify.c.
+
+Tue Jun 19 22:10:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a possible crash in silc_packet_send_prepare.  It now
+         assures always that there is enough space in the buffer and
+         at the tail area of the buffer (for MAC). 
+
+         Fixed the inbound buffer reallocation in silc_packet_read.
+         It was old code and did not handle the reallocation correctly.
+         Affected
+
+         The affected file is lib/silccore/silcpacket.c.
+
+       * Fixed buffer overflow in silc_parse_nickname in the file
+         lib/silcutil/silcutil.c.
+
+Tue Jun 19 13:40:09 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * make install generates new server keys only if there is not
+         keys already.
+
+Mon Jun 18 18:49:07 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Set SILC_MESSAGE_FLAG_NOREPLY when sending the away message.
+         Added check that if the NOREPLY is set then we will not send
+         the away message.  This avoids infinite loop of away messages
+         if both clients are away.  The affected file is
+         lib/silcclient/client_prvmsg.c.
+
+       * Fixed client crash if /NICK was given without arguments.
+         Affected file lib/silcclient/command.c.
+
+       * Server does not send the invite list in INVITE command back
+         to the client if the list was not altered.  Added this notion
+         to the protocol spec as well.  Affected file silcd/command.c.
+
+         Fixed possible crash in INVITE command by checking the
+         value of silc_server_get_client_route command.
+
+       * Fixed the INVITE notify type handling.  The arguments are now
+         taken in correct order and client does not crash.  The affected
+         file is irssi/src/silc/core/silc-channels.c.
+
+         Removed the "Inviting xxx to channel" message from the
+         client library away and let the application handle it.
+         Affected file lib/silcclient/command.c.  Added that message
+         to Irssi SILC client's message formats.
+
+       * Fixed CMODE command crash in client.  It now checks the
+         amount of arguments correctly and does not crash.  The affected
+         file is lib/silcclient/command.c.
+
+       * Do not create new channel automatically in silc_channels_join
+         but check whether the channel by that name already exists.
+         Affected file irssi/silc/core/silc-channels.c.
+
+       * Do not send the SERVER_SIGNOFF to router if the disconnected
+         entity was the router.  Affected file silcd/server.c.
+
+       * Added the handling of the SERVER_SIGNOFF notify to the Irssi
+         SILC client as it was missing from there.
+
+         Added the handling of the KICK notify to the Irssi SILC client
+         as it was missing.  Added "you have been kicked" message to
+         Irssi SILC client's message modules formats.
+
+         Added the handing of the KILL notify to the Irssi SILC client
+         as it was missing.  Added the kill message module formats 
+         as well.
+
+         The affected file is irssi/src/silc/core/silc-channels.c.
+
+       * The router did not save the channel mode the server announced.
+         Affected file silcd/packet_receive.c.
+
+       * Fixed a possible crash in INFO command in server.  If the
+         server did not provide the server info it crashed.  Affected
+         file silcd/command.c.
+
+Sun Jun 17 15:26:05 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the GETKEY command in the server to check also the
+         global list.  Otherwise the GETKEY would not work correctly
+         in normal SILC server.  Affected file silcd/command.c.
+
+Sat Jun 16 18:00:00 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed GETKEY crash, it crashed if the command did not succseed.
+
+Tue Jun 12 21:36:18 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Redefined the SILC MP API in lib/silcmath/silcmp.h. The API
+         is now real and not just an macro interface to GMP.
+
+         Removed the entire GMP from the source tree and imported new
+         NSS MPI library instead.  Reason for removing GMP is that it is
+         extremely large and compiles extremely slow.  The NSS MPI
+         is only a few files and compiles in less than 10 seconds.
+         The speed is also about the same as GMP.  The MPI is imported
+         to lib/silcmath/mpi.
+
+         If the system has GMP installed we will still use the GMP.
+         If it is not then the NSS MPI will be compiled.
+
+Mon Jun 11 18:07:24 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Merged a long nickname (127 characters long) crash bugfix from
+         Irssi CVS tree.  Affected file irssi/src/core/misc.c.
+
+       * Merged a freed memory reference bugfix from Irssi CVS tree.
+         Affected file irssi/src/core/commands.c.
+
+Sun Jun 10 16:08:35 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added the server's public key sving and verification to the
+         server when performing the SKE.  This was missing and the
+         remote server's (or router's) public key was accepted without
+         checking whether we have it previously or trust it at all.
+         Affected file silcd/protocol.c.
+
+Sat Jun  9 20:17:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Check in the silc_server_timeout_remote if protocol is active
+         and make sure that the protocol's final callback is called so
+         that all memory if freed.  Affected file silcd/server.c.
+
+Sat Jun  9 12:51:27 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * silc_server_whois_send_reply crashed the server if the nickname
+         was 127 characters long.  Affected file silcd/command.c.
+
+Thu Jun  7 16:29:56 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added sanity check to the silc_server_new_client. If the hostname
+         is provided inside username then check that the provided hostname
+         really is the same as the resolved one.  If the hostname was not
+         resolved then check it from the public key.  Affected file is
+         silcd/packet_receive.c.
+
+       * Fixed a fatal bug in Irssi SILC client. Do not send QUIT command
+         if the server disconnected us and the connection is not valid
+         anymore.  Affected file irssi/src/silc/core/silc-channels.c.
+
+       * Moved the silc_client_[chmode|chumode|chumode_char] away from
+         the library to the lib/silcutil/silcutil.[ch].
+
+Thu Jun  7 08:57:16 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Close log file after open.  Affected file 
+         lib/silcutil/silclog.c.
+
+       * Check whether sock == NULL in silc_client_send_packet and return
+         if it is.  Affected file lib/silcclient/silcclient.c.
+
+       * Check rec->entry == NULL in the Irssi SILC Client before
+         sending the channel message.  Affecte file is
+         irssi/src/silc/core/silc-servers.c.
+
+Tue Jun  5 08:08:21 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Merged a splitted window bugfix from Irssi CVS tree.  The 
+         affected file is irssi/src/fe-text/textbuffer-view.c.
+
+       * Fixed the ME, ACTION and NOTICE printing in Irssi Client.
+         It did not print nickname.
+
+       * Improved the distributions system a bit.
+
+Mon Jun  4 17:57:16 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Merged /WINDOW bugfix from irssi CVS tree. Affected file is
+         irssi/src/fe-text/gui-window.c.
+
+       * Fixed a fatal bug in Irssi SILC client. Crashed if sent message
+         to in-active server.  The affected file is
+         irssi/src/silc/core/client_ops.c.
+
+       * Resolve the client in USERS command reply if the entry does
+         not have username resolved.  The affected file is
+         lib/silcclient/command_reply.c.  Also, changed the IDENTIFY
+         command to WHOIS command to really resolve stuff.  The USERS
+         is not used any more in any critical section so WHOIS can
+         be used even though it might be slower than IDENTIFY.
+
+       * Changed the lib/silcutil/silchashtable.h header to ROBODoc
+         format.
+
+Sun Jun  3 14:21:32 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the protocol API a bit more consistent in the
+         lib/silccore/silcprotocol.[ch].
+
+       * Changed the following headers to ROBODoc format:
+
+               lib/silccore/silcpayload.h
+               lib/silccore/silcprotocol.h
+               lib/silccore/silcsockconn.h
+
+         All core library headers are now formatted.
+
+Sat Jun  2 10:45:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a bug in Irssi SILC client; do not show that you are
+         server/router operator if you really are not.  Affected file is
+         irssi/src/silc/core/client_ops.c.
+
+       * Renamed silc_command_free_payload to silc_command_payload_free.
+         Affected file lib/silccore/silccommand.h
+
+       * Added silcmath.h to include the prototoypes of various routines
+         in the lib/silcmath.  Removed the old modinv.h, mpbin.h and
+         silcprimegen.h.
+
+       * Changed the following headers to ROBODoc format:
+
+               lib/silccore/silcchannel.h
+               lib/silccore/silccommand.h
+               lib/silccore/silcid.h
+               lib/silccore/silcidcache.h
+               lib/silccore/silcmode.h
+               lib/silccore/silcnotify.h
+               lib/silccore/silcpacket.h
+               lib/silcmath/silcmath.h
+
+Fri Jun  1 22:19:37 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added checking to the server code not to start the server if
+         ciphers and stuff are not configured properly.  Affected files
+         silcd/serverconfig.[h] and silcd/server.c.
+
+       * Changed the layout of the header files of the public interfaces
+         in the SILC libraries.  The new layout supports ROBODoc 
+         documentation tool (and some others) so that it is easy to create
+         a library reference manual.  All the other headers and source
+         code must still follow the CodingStyle document.  Also source
+         code must not include these ROBODoc stuffs, only the headers.
+         Furthermore, all public interface headers must now be named
+         by using `silc' prefix, example: silcapi.h, silccipher.h.
+         Some files were renamed due to this.  All the other headers
+         must not be used as public interfaces.  I will update the
+         CodingStyle document later.  Changed following headers, so far:
+
+               lib/silcclient/silcapi.h
+               lib/silccore/silcauth.h
+               lib/silccore/silcprivate.h
+               lib/silccrypt/silcdh.h
+
+Fri Jun  1 10:28:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Updated TODO.
+
+       * Removed silc_client_packet_send_flush from the client library
+         as it is not needed.  Affected file lib/silcclient/client.[ch].
+
+       * Added printing of message of unresolved authentication method
+         to the Irssi SILC client.  Added it to the module formats.
+         Removed the same message from the client library.
+
+Thu May 31 13:57:33 CEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new distribution feature, DISTLABEL.  Every distribution
+         can define own preprocessor label that can be used in the
+         source code.  For example: #ifdef SILC_DIST_CLIENT.  Affected
+         file distributions, acconfig.h.pre and prepare.
+
+Tue May 29 22:16:40 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added Makefile.defines_int to include the actual definitions
+         for Makefile.defines.in.  Tested the new distribution system,
+         created distributions and tested installation.
+
+       * Added AWAY message printing to the Irssi SILC client.  Added
+         the messages to the irssi/src/fe-common/silc/module-formats.[ch].
+
+       * Added SCONNECT command to call the SILC's CONNECT command.
+         Cannot use CONNECT directly since Irssi uses that internally.
+         Affected file irssi/src/silc/core/silc-servers.c.
+
+         Added ACTION local command.  It is same as ME command but takes
+         the channel as mandatory argument.
+
+         Rewrote some of the Irssi's help files to suite for SILC
+         protocol.
+
+Mon May 28 19:05:22 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added Makefile.defines[.in] that should for now on be included
+         in all Makefile.am file in the source tree.  That file includes
+         all common compilation definitions for SILC source tree.
+
+Mon May 28 10:30:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Minor changes to the ./prepare script to change the package
+         name according the distribution name to the configure.in.
+
+Sun May 27 22:24:57 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Created new distribution system.  Added file `distributions'
+         that defines all the distributions that can be created out of
+         the SILC source tree.  The ./prepare script now reads that
+         file to determine how to prepare the distributions.  The
+         first argument to the ./prepare is the name of the distribution
+         and second is the version of the distribution.  If given
+         without arguments it creates the default (toolkit) distribution
+         with the default version (defined in ./prepare).
+
+         All Makefile.am files that are subject to the distributions
+         are now named as Makefile.am.pre.  These are ./Makefile.am
+         and lib/Makefile.am.  Others may be changed later.
+
+Sun May 27 15:57:17 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added invite list, ban list, some key management and connection
+         error message printing to module formats in the Irssi SILC client.
+
+       * Added new silc_client_set_away_message to set the away message
+         that is back to the person who sent private message.  The 
+         affected file lib/silcclient/silcapi.h and the
+         lib/silcclient/client_prvmsg.c.
+
+Sun May 27 12:39:48 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the private message sending in the Irssi SILC client,
+         added local command KEY to the Irssi SILC client.
+
+         Added key management and key agreement message formats to the
+         irssi/src/fe-common/silc/module-formats.[ch].
+
+         Added USERS (alias WHO) printing, server/router operator
+         indication and LIST command printing to the module formats.
+
+Sat May 26 17:43:42 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed channel joining notify handling, cumode notify handling
+         from Irssi SILC client.
+
+       * Added SILC specific module-formats to the Irssi SILC client so
+         that SILC specific message hilighting, colors etc is possible.
+         Affected file irssi/src/fe-common/silc/module-formats.[ch].
+
+         Added channel mode, channel user mode, actions, notices,
+         whois and whowas printing to the the module-formats.c.
+
+       * Fixed a bug in channel deletion in the server.  The channel
+         is not left to the cache even if the channel founder auth mode
+         is set when there are no users anymore on the channel.  Affected
+         file silcd/server.c.
+
+       * The silc_net_localhost now resolves the entire hostname including
+         the domain name.  Affected file lib/silcutil/silcnet.c.
+
+Sat May 26 12:13:37 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the ask_passphrase client operation to be ascynchronous.
+         It has now a completion callback and a context that the 
+         application must call after it has got the passphrase from
+         the user.  Affected files lib/silcclient/silcapi.h,
+         lib/silcclient/protocol.c, lib/silcclient/command.c and
+         silc/client_ops.c.
+
+         Added SilcAskPassphrase callback that the application calls
+         to deliver the passphrase to the library.
+
+       * Changed the SKE protocol's SilcSKEVerifyCb to be asynchronous.
+         The public key verification and especially a certificate
+         verification is asynchronous procedure.
+
+         Added new SILC_SKE_STATUS_PENDING status to indicate the
+         request is pending and a callback will be called to finalize
+         the request.
+
+         Added also SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED status to
+         indicate that remote end did not send its public key (or
+         certificate), even though we require it.  Added check for this
+         condition in the SKE.  This was a security bug, now fixed.
+
+         Defined new SilcSKEVerifyCbCompletion callback that is called
+         when the verification process is completed.
+
+         The affected files lib/silcske/silcske_status.h and
+         lib/silcske/silcske.[ch].
+
+       * Changed the verify_public_key client operation to be async
+         as well.  Defined SilcVerifyPublicKey callback that is used to
+         indicate the success of the public key verification process.
+
+         Changed the server and client to use the new async client 
+         operations.
+
+       * Changed the Irssi SILC client's internal scheduler to be called
+         twice as many times as it used to be.  As a result the client
+         should be a bit faster now.  Affected file is
+         irssi/src/silc/core/silc-core.c.
+
+       * Added support to Irssi SILC client of asynchronous public key
+         verification and passphrase inquiry.  Affected file is
+         irssi/src/silc/core/silc-core.c.
+
+Fri May 25 14:38:38 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Do not say "You have left channel %s" in client library.
+         Moved it to the application.  Affected files are
+         lib/silcclient/command.c and silc/client_ops.c.
+
+       * Fixed silc_client_get_clients.  Command context was not
+         duplicated and was freed memory in the callback.  Affected
+         file lib/silcclient/idlist.c.
+
+       * Do not say "you are now talking..." on JOIN command in the
+         client library.  The appliation must handle it.
+
+       * Do not say ".. changed topic to" in command reply in the
+         client libary.  The application must handle it.
+
+       * Fixed TOPIC command sending in the client library.
+
+       * Fixed a memory leak in silc_client_command_free in the file
+         lib/silcclient/command.c.
+
+Thu May 24 19:08:55 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Imported a modified version of Irssi client to the source tree.
+         The Irssi will be used to create a new client called
+         Irssi SILC.  Imported to irssi/.
+
+         Added silc_core_init_finish function to the Irssi.  Affected
+         file irssi/configure.in.
+
+         A lot changes in the Makefile.ams around the irssi tree.
+
+Tue May 22 22:23:49 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Do not rehash if the new size is same as the old size of the
+         hash table, in the silc_hash_table_rehash*.  The affected file
+         lib/silcutil/silchashtable.c.
+
+       * Replaced hash_table_del_by_context calls from the server
+         (when channel->user_list and client->channels) to the
+         hash_table_del as it is sufficient and faster.
+
+Tue May 22 17:27:16 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_hash_table_list, silc_hash_table_get and the
+         SilcHashTableList structure to provide an alternative way to
+         traverse the hash table.  The affected files are
+         lib/silcutil/silchashtable.[ch].
+
+       * Changed the server's idlist routines to use the hash table
+         routines to optimize the code.
+
+Mon May 21 21:46:20 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Replaced the client entry's `channel' list and channel entry's
+         `user_list' list to hash tables for optimized lookup.  Changed
+         the code to use the hash table interface around the code. 
+         Affected file lib/silcd/idlist.[ch].
+
+       * Added `auto_rehash' boolean argument to the function
+         silc_hash_table_alloc to indicate whether the hash table should
+         auto-rehash when it thinks is appropriate time.  It will
+         increase the hash table size if the there is twice as much
+         entries in the table than the size of the table, and will
+         decrease the size if there are twice as less entries than
+         the size of the table.
+
+Mon May 21 09:51:11 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed silc_xxx_get_supported to not crash at some circumstances.
+
+Sun May 20 13:45:58 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * silc_idcache_purge_by_context deletes the entry now by context
+         as it is supposed to do.  Affected file lib/silccore/idcache.c.
+
+       * Send the ERR_NO_SUCH_NICK in the WHOIS command reply if the
+         client is not anymore valid (WHOWAS givens the info) and not
+         the ERR_NO_SUCH_CLIENT_ID if the nickname still exists.
+
+Sat May 19 16:30:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Removed the `data' and `data_len' arguments from the ID Cache
+         interfaces and added `name' argument.  ID Cache does not handle
+         anymore the binary data only a names associated with given ID.
+
+       * When hashing a Client ID with silc_hash_id the entire ID is
+         not hashed anymore, instead only the hash of the Client ID is
+         hashed.  This way we can access the Client ID from the cache
+         with Client ID but with the hash of the ID (which is a hash of
+         the nickname) as well without any difference in performance.
+
+         Added also silc_idcache_find_by_id_one_ext to do one on one 
+         searching when we have the actual ID.  Added also function
+         silc_hash_client_id_compare.  The affected files are
+         lib/silccore/idcache.[ch] and lib/silcutil/silcutil.[ch].
+
+       * When hashing the name associated with a ID it is always done
+         in lowercase.  This way we can access the cache without worrying
+         about case-sensitivity, even though, for example nicknames are
+         case sensitive.
+
+       * Fixed a bug in server with channel message sending.  It put
+         wrong ID type as destination ID.  The affected file 
+         silcd/packet_send.c.
+
+       * silc_idcache_del_by_context now deletes from all hash tables
+         by context.  Affected file lib/silccore/idcache.c.
+
+Fri May 18 17:42:00 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the client library to use the new ID Cache interface.
+         Changes around the source tree.
+
+       * Added silc_hash_table_rehash_ext to rehash with specific
+         hash function.  Affected file lib/silcutil/silchashtable.[ch].
+
+       * Added silc_hash_string_compare to compare two strings in the
+         hash table.  Affected file lib/silcutil/silcutil.[ch].
+
+Fri May 18 11:18:45 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new function silc_idcache_del_by_context into the
+         lib/silccore/idcache.[ch].
+
+       * Changed the server's ID list routines to use the new ID Cache
+         interface.  Changes around the source tree.
+
+Fri May 18 08:35:31 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_hash_table_del[_by_context]_ext functions in to the
+         lib/silcutil/silchashtable.[ch].
+
+         Removed silc_hash_table_find_all* routines and added new
+         silc_hash_table_find_foreach to replace them.
+
+         Added silc_hash_table_replace_ext function as extended
+         replacing function.  Separated the simple hash table interface
+         from the extended hash table interface in the file
+         lib/silcutil/silchashtable.h.
+
+       * Fixed minor bugs and changed it to use some of the new
+         hash table functions in lib/silccore/idcache.c
+
+Thu May 17 18:15:12 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new function silc_hash_table_find_all to return all keys
+         in the hash table by the specified key.  As the hash table is
+         collision resistant it also makes it possible to have several
+         duplicate keys in the hash table.  This function may be used to
+         find all of the keys from the hash.
+
+         Added user_context arguments to the SilcHashFunction,
+         SilcHashCompare and SilcHashDestructor to deliver user specified
+         context.
+
+         Added new fuctions silc_hash_table_find[_all]_ext to do
+         extended lookup with specified hash and compare functions and
+         specified user contexts.
+
+         Added new function silc_hash_table_add_ext to add the key
+         with specified hash function and user context.
+
+         Added new function silc_hash_table_foreach to traverse all
+         entrys in the hash table.  Added SilcHashForeach callback
+         function.
+
+         Added new function silc_hash_table_del_by_context to delete
+         the entry only if the context associated with the key matches.
+
+         Affected files are lib/silcutil/silchashtable.[ch].
+
+       * Removed silc_hash_[server/client/channel]_id and added just
+         silc_hash_id to the lib/silcutil/silcutil.[ch].  Added also
+         silc_hash_id_compare to compare two ID's using as the hash table
+         comparison function.  Added also silc_hash_data to hash
+         binary data and silc_hash_data_compare to compare it.
+
+       * Removed silc_idlist_find_client_by_hash as it is not needed
+         anymore.  Affected file silcd/idlist.[ch].
+
+       * Rewrote the entire ID Cache system (in lib/silccore/idcache.[ch])
+         to use internally the SilcHashTable.  The new ID Cache is a lot
+         faster than the old one.  Some of the ID Cache interface was also
+         rewritten and obsolete and stupid functions were removed.
+
+Wed May 16 23:03:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added entry_count field to the SilcHashTable to keep the number
+         of the entries in the table.  Implemented the function
+         silc_hash_table_rehash.  Added new function
+         silc_hash_table_count.  Affected file lib/silcutil/silchashtable.c.
+
+         Fixed a minor bug in silc_hash_table_free.
+
+       * Added silc_hash_string, silc_hash_uint, silc_hash_ptr,
+         silc_hash_client_id, silc_hash_server_id and silc_hash_channel_id
+         into the lib/silcutil/silcutil.[ch].
+
+Wed May 16 20:02:47 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented a collision resistant hash table into the
+         lib/silcutil/silchashtable[ch].  See the header and the source
+         for the SilcHashTable API.
+
+Tue May 15 22:05:46 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Merged dotconf version 1.0.2 into lib/dotconf.
+
+Sun May 13 19:32:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Do not compile anything in lib/silcsim/* if the SIM support
+         is not enabled.  The tree should now compile without problems
+         under cygwin.
+
+Thu May 10 22:49:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Compiled the SILC under cygwin.  Compiled and tested briefly
+         without problems.  More tests needed.  The SIMs didn't compile
+         though.
+
+       * Added various #ifdef HAVE_* stuff to lib/silccrypt/silrng.c.
+
+       * Fixed possible crash in silc_get_username in the
+         lib/silcutil/silcutil.c.
+
+Tue May  8 09:04:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a va_arg in silc/client_ops.c.
+
+       * Oops, RC5 routines were named AES and caused some problems
+         when not using SIM's.  Affected file lib/silccrypt/rc5.c.
+
+Sun May  6 13:59:48 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new SilcIDIP structure into the lib/silccore/id.h and
+         replaced the old `ip' fields from all SILC ID's to that type.
+         This is a step towards IPv6 support.
+
+         The silc_id_get_len takes now the ID as an extra argument.
+         The silc_id_id2str, silc_id_str2id and silc_id_dup now supports
+         both IPv4 and IPv6 based ID's.
+
+         The affected files are lib/silccore/id.[ch] and other files
+         around the tree using these routines.
+
+       * Removed the ID length arguments in server from various 
+         silc_server_send_notify_* routines -> they are not needed 
+         anymore.
+
+Sat May  5 13:56:33 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed memory leak in silc_encode_pem_file in the file
+         lib/silcutil/silcutil.c.
+
+Thu May  3 21:23:50 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Check minor version as well in the SKE.  Affected files are
+         silcd/protocol.c and lib/silcclient/protocol.c.
+
+       * Added --identifier option to the server so that an identifier
+         can be when creating the public key for the server.  Affected
+         file is silcd/silcd.c.
+
+       * Fixed minor decoding bug in silc_pkcs_decode_identifier in
+         lib/silccrypt/silcpkcs.c.
+
+Wed May  2 20:50:49 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Register default ciphers and stuff when using -C option with
+         the server.  Affected file sildc/silcd.c.
+
+       * Put back the servers public key filename format, it is better
+         than the new one.  For now, the client keys are saved with the
+         new filename format.  The affected file silc/client_ops.c.
+
+       * Implemented the Cipher API for the rest of the ciphers that
+         did not implement it or implemented it the wrong way.
+
+Wed May  2 13:31:26 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Register default ciphers and stuff when using the -S option
+         in the client.  Affected file silc/silc.c.  Same also when
+         creating new key pair with -C option.
+
+Tue May  1 14:18:13 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the silc_verify_public_key client operation function to
+         save the public keys differently.  The fingerprint is now 
+         used as filename and not the hostname.  This way also the
+         client keys are saved uniquely and not with hostnames.  The
+         affected file is silc/client_ops.c.
+
+       * Trimmed the silc_hash_fingerprint function to remove extra
+         whitespaces from the end of the fingerprint.  The affected
+         file is lib/silccrypt/silchash.c.
+
+       * Updated TODO.
+
+       * Added silc_cipher_register_default function to register all
+         default ciphers.  It can be used when configuration files
+         does not exist and the application does not want any specific
+         ciphers in any specific order.
+
+         The SilcDList is now used as silc_cipher_list dynamically
+         allocated cipher list.  Removed the static list all together
+         and now all ciphers must be allocated to the dynamic list.
+         The silc_cipher_alloc routine was changed to check only the
+         dynamic list.
+
+         All silc_cipher_* routines that used to return int returns
+         now bool.
+
+         The affected files lib/silccrypt/silccrypt.[ch].
+
+       * The same thing was done to silc_hash_* as for silc_cipher_*
+         routines.  Affected files lib/silccrypt/silchash.[ch].
+
+       * The same thing was done to silc_pkcs_* as for silc_cipher_*
+         routines.  Affected files lib/silccrypt/silcpkcs.[ch].
+         Added also silc_pkcs_[un]register[_default] functions.
+         Removed the data_context from the PKCS API.
+
+       * Added silc_hmac_register_default function to register default
+         hmacs.  Affected files lib/silccrypt/silchmac.[ch].  Added also
+         SILC_ALL_HMACS macro that can be used with silc_hmac_unregister
+         to unregister all hmacs at once.
+
+       * Register the default ciphers, hash functions, PKCSs and HMACs
+         if client's configuration file does not exist.  The affected
+         file silc/silc.c.
+
+       * The client did not load the hash functions from the SIM
+         modules at all.  Added support for this.  Affected file is
+         silc/clientconfig.c.
+
+       * When decoding public key with silc_pkcs_public_key_decode, check
+         the supported algorithm only if PKCS are registered.  Affected
+         file lib/silccrypt/silcpkcs.c.  The same was done with the
+         silc_pkcs_private_key_decode.
+
+       * Fixed the SILC List routines to keep the list always in order.
+         It used to change the list's order when traversing the list but
+         not it preserves the order.  Affected file lib/trq/silclist.h.
+
+Mon Apr 30 17:29:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added the client library to use the SilcSocketConnection's
+         reference counter (by silc_socket_dup) to prevent the bug that
+         the socket object may be freed underneath async operation.
+
+       * The name resolv library checking fixes in the configure.in.pre.
+         The patch by salo.
+
+       * Created new version of the protocol drafts for future
+         development. The -03 drafts are the ones that will be changed
+         in the trunk now and the -02 will remain as they are.
+
+       * Send list of CUMODE notifys to the router when announcing
+         the channel users to the router.  Affected file silcd/server.c.
+         If the router receiving channel founder CUMODE for a channel
+         that already has channel founder it will send CUMODE notify
+         to the sender to remove the channel founder rights from the
+         announced client.  Affected file silcd/packet_receive.c.
+
+       * The CUMODE notify may now use Server ID as well as the entity
+         who changes the mode.  Updated protocool specs.
+
+       * Updated INSTALL and README files.
+
+Sun Apr 29 23:17:50 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * New web pages in the http://silc.pspt.fi.  The pages was
+         designed by salo.
+
+       * Updated CREDITS.
+
+Sun Apr 29 13:33:41 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented the [DenyConnectin] config section in the server.
+         Added silc_server_config_denied_conn to check whether incoming
+         connection is denied.  Affected file silcd/serverconfig.[ch].
+
+       * Do not check the ports when checking the incoming configuration
+         data if the port is 0, meaning any.  Affected file is
+         silcd/serverconfig.c.
+
+Fri Apr 20 18:58:43 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed buffer overflow in silc_string_compare in the file
+         lib/silcutil/silcutil.c.
+
+       * Fixed double free in silc_server_command_leave in the file
+         silcd/command.c.
+
+Fri Apr 20 14:00:11 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the version checking in the server.  Affected file is
+         silcd/protocol.c.
+
+Thu Apr 19 19:52:46 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the configuration data fetching when accepting new
+         connections in the server.  Affected file silcd/server.c.
+
+Thu Apr 19 11:40:20 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added `sender_entry' argument to the function
+         silc_server_packet_relay_to_channel so that we can check
+         whether some destination actually belongs to the same route
+         the sender belongs (ie, we must not resend the packet to the
+         sender).  Affected file silcd/packet_send.[ch].
+
+       * Added `servername' field to the SilcClientEntry in the server
+         to hold the name of the server where client is from.  Affected
+         file is silcd/idlist.h.
+
+Wed Apr 18 22:19:03 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Moved the channel message encrypting in the router betwen
+         router connections from silc_server_channel_message to the
+         silc_server_packet_relay_to_channel since we want to check
+         whether we have anybody channel before encrypting anything.
+         Affected files silcd/packet_[receive/send].c.
+
+Tue Apr 17 21:18:19 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the [AdminConnection] server config section to support
+         multiple entries.  Affected file silcd/serverconfig.c.
+
+       * Added support into the server to check the validity of the
+         incoming connection before executing any KE or authentication
+         protocols.
+
+       * The connection configuration is now saved to the KE and 
+         connection auth protocol contexts and not fetched anymore in 
+         the protocol.  Affected files silcd/server.c, silcd/protocol.[ch].
+
+       * The local hosts listenning address and port is also resolved
+         now when starting the server.  We want to have the socket object
+         to include the real address and port for the listener.  Added
+         new function silc_net_check_local_by_sock into the files
+         lib/silcutil/silcnet.[ch].
+
+       * Fixed a broadcast bug in server -> do not broadcast if we
+         are standalone.
+
+       * Fixed a routing bug.  Do not route broadcast packets ever.
+         Broadcast packets must be processed always and not routed since
+         they may be destined to some other host than yourself and thus
+         would get routed without no good reason.  Affected file is
+         silcd/server.c.
+
+       * Added function silc_server_config_is_primary_route to check
+         whether primary router connection has been configured (a router
+         configuration that we are initiating).  If there is not, we 
+         will assume that there is only two routers in the SILC network
+         and we will use the incoming router connection as our primary
+         route.  Affected files silcd/serverconfig.[ch], silcd/server.c.
+
+       * Changed the order of the broadcasting.  Broadcast _after_ the
+         packet has been processed not before.  Affected file is
+         silcd/server.c.
+
+       * Fixed a [ClientConnection] parsing bug.  The port was never
+         parsed correctly thus resulting to port 0.  Affected file
+         silcd/serverconfig.c.
+
+       * Fixed silc_server_send_notify_args -> it ignored the `broadcast'
+         argument and did not set the broadcast packet flag.  Affected
+         file silcd/packet_send.c.  Fixed same bug in the function
+         silc_server_send_notify as well.
+
+       * If we receive NEW_ID packet for our own ID in the server, ignore
+         the packet.
+
+Mon Apr 16 12:10:33 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Updated TODO.
+
+       * Removed the nickname from the Private Message Payload.
+         Updated the code and the protocol specs.
+
+       * Updated protocol specs for submitting to the IETF.
+
+       * Tweaked the Random Number Generator a bit.  Affected file
+         lib/silccrypt/silcrng.c.  Exported a new function
+         silc_rng_[global]_add_noise which can be used to add more
+         noise to the RNG.
+
+Sat Apr 14 16:21:32 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Do not parse packets with different timeout when protocol
+         is active -> may cause problem with rekey.  Affected file
+         silcd/server.c.
+
+       * When server receives signoff notify it must not create
+         new channel key if the client is on any channels since the
+         sender of the signoff notify will create it.
+
+Fri Apr 13 17:12:46 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added printing of error messages during SKE protocol from the
+         failure packet sent by server during SKE.  Affected file
+         silc/client_ops.c.
+
+       * Removed the client's failure_callback handling with timeout
+         and handle it immediately when received.
+
+       * The SKE library returned wrong type in SUCCESS and FAILURE 
+         packets.  They must be 32 bit MSB not 16 bit MSB.
+
+Fri Apr 13 00:09:08 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Ok, rewrote the logic of the re-key and now it seems to work.
+         I tested it on high traffic with frequent re-keys without
+         problems.  Added hmac_receive (and renamed hmac to hmac_send)
+         in SilcClientConnection in lib/silcclient/client.h and
+         in SilcIDListData in silcd/idlist.h.  Also, removed the
+         SilcPacketParserContext's cipher and hmac fields as they are
+         not needed anymore and actually caused some problems when
+         the ciphers and hmac's changed underneath the packet parser.
+
+Thu Apr 12 14:42:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * If re-key protocol is active then process the incoming packets
+         synchronously since we must assure that icoming packets encrypted
+         with the old key is processed before the new keys is set to
+         use.  This is true other packets than for REKEY packets.
+         Affected file silcd/server.c.  The same was done to client library
+         as well, affected file lib/silcclient/client.c.
+
+Thu Apr 12 12:01:52 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed bug in client and server to accept the force send if
+         the packet is send from silc_[server/client]_packet_process
+         function.  Otherwise the packets are never delivered, oops.
+
+Wed Apr 11 22:10:15 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Disable force sending of packets when REKEY protocol is active.
+         We must assure that no packet is sent directly when rekey is
+         performed.  All packets must be sent through packet queue.
+         Added macro SILC_SERVER_IS_REKEY to silcd/server.h and
+         SILC_CLIENT_IS_REKEY to lib/silcclient/client.h.  Affected
+         function is silc_[server/client]_packet_send_real to check
+         the situation.
+
+       * Replaced the SIM paths from example config files to 
+         /usr/local/modules.  Also, make install creates now
+         /usr/local/silc/logs directory to hold all the SILC server
+         logs.
+
+Wed Apr 11 16:59:59 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Made the configure.in.pre work on Solaris.  Patch by salo.
+
+       * Made all ciphers compatible with non-x86 machines.  Defined
+         CBC mode macros into lib/silccrypt/ciphers_def.h.
+
+Tue Apr 10 20:32:44 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the make install.
+
+Tue Apr 10 16:20:34 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * When MAC computation fails the silc_packet_decrypt returned 0
+         even though it was supposed to return -1.  Fixed this.  The
+         affected file is lib/silccore/silcpacket.c.
+
+       * Do not replace the config files in /etc/silc (in make install)
+         if they already exist.  Affected file ./Makefile.am.
+
+       * Do not send re-key packets immediately but through packet queue.
+         Affected file silcd/protocol.c and lib/silcclient/protocol.c.
+
+       * Changed silc_net_check_host_by_sock to return FALSE if the
+         IP/DNS could not be resolved.  Though, it returns the IP address
+         now even if it could not resolve it (but returns also FALSE).
+         Affected file lib/silcutil/silcnet.[ch].
+
+Mon Apr  9 21:54:44 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_pkcs_decode_identifier to decode the public key's
+         identifier.  Affected file lib/silccrypt/silpkcs.[ch].
+         Added also silc_pkcs_free_identifier.  Added also new context
+         SilcPublicKeyIdentifier.
+
+       * Added -S option to the silc client.  It is used to dump the
+         contents of the specified public key file.
+
+       * Changed the PKCS api to return the public key length when
+         setting the public key.
+
+       * Fixed a fatal bug in the public and private key file loading.
+         Affected file lib/silccrypt/silcpkcs.c.
+
+       * Execute the packet parsing for client with zero (0) timeout
+         if the protocol is active.  Affected file silcd/server.c.
+
+Sun Apr  8 19:30:56 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Made the key generation options to the silcd program.  Added
+         -C option, equivalent to client's option.
+
+       * Added new [ServerKeys] config section to the server.  It
+         configures the server's public and private key.
+
+       * Defined generic Public Key Payload into the protocol
+         specification to send specific type of public keys and
+         certificates.
+
+       * Defined new command SILC_COMMAND_GETKEY to fetch a client's
+         public key or certificate.
+
+       * Implemented the GETKEY command to the server and to the
+         client library and on user interface.
+
+Sun Apr  8 01:37:21 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Made preliminary `make install' work.
+
+Thu Apr  5 17:42:30 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SilcServerRekey context into silcd/idlist.h.
+
+       * Added the PFS support as defined in the specification to the
+         SKE protocol.  Affected files lib/silcske/*.c.
+
+       * Added `ske_group' field to the SilcServerRekey context to hold
+         the number of the SKE group that is used with PFS in re-key.
+         Affected file silcd/idlist.h.
+
+       * Added PFS re-key support to the server.  Affected file is
+         silcd/protocol.c.
+
+       * Added silc_protocol_cancel to cancel execution of the next
+         state of the protocol.  Affected file is
+         lib/silccore/silcprotocol.[ch].
+
+       * Added the re-key support with and without PFS to the client
+         library.  Re-key is performed once in an hour, by default.
+
+         Added new protocol type SILC_PROTOCOL_CLIENT_REKEY.
+         Added silc_client_rekey_callback and silc_client_rekey_final.
+         Affected files are lib/silcclient/protocol.[ch] and
+         lib/silcclient/client.[ch].
+
+       * Removed the `hmac_key' and `hmac_key_len' fields from the
+         SilcClientConnection structure; not needed.  Affected file is
+         lib/silcclient/client.h.
+
+       * Updated TODO.
+
+Wed Apr  4 16:32:31 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Do not ask whether user wants to use the negotiated private key
+         for private messages, just use it.  Affected file is 
+         silc/local_command.c.
+
+       * Added `send_enc_key' and `enc_key_len' fields to the 
+         SilcIDListData structure since they are needed in the re-key
+         phase.  Affected file is silcd/idlist.[ch].
+
+       * Implemented the simple re-key protocol into the server.
+         Affected files silcd/server.c and silcd/protocol.[ch].  The
+         re-key will be performed once in an hour, by default.
+
+         Added new protocol type SILC_PROTOCOL_SERVER_REKEY.
+         Added silc_server_rekey, silc_server_rekey_callback and
+         silc_server_rekey_final.
+
+       * Removed Tunneled flag from the protocol.  Updated the code
+         and the specifications.
+
+       * Adde `pfs' field to the SilcIDListData to indicate whether
+         the PFS is to be performed in the re-key.  Affected file is
+         silcd/idlist.h.
+
+Tue Apr  3 21:52:42 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Defined uint8, int8, uint16, int16, uint32, int32, uint64 and
+         int64 of at least the xintXX size.  If void * is less that 4
+         bytes uint32 * will be used.  Defined bool as boolean.
+
+       * Changed _ALL_ unsigned long and unsigned int to uint32, 
+         unsgined short to uint16 in the source tree.
+
+       * Fixed a fatal bug in silc_server_remove_clients_by_server.  Do
+         not handle clients that has entry->data.registered == FALSE.
+         They are not in the network anymore.  Affected file is
+         silcd/server.c.
+
+Tue Apr  3 16:39:19 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented the sending of the SERVER_SIGNOFF notify in the
+         server.  Affected file is silcd/server.c.
+
+       * Added silc_server_send_notify_args into silcd/packet_send.[ch].
+         Added also silc_notify_payload_encode_args into the
+         lib/silccore/silcnotify.[ch].
+
+       * Implemented ther SERVER_SIGNOFF notify handling in the server.
+         Affected file silcd/packet_receive.c.
+
+       * Implemented the SERVER_SIGNOFF notify handling in the client
+         library.  Affected file lib/silcclient/client_notify.c.  Also,
+         implemnted the printing of the SERVER_SIGNOFF info to the
+         application.  Affected file silc/client_ops.c.
+
+       * The silc_idlist_del_server now returns TRUE or FALSE to indicate
+         if the deleting was successful.  Affected file silcd/idlist.[ch].
+
+       * Added support for public key authentication in the connection
+         authentication protocol in the client library.  Affected file
+         lib/silcclient/protocol.c.
+
+       * Changed the server's silc_idlist_get_clients_by_* interface
+         to support already allocated array so that new entries may be
+         added to pre-allocated array.  Affected file silcd/idlist.[ch].
+         This fixes some bugs with WHOIS, WHOWAS and IDENTIFY commands
+         and command replies.
+
+       * All command reply functions in the server now calls the 
+         pending command callback even if error occured.  This way the
+         error will be delivered to the client as well.  Affected files
+         silcd/command.c and silcd/command_reply.c.
+
+       * Fixed INFO command to return local server's info if no server
+         was provided.  Affected file lib/silcclient/command.c.
+
+       * Removed RESTART command for good.  Updated the code and the
+         protocol specs.
+
+       * Rewrote parts of the task system.  It is a bit simpler now.
+         Removed unsued task priorities. The affected files are
+         lib/silcutil/silctask.[ch].
+
+Mon Apr  2 20:02:33 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Moved the USERS printing from the library to the application.
+         Affected files lib/silcclient/command.c and silc/client_ops.c.
+
+Mon Apr  2 13:13:23 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Updated TODO.
+
+       * Added channel key re-key support.  The re-key is perfomed
+         only by the router and is done once in an hour.  Added `rekey'
+         field to the SilcChannelEntry in the server.  Affected files
+         silcd/server.c and silcd/idlist.h.
+
+       * Added silc_task_unregister_by_context into the file
+         lib/silcutil/silctask.[ch].
+
+Sun Apr  1 19:49:34 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SILC_UMODE_GONE mode to indicate when the client is not
+         present in the SILC network.  Added also support to the local
+         command AWAY that will set this mode.  Added support of showing
+         "xxx is gone" in WHOIS command.  The USERS command shows the
+         gone status as well.
+
+       * Fixed setting server and router operator privileges in the
+         server's UMODE command.  Affected file silcd/command.c.
+
+       * Merged the SKE KE1 and KE2 payloads into one payload.  The
+         new KE payload is equivalent to the old KE2 payload.
+
+         Cleaned up the SKE Start Payload parsing.  It now uses the
+         simple buffer unformatting to do the parsing.  A lot faster
+         now.
+
+         Added new Mutual Authentication flag (SILC_SKE_SP_FLAG_MUTUAL)
+         to the SKE that is used to indicate whether both of the SKE
+         parties should perform authentication.  By default only the
+         responder performs authentication.  By setting this flag also
+         the initiator must do authentication.  By default it is unset
+         since in normal SKE case, client to server connection, only
+         the responder should do authentication.  When doing SKE between
+         two clients both should perform authentication.  Updated the
+         code and the protocol specs.
+
+       * A little fix to IDENTIFY command in the server.  Search the
+         client first by hash not nickname.  Affected file is 
+         silcd/command.c.
+
+       * Fixed the silc_client_close_connection to support closing
+         the client to client connections wihtout deleting too much
+         data.  Affected file lib/silcclient/client.c.
+
+       * Fixed a fatal bug in server and client; if KE1 or KE2 packets
+         are received if protocol used to be active but is not anymore
+         the application would crash due to NULL pointer dereference.
+         Affected files silcd/server.c and lib/silcclient/client.c.
+
+       * Added `hash' field to the SilcClientConnection to include
+         the hash function negotiated in the SKE protocol.
+
+       * Added new channel mode SILC_CMODE_FOUNDER_AUTH that is used
+         to set the channel founder authentication data.  A client can
+         claim the founder rights later by providing the authentication
+         data to the CUMODE command using SILC_CUMODE_FOUNDER mode.
+         This way the channel founder can regain the channel founder
+         privileges even it is left the channel.  This works only on
+         local server and the client must be connected to the same
+         server to be able to regain the founder rights.  Updated the
+         protocol specs accordingly.
+
+         Added support to the CMODE command in the client to set the
+         founder auth data.  Read the README to see how to set it.
+
+         Added support to the CUMODE command to claim the founder
+         rights.  Read the README to see how to do it.
+
+         Added support for the founder authentication to the Channel
+         Entry in the server.  Affected file silcd/idlist.h.
+
+         Added support for the SILC_CMODE_FOUNDER_AUTH mode in the
+         server's CMODE command.  Affected file silcd/command.c.
+
+       * Added the following new functions into lib/silccore/silcauth.[ch]:
+         silc_auth_get_method and silc_auth_get_data.    
+
+       * The server now saves the remote hosts public key to the
+         SilcIDListData pointer.  Affected file silcd/protocol.c.
+
+       * The normal server now does not remove the channel entry from
+         the cache if the founder authentication data is set.  It used
+         to remove it if the founder was the last one on the channel on 
+         the server and left the channel.  The auth data is saved and
+         if the channel is re-joined later the old entry is used with
+         the old auth data.  Affected files silcd/command_reply.c and
+         silcd/server.c.
+
+       * Removed the `pkcs' field from the SilcIDListData structure
+         in the server; it is not used.  Affected file silcd/idlist.h.
+
+Sat Mar 31 15:38:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed packet processing on slow links.  Partial packets were
+         never re-processed because the incoming data buffer was cleared
+         by the application.  Application must not directly clear the
+         sock->inbuf, the packet processing routines handle it.  Fixed
+         this in client library and in server.
+
+Fri Mar 30 16:35:27 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the WHOIS and IDENTIFY send reply function to really
+         check whether to send list or just one entry.  Affected file
+         silcd/command.c.
+
+       * Cleaned up the LEAVE command's channel key distribution.  The
+         affected file silcd/command.c.
+
+       * Changed CMODE_CHANGE's <Client ID> to <ID Payload> as server
+         can enforce the channel mode as well.  In that case the ID
+         includes the ID of the server.  The code now enforces the
+         mode change if the router have different mode than the server.
+
+       * The notify client operation with CMODE_CHANGE notify can now
+         return NULL client_entry pointer if the CMODE was not changed
+         by client.  Application must check for this.
+
+       * Added <Server ID> argument to INFO command to support server
+         info fetching by Server ID.
+
+       * Added silc_server_announce_get_channel_users to get assembled
+         packets of channel users of the specified channel.  Affected
+         file silcd/server.[ch].
+
+       * Fixed bug in CHANNEL_CHANGE notify in the server.  The new ID
+         was freed underneath the ID Cache.
+
+       * Re-announce clients when the server received CHANNEL_CHANGE
+         notify from the router.  Affected file silcd/packet_send.c.
+
+Thu Mar 29 19:10:28 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a fatal bug when client does /join 1 2 3 4 5 6 the server
+         crashed since it did not handle the fact that there is no cipher
+         called "3" and didn't check the error condition.  Now fixed.
+
+       * Added SILC_MESSAGE_FLAG_REQUEST message flag as generic request
+         flag.  It can be used to send message requests.
+
+Thu Mar 29 12:26:25 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented the RESTART command in the client.
+
+       * Added SILC_MESSAGE_FLAG_NOTICE message flag for informational
+         notice type messages.  Added notice printing to the user
+         interface.
+
+       * The channel keys are not re-generated if the channel's mode
+         is PRIVKEY, ie private key on the channel exists.  Affected
+         files silcd/server.c and silcd/command.c.
+
+       * Fixed a little bug in channel message delivery when channel
+         private keys are set in the server.  Affected file is
+         silcd/packet_send.c.
+
+       * Changed the setting on channel->on_channel = TRUE from the
+         silc_client_save_channel_key to the JOIN command reply.  The
+         key payload is not received if the private channel key is set.
+         Affected file lib/silcclient/command_reply.c and the
+         lib/silcclient/client_channel.c.
+
+       * When the CMODE_CHANGE notify is sent and the channel private
+         key mode is removed the channel key must be re-generated in
+         other cells as well.  Added this support for the router in the
+         silcd/packet_receive.c.
+
+       * Added new local command NOTICE to send notice message on
+         channel.  Affected file silc/local_command.[ch].
+
+Wed Mar 28 23:55:54 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new local command ME to the client.  It is used to send
+         message to a channel with SILC_MESSAGE_FLAG_ACTION to indicate
+         some action.  Affected file silc/local_command.[ch].
+
+       * Changed channel_message and private_message client operations 
+         to deliver the message flags to the application.  Added also
+         the `flags' arguments to the silc_client_send_channel_message
+         and silc_client_send_private_message functions.  Affected file
+         silcapi.h.
+
+Wed Mar 28 20:50:47 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Redefined the Private Message Payload to support private message
+         keys and to support the new private message flags.  Updated
+         the protocol specs.  Flags makes it possible to have for example
+         CTCP style messages.
+
+       * Added new type SilcPrivateMessagePayload and defined an API
+         for it in the lib/silcclient/silcprivate.[ch].
+
+       * Tested private message private keys successfully.  Tested the
+         private message key set, unset and list commands with the new
+         KEY command.
+
+       * Redefined the Channel Message Payload to include the channel
+         message flags (equal with private message flags) to support
+         for example CTCP style messages.
+
+       * Defined some of the message (for channel and private message)
+         flags.  Updated the protocol specs and added the flags to the
+         lib/silccore/silcchannel.h.  The type is SilcMessageFlags.
+
+Wed Mar 28 15:52:36 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SilcKeyAgreementStatus type to the key agreement routines
+         to indicate the current status and error if one occured.
+         The status types are defined in the lib/silcclient/silcapi.h.
+
+       * Added new local command KEY that is used to set and unset private
+         keys for channels, set and unset private keys for private messages
+         with remote clients and to send key agreement requests and
+         negotiate the key agreement protocol with remote client.  The
+         key agreement is supported only to negotiate private message keys,
+         it currently cannot be used to negotiate private keys for channels,
+         as it is not convenient for that purpose.
+
+       * Fixed a minor pending callback setting bug in the function
+         silc_client_get_client_by_id_resolve, now the function works.
+         Affected file lib/silcclient/idlist.c.
+
+       * Added function silc_net_get_local_port to get local bound
+         port by socket.  Added to lib/silcutil/silcnet.[ch].
+
+       * Added `sockets' and `sockets_count' fields to the SilcClient
+         object.  They hold the sockets of the listenning sockets in
+         the client.  Listenning sockets may be for example the key 
+         agreement server.  Affected file lib/silcclient/client.[ch].
+         Added functions the silc_client_add_socket and the
+         silc_client_del_socket.  They are exported to the application
+         as well.
+
+       * Added ~./silc/clientkeys to support other client's public keys.
+
+       * Renamed verify_server_key client operation to verify_public_key
+         and added one argument to indicate the type of the connection
+         (server, client etc.).
+
+Tue Mar 27 22:22:38 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_connection_auth_request to handle the
+         incoming CONNECTION_AUTH_REQUEST packet.  Affected file is
+         silcd/packet_receive.[ch].
+
+       * Added silc_server_send_connection_auth_request into the
+         silcd/packet_send.c to send the connection auth request packet.
+
+       * Cleaned up the silcd/protocol.c a bit and fixed some memory
+         leaks.
+
+       * Fixed the public key authentication in responder side in the
+         server.  The `auth_data' pointer includes the SilcPublicKey
+         not the path to the public key.  Affected file silcd/protocol.c.
+
+       * Implemented the public key authentication in the initiator side
+         in the server.  Affected file silcd/protocol.c.
+
+       * Removed the [RedirectClient] config section from the server
+         configuration.  Is not needed and I don't want to implement it.
+
+Tue Mar 27 12:49:56 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Cleaned up the CMODE command in the server.  It now works
+         correctly and supports all the modes defined in the protocol.
+         Affected file is silcd/command.c.
+
+       * Added `hmac_name' field to the SilcChannelEntry in the server
+         to hold the default HMAC of the channel.  It can be set when
+         creating the channel (with JOIN command).  Affected files
+         silcd/idlist.[ch].
+
+       * Added <cipher> and <hmac> argument to the CMODE_CHANGE notify
+         type to indicate the change of the current cipher and hmac
+         on the channel.  Client can safely ignore the <cipher> argument
+         (if it chooses to do so) since the CHANNEL_KEY packet will 
+         force the channel key change anyway.  The <hmac> argument is
+         important since the client is responsible of setting the new
+         HMAC and the hmac key into use.
+
+       * Fixed the CMODE command in the client library as well.
+
+       * Tested CMODE command in router environment successfully.
+
+Mon Mar 26 14:39:48 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Show the version of the remote client (or server) when connecting
+         to the server.  It is logged to the log file.  Affected file
+         is silcd/protocol.c.
+
+       * Fixed the KILLED notify handling in the client library.  The
+         client must be removed from all channels when receiving the
+         KILLED notify.
+
+         Also, do not remove the client entry when giving the KILL 
+         command but when the KILLED notify is received.
+
+       * Removed silc_idlist_find_client_by_nickname from the server.
+         Not needed anymore.  Affected files silcd/idlist.[ch].
+
+       * Implemented the CHANNEL_CHANGE notify type handling to the
+         server.  Affected file silcd/server.c.
+
+       * Updated TODO.
+
+Mon Mar 26 12:11:14 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_send_notify_invite to send the INVITE
+         notify between routers.
+
+       * Implemented the INVITE command correctly to the server.
+
+       * Implemented the INVITE notify type handling in the server.
+
+       * Implemented the INVITE command to the client library and on the
+         user interface.
+
+Sun Mar 25 20:27:09 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added function silc_server_get_client_resolve to find the
+         client entry by ID from all ID lists and then resolve it
+         (using WHOIS) if it cannot be found.  Affected file is
+         silcd/server.[ch].
+
+Sun Mar 25 13:52:51 EEST 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented the BAN command to the client library.
+
+       * The JOIN command in the server now checks the invite list
+         and the ban list.
+
+       * Changed the silc_command_reply_payload_encode_va and the
+         silc_command_payload_encode_va to support that if argument is
+         NULL it ignores and checks the next argument.  Affected file
+         lib/silccore/silccommand.c.
+
+       * Added silc_server_send_notify_ban to send the BAN notify
+         type between routers.
+
+       * Chaned the silc_notify_payload_encode to support that if 
+         argument is NULL it ignores and checks the next argument.
+         Affected file lib/silccore/silcnotify.c.
+
+       * Tested ban lists in router environment successfully.
+
+Sat Mar 24 14:47:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented BAN command to the server, in silcd/command.[ch].
+
+       * Removed the BAN and INVITE_LIST modes from the CMODE command
+         in the server code.
+
+       * Added function silc_string_match to regex match two strings.
+         Affected files lib/silcutil/silcutil.[ch].
+
+Fri Mar 23 22:02:40 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Redefined parts of the SilcChannelEntry in the server to support
+         the new ban and invite lists.
+
+Fri Mar 23 16:25:11 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Redefined the INVITE command.  The same command can be used to
+         invite individuals to the channel but also to manage the invite
+         list of the channel (to add to and remove from the invite list).
+         Updated the protocol specs.
+
+       * Added new command SILC_COMMAND_BAN that can be used to manage
+         the ban list of the channel.  Updated the protocol specs.
+
+       * Removed the channel modes: the SILC_CMODE_BAN and the 
+         SILC_CMODE_INVITE_LIST as they were a bit kludge to be included
+         in the CMODE command.  The equivalent features are now available
+         using INVITE and BAN commands.  Updated the protocol specs.
+
+       * Added new SILC_NOTIFY_TYPE_BAN notify type to notify routers
+         in the network about change in the current ban list.  The notify
+         type is not used by the client.
+
+       * Redefined parts of the SILC_NOTIFY_TYPE_INVITE command to 
+         support the invite lists.
+
+Thu Mar 22 22:52:23 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new function silc_string_regexify that converts string
+         including wildcard characters into regex string that can
+         be used by the GNU regex library.  Added into the file
+         lib/silcutil/silcutil.[ch].
+
+         Added silc_string_regex_combine to combine to regex strings
+         into one so that they can be used as one regex string by
+         the GNU regex library.  Added into the file
+         lib/silcutil/silcutil.[ch].
+
+         Added silc_string_regex_match to match two strings.  It returns
+         TRUE if the strings match.  Added into lib/silcutil/silcutil.[ch].
+
+Thu Mar 22 15:29:42 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Imported GNU regex to the soruce tree into lib/contrib.
+         Fixed some compiler warning from the regex.c.
+
+Wed Mar 21 15:27:58 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed MOTD command in the server to work in router environment.
+
+       * Fixed the MOTD command in the client library to support
+         the server argument in the command.
+
+       * Added `nickname_len' argument to the silc_idlist_add_client
+         in the server, as the `nickname' argument may be binary data
+         (it may be hash).
+
+       * Added silc_idlist_get_channels to return all channels from
+         the ID list.
+
+       * Implemented LIST command to the server.  Affected file is
+         silcd/command.c.
+
+       * Implemented the LIST command to the client library and on the
+         user interface.
+
+       * Added [<user count>] argument to the LIST command reply.
+         With private channels the user count is not shown.
+
+       * Updated TODO and README.
+
+Tue Mar 20 21:05:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * The client entry's data.registered must be TRUE even with
+         global client entry on global client list.  The data.registered
+         is used to check whether the client is anymore in the network,
+         for example with WHOWAS command so it must be valid.
+
+       * Fixed the WHOWAS command in the server.  It now actually works
+         in router environment.  Added function into silcd/command_reply.c
+         silc_server_command_reply_whowas_save.
+
+       * Added silc_idlist_purge function to the silcd/idlist.c
+         to periodically purge the ID Cache.
+
+       * Fixed INFO command in the server.  It works now in router
+         environment.  Added <server name> argument to the INFO command
+         reply.  Updated the protocol specs.
+
+       * Fixed minor bug in silc_idcache_purge to not purge if the
+         expire value is zero.
+
+       * Fixed various bugs in WHOIS and IDENTIFY command handling as
+         they were buggy because of the WHOWAS information.
+
+       * Fixed local command MSG to handle the async resolving of 
+         the remote client properly.  It used to fail the first MSG.
+         Affected file silc/local_command.c.
+
+       * Added `data_len' field to SilcIDCache context.
+
+Tue Mar 20 16:29:00 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Update TODO.  Todo in commands in the server.
+
+Tue Mar 20 15:45:14 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new notify type SILC_NOTIFY_TYPE_UMODE_CHANGE that is
+         used by routers as broadcast packet to inform other routers
+         about the changed user mode.
+
+         Implemented the notify handling in the server.  Affected file is
+         silcd/packet_receive.c.  Added the function 
+         silc_server_send_notify_umode to the silcd/packet_send.[ch].
+
+       * Added new generic Channel Payload and deprecated the New Channel
+         Payload.  The New Channel Payload is now the generic Channel
+         Payload.
+
+       * Added new argument `mode' to the silc_server_send_new_channel
+         as it is required in the Channel Payload now.
+
+       * Renamed the SilcChannelPayload to SilcChannelMessagePayload
+         and created a new and real SilChannelPayload to represent the
+         new generic Channel Payload.  Implemented the encode/decode
+         for Channel Payload.  Affected file lib/silccore/silcchannel.[ch].
+
+       * Added silc_server_get_client_channel_list to return the list
+         of channels the client has joined for WHOIS command reply.
+         Affected file silcd/server.[ch].
+
+       * Implemented the channel list sending in the WHOIS command reply
+         in server and in the client.
+
+         Implemented the channel list displaying on the user interface
+         as well.  Affected file silc/client_ops.c.
+
+       * Added silc_channel_payload_parse_list to parse list of Channel
+         Payloads.  It returns SilcDList list of SilcChannelPayloads.
+         Client for example can use this function to parse the list of
+         channels it receives in the WHOIS command reply.  The caller
+         must free the list by calling silc_channel_payload_list_free.
+         Affected files lib/silccore/silcchannel.[ch].
+
+Mon Mar 19 21:39:15 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added one new argument <user mode> to the WHOIS command reply
+         to return the mode of the user in SILC.  Updated the protocol
+         specs.
+
+         Implemented it to the server and client.
+
+Mon Mar 19 18:43:06 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the mode printing on the user interface on joining.
+         Affected file silc/client_ops.c.
+
+       * Implemented the UMODE command and user modes in general to the
+         client library and to the user interface.
+
+       * Implemented the UMODE command to the server.
+
+       * The server now sends UNKNOWN_COMMAND error status if client sends
+         unknown command.  Affected file silcd/command.c.
+
+       * All server commands now handle the command identifier the right
+         way when sending the command reply to the client.  The client can
+         use to identify the command replies with the identifier.
+
+Mon Mar 19 16:13:07 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_get_client_route to resolve the route to
+         the client indicated by the client ID.  Affected file is
+         silcd/server.[ch].
+
+       * Added silc_server_relay_packet as general function to relay
+         packet to arbitrary destination.  This deprecates functions
+         like _send_private_message_key, _relay_notify etc.  Affected
+         file is silcd/packet_send.[ch].
+
+         Removed silc_server_send_key_agreement, 
+         silc_server_send_private_message_key and
+         silc_server_packet_relay_notify functions from the file
+         silcd/packet_send.[ch].
+
+       * Updated TODO.
+
+       * Implemented the SILC_NOTIFY_TYPE_KILLED notify handling in the
+         server.  Affected file silcd/packet_receive.[ch].
+
+       * Implemented the KILL command to the client.  Implemented the
+         SILC_NOTIFY_TYPE_KILLED notify handling in the client library.
+         Affected files lib/silcclient/command[_reply].c and
+         lib/silcclient/client_notify.c.  Implemented the KILL notify
+         printing in the user inteface.
+
+       * Fixed a lot silc_parse_nick memory leaks from the client
+         library in the file lib/silcclient/command.c.
+
+       * Changed the silc_server_send_notify_on_channels's `sender'
+         argument from SilcSocketConnection to SilcClientEntry to 
+         check the sender as entry and not as connection object and not
+         to send to the client provided as argument.  The affected file
+         is silcd/packet_send.[ch].
+
+       * The notify packets that are destined directly to the client used
+         to not to be processed by the server.  Now changed that and the
+         server processes all notify packets.  After relaying the packet
+         to the client the notify packet is processed in the server.
+
+       * The silc_server_free_client_data now checks whether there is
+         pending outgoing traffic for the client and purges the data to
+         the network before removing the client entry.
+
+Sun Mar 18 21:02:47 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SILC_NOTIFY_TYPE_KILLED notify type.  It is sent when
+         an client is killed from the SILC Network.  Updated the protocol
+         specs accordingly.
+
+         Added new function silc_server_send_notify_killed to the
+         silcd/packet_send.[ch].
+
+       * Added function silc_server_packet_relay_notify to relay notify
+         packets that are destined directly to a client.  In this case
+         the server does not process the notify packets but merely relays
+         it to the client.  Affected file silcd/packet_send.[ch].
+
+         Added also silc_server_packet_process_relay_notify to check
+         whereto relay the notify.  Affected file is 
+         silcd/packet_receive.[ch].
+
+       * Implemented the KILL command to the server.
+
+       * Updated TODO.
+
+       * Added the backup schema desgined last fall to the protocol
+         specs for everyone to see.  The specification is in the
+         *-spec-xx.txt draft and the packet type definitions for the
+         backup routers is in *-pp-xx.txt draft.  Thusly, added also
+         new packet type SILC_PACKET_CELL_ROUTERS.
+
+       * A big security problem in the implementation discovered.  The
+         signoff of an client did not cause new channel key generation
+         which it of course should've done.  The channel keys must be
+         always re-generated when client leaves (or signoffs) the channel.
+         The silc_server_remove_from_channels funtion now handles
+         the channel key re-generation.
+
+       * Added `sender' argument to the silc_server_send_notify_on_channels
+         to not to send the client provided as argument.  Affected file
+         silcd/packet_send.[ch].
+
+Fri Mar 16 15:52:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented OPER and SILCOPER commands into the server and
+         the client library.
+
+       * Added silc_auth_verify and silc_auth_verify_data to verify
+         the authentication directly from the authentication payload.
+         It supports verifying both passphrase and public key based
+         authentication.  Affected file lib/silccore/silcauth.[ch].
+
+       * Added `hash' field to the SilcIDListData structure.  It is the
+         hash negotiated in the SKE protocol.  Affected file is
+         silcd/idlist.[ch].
+
+       * Slight redesigning of the SilcAuthPayload handling routines.
+         Do not send SilcPKCS but SilcPublicKey as argument.
+
+       * Implemented the public key authentication support to the
+         serverconfig.  The public key is loaded from the provided path
+         and saved as authentication data to void * pointer.  Thus,
+         changed the unsigned char *auth_data to void *auth_data;
+
+       * Fixed SHUTDOWN command to send the reply before the server
+         is shutdown. :)  Affected file silcd/command.c.
+
+       * Fixed fatal bug in CONNECT command.  The hostname was invalid
+         memory and server crashed.  Affected file silcd/command.c.
+
+       * Fixed fatal bug in CLOSE command.  The server_entry became
+         invalid but was referenced later in the command.  Affected file
+         silcd/command.c.
+
+Thu Mar 15 12:46:58 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed fatal bug in failure packet handling.  Server ignored
+         the failure and thus crashed when it came.
+
+       * Updated TODO.
+
+Wed Mar 14 20:37:35 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new SILC_CF_LAG_STRICT command flag that strictly forces
+         that the command may be executed only once in (about) 2 seconds.
+         The old SILC_CF_LAG flag is same but allows command bursts up
+         to five before limiting.
+
+         Added the support for CF_LAG and CF_LAG_STRICT flags to the
+         server code.  Various commands now includes the CF_LAG_STRICT
+         flag to disallow any kind of miss-use of the command.
+
+       * Fixed the silc_buffer_unformat to not to allocate any data
+         if the length of the data is zero.  It used to allocate the
+         length + 1.  Affected file lib/silcutil/silcbuffmt.c.
+
+Wed Mar 14 16:10:30 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the format of AdminConnection configuration section
+         in the server.  Added username of the admin to the format.
+         Affected files silcd/serverconfig.[ch].
+
+         Added silc_server_config_find_admin into silcd/serverconfig.[ch]
+         to return admin configuration data by host, username and/or
+         nickname.
+
+Wed Mar 14 13:18:16 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented WHOWAS command to the server.  Added the functions:
+
+         silc_server_command_whowas_parse,
+         silc_server_command_whowas_send_reply,
+         silc_server_command_whowas_from_client and
+         silc_server_command_whowas_from_server
+
+       * Added <Client ID> argument to the WHOWAS command reply.  Updated
+         the protocol specs accordingly.
+
+       * Implemented WHOWAS command and command_reply to the client
+         library.
+
+         Implemented the WHOWAS printing on the user interface.
+
+Tue Mar 13 22:17:34 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new argument to the WHOWAS command reply, the real name.
+         It is an optional argument.  Updated the protocol specs.
+
+       * Added SilcIDCacheDestructor callback that is registered when
+         the SilcIDCache is allocated.  The callback is called when
+         an cache entry in the ID Cache expires, or is purged from the
+         cache.  Added into lib/silccore/idcache.[ch].
+
+         Added silc_idlist_client_destructor to the silcd/idlist.[ch]
+         to destruct the client entries when the cache entry expires.
+         Other ID Cache's in server and in the client library ignores
+         the destructor.
+
+       * If the ID Cache entry's `expire' field is zero then the entry
+         never expires.  Added boolean `expire' argument to the
+         silc_idcache_add function in the lib/silccore/idcache.[ch].
+         If it is TRUE the default expiry value is used.
+
+       * Added silc_server_free_client_data_timeout that is registered
+         when client disconnects.  By default for 5 minutes we preserve
+         the client entry for history - for WHOWAS command.
+
+Tue Mar 13 13:26:18 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added support to the server to enforce that commands are not
+         executed more than once in 2 seconds.  If server receives 
+         commands from client more frequently, timeout is registered
+         to process the commands.  Affected file silcd/command.c.
+         Added new function silc_server_command_process_timeout.
+
+       * Changed NICK_NOTIFY handling in client library to check that
+         if the client's nickname was changed, so there is no need to
+         resolve anything from the server.
+
+       * Removed error printing from the WHOIS and IDENTIFY commands.
+         If error occurs then it is ignored silently in the client library.
+         The application, however, may map the received error to 
+         human readable error string.  The application currently maps
+         the NO_SUCH_NICKNAME error to string.
+
+       * Made the command status message public to the application.  Moved
+         them from lib/silcclient/command_reply.c to 
+         lib/silcclient/command_reply.h.  The application can map the
+         received command status to the string with the
+         silc_client_command_status_message function.
+
+       * Added check to the server to check that client's ID is same
+         as the Source ID in the packet the client sent.  They must
+         match.
+
+Tue Mar 13 12:49:21 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added dist-bzip hook to the Makefile.am to make bzip2
+         compressed distributions.
+
+Mon Mar 12 18:43:38 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Server now enforces the maximum length for the nickname and
+         the channel as protocol specification dictates.  128 bytes for
+         nickname and 256 bytes for channel name.
+
+       * Moved the WHOIS printing to the application.  The client libary
+         does not print out the WHOIS information anymore, the application
+         must do it.  Renamed silc_client_command_reply_whois_print to
+         the silc_client_command_reply_whois_save.
+
+         The client's idle time is also sent to the application now, and
+         the idle is shown on screen.
+
+       * Added silc_client_command_reply_identify_save to save the
+         received IDENTIFY entries.
+
+       * Do not check for channel private keys in message sending and
+         reception if the channel does not have the PRIVKEY mode set.
+         Affected file lib/silclient/client_channel.c.
+
+Sun Mar 11 20:25:06 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a minor bug if WHOIS and IDENTIFY command parsing that
+         just surfaced after chaning the JOIN procedure.
+
+Sun Mar 11 14:59:05 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_client_get_clients_by_list to get client entries
+         from Client ID list, that is returned for example by JOIN
+         and USERS command replies.  The application should use this
+         function for example when JOIN command reply is received to
+         resolve the clients already on the channel (library does not
+         do that anymore as USERS command reply is not used in the JOIN
+         procedure anymore).  Affected files lib/silcclient/silcapi.h and
+         lib/silcclient/idlist.c.
+
+       * JOIN command reply and USERS command reply returns now SilcBuffer
+         pointers instead of unsigned char pointers when returning
+         the client list and mode list.
+
+       * Added <Client ID> argument to the JOIN command reply, mainly
+         for the server to identify for which client the command was
+         originally sent.  Updated protocol specs accordingly.
+
+       * Added SilcDlist private_key pointer to the SilcChannelEntry
+         in the client to support the channel private keys.  Affected
+         file is lib/silcclient/idlist.h.
+
+       * Added SilcChannelPrivateKey argument to the function
+         silc_client_send_channel_message so that application can choose
+         to use specific private ke if it wants to.  If it is not provided,
+         the normal channel key is used, unless private keys are set. 
+         In this case the first (key that was added first) is used 
+         as the encryption key.
+
+       * Implemented the support for channel private key handling.
+         Implemented the following functions:
+
+         silc_client_add_channel_private_key,
+         silc_client_del_channel_private_keys,
+         silc_client_del_channel_private_key,
+         silc_client_list_channel_private_keys and
+         silc_client_free_channel_private_keys
+
+         Affected file lib/silcclient/client_channel.c.
+
+       * Added the support for the private keys in the channel message
+         sending and encryption and in the message reception and
+         decryption.  Affected funtions are
+         silc_client_send_channel_message and silc_client_channel_message.
+
+Sat Mar 10 21:36:22 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SKE's key verify callback to the client library's
+         KE protocol context. Affected files lib/silcclient/protocol.[ch].
+
+       * Removed the statement that server (or router) must send USERS
+         command reply when joining to the channel so that the client
+         knows who are on the channel.  Instead, the client list and 
+         client's mode list is now sent in the JOIN command reply to the
+         client who joined channel.  This is better solution.
+
+       * Added function silc_server_get_users_on_channel and function
+         silc_server_save_users_on_channel to the silcd/server.[ch].
+
+       * Removed function silc_server_command_send_users from the
+         silcd/command.c.
+
+       * Do not show topic on the client library anymore.  The topic is
+         sent in the command reply notify to the application and the
+         application must show the topic now.
+
+Sat Mar 10 00:07:37 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added client searching by nickname hash into the IDENTIFY and
+         WHOIS commands in the server as they were clearly missing from
+         them.  Affected file is silcd/command.c.
+
+       * Fixed a bug in private message receiving in the client library.
+         The remote ID was freed and it wasn't supposed, now it is
+         duplicated.
+
+Fri Mar  9 12:40:42 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Minor fix to the channel payload; allocate the data area, as it
+         needs to be of specific length.
+
+       * If the key agreement port is zero then the operating
+         system will define the bound port.  Affected files are
+         lib/silcclient/silcapi.h and lib/silcclient/client_keyagr.c.
+
+       * Added new function silc_channel_payload_decrypt into the file
+         lib/silccore/silcchannel.[ch].
+
+       * Moved the channel message etc, check from silc_packet_decrypt
+         to applications.  The library calls now a generic 
+         SilcPacketCheckDecrypt callback which is to return TRUE or FALSE
+         when the packet is either normal or special.  This was done to
+         allow more wide range of checking that was not allowed when
+         the code was in library.  Now applications can do virtually any
+         checks to the packet and return to the library the decision how
+         the packet should be processed.  Affected files are
+         lib/silccore/silcpacket.[ch].
+
+         Added silc_server_packet_decrypt_check to the server and
+         silc_client_packet_decrypt_check to the client library.
+
+       * Added silc_server_packet_send_srcdest into silcd/packet_send.[ch]
+         to send with specified source and destination information.
+
+       * Channel message delivery between routers was broken after the
+         channel key distribution was fixed earlier.  The channel key
+         was used be to distributed to other routers as well which is not
+         allowed by the protocol.  Now this is fixed and channel keys
+         really are cell specific and the channel message delivery between
+         routers comply with the protocol specification.
+
+       * Fixed various commands in server to check also the global list
+         for the channel entry and not just the local list.  The affected
+         file silcd/command.c.
+
+Thu Mar  8 21:39:03 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added assert()s to buffer formatting and unformatting routines
+         to assert (if --enable-debug) when error occurs.  Affected
+         file: lib/silcutil/silcbuffmt.c.
+
+       * Changed to auto-reconnect to check whether the remote host is
+         router and register the re-connect timeout if it is.  It used 
+         to check that whether we are normal server, but router must do
+         auto-reconnect with another router as well.  Affected file
+         silcd/server.c.
+
+       * Removed the [<key len>] option from CMODE command as the cipher
+         name decides the key length, nowadays.  See the defined ciphers
+         from the protocol specification.
+
+       * Added [<hmac>] option to the CMODE command to define the HMAC
+         for the channel.  Added SILC_CMODE_HMAC channel mode.
+
+       * Added [<hmac>] option for the JOIN command so that user can
+         select which HMAC is used to compute the MACs of the channel
+         messages.
+
+       * Added Hmac field to the Channel Message Payload.  The integrity
+         of plaintext channel messages are now protected by computing
+         MAC of the message and attaching the MAC to the payload.  The
+         MAC is encrypted.  Now, it is clear that this causes some
+         overhead to the size of the packet but rationale for this is that
+         now the receiver can verify whether the channel message decrypted
+         correctly and also when private keys are set for the channel the
+         receiver can decrypt the packet with several keys and check from
+         the MAC which key decrypted the message correctly.
+
+       * Added silc_cipher_encrypt and silc_cipher_decrypt into the
+         lib/silccrypt/silccipher.[ch].
+
+       * Added silc_hash_len to return the digest length into the
+         lib/silcrypt/silchash.[ch].
+
+       * Rewrote parts of Silc Channel Payload interface in the
+         lib/silccore/silcchannel.[ch].  The encode function now also
+         encrypts the packet and parse function decrypts it.
+
+Wed Mar  7 20:58:50 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a minor formatting bug in the SKE's key material processing.
+         It actually might have processed the keys wrong way resulting
+         into wrong keys.
+
+       * Redefined the mandatory HMAC algorithms and added new algorithms.
+         Added hmac-sha1-96 and hmac-md5-96 which are normal hmac-sha1
+         and hmac-md5 truncated to 96 bits.  The mandatory is now 
+         hmac-sha1-96.  Rest are optional (including the one that used
+         to be mandatory).  Rationale for this is that the truncated HMAC
+         length is sufficient from security point of view and can actually
+         make the attack against the HMAC harder.  Also, the truncated
+         HMAC causes less overhead to the packets.  See the RFC2104 for
+         more information.
+
+       * Added new [hmac] configuration section.  The SKE used to use
+         the hash names (md5 and sha1) in the SKE proposal as HMCAS which
+         is of course wrong.  The official names that must be proposed in
+         the SKE are the ones defined in the protocol specification
+         (hmac-sha1-96 for example).  The user can configure any hmac
+         using any hash function configured in the [hash] section.  At
+         least, the mandatory must be configured.
+
+         Rewrote the HMAC interface in lib/silccrypt/silchmac.[ch].
+
+       * Added HMAC list to the SKE proposal list.  It has now both
+         hash algorithm list and HMAC list.  This makes the protocol
+         incompatible with previous versions.  The SKE now seems to work
+         the way it is supposed to work, for the first time actually.
+
+       * Defined plain Hash algorithms to the protocol specification.
+         Added sha1 and md5.
+
+Tue Mar  6 15:36:11 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented support for key agreement packets into the server.
+         Added functions silc_server_key_agreement and
+         silc_server_send_key_agreement.  Other than these functions,
+         server has nothing to do with this packet.
+
+       * Added support for private message key packets into the server.
+         Added functions silc_server_private_message_key and
+         silc_server_send_private_message_key.
+
+       * Updated TODO.
+
+       * Changed the silc_[client|server]_protocol_ke_set_keys to be
+         called in the protocol's final callback instead in the END
+         protocol state.  This makes a little more sense and in the same
+         time in client we can use the same protocol routines for normal
+         key exchange and to key agreement packet handling as well.
+
+       * Added to both client's and server's KE protocol context the
+         SilcSKEKeyMaterial pointer to save the key material.  We will
+         bring the key material to the protocol's final callback by doing
+         this.  The final callback must free the key material.
+
+       * Added SKE's packet_send callback into client's KE protocol
+         context so that the caller can choose what packet sending function
+         is used.  This way we can use different packet sending when
+         doing normal SKE when doing key agreement packet handling (in
+         the key agreement packet handling we do not want to encrypt
+         the packets).
+
+       * Implemented the responder side of the key agreement routines
+         in the client.  The client can now bind to specified port and
+         accept incoming key negotiation.  The key material is passed
+         to the application after the protocol is over.
+
+       * Implemented the processing of incoming Key Agreement packet
+         in the client.  Added function silc_client_key_agreement to
+         process the packet.
+
+       * Implemented the intiator side of the key agreement routines
+         in the client.  The client can now initiate key agreement with
+         another remote client.  The key material is passed to the
+         application after the protocol is over.
+
+       * Created client_keyagr.c to include all the key agreement 
+         routines.
+
+       * Added macro SILC_TASK_CALLBACK_GLOBAL which is equal to the
+         SILC_TASK_CALLBACK except that it is not static.
+
+       * Created client_notify.c and moved the Notify packet handling
+         from the client.[ch] into that file.
+
+       * Created client_prvmsg.c and moved all private message and
+         private message key routines from the client.[ch] into that file.
+
+       * Create client_channel.c and moved all channel message and
+         channel private key routines from the client.[ch] into that file.
+
+       * Changed silc_client_get_client_by_id_resolve to resolve with
+         WHOIS command instead of IDENTIFY command, in the file
+         lib/silclient/idlist.c.
+
+Mon Mar  5 18:39:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented the SKE's responder side to the Client library.
+
+       * When FAILURE is received to the protocol do not trust it
+         blindly.  Register a timeout to wait whether the remote closes
+         the connection as it should do it, only after that process the
+         actual failure.  This was changed to both client and server.
+
+       * Added client_internal.h to include some of the structures
+         there instead of client.h in lib/silcclient/.
+
+       * Added function silc_task_unregister_by_callback to unregister
+         timeouts by the callback function.
+
+Sat Mar  3 19:15:43 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Some "Incomplete WHOIS info" errors has been appearing on the
+         log files.  Took away the entry->userinfo check from WHOIS
+         reply sending.  The entry->userinfo is now " " if client did not
+         provide one.  I thought this was fixed earlier but something
+         is wrong still.  Let's see if the error still appears.
+
+Wed Feb 28 20:56:29 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a minor bug in the login when the channel key is
+         re-generated in the server.  It used to generate the key in
+         wrong order and thus caused problems in the channel traffic.
+
+       * Fixed a minor bug in channel key distsribution after
+         KICK command.  The key was not sent to the router even though
+         it should've been.
+
+Tue Feb 27 20:24:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_ske_process_key_material_data as generic routine
+         to process any key material as the SILC protocol dictates.  The
+         function is used by the actual SKE library but can be used by
+         applications as well.  This relates to the private message keys
+         and the channel private keys since they must be processed the
+         same way the normal SILC session keys.  The protocol dictates
+         this.  Affected files: lib/silcske/silcske.[ch].
+
+         Added also silc_ske_free_key_material to free the
+         SilcSKEKeyMaterial structure.
+
+       * Defined silc_cipher_set_key function to set the key for
+         cipher without using the object's method function.  The affected
+         files: lib/silccrypt/silccipher.[ch].
+
+       * Implemented silc silc_client_add_private_message_key,
+         silc_client_add_private_message_key_ske, 
+         silc_client_del_private_message_key,
+         silc_client_list_private_message_keys and
+         silc_client_free_private_message_keys functions in the
+         client library.
+
+         Added functions silc_client_send_private_message_key to send
+         the Private Message Key payload and silc_client_private_message_key
+         to handle incoming Private Message Key payload.
+
+       * Added Cipher field to the Private Message Key payload to set
+         the cipher to be used.  If ignored, the default cipher defined
+         in the SILC protocol (aes-256-cbc) is used.
+
+Tue Feb 27 13:30:52 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Removed lib/silcclient/ops.h file.
+
+         Redefined parts of the SILC Client Library API. Created new
+         file silcapi.h that deprecates the ops.h file and defines the
+         published Client Library API.  Defined also private message key
+         API and channel private key API into the file.
+
+         This is the file that the application must include from the
+         SILC Client Library.  Other files need not be included by
+         the application anymore.
+
+       * Added new key_agreement client operation callback and also
+         defined the Key Agreement library API for the application.
+
+Tue Feb 27 11:28:31 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new packet type: SILC_PACKET_KEY_AGREEMENT.  This packet
+          is used by clients to request key negotiation  between another
+          client in the SILC network.  If the negotiation is started it
+          is performed using the SKE protocol.  The result of the
+          negotiation, the secret key material, can be used for example
+          as private message key.
+
+         Implemented the Key Agreement payload into the files
+         lib/silccore/silauth.[ch].
+
+Mon Feb 26 12:13:58 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Redefined ciphers for the SILC protocol.  Added some new ciphers
+         and defined the key lengths for the algorithms.  Changed the
+         code accordingly.  The default key length is now 256 bits.
+
+       * Fixed SKE key distribution function silc_ske_process_key_material
+         when the key length is more than 128 bits.  The default key 
+         length in SILC is now 256 bits.
+
+       * Added new command status type: SILC_STATUS_ERR_UNKOWN_ALGORITHM
+         to indicate unsupported algorithm.
+
+       * Renamed rijndael.c to aes.c and all functions as well.
+
+       * Fixed a long standing channel key setting bug in client library.
+         Weird that it has never surfaced before.
+
+       * Fixed bug in channel deletion.  If the entire channel is removed
+         then it must also delete the references of the channel entry
+         from the client's channel list as the client's channel entry and
+         the channel's client entry share same memory.
+
+Sun Feb 25 20:47:29 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented CONNECT and SHUTDOWN commands in the client.
+
+       * Implemented CLOSE command to the client.
+
+       * Added the function silc_idlist_find_server_by_name into the
+         files silcd/idlist.[ch].
+
+         Added the function silc_idlist_find_server_by_conn into the
+         files silcd/idlist.[ch].
+
+Sat Feb 24 23:45:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * DIE command was renamed to SHUTDOWN.  Updated the both code
+         and protocol specs.
+
+       * Defined SILC_UMODE_NONE, SILC_UMODE_SERVER_OPERATOR and
+         SILC_UMODE_ROUTER_OPERATOR modes into lib/silccore/silcmode.h.
+
+       * Implemented CONNECT, CLOSE and SHUTDOWN commands to the server
+         side.
+
+       * Added function silc_server_create_connection function to create
+         connection to remote router.  My server implementation actually
+         does not allow router to connect to normal server (it expects
+         that normal server always initiates the connection to the router)
+         so the CONNECT command is only good for connecting to another
+         router.
+
+Sat Feb 24 16:03:45 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SILC_NOTIFY_TYPE_KICKED to indicate that the client
+         or some other client was kicked from the channel.
+
+         Implemented the handling of the notify type to both client
+         and server.
+
+         Implemented silc_server_send_notify_kicked to send the KICKED
+         notify.  It is used to send it to the server's primary router.
+
+       * Implemented the KICK command into server and client.
+
+       * Added `query' argument to the silc_idlist_get_client function
+         to indicate whether to query the client from server or not if
+         it was not found.
+
+       * Added new command status type SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
+         to indicate that the client is not channel founder.
+
+       * Updated TODO.
+
+Sat Feb 24 00:00:55 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Removed the rng context from SilcPacketContext structure and
+         changed that the packet routine uses the Global RNG API.
+
+Fri Feb 23 11:22:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added support for quit message that client can "leave" on the
+         channel when it quits the SILC.  It is ditributed inside the
+         SILC_NOTIFY_TYPE_SIGNOFF notify type.
+
+         Added silc_server_free_client_data that will take the
+         signoff message as argument.
+
+       * Changed SKE routines to use the silc_pkcs_sign/verify routines.
+
+Thu Feb 22 23:05:36 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Updated parts of the protocol specification to keep it up
+         to date.
+
+Thu Feb 22 15:08:20 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added List flag (SILC_PACKET_FLAG_LIST) to indicate list of
+         payloads in one packet.
+
+       * Deprecated following packet types: NEW_ID_LIST, NEW_CHANNEL_LIST,
+         NEW_CHANNEL_USER_LIST, SET_MODE and SET_MODE_LIST.  List packets
+         use now the new List flag.
+
+       * Also deprecated the following packet types: REPLACE_ID,
+         NEW_CHANNEL_USER and REMOVE_CHANNEL_USER packet types.
+        
+       * Added list support for Notify packet in server.
+
+       * Added silc_server_send_notify_channel_change to send the
+         CHANNEL_CHANGE notify type to replace channel ID's.  Deprecates
+         the silc_server_send_replace_id.
+
+       * Added silc_server_send_notify_nick_change to send the
+         NICK_CHANGE notify type.  Deprecates the function
+         silc_server_send_replace_id.
+
+       * Added silc_server_send_notify_join to send the JOIN notify type.
+         Deprecates the function silc_server_send_new_channel_user.
+
+       * Added silc_server_send_notify_leave to send LEAVE notify type.
+         Deprecates the function silc_server_send_remove_channel_user.
+
+       * Added silc_server_send_notify_cmode and 
+         silc_server_send_notify_cumode to send CMODE and CUMODE notify
+         types.  Deprecates the silc_server_send_set_mode function.
+
+       * Added SERVER_SIGNOFF notify type to indicate that server has
+         quit.  This means that all clients on the channel from that 
+         server will drop.  This can be also used when netsplit happens.
+
+         Deprecated REMOVE_ID packet type since it is not needed anymore
+         even from server.
+
+         Added silc_server_send_notify_server_signoff to send the
+         SERVER_SIGNOFF notify type.  Deprecates the function
+         silc_server_send_remove_id.
+
+         Added also silc_server_send_notify_signoff to send the
+         SIGNOFF notify type.
+
+       * Employed the PKCS #1. It is the mandatory way to do RSA in the
+         SILC protocol from this day on.  Changed the protocol 
+         specification as well.
+
+       * Added silc_server_send_notify_topic_set to send TOPIC_SET
+         notify type.  It is used between routers to notify about
+         topic changes on a channel.
+
+       * Added silc_id_dup into lib/silccore/id.[ch] to duplicate
+         ID data.
+
+       * Partly updated the protocol specification to comply with the
+         changes now made.  It is still though a bit outdated.
+
+       * The JOIN notify type now takes one extra argument <Channel ID>.
+         The packet used to be destined to the channel but now the
+         JOIN type may be sent as list thus it is impossible to 
+         destine it to any specific channel.  By adding this argument
+         it is again possible.
+
+Wed Feb 21 22:39:30 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added CREDITS file.  The CHANGES and CREDITS file will appear
+         in the distribution as well.
+
+Wed Feb 21 14:17:04 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented CMODE_CHANGE, CUMODE_CHANGE and TOPIC_SET notify
+         types in the server's silcd/packet_receive.c.
+
+       * Implemented CMODE and CUMODE to work in router environment.
+
+       * Fixed minor encoding and decoding buglet from the
+         lib/silccore/silcmode.c.
+
+       * Fixed buffer overflow from lib/silcclient/command.c in USERS
+         command parsing.
+
+Wed Feb 21 12:44:00 EET 2001  Mika Boström <bostik@lut.fi>
+
+       * Changed all SilcConfigServer* and silc_config_server* to
+         SilcServerConfig* and silc_server_config*, respectively.
+         Patch by Bostik.
+
+Wed Feb 21 00:10:00 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Associated the ID (client or server ID) to the Authentication
+         Payload to avoid any possibility of forging.  Updated the
+         protocol specification and the code accordingly.
+
+Tue Feb 20 14:14:14 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * The RSA key length is now save to the RsaKey context in the
+         key generation process in lib/silccrypt/rsa.c.  The key length
+         is now used to figure out the maximum size of the block allowed
+         to be encrypted/signed.
+
+       * Added silc_mp_mp2bin_noalloc into lib/silcmath/mpbin.[ch].  It
+         is equivalent to the silc_mp_mp2bin but does not allocate any
+         memory.
+
+       * Changed silc_mp_mp2bin API to take length argument.  If it is
+         non-zero then the buffer is allocated that large.  If zero, then
+         the size is approximated using silc_mp_sizeinbase, which however
+         is not relieable.
+
+       * Created Global RNG API which is global RNG that application can
+         initialize.  After initializing, any routine anywhere in the
+         code (including library) can use RNG without allocating a new
+         RNG object.  This was done to allow this sort of use of the 
+         RNG in code that has no chance to allocate RNG object.  All
+         applications currently allocate this and many routines in the
+         library use this.  Affected file lib/silccrypt/silcrng.[ch].
+
+       * Removed the RNG kludge from lib/silcmath/primegen.c and changed
+         it to use the Global RNG API.
+
+       * Defined Authentication Payload into protocol specification that
+         is used during SILC session to authenticate entities.  It is
+         used for example by client to authenticate itself to the server
+         to obtain server operator privileges.
+
+         Implemented this payload into the lib/silccore/silcauth.[ch].
+         Implemented also routines for public key based authentication
+         as the new protocol specification dictates.
+
+         Moved definitions of different authentication methods from
+         lib/silccore/silcprotocol.h into lib/silccore/silcauth.h.
+
+       * Added silc_pkcs_encrypt, silc_pkcs_decrypt, silc_pkcs_sign,
+         silc_pkcs_verify and silc_pkcs_sign_with_hash and
+         silc_pkcs_verify_with_hash functions into the file 
+         lib/silccrypt/silcpkcs.[ch].
+
+Mon Feb 19 19:59:28 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * The client entry's userinfo pointer must be always valid. 
+         Otherwise the [<unknown>] bug will surface beacuse the WHOIS
+         will fail since it requires the userinfo.  Now, the userinfo
+         is allocated as "" if actual userinfo does not exist.  Actually,
+         it must exist and it is totally Ok to drop client connections
+         that does not announce the userinfo.  However, we will make
+         this workaround for now.
+
+       * Added silc_net_get_remote_port into lib/silcutil/silcnet.[ch]
+         to return the remote port by socket.
+
+Mon Feb 19 14:26:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed SILC_SERVER_COMMAND_EXEC_PENDING macro to the name
+         SILC_SERVER_PENDING_EXEC and added an new macro
+         SILC_SERVER_PENDING_DESTRUCTOR which is called to free the
+         data or when error occurs while processing the pending command.
+
+         Added new argument `destructor' into silc_server_command_pending
+         and to the SilcServerCommandPending object.  This destructor is
+         now called after calling the pending callback or if error occurs
+         immediately.  If error occurs the actual pending callback won't
+         be called at all - only the destructor.  The destructor may be
+         NULL if destructor is not needed.
+
+         All this applies for client library code as well.  Similar
+         changes were made there as well for the pending commands.
+
+         In the client, the application must now allocate the 
+         SilcClientCommandContext with the silc_client_command_alloc
+         function.
+
+       * Added reference counter to the SilcServerCommandContext.  Added
+         function silc_server_command_alloc and silc_server_command_dup 
+         functions.
+
+         Same type of functions added to the client library for the same
+         purpose as well.
+
+       * Removed the cmd_ident from IDListData away since it is now 
+         global for all connections.  It is the command identifier used
+         in command sending and with pending commands.  The affected file
+         is silcd/idlist.h.
+
+       * Added reference counter to the SilcSocketConnection objecet to
+         indicate the usage count of the object.  The object won't be
+         freed untill the reference counter hits zero.  Currently only
+         server uses this, and client ignores it.  The client must be
+         set to use this too later.  The affected files are
+         lib/silccore/silcsockconn.[ch].  Added also the function
+         silc_socket_dup to increase the reference counter.
+
+         This was mainly added because it is possible that the socket
+         is removed underneath of pending command or other async
+         operation.  Now it won't be free'd and proper DISCONNECTING
+         flags, etc. can be set to avoid sending data to connection that
+         is not valid anymore.
+
+       * Added SILC_SET_DISCONNECTING to server.c when EOF is read from
+         the connection.  After that it sets SILC_SET_DISCONNECTED.
+         It is, however, possible that the socket data is not still freed.
+         The silc_server_packet_process now checks that data is not
+         read or written to connection that is DISCONNECTED.  The socket
+         get's freed when the reference counter hits zero.
+
+Mon Feb 19 00:50:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the client operation API: channel_message operation's
+         `sender' is now the client entry of the sender, not the nickname
+         and the `channel' is the channel entry, not the channel name.
+
+         In the private_message operation the `sender' is now also the
+         client entry of the sender not the nickname.
+
+         Affected file is lib/silcclient/ops.h and all applications
+         using the client operations.
+
+Sat Feb 17 22:11:50 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Moved the calling of ops->connect() from connect_to_server_final
+         into receive_new_id functin since that is the point when the
+         client is actually allowed to send traffic to network.  The
+         affected file is lib/silcclient/client.c.
+
+Sat Feb 17 13:15:35 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * When receiving NEW_CHANNEL_LIST, NEW_CHANNEL_USER_LIST,
+         NEW_ID_LIST and SET_MODE_LIST packets, broadcast the list packet
+         (if needs broadcasting) instead of broadcasting the packets one
+         by one which would make a burst in the network traffic.
+
+       * Added `broadcast' argument to the functions in silcd/server.[ch]
+         silc_server_create_new_channel[_with_id] to indicate whether
+         to send New Channel packet to primary router.
+
+Sat Feb 17 01:06:44 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new function into the silcd/server.[ch] files:
+         silc_server_create_new_channel_with_id to create new channel with
+         already existing Channel ID.
+
+       * Added new packet type SILC_PACKET_SET_MODE_LIST into the file
+         lib/silccore/silcpacket.h.  This packet is used t send list of
+         Set Mode payloads inside one packet.  Server uses this to set
+         the modes for the channels and clients on those channels, that it
+         announced to the router when it connected to it.  The protocol
+         specification has been updated accordingly.
+
+       * The silc_server_new_channel did not handle the packet coming
+         from normal server as it normally does not send that.  However,
+         when it announces its channels it does send it.  Implemented
+         the support for that.
+
+       * Added SILC_ID_CHANNEL_COMPARE macro to compare to Channel ID's
+         into the file lib/silccore/id.h.
+
+Fri Feb 16 23:57:29 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed memory leaks in the functions silc_idlist_del_client,
+         silc_idlist_del_channel and silc_idlist_del_server in the file
+         silcd/idlist.c.  All of those leaked like a sieve.
+
+       * Fixed some small memory leaks in the client's function
+         silc_client_notify_by_server.
+
+       * Added functions into silcd/server.c: silc_server_announce_clients,
+         silc_server_announce_channels and silc_server_announce_server.
+         These functions are used by normal and router server to announce
+         to its primary router about clients, channels and servers (when
+         router) that we own.  This is done after we've connected to the
+         router.
+
+         These functions effectively implements the following packet types:
+         SILC_PACKET_NEW_CHANNEL_LIST, SILC_PACKET_NEW_CHANNEL_USER_LIST
+         and SILC_PACKET_NEW_ID_LIST.
+
+       * Added new functions into the silcd/packet_receive.[ch]:
+         silc_server_new_id_list, silc_server_new_channel_list and
+         silc_server_new_channel_user_list to handle the incoming 
+         NEW_ID_LIST, NEW_CHANNEL_LIST and NEW_CHANNEL_USER_LIST packets.
+
+       * Added support of changing Channel ID in the function
+         silc_server_replace_id.  If the server that announces a channel
+         to the router already exists in the router (with same name but
+         with different Channel ID), router is responsible to send
+         Replace ID packet to the server and force the server to change
+         the Channel ID to the one router has.
+
+       * Added new notify type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to notify
+         client that the Channel ID has been changed by the router.  The
+         normal server sends this to the client.  Client must start using
+         the new Channel ID as the channel's ID.
+
+         Implemented handling of this new type into lib/silcclient/client.c
+         into the function silc_client_notify_by_server.
+
+       * Added new function silc_idlist_replace_channel_id into the files
+         silcd/idlist.[ch] to replace the Channel ID.
+
+Fri Feb 16 14:14:00 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Call silc_server_command_identify_check always when processing
+         the IDENTIFY command in silcd/command.c
+
+Thu Feb 15 20:07:37 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new packet type SILC_PACKET_HEARTBEAT that is used to
+         send keepalive packets.  The packet can be sent by clients, 
+         servers and routers.
+
+         Added function silc_socket_set_heartbeat into the file
+         lib/silccore/silcsockconn.[ch] to set the heartbeat timeout.
+         If not set, the heartbeat is not performed.  The actual 
+         heartbeat is implemented in the low level socket connection
+         library.  However, application is responsible of actually
+         sending the packet.
+
+         Added silc_server_send_heartbeat to send the actual heartbeat
+         packet into silcd/packet_send.[ch].  Server now performs
+         keepalive with all connections.
+
+       * Added silc_task_get_first function into lib/silcutil/silctask.c
+         to return the timeout task with shortest timeout.  There was a bug
+         in task unregistration that caused problems.  TODO has been
+         updated to include that task system must be rewritten.
+
+       * The client library will now resolve the client information when
+         receiving JOIN notify from server for client that we know but
+         have incomplete information.
+
+       * Rewrote parts of silc_server_remove_from_channels and
+         silc_server_remove_from_one_channel as they did not remove the
+         channel in some circumstances even though they should've.
+
+       * Encryption problem encountered in server:
+
+         The LEAVE command used to send the Channel Key packet to the
+         router immediately after generating it.  However, the code
+         had earlier sent Remove Channel user packet but not immediately,
+         ie. it was put to queue.  The order of packets in the router
+         was that Channel Key packet was first and Remove Channel User
+         packet was second, even though they were encrypted in the
+         reverse order.  For this reason, MAC check failed.  Now, this
+         is fixed by not sending the Channel Key packet immediately but
+         putting it to queue.  However, this is more fundamental problem:
+         packets that are in queue should actually not be encrypted 
+         because packets that are sent immediately gets encrypted
+         actually with wrong IV (and thus MAC check fails).  So, packets
+         that are in queue should be encrypted when they are sent to
+         the wire and not when they put to the queue.
+
+         However, the problem is that the current system has not been
+         designed to work that way.  Instead, the packet is encrypted
+         as soon as possible and left to the queue.  The queue is then
+         just purged into wire.  There won't be any fixes for this
+         any time soon.  So, the current semantic for packet sending
+         is as follows:
+
+         o If you send packet to remote host and do not force the send
+         (the packet will be in queue) then all subsequent packets to the
+         same remote host must also be put to the queue.  Only after the
+         queue has been purged is it safe again to force the packet
+         send immediately.
+
+         o If you send all packets immediately then it safe to send
+         any of subsequent packets through the queue, however, after
+         the first packet is put to queue then any subsequent packets
+         must also be put to the queue.
+
+         Follow these rules and everything works fine.
+
+Thu Feb 15 14:24:32 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new function silc_server_remove_clients_by_server to
+         remove all client entries from ID list when the server connection
+         is lost.  In this case it is also important to invalidate all
+         client entires as they hold the invalid server entry.  This
+         fixes fatal bug when server has lost connection and will reconnect
+         again.
+
+Wed Feb 14 16:03:25 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Made some sanity checks to silc_server_daemonise like to check
+         whether the requested user and group actually exists.
+
+       * Added sanity check to SKE's silc_ske_responder_finish to check
+         that the public and private key actually is valid. 
+
+       * Invalidate the client's nickname when receiving Replace ID
+         packet and the Client ID is being replaced.  This means that the
+         server will query the nickname if someone needs it (client)
+         a bit later.
+
+       * Sort the ID Cache in client library when the ID Cache data
+         has changed (needs sorting).
+
+       * Do not allow for SILC client to create several connections to
+         several servers.  The client does not support windows right now
+         and generating multiple connections causes weird behaviour.
+
+         Irssi-silc client does support windows and can handle several
+         connections without problems, see: www.irssi.org and SILC plugin.
+
+       * Fixed some places where client was added to the IDList.  The
+         rule of thumb is following (in order to get everything right):
+         If the client is directly connected local client then the 
+         `connection' argument must be set and `router' argument must be 
+         NULL to silc_idlist_add_client function.  If the client is not
+         directly connected client then the `router' argument must
+         bet set and the `connection' argument must be NULL to the
+         silc_idlist_add_client function.
+
+       * The funtion silc_server_packet_send_local_channel actually did
+         not check whether the client was locally connected or not.  It
+         does that now.  Fixed a bug related to LEAVE command.
+
+       * Fixed Remove Channel User payload parsing bug in server's
+         silcd/packet_receive.c.  Fixed a bug related to LEAVE command.
+
+       * The server's silc_server_save_channel_key now checks also the
+         global ID list for the channel as it might not be in the local
+         list.  Fixed a bug related to LEAVE command.
+
+       * Is this the end of the [<unknown>] buglet that has been lurking
+         around for a long time?  A little for loop fix in server's
+         silc_server_command_whois_parse that is used by both IDENTIFY
+         and WHOIS command.  At least, this was a clear bug and a cause
+         of one type of [<unknown>] buglet.
+
+       * WHOIS and IDENTIFY commands call the function
+         silc_server_command_[whois/identify]_check function even if
+         we are not router server.
+
+Tue Feb 13 19:55:59 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added --with-gmp configuration option.  If set the GMP
+         is always compiled in the SILC source tree.  If not set then
+         it is checked whether the system has the GMP3 installed.  If
+         it has then the GMP won't be compiled (the system's headers
+         and library is used), if it doesn't have it then the GMP is
+         compiled in the SILC source tree.
+
+Mon Feb 12 11:20:32 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed RSA private exponent generation to what PKCS #1
+         suggests.  We try to find the smallest possible d by doing
+         modinv(e, lcm(phi)) instead of modinv(e, phi).  Note: this is
+         not security fix but optimization.
+
+Sun Feb 11 18:19:51 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new config entry [Identity] to fork the server and run
+         it as specific user and group.  A patch from Bostik.
+
+       * Imported Dotconf configuration library into lib/dotconf.
+         This will be used to create the SILC configuration files later.
+         It will appear in the distsribution after this commit.
+
+Sat Feb 10 21:13:45 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * A big code auditing weekend happening.  Auditing code for 
+         obvious mistakes, bugs and errors.  Also, removing any code
+         that is obsolete.
+
+         Removed files for being obsolete:
+
+         o lib/silcutil/silcbuffer.c (the buffer interface is entirely in
+         inline in the file lib/silcutil/silcbuffer.h)
+
+         o lib/silcutil/silcbufutil.c (the header has inline versions)
+
+         Changed code to fix possible error conditions:
+
+         o The buffer formatting routines now check that the destination
+         buffer really has enough space to add the data.  This applies for
+         both buffer formatting and unformatting 
+         (lib/silcutil/silcbuffmt.[ch]).  Also, the entire buffer
+         unformatting was changed to accomodate following rules: 
+         XXX_*STRING_ALLOC will allocate space for the data into the pointer
+         sent to the function while XXX_*STRING will not allocate or copy 
+         the data into the buffer.  Instead it sets the pointer from the
+         buffer into the pointer sent as argument (XXX_*STRING used to
+         require that the pointer must be allocated already).  This change
+         makes this whole thing a bit more consistent and more optimized
+         (note that the data returned in the unformatting with XXX_*STRING
+         must not be freed now).  The routines return now -1 on error.
+
+         o Tried to find all code that use buffer_format and buffer_unformat
+         and added return value checking to prevent formatting and
+         especially unformatting errors and possible subsequent fatal
+         errors.
+
+         o Changed ske->x and ske->KEY to mallocated pointers in
+         lib/silcske/silcske.h.  Fixed possible data and memory leak.
+
+         o Added return value checking to all *_parse* functions.  Fixed
+         many memory leaks as well.
+
+         o Added length argument to silc_id_str2id in lib/silccore/id.[ch]
+         so that buffer overflows would not happen.  All code now also
+         checks the return value as it can fail.
+
+Mon Feb  5 20:08:30 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added reconnection support to server if the normal server looses
+         its connection to the router (for example if router is rebooted).
+         The server performs normal reconnection strategy implemented
+         to the server.  Affected file silcd/server.c.
+
+Sun Feb  4 13:18:32 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new packet type SILC_PACKET_SET_MODE that is used to
+         distribute the information about changed modes (for clients,
+         channels and clients channel modes) to all routers in the
+         network.  Updated the protocol specification accordingly.
+
+         Added functions into silcd/packet_send.c and 
+         silcd/packet_receive.c: silc_server_send_set_mode, 
+         silc_server_set_mode.
+
+         Added new files silcmode.[ch] into lib/silccore that implements
+         the encoding and decoding of Set Mode Payload.  Added new type
+         SilcSetModePayload.  Moved the definitions of different modes
+         from lib/silccore/silcchannel.h into lib/silccore/silcmode.h.
+
+Sat Feb  3 15:44:54 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Oops, a little mistake in server's connection authentication 
+         protocol.  The protocol is not ended with FAILURE but with
+         SUCCESS if the authentication is Ok. :)  Affected file is
+         silcd/protocol.c.
+
+       * Implemented NICK_CHANGE notify handling in server in the file
+         silcd/packet_receive.c  The NICK_CHANGE notify is distributed to
+         the local clients on the channel.  After the changing nickname
+         in router environment snhould work and the [<unknown>] nickname
+         should appear no more.
+         The silc_server_replace_id function that receives the Replace ID
+         payload now sends the NICK_CHANGE notify type also in the file
+         silcd/packet_receive.c
+
+       * Changed WHOIS and IDENTIFY command to support the maximum amount
+         of arguments defined in protocol specs (3328 arguments).  This 
+         fixed a bug that caused problems when there were more than three
+         users on a channel.
+
+Fri Feb  2 11:42:56 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added extra parameter, command identifier, to the
+         silc_client_send_command so that explicit command identifier
+         can be defined.
+
+         Changed that ID list routines uses specific command identifier
+         when sending WHOIS/IDENTIFY requests to the server so that they
+         can be identified when the reply comes back.
+
+         Affected files lib/silcclient/command.[ch],
+         lib/silcclient/client.c and lib/silcclient/idlist.[ch].
+
+       * Added `sender' argument to silc_server_packet_send_to_channel
+         to indicaet the sender who originally sent the packet to us
+         that we are now re-sending.  Ignored if NULL.  Affected file
+         silcd/packet_send.[ch].
+
+       * Added some server statistics support in silcd/server_internal.h
+         SilcServerStatistics structure and around the server code.  Also
+         send some nice statistics information when client is connecting
+         to the client.
+
+Thu Feb  1 23:31:21 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed channel ID decoding in server's JOIN command reply in
+         silcd/command_reply.c
+
+       * Fixed braodcasting of replace ID payload to not to send it if
+         we are standalone server in silcd/packet_receive.c.
+
+       * Fixed all channel message sending routines to not to send
+         packets to clients that has router set, since they are routed
+         separately in the same function earlier.  Affects file
+         silcd/packet_send.c and all channel packet sending functions.
+
+        * In USERS reply, res_argv[i] are not allocated, the table
+          is allocated.  Thus changed that free the table, not its
+          internals.
+
+       * In server's whois_check and identify_check if the client is
+         locally connected do not send any WHOIS commands - they are not
+         needed.
+
+Thu Feb  1 21:32:27 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed some minor bugs in client when sending WHOIS command.  The
+         arguments was in wrong order.
+
+       * Removed statis function add_to_channel from server in 
+         silcd/command.c that was previously used with the joining but
+         is obsolete now.
+
+       * Tested USERS command in router environment successfully with two
+         routers, two servers and two clients.
+
+Thu Feb  1 00:54:26 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Reorganized the USERS command and command reply in client library
+         in lib/silcclient/command.c and lib/silcclient/command_reply.c.
+         When the command is given by user we register a pending command
+         callback that will reprocess the command after the reply has been
+         received from the server.  When reprocessing the packet we then
+         display the information.  Thus, the USERS information is displayed
+         now in the command callback instead of in the command reply
+         callback.  The processing of the command is same as previously
+         when server has sent the command reply in the JOINing process.
+
+       * Added to USERS command in silcd/command_reply.c to join the client,
+         we didn't use to know about, to the channel after we've created
+         a client entry for it.  Also, for clienet we did know already still
+         check whether it is on the channel or not and add it if not.
+
+       * Removed silc_server_command_join_notify as the function and its
+         use was obsolete.
+
+Tue Jan 30 22:39:15 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed the client's pending command handling to the same as the
+         server's pending command handling.  It is also now possible to
+         execute command reply functions from other command reply
+         function as the function callbacks for commands and command
+         replies are one and same.  The pending commands are not static
+         list anymore, it is mallocated SilcDList in lib/silcclient/client.h
+         in client connection context.  Thus, pending commands are server
+         connection specific as it is convenient.
+
+         Changed the function silc_client_command_pending and
+         silc_client_command_pending_del and added new function
+         silc_client_command_pending_check.  Removed the 
+         SILC_CLIENT_CMD_REPLY_EXEC, and SILC_CLIENT_PENDING_COMMAND_CHECK
+         macros.
+
+       * Added cmd_ident, current command identifier, to the client
+         connection context in lib/silcclient/client.h to keep track on
+         command identifiers used in command sending.  Client's command reply
+         function handling now supports the mandatory command identifiers.
+
+       * Added SILC_CLIENT_COMMAND_EXEC_PENDING macros to all command reply
+         funtions in client to fully support pending command callbacks.
+
+       * NOTE: the name_list in USERS (old NAMES) command is NOT sent anymore
+         as one of the arguments to the application in the command reply
+         client operation.
+
+       * NOTE: The FORWARDED flag is depracated.  It used to be depracated
+         before first releasing SILC but came back.  Now it is removed again
+         and should come back nomore.  The FORWARDED flag was used only
+         by the JOINing procedure by forwarding the command packet to router.
+         Now, the JOINing procedure has been changed to more generic (due
+         to various router environment issues) and FORWARDED is not needed
+         anymore for anything.  The protocol specification is yet to be
+         updated.
+
+         Now, removed silc_server_packet_forward from server and the flag
+         SILC_PACKET_FORWARDED from lib/silccore/silcpacket.h.
+
+Tue Jan 30 00:05:05 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Renamed NAMES command to USERS command.  The NAMES was named that
+         due to historical reasons.  Now it is renamed.  Also, rewrote
+         parts of the USERS command.  The nickname list is not sent anymore
+         by the server.  Only Client ID and mode lists are sent in the USERS
+         command.  Changed this also to the protocol specification.
+
+         The client now resolves the names and stuff after it receives
+         the USERS list from the server when joining to the channel.
+
+       * WHOIS and IDENTIFY commands has been changed to support multiple
+         Client ID's per command.  One can now search for multiple users
+         in the network by sending only one WHOIS or IDENTIFY command.
+         Changed the code and the protocol specifications.
+
+       * Removed silc_server_command_identify_parse and changed that IDENTIFY
+         uses silc_server_command_whois_parse to parse the request. */
+
+       * If normal server, do not parse the WHOIS and IDENTIFY requests
+         before sending it to the router.  Saves some time.
+
+Sun Jan 28 16:19:49 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed JOIN command on client library.  Wrong number of arguments
+         used to crash the client.
+
+       * Added silc_server_channel_has_global function to check whether
+         channel has global users or not.
+
+       * Added silc_server_channel_has_local function to check whether channel
+         has locally connected clients on the channel.
+
+       * The silc_server_remove_from_one_channel now checks whether the
+         channel has global users or not after given client was removed from
+         the channel.  It also checks whether the channel has local clients
+         on the channel anymore.  If it does not have then the channel entry
+         is removed as it is not needed anymore.
+
+       * The silc_server_notify now checks on JOIN notify whether the joining
+         client is one of locally connected or global.  If it is global then
+         the channel has now global users on the channel and that is marked
+         to the channel entry.  Also, it now saves the global client to
+         global list who is joining and JOINs it to the channel.  This is
+         for normal server, that is.
+
+         Changed silc_server_send_notify_on_channel, 
+         silc_server_packet_relay_to_channel and 
+         silc_server_packet_send_to_channel check if we are normal server
+         and client has router set (ie. global client) do not send the
+         message to that client, as it is already routed to our router.
+
+       * Implemented LEAVE notify type handling in silc_server_notify 
+         function.
+
+       * Tested LEAVE command in router environment successfully.  Tested
+         with two routers, two servers and two clients.
+
+       * Updated TODO.
+
+       * idlist_find_xxx_by_id routines now dumps the ID on the debug mode.
+
+       * Implemented SIGNOFF notify type handling in silc_server_notify
+         function.
+
+       * silc_server_remove_id now removes the client entry from all channels
+         it has joined and thusly sends SIGNOFF notify type.
+
+       * Rewrote the NAMES list generation in server by removing two excess
+         loops.  The lists are created now inside one loop.
+
+Sat Jan 27 22:34:56 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * silc_server_remove_channel_user checks now also global list
+         for channel and client.
+
+       * silc_server_new_channel_user checks now both local and global
+         list for channel and client.  Fixed a bug in client id decoding.
+         Used to decode wrong buffer.
+
+       * silc_server_channel_message checks now both local and global
+         list for channel entry.
+
+       * Tested channel joining (hence JOIN) in router environment
+         successfully.  Tested with two routers, two servers and two
+         clients.
+
+       * Tested channel message sending in router environment successfully.
+
+Thu Jan 11 03:22:57 EET 2001  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_save_channel_key into server.[ch] to save the
+         received channel key in Channel Key payload processing. It is
+         also used in JOIN command reply handling.
+
+         Equivalent function silc_client_save_channel_key added into
+         client.[ch] into client library.
+
+       * Changed JOIN command reply to send information whether the channel
+         was created or not (is existing already) and the channel key 
+         payload.  Changed protocol specs accordingly.
+
+       * Fixed bugs in WHOIS and IDENTIFY command reply sending when
+         the request was sent by ID and not by nickname.  Crashed on
+         NULL dereference.
+
+Sat Dec 23 21:55:07 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a bug in Client library.  IDENTIFY and WHOIS reply functions
+         now correctly save the received data.
+
+       * silc_server_free_sock_user_data now notifies routers in the 
+         network about entities leaving the network.
+
+         At the same time implemented functions silc_server_remove_id
+         and silc_server_send_remove_id to receive and send REMOVE_ID
+         packets.  The packet is used to notify routers in the network
+         about leaving entities.  The ID removed will become invalid in
+         the network.
+
+       * Added function silc_idlist_del_server into server. Removes and
+         free's server entry from ID list.
+
+       * silc_server_private_message function now checks, if we are router,
+         that the destination ID really is valid ID, naturally.
+
+       * In router when NEW_ID packet is received (for new client) the
+         hash of the Client ID is saved in the ID Cache but the
+         client->nickname is set to NULL, instead of putting the hash
+         to it as well.
+
+         IDENTIFY command now also checks that client->nickname must be
+         valid. If it is not if will request the data from the server who
+         owns the client.  Added new function 
+         silc_server_command_identify_check.
+
+       * Added silc_command_set_command into lib/silccore/silcommand.[ch]
+         to set the command to already allocated Command Payload.
+
+       * Tested private message sending in router environment with two
+         routers, two servers and two clients.  Fixed minor bugs and now
+         it works fine.
+
+       * Fixed segfault from client's NAMES command. Used to crash if
+         not on any channel.
+
+       * Forwarded packets must not be routed even if it is not destined
+         to the receiver.  Changed server code comply with this.
+
+Sun Dec 17 14:40:08 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added `require_reverse_mapping' boolean value to ServerParams
+         structure. If TRUE (not default) the server will require that
+         the connecting host has fully qualified domain name.
+
+         If the reverse mapping is not required and hostname could not be
+         found the IP address is used as hostname.
+
+Sat Dec 16 17:39:54 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented version string checking to both client and server.
+         The check is incomplete currently due to the abnormal version 
+         strings used in development version of SILC.
+
+       * Changed all command functions in server to use the new
+         CHECK_ARGS macro.
+
+Fri Dec 15 15:55:12 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed char *data to unsigned char *data in ID Cache system to
+         support binary data as ID Cache data. Changed code to support
+         binary data in lib/silccore/idcache.c.
+
+       * Renamed silc_server_packet_relay_command_reply to 
+         silc_server_command_reply as it is normal packet receiving
+         function. Rewrote the function to accept command replys for
+         servers and not only for clients.
+
+       * Mark remote router always as registered server if we are connecting
+         to it.  Otherwise, commands sent by the router to us are ignored.
+
+       * All ID List find routines now returns the ID Cache Entry pointer
+         as well if requested.
+
+       * WHOIS command works now in router environment, tested with two
+         routers, two servers and two clients.
+
+       * Cleaned up and rewrote IDENTIFY command. IDENTIFY should work now
+         in router environment (as it is almost equivalent to WHOIS) but
+         hasn't been tested thoroughly.  Added new functions:
+
+         silc_server_command_identify_parse
+         silc_server_command_identify_send_reply
+         silc_server_command_identify_from_client
+         silc_server_command_identify_from_server
+
+       * Disabled route cache adding because adding two different ID's with
+         same IP replaces the old cache entry thus giving wrong route.
+         The entry->router->connection is always the fastest route anyway
+         so route cache may not be needed.  Of course, new routes maybe
+         established after receiving the ID when the entry->router->connection
+         might not be anymore the most optimal.
+
+Thu Dec 14 15:55:35 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Add route cache for received ID for fast routing.
+
+       * Added silc_server_packet_route to route received packet on router
+         that is not destined to us.
+
+       * Renamed silc_server_get_route to silc_server_route_get.
+
+       * Added id_string and id_string_len fields into SilcServer to
+         include encoded ServerID for fast comparing without excess
+         encoding of the ID's.
+
+       * Cleaned up WHOIS command on server side. Added following static
+         functions:
+
+         silc_server_command_whois_parse
+         silc_server_command_whois_check
+         silc_server_command_whois_send_reply
+         silc_server_command_whois_from_client
+         silc_server_command_whois_from_server
+
+       * Added macro SILC_SERVER_COMMAND_CHECK_ARGC to check mandatory
+         arguments in command replies. All command functions should be
+         updated to use this macro.
+
+Sun Dec 10 23:52:00 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Minor typo fixes on command reply handling on server.
+
+Tue Nov 28 11:05:39 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_command_add_to_channel internal routine to add
+         the client to the channel after router has created the channel and
+         sent command reply to the server.
+
+       * Added generic silc_server_send_command to send any command from
+         server.
+
+       * Use static buffer with ID rendering instead of duplicating data.
+
+Mon Nov 27 21:39:40 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed a channel user mode bug when joining to a channel server gave
+         everybody channel founder rights, oops.
+
+       * We mark ourselves as the router of the incoming server connection
+         if we are router ourselves.  This way we can check in some packet
+         sending functions whether it is locally connected server.  For
+         incoming router connections we put NULL.
+
+       * For router sending packets locally means now always sending the
+         packet cell wide; to local clients and local servers.  For normal
+         server sending packet locally means sending it to only local
+         clients.
+
+       * Fixed the JOIN command to really work in router environment.  If the
+         channel is created it is always created by the router.  Router is
+         also responsible of making the initial joining to the channel,
+         sending JOIN notify to the sending server and distributing 
+         NEW_CHANNEL and NEW_CHANNEL_USER packets.  Hence, if the channel
+         does not exist server doesn't do anything else but forward the
+         command to the router which performs everything.
+
+       * Added silc_server_send_channel_key function to send the Channel Key
+         payload.
+
+       * Added silc_server_create_channel_key to create new channel key.  The
+         channel key is now re-generated everytime someone joins or leaves
+         a channel, as protocol dictates.  Note: channel->key_len is the
+         key length in bits.
+
+Wed Nov 22 22:14:19 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Splitted server.[ch] finally.  Created now packet_send.[ch] and
+         packet_receive.[ch] to separate packet sending and receiving
+         routines.  The server.[ch] now includes everything else including
+         actual packet processing (writing and reading data) and other
+         server issues.
+
+         Renamed silc_server_private_message_send_internal to
+         silc_server_send_private_message.  The routine is still though
+         used only to relay private messages as server does not send
+         private messages itself.
+
+         Renamed silc_server_new_channel to silc_server_create_new_channel
+         and added new function sicl_server_new_channel that handles the
+         incoming New Channel packet.  Added also new sending function
+         silc_server_send_new_channel to send New Channel Payload.
+
+       * Added new function silc_server_notify to process incoming notify
+         packet to the server/router. Server may then relay the notify
+         to clients if needed.
+
+       * Added new function silc_server_new_channel_user to process incoming
+         New Channel User packet.  Router will redistribute the packet and
+         send JOIN notify to its local clients and locally connected servers
+         if needed.  Normal server will send JOIN notify to its local client
+         on same channel when received this packet.  Added also corresponding
+         sending function silc_server_send_new_channel_user to sent the
+         payload.
+
+       * Added boolean route argument to send_notif_to_channel and
+         packet_send_to_channel functions to attempt to route the packet
+         if it is TRUE and send only locally if it is FALSE.
+
+Tue Nov 21 19:49:31 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * silc_server_replace_id now broadcasts the received replace ID
+         packet if it is not broadcast packet already. The router must
+         broadcast to inform other routers about changed ID.
+
+       * Added backpointer to server's router into SilcServer context in
+         silcd/server_internal.h.
+
+       * Fixed silc_server_packet_broadcast to send correct broadcast
+         packets.
+
+       * The channel key is now distributed to the local client as soon
+         as it is received from the router (in router environment) so that
+         no other packet may be sent for the channel until client has 
+         received the key.
+
+       * silc_server_remove_channel_user now broadcasts the received
+         Remove Channel User packet if it is not broadcast packet already.
+         The router must broadcast to inform other routers about removed
+         channel user.
+
+       * Added users field into SilcPacketContext that is a reference count
+         of the context.  One can increase the reference count by calling
+         silc_packet_context_dup which is now changed to just increase the
+         reference count instead of duplicating the data.  The reference
+         count is decresed by calling silc_packet_context_free that will
+         free the data after the reference count hits zero.
+
+         For now on the packet context and everything allocated into it
+         (including the raw packet from network) must be freed by calling
+         the new silc_packet_context_free function.  Added also new function
+         silc_packet_context_alloc that must be used now to allocate the
+         context.  This also means that if a routine is asynchronous from
+         silc_[client/server]_packet_parse_type the packet context must
+         be duplicated by calling silc_packet_context_dup.  Otherwise it
+         gets free'd after silc_[client/server]_packet_parse_type returns.
+         Also, one must remember that if packet is duplicated then its 
+         reference count must be decresed by calling the free function as
+         many times as it was duplicated.
+
+       * Changed SilcBuffer field from protocol contexts to SilcPacketContext
+         from both client and server.
+
+Mon Nov 20 23:47:03 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Made joining to a channel working in router environment.
+
+       * Cleaned up JOIN command on server side and create function
+         silc_server_command_join_channel internal routine to make the
+         joining happen.
+
+Thu Nov  9 21:12:39 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed silc_command_pending list to SilcDList.  Also, added
+         `ident' field to SilcServerCommandPending structure to identify
+         the reply and to call correct callback.
+
+         Added silc_server_command_pending_check function to replace the
+         corresnponding macro.  The silc_command_pending list is not
+         extern anymore.
+
+       * Added silc_command_set_ident into lib/silccore/silccommand.[ch]
+         to set identifier to previously allocated Command Payload.  It
+         is used to set identifier for command when resending Command
+         Payload.
+
+       * Added silc_command_payload_encode_payload to encode Command
+         Payload buffer from SilcCommandPayload structure.
+
+       * Added silc_argument_payload_encode_payload to encode Argument
+         payload buffer from SilcArgumentPayload structure.
+
+Wed Nov  8 21:03:28 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed WHOIS command to support router connection on server side.
+         The whois request is always sent to router unless the server is
+         standalone server.  After server has received the reply from the
+         router will it send the reply to the client.
+
+       * Added silc_server_packet_broadcast into silcd/server.[ch] to
+         broadcast received broadcast packet.  The function is used only
+         by router.  The broadcast packet is always sent to the router's
+         primary route.
+
+       * Added silc_id_render function in lib/silcutil/silcutil.[ch] to
+         render given ID to printable string, for log files for example.
+
+Tue Nov  7 22:14:19 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Made basic router to router connections working.  At least they
+         can now connect to each other but nothing really works the way
+         they are supposed - yet.
+
+       * Added new initiator token to RouterConnection configuration
+         file in silcd/serverconfig.[ch].  It is used to tell whether we
+         are the initiator to the remote router or whether we'll expect
+         the other end to connect.
+
+       * Moved registering of listener task to silc_server_init, hence
+         the server starts listenning as soon as it is run, even if it
+         does not have connections to other routers.  Let's see how well
+         this will work.
+
+       * Changed default connection retry timeouts for more suitable in
+         silcd/server.h.
+
+       * Removed cipher and such arguments from silc_idlist_add_client
+         and silc_idlist_add_server prototypes from silcd/idlist.[ch].
+         Added new function silc_idlist_add_data to add the keys and stuff
+         to any ID entry.
+
+       * Added SilcIDListData structure and added it to SilcClientEntry
+         and SilcServerEntry as their first field in the structure.  This
+         way we can explicitly cast the ID entries to the SilcIDListData
+         structure and get common data for the entries.  In past, we had
+         to first check what type of connection it is and then cast it to
+         correct ID entry type.  Now, we can directly cast the opaque
+         pointer to the SilcIDListData (no matter what ID entry it actually
+         is) and get the data needed.
+
+Mon Nov  6 21:56:12 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Wow, found a bug in scheduler.  The scheduler uninitialized itself
+         in some circumstances even if threre were timeout tasks, though not
+         IO tasks, but tasks anyway.  Now fixed.
+
+       * Defined SilcServerConnection structure to hold connection specific
+         stuff about directly connected servers and routers.  The definition
+         is currently in silcd/server_internal.h.  I thought about having
+         a bit more important role fro this struct but for now it is used
+         only when connecting to other server (or router actually).
+
+       * Added connecting retry support in server when connecting to
+         router(s).  The retry feature implement exponential backoff
+         algorithm.  Also, added SilcServerParams structure to hold default
+         parameters for server.  For now, it include these retry settings
+         and are hard coded.  After server is moded to be as Silc Server
+         Library this structure will be more important.
+
+Sun Nov  5 22:28:44 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed client librarys channel->clients table to SilcList and
+         changed code accordingly.
+
+Thu Nov  2 16:28:01 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed client's channel table to SilcList and changed code 
+         accordingly.  Also changed SilcChannelClientEntry to include back-
+         pointer to the channel so that client entry can use that structure
+         as list as well and we have fast cross-reference to the channel.
+         This change dramatically decreased the complexity of channel
+         handling with client entry and vice versa (removed one extra
+         loop when searching for channel entry from many functions).
+
+       * Changed server->sim from table to SilcDList and changed code
+         accordingly.
+
+       * NAMES command can now be used from user interface.  It will show
+         the user list on the channel, neatly.
+
+       * Added realname pointer to SilcClientEntry in lib/silcclient/idlist.h.
+         Code now saves realname of the user if it becomes available.
+
+       * Renamed configure.in to configure.in.pre and made ./prepare
+         script to automatically add correct version string to
+         configure.in which it creates from configure.in.pre.
+
+Wed Nov  1 17:21:26 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * NAMES command reply now shows users mode with the nickname when
+         joining to channel.
+
+       * Moved silc_client_ch[u]mode[_char] functions from 
+         silc/clientutil.[ch] to lib/silcclient/client.[ch].  Though, that
+         place sucks, they are utility functions and should be in some
+         other file.
+
+       * Fixed some unsigned int's to unsigned short's.  Patch by cras.
+
+       * Fixed contrib/getopt*.[ch] to not require config.h.  Patch by
+         cras.
+
+Tue Oct 31 20:10:37 EET 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Updated README.
+
+       * Added TRQ (efficient deque and list library) into lib/trq.  This is
+         a very good list library that is currently used in the SILC.  Defined
+         SilcList API over the library because I didn't like the API very
+         much.  See lib/trq/silclist.h for the API and examples of how to
+         use the API.  Fixed various places in the code to use the new
+         SilcList API. The SilcList is meant for lists that has a structure
+         already defined as a list.  It is not suitable to add just some
+         context to the list (in TRQ, the context is the list actually).
+
+         So, I defined SilcDList that can be used for the purpose where 
+         predefined list structure does not exit.  This can be used as
+         such list.  Now some context just can be added to the SilcDList.
+         Currently this list is not used in the SILC just yet, though there
+         are a lot places where this can replace dynamically allocated
+         tables and I will fix these places, later, to use SilcDList.
+         See lib/trq/silcdlist.h for SilcDList (they are all inline functions,
+         and use TRQ internally).
+
+         Also fixed some annoying warning messages that the original TRQ
+         code generated.  Also minor changes to TRQ's Makefile.in.
+
+       * Added support for querying by Client ID to both WHOIS and 
+         IDENTIFY commands into server, as required by the protocol.
+
+       * Removed method function pointers from SilcBuffer structure. They
+         weren't used to anything and just increased the context size for
+         no good reason.  This change also made silc_buffer_alloc and
+         silc_buffer_free functions inline functions.
+
+       * Disabled command flooding detection support until it's fixed so 
+         that it accepts commands in but does not execute them more than once
+         in two seconds.
+
+       * Added silc_net_localhost(), to return local hostname, into
+         lib/silcutil/silcnet.[ch].  Also added client->hostname pointer
+         that must be initialized before calling silc_client_init.
+
+       * Added new function: silc_server_send_notify_on_channels to send
+         notify messages to all channels client has joined.  It is assured
+         that the message is sent only once per client.
+
+       * Moved silc_log_format (from lib/silcutil/silclog.[ch] into
+         lib/silcutil/silcutil.[ch] as silc_format function.  The new 
+         function is generic and is used by server as well, not only by
+         the logging routines.
+
+       * Added new SKE status type: SILC_SKE_STATUS_BAD_VERSION to indicate
+         the provided version string was not acceptable.  Added new function:
+         silc_ske_check_version into lib/silcske/silcske.h.  The function
+         must be implemented by the application (client or server) and it
+         does not reside in the SKE library.  The function checks the version
+         string remote end sent.
+
+       * Added back pointers (to opaque context and to SilcSocketConnection) 
+         into SilcPacketContext structure into lib/silccore/silcpacket.h.
+
+       * Added silc_packet_context_dup into lib/silccore/silcpacket.[ch] to
+         duplicate packet context structure.
+
+       * Changed `notify' client operation to send same arguments as client
+         receives from server except for ID's.  ID's are mapped to correct
+         ID entry and that is returned.  Also, if channel entry is not sent
+         by server but the notify is for channel the channel entry is sent
+         to application (otherwise application doesn't know that it is for
+         channel (library gets it from packet's Destination ID)).
+
+       * Added silc_client_remove_from_channels into client library to 
+         remove a client from all channels it has joined to.  Used when 
+         received SIGNOFF notify from server.  Added also new function
+         silc_client_replace_from_channels to replace old ID entry with
+         new ID entry on all channels.  Used when received NICK_CHANGE
+         notify from server.
+
+       * Fixed ID Cache list handling in silc_idlist_get_client in 
+         lib/silcclient/idlist.c.  Also, added silc_idlist_get_client_by_id
+         to get (or query) client by ID.
+
+       * Updated TODO list.
+
+       * Added connection authentication status message defined by the
+         protocol: SILC_CONN_AUTH_OK and SILC_CONN_AUTH_FAILED and added the
+         support for these into the code in client and server side.
+
+       * Added generic function silc_client_send_command to send any command
+         with variable argument list.  Application should use this function
+         to send commands if the command functions provided by the library
+         does not suite for the application's user interface needs.
+
+       * Added new `failure' client operation.  Application is notified about
+         received failure packet if client is executing a protocol.  In this
+         case the protocol's execution has failed.
+
+       * Added SKE's end notify to send the SKE_SUCCESS notify message that
+         is required by the protocol.
+
+       * Added SILC_PROTOCOL_STATE_FAILURE to indicate received failure
+         packet from remote.  SILC_PROTOCOL_STATE_ERROR indicates local
+         error at our end.
+
+       * Added status flag to SilcSKE object to indicate realtime status
+         of the SKE protocol.
+
+       * Application receives now exactly same command reply arguments as
+         the library receives from server.  However, if ID is received the
+         corresponding ID entry is returned to the application (eg. Client
+         ID is mapped to correct SilcClientEntry entry and that is returned).
+         Changed command_reply client operation due to this change.
+
+       * Changed all ID's in commands and in command replys as ID Payloads.
+         Change affected both client and server side codes.
+
+         All ID's sent in SILC network (with execption of ID's in SILC
+         Packet header) are sent in ID Payload to support variable length
+         ID's.
+
+       * Server now notifies nick changes and notifies all clients on
+         the channels about the new nickname (about the new Client ID,
+         actually).
+
+       * Implemented CMODE command to change channel modes. Supports all
+         channel modes defined by the protocol specs except ban and invite
+         lists. (Also, private channel key mode is supported but support for
+         setting private channel key in client is missing, thus, this mode
+         has no effect on client side (except that server requires that the
+         client uses private channel key and normal channel traffic does not
+         work anymore)).
+
+         Also, invite mode works per se, but INVITE command does not work
+         yet correctly, so you can set channel as invite only channel but
+         inviting clients to the channel does not work (it is yet to be
+         thought what's the best way to do it).
+
+       * Added new command SILC_COMMAND_CUMODE to change user mode on the
+         channel.  Defined user modes: CHANNEL_FOUNDER and CHANNEL_OPERATOR.
+         Implemented CUMODE command to change user's mode on the channel.
+         Supports all modes defined by the protocol specs.
+
+       * Added NAMES command reply to return users modes on the channel.
+
+       * Removed unnecessary and slow ciphers from lib/silccrypt.
+
+       * Set SO_KEEPALIVE option to connection sockets by default.
+
+       * Added new command reply status: SILC_STATUS_USER_NOT_ON_CHANNEL.
+
+       * Added notify types: MOTD, CMODE_CHANGE and CUMODE_CHANGE.  Also,
+         redefined the Notify Payload into protocol specs.
+
+       * Added silc_id_payload_parse_id to get ID directly from raw
+         ID payload data.
+
+Mon Oct  9 20:57:02 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Changed SILC_COMMAND_IDENTIFY in protocol specification to 
+         accept searching by Client ID as well.
+
+       * Added support for LEAVE and SIGNOFF notify types in client library.
+
+       * Added silc_id_payload_parse_data into lib/silccore/silcpayload.[ch]
+         to parse ID Payload from raw data.
+
+Sun Oct  8 19:33:08 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added flags parameter into silc_ske_assemble_security_properties
+         function in lib/silcske/silcske.[ch].
+
+       * Changed notify client operation to fit better for notify messages
+         sent by server.  The notify payload received from server is now
+         passed to the application (after parsing it to SilcNotifyPayload).
+         It is application's responsibility to retrieve the arguments
+         from the payload and show the message the way it wants.  The message
+         sent by server is implementation specific.
+
+       * Changed public keys to comply with the protocol specification.
+         Old public keys are not supported anymore and are not compatible.
+
+       * Removed nickname from Channel Payload as the latest draft removed
+         it.  The client must resolve the nickname from the NAMES command
+         reply received when it joined the channel.
+
+         Also, changed all channel_xxxx_payload to channel_payload_xxxx.
+
+Sat Oct  7 21:55:01 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed some errors in protocol specification drafts.
+
+       * Created lib/silccore/silcnotify.c to implement Notify Payload
+         encoding and decoding, lib/silccore/silcpayload.[ch] to implement
+         generic payloads described by protocol specifications.  The file
+         includes implementations for ID Payload and Argument Payload.
+
+       * Changed Command Payload implementation to use the new Argument
+         Payload.  Changed command_xxxx_payload to command_payload_xxxx
+         to comply with SILC coding conventions.
+
+       * Added suppport for Argument Payload handling in Notify Payload
+         implementation as protocol requires it.  Added the new support
+         into server and client lib as well.
+
+Thu Oct  5 21:16:28 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added support for multiple nicknames on same channel.  [n] is
+         added locally to the nickname if there are more than one same
+         nicknames on the channel.
+
+       * Server now sends all nicknames that matched WHOIS request.
+         Client also shows the list received from server.
+
+       * Added TOPIC command to client side.  User can now set and show
+         current topic on channel.
+
+       * Added MOTD command to client and server.  Also, server sends the
+         motd when client connects to the server.
+
+       * Changed version strings to comply ISO 8601.
+
+Wed Oct  4 23:29:06 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed protocol error handling in client library.  It should now
+         cope even if the SKE fails for some reason.
+
+       * Made new protocol specification drafts for submitting to IETF.
+
+       * Implemented TOPIC command to server in silcd/command.c.
+
+       * Added two new notify types into lib/silccore/silcnotify.h:
+         SILC_NOTIFY_TYPE_NICK_CHANGE and SILC_NOTIFY_TYPE_TOPIC_SET to
+         notify nickname change and topic setting/change on a channel.
+
+       * API change of command_reply operation in client library.  The
+         application gets now the status type received from server as well.
+
+Sat Sep 30 16:57:42 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Removed the function just added to lib/silcutil/silcschedule.[ch].
+
+       * Cras fixed and optimized the packet handling even further and
+         it should work now.  Minor change to the prototype of function
+         silc_packet_receive_process in lib/silccore/silcpacket.[ch].
+
+Sat Sep 30 08:48:47 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added new function into lib/silcutil/silcschedule.[ch]:
+         silc_schedule_with_fd to select() a specified fd.  The function
+         returns after timeout expires or data arrives or goes.  The
+         function is used by packet routines to wait that all data is
+         received from network.
+
+       * Fixed data reading from network in lib/silccore/silcpacket.c.
+         The code now assures that all data is read from the fd and then
+         continues packet processing.  This was a bug fix since the code
+         used to drop some data in some circumstances.
+
+       * Added new function into lib/silcclient/client.[ch]:
+         silc_client_start_key_exchange to start key exchange after
+         connection has been established to server.  The code internally
+         now uses this funtion but its main purpose was to provide it
+         for applications that perform their own connecting.  After
+         application has created a connection it merely calls this
+         function to start the key exchange between client and server.
+         The library takes care of everything else after that.
+
+         Updated also lib/silcclient/README to explain the usage of
+         this new function.
+
+       * Do not send to application information that connection has
+         been established.  Application gets notified it by connect
+         operation anyway.
+
+Thu Sep 28 23:40:19 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Applied cras's patch to add silc_schedule_one function.  The
+         function runs scheduler once and returns.
+
+       * Fixed the scheduler after cras messed it up.  The timeout
+         handling works now as it's supposed to work.
+
+       * Added into lib/silccore/ silcnotify.h to include notify
+         message types support.  Changed silc_server_send_notify*
+         functions, in server.[ch], to support those new notify types.
+         Added the support for the notify types into client library,
+         as well.  Added new notify client operation into ops.h in
+         lib/silcclient/.
+
+       * Changed silc_server_packet_send_to_channel to send normal
+         packets instead of just channel message packets.  The function
+         is now used to send the notify packets to channels.  It is not
+         used to send channel message packets anymore, as server never
+         sends them anymore.
+
+       * Added explicit casting into lib/silcutil/silcbuffmt.c to few
+         va_arg()s as it seems to require it nowadays.  I guess, if SILC
+         is compiled with older va_arg() the new code should work anyway.
+
+Wed Sep 13 18:10:14 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Splitted core library.  Core library (lib/silccore) includes
+         now only SILC protocol specific core (and common) components.
+         Created new utility library (lib/silcutil) that includes more
+         generic purpose stuff.  The stuff for util library was taken
+         from the old core library.  This was minor and easy split.
+
+       * Created SILC Client Library (lib/silcclient) that includes
+         implementation of the SILC client without user interface.  This
+         was major move from silc/ directory.  The code has been changed
+         so that it is transparent towards the user interface.  The
+         silc/ directory includes now the same user interface as before
+         and it uses the new client library.  Read lib/silcclient/README.
+         Basicly, the client library performs everything else related
+         to SILC except user interface handling.  Also, configuration
+         files are considered to be part of user interface and library
+         does not handle them.
+
+         This change also changed a lot of structures, function naming etc.
+         Most important change was that SilcClientWindow object was
+         renamed to SilcClientConnection in the client library.  Created
+         also new file lib/silcclient/ops.h.  Also added new files
+         silc/local_command.[ch] and silc/client_ops.[ch].
+
+         All these changes were made to make it easier for user interface
+         designers to create what ever user interface for the SILC client
+         they want.
+
+         It is also expected that the server will be moved to lib
+         directory as well and SILC Server Library will be created;
+         sometimes in the future.
+
+       * Removed Local commands from lib/silccore/silccommand.h as
+         they are application specific and new client library does not
+         handle any of those anymore.
+
+       * Several functions moved to lib/silcutil/silcutilc.[ch] from
+         old client implementation in silc/.
+
+       * Added support for callback functions in SILC_LOG_* macros.
+         Application can now set its own callbacks that will be called
+         instead of using the default functions that will always print
+         the debug messages to stderr (or stdout).  Also, debugging can
+         now be disabled by setting silc_debug to FALSE and re-enabled by
+         setting it to TRUE.  Note, that logging will still work even
+         if debugging is disabled.
+
+         New functions in lib/silcutil/silclog.[ch]: silc_log_set_callbacks,
+         silc_log_reset_callbacks, silc_log_set_debug_callbacks and
+         silc_log_reset_debug_callbacks.
+
+       * To enable debugging in silc client one must give now -d
+         option on command line.
+
+       * Changed silc_schedule_init to automatically allocate task queues
+         if they are not allocated before calling it.
+
+Thu Sep  7 10:49:33 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added GMP 3.1 into math library.
+
+Sun Aug 20 21:27:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SILC_PACKET_REMOVE_CHANNEL_USER to remove a client from
+         a channel in SILC network.  The packet is used by servers and
+         routers to notify other routers that user has left a channel.
+         This little feature was missing until now.  Added the feature
+         to protocol specification as well.
+
+         Added functions: silc_server_send_remove_channel_user and
+         silc_server_remove_channel_user into server.[ch].
+
+       * Added SILC_PACKET_REKEY and SILC_PACKET_REKEY_DONE into
+         lib/silccore/silcpacket.h.  However, they are not implemented
+         yet.
+
+Sat Aug 19 23:04:16 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed joining to a channel and sending channel messages
+         between server and router.  The channel message sending should
+         now work inside a cell.
+
+Tue Jul 25 20:46:13 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the private message sending between server and router.
+         The private message sending should now work inside a cell.
+
+       * Added silc_server_replace_id into server.[ch] to replace
+         existing ID in the SILC network.
+
+       * Added silc_idlist_find_server_by, silc_idlist_replace_client_id
+         and silc_idlist_replace_server_id into idlist.[ch] in server.
+
+Mon Jul 24 18:33:31 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed the server to server connections.  Server can again now
+         connect to router.  Router to router connections probably does
+         not work just yet.
+
+Thu Jul 20 13:15:01 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added dynamic protocol registering support.  Now protocols can
+         registered and unregistered on the fly.  Patch by cras.
+
+Wed Jul 19 19:08:46 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added lib/contrib directory to hold routines that some platforms
+         don't have but are needed by SILC.
+
+       * Added getopt.c, getopt1.c and getopt.h from GNU C library
+         into lin/contrib to provide getopt() and getopt_long() for
+         those who don't have it.
+
+Tue Jul 18 20:41:20 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added AWAY command to client.  When away message is set and
+         client receives a private message packet the client automatically
+         replies to the sender with the away message.
+
+       * Fixed a bug in lib/silcmath/mpbin.c: silc_mp_mp2bin.  This
+         bug seemed to be the cause of recent problems when compiling
+         with gcc-2.95.
+
+       * Added version detection support to SKE protocol specification
+         and added the new changes to the SKE implementation as well.
+         There were other minor changes in the SKE protocol as well.
+
+         Many changes in lib/silcske/silcske.[ch] and in
+         lib/silcske/payload.[ch].
+
+       * Added ^U functionality, clear input line.  Patch from cras.
+
+Mon Jul 17 23:33:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Mainly small bugfixes on core library.  Fixed some debugging
+         logging and buffer overflow in silclog.c.
+
+       * Updated config.sub and config.guess on the distribution tree.
+
+Sat Jul 15 15:33:48 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added command lagging support in server. Client may execute
+         commands now only once in two seconds.
+
+Thu Jul 13 22:10:21 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Optimized packet reception. MAC computation and checking is now
+         also more optimized.  A lot previously duplicated code is now
+         used as generic by both client and server.
+
+       * Fixed key pair generation in clientutil.c
+
+Wed Jul 12 18:28:07 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added into lib/silccore/silcbufutil.[ch] new function;
+         silc_buffer_realloc.
+
+       * Moved generic packet sending/encryption functions to 
+         lib/silccore/silcpacket.[ch] from client and server.  Some
+         rewriting of the functions.
+
+       * Moved all generic packet reception/decryption functions to
+         lib/silccore/silcpacket.[ch] from client and server.  The
+         packet processing is now much cleaner in both client and server.
+         These were major changes in both client and server.
+
+       * Created many common functions in server to do packet sending.
+         Previously code were duplicated a lot, this has been removed
+         with these changes.
+
+Tue Jul 11 20:27:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Rewrote major parts of the ID cache system.  Don't know 
+         whether it is better now or not but at least the API is more
+         cleaner now.
+
+       * Major rewrite on ID cache stuff on client because of the ID
+         cache API changes.  Added idlist.c to client.
+
+       * Also major rewrite on ID cache stuff on server as well.
+         Major rewrite of idlist.[ch]. SilcXXXList's are now named
+         SilcXXXEntry's.  We won't keep anymore idlist specific pointers
+         in hand, instead they are all put into the ID cache system now.
+         All server_idlist_* routines uses ID cache now instead of
+         traversing its own lists (those lists does not exist anymore).
+         SilcIDList though still exists.  Also, SilcXXXEntry's are
+         now pointers.
+
+Sun Jul  9 15:19:24 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Finally made the SKE implementation compliant to the protocol
+         specification.  All mp integers are now binary encoded as
+         opposed being HEX encoded.
+
+       * Added lib/silcmath/mpbin.[ch].  Encoding mp intergers to and
+         from binary data.
+
+       * Added into lib/silccore/silcutil.[ch] PEM encoding/decoding
+         functions: silc_[encode/decode]_pem.  Also added function
+         silc_encode_pem_file to PEM encode with newlines ('\n') for
+         saving into a file.
+
+       * SILC public keys are now encoded either PEM or binary.  Same
+         option is for private keys as well.  By default private keys
+         are binary encoded and public keys PEM encoded.  Silly HEX
+         encoding were removed.
+
+       * Added into lib/silccrypt/silchash.[ch] silc_hash_fingerprint
+         function to create fingerprints.
+
+       * Fixed a bug in SHA1; does not change the original data anymore.
+
+       * Partly implemented INFO command on client and server side.
+         Fixed CLEAR command.  Changes to SERVER command; show current
+         server(s) when giving command without arguments.  Added
+         VERSION command to client.
+
+       * Added check to server that unregistered connections cannot
+         execute commands (unless it is specificly allowed).
+
+Thu Jul  6 18:12:24 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Fixed screen refresh.
+
+       * Fixed channel joining bug from client.  On some circumstances
+         client tried to join to a channel it had already joined.
+
+       * Added public key verification process into client's protocol.c.
+         The client now verifies the public key from user and saves
+         it into ~./silc/serverkeys/ directory. 
+
+         Added into: clientutil.[ch]: silc_client_verify_server_key.
+
+       * Changed SKE protocol's silc_ske_initiator_finish function
+         to accept callback function that verifies the received public
+         key.  Removed old silc_ske_verify_public_key function.
+
+Wed Jul  5 19:19:02 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added into silcpkcs[ch]: silc_pkcs_public_key[_data]_set and
+         silc_pkcs_private_key[_data]_set.
+
+       * Made the password and public authentication more cleaner in
+         server's protocol.c.
+
+       * Removed historic and obsolete protocol `channel_auth' from
+         both client and server.
+
+       * Removed wrong way of sending command status messages from
+         server to client in server's command.c.  The old way violated
+         protocol specification.  
+
+         Changes to silccore/silccommand.[ch]: removed
+         silc_command_encode_status_payload -> not needed anymore,
+         changed silc_command_encode_payload_va to accept extra
+         argument on variable argument list.  The argument type must
+         now be provided to the function.  Also, added new function:
+         silc_command_encode_reply_payload_va which is same as
+         normal command_encode_payload_va except command status type
+         is provided as extra argument.
+
+Tue Jul  4 18:26:39 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added ~./silc directory handling.  The directory includes the
+         public and private keys for the client.
+
+         Added silc_client_check_silc_dir, silc_client_create_identifier
+         and silc_client_load_keys.
+
+       * Implemented SILC protocol compliant public key.  Added public
+         and private key saving to and loading from files.
+
+         Added into silcpkcs.[ch]: silc_pkcs_encode_identifier,
+         silc_pkcs_public_key_encode[_data], silc_pkcs_public_key_decode,
+         silc_pkcs_private_key_encode[_data], silc_pkcs_private_key_decode,
+         silc_pkcs_public_key_alloc, silc_pkcs_public_key_free,
+         silc_pkcs_private_key_alloc and silc_pkcs_private_key_free.
+
+         Implemented: silc_pkcs_save_[public/private]_key[_data] and
+         silc_pkcs_load_[public/private]_key.
+
+Mon Jul  3 18:51:27 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added silc_server_get_route (route.[ch]) to get connection
+         data for the fastest route for given ID.
+
+       * Implemented INVITE command on client and server.  The command
+         were re-defined in the SILC Protocol Specification and the
+         implementation now complies with the specification.
+
+       * Implemented PING command on client and server.
+
+       * Implemented NAMES command on client and server.  The server side
+         supports currently only normal server not router server yet.
+         Some changes to NAMES definition in SILC protocol specification.
+
+Sun Jul  2 18:23:01 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Implemented LEAVE command on client and server.
+
+       * Previously deprecated SILC_PACKET_FORWARDED flag is now in use 
+         again.  This change was made to the protocol as well.  Server
+         should not violate the protocol specification anymore.
+
+Fri Jun 30 14:03:26 EEST 2000  Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+       * Added SOCKS4 and SOCKS5 support to SILC client.  SOCKS5
+         was tested.  SOCKS4 was not but should work anyway.
diff --git a/CREDITS b/CREDITS
new file mode 100644 (file)
index 0000000..e070a2f
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,84 @@
+----------------------------------------------------------------------------
+
+This is the credits file of people that have contributed to the SILC
+project.  It is supposed to by sorted by name and of the following format:
+name (N), email (E), web-address (W), PGP key ID and fingerprint (P),
+description (D), and snail-mail address (S).  The format is from the Linux
+kernel credits file. :)
+
+----------------------------------------------------------------------------
+
+N: Mika "Bostik" Boström
+E: bostik@lut.fi
+W: http://www.lut.fi/~bostik
+D: Server runs as a daemon on dedicated account
+D: Fixed inconsistencies in server side code routine names
+D: Transition to new configuration file format
+S: Skinnarilankatu 28 E 2
+S: 53850 Lappeenranta
+S: Finland
+
+N: Johnny Mnemonic
+E: johnny@themnemonic.org
+W: http://www.themnemonic.org/
+P: 1024D/34E2AB40 9AC6 1460 A5D0 4DB7 70D0  5DA5 C17F 50CD 34E2 AB40
+D: RPM packages
+D: silclog, misc bugfixes
+S: 35100 Padova
+S: Italy
+
+N: Pekka Riikonen
+E: priikone@silcnet.org
+E: priikone@ssh.com
+P: 1024/A924ED4F AD 82 53 2D 84 FF C7 D1  FF 63 19 0E 1A 78 9F 8A  A9 24 ED 4F
+D: Protocol architet
+D: Main developer
+S: Snellmanninkatu 34 A 15
+S: 70100 Kuopio
+S: Finland
+
+N: Juha Räsänen
+E: jmrasane@lut.fi
+D: Persistent nagger
+D: Desultory coder
+D: ElGamal implementation
+S: Laserkatu 4 A 4
+S: 53850 Lappeenranta
+S: Finland
+
+N: Lubomir Sedlacik
+E: salo@Xtrmntr.org
+W: http://Xtrmntr.org
+P: 1024/7E3B70E2 DB EC 8B EC 9A 90 EC EC  0F EF 71 6E 59 CE B7 0B  7E 3B 70 E2
+D: webpage
+D: configure.in bugfixes
+S: A.Hlinku 59/83
+S: 92101 Piestany
+S: Slovakia
+
+N: Timo Sirainen
+E: tss@iki.fi
+W: http://www.irssi.org
+D: Irssi/SILC client
+D: Several bugfixes
+S: Lintulahdenaukio 8 as 30
+S: 00500 Helsinki
+S: Finland
+
+N: Saku Ytti
+E: saku@ytti.fi
+D: wannabe 
+S: Pursimiehenkatu 16 d 60
+S: 00150 Helsinki
+S: Finland
+
+N: Tamas SZERB
+E: toma@rulez.org
+P: 1024D/69C0FA93 D75C E26A 13D7 56EA 4808  38C6 3ED7 0955 69C0 FA93
+D: bugfixes
+D: wannabe
+S: Arnyas ut 38-40/H
+S: 1121 Budapest
+S: Hungary
+
+----------------------------------------------------------------------------
diff --git a/INSTALL b/INSTALL
index c2d5f4b5028c088df3b11bf4f4bfe7027ec22d16..71fb1948b6d30ce340bdbe54c44c3f4c0122fe30 100644 (file)
--- a/INSTALL
+++ b/INSTALL
-Installing SILC Developer's Version
-===================================
+Quick Installation
+==================
 
-./configure
-./make
+   To configure and compile SILC package give the comands:
 
-You should not install the SILC into your system, instead, you should
-run it from the current directory.
+       ./configure
+       make (or gmake)
+       make install
 
-To see different compilation options, give,
+   This will install the SILC binaries and configuration files into the
+/usr/local/silc/ directory.  System wide configuration files are installed
+into the /etc/silc/ directory.
 
-./configure --help
+Some Configuration Options
+==========================
 
+   You can give various options to the `configure' shell script.  You should
+give --help command to the `configure' to see all of them.  Here is listed
+few options that you might want to use.  Please refer to the rest of this
+file for more generic installation instructions.
 
-Generally, developers wants to compile with debugging, in this case,
-give,
+`--with-gmp=PATH'
 
-./configure --enable-debug
+   If you wish to use GMP library for arbitrary precision arithmetic
+library instead of using the MPI library included in the package, you can
+give the --with-gmp=PATH option to the `configure'.  The PATH is the path
+to the GMP library in your system.
 
-WARNING: The debugging is very very heavy and you currently cannot turn
-it off if you have compiled it with this option.  However, if you're
-going to develop or debug SILC you whould compile with this option.
+`--disable-asm'
 
+   If you have trouble compiling the assembler optimized code in the
+package or does not want to use them, you can give the --disable-asm
+option to the `configure' script.  This will assure that assembler
+optimized code is not compiled in.
+
+`--enable-debug'
+
+   If you would like to enable the debugging for the compiled programs
+you can give this option to the `configure'.
+
+`--disable-threads'
+
+   If you do not want to compile the programs with multi threads support
+you can give --disable-threads option.  In this case all compiled programs
+will work in single thread only.
+
+`--enable-ipv6'
+
+   The `configure' will attempt to check for IPv6 support in your system.
+   However, if it fails, but you still want to compile in the IPv6 support
+   you can give --enable-ipv6 option to force the IPv6 support.
+
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
diff --git a/Makefile.am.pre b/Makefile.am.pre
new file mode 100644 (file)
index 0000000..177e366
--- /dev/null
@@ -0,0 +1,102 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2000 - 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; version 2 of the License.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+COMMONDIRS = lib irssi silc silcd doc includes
+SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+DIST_SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+
+include $(top_srcdir)/Makefile.defines.in
+
+dist-bzip: distdir
+       -chmod -R a+r $(distdir)
+       -tar chof $(distdir).tar $(distdir)
+       -bzip2 $(distdir).tar
+       -rm -rf $(distdir)
+
+SILC_EXTRA_DIST = SILC_DISTRIBUTION_EXTRA
+EXTRA_DIST = CHANGES CREDITS $(SILC_EXTRA_DIST)
+
+#
+# Installing of SILC into the system
+#
+
+etcdir = $(DESTDIR)$(silc_etcdir)
+modulesdir = $(DESTDIR)$(silc_modulesdir)
+helpdir = $(DESTDIR)$(silc_helpdir)
+docdir = $(DESTDIR)$(silc_docdir)
+logsdir = $(DESTDIR)$(silc_logsdir)
+
+install-dirs:
+       -mkdir -p $(etcdir)
+       -mkdir -p $(modulesdir)
+       -mkdir -p $(helpdir)
+       -mkdir -p $(docdir)
+       -mkdir -p $(logsdir)
+
+generate-server-key:
+       -@if test '!' -f $(etcdir)/silcd.pub ; then \
+         $(sbindir)/silcd -C $(etcdir); \
+       fi
+
+sim-install:
+       -$(INSTALL_DATA) $(srcdir)/lib/silcsim/*.so $(modulesdir)/
+
+doc-install:
+       $(INSTALL_DATA) $(srcdir)/doc/CodingStyle $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/doc/FAQ $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/doc/example_* $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/doc/*.txt $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/COPYING $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/CHANGES $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/CREDITS $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/README $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/INSTALL $(docdir)/
+       $(INSTALL_DATA) $(srcdir)/TODO $(docdir)/
+
+toolkit-install:
+       -mkdir -p $(docdir)/toolkit/
+       -$(INSTALL_DATA) $(srcdir)/doc/toolkit/* $(docdir)/toolkit
+       -$(INSTALL_DATA) $(srcdir)/lib/doc/*.gif $(docdir)/toolkit
+
+examples-install:
+       -mkdir -p $(docdir)/examples/
+       $(INSTALL_DATA) $(srcdir)/doc/examples/README $(docdir)/examples/
+       $(INSTALL_DATA) $(srcdir)/doc/examples/silc* $(docdir)/examples/
+       $(INSTALL_DATA) $(srcdir)/doc/examples/cell* $(docdir)/examples/
+
+etc-install:
+       -@if test '!' -f $(etcdir)/silcd.conf ; then \
+         $(INSTALL_DATA) $(srcdir)/doc/example_silcd.conf \
+         $(etcdir)/silcd.conf; \
+          chmod go= $(etcdir)/silcd.conf; \
+       fi
+       -@if test '!' -f $(etcdir)/silc.conf ; then \
+         $(INSTALL_DATA) $(srcdir)/doc/example_silc.conf \
+         $(etcdir)/silc.conf; \
+       fi
+
+if SILC_DIST_CLIENT
+install-data-hook: install-dirs sim-install doc-install etc-install
+else
+if SILC_DIST_TOOLKIT
+install-data-hook: install-dirs generate-server-key sim-install doc-install toolkit-install examples-install etc-install
+else
+install-data-hook: install-dirs generate-server-key sim-install doc-install examples-install etc-install
+endif
+endif
diff --git a/Makefile.defines.pre b/Makefile.defines.pre
new file mode 100644 (file)
index 0000000..e85d49b
--- /dev/null
@@ -0,0 +1,67 @@
+#
+#  Makefile.defines.pre
+#
+#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+#
+# This file is intended to include all common compilation defines for the
+# SILC source tree.  All Makefile.ams in the SILC source tree are expected
+# to include this file (Makefile.defines.in).  Also this file may be included
+# in any external project that is included in the SILC source tree.
+#
+# Add following to your Makefile.am:
+#
+# include $(top_srcdir)/Makefile.defines.in
+#
+# All packages in the SILC source tree that include the Makefile.defines.in
+# must also include the following two lines in their configure.in file.
+#
+# INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int"   
+# AC_SUBST(INCLUDE_DEFINES_INT)
+#
+# (See the Makefile.defines_int.pre for all different definitions but DO NOT
+#  directly include that file!)
+#
+
+@INCLUDE_DEFINES_INT@
+
+#
+# INCLUDE defines
+#
+INCLUDES = $(ADD_INCLUDES) $(SILC_CFLAGS) \
+       -I$(srcdir) -I$(top_srcdir) \
+       -I$(silc_top_srcdir) \
+       -I$(silc_top_srcdir)/lib/silccore \
+       -I$(silc_top_srcdir)/lib/silccrypt \
+        -I$(silc_top_srcdir)/lib/silcmath \
+        -I$(silc_top_srcdir)/lib/silcmath/mpi \
+       -I$(silc_top_srcdir)/lib/silcske \
+       -I$(silc_top_srcdir)/lib/silcsim \
+       -I$(silc_top_srcdir)/lib/silcutil \
+       -I$(silc_top_srcdir)/lib/silcsftp \
+       -I$(silc_top_srcdir)/lib/silcclient \
+       -I$(silc_top_srcdir)/lib/contrib \
+        -I$(silc_top_srcdir)/includes \
+        -I$(silc_top_srcdir)/doc
+
+#
+#includes-install: Makefile
+#      for i in $(include_HEADERS); do s=$(srcdir)/$$i;
+#d=$(silc_top_srcdir)/includes/$$i; \
+#         ln $$s $$d 2>/dev/null || (rm -f $$d; cp -p $$s $$d;); \
+#      done;
+#
+#all-local: includes-install
diff --git a/Makefile.defines_int.pre b/Makefile.defines_int.pre
new file mode 100644 (file)
index 0000000..4c7f414
--- /dev/null
@@ -0,0 +1,47 @@
+#
+#  Makefile.defines_int.pre
+#
+#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#
+#  Copyright (C) 2000 - 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+#
+# Internal file for definitions. This is read by Makefile.defines. DO NOT
+# include this file directly to your Makefile.ams.
+#
+
+#
+# Generic definitions
+#
+silc_top_srcdir=@SILC_TOP_SRCDIR@
+silc_install_prefix=@prefix@
+
+#
+# Common libraries that are linked against the created executable
+#
+SILC_COMMON_LIBS= @LIBS@ -L$(silc_top_srcdir)/lib -lsilc
+
+#
+# Common compilation flags
+#
+SILC_CFLAGS=@CFLAGS@
+
+#
+# Installation defines
+#
+silc_etcdir=@ETCDIR@
+silc_modulesdir=@MODULESDIR@
+silc_helpdir=@HELPDIR@
+silc_docdir=@DOCDIR@
+silc_logsdir=@LOGSDIR@
diff --git a/README b/README
index 1953d9423e2a46defa991fd636baf96eab0493b4..78f394db2fc3e5cfe533cc612897368bedfa5ad9 100644 (file)
--- a/README
+++ b/README
@@ -1,27 +1,32 @@
 SILC - Secure Internet Live Conferencing
 ========================================
 
-[NOTE: SILC is still in middle of development and this package is known
-as Developer's Version which means that the package is in no means stable
-or ready to be in production use.  This package is for those who wants
-to test SILC, find bugs and maybe contribute some time and code for the
-SILC project.  There is no guarantees that this package even compiles and
-even if it compiles there is no guarantees that it would work, and even
-if it works there is no guarantees that it would work correctly, and even
-if it seems to work correctly it may be just plain luck.]
+SILC (Secure Internet Live Conferencing) is a protocol which provides
+secure conferencing services on the Internet over insecure channel.
+SILC is IRC-like software although internally they are very different.
+The biggest similarity between SILC and IRC is that they both provide
+conferencing services and that SILC has almost the same commands as IRC.  
+Other than that they are nothing alike.  Major differences are that SILC 
+is secure what IRC is not in any way.  The network model is also entirely
+different compared to IRC.
 
 
-Description
-===========
+Running SILC
+============
 
-SILC (Secure Internet Live Conferencing) is a protocol which provides
-secure conferencing services in the Internet over insecure channel.
-SILC is IRC like softwarre although internally they are very different.
-Biggest similiarity between SILC and IRC is that they both provide
-conferencing services and that SILC has almost same commands as IRC.  Other
-than that they are nothing alike.  Biggest differences are that SILC is 
-secure what IRC is not in any way.  The network model is also entirely
-different compared to IRC.
+After installing the SILC to the system the SILC client is started by
+giving command:
+
+       silc
+
+If you want to run with specific configuration file give -f option.
+
+To run the server you should configure the server first.  To run the
+server give the command:
+
+       silcd
+
+This will launch the server on to the background.
 
 
 Features
@@ -55,6 +60,8 @@ TODO file for more information.]
 
  o Supports data compression with GZIP to improve performance.
 
+ o Supports SOCKS4 and SOCKS5 firewall traversal protocols.
+
  o SIM (SILC Module) support.  Support for loading of shared objects at 
    run-time that provides new and extended features to both SILC client
    and server.  These can provide extra ciphers and extra features to
@@ -69,63 +76,43 @@ TODO file for more information.]
 History
 =======
 
-Even though SILC were just released to the public the idea and the protocol
-itself is quite old.  I got the idea about SILC in its current form in
-the year 1996 and first lines of codes were written in early 1997.  This
-release is now third rewrite of the SILC.  The very first version were
-written in 1997 and it included SILC client and very very preliminary
-SILC server.  The server actually weren't usable but the client looked
-pretty much the same as it does now.  At that time the SILC also included
-RSA implementation and 3DES implementation.  The random number generator
-that exists in this current release is actually based on the RNG written
-in 1997.  The RNG written in 1997, on the other hand, were based on
-the SSH's random number generator.  The RNG has been rewritten twice
-since the first version.
-
-I stopped writing the SILC later in 1997 when I got busy at school and
-in work.  The pause lasted several months.  The development resumed in
-1998 when my friend (Juha Räsänen) and I implemented ElGamal algorithm.
-I rewrote some other parts as well.  However, for the same reasons as
-previously the development stopped again.  I resumed the development
-later in 1998 by doing rewrite of the SILC in C++.  This was obviously 
-a mistake but at that time it seemed like a good idea.  Again, in the 
-winter 1999 I got very busy writing my thesis and was forced to stop the 
-development again.  I also, started a new job in the spring.
-
-Later, in 1999, I decided that this time I'm going to make it the right
-way.  C++ was obviously a bad choice so I decided to fall back to plain
-C language.  I also decided to do complete rewrite and started doing
-more thorough planning of what the SILC actually should include.  I also
-decided that this time it is going to kill me before I stop the 
-development.  I started writing SILC in the weekends and actually 
-everytime I had some spare time.  I also started a new job but I didn't
-let that get to my way.  The result of this development effort is the
-release now in public.
-
-I've learned a lot by doing the SILC.  I guess, when I started it I wasn't
-that good of a C programmer.  That alone was a reason why SILC hasn't
-seen the day of light before now.  My programming style has also changed 
-dramatically during these years.  Actually, it has changed couple times 
-since this last rewrite as well.  However, the code style of current SILC 
-release is quite consistent (actually the coding style SILC has been 
-written now I've learned in my current job).
-
-There is probably over 85% of new code in this third rewrite.  Rest has 
-just been copied from the old versions and only minor changes has been
-made (like changed function names and overall coding style).  I've 
-preserved the dates of the old files (dating back to 1997) that has 
-existed in some forms in the old versions.  There is a lot of new code but
-already I see a lot that needs rewriting.  The development continues.
+SILC was released in the summer 2000 to the public, but the idea and the
+protocol itself is quite old. The SILC was designed by Pekka Riikonen in
+the year 1996 and first lines of codes were written in the early 1997. The
+SILC has been rewritten three times since its very first version in 1997.
+The first version included SILC client, very preliminary SILC server, RSA
+implementation and 3DES implementation. The server actually was not usable
+but the client looked pretty much the same as the first client released in
+the summer 2000. The first version had also random number generator which
+were based on the SSH's random number generator. The current RNG is based
+on the first RNG but has been rewritten twice since the first version. 
+
+The development of SILC was suspended in 1997 when Pekka got busy at
+school and in work. The pause laster several months. The development
+resumed in 1998 when Juha Räsänen and Pekka implemented the ElGamal
+algorithm. However, for the same reasons as previously the development
+stopped again, and was resumed again later in 1998 by doing rewrite of
+ther SILC in C++. This was obviously a mistake but at that time it seemed
+like a good idea. Again, in the winter 1999 the development suspended when
+Pekka got busy writing his thesis and was forced to stop the development. 
+
+Later, in 1999, it was decided that this time SILC will be rewritten from
+scratch in the right way. C++ was obviously a bad choice so plain C 
+language was selected again. The protocol itself faced some rework by
+redesigning some core parts of the protocol. The protocol was also fully
+documented and the protocol specifications were submitted to the IETF. The
+result of this development effort is the release now in public. Since the
+release in the summer 2000 several other people have contributed to the
+project as well. And, the development continues. 
 
 
 Contact
 =======
 
-Feedback and comments are welcome.  You can reach me in the following
-Address. 
-
-[Note that generally bug reports should not be sent just yet as the 
-Developer's Version is full of them and the bug hunt has not even started 
-yet.]
+Feedback and comments are welcome.  Bug reports should be sent to the
+development mailing list.
 
-                               Pekka Riikonen <priikone@poseidon.pspt.fi>
+Official SILC project web site      : http://silcnet.org/
+FTP archive for SILC project        : ftp://ftp.silcnet.org/
+Development mailing list address    : silc-devel@lists.sourceforge.net
+SILC Server                         : /server silc.silcnet.org
diff --git a/README.CVS b/README.CVS
new file mode 100644 (file)
index 0000000..e7568ea
--- /dev/null
@@ -0,0 +1,227 @@
+Anonymous CVS Access
+====================
+
+Anonymous CVS access is now available to SILC CVS repository. The
+repository includes everything related to SILC project; source codes,
+documentation and web pages.
+
+Also note that this is the closest to real time development you can get
+thus you cannot expect that the source tree would work or even compile.
+While it is our intention that the trunk would always at least compile
+there might be situations when it will not.
+
+
+Howto Checkout The Source Tree
+==============================
+
+The repository can be checked out by using anonymous pserver with CVS.
+There are no password restrictions in the SILC anonymous CVS repository.
+
+For those who are using sh/ksh/bash the check out is done as follows:
+
+export CVSROOT=:pserver:cvs@cvs.silcnet.org:/cvs/silc
+cvs login
+cvs co silc
+
+For those who are using csh/tcsh the check out is done as follows:
+
+setenv CVSROOT :pserver:cvs@cvs.silcnet.org:/cvs/silc
+cvs login
+cvs co silc
+
+If you don't want to set $CVSROOT environment variable you can set the
+path to the cvs as command line options:
+
+cvs -d:pserver:cvs@cvs.silcnet.org:/cvs/silc login
+cvs -d:pserver:cvs@cvs.silcnet.org:/cvs/silc co silc
+
+What ever method you decide to use, after you have done cvs login you will
+be prompted for password:
+
+       CVS password: silc
+
+Type the password "silc" and press Enter.
+
+The actual SILC source tree is checked out using the cvs co silc command,
+described above. This command will fetch the source tree and save it into
+directory named silc. SILC CVS repository currently does not have any
+branches thus this will check out the trunk. The size of the trunk is
+currently about 8 Mb but will grow in the future.
+
+
+What SILC Source Tree Includes
+==============================
+
+SILC Source tree includes a lot more stuff that appears in public
+distribution.  The source tree includes, for example, internal scripts,
+configuration files, SILC webpages etc.  These never appear on a public
+distribution.
+
+Following directories currently exist in SILC source tree.
+
+  doc/
+
+        Includes all the SILC documentation.  Some of the documentation
+        are generated when distribution is generated.  The automatically
+        generated files must never be commited to CVS.
+
+  includes/
+
+        Includes SILC include files.
+
+  irssi/
+
+       Includes the Irssi SILC Client.
+
+  lib/
+
+        Includes SILC libraries.  There maybe libraries on the CVS that
+        does not appear on public distribution.
+
+  lib/contrib/
+
+        Contrib directory for routines that some of the platforms might
+        not have.  In that case these routines are provided by the SILC.
+
+  lib/silcclient/
+
+        The SILC Client library. Implementation of the SILC Client without
+        the user interface.  The library provides an interface for user
+        interface designers.
+
+  lib/silccore/
+
+        The SILC Protocol Core library.  Implementation of all the core
+        components of the SILC Protocol.  This is used by all the SILC
+        applications.
+
+  lib/silccrypt/
+
+        The SILC Crypto library. Provides all cryptographic algorithms
+        used in the SILC.  Provides also the Cryptographically strong
+        random number generator.
+
+  lib/silcmath/
+
+        The SILC Math library. Provides the Math and MP routines for
+        SILC applications.  The MP library is actually the GMP.
+
+  lib/silsim/
+
+        The SILC Modules library.  Provides the dynamically loadable
+        modules.
+
+  lib/silcske/
+
+        The SILC Key Exchange (SKE) library.  Implementation of the
+        SKE protocol.  This is used by all SILC applications.
+
+  lib/silcutil/
+
+        The SILC Utility library.  Provides various utility functions
+        for the applications.
+
+  lib/silcutil/unix/
+
+        The SILC Utility library.  Provides various Unix specific utility
+        functions for the applications.
+
+  lib/silcutil/win32/
+
+        The SILC Utility library.  Provides various WIN32 specific utility
+        functions for the applications.
+
+  public_html/
+
+        Includes the official SILC web pages and everything that relates
+        to them.  This directory never appears on public distribution.
+
+  silc/
+
+       Includes an example implementation of ncurses based SILC client.
+       It won't compile with current Toolkit since it is not being 
+       updated.  It is still good example for Toolkit programmer to 
+       figure out how to use SILC Toolkit.
+
+  silcer/
+
+       Includes an example implementation of GUI (Gnome) base SILC
+       client.  Please read silcer/README for more information.
+
+  silcd/
+
+        Includes SILC server.  There can be some extra files that will
+        never appear in public distribution, such as, configuration files.
+
+  win32/
+
+       Includes win32 Toolkit specific files.  It includes MSVC++
+       Workspace files.  The win32/tests includes example code for
+       use of SILC Toolkit and SILC Client Library on Win32 GUI 
+       application.
+
+
+Howto Compile SILC Source Tree
+==============================
+
+After checkout from CVS the SILC source tree must be prepared for 
+configuration and compilation.  To compile the source tree, give,
+
+       ./prepare
+       ./configure --enable-debug
+       make
+
+The ./prepare script is included in to the source tree and it never
+appears in public distribution.  The script prepares the source tree
+by creating configuration scripts and Makefiles.  The prepare must be
+run every time you make some changes to configuration scripts (however,
+making changes to Makefile.am's does not require running ./prepare).
+
+As a developer you should read the ./configure script's help by
+giving ./configure --help and study all of its different options.  Also,
+you should configure the script with --enable-debug option as it
+compiles SILC with -g (debugging) option and it enables the 
+SILC_LOG_DEBUG* scripts.  Warning is due here:  The debugging produced
+by both cilent and server is very heavy, thus it is common to test
+the programs as follows:
+
+       ./silc -d "*" -f configfile 2>log
+       ./silcd -d "*" -f configfile 2>log
+
+The -d option enables the debug printing.  The argument for the -d option
+is a string that is used to match the output debug.  The example "*" will
+match for everything, and all debugs will be printed.  If you want to
+limit the debugs you want to printout you can give for example a string
+like "*server*,*rng*" to match all functions, and filenames that has
+"server" or "rng" string in them.  Others will not be printed out.  You   
+can freely define regural expressions as debug string.
+
+
+Howto Clean SILC Source Tree
+============================
+
+To entirely clear the source tree to the state after it was checked out
+from CVS, give,
+
+       ./prepare-clean
+
+This calls `make distclean' plus removes automatically generated files
+by hand.  It also removes *.log files. However, it will not remove
+any other files you might have created.
+
+
+Makefiles and configuration files
+=================================
+
+Developers should never directly write a Makefile.  All Makefiles are
+always automatically generated by ./prepare and later by ./configure
+scripts.  Instead, developers must write Makefile.am files.  There
+are plenty of examples what they should look like.  If you change
+Makefile.am during development you don't have to run ./prepare, just
+run normal make.
+
+Configuration files are the files that ./prepare automatically generates
+and what will be included into public distribution.  ./prepare creates
+for example the ./configure script that is not commited to the CVS.
+`configure.in' is the file that developers must edit to change ./configure
+script.  After changing one must run  ./prepare.
diff --git a/README.WIN32 b/README.WIN32
new file mode 100644 (file)
index 0000000..14364bc
--- /dev/null
@@ -0,0 +1,71 @@
+Compiling SILC Toolkit on WIN32
+===============================
+
+SILC Toolkit works on native WIN32 systems as well.  This document is
+intended for those who needs to compile the Toolkit for native WIN32
+systems.  The Toolkit can be compiled for native WIN32 systems using
+generally any compiler.  However, the compilation environment is designed
+to currently work with the MSVC++ (version 6.0) and with the MinGW (under
+cygwin).
+
+
+Compiling SILC Toolkit with MSVC++
+==================================
+
+The MSVC++ workspace and project files resides in the win32/ subdirectory
+of the Toolkit package.  The `silc.dsw' file is the workspace file that
+automatically supports compiling the Toolkit and to generate the SILC Core
+DLL and SILC Client DLL libraries.
+
+The SILC Core DLL is named as libsilc and will generate libsilc.dll, and
+the SILC Client DLL is named as libsilcclient and will generate
+libsilcclient.dll.  Both of the projects also automatically generates
+libsilc.lib and libsilcclient.lib import libraries that may be used to
+link against a client application.
+
+Generally you do not need to do any specific settings to compile the
+Toolkit.  However, you must compile the libsilc before compiling the
+libsilclient, since the SILC Client DLL depends on the SILC Core DLL.
+
+You may compile the DLLs as either Release or Debug version.  Just select
+the preferred method of compilation.  The Debug version will compile the
+SILC Toolkit with debugging which you can conditionally use in your client
+application by setting the global variable silc_debug to TRUE or FALSE.
+
+
+Compiling SILC Toolkit with MinGW
+=================================
+
+To compile the Toolkit with MinGW you first need to install the cygwin and
+the MinGW into your system.  After that you can just normally give the
+./configure with the following option:
+
+       ./configure --with-win32
+
+If you want to compile debug version give also the --enable-debug option
+to the ./configure.  After configuration the source tree is ready for
+compilation which you can simply start by giving the command:
+
+       make
+
+Note that some of the subdirectories in the Toolkit will not compile under
+WIN32 (namely the silcd/ that includes the SILC Server).  For this reason
+it is suggested that you will give the command make in the lib/ directory
+to compile the DLLs.  Thus, you should give the following commands after
+giving the ./configure.
+
+       cd lib
+       make
+
+After compilation there should be silc.dll and silcclient.dll files in
+the lib/ directory.  It will also generate silc.lib and silcclient.lib
+files for linking against a client application.
+
+
+Compiling SILC Toolkit with Cygwin
+==================================
+
+Compiling the Toolkit with Cygwin is equivalent to compiling with MinGW
+except that the ./configure does not take the --with-win32 option.  In this
+case it will compile using Cygwin's libraries and the binaries will require
+the Cygwin DLL.
diff --git a/TODO b/TODO
index ece615c1f8f68cbea8938252ad42fe6f5fb48c3f..9fa07e40695668de2939466594ca5f61c4df60b9 100644 (file)
--- a/TODO
+++ b/TODO
-TODO
-====
+TODO/bugs in Irssi SILC client
+==============================
 
-This is more or less complete list of tasks that has to be done before
-SILC 1.0 could ever be released.  It is clear that the list does not
-include all the bugs that exists.  At the end of list are tasks that 
-needs to be done but are probably post 1.0.
+ o Rewrite the notify handling in the new Irssi SILC client.
 
-Feel free to contribute if you have the ability and free time - all the
-help is really appreciated - and needed.
+ o /cumode for unknown nick does not give any error message.
 
-                                                       - Pekka
+ o Add local command to switch the channel's private key when channel has
+   several private keys.  Currently sending channel messages with many
+   keys is not possible because changing the key is not possible by the
+   user.
 
-[Latest Note:  The protocol has changed a bit in some parts which 
-causes that the current implementation violates some requirements.
-These are not listed here, currently.]
+ o JOINing to +a (requires passphrase to JOIN) does not work on autojoin.
+   Seems the passwords in the .silc/config has no effect.
 
+ o Add local commands to list the current server and client public keys
+   that the user has.  And a local command to dump the contents of the
+   public key to the screen.  Something like LISTKEYS, SHOWKEY...
 
-New features TODO
-=================
 
- o Extended SIM (SILC Module) support.  Currently only SILC Cipher API
-   and SILC Hash API may be used as SIM's.  What I have in mind is to
-   have extended support for SIM's so that basically any SILC API could
-   be used as SIM's.  This would open tremendous possiblities but
-   opens also issues on security that needs to be dealt with.
+TODO/bugs In SILC Client Library
+================================
 
-   Some sort of SIM compilation environment should be defined so that
-   the SIM's could use SILC specific symbols from the modules (which they
-   cannot do currently).  In the future modules could add new features
-   to SILC easily with this support.  I'm more thinking this from client's
-   perspective to add new features to client (such as IRC support as SIM)
-   but server should have the support as well.  Anyhow, this is an 
-   interesting feature...
+ o The PRIVATE_MESSAGE_KEY packet is not handled (it is implemented 
+   though).  This should be added and perhaps new client operation
+   should be added to notify application that it was received and
+   set the key only if application wishes to set (accept the key) it.
 
-   This maybe post 1.0 task - dunno.
+ o Additions to do after protocol version 1.1:
 
- o SIM support for other platforms than just for Linux.  Apache has
-   example code (code that we could use directly pretty easily) for
-   other platforms.
+       o Fix the NICK_CHANGE notify handling not to create new entry
+         for the changed client, but take the nickname from the notify
+         (removes need for resolving as well).  Protocol TODO entry 3.
 
- o We should replace all short, int, long, unsigned short, unsigned int,
-   unsigned long with some pre-defined datatypes that really are what
-   we want on all platforms.  int16, uint16, int32, uint32 etc. are
-   what we could use or maybe SilcInt16, SilcUInt16 etc.  Also, boolean
-   datatype should be defined.
+       o Add support for list of errors in command replies.  Protocol
+         TODO entry 1.
 
- o More platform supports should be added.  The code is pretty much
-   generic but there are some parts that require porting (SIM).  Also, 
-   some support for different platforms is needed into configure.in.
 
- o SILC requires currently GCC to work because we use GCC specific 
-   compilation options.  Generally any compiler that supports inline
-   functions and can build shared libraries (for SIMs) should work.  
-   These cases should be included into configure.in.
+TODO/bugs In SILC Server
+========================
 
+ o Assure that server is allowed to connect only once to router.  Meaning
+   if server connection is established already, same connection cannot be
+   established again.
 
-TODO In SILC Client
-===================
+ o If server send CUMODE_CHANGE notify (like setting founder) to router
+   and router does not have founder on channel (founder is left or there's
+   no founder on channel at all), the router will accept the server's
+   founder mode change, even though it perhaps should not do that.
 
- o Implement all commands.  A lot of commands are still yet to be
-   implemented.  Most of them are trivial but some will require some
-   planning.  Go see the command.c for unimplemented commands.
+ o Make the normal server save user counts with LIST command reply.
 
- o Non-blocking connection on the background must be stopped if some
-   other connection on same window has established.  Now it is possible
-   that some non-blocking connection timeouts on the background when
-   we already have a working connection to some other place; things
-   goes bad.
+ o Add hashed passwords to silcd.conf file.
 
- o Finish WHOIS, finish JOIN and other commands that are partly
-   implemented.
-
- o Input line on UI is buggy.  Cursor movement etc bugs.  Too lazy to
-   fix it.
-
- o Logic for handling multiple same nicknames for example in private
-   message sending.  I guess the logic is done in server side but is
-   missing from client.
-
- o Private message key setting is missing and must be implemented.
-   Currently private messages are encrypted with session keys.  This
-   is required by the protocol.
-
- o Channel private key setting is missing and must be implemented.
-   Currently there cannot be private keys for channels.  Normal channel
-   keys (generated by server) are used.  This is required by the protocol.
-
- o Public and private key generation is now done everytime the program
-   is run.  Now, this is only for testing period as I've been lazy to
-   do it any better for now.  This must be fixed.
-
- o I guess, public key authentication (when connecting to a server)
-   is not working currently.  It is just matter of loading the keys
-   from file and using them (see corresponding code in server, it should
-   support public key authentication already).
-
- o Multiple windows support.  Basic support for multiple windows already
-   exists but a lot is still missing to get it working.  Also, some
-   of the existing stuff probably needs to be tweaked a bit before the
-   multiple windows support could be done.  And of course the actual
-   commands that control the windows needs to be written (/WINDDOW).
-
- o Implement /KEYMAP (or similiar) command to remap control and function
-   keys.
-
- o Implement /ALIAS command to make command aliases.
-
- o Implement /set/if/do/while etc as in IRC2.  Maybe post 1.0 task.
-   Some scripting would be good.
-
- o Connection Authentication request resolving is missing and must be
-   done.  This is required by the protocol.
-
- o Key Exchange protocol's responder side is missing from client.  
-   Generally it is possible for the client to be responder so it should
-   be implemented (See corresponding code from server).  Error handling
-   in the KE protocol is also in pretty bad shape in client.
-
- o Configuration file loading from global and from local dirs.  This
-   is currently missing and I guess the global is only used.  Old SILC
-   version (in 1997) had ~./silc directory that I guess should be done
-   now as well.  The code for handling those exists but not in current
-   source tree.
-
- o Configuration file format - could be better.
-
- o Write help files for commands.  Nice format for the help files should
-   be selected.  I'm open for ideas.
-
- o All allocations and freeing needs to be checked for memory leaks.
-   Also, return values from various allocations and functions needs to
-   checked.
-
-
-TODO In SILC Server
-===================
-
- o Implement all commands on server side.  A lot of commands are still yet
-   to be implemented.  Most of them are trivial but some will require some
-   planning.  Go see the command.c for unimplemented commands.
-
- o DNS/IP lookup blocks the server.  This must be fixed.  Check the
-   resolver stuff (resolver(3), resolver(5)).  Either we have to do the
-   own resolver stuff (through scheduler, if possible without writing
-   too much own stuff) or use threads.
-
- o Lenght of the packet processing timeouts needs to be checked whether
-   they are too short or too long.  I haven't really tested whether they
-   are suitable.  They should be tested on high load which I haven't done
-   at all yet.
+ o The router should check for validity of received notify packets from
+   servers (after all buggy servers may send notify that is actually
+   something that should have not been sent).
 
- o Public and private key generation is now done everytime the program
-   is run.  Now, this is only for testing period as I've been lazy to
-   do it any better for now.  This must be fixed.
+ o Add a timeout to handling incoming JOIN commands.  It should be 
+   enforced that JOIN command is executed only once in a second or two
+   seconds.  Now it is possible to accept n incoming JOIN commands
+   and process them without any timeouts.  THis must be employed because
+   each JOIN command will create and distribute the new channel key
+   to everybody on the channel.
 
- o Server says that it is able to listen on multiple ports but currently
-   that is bogus.  It can, but internals are for single server.
+ o Backup router related issues
 
- o Command lagging must implemented.  Those commands (all currently) that
-   has the LAG flag set they must not be allowed to be executed more than
-   once, say, in 2 seconds.
+       o Channel user mode changes are notified unnecessarely when
+         switching to backup router on router crash.
 
- o Command flag usage in general is not implemented yet.
+ o New configuration file format must be added.  The following
+   tasks relates closely to this as well and must be done at the same time
+   when adding the new config file format:
 
- o Client history must be implemented.  Protocol says that server must
-   keep history information about clients for some period of time.
+       o Server says that it is able to listen on multiple ports but
+         currently that is bogus.  It can, but internals are for single
+         server.
 
- o Channel flags and user modes on channels are not implemented yet as
-   /MODE command is not implemented yet in client and server.
+       o Protocol execution timeouts are hard coded, should be
+         configurable.
 
- o Protocol execution timeouts are hard coded, should be configurable.
+       o IP address fields in configuration file should accept mask
+         format as well, IP/MASK, and not just plain IP.
 
- o Channel message sending routines uses a lot of common code.  Should
-   create a common function for those instead of writing the same code
-   again everytime, as done now.
+ o Lots of statistics updating is missing around the server.
 
- o serverutil.c I guess should be created for util-like functions that
-   now resides in server.c, which is getting too big.
+ o If client's public key is saved in the server (and doing public key
+   authentication) then the hostname and the username information could
+   be taken from the public key.  Should be a configuration option!
 
- o serverconfig.c and the function naming in it is inconsistent.  It is 
-   not silc_config_server* it should be silc_server_config*.  As should
-   all the SilcConfigServer* types be SilcServerConfig*.
 
- o Implement DENY_CONNECTION section in serverconfig.c and in server.
+TODO/bugs In SILC Libraries
+===========================
 
- o Implement REDIRECT_CLIENT section in serverconfig.c and in server.
+ o WIN32 silc_net_create_connection_async does not work the same way
+   than on Unix.  Do it with threads on WIN32.  The function works but
+   is not actually async currently.
 
- o Configuration file format - could be better.
+ o Rewrite the lib/silcsim/silcsim.h.  The SilcSimContext should be
+   private and silc_sim_alloc should take necessary arguments.
 
- o IP address fields in configuration file should accept mask format
-   as well, IP/MASK, and not just plain IP.
+ o lib/silcsftp/sftp_fs_memory.c use directly open(), close() etc. 
+   routines.  Change to use silc_file_* routines.
 
- o Connection classes should be actually implemented in serverconfig.c.
-   They can be defined but they are totally ignored currently.
-
- o Acceptance of incoming connections (client and server connections)
-   should be checked before key exchange protocol.  Currently it is
-   checked at the authentication phase after KE, that is ok, but it should
-   be checked before starting KE, as well.
-
- o Statistics are totally missing from the server.  It would be nice
-   to gather some statistics.
-
- o All allocations and freeing needs to be checked for memory leaks.
-   Also, return values from various allocations and functions needs to
-   checked.
-
-
-TODO In SILC Libraries
-======================
-
- o Public key verification in SKE (SILC Key Exchange) protocol is missing,
-   thus currently we trust on all public keys.  This probably doesn't cause
-   bad problems but the mechanism of verifying it from local database
-   (from files) needs to be done (it can open man-in-the-middle-attacks).
-
- o Implement PFS (Perfect Forward Secrecy) flag in SKE (and in client and
-   server, actually).  If PFS is set, re-key must cause new key exchange.
-   This is required by the SILC protocol.
-
- o Re-key in general is actually missing (from everywhere) and must be done.
-
- o SKE does not send correct status types.  Types are defined but not
-   sent.
-
- o Connection authentication protocol does not send correct status types.
-   These types are not defined currently at all.
-
- o PKCS#1 style RSA public key encryption/decryption/sign/verify is 
-   missing, and should be added for interoperability reasons.  The thing 
-   I've done now is bad and should be removed as soon as possible (or 
-   the protocol should then state the method of how they should be done).
-
- o SILC public key file type is bad.  I'd like to see PEM encoded files.
-   I have public domain code for base64 encoding but it needs to be 
-   rewritten.
-
- o Slow ciphers should be removed.  I think we don't need more than
-   the AES finalists plus blowfish and RC5.
-
- o These slow ciphers actually don't work currently as I've tested
-   only the ones that are worth testing.  The CBC mode on these slow
-   ciphers probably don't work.  No need to worry, these ciphers should
-   be removed.
-
- o Scheduler needs to be analyzed on high load as it might be unfair
-   towards select() because it may run timeout tasks before select() and
-   after select().  If it is found to be unfair the timeout task running
-   before select() should probably be removed.
+ o SILC RNG does not implement random seed files, and they should be
+   implemented.
 
- o On select() issue; maybe we should use poll() instead if it is
-   available? poll() doesn't have max fd limit...
 
- o SIM support for SILC PKCS API needs to made so that they could be
-   used as SIM's.  At the same time some work is required on prime
-   generation as the way it is done now sucks.  Read from code for
-   more (silcpkcs.h).
-
- o Compression routines are missing.  The protocol supports packet
-   compression thus it must be implemented.  SILC Comp API must be
-   defined.  zlib package is already included into the lib dir (in CVS,
-   not in distribution), but it is not used yet, and it requires some
-   tweaking on the Makefiles (we want static lib not shared).
-
- o Cipher API needs to be made more consistent.  Some parts of the
-   code generated with current Cipher API looks really bad.  Same
-   is with PKCS API, even worse actually.  They need to be made
-   cleaner.  Introducing silc_cipher_encrypt/decrypt/set_key etc.
-   functions (I actually don't understand why have I left these un-done).
-
- o Scheduler should automatically allocate task queues if NULL pointers 
-   are passed to the silc_schedule_init.  Would make initialization 
-   cleaner.
-
- o Packet processing routines in client and server are actually pretty
-   much generic and should be moved from the client/server to the library
-   as generic routines (silc_<client/server>_packet_decrypt_rest* etc).
-   This requires heavy changes to the client and server.
-
- o Random Number Generator needs some tweaking.  Reading /dev/random may
-   block resulting slow initialization of RNG.  Some other things in the
-   RNG may block as well.  Also, I have some pending changes to the RNG 
-   that needs to be commited (from Schneier's Yarrow-160 paper).  They 
-   should make the RNG even better.
-
- o Logging should be made more generic in a way that application can
-   set to where the logging is destined to.  Now, it is always destined
-   to stdout (or stderr) which is a bad thing for client.  Ie. some
-   sort of logging registration functions or similiar should be done
-   (silclog.[ch] in core).  The actual output of logs should be done
-   by callback function in the application not in lib.
-
- o I don't like the ID cache system currenly implemented.  Ugly and
-   not so good.  Must be rewritten very soon.
-
- o All allocations and freeing needs to be checked for memory leaks.
-
- o There are also checks missing from allocations whether the allocation
-   returns valid memory or NULL.  These are missing in library as well
-   in client and server.  Either all checks has to be added or we will
-   have to make sure that silc_*alloc()s always return valid memory
-   and assert()s if the system's memory allocator (*alloc()) fails.
-
- o silc_buffer_[un]format() needs to be made more stable as it may
-   crash the SILC if malformed data is sent as argument.  There are a
-   lot of places in client and server where we trust directly data coming
-   from network and try to unformat it.  The unformatting routine needs
-   to be able handle situations where data sent is malformed, by mistake
-   or intentionally.  This is important as it is easy to crash the SILC
-   now by just sending malformed data.  Also, in client and server we
-   must start checking the return value from silc_buffer_[un]format.
-
-
-Other Things TODO
-=================
-
- o Write manuals for server.
-
- o Write manuals for client.
-
- o Write SILC Library Reference manual.  This would include all the SILC
-   API's with simple examples how the functions are to be used.  This is
-   pretty easy to create by taking all the functions plus their comments
-   from source/header files.  However, same sort of reference manual 
-   should be written for client and server as well.
+TODO in Toolkit Documentation
+=============================
+
+Stuff that needs to be done in order to complete the Tooolkit Reference
+Manual.
+
+ o Lots of ROBOdoc header formatting is undone in lib/silcske, 
+   lib/silcutil, and lib/silccrypt.
+
+ o Write "Programming with Toolkit" document, describing how to build
+   Toolkit, how the build system works, where is everything, how
+   new (external) projects can be glued into Toolkit (use irssi as an
+   example), and how external projects can use Toolkit without gluing into
+   it (how to link etc).
+
+ o Write "Programming conventions" document to describe the coding
+   and naming conventions used in the Toolkit (should not be 
+   actually the CodingStyle document, but something more general).
+
+ o Move the lib/silccrypt/silcrng.h's "how the RNG works" documentation
+   to its own html file and link it to the reference manual.
+
+
+TODO in SILC Protocol
+=====================
+
+Current protocol version is 1.0.  However, it is far from being perfect,
+and needs to include additional features.  Following protocol TODO entries
+describe new stuff to be added to protocol versions 1.x.
+
+ 1. Re-define the Status Payload: it is now 16 bits, split it into two
+    8 bits fields.  First field includes status types from 0 - 9 and
+    10 - n *if* it is not an list of errors.  If it is list of errors then
+    the first field includes 1, 2 and/or 3, and the second field includes
+    the error status 10 - n.  This way it is possible to send multiple
+    errors (list of errors) and we have a way to tell the receiver that
+    there will be other errors as well.  The second field is used only
+    if there is list of errors.  If normal status, or normal (single)
+    error status the second field is set to zero, and must be ignored.
+    Hence, the status works same way as now except for list of errors.
+    To be included in protocol version 1.1.
+
+ 2. Define that WHOIS and IDENTIFY commands must send list of errors
+    if multiple Client ID (or Channel ID and Server ID for IDENTIFY) was
+    requested and was not found.  Each unfound entry must cause an error
+    command reply to the sender.  Also define that errors must be sent
+    *after* sending successfully found entries (this way receiver may
+    ignore them).  To be included in protocol version 1.1.
+
+ 3. Define the NICK_CHANGE notify to send the changed nickname as a new
+    third argument.  This will make the NICK_CHANGE notify handling easier
+    in the receiver's end (client primarily) since it removes the
+    requirement that receiver must resolve (using IDENTIFY or WHOIS) the
+    new Client ID received in the notify (because of the new nickname is
+    unknown).  To be included in protocol version 1.1.
+
+ 4. Add "request parameters" or similar to the WHOIS command, which can
+    be used to request various parameters (something not returned by
+    standard WHOIS command) about clients (info that could be fetched
+    even from clients).  Additional specification (or appendix) should 
+    be done to define the payload and the parameters.  It could be used
+    to make the WHOIS command support various search conditions as well.
+    This would be the way to extend the WHOIS command to support various
+    new features without always making the command incompatible to previous
+    version.  To be included in protocol version 1.1.
+
+ 5. Inviting and banning by public key should be made possible.  To be
+    included in protocol version 1.x.
+
+ 6. Add perhaps SILENCE_USERS, SILENCE_OPERS channel user modes which
+    can be used to silence (moderate) normal users and opers (this set
+    only by founder).  To be included in protocol version 1.1.
+
+ 7. Channel Message Payload needs slight redesining to include the IV
+    field to the MAC generation of the payload.  It is authenticated
+    by the packet's MAC but not by the payload's MAC.  Since the IV
+    belongs to the payload, its integrity should be protected by the
+    payload MAC and not alone by packet MAC.  To be included in protocol 
+    version 1.1.
+
+ 8. Remove the administrative commands from the protocol all together.
+    It does not make sense for the protocol to define how a server is
+    reconnected or shutdown, since they are implementation and 
+    configuration issues.  Besides protocol provides only limited set of
+    administrative commands and cannot define all that one could imagine.
+    To be included in protocol version 1.1.
+
+ 9. Add SILC_MESAGE_FLAG_REPLY for being other side to the
+    SILC_MESSAGE_FLAG_REQUEST.  Add generic SILC_MESSAGE_FLAG_DATA, which
+    can include generic payload, which can include generic data.  The
+    payload definition is left out for now.  To be included in protocol
+    version 1.1.
+
+ 10. Check command reply error status types in various commands,
+     specifically NO_FOPRIV is missing from many commands.  To be 
+     included in protocol version 1.1.
 
 
 TODO After 1.0
 ==============
 
- o Pthreads support.  A lot of problems are solved with server (and with
-   client as well) if we add pthread support.  We can forget things such
-   as non-blocking connecting etc, and we can do things such as DNS/IP
-   lookups async.  The server itself also benefits great deal from 
-   threads, especially from performance point of view.
+A rough list of stuff that is going to be done to SILC after 1.0 or at
+least could be done.
 
-   But, this is not a small task and almost entire SILC Library has to
-   be made re-entrant.  Own API is probably added for the threads support
-   to make changes in the future as painless as possible.  So the API 
-   would have things like silc_mutex_lock, silc_mutex_unlock and 
-   friends...
+ o Implement the defined SilcDH API.  The definition is in
+   lib/silccrypt/silcdh.h.
 
  o X.509 certificate support.  SILC protocol supports certificates and
    it would be great to have support for them.  This is a big task as
@@ -356,14 +228,121 @@ TODO After 1.0
    to start writing one myself.  Anyhow, the OpenSSL X.509 lib should
    be checked.
 
- o SSH2 public keys support.  Maybe - not really needed but could be
-   nice as SSH is widely used all over the place.  SILC Protocol 
-   supports SSH2 public keys.
+   Other package that should be checked is the NSS's X509 library,
+   which I like more over OpenSSL package.
+
+ o SSH2 public keys support, allowing the use of SSH2 public keys in
+   SILC.
 
- o IRC support for SILC client.  This would be nice to have on client
-   as it could be used to connect to SILC and IRC.  People wouldn't
-   have to have two different clients when same would work on both.
-   I'd like to see this done as SIM, after the extended SIM support
-   has been added to SILC.
+ o OpenPGP certificate support, allowing the use of PGP public keys
+   in SILC.
+
+ o Compression routines are missing.  The protocol supports packet
+   compression thus it must be implemented.  SILC Zip API must be
+   defined.
+
+ o Rewrite the lib/silcutil/silcprotocol.[ch] not to have [un]register 
+   functions, but to make it context based all the way.  The alloc should 
+   take as argument the protocol type and its callback (not only 
+   final callback).  It is not good that we have now global list of 
+   registered protocols.
+
+ o Optimizations in Libraries
+
+       o There is currently three (3) allocations per packet in the
+         silc_packet_receive_process, which is used to process and
+         dispatch all packets in the packet queue to the parser callback
+         function.  First allocation is for parse_ctx, second for the
+         SilcPacketContext, and third for packet->buffer where the actual
+         data is saved.
+
+         The parse_ctx allocation can be removed by adding it as a
+         structure to the SilcPacketContext.  When the SilcPacketContext
+         is allocated there is space for the parse context already.
+
+         The silc_packet_context_alloc could have a free list of
+         packet contexts.  If free packet context is found from the list
+         it is returned instead of allocating a new one.  The library
+         could at first allocate them and save them to the free list
+         until enough contexts for smooth processing exists in the list.
+         This would remove a big allocation since the structure is
+         quite big, and even bigger if it would include the parse_ctx.
+
+         The packet->buffer can be optimized too if the SilcBuffer
+         interface would support free lists as well.  Maybe such could
+         be done in the same way as for SilcPacketContext.  The
+         silc_buffer_alloc would check free list before actually 
+         allocating new memory.  Since the packets in the SILC protocol
+         usually are about the same size (due to padding) it would be
+         easy to find suitable size buffer from the free list very
+         quickly.
+
+         These naturally cause the overal memory consumption to grow
+         but would take away many allocations that can be done several
+         times in a second.
+
+       o Move the actual file descriptor task callback (the callback that
+         handles the incoming data, outgoing data etc, that is implemnted
+         in server and client separately (silc_server_packet_process and
+         silc_client_packet_proces)) to the low level socket connection
+         handling routines, and create an interface where the application
+         can register a callbacks for incoming data, outoing data and EOF
+         receiving, which the library will call when necessary.  This way 
+         we can move the data handling in one place.
+
+       o Add silc_id_str2id to accept the destination buffer as argument
+         and thus not require any memory allocation.  Same will happen
+         with silc_id_payload_* functions.
+
+       o Remove the `truelen' field from SilcBuffer as it is entirely
+         redundant since we can get the true length of the buffer by
+         doing buffer->end - buffer->header.  Add SILC_BUFFER_TRUELEN
+         macro instead.  Consider also removing `len' field too since
+         it effectively is buffer->tail - buffer->data, and adding
+         SILC_BUFFER_LEN macro can do the same.  These would save
+         totally 8 bytes of memory per buffer.
+
+         Add also perhaps function silc_buffer_alloc_size that would
+         effectively do: 
+
+               return silc_buffer_pull_tail(silc_buffer_alloc(size),
+                                            size);
+
+         to not require the user to give the pull_tail anymore.
+
+ o Optimizations in Server
+
+       o Remove the big switch statement from the function 
+         silc_server_packet_parse_type and replace it with predefined
+         table of function pointers where each of the slot in table
+         represents the packet type value.
+
+         Same could be done with notify packets which has big switch 
+         statement too.  Same kind of table of notify callbacks could be
+         done as well.
+
+       o The parser callback in the server will add a timeout task for
+         all packets.  It will require registering and allocating a
+         new task to the SilcSchedule.  Maybe, at least, for server
+         and router packets the parser would be called immediately
+         instead of adding it to the scheduler with 0 timeout.  It
+         should be analyzed too how slow the task registering process
+         actually is, and find out ways to optimize it.
+
+       o The SERVER_SIGNOFF notify handing is not optimal, because it'll
+         cause sending of multiple SIGNOFF notify's instead of the one
+         SERVER_SIGNOFF notify that the server received.  This should be
+         optimized so that the only SERVER_SIGNOFF is sent and not
+         SIGNOFF of notify at all (using SIGNOFF takes the idea about
+         SERVER_SIGNOFF away entirely).
+
+ o Add SilcAsyncOperation to utility library.  Any function that takes
+   callback as an argument must return SilcAsyncOperation.
+
+ o Add DSS support.
 
  o Cipher optimizations (asm, that this) at least for i386 would be nice.
+
+ o Add builtin SOCKS and HTTP Proxy support, well the SOCKS at least.
+   SILC currently supports SOCKS4 and SOCKS5 but it needs to be compiled
+   in separately.
diff --git a/acconfig.h b/acconfig.h
deleted file mode 100644 (file)
index 65e1903..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Name of the package. */
-#undef PACKAGE
-
-/* Version of the package. */
-#undef VERSION
-
-/* Debugging */
-#undef SILC_DEBUG
-
-/* Default configuration file */
-#undef SILC_SERVER_CONFIG_FILE
-
-/* SIM (SILC Module) support */
-#undef SILC_SIM
-#undef HAVE_RTLD_NOW
-#undef HAVE_RTLD_LAZY
diff --git a/acconfig.h.pre b/acconfig.h.pre
new file mode 100644 (file)
index 0000000..ff11e7d
--- /dev/null
@@ -0,0 +1,82 @@
+/* Name of the package. */
+#undef PACKAGE
+
+/* Version of the package. */
+#undef VERSION
+
+/* Debugging */
+#undef SILC_DEBUG
+
+/* Default configuration file */
+#undef SILC_SERVER_CONFIG_FILE
+
+/* Multi-thread support */
+#undef SILC_THREADS
+#undef SILC_HAVE_PTHREAD
+
+/* IPv6 support */
+#undef HAVE_IPV6
+
+/* Default paths */
+#undef SILC_ETCDIR
+#undef SILC_HELPDIR
+#undef SILC_DOCDIR
+#undef SILC_MODULESDIR
+#undef SILC_LOGSDIR
+
+/* SIM (SILC Module) support */
+#undef SILC_SIM
+#undef HAVE_RTLD_NOW
+#undef HAVE_RTLD_LAZY
+
+/* Types */
+#undef SILC_SIZEOF_LONG_LONG
+#undef SILC_SIZEOF_LONG
+#undef SILC_SIZEOF_INT
+#undef SILC_SIZEOF_SHORT
+#undef SILC_SIZEOF_CHAR
+#undef SILC_SIZEOF_VOID_P
+
+/* MP library */
+#undef SILC_MP_GMP
+#undef SILC_MP_NSS_MPI
+
+/* Redefs for SOCKS5 library */
+/* macros/curses checks */
+#undef HAS_CURSES
+#undef USE_SUNOS_CURSES
+#undef USE_BSD_CURSES
+#undef USE_SYSV_CURSES
+#undef USE_NCURSES
+#undef NO_COLOR_CURSES
+#undef SCO_FLAVOR
+
+#undef SOCKS
+#undef SOCKS5
+#undef Rconnect
+#undef Rgetsockname
+#undef Rgetpeername
+#undef Rbind
+#undef Raccept  
+#undef Rlisten
+#undef Rselect
+#undef Rrecvfrom
+#undef Rsendto
+#undef Rrecv
+#undef Rsend
+#undef Rread
+#undef Rwrite
+#undef Rrresvport
+#undef Rshutdown
+#undef Rlisten
+#undef Rclose
+#undef Rdup
+#undef Rdup2
+#undef Rfclose
+#undef Rgethostbyname
+
+/* Native WIN32 compilation (-mno-cygwin GCC option) under cygwin, though
+   the code compiles with any native WIN32 compiler. */
+#undef SILC_WIN32
+
+/* SILC distribution definitions (leave this at the end of file) */
diff --git a/apps/irssi/AUTHORS b/apps/irssi/AUTHORS
new file mode 100644 (file)
index 0000000..b82b08a
--- /dev/null
@@ -0,0 +1 @@
+Timo Sirainen, tss@iki.fi
diff --git a/apps/irssi/COPYING b/apps/irssi/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
index 304c44d868e1715483b63b7faca403ec7ded0092..5d3888cf8347bad23e908322ef00bf600215eaf8 100644 (file)
@@ -1,28 +1,45 @@
 # create default-config.h
 config.h: default-config.h default-theme.h
 
-default-config.h: $(srcdir)/config
-       $(srcdir)/file2header.sh $(srcdir)/config default_config > default-config.h
+default-config.h: $(srcdir)/silc.conf
+       $(srcdir)/file2header.sh $(srcdir)/silc.conf default_config > default-config.h
 default-theme.h: $(srcdir)/default.theme
        $(srcdir)/file2header.sh $(srcdir)/default.theme default_theme > default-theme.h
 
-SUBDIRS = src docs
+if BUILD_PLUGINS
+PLUGINS=plugins
+endif
+if BUILD_SERVERTEST
+SERVERTEST=servertest
+endif
 
-confdir = $(sysconfdir)/irssi
-conf_DATA = config default.theme
+SUBDIRS = src docs scripts
 
-noinst_HEADERS = irssi-version.h
+include $(top_srcdir)/Makefile.defines.in
+
+#confdir = $(sysconfdir)
+confdir = $(silc_etcdir)
+conf_DATA = silc.conf
+
+themedir = $(datadir)/irssi/themes
+theme_DATA = default.theme
+
+noinst_HEADERS = irssi-version.h.in
 
 EXTRA_DIST = \
        autogen.sh \
        curses.m4 \
        README \
+       README.cygwin \
        file2header.sh \
        irssi.spec \
        irssi.spec.in \
        $(conf_DATA) \
+       $(theme_DATA) \
        irssi-config.in \
-       irssi-icon.png
+       irssi-icon.png \
+       Makefile.defines.in \
+       Makefile.defines_int.in
 
 ## make rpms
 rpm: Makefile
index 753505964ac99bb768cd9dd566a8db7758278a4e..33f02000a4bb60cae063148eac3a4eca25b364e5 100644 (file)
@@ -4,7 +4,6 @@
 #undef PLUGINSDIR
 
 /* misc.. */
-#undef MEM_DEBUG
 #undef HAVE_IPV6
 #undef HAVE_POPT_H
 #undef HAVE_SOCKS_H
 #undef SCO_FLAVOR
 
 /* our own curses checks */
-#undef USE_CURSES_WINDOWS
 #undef HAVE_NCURSES_USE_DEFAULT_COLORS
 #undef HAVE_CURSES_IDCOK
 #undef HAVE_CURSES_RESIZETERM
 #undef HAVE_CURSES_WRESIZE
-#undef USE_CURSES_WINDOWS
+
+/* terminfo/termcap */
+#undef HAVE_TERMINFO
 
 /* nls */
 #undef ENABLE_NLS
index daf8a990b18d860fd8f43e47ca5d51c34f4b5997..5ad69a2724375d696038fbb29bc27b8967ea7a45 100755 (executable)
@@ -15,10 +15,6 @@ fi
 # get versions
 version_date=`date +%Y%m%d`
 
-echo "/* automatically created by autogen.sh */" > irssi-version.h.in
-echo "#define IRSSI_VERSION \"@VERSION@\"" >> irssi-version.h.in
-echo "#define IRSSI_VERSION_DATE \"$version_date\"" >> irssi-version.h.in
-
 # create help files
 echo "Creating help files..."
 perl syntax.pl
@@ -80,6 +76,13 @@ if test "$DIE" -eq 1; then
   exit 1
 fi
 
+#if test -z "$*"; then
+#  echo "**Warning**: I am going to run \`configure' with no arguments."
+#  echo "If you wish to pass any to it, please specify them on the"
+#  echo \`$0\'" command line."
+#  echo
+#fi
+
 case $CC in
 xlc )
   am_opt=--include-deps;;
@@ -102,4 +105,12 @@ autoconf
 echo "Running automake --gnu $am_opt ..."
 automake --add-missing --gnu $am_opt
 
-conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
+#conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
+
+#if test x$NOCONFIGURE = x; then
+#  echo Running $srcdir/configure $conf_flags "$@" ...
+#  $srcdir/configure $conf_flags "$@" \
+#  && echo Now type \`make\' to compile $PKG_NAME || exit 1
+#else
+#  echo Skipping configure process.
+#fi
diff --git a/apps/irssi/config b/apps/irssi/config
deleted file mode 100644 (file)
index 876b402..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-lservers = (
-  { address = "irc.stealth.net"; chatnet = IRCNet; port = 6668; },
-  { address = "irc.efnet.net"; chatnet = EFNet; port = 6667; },
-  { address = "irc.undernet.org"; chatnet = Undernet; port = 6667; },
-  { address = "irc.dal.net"; chatnet = DALnet; port = 6667; },
-  { address = "irc.openprojects.net"; chatnet = OPN; port = 6667; },
-  { address = "irc.ptlink.net"; chatnet = PTlink; port = 6667; }
-  { address = "silc.pspt.fi"; chatnet = SILC; port = 706; }
-);
-
-chatnets = {
-  IRCNet = { type = "IRC"; max_kicks = 4; max_modes = 3; max_msgs = 5; max_whois = 4; };
-  EFNet = { type = "IRC"; max_kicks = 4; max_modes = 4; max_msgs = 3; };
-  Undernet = { type = "IRC"; max_kicks = 4; max_modes = 3; max_msgs = 3; max_query_chans = "1"; };
-  DALNet = { type = "IRC"; max_kicks = 4; max_modes = 6; max_msgs = 3; };
-  OPN = { type = "IRC"; max_kicks = 1; max_modes = 6; max_msgs = 100; };
-  PTLink = { type = "IRC"; max_kicks = 1; max_modes = 6; max_msgs = 100; };
-  SILC = { type = "SILC"; };
-};
-
-channels = (
-  { name = "#irssi"; chatnet = ircnet; autojoin = No; },
-  { name = "#irssi"; chatnet = opn; autojoin = No; },
-  { name = "#silc"; chatnet = silc; autojoin = No; }
-);
-
-aliases = {
-  J = "join";
-  WJOIN = "join -window";
-  WQUERY = "query -window";
-  LEAVE = "part";
-  BYE = "quit";
-  EXIT = "quit";
-  SIGNOFF = "quit";
-  DESCRIBE = "action";
-  DATE = "time";
-  HOST = "userhost";
-  LAST = "lastlog";
-  SAY = "msg *";
-  WI = "whois";
-  WII = "whois $0 $0";
-  WW = "whowas";
-  W = "who";
-  N = "names";
-  M = "msg";
-  T = "topic";
-  C = "clear";
-  CL = "clear";
-  K = "kick";
-  KB = "kickban";
-  KN = "knockout";
-  BANS = "ban";
-  B = "ban";
-  MUB = "unban *";
-  UB = "unban";
-  IG = "ignore";
-  UNIG = "unignore";
-  SB = "scrollback";
-  UMODE = "mode $N";
-  WC = "window close";
-  WN = "window new hide";
-  SV = "say Irssi $J - http://irssi.org/";
-  GOTO = "sb goto";
-  CHAT = "dcc chat";
-};
diff --git a/apps/irssi/config.h.in b/apps/irssi/config.h.in
deleted file mode 100644 (file)
index 14b0915..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/* config.h.in.  Generated automatically from configure.in by autoheader.  */
-
-/* Define if you need to in order for stat and other things to work.  */
-#undef _POSIX_SOURCE
-
-/* Define if you have the ANSI C header files.  */
-#undef STDC_HEADERS
-
-/* misc.. */
-#undef MEM_DEBUG
-#undef HAVE_IPV6
-#undef HAVE_POPT_H
-#undef HAVE_SOCKS_H
-#undef HAVE_PL_PERL
-#undef HAVE_STATIC_PERL
-#undef HAVE_GMODULE
-#undef WANT_BIG5
-
-/* macros/curses checks */
-#undef HAS_CURSES
-#undef USE_SUNOS_CURSES
-#undef USE_BSD_CURSES
-#undef USE_SYSV_CURSES
-#undef USE_NCURSES
-#undef NO_COLOR_CURSES
-#undef SCO_FLAVOR
-
-/* our own curses checks */
-#undef USE_CURSES_WINDOWS
-#undef HAVE_NCURSES_USE_DEFAULT_COLORS
-#undef HAVE_CURSES_IDCOK
-#undef HAVE_CURSES_RESIZETERM
-#undef HAVE_CURSES_WRESIZE
-#undef USE_CURSES_WINDOWS
-
-/* Define if you have the fcntl function.  */
-#undef HAVE_FCNTL
-
-/* Define if you have the mkfifo function.  */
-#undef HAVE_MKFIFO
-
-/* Define if you have the <dirent.h> header file.  */
-#undef HAVE_DIRENT_H
-
-/* Define if you have the <libintl.h> header file.  */
-#undef HAVE_LIBINTL_H
-
-/* Define if you have the <regex.h> header file.  */
-#undef HAVE_REGEX_H
-
-/* Define if you have the <stdlib.h> header file.  */
-#undef HAVE_STDLIB_H
-
-/* Define if you have the <string.h> header file.  */
-#undef HAVE_STRING_H
-
-/* Define if you have the <sys/ioctl.h> header file.  */
-#undef HAVE_SYS_IOCTL_H
-
-/* Define if you have the <sys/time.h> header file.  */
-#undef HAVE_SYS_TIME_H
-
-/* Define if you have the <sys/utsname.h> header file.  */
-#undef HAVE_SYS_UTSNAME_H
-
-/* Define if you have the <unistd.h> header file.  */
-#undef HAVE_UNISTD_H
-
-/* Name of package */
-#undef PACKAGE
-
-/* Version number of package */
-#undef VERSION
-
-/* Define to 'int' if <sys/socket.h> doesn't define. */
-#undef socklen_t
-
index 87063b4ee2ab0aaa21c89b570edb0c915db14395..dfa63c847734d9d8880b89d98417c3937375c0b2 100644 (file)
@@ -1,7 +1,13 @@
 AC_INIT(src)
 
+# we don't want VERSION in our config.h
+if test -n "`grep '^#undef VERSION' config.h.in`"; then
+  grep -v '^#undef VERSION' config.h.in > config.h.in.temp
+  mv -f config.h.in.temp config.h.in
+fi
+
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(Irssi SILC, 0.7.98.3)
+AM_INIT_AUTOMAKE(Irssi-SILC, 0.7.99)
 
 AM_MAINTAINER_MODE
 
@@ -9,30 +15,17 @@ AC_ISC_POSIX
 AC_PROG_CC
 AC_PROG_CPP
 AC_STDC_HEADERS
-AC_ARG_PROGRAM
 AM_PROG_LIBTOOL
 
-dnl * ahem.. :) we don't want static libraries for modules
-if test "x$lt_target" = "x"; then
-        if test "$target" = "NONE"; then
-               lt_target="$host"
-       else
-               lt_target="$target"
-       fi
-fi
+AC_PATH_PROG(sedpath, sed)
+AC_PATH_PROG(perlpath, perl)
+
 dnl * --disable-static isn't a good idea, complain if it's used
 if test "x$enable_static" = "xno"; then
        AC_ERROR([Don't give --disable-static option to configure])
 fi
 
-${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \
-$libtool_flags --disable-static --output=libtool-shared --no-verify $ac_aux_dir/ltmain.sh $lt_target \
-|| { echo "configure: error: libtool configure failed" 1>&2; exit 1; }
-${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \
-$libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \
-|| { echo "configure: error: libtool configure failed" 1>&2; exit 1; }
-
-AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h libintl.h)
+AC_CHECK_HEADERS(string.h stdlib.h unistd.h dirent.h sys/ioctl.h sys/resource.h)
 
 # check posix headers..
 AC_CHECK_HEADERS(sys/time.h sys/utsname.h regex.h)
@@ -102,6 +95,18 @@ AC_ARG_WITH(proxy,
        fi,
        want_irssiproxy=no)
 
+AC_ARG_WITH(terminfo,
+[  --with-terminfo         Use terminfo directly instead of curses],
+       if test x$withval = xyes; then
+               want_terminfo=yes
+       else
+               if test "x$withval" = xno; then
+                       want_terminfo=no
+               else
+                       want_terminfo=yes
+               fi
+       fi,
+       want_terminfo=yes)
 
 AC_ARG_WITH(modules,
 [  --with-modules          Specify what modules to build in binary],
@@ -109,39 +114,73 @@ AC_ARG_WITH(modules,
                build_modules="$withval"
        fi)
 
-if test "x$prefix" = "xNONE"; then
-       PERL_LIB_DIR=""
-else
-       PERL_LIB_DIR="$prefix"
+if test "x$prefix" != "xNONE"; then
+        prefix=`eval echo $prefix`
+       PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=$prefix"
+       perl_library_dir="PERL_USE_LIB"
+       perl_set_use_lib=yes
+
+       perl_prefix_note=yes
 fi
 
-AC_ARG_ENABLE(perl-path,
-[  --enable-perl-path=dir  Specify where to install the Perl libraries for irssi],
-       if test x$enableval = xyes; then
-               want_perl=yes
+AC_ARG_WITH(perl-staticlib,
+[  --with-perl-staticlib   Specify that we want to link perl libraries
+                        statically in irssi, default is no],
+       if test x$withval = xyes; then
+               want_staticperllib=yes
        else
-               if test "x$enableval" = xno; then
-                       want_perl=no
+               if test "x$withval" = xno; then
+                       want_staticperllib=no
                else
-                       want_perl=yes
-                       PERL_LIB_DIR="$enableval"
-                       perl_lib_dir_given=yes
+                       want_staticperllib=yes
                fi
        fi,
-       want_perl=yes)
+       want_staticperllib=no)
 
-AC_ARG_ENABLE(perl,
-[  --enable-perl[=yes|no|static]  Build with Perl support - also specifies
-                          if it should be built into main irssi binary
-                          (static) or as module (default)],
-       if test x$enableval = xyes; then
+
+AC_ARG_WITH(perl-lib,
+[  --with-perl-lib=[site|vendor|DIR]  Specify where to install the
+                        Perl libraries for irssi, default is site],
+       if test "x$withval" = xyes; then
+               want_perl=yes
+       elif test "x$withval" = xno; then
+               want_perl=no
+       elif test "x$withval" = xsite; then
                want_perl=yes
-       elif test x$enableval = xstatic; then
+               perl_prefix_note=no
+               PERL_MM_PARAMS=""
+       elif test "x$withval" = xvendor; then
+               want_perl=yes
+               perl_prefix_note=no
+               if test -z "`$perlpath -v|grep '5\.0'`"; then
+                       PERL_MM_PARAMS="INSTALLDIRS=vendor"
+               else
+                       PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`perl -e 'use Config; print $Config{prefix}'`"
+               fi
+               perl_library_dir="(vendor default - `$perlpath -e 'use Config; print $Config{archlib}'`)"
+       else
+               want_perl=yes
+               perl_prefix_note=no
+               PERL_MM_PARAMS="INSTALLDIRS=perl LIB=$withval"
+               perl_library_dir="PERL_USE_LIB"
+               perl_set_use_lib=yes
+       fi,
+       want_perl=yes)
+
+AC_ARG_WITH(perl,
+[  --with-perl[=yes|no|module]  Build with Perl support - also specifies
+                        if it should be built into main irssi binary
+                        (static, default) or as module],
+       if test x$withval = xyes; then
+               want_perl=static
+       elif test x$withval = xstatic; then
                want_perl=static
+       elif test x$withval = xmodule; then
+               want_perl=module
        else
                want_perl=no
        fi,
-       want_perl=yes)
+       want_perl=static)
 
 AC_ARG_WITH(tests,
 [  --with-tests           Run all the tests],
@@ -151,26 +190,6 @@ AC_ARG_WITH(tests,
        TEST_DIR=)
 AC_SUBST(TEST_DIR)
 
-AC_ARG_ENABLE(curses-windows,
-[  --enable-curses-windows Use curses windows],
-       if test x$enableval != xno; then
-               AC_DEFINE(USE_CURSES_WINDOWS)
-       fi,
-       AC_DEFINE(USE_CURSES_WINDOWS))
-
-AC_ARG_ENABLE(memdebug,
-[  --enable-memdebug       Enable memory debugging],
-       if test x$enableval = xyes; then
-               want_memdebug=yes
-       else
-               if test "x$enableval" = xno; then
-                       want_memdebug=no
-               else
-                       want_memdebug=yes
-               fi
-       fi,
-       want_memdebug=no)
-
 AC_ARG_ENABLE(ipv6,
 [  --enable-ipv6           Enable IPv6 support],
        if test x$enableval = xyes; then
@@ -188,30 +207,35 @@ dnl **
 dnl ** just some generic stuff...
 dnl **
 
+dnl * OS specific options
+case "$host_os" in
+  hpux*)
+    CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED"
+    ;;
+  *)
+    ;;
+esac
+
+
 AC_CHECK_FUNCS(mkfifo fcntl)
 
-AC_CHECK_LIB(socket, socket, [
-       PROG_LIBS="$PROG_LIBS -lsocket"
+AC_CHECK_FUNC(socket, [], [
+       AC_CHECK_LIB(socket, socket, [
+               LIBS="$LIBS -lsocket"
+       ])
 ])
 
-AC_CHECK_LIB(nsl, inet_addr, [
-       PROG_LIBS="$PROG_LIBS -lnsl"
-], -lsocket)
+AC_CHECK_FUNC(inet_addr, [], [
+       AC_CHECK_LIB(nsl, inet_addr, [
+               LIBS="$LIBS -lnsl"
+       ], -lsocket)
+])
 
 dnl * gcc specific options
 if test "x$ac_cv_prog_gcc" = "xyes"; then
   CFLAGS="$CFLAGS -Wall"
 fi
 
-dnl * OS specific options
-case "$host_os" in
-  hpux*)
-    CFLAGS="$CFLAGS -D_XOPEN_SOURCE_EXTENDED"
-    ;;
-  *)
-    ;;
-esac
-
 dnl * socklen_t - AC_CHECK_TYPE() would be _really_ useful if it only would
 dnl * accept header files where to find the typedef..
 AC_MSG_CHECKING([for socklen_t])
@@ -234,7 +258,7 @@ dnl **
 
 if test "x$want_socks" = "xyes"; then
        AC_CHECK_LIB(socks, connect, [
-               PROG_LIBS="$PROG_LIBS -lsocks"
+               LIBS="$LIBS -lsocks"
                AC_CHECK_HEADER(socks.h, [
                        AC_DEFINE(HAVE_SOCKS_H)
                        CFLAGS="$CFLAGS -DSOCKS"
@@ -250,13 +274,11 @@ dnl **
 dnl ** fe-text checks
 dnl **
 
-AC_PATH_PROG(sedpath, sed)
-
 AC_DEFUN(AC_CHECK_GLIBDIR,[
   AC_MSG_CHECKING([whether GLib is unpacked to irssi dir])
 
   GLIB_DIR=`for d in *; do test -f $d/glib.h && echo $d; done`
-  if test "x$GLIB_DIR" != "x"; then
+  if test -n "$GLIB_DIR"; then
     dnl * glib in irssi directory, use it
     AC_MSG_RESULT([yes, using it])
 
@@ -299,9 +321,9 @@ AC_DEFUN(AC_CHECK_GLIBDIR,[
 
 AC_CHECK_GLIBDIR
 
-if test "x$GLIB_DIR" = "x"; then
+if test -z "$GLIB_DIR"; then
   AM_PATH_GLIB(1.2.0,,, gmodule)
-  if test "x$GLIB_LIBS" = "x"; then
+  if test -z "$GLIB_LIBS"; then
     echo "*** trying without -lgmodule"
     glib_config_args=
     AM_PATH_GLIB(1.2.0)
@@ -309,7 +331,7 @@ if test "x$GLIB_DIR" = "x"; then
     AC_DEFINE(HAVE_GMODULE)
   fi
 
-  if test "x$GLIB_LIBS" = "x"; then
+  if test -z "$GLIB_LIBS"; then
     echo
     echo "*** If you don't have GLIB, you can get it from ftp://ftp.gtk.org"
     echo "*** If you can't install GLIB anywhere or if you don't want to,"
@@ -324,14 +346,16 @@ if test "x$GLIB_DIR" = "x"; then
     glib_file=glib-1.2.10.tar.gz
 
     dlcmd=
-    if test "x`ncftpget 2>/dev/null|grep -i ncftp`" != "x"; then
+    if test -n "`ncftpget 2>/dev/null|grep -i ncftp`"; then
       dlcmd="ncftpget ftp://ftp.gtk.org/pub/gtk/v1.2/$glib_file"
     fi
-    if test "x`wget 2>/dev/null|grep -i wget`" != "x"; then
+    if test -n "`wget 2>/dev/null|grep -i wget`"; then
       dlcmd="wget http://irssi.org/files/$glib_file"
     fi
-    if test "x$dlcmd" != "x"; then
+    if test -n "$dlcmd"; then
       echo "*** I can download GLib for you now. If you don't want to, press CTRL-C now."
+      echo
+      echo "*** Press ENTER to continue"
       read answer
       eval $dlcmd
       if `gunzip $glib_file`; then
@@ -343,13 +367,13 @@ if test "x$GLIB_DIR" = "x"; then
       fi
     fi
 
-    if test "x$GLIB_LIBS" = "x"; then
+    if test -z "$GLIB_LIBS"; then
       AC_ERROR([GLIB is required to build irssi.])
     fi
   fi
 fi
 
-PROG_LIBS="$PROG_LIBS $GLIB_LIBS"
+LIBS="$LIBS $GLIB_LIBS"
 
 dnl **
 dnl ** check if we can link dynamic libraries to modules
@@ -362,7 +386,7 @@ DYNLIB_MODULES=no
 dnl ** compile object file
 cat > conftest.c <<EOF
 #include <math.h>
-int modfunc(void){return (int)floor(1.2);}
+int modfunc(){return (int)floor(1.2);}
 EOF
 
 ./libtool --mode=compile $CC $CFLAGS -c conftest.c 2> /dev/null > /dev/null
@@ -392,7 +416,7 @@ else if (!g_module_symbol(m, "modfunc", (gpointer *) &modfunc))
 else if (modfunc() == 1) g_print("ok"); else g_print("wrong result?! 1 vs %d", modfunc());
 return 0; }
 EOF
-  $CC $CFLAGS conftest.c -o conftest $GLIB_CFLAGS $GLIB_LIBS 2> /dev/null > /dev/null
+  $CC $CFLAGS $LDFLAGS conftest.c -o conftest $GLIB_CFLAGS $GLIB_LIBS 2> /dev/null > /dev/null
   if test ! -s conftest; then
     AC_MSG_RESULT([no, error compiling test program])
   else
@@ -414,35 +438,38 @@ dnl **
 if test "x$want_textui" = "xyes"; then
        AC_CHECK_CURSES
 
-       if test "x$has_ncurses" != "x"; then
-               AC_CHECK_LIB(ncurses, use_default_colors, [
-                       AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(ncurses, idcok, [
-                       AC_DEFINE(HAVE_CURSES_IDCOK)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(ncurses, resizeterm, [
-                       AC_DEFINE(HAVE_CURSES_RESIZETERM)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(ncurses, wresize, [
-                       AC_DEFINE(HAVE_CURSES_WRESIZE)
-               ],, $CURSES_LIBS)
-       elif test "x$has_curses" = "xtrue"; then
-               AC_CHECK_LIB(curses, idcok, [
-                       AC_DEFINE(HAVE_CURSES_IDCOK)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(curses, resizeterm, [
-                       AC_DEFINE(HAVE_CURSES_RESIZETERM)
-               ],, $CURSES_LIBS)
-               AC_CHECK_LIB(curses, wresize, [
-                       AC_DEFINE(HAVE_CURSES_WRESIZE)
-               ],, $CURSES_LIBS)
+       LIBS="$LIBS $CURSES_LIBS"
+       if test "x$has_curses" = "xtrue"; then
+                       AC_CHECK_FUNC(use_default_colors, AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS))
+                       AC_CHECK_FUNC(idcok, AC_DEFINE(HAVE_CURSES_IDCOK))
+                       AC_CHECK_FUNC(resizeterm, AC_DEFINE(HAVE_CURSES_RESIZETERM))
+                       AC_CHECK_FUNC(wresize, AC_DEFINE(HAVE_CURSES_WRESIZE))
+               if test "x$want_terminfo" = "xyes"; then
+                       AC_CHECK_LIB(curses, setupterm,, [
+                               want_termcap=yes
+                       ])
+               fi
        else
-               want_textui=no
-               curses_error=yes
+               AC_CHECK_LIB(tinfo, setupterm, [
+                 LIBS="$LIBS -ltinfo"
+                 want_terminfo=yes
+               ], AC_CHECK_LIB(termlib, tgetent, [
+                 LIBS="$LIBS -ltermlib"
+                 want_termcap=yes
+               ], AC_CHECK_LIB(termcap, tgetent, [
+                 LIBS="$LIBS -ltermcap"
+                 want_termcap=yes
+               ], [
+                 AC_MSG_WARN(Terminfo/termcap not found)
+                 want_textui=no
+                 curses_error=yes
+               ])))
         fi
-else
-       has_curses=false
+               if test "x$want_termcap" = "xyes"; then
+                       AC_CHECK_FUNC(tparm,, need_tparm=yes)
+       else
+               AC_DEFINE(HAVE_TERMINFO)
+               fi
 fi
 
 dnl **
@@ -450,17 +477,18 @@ dnl ** perl checks
 dnl **
 
 if test "$want_perl" != "no"; then
-       AC_PATH_PROG(perlpath, perl)
        AC_MSG_CHECKING(for working Perl support)
 
-       if test "x$perlpath" = "x"; then
+       if test -z "$perlpath"; then
                        perl_check_error="perl binary not found"
        else
                PERL_CFLAGS=`$perlpath -MExtUtils::Embed -e ccopts 2>/dev/null`
        fi
 
-       if test "x$PERL_CFLAGS" = "x"; then
-               perl_check_error="Error getting perl CFLAGS"
+       if test -z "$PERL_CFLAGS"; then
+               if test -n "$perl_check_error"; then
+                       perl_check_error="Error getting perl CFLAGS"
+               fi
                AC_MSG_RESULT([not found, building without Perl])
                want_perl=no
        else
@@ -485,10 +513,10 @@ if test "$want_perl" != "no"; then
                fi
 
                dnl * don't check libperl.a if dynaloader.a wasn't found..
-               if test "x$DYNALOADER_A" != "x"; then
+               if test -n "$DYNALOADER_A"; then
                        dnl * find either libperl.a or libperl.so
                        LIBPERL_A=`echo "$PERL_LDFLAGS -L/usr/lib"|$perlpath -e 'foreach (split(/ /, <STDIN>)) { if (/^-L(.*)/) { my $dir=$1; if (\`ls $dir/libperl.so* 2>/dev/null\`) { print "-lperl"; last; }; if (-e "$dir/libperl.a") { print "$dir/libperl.a"; last } } };'`
-                       if test "x$LIBPERL_A" = "x"; then
+                       if test -z "$LIBPERL_A"; then
                                perl_mod_error="Didn't find location of -lperl"
                                DYNALOADER_A=
                        elif test "$LIBPERL_A" = "-lperl"; then
@@ -521,7 +549,7 @@ if test "$want_perl" != "no"; then
                dnl * check that perl's ldflags actually work
                AC_CACHE_VAL(irssi_cv_lib_perl_works, [
                        echo "main(){perl_alloc(); return 0;}" > conftest.c
-                       $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> /dev/null > /dev/null
+                       $CC $CFLAGS conftest.c -o conftest $LDFLAGS $PERL_LDFLAGS 2> perl.error.tmp > /dev/null
                        if test -s conftest; then
                                irssi_cv_lib_perl_works=yes
                        else
@@ -530,22 +558,23 @@ if test "$want_perl" != "no"; then
                ])
 
                if test "x$irssi_cv_lib_perl_works" = "xno"; then
-                       perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS"
+                       perl_check_error="Error linking with perl libraries: $PERL_LDFLAGS: `cat perl.error.tmp`"
                        AC_MSG_RESULT([error linking with perl libraries, building without Perl])
                        want_perl=no
                fi
+               rm -f perl.error.tmp
        fi
 
        if test "x$want_perl" != "xno"; then
                if test "x$want_perl" = "xstatic"; then
                        AC_MSG_RESULT(ok)
-               elif test "x$DYNALOADER_A" = "x"; then
+               elif test -z "$DYNALOADER_A"; then
                        AC_MSG_RESULT([error parsing ldopts, building Perl into irssi binary instead of as module])
                        want_perl=static
                else
                        AC_MSG_RESULT(ok)
                        PERL_LDFLAGS=`echo $PERL_LDFLAGS | $perlpath -pe 's/^(.* )*[[^ ]]*DynaLoader\.a/\1libperl_dynaloader.la/'`
-                       if test "x$LIBPERL_A" != "x"; then
+                       if test -n "$LIBPERL_A"; then
                                PERL_LDFLAGS=`echo $PERL_LDFLAGS | $sedpath -e 's/-lperl /libperl_orig.la /' -e 's/-lperl$/libperl_orig.la$/'`
                        fi
                        AC_SUBST(LIBPERL_A)
@@ -555,8 +584,8 @@ if test "$want_perl" != "no"; then
                if test "x$want_perl" = "xstatic"; then
                        dnl * building with static perl support
                        dnl * all PERL_LDFLAGS linking is done in fe-text
-                       PERL_LDFLAGS="../perl/libperl_static.la $PERL_LDFLAGS"
-                       PERL_LINK_LIBS="$PERL_LDFLAGS"
+                       PERL_LINK_FLAGS="$PERL_LDFLAGS"
+                       PERL_LINK_LIBS="../perl/libperl_core_static.la"
                        PERL_FE_LINK_LIBS="../perl/libfe_perl_static.la"
                        PERL_LDFLAGS=
                        AC_DEFINE(HAVE_STATIC_PERL)
@@ -564,31 +593,51 @@ if test "$want_perl" != "no"; then
                        dnl * build only static library of perl module
                        perl_module_lib=
                        perl_module_fe_lib=
-                       perl_static_lib=libperl_static.la
+                       perl_static_lib=libperl_core_static.la
                        perl_static_fe_lib=libfe_perl_static.la
                        PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool'
                else
-                       dnl * build dynamic library of perl module,
-                       dnl * use libtool-shared to prevent creating of
-                       dnl * libperl.a
+                       dnl * build dynamic library of perl module
                        perl_module_lib=libperl_core.la
                        perl_module_fe_lib=libfe_perl.la
                        perl_static_lib=
                        perl_static_fe_lib=
-                       PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool-shared'
+                       PERL_LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+               fi
+
+               if test "x$want_staticperllib" = "xyes"; then
+                       PERL_MM_PARAMS="$PERL_MM_PARAMS LINKTYPE=static"
+                       PERL_LINK_LIBS="$PERL_LINK_LIBS ../perl/common/blib/arch/auto/Irssi/Irssi.a ../perl/irc/blib/arch/auto/Irssi/Irc/Irc.a ../perl/ui/blib/arch/auto/Irssi/UI/UI.a ../perl/textui/blib/arch/auto/Irssi/TextUI/TextUI.a"
+                       PERL_STATIC_LIBS=1
+               else
+                       PERL_STATIC_LIBS=0
                fi
+
+               # figure out the correct @INC path - we'll need to do this
+               # through MakeMaker since it's difficult to get it right
+               # otherwise.
+               if test "x$perl_set_use_lib" = "xyes"; then
+                       perl -e 'use ExtUtils::MakeMaker; WriteMakefile("NAME" => "test", "MAKEFILE" => "Makefile.test");' $PERL_MM_PARAMS >/dev/null
+                       PERL_USE_LIB=`perl -e 'open(F, "Makefile.test"); while (<F>) { chomp; if (/^(\w+) = (.*$)/) { $keys{$1} = $2; } }; $key = $keys{INSTALLARCHLIB}; while ($key =~ /\\$\((\w+)\)/) { $value = $keys{$1}; $key =~ s/\\$\($1\)/$value/; }; print $key;'`
+                       rm -f Makefile.test
+               fi
+
                AC_SUBST(perl_module_lib)
                AC_SUBST(perl_static_lib)
                AC_SUBST(perl_module_fe_lib)
                AC_SUBST(perl_static_fe_lib)
                AC_SUBST(PERL_LIBTOOL)
 
+               AC_SUBST(PERL_LINK_FLAGS)
                AC_SUBST(PERL_LINK_LIBS)
-               AC_SUBST(PERL_FE_LINK_LIBS)
+                AC_SUBST(PERL_FE_LINK_LIBS)
 
                AC_SUBST(PERL_LDFLAGS)
                AC_SUBST(PERL_CFLAGS)
-               AC_SUBST(PERL_LIB_DIR)
+
+               AC_SUBST(PERL_USE_LIB)
+               AC_SUBST(PERL_MM_PARAMS)
+               AC_SUBST(PERL_STATIC_LIBS)
        fi
 fi
 
@@ -597,10 +646,15 @@ AM_CONDITIONAL(BUILD_TEXTUI, test "$want_textui" = "yes")
 AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes")
 AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes")
 AM_CONDITIONAL(BUILD_PLUGINS, test "$want_plugins" = "yes")
-AM_CONDITIONAL(BUILD_SERVERTEST, test "x$TEST_DIR" != "x")
+AM_CONDITIONAL(BUILD_SERVERTEST, test -n "$TEST_DIR")
 AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no")
 AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static")
+AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes")
+AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes")
 
+# move LIBS to PROG_LIBS so they're not tried to be used when linking eg. perl libraries
+PROG_LIBS=$LIBS
+LIBS=
 AC_SUBST(PROG_LIBS)
 
 dnl **
@@ -611,10 +665,9 @@ dnl ** (these could be made configurable)
 
 CHAT_MODULES="silc"
 silc_MODULES=""
-if test "x$build_modules" != "x"; then
+if test -n "$build_modules"; then
        silc_MODULES="$silc_MODULES $build_modules"
 fi
-#PROG_LIBS="$PROG_LIBS -lsilc"
 
 dnl ****************************************
 
@@ -635,11 +688,11 @@ for c in $CHAT_MODULES; do
                FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/libfe_common_$c.a "
        fi
        for s in `eval echo \\$${c}_MODULES`; do
-               CHAT_LIBS="$CHAT_LIBS ../$c/$s/.libs/lib${c}_$s.a"
+               CHAT_LIBS="$CHAT_LIBS ../$c/$s/lib${c}_$s.a"
                module_inits="$module_inits ${c}_${s}_init();"
                module_deinits="${c}_${s}_deinit(); $module_deinits"
                if test -f $srcdir/src/fe-common/$c/$s/module.h; then
-                       FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/.libs/libfe_${c}_$s.a "
+                       FE_COMMON_LIBS="$FE_COMMON_LIBS../fe-common/$c/$s/libfe_${c}_$s.a "
                        fe_module_inits="$fe_module_inits fe_${c}_${s}_init();"
                        fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits"
                fi
@@ -648,7 +701,7 @@ for c in $CHAT_MODULES; do
        file="$srcdir/src/$c/$c.c"
         echo "/* this file is automatically generated by configure - don't change */" > $file
        echo "void ${c}_core_init(void); void ${c}_core_deinit(void);" >> $file
-       if test "x$module_inits" != "x"; then
+       if test -n "$module_inits"; then
                echo "$module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
                echo "$module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
        fi
@@ -658,7 +711,7 @@ for c in $CHAT_MODULES; do
        if test -f $srcdir/src/fe-common/$c/module.h; then
                file="$srcdir/src/fe-common/$c/${c}-modules.c"
                echo "/* this file is automatically generated by configure - don't change */" > $file
-                if test "x$fe_module_inits" != "x"; then
+                if test -n "$fe_module_inits"; then
                        echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
                        echo "$fe_module_deinits" | $sedpath -e 's/ *$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
                fi
@@ -675,15 +728,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS"
 AC_SUBST(COMMON_NOUI_LIBS)
 AC_SUBST(COMMON_LIBS)
 
-dnl **
-dnl ** memory debugging
-dnl **
-
-if test "x$want_memdebug" = "xyes"; then
-       AC_DEFINE(MEM_DEBUG)
-fi
-AM_CONDITIONAL(BUILD_MEMDEBUG, test "x$want_memdebug" = "xyes")
-
 dnl **
 dnl ** tr-Chinese Big5 support
 dnl **
@@ -697,9 +741,38 @@ dnl ** IPv6 support
 dnl **
 
 if test "x$want_ipv6" = "xyes"; then
-       AC_DEFINE(HAVE_IPV6)
+       AC_MSG_CHECKING([for IPv6])
+       AC_CACHE_VAL(irssi_cv_type_in6_addr,
+       [AC_TRY_COMPILE([
+       #include <sys/types.h>
+       #include <sys/socket.h>
+       #include <netinet/in.h>
+       #include <netdb.h>
+       #include <arpa/inet.h>],
+       [struct in6_addr i;],
+       irssi_cv_type_in6_addr=yes,
+       irssi_cv_type_in6_addr=no,
+       )])
+       if test $irssi_cv_type_in6_addr = yes; then
+               AC_DEFINE(HAVE_IPV6)
+       fi
+       AC_MSG_RESULT($irssi_cv_type_in6_addr)
 fi
 
+dnl **
+dnl ** IRSSI_VERSION_DATE and IRSSI_VERSION_TIME
+dnl **
+#VERSION_DATE=`head -1 $srcdir/ChangeLog|sed 's/^\(....\)-\(..\)-\(..\).*/\1\2\3/'`
+#VERSION_TIME=`head -1 $srcdir/ChangeLog|sed -e 's/^[[^ ]]* \(..\):\(..\).*/\1\2/' -e 's/^0*//'`
+#AC_SUBST(VERSION_DATE)
+#AC_SUBST(VERSION_TIME)
+
+#
+# Glue into SILC build system
+#
+INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int"
+AC_SUBST(INCLUDE_DEFINES_INT)
+
 AC_OUTPUT(
 Makefile
 src/Makefile
@@ -712,20 +785,30 @@ src/lib-config/Makefile
 src/lib-popt/Makefile
 src/silc/Makefile
 src/silc/core/Makefile
+src/perl/Makefile
+src/perl/common/Makefile.PL
+src/perl/ui/Makefile.PL
+src/perl/textui/Makefile.PL
+scripts/Makefile
 docs/Makefile
 docs/help/Makefile
 docs/help/in/Makefile
+irssi-version.h
 stamp.h
 irssi.spec
-irssi-version.h
 irssi-config)
 
 dnl ** for building from objdir
-if test "x$want_perl" != "xno"; then
-       old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir
+old_dir=`pwd` && cd $srcdir && whole_dir=`pwd` && cd $old_dir
+if test "x$old_dir" != "x$whole_dir"; then
+       $LN_S $srcdir/irssi-version.h irssi-version.h
 
-       if test "x$old_dir" != "x$whole_dir"; then
-               for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $whole_dir/src/perl/common/typemap $whole_dir/src/perl/common/module.h $whole_dir/src/perl/common/*.xs $whole_dir/src/perl/irc/typemap $whole_dir/src/perl/irc/module.h $whole_dir/src/perl/irc/*.xs; do
+       if test "x$want_perl" != "xno"; then
+               subdirfiles=""
+               for i in $whole_dir/src/perl/common $whole_dir/src/perl/irc $whole_dir/src/perl/ui $whole_dir/src/perl/textui; do
+                       subdirfiles=`echo $subdirfiles $i/typemap $i/module.h $i/*.pm $i/*.xs`
+               done
+               for file in $whole_dir/src/perl/*.[[ch]] $whole_dir/src/perl/libperl_orig.la $whole_dir/src/perl/libperl_dynaloader.la $subdirfiles; do
                        link=`echo $file|$sedpath "s?$whole_dir/??"`
                        rm -f $link
                        $LN_S $file $link
@@ -736,7 +819,16 @@ fi
 echo
 
 if test "x$curses_error" != "xyes"; then
-       echo "Building text frontend ..... : $want_textui"
+       if test "x$want_textui" = "xno"; then
+               text=no
+       elif test "x$want_termcap" = "xyes"; then
+               text="yes, using termcap"
+       elif test "x$want_terminfo" = "xyes"; then
+               text="yes, using terminfo"
+       else
+               text="yes, using curses"
+       fi
+       echo "Building text frontend ..... : $text"
 else
        echo "Building text frontend ..... : NO!!"
        echo " - Because curses was not found, specify the path to it with"
@@ -749,10 +841,10 @@ echo "Building with IPv6 support . : $want_ipv6"
 
 if test "x$want_perl" = "xstatic"; then
        echo "Building with Perl support . : static (in irssi binary)"
-elif test "x$want_perl" = "xyes"; then
+elif test "x$want_perl" = "xmodule"; then
        echo "Building with Perl support . : module"
 else
-       if test "x$perl_check_error" = "x"; then
+       if test -z "$perl_check_error"; then
                echo "Building with Perl support . : no"
        else
                echo "Building with Perl support . : NO!"
@@ -766,18 +858,19 @@ if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then
        echo "   $perl_mod_error"
 fi
 
-if test "x$want_perl" = "xyes"; then
-       if test "x$PERL_LIB_DIR" = "x"; then
-               echo "Perl library directory ..... : (default - usually /usr/local/lib/perl_site)"
-       else
-               echo "Perl library directory ..... : $PERL_LIB_DIR"
-               if test "x$perl_lib_dir_given" != "xyes"; then
-                       echo " - NOTE: This was automatically set to the same directory you gave with"
-                       echo "   --prefix. If you want the perl libraries to install to their 'correct'"
-                       echo "   path, you'll need to give --enable-perl-path= (nothing after '=') option"
-                       echo "   to configure. Anyway, installing perl to this directory should work"
-                       echo "   just as well.."
-               fi
+if test "x$want_perl" != "xno"; then
+       if test "$perl_library_dir" = "PERL_USE_LIB"; then
+               perl_library_dir=$PERL_USE_LIB
+       fi
+       if test -z "$perl_library_dir"; then
+               perl_library_dir="(site default - `$perlpath -e 'use Config; print $Config{sitearch}'`)"
+       fi
+               echo "Perl library directory ..... : $perl_library_dir"
+       if test "x$perl_prefix_note" = "xyes"; then
+               echo "  - NOTE: This was automatically set to the same directory you gave with"
+               echo "  --prefix. If you want the perl libraries to install to their 'correct'"
+               echo "  path, you'll need to give --with-perl-lib=site option to configure."
+               echo "  Anyway, installing perl to this directory should work just as well."
        fi
 fi
 echo "Install prefix ............. : $prefix"
index 7865b1cbfde3a738bc3d9710babc3d0e7c8113b3..83a08126cbc7cb104308145ed73411f725199359 100644 (file)
@@ -211,6 +211,7 @@ AC_DEFUN(AC_NCURSES, [
 
            CURSES_LIBS="$3"
            AC_CHECK_LIB(ncurses, initscr, [
+               true;
            ], [
                 CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
                AC_CHECK_LIB(curses, initscr, [
diff --git a/apps/irssi/default-config.h b/apps/irssi/default-config.h
deleted file mode 100644 (file)
index 635030b..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-const char *default_config =
-"lservers = (\n"
-"  { address = \"irc.stealth.net\"; chatnet = IRCNet; port = 6668; },\n"
-"  { address = \"irc.efnet.net\"; chatnet = EFNet; port = 6667; },\n"
-"  { address = \"irc.undernet.org\"; chatnet = Undernet; port = 6667; },\n"
-"  { address = \"irc.dal.net\"; chatnet = DALnet; port = 6667; },\n"
-"  { address = \"irc.openprojects.net\"; chatnet = OPN; port = 6667; },\n"
-"  { address = \"irc.ptlink.net\"; chatnet = PTlink; port = 6667; }\n"
-"  { address = \"silc.pspt.fi\"; chatnet = SILC; port = 706; }\n"
-");\n"
-"\n"
-"chatnets = {\n"
-"  IRCNet = { type = \"IRC\"; max_kicks = 4; max_modes = 3; max_msgs = 5; max_whois = 4; };\n"
-"  EFNet = { type = \"IRC\"; max_kicks = 4; max_modes = 4; max_msgs = 3; };\n"
-"  Undernet = { type = \"IRC\"; max_kicks = 4; max_modes = 3; max_msgs = 3; max_query_chans = \"1\"; };\n"
-"  DALNet = { type = \"IRC\"; max_kicks = 4; max_modes = 6; max_msgs = 3; };\n"
-"  OPN = { type = \"IRC\"; max_kicks = 1; max_modes = 6; max_msgs = 100; };\n"
-"  PTLink = { type = \"IRC\"; max_kicks = 1; max_modes = 6; max_msgs = 100; };\n"
-"  SILC = { type = \"SILC\"; };\n"
-"};\n"
-"\n"
-"channels = (\n"
-"  { name = \"#irssi\"; chatnet = ircnet; autojoin = No; },\n"
-"  { name = \"#irssi\"; chatnet = opn; autojoin = No; },\n"
-"  { name = \"#silc\"; chatnet = silc; autojoin = No; }\n"
-");\n"
-"\n"
-"aliases = {\n"
-"  J = \"join\";\n"
-"  WJOIN = \"join -window\";\n"
-"  WQUERY = \"query -window\";\n"
-"  LEAVE = \"part\";\n"
-"  BYE = \"quit\";\n"
-"  EXIT = \"quit\";\n"
-"  SIGNOFF = \"quit\";\n"
-"  DESCRIBE = \"action\";\n"
-"  DATE = \"time\";\n"
-"  HOST = \"userhost\";\n"
-"  LAST = \"lastlog\";\n"
-"  SAY = \"msg *\";\n"
-"  WI = \"whois\";\n"
-"  WII = \"whois $0 $0\";\n"
-"  WW = \"whowas\";\n"
-"  W = \"who\";\n"
-"  N = \"names\";\n"
-"  M = \"msg\";\n"
-"  T = \"topic\";\n"
-"  C = \"clear\";\n"
-"  CL = \"clear\";\n"
-"  K = \"kick\";\n"
-"  KB = \"kickban\";\n"
-"  KN = \"knockout\";\n"
-"  BANS = \"ban\";\n"
-"  B = \"ban\";\n"
-"  MUB = \"unban *\";\n"
-"  UB = \"unban\";\n"
-"  IG = \"ignore\";\n"
-"  UNIG = \"unignore\";\n"
-"  SB = \"scrollback\";\n"
-"  UMODE = \"mode $N\";\n"
-"  WC = \"window close\";\n"
-"  WN = \"window new hide\";\n"
-"  SV = \"say Irssi $J - http://irssi.org/\";\n"
-"  GOTO = \"sb goto\";\n"
-"  CHAT = \"dcc chat\";\n"
-"};\n"
-;
index 501a161797d60081456a0615c290caf62098ff50..55cff69d34486105ae28f9aa9507cacbe2a8c908 100644 (file)
@@ -10,7 +10,7 @@
 # up in those formats, and it was really hard to change the colors since you
 # might have had to change them in tens of different places. So, then came
 # this templating system.
-
+    
 # Now the /FORMATs don't have any colors in them, and they also have very
 # little other styling. Most of the stuff you need to change is in this
 # theme file. If you can't change something here, you can always go back
 
 #############################################################################
 
-# default foreground color (%N) - 0 is the "default terminal color"
+# default foreground color (%N) - 0 is the "default terminal color" 
 default_color = 0;
+
 # default foreground color when "0" can't be used,
 # such as with bolds and reverses. white is default.
 default_real_color = 7;
 
 # these characters are automatically replaced with specified color
 # (dark grey by default)
-replaces = { "[]<>=" = "%K$0-%n"; };
+replaces = {};
 
 abstracts = {
   ##
@@ -63,10 +64,10 @@ abstracts = {
   ##
 
   # text to insert at the beginning of each non-message line
-  line_start = "%B-%W!%B-%n ";
+  line_start = "*** ";
 
   # timestamp styling, nothing by default
-  timestamp = "$0-";
+  timestamp = "[$0-]";
 
   # any kind of text that needs hilighting, default is to bold
   hilight = "%_$0-%_";
@@ -75,50 +76,71 @@ abstracts = {
   error = "%R$0-%n";
 
   # channel name is printed
-  channel = "%_$0-%_";
+  channel = "%c$0-%n";
 
   # nick is printed
-  nick = "%_$0-%_";
+  nick = "%c$0-%n";
 
   # nick host is printed
-  nickhost = "[$0-]";
+  nickhost = "($0-)";
 
   # server name is printed
-  server = "%_$0-%_";
+  server = "$0-";
 
   # some kind of comment is printed
-  comment = "[$0-]";
+  comment = "($0-)";
 
   # reason for something is printed (part, quit, kick, ..)
   reason = "{comment $0-}";
 
-  # mode change is printed ([+o nick])
-  mode = "{comment $0-}";
+  # mode change is printed
+  mode = "[$0-]";
 
   ##
   ## channel specific messages
   ##
 
   # highlighted nick/host is printed (joins)
-  channick_hilight = "%C$0-%n";
+  channick_hilight = "%c$0-%n";
   chanhost_hilight = "{nickhost %c$0-%n}";
 
   # nick/host is printed (parts, quits, etc.)
-  channick = "%c$0-%n";
+  channick = "$0-";
   chanhost = "{nickhost $0-}";
 
   # highlighted channel name is printed
   channelhilight = "%c$0-%n";
 
   # ban/ban exception/invite list mask is printed
-  ban = "%c$0-%n";
+  ban = "$0-";
+
+  ##
+  ## Action (/ME command)
+  ##
+
+  # Generic action
+  action = "%Y* $0 $1-";
+
+  # Own sent action
+  ownaction = "%c* $0 $1-";
+
+  ##
+  ## Notice (/NOTICE command)
+  ##
+
+  # Generic notice
+  notice = "%C- $0 $1-";
+
+  # Own sent notice
+  ownnotice = "%g- $0 $1-";
+
 
   ##
   ## messages
   ##
 
   # the basic styling of how to print message, $0 = nick mode, $1 = nick
-  msgnick = "<$0$1-> %|";
+  msgnick = "%c%|<$0$1->%n ";
 
   # message from you is printed. "msgownnick" specifies the styling of the
   # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the
@@ -132,129 +154,132 @@ abstracts = {
   # Example2.2: But you still want to keep <> grey for other messages:
   #  pubmsgnick = "%K{msgnick $0 $1-%K}%n";
   #  pubmsgmenick = "%K{msgnick $0 $1-%K}%n";
-  #  pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n";
+  #  pubmsghinick = "%K{msgnick $1 $0$2-%K}%n";
   #  ownprivmsgnick = "%K{msgnick  $0-%K}%n";
   #  privmsgnick = "%K{msgnick  %R$0-%K}%n";
 
   # $0 = nick mode, $1 = nick
-  ownmsgnick = "{msgnick $0 $1-}";
-  ownnick = "%W$0-%n";
+  ownmsgnick = "{msgnick $0 $1-}%g";
+  ownnick = "$0-";
 
   # public message in channel, $0 = nick mode, $1 = nick
   pubmsgnick = "{msgnick $0 $1-}";
-  pubnick = "%N$0-%n";
+  pubnick = "$0-";
 
   # public message in channel meant for me, $0 = nick mode, $1 = nick
-  pubmsgmenick = "{msgnick $0 $1-}";
-  menick = "%Y$0-%n";
+  pubmsgmenick = "%g<$0$1->%n %|";
+  menick = "$0-";
 
   # public highlighted message in channel
   # $0 = highlight color, $1 = nick mode, $2 = nick
-  pubmsghinick = "{msgnick $1 $0$2-%n}";
+  pubmsghinick = "{msgnick $1 $2-}$0";
 
   # channel name is printed with message
-  msgchannel = "%K:%c$0-%n";
+  msgchannel = "%w|%c$0-";
 
   # private message, $0 = nick, $1 = host
-  privmsg = "[%R$0%K(%r$1-%K)%n] ";
+  privmsg = "*%c$0%n* ";
 
   # private message from you, $0 = "msg", $1 = target nick
-  ownprivmsg = "[%r$0%K(%R$1-%K)%n] ";
-
-  # own private message in query
-  ownprivmsgnick = "{msgnick  $0-}";
-  ownprivnick = "%W$0-%n";
+  ownprivmsg = "->*%c$1-%n* %g";
 
   # private message in query
-  privmsgnick = "{msgnick  %R$0-%n}";
+  privmsgnick = "*%c$0%n* ";
 
-  ##
-  ## Actions (/ME stuff)
-  ##
-
-  # used internally by this theme
-  action_core = "%W * $0-%n";
-
-  # generic one that's used by most actions
-  action = "{action_core $0-} ";
-
-  # own action, both private/public
-  ownaction = "{action $0-}";
-
-  # own action with target, both private/public
-  ownaction_target = "{action_core $0}%K:%c$1%n ";
-
-  # private action sent by others
-  pvtaction = "%W (*) $0-%n ";
-  pvtaction_query = "{action $0-}";
-
-  # public action sent by others
-  pubaction = "{action $0-}";
+  # own private message in query
+  ownprivmsgnick = "->*%c$0%n* %g$1-";
+  ownprivnick = "$0-";
 
 
   ##
   ## other IRC events
   ##
 
-  # notices
-  ownnotice = "[%r$0%K(%R$1-%K)]%n ";
-  notice = "%K-%M$0-%K-%n ";
-  pubnotice_channel = "%K:%m$0-";
-  pvtnotice_host = "%K(%m$0-%K)";
-  servernotice = "%g!$0-%n ";
-
   # CTCPs
-  ownctcp = "[%r$0%K(%R$1-%K)] ";
-  ctcp = "%g$0-%n";
+  ownctcp = "[$0$1-] ";
+  ctcp = "$0-";
 
   # wallops
-  wallop = "%W$0-%n: ";
-  wallop_nick = "%n$0-";
-  wallop_action = "%W * $0-%n ";
+  wallop = "$0-: ";
+  wallop_nick = "$0-";
+  wallop_action = " * $0- ";
 
   # netsplits
-  netsplit = "%R$0-%n";
+  netsplit = "%c$0-%n";
   netjoin = "%C$0-%n";
 
   # /names list
-  names_nick = "[%_$0%_$1-] ";
-  names_users = "[%g$0-%n]";
-  names_channel = "%G$0-%n";
+  names_prefix = "";
+  names_nick = "[ %n%_$0%_$1- ] ";
+  names_nick_op = "{names_nick $*}";
+  names_users = "$0-";
+  names_channel = "{channel $0-}";
 
   # DCC
-  dcc = "%g$0-%n";
+  dcc = "$0-";
   dccfile = "%_$0-%_";
 
   # DCC chat, own msg/action
-  dccownmsg = "[%r$0%K($1-%K)%n] ";
-  dccownnick = "%R$0-%n";
+  dccownmsg = "*%c=$1-%n*> %g";
   dccownaction = "{action $0-}";
-  dccownaction_target = "{action_core $0}%K:%c$1%n ";
+  dccownaction_target = "{ownaction_target $0-}";
 
   # DCC chat, others
-  dccmsg = "[%G$1-%K(%g$0%K)%n] ";
-  dccquerynick = "%G$0-%n";
-  dccaction = "%W (*dcc*) $0-%n %|";
+  dccmsg = "*%c=$1-%n* ";
+  dccquerynick = "$0-";
+  dccaction = " (*dcc*) $0- %|";
 
   ##
   ## statusbar
   ##
 
-  # background of statusbar
-  sb_background = "%4";
-
-  # default statusbar item style
-  sb = "%c[%n$0-%c]%n";
-
-  sbmode = "(%c+%n$0-)";
+  # default background for all statusbars. You can also give
+  # the default foreground color for statusbar items.
+  sb_background = "%4%w";
+
+  # default backround for "default" statusbar group
+  #sb_default_bg = "%4";
+  # background for prompt / input line
+  sb_prompt_bg = "%n";
+  # background for info statusbar
+  sb_info_bg = "%8";
+  # background for topicbar (same default)
+  #sb_topic_bg = "%4";
+
+  # text at the beginning of statusbars. sb-item already puts
+  # space there,so we don't use anything by default.
+  sbstart = "";
+  # text at the end of statusbars. Use space so that it's never
+  # used for anything.
+  sbend = " ";
+
+  prompt = "[$*] ";
+
+  sb = " %c[%n$*%c]%n";
+  sbmode = "(%c+%n$*)";
   sbaway = " (%GzZzZ%n)";
-  sbservertag = "%c:%n$0 (change with ^X)";
-  sbmore = "%_-- more --%_";
-  sblag = "{sb Lag: $0-}";
-  sbmail = "{sb Mail: $0-}";
-
-  # activity. Det is used for hilights when display doesn't support colors
-  sbact = "{sb {sbact_act $0}{sbact_det $1}}";
-  sbact_act = "Act: $0-";
-  sbact_det = " Det: $0-";
+  sbservertag = ":$0 (change with ^X)";
+
+  # activity in statusbar
+
+  # ',' separator
+  sb_act_sep = "%c$*";
+  # normal text
+  sb_act_text = "%c$*";
+  # public message
+  sb_act_msg = "%W$*";
+  # hilight
+  sb_act_hilight = "%M$*";
+  # hilight with specified color, $0 = color, $1 = text
+  sb_act_hilight_color = "$0$1-%n";
+};
+
+#
+# Some default formats how to print stuff on screen
+#
+formats = {
+  "fe-common/core" = {
+    endofnames = "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $4} normal}";
+    line_start_irssi = "{line_start}";
+  };
 };
diff --git a/apps/irssi/docs/.cvsignore b/apps/irssi/docs/.cvsignore
new file mode 100644 (file)
index 0000000..c5b9e55
--- /dev/null
@@ -0,0 +1,3 @@
+Makefile
+Makefile.in
+startup-HOWTO.txt
index 80499614ff424e95004f6381f69a5656861aa14c..e78d297c34a33dffab2d5c83473ee9b953eac9f9 100644 (file)
@@ -1,11 +1,14 @@
-docdir = $(prefix)/doc/irssi
-
-doc_DATA = \
-       formats.txt \
-       manual.txt \
-       faq.txt \
-       startup-HOWTO.html \
-       startup-HOWTO.txt
+include $(top_srcdir)/Makefile.defines.in
+
+#docdir = $(prefix)/doc/irssi
+docdir = $(silc_docdir)
+
+doc_DATA =
+#      formats.txt \
+#      manual.txt \
+#      faq.txt \
+#      startup-HOWTO.html \
+#      startup-HOWTO.txt
 
 EXTRA_DIST = $(doc_DATA)
 
diff --git a/apps/irssi/docs/botnet.txt b/apps/irssi/docs/botnet.txt
new file mode 100644 (file)
index 0000000..8b5f9ba
--- /dev/null
@@ -0,0 +1,316 @@
+
+ Irssi's botnet description
+
+ Copyright (c) 1999-2000 Timo Sirainen
+
+
+ 0. History
+
+ draft v0.1 : 21.8.1999
+
+       Just a first draft of my botnet design I did on a boring friday
+       work afternoon :) I'll try to implement this to irssi some day, it
+       feels pretty interesting now so it might be pretty soon even. Any
+       comments are welcome :)
+
+ draft v0.2 : 21.11.1999
+
+       Exactly three months since the first draft :) Now I actually have
+       some code done, just committed pretty simple botnet to irssi CVS.
+       Made several changes to this document.. Still missing much details
+       but the basic idea should be clear.
+
+ draft v0.3 : 21.05.2000
+
+       Strange, again the same day. I really didn't plan this :)
+       Reformatted the text, added lots of text, implemented more of the
+       stuff.
+
+
+ 1. General
+
+ 1.1 Description
+
+       A small description of what botnet would do: A group of bots
+       efficiently working together to perform their tasks. Like when
+       someone's trying to take over your channel, bots will quickly
+       decide who deops/kicks whom instead of multiple bots deopping or
+       trying to kick the same people.
+
+       Irssi's botnet is pretty much based on trust. Some malicious bot
+       can quite well mess up the whole botnet. Connecting the bots to
+       each other via ssh would be a good idea.
+
+ 1.2 Configuration
+
+       example config file:
+
+       mybotnet =
+       {
+         priority=5;
+         nick=mybot;
+         uplinks = (
+           { host = "main.botnet.org"; password = "mypass"; },
+           { host = "alter.botnet.org"; password = "pass2"; }
+         );
+         downlinks = (
+           { password = "thepass"; valid_addrs = ( "192.168.0.*" ); },
+           { password = "blah"; valid_addrs = ( "*.botnet.org" ); },
+           { password = "localpass"; valid_addrs = ( "127.*" ); }
+         );
+       }
+
+       When connecting to botnet, the bot first tries to connect to the
+       first bot in uplinks list, then the second, etc. Setting port to -1
+       will prevent connecting to the bot, 0 uses the default.
+
+ 1.3 Botnet master
+
+       To avoid total chaos inside botnet, the bots shouldn't do (almost)
+       anything without a command from botnet's master. The master should
+       know everything, and give commands to clients that can perform the
+       task best.
+
+       Master is the bot with the highest priority. If there's multiple
+       with the same priority, the one that already was the master will
+       stay master. When joining two botnets to one, the uplink's master
+       stays. If link to master breaks, the closest bot to it will choose
+       a new one.
+
+       The priorities should be given so that the bots that have the
+       fastest connections and are in the middle of the botnet have the
+       highest priorities.
+
+ 1.4 Command format
+
+       Commands that are sent inside botnet are in the following format:
+
+         <from_nick> <to_nick> COMMAND [command specific data..]
+
+        If to_nick is '-', the command should be sent to everyone.
+
+
+ 2. Handshake
+
+       First host checks from bots' valid_addrs who is connecting. If
+       there's no matches it just disconnects the client.
+
+         CLIENT: PASS <password>
+         HOST  : (if error, disconnect)
+
+         CLIENT: NICK <nick>
+         HOST  : NICKERROR | CONNECTED
+
+       If nick is already in use, the host sends NICKERROR and waits for
+       new nick.
+
+       Now we're connected to botnet. The commands from now on use the
+       format specified in section 1.4.
+
+       Both the client and the host sends information to other side of
+       all the clients they serve (if any):
+
+         BOTINFO <nick> <connected_to_nick> <priority>
+
+       BOTINFOs must be sent sorted so that connected_to_nick bot is
+       always known. Like first comes the bots connected to the
+       host/client, then the bots connected to them etc.
+
+       If the client had downlinks, nick collisions might happen. The
+       uplink is responsible for noticing them from BOTINFO commands.
+       It should automatically replace the nicks with new ones and
+       send nick change command to client and all it's downlinks. For
+       example if host received:
+
+         BOTINFO bot highbot 10
+
+       And the bot already exists, the BOTINFO is changed to:
+
+         BOTINFO bot2 highbot 10
+
+       And the client and it's downlinks are notified:
+
+         BOTNICK bot2 bot
+
+       After sending BOTINFOs, the host tells the current master:
+
+         MASTER <nick>
+
+       The client now checks if it's priority is higher than the current
+       master's. If it is, it will send the MASTER command without any
+       parameters.
+
+
+ 3. Bot connections
+
+ 3.1 General
+
+       Everyone's connections should be kept in n-way tree. Example:
+
+
+                              [highuplink]
+                 _____________/    |  |   \
+                /                  | [h5] [h6]
+              [h1]                 |     /  | \
+             /    \                |   [h7] | [h8]
+           [h2]   [h3]             |        |    \
+                   |            [uplink]   [h9] [h10]
+                  [h4]         /   |    \
+                            [up2]  |   [up1]
+                           /   |   |     |
+                       [up3] [up4] |    [up5]
+                                   |
+                                  [we]
+                                 /    \
+                         [client1]     [client2]
+                                        /     \
+                                      [c3]    [c4]
+
+
+       Botnet should try to keep together even if some hub in the middle
+       crashes. Each bot should have at least two uplinks in case one
+       dies. For example if [uplink] dies, [we] and [up1] could connect
+       to [up2], and [up2] could connect to [highuplink].
+
+       When connection is closed to some bot, a notice is sent by the
+       bot's uplink:
+
+         BOTQUIT <nick>
+
+       The quit notice should be sent only about the bot that was
+       disconnected. Bots should figure out themselves the other bots and
+       remove them too from their lists.
+
+ 3.2 Lag
+
+       Each bot should send PING commands to their up/downlinks every
+       now and then (1min?) to check that the connection is still active.
+       If the PONG isn't received in 10 seconds, it's priority should be
+       temporarily lowered to -1. If the PONG isn't received in 3
+       minutes, the whole connection should be closed.
+
+       Master should know lag times of every bots. It could then
+       automatically raise/lower bots' priorities depending on how big
+       their lag is. Master could also lower it's own priority and pass
+       the master status to someone else with lower lag.
+
+       If there's lot of lag (>3sec?) somewhere and something urgent
+       happens, the botnet could split and behave independently.
+
+
+ 4. IRC networks
+
+ 4.1 Server connections
+
+       When bot is connected to some irc server and is ready to take
+       commands, it says:
+
+         IRCJOIN <tag> <ircnet> <server> <nick>
+
+       Tag is the bot specific unique tag of the server, so that the bot
+       can connect multiple times to same IRC network. All IRC related
+       commands should specify the server tag where it should be sent.
+
+       If bot quits an irc network, it says:
+
+         IRCQUIT <tag>
+
+ 4.2 IRC commands
+
+       Master asks a bot to send some command to IRC server by saying:
+
+         CMD <id> <tag> <command>
+
+       <command> can't really be anything, since the bot should also be
+       able to reply to it. The <id> is for identifying the command/reply
+       pair. Master should keep the command in memory until it receives
+       the reply:
+
+         CMDREPLY <id> <last> <reply>
+
+       The command could get a reply of multiple lines, so <last>
+       specifies if the reply is the last line (1 or 0).
+
+       If the command failed for some reason, the bot will reply with
+
+         CMDFAIL <id>
+
+       and master should send the command to some other bot.
+
+ 4.3 Channels
+
+       When joined/left channels, the bot says:
+
+         CHANJOIN <tag> <channel>
+         CHANPART <tag> <channel>
+
+       After BOTJOIN, master tries to op the bot. When bot receives +o,
+       it says:
+
+         CHANOP <tag> <channel>
+
+       If it is the first opped bot in channel, master orders the bot to
+       op the rest of the bots.
+
+       If the bot is kicked, it says:
+
+         CHANKICK <tag> <channel>
+
+       When master notices that bot is kicked, it first checks if there's
+       any other opped bots in channel. If not, it waits for a random
+       pause, 5-10sec before letting the bot join the channel again so
+       that it won't get autorejoin ban.
+
+       If bot can't join channel, it says:
+
+         CHANBANNED <tag> <channel>
+         (or)
+         CHANCANTJOIN <tag> <channel>
+
+       When received BOTBANNED, master tries to unban bot or set a ban
+       exception. BOTCANTJOIN results as invite to channel.
+
+ 4.4 Channel information
+
+       When master notices that bot is the first one joined to channel,
+       it asks the bot for some channel information:
+
+         CMD <id> <tag> NAMES <channel>
+         CMD <id> <tag> WHO <channel>
+         CMD <id> <tag> MODE <channel>
+         CMD <id> <tag> MODE b <channel>
+         CMD <id> <tag> MODE e <channel> (if IRC network supports this)
+         CMD <id> <tag> MODE I <channel> (if IRC network supports this)
+
+       It's also possible that if several bots join immediately after the
+       first bot, the commands are shared between all the bots.
+
+       Bots should cache the information as much as possible, at least
+       NAMES command.
+
+ 4.5 Channel priorities
+
+       Every channel has a priority: LOW, NORMAL, HIGH.
+
+       Normally LOW operates just as NORMAL channels, except when some
+       channel has HIGH priority and bots are really busy, LOW channels
+       just wait until there's time for them.
+
+       In NORMAL channels, the most urgent operations (kicks, ops, deops)
+       are performed quite soon even while bots are busy handling HIGH
+       priority commands.
+
+       Channels shouldn't normally be HIGH priority, but if attack
+       against channel is detected (like someone comes from split, gets
+       ops and gets to op someone else), it's priority is set to HIGH.
+       When channel's priority is HIGH, botnet does everything it can to
+       get rid of unauthorized opped people as fast as possible.
+
+       LOW channel's priority can also be raised to HIGH, but it's
+       priority is dropped back to LOW if some NORMAL channel's priority
+       is raised to HIGH too.
+
+       Master notifies about channel's priority change by saying:
+
+         CHANPRIORITY <ircnet> <channel> <LOW/NORMAL/HIGH>
+
diff --git a/apps/irssi/docs/crash.txt b/apps/irssi/docs/crash.txt
new file mode 100644 (file)
index 0000000..d3100f5
--- /dev/null
@@ -0,0 +1,59 @@
+How to submit a good bug report?
+
+First you should give the following information:
+ - irssi version, if CVS (or devel. tarball) then which day?
+ - operating system / distribution and it's version
+ - when did it crash? did you do something? can you reproduce the crash?
+
+Getting backtrace of the crash also helps a lot, especially if irssi crashes
+randomly. If after crash you see text:
+
+ "segmentation fault (core dumped)"
+
+It writes a file named "core" or "irssi.core" depending on your OS to
+directory where you started irssi. If it doesn't print the "(core dumped)"
+or you can't find the core file, you'll have to raise the limit for max.
+core file size before running irssi. To do this, say:
+
+ ulimit -c unlimited
+
+So, if you have the core file and GNU debugger (gdb), you can get the
+backtrace with:
+
+ gdb irssi core
+ bt
+
+Paste all the lines starting from line having #0 at the beginning.
+
+Here's an example session:
+
+[cras@hurina] ~/cvs/m/irssi/src/fe-text$ gdb ./irssi core
+
+GNU gdb 5.0
+Copyright 2000 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB.  Type "show warranty" for details.
+This GDB was configured as "i686-pc-linux-gnu"...
+
+Core was generated by ./irssi'.
+Program terminated with signal 11, Segmentation fault.
+#0  0x805e949 in view_scroll (view=0x816cfb5, lines=0x816cfd9, 
+    subline=0x816cfdd, scrollcount=-11, draw_nonclean=1)
+    at textbuffer-view.c:528
+528                             realcount += view->bottom_subline;
+
+(gdb) bt
+
+#0  0x805e949 in view_scroll (view=0x816cfb5, lines=0x816cfd9, 
+    subline=0x816cfdd, scrollcount=-11, draw_nonclean=1)
+    at textbuffer-view.c:528
+#1  0x805ecb4 in textbuffer_view_scroll (view=0x816cfb5, lines=-11)
+    at textbuffer-view.c:669
+#2  0x8058387 in gui_window_scroll (window=0x816cead, lines=-11)
+    at gui-windows.c:128
+#3  0x8056b64 in window_prev_page () at gui-readline.c:109
+#4  0x8057047 in key_scroll_backward () at gui-readline.c:334
+...
+(gdb) 
diff --git a/apps/irssi/docs/design.txt b/apps/irssi/docs/design.txt
new file mode 100644 (file)
index 0000000..5c8ddcf
--- /dev/null
@@ -0,0 +1,156 @@
+
+ Irssi's hierarchy is something like this:
+
+
+         sub1 sub2
+            \ /
+       xxx  IRC       COMMON ICQ  yyy
+        |____|___________|____|____|
+                   |
+                  GUI (gtk/gnome, qt/kde, text, none)
+                   |
+         sub1 sub2 |
+            \ /    |
+       xxx  IRC    |  COMMON ICQ  yyy
+        |____|_____|_____|____|____|
+                   |
+               COMMON UI
+                   |
+         sub1 sub2 |
+            \ /    |
+       xxx  IRC    |    ICQ  yyy
+        |____|_____|_____|____|
+                   |
+                 CORE
+                 /  \
+        lib-config  lib-popt
+
+
+ (IRC, ICQ, xxx and yyy are chat protocols ..)
+ (sub1 and sub2 are submodules of IRC module, like DCC and flood protect)
+
+
+ Chat protocols and frontends are kept in separate modules. Common UI
+ and GUI modules also have the common parts which don't know anything
+ about the chat protocols. This should allow implementing modules to
+ whatever chat protocols and with whatever frontends easily.
+
+ ** Signals
+
+ Communication between different modules are done with "signals". They are
+ not related to UNIX signals in any way, you could more like think of them
+ as "events" - which might be a better name for them, but I don't really
+ want to change it anymore :)
+
+ So, you send signal with signal_emit() and it's sent to all modules that
+ have grabbed it by calling signal_add() in their init function. For
+ example:
+
+   signal_emit("mysignal", 1, "hello");
+
+ Sends a "mysignal" function with one argument "hello" - before that, you
+ should have grabbed the signal somewhere else with:
+
+   static void sig_mysignal(const char *arg1)
+   {
+     /* arg1 contains "hello" */
+   }
+
+   signal_add("mysignal", (SIGNAL_FUNC) sig_mysignal);
+
+ There are three different signal_add() functions which you can use to
+ specify if you want to grab the signal first, "normally" or last. You can
+ also stop the signal from going any further.
+
+ Emitting signal with it's name creates a small overhead since it has to
+ look up the signal's numeric ID first, after which it looks up the signal
+ structure. This is done because if you call a signal _really_ often,
+ it's faster to find it with it's numeric ID instead of the string. You
+ can use signal_get_uniq_id() macro to convert the signal name into ID -
+ you'll have to do this only once! - and use signal_emit_id() to emit the
+ signal. Don't bother to do this unless your signal is sent (or could be
+ sent) several times in a second.
+
+ See src/core/signals.h for defination of the signal function, and
+ signals.txt for a list of signals.
+
+
+ ** lib-popt
+
+   CORE depends on this for command line parameter handling.
+   (distributed with irssi)
+
+
+ ** lib-config
+
+   Irssi depends on this for reading and saving configuration.
+   (created by me for irssi)
+
+
+ ** CORE module
+
+ Provides some functionality that all other modules can use:
+   - signal handling
+   - keeping list of settings
+   - keeping list of /commands
+   - keeping track of loaded modules
+   - networking functions (with nonblocking connects, IPv6 support)
+   - handles connecting to servers
+   - raw logging of server's input/output data
+   - /EVAL support
+   - fgets() like function line_split() without any maximum line limits
+   - command line parameter handling
+   - miscellaneous useful little functions
+   - handles logging
+
+
+ ** COMMON UI module
+
+   - knows basics about windows and window items (=channels, queries, ..)
+   - printtext() - parsing texts and feeding it for GUI to print.
+   - themes
+   - translation tables
+   - text hilighting
+   - command history
+   - user interface (/commands) for CORE's functionality
+
+
+ ** GUI modules
+
+   - all the rest of the functionality needed for a working client.
+
+
+ ** IRC module
+
+   * CORE
+
+     - IRC specific /commands
+     - flood protecting commands sent to server
+     - creating IRC masks based on nick/address for bans, ignores, etc.
+     - keeps list of channels, nicks, channel modes, bans, etc.
+     - keeps list of servers, server settings, irc networks,
+       server reconnections and irc network splits
+     - redirection of commands' replies
+     - lag detection
+     - ctcp support and flood protection
+     - Handles ignoring people
+
+   * DCC
+
+     - DCC chat, send and get
+
+   * FLOOD
+
+     - detects private or channel flooding and sends "flood" signal
+     - automatic ignoring when flooding
+
+   * NOTIFYLIST
+
+     - handles notifylist
+
+
+ ** IRC UI module
+
+   - placing channels and queries in windows
+   - nick completion
+   - printing infomation of some events
index 6dabc43c381e23a5eae0c472840b62aa78cfe950..e98f2719e7feb0a1a2297939a6195cb8e84fe18d 100644 (file)
 
                foreground (fg)     background (bg)
    -------------------------------------------------------
-    0          white               black       with 0 as fg
-                                  light gray   with fg > 0
-    1          ligh gray           black
+    0          white               light gray   + blinking fg
+    1          black               black
     2          blue                blue
     3          green               green
-    4          light red           red         + blinking fg
-    5          orange              orange
+    4          light red           red          + blinking fg
+    5          red                 red
     6          magenta (purple)    magenta
-    7          red                 red
-    8          yellow              orange      + blinking fg
-    9          light green         light green + blinking fg
-    10         cyan                cyan                
-    11         light cyan          cyan                + blinking fg
-    12         light blue          blue                + blinking fg
-    13         light magenta       magenta     + blinking fg
-    14         gray                black       + blinking fg 
-    15         light gray          black on black
-                                   gray  on light gray (with bold)
+    7          orange              orange
+    8          yellow              orange       + blinking fg
+    9          light green         green        + blinking fg
+    10         cyan                cyan         
+    11         light cyan          cyan         + blinking fg
+    12         light blue          blue         + blinking fg
+    13         light magenta       magenta      + blinking fg
+    14         gray                black        + blinking fg 
+    15         light gray          light gray
 
- These colors may differ depending on your terminal.
+ These colors may differ depending on your terminal. In particular
+ the meaning for background may be the same as for the foreground
+ (bright colors, no blinking), and orange often looks like brown or
+ dark yellow.
 
  How to use these colors ('#' means a number as MIRC color code):
  
index abf824b4a25c9672e599913e4d969352ea20cf83..c836962bb38cc2dbdd2498c7e8dd34f2228d2f63 100644 (file)
@@ -1,6 +1,8 @@
 # Makefile.am is autogenerated by autogen.sh from Makefile.am.gen
 
-helpdir = $(datadir)/irssi/help
+include $(top_srcdir)/Makefile.defines.in
+
+helpdir = $(silc_helpdir)
 
 help_DATA = \
 @HELPFILES@
index 9b0f2aaa10479b3f20f4fad0797a302dda1d8300..7bf5760b01c3d499460f362b8677a8c5ebdd1e83 100644 (file)
@@ -1,5 +1,4 @@
 # Makefile.am is autogenerated by autogen.sh from Makefile.am.gen
 
 EXTRA_DIST = \
-       Makefile.am.gen \
-@HELPFILES@
+       Makefile.am.gen
index 896e8d19b1c0214c05addd8486c788256925d23d..89123fccd57b2f4d8b59499c67eb4f66fc2db426 100644 (file)
@@ -1,7 +1,7 @@
 
 @SYNTAX:action@
 
-Same as ME, but gets channel or nick as an additional parameter.
+Same as ME, but gets channel as an additional parameter.
 Example: /ACTION #irssi yawns
 (This outputs the following to #irssi: * Nick yawns)
 
index b5d95254118e8d4ec21a4d0ce9243abb89e6281d..f854cd0f4f53d4e7c5b7a93b9fd83ddfdfab3a5b 100644 (file)
@@ -3,6 +3,9 @@
 
 Displays the administrative details about the given server. If
 no server is specified, the server you are connected to is
-used. If a nickname is supplied then it gives the administrative 
-information for that person's current server.
+used.
+
+This command may be an alias.
+
+See also: INFO
 
index 8283d63dbfe77f2df89c64b526a91292604b2ff1..c83b275deab7f5e1c3c866489bab5bbf62b65e3c 100644 (file)
@@ -1,22 +1,17 @@
 
 @SYNTAX:away@
 
-   -one 
-   -all
-
 This command marks you as being "away". It is used to tell people that 
 you currently aren't paying attention to your screen. You might use it 
 if you are taking a nap, in the shower, getting some food, or otherwise 
-just aren't there at the moment. When you're "away" you will see "(zZzZ)"
-in your statusbar.
-
-Anyone who does a WHOIS on your nickname will see that you are away, 
-as well as your away message. Anyone doing a WHO that returns information 
-about you will also see that you're gone. 
+just aren't there at the moment.
 
 By default, if someone sends you a MSG while you are away, your client 
 will beep. You can turn this off by setting BEEP_WHEN_AWAY to OFF. 
 
+If someone sends you a message when you're away the set away message
+will be automatically sent back to that person.
+
 If you send a MSG to someone who is away, you will automatically be 
 notified of this. By default, you will only receive this notification 
 once. If you wish to see it every time (to tell when a person is no 
index 4f8a541d7f0728b0924b9d674aff84aa61bb5ac6..32bf76e931790afbdc51170562cc57e14b391473 100644 (file)
@@ -1,24 +1,28 @@
 
 @SYNTAX:ban@
 
-Bans the specified nick or userhost mask.
+This command is used to manage the ban list of the channel.
+You must be channel operator to be able to use this command.
+Wildcards may be used with this command.
 
-If nick is given as parameter, the ban type is used to generate the ban
-mask. /SET ban_type specified the default ban type. Ban type is one of
-the following:
+Examples:
+    /BAN #mychannel +foobar!mr.bar@foo.bar.com
+        Adds nickname `foobar' with username `mr.bar' from host
+        `foo.bar.com' on #mychannel to the ban list.
 
-    Normal - *!user@*.domain.net
-    Host   - *!*@host.domain.net
-    Domain - *!*@*.domain.net
-    Custom [nick] [user] [host] [domain]
+    /BAN * +looser
+        Adds nickname `looser' to the ban list on current channel.
 
-Examples:
-    /BAN looser    - This bans the nick 'looser'
-    /BAN *!*@*.org - This bans all the users coming from any 
-                     .org domain.
+    /BAN * +foo*@*!@*.foobar.com
+        Adds foo* nicknames from any server with any username from
+        *.foobar.com hosts to the ban list on current channel.
+
+    /BAN * -looser
+        Removes the nickname `looser' from the ban list on current
+        channel.
 
-    /SET ban_type custom nick domain - nick!*@*.domain.net
-    /SET ban_type custom user host   - *!user@host.domain.net
+    /BAN *
+        Shows the ban list of the current channel.
 
 See also: KNOCKOUT, KICKBAN
 
index 7484ac15a38a538fb8aaa679912f48a2a2e63bce..b11f3645c6a93076f0ee0b6ddcee73691de97768 100644 (file)
@@ -2,7 +2,7 @@
 @SYNTAX:channel@
 
 Irssi can automatically join to specified channels in specified
-IRC networks. It can also automatically send the password when
+networks. It can also automatically send the password when
 manually joining to channel without specifying the password.
 
 /CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
index b3db54ef720a2fa33af2de5d84bcba6d875a0932..5c7e73bec04ce49ac22d9fc92094dc6493b8770e 100644 (file)
@@ -4,6 +4,5 @@
 This command clears the current window of all text. It is useful 
 for wiping a screen that has rendered improperly (such as due 
 to a bad termcap entry) or that contains sensitive information 
-(such as one's OPER password). 
 
 
diff --git a/apps/irssi/docs/help/in/close.in b/apps/irssi/docs/help/in/close.in
new file mode 100644 (file)
index 0000000..7ce6dff
--- /dev/null
@@ -0,0 +1,8 @@
+
+@SYNTAX:close@
+
+Operator command.  Makes the server to close connection to another server
+or router.
+
+See also: OPER, SILCOPER
+
diff --git a/apps/irssi/docs/help/in/cmode.in b/apps/irssi/docs/help/in/cmode.in
new file mode 100644 (file)
index 0000000..4370809
--- /dev/null
@@ -0,0 +1,35 @@
+
+@SYNTAX:cmode@
+
+This command is used to manage the modes of the channel.  Most
+of the modes require special privileges, such as channel operator
+or channel founder privileges to work.  The mode is added by
+adding + before the option(s) and removed by adding - before the
+option(s).  The following modes are available:
+
+    p               Set/unset channel as private channel
+    s               Set/unset channel as secret channel
+    k               Set/unset that channel uses private channel key
+    i               Set/unset channel as invite only channel
+    t               Set/unset that only channel operator or 
+                    founder may set channel topic
+    l <limit>       Set/unset channel's user limit
+    a <passphrase>  Set/unset passphrase for channel that must
+                    be provided when joining to the channel.
+    c <cipher>      Set/unset channel's cipher
+    h <hmac>        Set/unset channel's hmac
+    f <-pubkey|<password>
+                    Set/unset channel founder authentication.
+                    Channel founder may set this mode so that
+                    if the client leaves the channel it can
+                    claim the founder rights when it returns
+                    to the channel.  If -pubkey is set then
+                    the authentication will be done using the
+                    client's public key.  You can claim the
+                    founder rights using the CUMODE command.
+
+Multiple modes can be set/unset at once if the modes does not
+require any arguments.  If mode requires an argument then only
+one mode can be set at once.
+
+See also: CUMODE, UMODE
index f4671ab49ce093ef62e51b7d8a1c8e486b301b5c..98c91680a6bb591ab07de650db2b8d7a55ebd880 100644 (file)
@@ -2,7 +2,7 @@
 @SYNTAX:connect@
 
      -4, -6: specify explicitly whether to use IPv4 or IPv6 address
-     -ircnet: the IRCNet
+     -silcnet: the specified network
      -host: the host
 
 This command makes irssi to connect to specified server.
diff --git a/apps/irssi/docs/help/in/ctcp.in b/apps/irssi/docs/help/in/ctcp.in
deleted file mode 100644 (file)
index 77c78b7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:ctcp@
-
-Sends a CTCP-message. For example CTCP ACTION, or CTCP VERSION.
-
-See also: ME, ACTION
-
diff --git a/apps/irssi/docs/help/in/cumode.in b/apps/irssi/docs/help/in/cumode.in
new file mode 100644 (file)
index 0000000..9bba22f
--- /dev/null
@@ -0,0 +1,34 @@
+
+@SYNTAX:cumode@
+
+This command is used to manage the client's modes on the channel.
+Most of the modes require that the client which changes some
+client's mode must be channel founder or channel operator.  The
+mode is added by adding + before the option(s) and removed by
+adding - before the option(s).  The following channel user modes
+are available:
+
+    a <nickname>[@<server>]
+
+        Set/unset all modes (cannot be used to set
+        both founder and operator rights, can be used
+        only to remove both modes at once).
+
+    f <nickname>[@<server>] [-pubkey|<password>]
+
+        Set/Unset channel founder.  If the -pubkey
+        option or <password> is provided then the
+        client is claiming the founder rights by
+        providing the channel founder authentication
+        data.  If the -pubkey is provided then the
+        authentication is performed using the
+        client's public key.  If you are channel
+        founder you can set the channel founder
+        authentication using CMODE command.
+
+    o <nickname>[@<server>]
+
+        Set/unset channel operator.  Requires that 
+        you are channel operator or channel founder.
+
+See also: CMODE, UMODE
index 5b2ca4b942ed7df47102f8988ec5844ad2e4f23a..8570771974df6ecae8bddfb0d6569022d24318c4 100644 (file)
@@ -9,3 +9,4 @@ If a nickname is given, that client's server is queried.
 
 Same as /TIME.
 
+NOTE: This command has no effect on SILC.
\ No newline at end of file
diff --git a/apps/irssi/docs/help/in/dcc.in b/apps/irssi/docs/help/in/dcc.in
deleted file mode 100644 (file)
index eb9caa3..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-
-@SYNTAX:dcc@
-
-This is a command to handle different DCC-connections. DCC is mainly
-used for more reliable and faster chatting and for sending and receiving 
-files.
-
-/DCC LIST
-    - Shows all the open DCC connections.
-/DCC RESUME [<nick> [<file>]]
-    - Resumes a DCC SEND/GET connection.
-/DCC CHAT [<nick>]
-    - Sends a chat connection request to remote client or accepts 
-      a chat connection if the remote end has already sent a request.
-/DCC GET [<nick> [<file>]]
-    - Gets the file offered by remote client. The file is downloaded and
-      saved into the current working directory.
-/DCC SEND <nick> <file>
-    - Sends a DCC SEND request to remote client. Remote end has to accept
-      the request before the transmission can be started.
-/DCC CLOSE <type> <nick> [<file>]
-    - Closes a DCC-connection. Type can be either SEND, GET or CHAT.
-
-See also: CD
-
diff --git a/apps/irssi/docs/help/in/deop.in b/apps/irssi/docs/help/in/deop.in
deleted file mode 100644 (file)
index e9893e6..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-@SYNTAX:deop@
-
-Takes off the channel operator privileges from the
-specified nick(s). 
-
-Wildcards in the nick are allowed.
-
-See also: OP
-
diff --git a/apps/irssi/docs/help/in/devoice.in b/apps/irssi/docs/help/in/devoice.in
deleted file mode 100644 (file)
index ce09019..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-@SYNTAX:devoice@
-
-Takes off the voice from the specified nick(s). This makes them
-not to be able to send messages to the moderated (+m) channel.
-
-Wildcards in the nick are allowed.
-
-See also: VOICE, MODE
-
diff --git a/apps/irssi/docs/help/in/die.in b/apps/irssi/docs/help/in/die.in
deleted file mode 100644 (file)
index 4ef6da0..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:die@
-
-IRC-operator command. Makes IRC-server to die.
-
-See also: OPER
-
index cfd221d526df449aefc0004f3587b81100af16c6..b7f8c8709fbc3f5b22a0ab2ff7059b309354fb02 100644 (file)
@@ -1,9 +1,7 @@
 
 @SYNTAX:disconnect@
 
-Disconnects from the specified IRC-server.
-The server tags can be seen with:
-/SERVER LIST
+Disconnects from the specified server.
 
 See also: CONNECT, SERVER
 
diff --git a/apps/irssi/docs/help/in/file.in b/apps/irssi/docs/help/in/file.in
new file mode 100644 (file)
index 0000000..4e85a92
--- /dev/null
@@ -0,0 +1,43 @@
+
+@SYNTAX:file@
+
+This command is used to tranfer files between clients.
+The actual file transfer stream is sent outside SILC network
+peer to peer between the clients.  Before the file transfer
+begins the SILC Key Exchange protocol is performed between
+the two clients to exchange key material.  This key material
+is then used to secure the file transfer stream between the
+clients.
+
+The currently active file transfer sessions can be seen by
+giving the FILE command without arguments.
+
+Commands:
+
+    SEND     <filepath> <nickname> [<local IP> [<local port>]]
+
+      Sends file transfer request to <nickname>.  This
+      makes the <filepath> available to <nickname>.
+
+      If the <local IP> is provided then the key exchange
+      protocol listener will be bound to that address.  If
+      <local port> is defined it is bound to that port. 
+      If they are not defined then the local IP address
+      of your machine is used to bind the listener.  If that
+      fails then the <nickname> is assumed to provide the
+      listener.  If you do not know whether you need to
+      provide <local IP> or not, do not provide it.
+
+    RECEIVE  [<nickname>]
+
+      Accepts the file transfer request and starts
+      the file transfer session.  If the <nickname> is
+      omitted the last received request is used.
+
+    CLOSE    [<nickname>]
+
+      Closes the file transfer session, or rejects
+      file transfer request.  If this command is given
+      during the file transfer process it will be cancelled.
+
+
diff --git a/apps/irssi/docs/help/in/getkey.in b/apps/irssi/docs/help/in/getkey.in
new file mode 100644 (file)
index 0000000..bb1c4c5
--- /dev/null
@@ -0,0 +1,10 @@
+
+@SYNTAX:getkey@
+
+This command is used to fetch remote client's or server's public
+key.  When fetching client's public key it is fetched from the
+server the client is connected to.  The server has verified that the
+client posesses the corresponding private key as well.  You will be
+prompted to verify and accept the fetched public key.  The public key
+is saved into your local key directory (~/.silc/clientkeys/).
+
diff --git a/apps/irssi/docs/help/in/hash.in b/apps/irssi/docs/help/in/hash.in
deleted file mode 100644 (file)
index 62a889f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-
-@SYNTAX:hash@
-
-Not available.
-
index e9aad17d28fcc2e6238241324672ddea4a9e6e15..12e0b2f39fa5d58d4a5bbc9cf68a30394ee74a58 100644 (file)
@@ -1,6 +1,7 @@
 
 @SYNTAX:info@
 
-Shows information about the IRC creators, debuggers, slaves and 
-a lot of other people who no longer have much to do with irc.
+Displays the administrative details about the given server. If
+no server is specified, the server you are connected to is
+used.
 
index 1e7f69b11fdeddb6eef07b41ba13a112b485d532..b05fc42d9ef4612ca5662accaff65706333b577f 100644 (file)
@@ -1,10 +1,24 @@
  
 @SYNTAX:invite@
 
-Invites the specified nick to the current or specified channel.
+This command is used to invite an client to a channel and to manage
+the channel's invite list.  Wildcards may be used with this command.
 
-Example:
-   /INVITE buddy #mychannel
+Examples:
+    /INVITE #silc joe
+        Invites nickname `joe' to channel #silc.
 
-See also: MODE
+    /INVITE #silc +joe!*@*
+        Adds nickname `joe' from anywhere to the invite list of the
+        channel #silc
+
+    /INVITE * +foo*@silcnet.org!*@*.foobar.com
+        Adds nicknames foo* from silcnet.org server from *.foobar.com
+        hosts to the invite list of the current channel.
+
+    /INVITE * -joe
+        Removes nickname `joe' from the invite list of the current
+        channel.
+
+See also: CMODE
 
diff --git a/apps/irssi/docs/help/in/ircnet.in b/apps/irssi/docs/help/in/ircnet.in
deleted file mode 100644 (file)
index 51a123f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-
-@SYNTAX:ircnet@
-
-     -kicks: Maximum number of nicks in one /KICK command
-     -msgs: Maximum number of nicks in one /MSG command
-     -modes: Maximum number of mode changes in one /MODE command
-     -whois: Maximum number of nicks in one /WHOIS command
-     -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1   
-     -cmdmax: Same as /SET cmd_max_at_once, see section 3.1
-     -nick, -user, -realname: Specify what nick/user/name to use
-     -host: Specify what host name to use, if you have multiple
-     -autosendcmd: Command to send after connecting to a server   
-        
-With -autosendcmd argument you can automatically run any commands
-after connecting to ircnet. This is useful for automatically
-identifying yourself to NickServ, for example
-
-Shows and changes the settings of defined IRC networks.
-
-See also: CONNECT
-
diff --git a/apps/irssi/docs/help/in/ison.in b/apps/irssi/docs/help/in/ison.in
deleted file mode 100644 (file)
index b97ff5d..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:ison@
-
-Tells whether specified nicks are online.
-
-See also: WHOIS, WHOWAS, NOTIFY
-
index bdd4b15c48deef4d6359f7d8d60b294a9b84bfef..bd95134542b70bd610b752ff9ca680a07b0dd1c1 100644 (file)
@@ -2,12 +2,23 @@
 @SYNTAX:join@
 
 Joins a specified channel. Channel names usually begin with #-sign,
-which may be omitted here.
+but note that the #-sign is not mandatory in channel names.
 
-JOIN is aliased to J by default. Example: /j irssi
-(This joins to the channel #irssi)
+If -cipher is provided and the channel does not exist the cipher to
+secure the channel messages on the channel will be set to <cipher>.  If
+the -hmac is provided and the channel does not exist the hmac to
+secure the channel messages on the channel will be set to <hmac>.
+
+If -founder is provided, and the channel's mode includes founder mode
+it is possible to gain channel founder privileges at the same time
+joining the channel.  If the channel has user limit, active bans,
+or is invite-only channel the founder can override these conditions
+and join the channel.  Only the client who set the founder mode on the
+channel is able to use -founder option.
+
+JOIN is aliased to J by default.
 
 Description
 
-See also: LEAVE, WINDOW CLOSE
+See also: LEAVE, WINDOW CLOSE, CMODE, CUMODE
 
diff --git a/apps/irssi/docs/help/in/key.in b/apps/irssi/docs/help/in/key.in
new file mode 100644 (file)
index 0000000..9e54b7b
--- /dev/null
@@ -0,0 +1,84 @@
+
+@SYNTAX:key@
+
+This command is used to set and unset private keys for
+channels, set and unset private keys for private messages
+with remote clients and to send key agreement requests and
+negotiate the key agreement protocol with remote client.
+The key agreement is supported only to negotiate private
+message keys, it currently cannot be used to negotiate
+private keys for channels, as it is not convenient for that
+purpose.
+
+Types:
+
+  MSG        The command is performed for private messages
+             affecting the <nickname>.
+
+  CHANNEL    The command is performed for channel affecting
+             the <channel>.
+
+Commands:
+
+  set        [<key> [<cipher>] [<hmac>]]
+
+    Set the key into use.  If the <key> is provided it is used
+    as the key material.  If the <key> is not provided the
+    negotiated key material is used.  If the negotiation has not
+    been performed this command has no effect.
+
+    If the type is `msg' and the <key> is `*' then random key
+    will be generated automatically.  The <cipher> may be set
+    for both private message and channel private keys and the
+    <hmac> may be set only to the channel private keys.
+
+  unset      [<number>]
+
+    Unset the key.  The private key is not used after this
+    command.  The key must be set again or the key material must
+    be re-negotiated to be able to use the private keys again.
+    The channel may have several private keys set.  The <number>
+    can be used to indicate what key is being unset.  If it is
+    not provided all keys are removed.
+
+  list
+
+    List all private keys that has been set.  If the type is
+    `msg' and the <nickname> is Â´*' then all private message
+    keys that you've set will be listed.
+
+  agreement  [<hostname> [<port>]]
+
+    Send key agreement request to remote client.  If the
+    <hostname> is provided it is sent in the request. The
+    receiver may use the hostname to start the key agreement.
+    If the <port> is also provided your key agreement protocol
+    server is bound to that port.  Note that it cannot be
+    privileged port (<1024).  If the <hostname> and <port> is
+    not provided then the receiver will never initiate the key
+    agreement.  In this case you may start the key agreement
+    after receiving the reply to the request, by giving the
+    negotiate command.
+
+    This command may be used to send reply to the remote client.
+    When receiving empty key agreement you can reply to the
+    sender with the hostname and port of your key agreement
+    server with this command.
+
+    If the hostname and port are ommitted, the irssi boolean
+    variable use_auto_addr will be examined.  If it is set
+    the value of auto_bind_ip will be used as the IP address
+    to listen for the return reply, the value of auto_public_ip
+    will be the IP address sent to the remote client, and the
+    auto_bind_port will be the port value to be bound to and
+    sent to the remote client.  If auto_public_ip is unset, but
+    auto_bind_ip is set, irssi will send the auto_bind_ip
+    variable's value to the remote client.
+
+  negotiate  [<hostname> [<port>]]
+
+    This may be called to start the key agreement with <nickname>.
+    This command has effect only if the <nickname> has replied to
+    your key agreement request.  You will see a notify on the
+    screen when the reply arrives.  The <hostname> and <port> is the
+    hostname and port of the remote client's key agreement server.
index 304c4d3145fa5e4199651b7ada90edd322786c14..3e73dbe247cc5ec07a72dd3e56ac3486dccd7f30 100644 (file)
@@ -1,14 +1,10 @@
 
 @SYNTAX:kick@
 
-This command "kicks" the specified user off of the specified 
-channel. It is typically used to remove troublemakers, flooders, 
-or people otherwise making a nuisanse of themselves on the channel. 
-The reason for the kick is recommended, but not required by the IRC 
-servers
-
-If the <channel> is omitted, removes the nick from the current
-channel.
+This command kicks client from channel.  You have to be
+at least channel operator to be able to kick client from
+channel.  Note: you cannot kick channel founder even if
+you are channel operator.
 
 The default alias for /KICK is /K.
 
index 338871d9b2600740f3b4f97826555c2ac5963efc..8b3b91cd1701634f67f9ae9d358198d29eb5cedb 100644 (file)
@@ -1,18 +1,11 @@
 
 @SYNTAX:kill@
 
-IRC operator command.
+This is operator command. KILL is used to forcibly remove
+a client from the network. It works similarly to KICK expect
+that the client is removed from the entire network.  In general,
+KILL is useful only as a warning tool for abusive users and
+it has only temporary effects.
 
-KILL is used to forcibly remote a client from the irc network. 
-It works similarly to KICK, except that a reason must be 
-given (even if it is meaningless or flat-out wrong). 
-
-In general, KILL is useful only as a warning tool for abusive 
-users. Modern irc clients (this one included) have automated 
-means for reconnecting to a server after a disconnection (whether 
-due to a KILL or something else), so KILL is by no means a 
-permanent solution. It is not intended as a means for personal 
-vendettas; this practice is generally frowned upon. 
-
-See also: OPER
+See also: OPER, SILCOPER
 
index d5e814382ef6924010df707ce933ddf5a500f8f0..d0cde15eb12d20d7b0ae212594f255bb0e3ade17 100644 (file)
@@ -2,9 +2,9 @@
 @SYNTAX:layout@
 
 Saves the current window layout to configuration (yes, you'll still
-need to use /SAVE to save the configuration to file). Next time you run
-irssi, all the channels and queries are exactly in the same windows
-where they were when you called /LAYOUT SAVE.
+need to use /SAVE to save the configuration to file). Next time you
+run irssi, all the channels and queries are exactly in the same
+windows where they were when you called /LAYOUT SAVE.
 
 Channels aren't actually joined in those windows immediately, they're
 just marked "next time you join to '#channel' in server that has tag
diff --git a/apps/irssi/docs/help/in/links.in b/apps/irssi/docs/help/in/links.in
deleted file mode 100644 (file)
index 726fcb6..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:links@
-
-Shows the links between the IRC servers of the
-current IRC network. If a wildcard parameter is
-specified, shows only the matching entries.
-
-See also:
-
index 134c7623439646027668023fdf124e6bdff81121..16ce9be588df1dcc18a6d8cbc764a9141d437a77 100644 (file)
@@ -1,8 +1,8 @@
 
 @SYNTAX:load@
 
-Load a plugin. If full path isn't given, irssi searches the plugin from
-directories:
+Load a plugin. If full path isn't given, irssi searches the
+plugin from directories:
 
  ~/.irssi/modules/
  <install dir, /usr/local or /usr maybe>/lib/irssi/modules/
diff --git a/apps/irssi/docs/help/in/lusers.in b/apps/irssi/docs/help/in/lusers.in
deleted file mode 100644 (file)
index abcd55c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-
-@SYNTAX:lusers@
-
-Shows user statistics of the current IRC network.
-
diff --git a/apps/irssi/docs/help/in/map.in b/apps/irssi/docs/help/in/map.in
deleted file mode 100644 (file)
index 824dce3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-
-@SYNTAX:map@
-
-Not available in IRC.
-
index b918ec71abecc47798a0fe3a1862c4cb22b73223..44871274cfa7fca6e505d842545fcc7f6b0c6226 100644 (file)
@@ -1,8 +1,7 @@
 
 @SYNTAX:me@
 
-Sends a CTCP ACTION to the current channel or query.
-For example: /me sits back.
-
-See also: ACTION, CTCP
+Sends an ACTION channel message to the current channel.
+For example:  /ME sits back.
 
+See also: ACTION
diff --git a/apps/irssi/docs/help/in/mircdcc.in b/apps/irssi/docs/help/in/mircdcc.in
deleted file mode 100644 (file)
index d3402e0..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-@SYNTAX:mircdcc@
-
-Selects whether to send mIRC style CTCPs in DCC chat
-session.
-
-If a mIRC user sends first a CTCP, mIRC style CTCPs is
-automatically selected for that DCC Chat session.
-
-See also: SET MIRC
-
diff --git a/apps/irssi/docs/help/in/mode.in b/apps/irssi/docs/help/in/mode.in
deleted file mode 100644 (file)
index 5bef27c..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-
-@SYNTAX:mode@
-
-Irssi knows these channel modes:
-
-     i - Invite only - People can't join to channel without being
-        /INVITEd, or being in invite list (+I, see below).
-     m - Moderated - People who don't have voices (+v) can't send
-        messages to channel
-     p - Private - People who aren't joined to channel can't see it
-        for example with /WHOISing people who are in channel.
-     s - Secret - Like private, but the channel isn't displayed in
-        /LIST's output.
-     n - No external msgs - Without this mode, anyone can send messages
-        to channel without even being joined.
-     t - Topic can be changed only by channel operators.
-
-     k <key> - Channel password (aka. key) - The channel can't be joined
-              without specifying the channel key (see section 6.2).
-
-     l <count> - User limit - No more than <count> people can join to
-                channel. This can be overridden with /INVITE with some
-                servers.
-
-                This is usually used for protecting channel from join
-                flooding, like some bot allows max. 5 users to join in
-                one minute or so.
-
-     a - Anonymous - No-one's nick name, host or anything else can be
-        seen. All messages, joins, parts, modes, etc. are seen as coming
-        from nick "anonymous", this could be pretty confusing but nice
-        feature if you want total anonymity. This mode can only be set,
-        never unset. This mode isn't supported by all servers.
-
-        NOTE: there is/was one bug :) Channel operators can guess if some
-        nick might be in the channel and try to kick it. If nick was in
-        channel, everyone will see the nick that was kicked.
-
-     r - Re-op - If channel becomes opless for longer than 45 (?) minutes,
-        op everyone in the channel. This works only in !channels. This
-        mode can only be set, not unset by channel creator.
-
-     b - Set/remove ban. For example MODE #channel +b *!*@*.org bans
-        everyone from .org domain.
-
-        If someone from .org domain was already in channel before the
-        ban was set, he/she couldn't be able to write any messages to
-        channel (doesn't work with all servers).
-
-        Ban can also be overridden with /INVITE, although many stupid
-        IRC clients automatically kick the user out because they see
-        the ban and think that because of it the user shouldn't be in
-        the channel (doesn't work with all servers).
-
-     e - Ban exceptions. You could for example ban everyone from
-        *!*@*.org but set ban exception to *!*@*.host.org - works only
-        in IRCnet/EFnet servers.
-
-     I - Invite list. If channel is invite only (+i), people in this
-        list can join it without being /INVITEd - works only in
-        IRCnet/EFnet servers.
-
-        This is excellent for in-country channels that don't want
-        foreigners (spammers!) to join the channel, for example setting
-        channel's mode to +i and +I *!*@*.fi allows only finnish people
-        to join the channel. In addition to this, there's usually a bot
-        in the channels and sending /MSG bot invite command to it
-        /INVITEs you to the channel.
-
-        The ':' feature in channel modes is quite similiar, see section
-        6.2.
-
-     O - Channel owner, the nick who creates a !channel receives this
-        mode. It isn't displayed anywhere, you can't pass it to anyone
-        else and you can't regain it again. This is needed for setting
-        +r mode in channel when it's first created.
-
-     o <nick> - Grant or revoke channel operator status from nick
-     v <nick> - Grant or revoke voice status from nick, only people with
-               +v (or +o) can talk to channel when it's moderated (+m).
-
-You can send multiple mode changes with one mode command:
-
-/MODE #channel +nto-o+v nick1 nick2 nick3
-
-This would set channel's mode to +nt, give ops to nick1, take ops
-from nick2 and give voices to nick3.
-
-You can set only limited number of modes that requires argument in
-one command. In IRCnet it's 3, in EFnet it's 4 and in many others
-it's 6. If it's not known, Irssi defaults to 3. Irssi will also
-automatically split them, so you can use /MODE +oooooo n1,n2,..
-command to op 6 people and Irssi will split it to two commands in
-IRCnet/EFnet.
-
-See also: OP, DEOP, VOICE, DEVOICE, BAN, UNBAN
-
index f58ba41a2da7917da76ee15a77cce9f946bdae8c..5224b27a77a7e3368d4ab5b44847fd1e0f380239 100644 (file)
@@ -11,5 +11,3 @@ Examples:
 /MSG #irssi Hello, is the new gtk-version out already?
 (This format is rarely needed.)
 
-See also: CTCP
-
index 2d515c1ce026e94d97f1b977b90992d3766c6f34..d3512d0bba50084bca7737ff8be8899e1dfa03f1 100644 (file)
@@ -6,8 +6,9 @@
      -voices: show voiced people in list
      -normal: show rest of the people in list
 
-Shows the names (nicks) in the specified channels. /NAMES ** shows all
-nicks in all channels, you probably don't want to do this.
+Shows the names (nicks) in the specified channels. /NAMES **
+shows all nicks in all channels, you probably don't want
+to do this.
 
 Examples:
 
diff --git a/apps/irssi/docs/help/in/nctcp.in b/apps/irssi/docs/help/in/nctcp.in
deleted file mode 100644 (file)
index 58b8054..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:nctcp@
-
-Sends a CTCP reply notice to the nick/channel.
-
-See also: CTCP, ACTION, MSG, NOTICE
-
diff --git a/apps/irssi/docs/help/in/netsplit.in b/apps/irssi/docs/help/in/netsplit.in
deleted file mode 100644 (file)
index 2e244ca..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-
-@SYNTAX:netsplit@
-
-Irssi keeps track of people who were lost in net splits. With this
-command you can get a list of them.
-
index 57313b7fb8bf1b9396cc65a95136a3dda64be15b..d657288da915bb49a0001b290f3d7b2e97566d8e 100644 (file)
@@ -1,6 +1,6 @@
 
 @SYNTAX:nick@
 
-Changes your nick. This should be hardly rarely 
-used or needed.
+Changes your nickname.
+
 
diff --git a/apps/irssi/docs/help/in/note.in b/apps/irssi/docs/help/in/note.in
deleted file mode 100644 (file)
index d77bb23..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-
-@SYNTAX:note@
-
-NOTE is a sort of turbo-charged messaging system for irc. In short, 
-it achieves at the server level what the client attempts to do with MSG 
-and NOTIFY. The messaging system resembles modern voicemail systems 
-(except in text); messages can be sent, stored, or set for deferred 
-delivery. The client notification system works like NOTIFY, except with
-greater accuracy and flexibility. 
-
-The most common uses of NOTE are its SPY and SEND functions. SPY is similar 
-to NOTIFY, except it can accept a full address to spy on, not just a nickname. 
-SEND, as its name implies, sends a note to a user; if that user is not currently 
-online, it will be delivered if the user logs onto irc within a set time period. 
-
-When referring to a particular user, NOTE can deal with the standard 
-nick!user@host notation. Wildcards are allowed, and any portion may be omitted, 
-so long as the identifier remains unambiguous. 
-
-Examples: 
-To send a note to Joebob (whose account is jbriggs@drivein.com): 
-/NOTE SEND joebob!jbriggs@drivein.com Hey there! Great movie! 
-
-To spy on anyone from blah.com for the next 30 days: 
-/NOTE SPY +30 *!*@*.blah.com A blah.com user is active 
-This command is Not available in the IRCNet. 
-
index 6b4da9b08f679544f40e096d76403a3b01ce9348..e93d2c2a6aeaadb0501790a77d15bdea9337f5a6 100644 (file)
@@ -1,10 +1,8 @@
 
 @SYNTAX:notice@
 
-Sends a notice to the nick or the channel. Usually notices are 
-used in bots and scripts for different kinds of replies. The 
-IRC protocol states that notices may not generate replies to
-avoid msg loops.
+Sends a notice to the nick or the channel. Usually notices
+are used in bots and scripts for different kinds of replies.
 
-See also: NCTCP, MSG
+See also: ACTION
 
diff --git a/apps/irssi/docs/help/in/notify.in b/apps/irssi/docs/help/in/notify.in
deleted file mode 100644 (file)
index 5f278e0..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-
-@SYNTAX:notify@
-
-     -away: Notifies about away-status changes
-     -idle: Notifies if idle time is first larger than <minutes>
-            (default is hour) and then it drops down.
-     -list: Lists the notify list entries with all their settings   
-            <mask>: Either a simple "nick" or "nick!*@*blah.org". 
-            The nick can't contain wildcards, but the user/host can.
-        
-/NOTIFY without any arguments displays if the people in notify
-list are online or offline.
-
-See also: UNNOTIFY, SET NOTIFY
-
diff --git a/apps/irssi/docs/help/in/op.in b/apps/irssi/docs/help/in/op.in
deleted file mode 100644 (file)
index ce46cb6..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-@SYNTAX:op@
-
-Gives the channel operator privileges for the specified
-nick(s). Wildcards in the nick are allowed.
-
-See also: DEOP, MODE, VOICE, DEVOICE, KICK
-
index 6cb6518c98f3a491be82919f05c14823c6a64978..40dc9c54d3a88d0caf93ac84463f0afb5f1af9f4 100644 (file)
@@ -1,10 +1,9 @@
 
 @SYNTAX:oper@
 
-Gives you operator priviledges if the correct nickname and
-password are given.  If password is not given, you will be
-prompted for one.  If no nickname is given, your current
-nickname will be used.
+Gives you server operator priviledges if the correct
+username and passphrase are given. User will be prompted
+for the passphrase if the -pubkey option is not provided.
 
-See also: KILL, DIE
+See also: KILL, SCONNECT, CLOSE, SILCOPER
 
index 3518b0c96a4150b60a676e6704b785f9ffdfdfec..806e1b9b71049b7aa2fbd8fcfcb1c60f569dcde9 100644 (file)
@@ -1,10 +1,7 @@
 
 @SYNTAX:ping@
 
-Sends CTCP PING to another IRC client. This is used
-to find out the speed of IRC network. When the PONG
-reply comes in, irssi shows the interval time between
-sending the request and receiving the reply.
+Sends PING to the connected server.
+
 
-See also: CTCP
 
index 818bbe64db8848c27c9b9a7f3782fdb32b6717a7..52f191eef1dad3abdcf8c7226b84a7df96709426 100644 (file)
@@ -7,5 +7,7 @@ the specified nick in the form of MSGs.
 
 Usually this command opens up a new window, too.
 
-See also: WINDOW, MSG, SET QUERY
+The query is ended by giving command UNQUERY
+
+See also: UNQUERY, WINDOW, MSG, SET QUERY
 
index f753621156978d159188e83d0701d510dbb1082e..1c1e7bac089d226010a2d238e6997c8efd50146d 100644 (file)
@@ -1,7 +1,7 @@
 
 @SYNTAX:quit@
 
-This ends your irc session. If a quit message is supplied, it 
+This ends your session. If a quit message is supplied, it 
 will be displayed to anyone else on any channel you were on 
 before quitting. If one isn't specified, the text "Leaving" is
 used.
diff --git a/apps/irssi/docs/help/in/quote.in b/apps/irssi/docs/help/in/quote.in
deleted file mode 100644 (file)
index eceb409..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-@SYNTAX:quote@
-
-Sends server raw data without parsing.
-
-Example:
-   /QUOTE PRIVMSG cras :Hey, this works!
-
diff --git a/apps/irssi/docs/help/in/rawlog.in b/apps/irssi/docs/help/in/rawlog.in
deleted file mode 100644 (file)
index b863e23..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-
-@SYNTAX:rawlog@
-
-All data that is received or sent to server is kept in a raw log
-buffer for a while. Also event redirections are kept there. This is
-very useful for debugging purposes.
-
-/RAWLOG SAVE <filename> - Save the current raw log buffer to file 
-/RAWLOG OPEN <filename> - Like /RAWLOG SAVE, but keep the log file
-                          open and write all new log to it.
-/RAWLOG CLOSE - Close the open raw log
-        
-/SET rawlog_lines <count> - Specify the number of raw log lines to
-                            keep in memory.
-
diff --git a/apps/irssi/docs/help/in/rehash.in b/apps/irssi/docs/help/in/rehash.in
deleted file mode 100644 (file)
index 2fb5579..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-@SYNTAX:rehash@
-
-IRC Operator command.
-
-This command is used to force the current server to reload it's
-ircd.conf configuration file. This is useful for effecting
-configuration changes without starting a new server.
-
-See also: OPER, RESTART
-
diff --git a/apps/irssi/docs/help/in/restart.in b/apps/irssi/docs/help/in/restart.in
deleted file mode 100644 (file)
index 14cd940..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-@SYNTAX:restart@
-
-IRC Operator command.
-
-This command is used to completely restart the server. A side effect of
-this is that the configuration file will be read again. However, it is
-generally more useful for clearing out internal buffers and other
-wasted memory.
-
-See also: OPER, DIE
-
index a485e93a67dea108de40c582670f32e475c9fd58..35e8ea3bfe96697a41c90a2c37aba72460def60a 100644 (file)
@@ -1,9 +1,9 @@
 
 @SYNTAX:rmrejoins@
 
-Removes the pending rejoins from the channel rejoin list in active
-server. Channels are added to rejoin list when join failed because of
-netsplits in server ("Channel is temporarily unavailable").
+Removes the pending rejoins from the channel rejoin list in
+active server. Channels are added to rejoin list when join
+failed because of netsplits in server.
 
 See also: JOIN
 
diff --git a/apps/irssi/docs/help/in/rping.in b/apps/irssi/docs/help/in/rping.in
deleted file mode 100644 (file)
index e062317..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-@SYNTAX:rping@
-
-IRC Operator command.
-
-This command works like the PING command (CTCP PING), except 
-it is used on a server instead of a client. As with PING, it 
-is used to test the relative distance another server is from 
-you across the irc network.
-
-See also: OPER
-
diff --git a/apps/irssi/docs/help/in/run.in b/apps/irssi/docs/help/in/run.in
deleted file mode 100644 (file)
index 982808c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:run@
-
-Runs a perl-script. For more information, see the
-perl.txt in the docs-directory of irssi.
-
-See also: PERLFLUSH
index 0f19d3492d342d4c7c11841c78744ff987c7c2c3..f77c5d3d978752c89a1acd3affe52d2354edf6da 100644 (file)
@@ -1,8 +1,11 @@
 
 @SYNTAX:sconnect@
 
-IRC Operator command. Makes an IRC server to connect
-to another server.
+Operator command. Makes an server to connect to another
+server or router.  The <server> is the hostname or IP 
+addres of the server.
 
-See also: OPER, SQUIT, RESTART
+Example: /SCONNECT silc.silcnet.org 706
+
+See also: OPER, SILCOPER, CLOSE, SHUTDOWN
 
index f75462cb0d747ba3980a7f84dd67a572f65c4301..75170f1273f015b170a70c724e2b7342704c5eac 100644 (file)
@@ -4,7 +4,7 @@
      -4, -6: specify explicitly whether to use IPv4 or IPv6 address
      -auto: Automatically connect to server at startup (default)
      -noauto: Don't connect to server at startup
-     -ircnet: Specify what IRC network this server belongs to
+     -silcnet: Specify what network this server belongs to
      -host: Specify what host name to use, if you have multiple
      -cmdspeed: Same as /SET cmd_queue_speed, see section 3.1
      -cmdmax: Same as /SET cmd_max_at_once, see section 3.1
diff --git a/apps/irssi/docs/help/in/servlist.in b/apps/irssi/docs/help/in/servlist.in
deleted file mode 100644 (file)
index cee9612..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-
-@SYNTAX:servlist@
-
-SERVLIST gives the list of services currently present on the
-IRC network.  It can take two arguments.
-    <mask> limits the output to the services which names matches
-           the mask.
-    <type> limits the output to the services of the specified type.
-  
-The fields returned are:
-    Service name.
-    Server who introduced the service.
-    Distribution mask.
-    Service type.
-    Hop count to the service.
-    A comment.
-
-See also: SQUERY
-
diff --git a/apps/irssi/docs/help/in/shutdown.in b/apps/irssi/docs/help/in/shutdown.in
new file mode 100644 (file)
index 0000000..48ff58c
--- /dev/null
@@ -0,0 +1,7 @@
+
+@SYNTAX:shutdown@
+
+Operator command.  Shutdowns the server.
+
+See also: OPER, SILCOPER, CLOSE
+
diff --git a/apps/irssi/docs/help/in/silcoper.in b/apps/irssi/docs/help/in/silcoper.in
new file mode 100644 (file)
index 0000000..5247bf4
--- /dev/null
@@ -0,0 +1,12 @@
+
+@SYNTAX:silcoper@
+
+Gives you router operator priviledges if the correct
+username and passphrase are given. User will be prompted
+for the passphrase if the -pubkey option is not provided.
+
+NOTE: This command works only on router server. It has
+no effect on normal SILC server.
+
+See also: KILL, SCONNECT, CLOSE, OPER
+
diff --git a/apps/irssi/docs/help/in/silence.in b/apps/irssi/docs/help/in/silence.in
deleted file mode 100644 (file)
index 4e37d7b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-
-@SYNTAX:silence@
-
-Works only in the Undernet and Open Projects (ircu).
-
-SILENCE is similar in many respects to IGNORE, except that it is 
-server-based. What this means is the server will never even send 
-you messages from anyone you have SILENCEd, whereas it will with 
-IGNORE, where your client is responsible for filtering the messages 
-out. This has the advantage of not bogging your client down with 
-excessive data as it tries to filter out messages. 
-
-The default behavior is to SILENCE a nick!user@host pattern, and 
-if such a pattern is not passed as the argument, it must be prepended 
-with a plus ('+') to be added to your silence list. If a pattern is 
-prepended with a minus ('-'), it will be removed from your silence list. 
-If you only specify a nickname, you can list the patterns in the 
-silence list owned by that nickname. If no arguments are given, your 
-own silence list is displayed. 
-See also: IGNORE
-
diff --git a/apps/irssi/docs/help/in/squery.in b/apps/irssi/docs/help/in/squery.in
deleted file mode 100644 (file)
index a0bb4f1..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-@SYNTAX:squery@
-
-   <service>   - Service name
-   <commands>  - Commands to pass to the service.
-
-/SQUERY sends a query to the specified service.
-
-See also: SERVLIST
-
diff --git a/apps/irssi/docs/help/in/squit.in b/apps/irssi/docs/help/in/squit.in
deleted file mode 100644 (file)
index 64b8616..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:squit@
-
-IRC Operator command. Makes server to quit IRC network.
-
-See also: OPER, DIE, RESTART
-
diff --git a/apps/irssi/docs/help/in/stats.in b/apps/irssi/docs/help/in/stats.in
deleted file mode 100644 (file)
index 3afc6eb..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-
-@SYNTAX:stats@
-
-Shows some irc server usage statistics.
-  c - Shows C and N lines for a given server.  These are 
-      the names of the servers that are allowed to connect.
-  h - Shows H and L lines for a given server (Hubs and Leaves).
-  k - Show K lines for a server.  This shows who is not 
-      allowed to connect and possibly at what time they are
-      not allowed to connect.
-  i - Shows I lines. This is who CAN connect to a server.
-  l - Shows information about amount of information passed
-      to servers and users.
-  m - Shows a count for the number of times the various 
-      commands have been used since the server was booted.
-  o - Shows the list of authorized operators on the server.
-  u - Shows the uptime for a server
-  y - Shows Y lines, which lists the various connection 
-      classes for a given server.
-
diff --git a/apps/irssi/docs/help/in/time.in b/apps/irssi/docs/help/in/time.in
deleted file mode 100644 (file)
index 7877fe7..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-@SYNTAX:time@
-
-This displays the time of day, local to the server queried (thus,
-the time returned may not be the same as the client's local time). 
-
-If the server name is omitted, the client's current server is used. 
-If a nickname is given, that client's server is queried.
-
-Same as /DATE.
-
index ae2564f81646cc769eb82612d98ecb9ab183e89a..bf44a3ad89f8883846a7f0597b303774798cf57d 100644 (file)
@@ -1,8 +1,6 @@
 
 @SYNTAX:topic@
 
-   -delete  - Deletes the topic.
-
-Shows or/and changes the topic of the current or specified 
-channel.
+Shows or/and changes the topic of the current or
+specified channel.
 
diff --git a/apps/irssi/docs/help/in/trace.in b/apps/irssi/docs/help/in/trace.in
deleted file mode 100644 (file)
index 27df384..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-@SYNTAX:trace@
-
-Without a specified server it shows the current connections on
-the local server.  If you specify a remote server it will show
-all servers between your current server and that remote server
-as well as the connections on that remote server.
-
diff --git a/apps/irssi/docs/help/in/ts.in b/apps/irssi/docs/help/in/ts.in
deleted file mode 100644 (file)
index 0f51f96..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:ts@
-
-Shows topics of all channels you're on.
-
-See also: CHANNEL, TOPIC
-
diff --git a/apps/irssi/docs/help/in/umode.in b/apps/irssi/docs/help/in/umode.in
new file mode 100644 (file)
index 0000000..a833a0d
--- /dev/null
@@ -0,0 +1,15 @@
+
+@SYNTAX:umode@
+
+This command is used to manage client's modes in the network.
+Note that some of the modes the client cannot set itself.
+The mode is added by adding + before the option(s) and removed
+by adding - before the option(s).  The following channel user
+modes are available:
+
+    a        Unset all modes
+    s        Unset server operator privileges
+    r        Unset router operator privileges
+    g        Set/unset to be gone (or use /AWAY command)
+
+See also: CMODE, CUMODE, AWAY
diff --git a/apps/irssi/docs/help/in/unban.in b/apps/irssi/docs/help/in/unban.in
deleted file mode 100644 (file)
index a22963a..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-@SYNTAX:unban@
-
-Removes the specified ban(s) from the channel.
-
-Examples:
-   /UNBAN *!*@*.fi
-   /UNBAN larry!*@* *!me@*.mydomain.net
-
-See also: BAN, KNOCKOUT
-
index 4bfdeb95edf571c23b25882294835313785481ac..2e1c4678aca7ebe627c1b3bf8fcc46545151900a 100644 (file)
@@ -1,8 +1,8 @@
 
 @SYNTAX:unload@
 
-Unload a running plugin. List of running plugins can be shown with
-/LOAD.
+Unload a running plugin. List of running plugins can
+be shown with /LOAD.
 
 See also: LOAD
 
diff --git a/apps/irssi/docs/help/in/unnotify.in b/apps/irssi/docs/help/in/unnotify.in
deleted file mode 100644 (file)
index 5d74ce8..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:unnotify@
-
-Removes an entry from the notify list.
-
-See also: NOTIFY
-
diff --git a/apps/irssi/docs/help/in/unsilence.in b/apps/irssi/docs/help/in/unsilence.in
deleted file mode 100644 (file)
index 110ff55..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:unsilence@
-
-Works only in the Undernet and Open Projects (ircu).
-
-Removes a pattern from your silence list.
-
-See also: SILENCE
-
diff --git a/apps/irssi/docs/help/in/uping.in b/apps/irssi/docs/help/in/uping.in
deleted file mode 100644 (file)
index 0fc003b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-@SYNTAX:uping@
-
-IRC Operator command. Works only in the Undernet and Open Projects (ircu).
-
-This command works like the PING command (CTCP PING), except 
-it is used on a server instead of a client. As with PING, it 
-is used to test the relative distance another server is from 
-you across the irc network.
-
-See also: RPING, OPER
-
diff --git a/apps/irssi/docs/help/in/userhost.in b/apps/irssi/docs/help/in/userhost.in
deleted file mode 100644 (file)
index a1039f9..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@SYNTAX:userhost@
-
-Shows the userhost info of the specified nick.
-
-See also: WHOIS
-
diff --git a/apps/irssi/docs/help/in/users.in b/apps/irssi/docs/help/in/users.in
new file mode 100644 (file)
index 0000000..952a882
--- /dev/null
@@ -0,0 +1,11 @@
+
+@SYNTAX:users@
+
+Shows users of the specified channel.  You must
+already be on the channel.
+
+Alias WHO is by default USERS * command.
+
+See also: WHOIS, WHOWAS
+
+
diff --git a/apps/irssi/docs/help/in/ver.in b/apps/irssi/docs/help/in/ver.in
deleted file mode 100644 (file)
index 975fbf7..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:ver@
-
-Sends a CTCP VERSION request to the nick. This is used
-to find out which client and/or script the nick
-is using.
-
-See also: CTCP
-
index a7dd5c6f62db7b4294e4ec49451c1f63b341d4a4..d29a4caeeac4f6b73040b598c54f36c2acd12f49 100644 (file)
@@ -1,8 +1,6 @@
 
 @SYNTAX:version@
 
-Shows the version info of the current or specified
-IRC server.
+Shows the version of the client.
 
-See also: ADMIN, STATS
 
diff --git a/apps/irssi/docs/help/in/voice.in b/apps/irssi/docs/help/in/voice.in
deleted file mode 100644 (file)
index ee6f243..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-@SYNTAX:voice@
-
-Gives the voice (+v mode) to the nick(s) on the current channel.
-Wildcards in the nick are allowed.
-See also: DEVOICE, OP, DEOP
-
diff --git a/apps/irssi/docs/help/in/wait.in b/apps/irssi/docs/help/in/wait.in
deleted file mode 100644 (file)
index 4f5dc2f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-@SYNTAX:wait@
-
-Wait for <milliseconds> before sending the next command to server.
-
-This could be useful for example when identifying to NickServ; after
-sending the identify message you'd wait 3 seconds before joining to
-channels so NickServ has time to identify you to ChanServ which then
-auto-ops you when joining.
-
diff --git a/apps/irssi/docs/help/in/wall.in b/apps/irssi/docs/help/in/wall.in
deleted file mode 100644 (file)
index 212c3a8..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-
-@SYNTAX:wall@
-
-This command sends a message to all operators in a channel. This is internal
-irssi command which sends a message separately to each opearator, so this
-may not be very good idea to use in channel with lots of operators.
-
-Some IRC servers support also /MSG @#channel or /WALLCHOPS which you should
-use if possible.
-
-See also: WALLCHOPS
diff --git a/apps/irssi/docs/help/in/wallchops.in b/apps/irssi/docs/help/in/wallchops.in
deleted file mode 100644 (file)
index 034b1f6..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:wallchops@
-
-Works only in the Undernet and Open Projects (ircu).
-
-Sends an message to all other channel operators of the current channel.
-
-See also: WALL
-
diff --git a/apps/irssi/docs/help/in/wallops.in b/apps/irssi/docs/help/in/wallops.in
deleted file mode 100644 (file)
index 1690db2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:wallops@
-
-This command sends the given message to everyone on 
-the network who has user mode +w turned on. If you 
-are not an operator, you will probably receive
-an error message when using this command
-
-See also: OPER, WALLOPS
diff --git a/apps/irssi/docs/help/in/who.in b/apps/irssi/docs/help/in/who.in
deleted file mode 100644 (file)
index 014231e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-
-@SYNTAX:who@
-
-Without parameters, shows all users and their user infos
-on the current channel. If you specify a channel, shows
-the all users of the given channel.
-
-If you specify an string with wildcards, you will be
-shown all users whose nick, userhost or realname matches
-the wildcard expression.
-
-If a channel is secret or private and you're not on it,
-you will be shown only those channel members, who do not 
-have the invisible (+i) mode set.
-
-Examples:
-   /WHO          - Shows users on current channel
-   /WHO #irssi   - Shows users on channel #irssi
-   /WHO timo*    - Shows users whose nick, userhost,
-                   or realname begins with string 'timo'
-
-See also: WHOIS, CHANNEL
-
index 55f3e330453fc9a54c86cb35654ffbc0f04f8438..f9ebe71de3837d4d0de75bcb0461b7547dcc9547 100644 (file)
@@ -1,15 +1,9 @@
 
 @SYNTAX:whois@
 
-Shows whois information of the specified nick.
+Shows whois information of the specified client.
 By default, this is aliased to /WI.
 
-/WHOIS nick1 nick1 also queries the idle time of
-the user. This is aliased to /WII by default.
+See also: WHOWAS, CHANNEL
 
-If given nick is not in the IRC, irssi automatically
-sends a WHOWAS query. Read carefully the reply to see
-if it is a WHOIS or WHOWAS reply. :)
-
-See also: WHO, CHANNEL
 
index 2c7c68bcde98b9918d4300cae974283546428edd..65f8040e8d12ec6999b2b438f726ecb86b0cb9a0 100644 (file)
@@ -1,19 +1,16 @@
 
 @SYNTAX:whowas@
 
-This command is similar to WHOIS, except it returns information 
-about nicknames that were recently in use. Like WHOIS, it shows 
-the nickname, address, real name, and server. It may also return 
-multiple entries if the nickname has been used recently by several 
-people. These multiples may be limited by specifying a count to show. 
-
-WHOWAS will work regardless of whether the queried nick is in use. 
-If no arguments are given, the client's current nickname is used. 
-
-Example: To show the last 5 users of the nickname JoeBob: 
-
-/whowas joebob 5 
-
+This command is similar to WHOIS, except it returns
+information about nicknames that were recently in use.
+Like WHOIS, it shows the nickname, address, real name,
+and server. It may also return multiple entries if the
+nickname has been used recently by several people. These
+multiples may be limited by specifying a count to show. 
+
+WHOWAS will work regardless of whether the queried nick
+is in use. If no arguments are given, the client's current
+nickname is used. 
 
 See also: WHOIS
 
diff --git a/apps/irssi/docs/help/in/wjoin.in b/apps/irssi/docs/help/in/wjoin.in
deleted file mode 100644 (file)
index 8386e17..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-@SYNTAX:wjoin@
-
-With this you can join multiple channels in same 
-window. This command does the same as /JOIN but
-it doesn't create a new window for the channel joined.
-
-See also: JOIN, WINDOW
-
diff --git a/apps/irssi/docs/help/in/wquery.in b/apps/irssi/docs/help/in/wquery.in
deleted file mode 100644 (file)
index ae4799a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-@SYNTAX:wquery@
-
-Starts a query in the current window without
-opening a new window.
-
-See also: QUERY, WINDOW, SET AUTOCREATE
-
index 7c25c7bc54c7b421fa42552bd97c8be0bd5aa904..2954deff23d114adb1ca5ad9510f646c2fcce889 100644 (file)
        --enable-memdebug  Enable memory debugging, great for finding
                           memory leaks
 
-       --enable-perl=static  Build Perl support statically to irssi binary
+       --with-perl=static Build Perl support statically to irssi binary
                           (default is to build a module)
-       --enable-perl-path=dir  Specify installation dir for Perl libraries
-       --disable-perl     Disable Perl support
+       --with-perl-lib=[site|vendor|DIR]  Specify installation dir for
+                          Perl libraries. Site is the default (usually
+                          /usr/local/lib/perl/...), vendor uses the path
+                          where the base of the perl is installed
+                          (/usr/lib/perl/...), or DIR specifies exactly
+                          where you want to install it.
+       --without-perl     Disable Perl support
 
        --with-socks       Build with socks library
        --with-bot         Build irssi-bot
diff --git a/apps/irssi/docs/perl.txt b/apps/irssi/docs/perl.txt
new file mode 100644 (file)
index 0000000..b04e0cb
--- /dev/null
@@ -0,0 +1,1143 @@
+ Running Perl scripts
+ --------------------
+
+First you'll need to have Perl support on. By default irssi compiles
+Perl as a module, so /LOAD perl probably helps. If you want to do this
+automatically at startup put the "/LOAD perl" to ~/.irssi/startup file.
+After that you can run scripts with /RUN script (you don't need to give
+the .pl extension). If /RUN complains about "unknown command", you
+don't have Perl module loaded, or maybe Perl support wasn't compiled at
+all.
+
+Place new scripts to ~/.irssi/scripts/ or /usr/local/lib/irssi/scripts/
+directory. Scripts in ~/.irssi/scripts/autorun/ directory are
+automatically run at startup.
+
+Using /PERLFLUSH closes and reopens the perl interpreter removing all
+Perl scripts from memory. There's currently no way to unload a single
+Perl script (/SCRIPT REMOVE will probably work soon). You can however
+run same script multiple times, and irssi will remove the old version
+from memory before running the new version.
+
+
+ Irssi's signals
+ ---------------
+
+Irssi is pretty much based on sending and handling different signals.
+Like when you receive a message from server, say
+
+  :nick!user@there.org PRIVMSG you :blahblah
+
+Irssi will first send a signal:
+
+  "server incoming", SERVER_REC, "nick!user@there PRIVMSG ..."
+
+You probably don't want to use this signal. Default handler for this
+signal interprets the header and sends a signal:
+
+  "server event", SERVER_REC, "PRIVMSG ...", "nick", "user@there.org"
+
+You probably don't want to use this either, since this signal's default
+handler parses the event string and sends a signal:
+
+  "event privmsg", SERVER_REC, "you :blahblah", "nick", "user@there.org"
+
+You can at any point grab the signal, do whatever you want to do with
+it and optionally stop it from going any further by calling
+Irssi::signal_stop();
+
+For example:
+
+  sub event_privmsg {
+    # $data = "nick/#channel :text"
+    my ($server, $data, $nick, $address) = @_;
+    my ($target, $text) = split(/ :/, $data, 2);
+
+    Irssi::signal_stop() if ($text =~ /free.*porn/ || $nick =~ /idiot/);
+  }
+
+Irssi::signal_add("event privmsg", "event_privmsg")
+
+This will hide all public or private messages that match the regexp
+"free.*porn" or the sender's nick contain the word "idiot". Yes, you
+could use /IGNORE instead for both of these :)
+
+You can also use signal_add_last() if you wish to let the Irssi's internal
+functions be run before yours.
+
+A list of signals that irssi sends can be found from signals.txt file.
+
+
+ Creating/replacing /COMMANDS
+ ----------------------------
+
+You can create your own commands, or replace existing ones with
+Irssi::command_bind(). The command handling work internally pretty much
+the same as signal handlers, so if you replace existing command and don't
+wish to let it run, call Irssi::signal_stop().
+
+Here's an example:
+
+  # Usage: /HELLO [<nick>]
+  sub cmd_hello {
+    # data - contains the parameters for /HELLO
+    # server - the active server in window
+    # witem - the active window item (eg. channel, query)
+    #         or undef if the window is empty
+    my ($data, $server, $witem) = @_;
+
+    if (!$server || !$server->{connected}) {
+      Irssi::print("Not connected to server");
+      return;
+    }
+
+    if ($data) {
+      $server->command("/MSG $data Hello!");
+    } elsif ($witem && ($witem->{type} eq "CHANNEL" ||
+                        $witem->{type} eq "QUERY")) {
+      # there's query/channel active in window
+      $witem->command("/MSG ".$witem->{name}." Hello!");
+    } else {
+      Irssi::print("Nick not given, and no active channel/query in window");
+    }
+  }
+
+  Irssi::command_bind('hello', 'cmd_hello');
+
+
+ Message levels
+ --------------
+
+Several functions expect message levels. They're used to roughly
+classify messages. They're used by a lot of things including logging,
+ignoring, highlighting, etc. so you should use as good level as
+possible. It's possible to have several levels in one message, like
+ACTIONS+PUBLIC or ACTIONS+MSGS.
+
+Here's all the levels that irssi supports currently:
+
+  CRAP, MSGS, PUBLIC, NOTICES, SNOTES, CTCPS, ACTIONS, JOINS, PARTS
+  QUITS, KICKS, MODES, TOPICS, WALLOPS, INVITES, NICKS, DCC, DCCMSGS,
+  CLIENTNOTICE, CLIENTCRAP, CLIENTERROR
+
+And a few special ones that could be included with the levels above:
+
+  HILIGHT - text is highlighted
+  NOHILIGHT - don't check highlighting for this message
+  NO_ACT - don't trigger channel activity when printing this message
+  NEVER - never ignore or log this message (not a good idea usually)
+
+You can use them with a MSGLEVEL_ prefix, for example:
+
+  $server->print("#channel", 'Hello, world', MSGLEVEL_CLIENTCRAP);
+
+Writes text to #channel window with CLIENTCRAP level.
+
+
+ Window items
+ ------------
+
+Meaning of "window" should be pretty clear, but "window item" is
+something I couldn't really figure out a better name for :) They're
+simply something that's inside a window, a channel or a query usually.
+Windows can have multiple items inside them. It's possible to create
+non-channel/query window items too, currently the third possible window
+item is created by /EXEC -interactive.
+
+In scripts, I think you can quite safely assume that the window item is
+query or channel if the script is intended to be run in one of them.
+Stupid users won't probably have other window items, and smart users
+know where to run the script, or at least later figure out why it
+didn't work :)
+
+
+ Functions that you can use in Irssi's Perl scripts
+ --------------------------------------------------
+
+If there's a "Xxxx::" text before the command, it means that it belongs to
+that package. Like "Server::command" means that you should either call it as
+  Irssi::Server::command($server, $cmd);
+or more easily:
+  $server->command($cmd);
+
+Commands that don't have the Xxxx prefix are called as Irssi::command();
+
+Information from most objects can be fetched with $object->{data}, for
+example current nick in server could be read with $server->{nick}. List
+of all the information that are in objects are in "Object->{}" sections
+below.
+
+Commands are split in two groups, generic ones that could be used with
+any chat protocol, and IRC specific commands. If you want to use IRC
+specific commands, or use IRC specific ->{data} in your scripts, you'll
+need to add "use Irssi::Irc" to your scripts. IRC specific commands are
+listed after the generic ones.
+
+
+ *** General
+
+Window active_win() - return active window
+Server active_server() - return server in active window
+
+windows() - return list of all windows
+servers() - return list of all servers
+reconnects() - return list of all server reconnections
+channels() - return list of all channels
+queries() - return list of all queries
+commands() - return list of all commands
+logs() - return list of all log files
+ignores() - returns list of all ignores
+
+Server::channels() - return list of channels in server
+Server::queries() - return list of queries in server
+
+print(str[, level])
+Server::print(channel, str[, level])
+Window::print(str[, level])
+Windowitem::print(str[, level])
+  Print `str'. Default level is MSGLEVEL_CLIENTNOTICE.
+
+command(cmd)
+Server::command(cmd)
+Window::command(cmd)
+Windowitem::command(cmd)
+  Send a command `cmd' (in current channel). This will work just as if you
+  had typed `cmd' in command line, so you'll need to use /COMMANDS or the
+  text will be sent to the channel.
+
+  Just like above, except different calling method.
+
+
+ *** Themes
+
+You can have user configurable texts in scripts that work just like
+irssi's internal texts that can be changed in themes.
+
+First you'll have to register the formats:
+
+Irssi::theme_register([
+  'format_name', '{hilight my perl format!}',
+  'format2', 'testing.. nick = $0, channel = $1'
+]);
+
+Printing happens with one of the functions:
+
+printformat(level, format, ...)
+Window::printformat(level, format, ...)
+Server::printformat(target, level, format, ...)
+Windowitem::printformat(level, format, ...)
+
+For example:
+
+  $channel->printformat(MSGLEVEL_CRAP, 'format2',
+                       'nick', $channel->{name});
+
+
+ *** Settings
+
+settings_get_str(key)
+settings_get_int(key)
+settings_get_bool(key)
+  Return value for setting.
+
+settings_add_str(section, key, def)
+settings_add_int(section, key, def)
+settings_add_bool(section, key, def)
+  Create new setting.
+
+settings_remove(key)
+  Remove a setting.
+
+
+ *** Signals
+
+signal_emit(signal, ...)
+  Send signal `signal'. You can give 6 parameters at maximum.
+
+signal_add(signal, func)
+  Bind `signal' to function `func'.
+
+signal_add_first(signal, func)
+  Bind `signal' to function `func'. Call `func' as soon as possible.
+
+signal_add_last(signal, func)
+  Bind `signal' to function `func'. Call `func' as late as possible.
+
+signal_remove(signal, func)
+  Unbind `signal' from function `func'.
+
+signal_stop()
+  Stop the signal that's currently being emitted.
+
+signal_stop_by_name(signal)
+  Stop the signal with name `signal' that's currently being emitted.
+
+
+  *** timeouts / IO listener
+
+timeout_add(msecs, func, data)
+  Call `func' every `msecs' milliseconds (1000 = 1 second) with
+  parameter `data'. Returns tag which can be used to stop the timeout.
+
+timeout_remove(tag)
+  Remove timeout with tag.
+
+input_add(source, condition, func, data)
+  Call `func' with parameter `data' when specified IO happens.
+  `source' is the file handle that is being listened. `condition' can
+  be INPUT_READ, INPUT_WRITE or both. Returns tag which can be used to
+  remove the listener.
+
+input_remove(tag)
+  Remove listener with tag.
+
+
+ *** Message levels
+
+level2bits(level)
+  Level string -> number
+
+bits2level(bits)
+  Level number -> string
+
+combine_level(level, str)
+  Combine level number to level string ("+level -level").
+  Return new level number.
+
+
+ *** Commands
+
+Command->{}
+  cmd - Command name
+  category - Category
+
+command_bind(cmd, func[, category])
+  Bind command `cmd' to call function `func'. `category' is the
+  category where the command is displayed in /HELP.
+
+command_runsub(cms, data, server, item)
+  Run subcommands for `cmd'. First word in `data' is parsed as
+  subcommand. `server' is Irssi::Server rec for current
+  Irssi::Windowitem `item'.
+  
+  Call command_runsub in handler function for `cmd' and bind
+  with command_bind("`cmd' `subcmd'", subcmdfunc[, category]);
+
+command_unbind(cmd, func)
+  Unbind command `cmd' from function 'func.
+
+
+ *** Windows
+
+UI::Window->{}
+  refnum - Reference number
+  name - Name
+
+  width - Width
+  height - Height
+
+  history_name - Name of named historylist for this window
+
+  active - Active window item
+  active_server - Active server
+
+  servertag - active_server must be either undef or have this same tag
+              (unless there's items in this window). This is used by
+             /WINDOW SERVER -sticky
+  level - Current window level
+
+  sticky_refnum - 1 if reference number is sticky
+
+  data_level - Current data level
+  hilight_color - Current activity hilight color
+
+  last_timestamp - Last time timestamp was written in window
+  last_line - Last time text was written in window
+
+  theme_name - Active theme in window, undef = default
+
+UI::TextDest->{}
+  window - Window where the text will be written
+  server - Target server
+  target - Target channel/query/etc name
+  level - Text level
+
+  hilight_priority - Priority for the hilighted text
+  hilight_color - Color for the hilighted text
+
+
+Window::items()
+  Return a list of items in window.
+
+Window
+window_create(automatic)
+Windowitem::window_create(automatic)
+  Create a new window.
+
+Window::destroy()
+  Destroy the window.
+
+Irssi::Window
+Windowitem::window()
+  Returns parent window for window item.
+
+Window
+window_find_name(name)
+  Find window with name.
+
+Window
+window_find_refnum(refnum)
+  Find window with reference number.
+
+Window
+window_find_level(level)
+Server::window_find_level(level)
+  Find window with level.
+
+Window
+window_find_closest(name, level)
+Server::window_find_closest(name, level)
+  Find window that matches best to given arguments. `name' can be either
+  window name or name of one of the window items.
+
+Window
+window_find_item(name)
+Server::window_find_item(name)
+  Find window which contains window item with specified name/server.
+
+Windowitem
+window_item_find(name)
+Server::window_item_find(name)
+Window::item_find(server, name)
+  Find window item that matches best to given arguments.
+
+window_refnum_prev(refnum, wrap)
+window_refnum_next(refnum, wrap)
+  Return refnum for window that's previous/next in windows list.
+
+windows_refnum_last()
+  Return refnum for last window.
+
+Window::item_add(item, automatic)
+Window::item_remove(item)
+Window::item_destroy(item)
+  Add/remove/destroy window item
+
+Window::set_active()
+  Set window active.
+
+Window::change_server(server)
+Window::set_refnum(refnum)
+Window::set_name(name)
+Window::set_history(name)
+Window::set_level(level)
+  Change server/refnum/name/history/level in window.
+
+Windowitem::set_active()
+  Change window item active in parent window.
+
+Window::item_prev()
+Window::item_next()
+  Change to previous/next window item.
+
+Windowitem::change_server(server)
+  Change server in window item.
+
+Windowitem::is_active()
+  Returns 1 if window item is the active item in parent window.
+
+Window::get_active_name()
+  Return active item's name, or if none is active, window's name
+
+
+ *** Server Connects
+
+Connect->{}
+  type - "SERVER CONNECT" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  address - Address where we connected (irc.blah.org)
+  port - Port where we connected
+  chatnet - Chat network
+
+  password - Password we used in connection.
+  wanted_nick - Nick which we would prefer to use
+  username - User name
+  realname - Real name
+
+Connect
+server_create_conn(address[, port=6667[, password=''[, nick=''[, channels='']]]])
+  Create new server connection.
+
+
+ *** Server functions
+
+Server->{}
+  type - "SERVER" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Connect above..)
+
+  connect_time - Time when connect() to server finished
+  real_connect_time - Time when server sent "connected" message
+
+  tag - Unique server tag
+  nick - Current nick
+
+  connected - Is connection finished? 1|0
+  connection_lost - Did we lose the connection (1) or was
+                    the connection just /DISCONNECTed (0)
+
+  rawlog - Rawlog object for the server
+
+  version - Server version
+  last_invite - Last channel we were invited to
+  server_operator - Are we server operator (IRC op) 1|0
+  usermode_away - Are we marked as away? 1|0
+  away_reason - Away reason message
+  banned - Were we banned from this server? 1|0
+  lag - Current lag to server in milliseconds
+
+Server
+Connect::connect()
+  Connect to server.
+
+Server::disconnect()
+  Disconnect from server.
+
+Server
+server_find_tag(tag)
+  Find server with tag
+
+Server
+server_find_chatnet(chatnet)
+  Find first server that is in `chatnet'
+
+Server::isnickflag(flag)
+  Returns 1 if flag is a nick mode flag (@, + or % in IRC)
+
+Server::ischannel(data)
+  Returns 1 if start of `data' seems to mean channel.
+
+Server::get_nick_flags()
+  Returns nick flag characters in order: op, voice, halfop ("@+%" in IRC).
+
+Server::send_message(target, msg)
+  Sends a message to nick/channel.
+
+
+ *** Server reconnections
+
+Reconnect->{}
+  type - "RECONNECT" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Connect above..)
+
+  tag - Unique numeric tag
+  next_connect - Unix time stamp when the next connection occurs
+
+
+ *** Chat networks
+
+Chatnet->{}
+  type - "CHATNET" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  name - name of chat network
+
+  nick - if not empty, nick preferred in this network
+  username - if not empty, username preferred in this network
+  realname - if not empty, realname preferred in this network
+
+  own_host - address to use when connecting this network
+  autosendcmd - command to send after connecting to this network
+
+chatnet_find(name)
+  Find chat network with name.
+
+
+ *** Server redirections
+
+This is a powerful feature of Irssi that I haven't seen in other IRC
+clients. You can EASILY grab the server's reply for a command you send
+to server without any horrible kludges.
+
+redirect_register(command, remote, timeout, start, stop, opt)
+   Register new redirection command. By default irssi has already
+   registered at least: whois, whowas, who, list, ison, userhost, ping,
+   "mode channel" (/MODE #channel), "mode b" (/MODE #channel b), "mode e"
+   and "mode I".
+
+   `command' specifies the name of the command to register, it doesn't
+   have to be a real command name, but something you just specify to
+   redirect_event() when using this redirection.
+
+   `remote' specifies if the command is by default a remote command
+   (eg. sent to another server). redirect_event() may override this.
+
+   `timeout' - If remote is TRUE, specifies how many seconds to wait for
+   reply before aborting.
+
+   `start', `stop', `opt' - hash references with "event" => argpos entries.
+   List of events that start and stop this redirection.
+   Start event list may be empty, but there must be at least one
+   stop event. Optional events are checked only if they are received
+   immediately after one of the stop-events. `argpos' specifies the
+   word number in event string which is compared to wanted argument,
+   -1 = don't compare, TRUE always.
+
+  Example (already done by irssi):
+
+  Irssi::redirect_register('mode channel', 0, 0,
+       undef, # no start events
+       { # stop events
+         "event 324" => 1, # MODE-reply
+         "event 403" => 1, # no such channel
+         "event 442" => 1, # "you're not on that channel"
+         "event 479" => 1  # "Cannot join channel (illegal name)"
+       }, { # optional events
+         "event 329", 1 # Channel create time
+       } );
+
+Server::redirect_event(command, count, arg, remote, failure_signal, signals)
+   Specify that the next command sent to server will be redirected.
+   NOTE: This command MUST be called before sending the command to server.
+
+   `command' - Name of the registered redirection that we're using.
+
+   `count' - How many times to execute the redirection. Some commands may
+   send multiple stop events, like MODE #a,#b.
+
+   `arg' - The argument to be compared in event strings. You can give multiple
+   arguments separated with space.
+
+   `remote' - Specifies if the command is a remote command, -1 = use default.
+
+   `failure_signal' - If irssi can't find the stop signal for the redirection,
+   this signal is called.
+
+   `signals' - hash reference with "event" => "redir signal" entries.
+   If the event is "", all the events belonging to the redirection but not
+   specified here, will be sent there.
+
+  Example:
+
+  # ignore all events generated by whois query, except 311.
+  $server->redirect_event("whois", 1, "cras", 0, undef, {
+                         "event 311" => "redir whois",
+                         "" => "event empty" });
+  $server->send_raw("WHOIS :cras");
+
+
+ *** Window items
+
+Windowitem->{}
+  type - Type of the window item, for example "CHANNEL" or "QUERY"
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  server - Active server for item
+  name - Name of the item
+
+  createtime - Time the window item was created
+  data_level - 0=no new data, 1=text, 2=msg, 3=highlighted text
+  hilight_color - Color of the last highlighted text
+
+
+ *** Channels
+
+Channel->{}
+  type - "CHANNEL" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Windowitem above..)
+
+  topic - Channel topic
+  topic_by - Nick who set the topic
+  topic_time - Timestamp when the topic was set
+
+  no_modes - Channel is modeless
+  mode - Channel mode
+  limit - Max. users in channel (+l mode)
+  key - Channel key (password)
+
+  chanop - You are channel operator
+  names_got - /NAMES list has been received
+  wholist - /WHO list has been received
+  synced - Channel is fully synchronized
+
+  joined - JOIN event for this channel has been received
+  left - You just left the channel (for "channel destroyed" event)
+  kicked - You was just kicked out of the channel (for
+           "channel destroyed" event)
+
+Server::channels_join(channels, automatic)
+  Join to channels in server. `channels' may also contain keys for
+  channels just like with /JOIN command. `automatic' specifies if this
+  channel was joined "automatically" or if it was joined because join
+  was requested by user. If channel join is "automatic", irssi doesn't
+  jump to the window where the channel was joined.
+
+Channel
+Server::channel_create(name, automatic)
+  Create new channel.
+
+Channel
+channel_create(chat_type, name, automatic)
+  Create new channel with specified chat type.
+  FIXME: should this be removed? is this useful for anything?
+
+Channel::destroy()
+  Destroy channel.
+
+Channel
+channel_find(channel)
+  Find channel from any server.
+
+Channel
+Server::channel_find(channel)
+  Find channel from specified server.
+
+
+ *** Nick list
+
+Nick->{}
+  type - "NICK" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  nick - Plain nick
+  host - Host address
+  realname - Real name
+  hops - Hop count to the server the nick is using
+
+  gone, serverop - User status, 1 or 0
+  op, voice, halfop - Channel status, 1 or 0
+
+  last_check - timestamp when last checked gone/ircop status.
+  send_massjoin - Waiting to be sent in a "massjoin" signal, 1 or 0
+
+Nick
+Channel::nick_insert(nick, op, voice, send_massjoin)
+  Add nick to nicklist.
+
+Channel::nick_remove(nick)
+  Remove nick from nicklist.
+
+Nick
+Channel::nick_find(mask)
+  Find nick from nicklist.
+
+Channel::nicks(channel)
+  Return a list of all nicks in channel.
+
+Server::nicks_get_same(nick)
+  Return all nick objects in all channels in server. List is in format:
+  Channel, Nick, Channel, ...
+
+
+ *** Queries
+
+Query->{}
+  type - "QUERY" text
+  chat_type - String ID of chat protocol, for example "IRC"
+
+  (..contains all the same data as Windowitem above..)
+
+  address - Host address of the queries nick
+  server_tag - Server tag used for this nick (doesn't get erased if
+               server gets disconnected)
+  unwanted - 1 if the other side closed or some error occured (DCC chats)
+
+Query
+query_create(chat_type, server_tag, nick, automatic)
+  Create a new query.
+
+Query::destroy()
+  Destroy the query.
+
+Query::query_change_server(server)
+  Change the active server of the query.
+
+Query
+query_find(nick)
+  Find query from any server.
+
+Query
+Server::query_find(nick)
+  Find query from specified server.
+
+
+ *** Masks
+
+You should use the Server version of the function if possible, since
+with different chat protocols the mask matching could be different.
+
+mask_match(mask, nick, user, host)
+Server::mask_match(mask, nick, user, host)
+  Return 1 if `mask' matches nick!user@host.
+
+mask_match_address(mask, nick, address)
+Server::mask_match_address(mask, nick, address)
+  Return 1 if `mask' matches nick!address.
+
+masks_match(masks, nick, address)
+Server::masks_match(masks, nick, address)
+  Return 1 if any mask in the `masks' (string separated with spaces)
+  matches nick!address.
+
+
+ *** Rawlog
+
+Rawlog->{}
+  logging - The rawlog is being written to file currently
+  nlines - Number of lines in rawlog
+
+Rawlog
+rawlog_create()
+  Create a new rawlog.
+
+Rawlog::destroy()
+  Destroy the rawlog.
+
+Rawlog::get_lines()
+  Returns all lines in rawlog.
+
+rawlog_set_size(lines)
+  Set the default rawlog size for new rawlogs.
+
+Rawlog::open(filename)
+  Start logging new messages in rawlog to specified file.
+
+Rawlog::close()
+  Stop logging to file.
+
+Rawlog::save(filename)
+  Save the current rawlog history to specified file.
+
+Rawlog::input(str)
+  Send `str' to raw log as input text.
+
+Rawlog::output(str)
+  Send `str' to raw log as output text.
+
+Rawlog::redirect(str)
+  Send `str' to raw log as redirection text.
+
+
+ *** Logging
+
+Log->{}
+  fname - Log file name
+  real_fname - The actual opened log file (after %d.%m.Y etc. are expanded)
+  opened - Log file is open
+  level - Log only these levels
+  last - Timestamp when last message was written
+  autoopen - Automatically open log at startup
+  failed - Opening log failed last time
+  temp - Log isn't saved to config file
+  items - List of log items
+
+Logitem->{}
+  type - 0=target, 1=window refnum
+  name - Name
+  servertag - Server tag
+
+Log
+log_create_rec(fname, level)
+  Create log file.
+
+Log::update()
+  Add log to list of logs / save changes to config file.
+
+Log
+log_find(fname)
+  Find log with file name.
+
+Log::close()
+  Destroy log file.
+
+Log::start_logging()
+  Open log file and start logging.
+
+Log::stop_logging()
+  Close log file.
+
+Log::item_add(type, name, server)
+  Add log item to log.
+
+Log::item_destroy(item)
+  Remove log item from log.
+
+Logitem
+Log::item_find(type, item, server)
+  Find item from log.
+
+
+ *** Ignores
+
+Ignore->{}
+  mask - Ignore mask
+  servertag - Ignore only in server
+  channels - Ignore only in channels (list of names)
+  pattern - Ignore text pattern
+
+  level - Ignore level
+
+  exception - This is an exception ignore
+  regexp - Regexp pattern matching
+  fullword - Pattern matches only full words
+
+ignore_add_rec(ignore)
+  Add ignore record.
+
+ignore_update_rec(ignore)
+  Update ignore record in configuration
+
+ignore_check(nick, host, channel, text, level)
+Server::ignore_check(nick, host, channel, text, level)
+  Return 1 if ignoring matched.
+
+
+ ***
+ *** IRC specific functions. All objects below this are prefixed with Irc::
+ ***
+
+ *** IRC servers
+
+Irc::Server->{}
+  (..contains all the same data as core Server object..)
+  real_address - Address the IRC server gives
+  usermode - User mode in server
+  userhost - Your user host in server
+
+Irc::Connect->{}
+  (..contains all the same data as core Connect object..)
+  alternate_nick - Alternate nick to use if default nick is taken.
+
+Connect::connect()
+  Connect to IRC server.
+
+Server::get_channels(server)
+  Return a string of all channels (and keys, if any have them) in server,
+  like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g"
+
+Server::send_raw(cmd)
+  Send raw message to server, it will be flood protected so you
+  don't need to worry about it.
+
+Server::send_raw_now(cmd)
+  Send raw message to server immediately without flood protection.
+
+Server::send_raw_split(cmd, nickarg, max_nicks)
+  Split the `cmd' into several commands so `nickarg' argument has only
+  `max_nicks' number of nicks.
+
+  Example:
+    $server->send_raw_split("KICK #channel nick1,nick2,nick3 :byebye", 3, 2);
+
+  Irssi will send commands "KICK #channel nick1,nick2 :byebye" and
+  "KICK #channel nick3 :byebye" to server.
+
+Server::ctcp_send_reply(data)
+  Send CTCP reply. This will be "CTCP flood protected" so if there's too
+  many CTCP requests in buffer, this reply might not get sent. The data
+  is the full raw command to be sent to server, like
+    "NOTICE nick :\001VERSION irssi\001"
+
+
+ *** IRC channels
+
+Ban->{}
+  ban - The ban
+  setby - Nick of who set the ban
+  time - Timestamp when ban was set
+
+Channel
+Server::channel_create(name, automatic)
+  Create new channel.
+
+Channel::bans()
+  Return a list of bans in channel.
+
+Channel::ebans()
+  Return a list of ban exceptions in channel.
+
+Channel::invites()
+  Return invite list (+I) of channel.
+
+Channel::ban_get_mask(nick)
+  Get ban mask for `nick'.
+
+Channel::banlist_add(ban, nick, time)
+   Add a new ban to channel.
+
+Channel::banlist_remove(ban)
+   Remove a ban from channel.
+
+Channel::banlist_exception_add(ban, nick, time)
+   Add a new ban exception to channel.
+
+Channel::banlist_exception_remove(ban)
+   Remove a ban exception from channel.
+
+Channel::invitelist_add(mask)
+   Add a new invite mask to channel.
+
+Channel::invitelist_remove(mask)
+   Remove invite mask from channel.
+
+modes_join(old, mode, channel)
+  Add `mode' to `old' - return newly allocated mode. If `channel' is 1,
+  we're parsing channel mode and we should try to join mode arguments too.
+
+
+ *** DCC
+
+Dcc->{}
+  type - Type of the DCC: chat, send, get
+  orig_type - Original DCC type that was sent to us - same as type except
+              GET and SEND are swapped
+  created - Time stamp when the DCC record was created
+
+  server - Server record where the DCC was initiated.
+  servertag - Tag of the server where the DCC was initiated.
+  mynick - Our nick to use in DCC chat.
+  nick - Other side's nick name.
+
+  chat - Dcc chat record if the request came through DCC chat
+  target - Who the request was sent to - your nick, channel or empty
+           if you sent the request
+  arg - Given argument .. file name usually
+
+  addr - Other side's IP address.
+  port - Port we're connecting in.
+
+  starttime - Unix time stamp when the DCC transfer was started
+  transfd - Bytes transferred
+
+Dcc::Chat->{}
+  id - Unique identifier - usually same as nick
+  mirc_ctcp - Send CTCPs without the CTCP_MESSAGE prefix
+  connection_lost - Other side closed connection
+
+Dcc::Get->{}
+  (..contains all the same data as core Dcc object..)
+  size - File size
+  skipped - Bytes skipped from start (resuming file)
+
+  get_type - What to do if file exists? 0=default, 1=rename, 2=overwrite,
+             3=resume
+  file - The real file name which we use.
+  file_quoted - 1 if file name was received quoted ("file name")
+
+Dcc::Send->{}
+  (..contains all the same data as core Dcc object..)
+  size - File size
+  skipped - Bytes skipped from start (resuming file)
+
+  file_quoted - 1 if file name was received quoted ("file name")
+  waitforend - File is sent, just wait for the replies from the other side
+  gotalldata - Got all acks from the other end
+
+
+dccs() - return list of all dcc connections
+
+Dcc::destroy()
+  Destroy DCC connection.
+
+Dcc
+dcc_find_item(type, nick, arg)
+  Find DCC connection.
+
+Dcc
+dcc_find_by_port(nick, port)
+  Find DCC connection by port.
+
+Dcc
+Windowitem::get_dcc(item)
+  If `item' is a query of a =nick, return DCC chat record of nick.
+
+Dcc::chat_send(data)
+  Send `data' to dcc chat.
+
+Server::dcc_ctcp_message(target, notice, msg)
+Dcc::ctcp_message(target, notice, msg)
+  Send a CTCP message/notify to target.
+
+
+ *** Netsplits
+
+Netsplit->{}
+  nick - Nick
+  address - Nick's host
+  destroy - Timestamp when this record should be destroyed
+  server - Netsplitserver object
+  channels - list of channels (Netsplitchannel objects) the nick was in
+
+Netsplitserver->{}
+  server - The server nick was in
+  destserver - The other server where split occured.
+  count - Number of splits in server
+
+Netsplitchannel->{}
+  name - Channel name
+  nick - Nick object
+
+Netsplit
+Server::netsplit_find(nick, address)
+  Check if nick!address is on the other side of netsplit. Netsplit records
+  are automatically removed after 30 minutes (current default)..
+
+Nick
+Server::netsplit_find_channel(nick, address, channel)
+  Find nick record for nick!address in channel `channel'.
+
+
+ *** Notify list
+
+Notifylist->{}
+  mask - Notify nick mask
+  away_check - Notify away status changes
+  idle_check_time - Notify when idle time is reset and idle was bigger
+                    than this (seconds)
+  ircnets - List of ircnets (strings) the notify is checked
+
+notifies() - Return list of all notifies
+
+Notifylist
+notifylist_add(mask, ircnets, away_check, idle_check_time)
+  Add new item to notify list.
+
+notifylist_remove(mask)
+  Remove item from notify list.
+
+Notifylist
+notifylist_find(mask, ircnet)
+  Find notify.
+
+Server
+notifylist_ison(nick, serverlist)
+  Check if `nick' is in IRC. `serverlist' is a space separated
+  list of server tags. If it's empty string, all servers will be checked.
+
+Server::notifylist_ison_server(nick)
+  Check if `nick' is on IRC server.
+
+Notifylist::ircnets_match(ircnet)
+  Returns 1 if notify is checked in `ircnet'.
+
+
+ *** /EXEC processes
+
+Process->{}
+  id - ID for the process
+  name - Name for the process (if given)
+  args - The command that is being executed
+
+  pid - PID for the executed command
+  target - send text with /msg <target> ...
+  target_win - print text to this window
+
+  shell - start the program via /bin/sh
+  notice - send text with /notice, not /msg if target is set
+  silent - don't print "process exited with level xx"
diff --git a/apps/irssi/docs/proxy.txt b/apps/irssi/docs/proxy.txt
new file mode 100644 (file)
index 0000000..20e5171
--- /dev/null
@@ -0,0 +1,26 @@
+Irssi proxy usage:
+
+First you'll need to have the proxy module installed, either configure
+irssi with --with-proxy and do make install, or manually:
+
+  cd src/irc/proxy
+  make
+  mkdir ~/.irssi/modules
+  cp .libs/libproxy.so ~/.irssi/modules/
+
+In irssi, say:
+
+  /LOAD proxy
+
+You really should set some password for the proxy with:
+
+  /SET irssiproxy_password secret
+
+Then you'll need to configure the ports/ircnets the proxy listens in,
+something like:
+
+  /SET irssiproxy_ports ircnet=2777 efnet=2778 opn=2779
+
+There we have 3 different irc networks answering in 3 ports. Note that
+you'll have to make the correct /IRCNET ADD and /SERVER ADD commands to
+make it work properly.
diff --git a/apps/irssi/docs/signals.txt b/apps/irssi/docs/signals.txt
new file mode 100644 (file)
index 0000000..f32789f
--- /dev/null
@@ -0,0 +1,322 @@
+List of signals irssi emits - see design.txt for more information about
+signals.
+
+core
+----
+
+* Requires to work properly:
+
+ "gui exit"
+ "gui dialog", char *type, char *text
+ "send command", char *command, SERVER_REC, WI_ITEM_REC
+
+* Provides signals:
+
+chat-protocols.c:
+ "chat protocol created", CHAT_PROTOCOL_REC
+ "chat protocol updated", CHAT_PROTOCOL_REC
+ "chat protocol destroyed", CHAT_PROTOCOL_REC
+
+channels.c:
+ "channel created", CHANNEL_REC, int automatic
+ "channel destroyed", CHANNEL_REC
+
+chatnets.c:
+ "chatnet created", CHATNET_REC
+ "chatnet destroyed", CHATNET_REC
+
+commands.c:
+ "commandlist new", COMMAND_REC
+ "commandlist remove", COMMAND_REC
+ "error command", int err, char *cmd
+
+ "send command", char *args, SERVER_REC, WI_ITEM_REC
+ "send text", char *line, SERVER_REC, WI_ITEM_REC
+ "command "<cmd>, char *args, SERVER_REC, WI_ITEM_REC
+ "default command", char *args, SERVER_REC, WI_ITEM_REC
+
+ignore.c:
+ "ignore created", IGNORE_REC
+ "ignore destroyed", IGNORE_REC
+ "ignore changed", IGNORE_REC
+
+log.c:
+ "log new", LOG_REC
+ "log remove", LOG_REC
+ "log create failed", LOG_REC
+ "log locked", LOG_REC
+ "log started", LOG_REC
+ "log stopped", LOG_REC
+ "log rotated", LOG_REC
+ "log written", LOG_REC, char *line
+
+modules.c:
+ "module loaded", MODULE_REC, MODULE_FILE_REC
+ "module unloaded", MODULE_REC, MODULE_FILE_REC
+ "module error", int error, char *text, char *rootmodule, char *submodule
+
+nicklist.c:
+ "nicklist new", CHANNEL_REC, NICK_REC
+ "nicklist remove", CHANNEL_REC, NICK_REC
+ "nicklist changed", CHANNEL_REC, NICK_REC, char *old_nick
+ "nicklist host changed", CHANNEL_REC, NICK_REC
+ "nicklist gone changed", CHANNEL_REC, NICK_REC
+ "nicklist serverop changed", CHANNEL_REC, NICK_REC
+
+pidwait.c:
+ "pidwait", int pid, int status
+
+queries.c:
+ "query created", QUERY_REC, int automatic
+ "query destroyed", QUERY_REC
+ "query nick changed", QUERY_REC, char *orignick
+ "query address changed", QUERY_REC
+ "query server changed", QUERY_REC, SERVER_REC
+
+rawlog.c:
+ "rawlog", RAWLOG_REC, char *data
+
+server.c:
+ "server looking", SERVER_REC
+ "server connected", SERVER_REC
+ "server connecting", SERVER_REC, ulong *ip
+ "server connect failed", SERVER_REC
+ "server disconnected", SERVER_REC
+ "server quit", SERVER_REC, char *msg
+
+settings.c:
+ "setup changed"
+ "setup reread", char *fname
+ "setup saved", char *fname, int autosaved
+
+signal.c:
+
+ "signal", char *name, ...
+ "last signal", char *name, ...
+
+IRC core
+--------
+
+* Provides signals:
+
+bans.c:
+ "ban type changed", char *bantype
+
+channels, nicklist:
+ "channel joined", CHANNEL_REC
+ "channel wholist", CHANNEL_REC
+ "channel sync", CHANNEL_REC
+
+ "channel topic changed", CHANNEL_REC
+
+ctcp.c:
+
+ "ctcp msg", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp msg "<cmd>, SERVER_REC, char *args, char *nick, char *addr, char *target
+ "default ctcp msg", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp reply", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp reply "<cmd>, SERVER_REC, char *args, char *nick, char *addr, char *target
+ "default ctcp reply", SERVER_REC, char *args, char *nick, char *addr, char *target
+ "ctcp action", SERVER_REC, char *args, char *nick, char *addr, char *target
+
+irc-log.c:
+ "awaylog show", LOG_REC, int away_msgs, int filepos
+
+irc-nicklist.c:
+ "server nick changed", SERVER_REC
+
+irc-servers.c:
+ "event connected", SERVER_REC
+
+irc.c:
+
+ "server event", SERVER_REC, char *data, char *sender_nick, char *sender_address
+ "event "<cmd>, SERVER_REC, char *args, char *sender_nick, char *sender_address
+ "default event", SERVER_REC, char *data, char *sender_nick, char *sender_address
+
+ "server incoming", SERVER_REC, char *data
+
+(for perl parser..)
+ "redir "<cmd>, SERVER_REC, char *args, char *sender_nick, char *sender_address
+
+lag.c:
+ "server lag", SERVER_REC
+ "server lag disconnect", SERVER_REC
+
+massjoin.c:
+ "massjoin", CHANNEL_REC, GSList of NICK_RECs
+
+mode-lists.c:
+ "ban new", CHANNEL_REC, BAN_REC
+ "ban remove", CHANNEL_REC, BAN_REC
+
+modes.c:
+ "channel mode changed", CHANNEL_REC
+ "nick mode changed", CHANNEL_REC, NICK_REC
+ "user mode changed", SERVER_REC, char *old
+ "away mode changed", SERVER_REC
+
+netsplit.c:
+ "netsplit server new", SERVER_REC, NETSPLIT_SERVER_REC
+ "netsplit server remove", SERVER_REC, NETSPLIT_SERVER_REC
+ "netsplit new", NETSPLIT_REC
+ "netsplit remove", NETSPLIT_REC
+
+IRC modules
+-----------
+
+* Provides signals:
+
+dcc*.c:
+
+ "dcc ctcp "<cmd>, char *args, DCC_REC
+ "default dcc ctcp", char *args, DCC_REC
+ "dcc unknown ctcp", char *args, char *sender, char *sendaddr
+
+ "dcc reply "<cmd>, char *args, DCC_REC
+ "default dcc reply", char *args, DCC_REC
+ "dcc unknown reply", char *args, char *sender, char *sendaddr
+
+ "dcc chat message", DCC_REC, char *msg
+
+ "dcc created", DCC_REC
+ "dcc destroyed", DCC_REC
+ "dcc connected", DCC_REC
+ "dcc rejecting", DCC_REC
+ "dcc closed", DCC_REC
+ "dcc request", DCC_REC, char *sendaddr
+ "dcc request send", DCC_REC
+ "dcc chat message", DCC_REC, char *msg
+ "dcc transfer update", DCC_REC
+ "dcc get receive", DCC_REC
+ "dcc error connect", DCC_REC
+ "dcc error file create", DCC_REC, char *filename
+ "dcc error file open", char *nick, char *filename, int errno
+ "dcc error get not found", char *nick
+ "dcc error send exists", char *nick, char *filename
+ "dcc error unknown type", char *type
+ "dcc error close not found", char *type, char *nick, char *filename
+
+autoignore.c:
+
+ "autoignore new", SERVER_REC, AUTOIGNORE_REC
+ "autoignore remove", SERVER_REC, AUTOIGNORE_REC
+
+flood.c:
+
+ "flood", SERVER_REC, char *nick, char *host, int level, char *target
+
+notifylist.c:
+
+ "notifylist new", NOTIFYLIST_REC
+ "notifylist remove", NOTIFYLIST_REC
+ "notifylist joined", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+ "notifylist away changed", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+ "notifylist unidle", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+ "notifylist left", SERVER_REC, char *nick, char *user, char *host, char *realname, char *awaymsg
+
+proxy/listen.c:
+
+ "proxy client connected", CLIENT_REC
+ "proxy client disconnected", CLIENT_REC
+
+FE common
+---------
+
+* Requires to work properly:
+
+ "gui print text", WINDOW_REC, int fg, int bg, int flags, char *text, int level
+
+(Can be used to determine when all "gui print text"s are sent (not required))
+ "gui print text finished", WINDOW_REC
+
+* Provides signals:
+
+completion.c:
+ "complete word", GList * of char*, WINDOW_REC, char *word, char *linestart, int *want_space
+
+fe-common-core.c:
+ "irssi init read settings"
+
+fe-exec.c:
+ "exec new", PROCESS_REC
+ "exec remove", PROCESS_REC, int status
+ "exec input", PROCESS_REC, char *text
+
+fe-messages.c:
+ "message public", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message private", SERVER_REC, char *msg, char *nick, char *address
+ "message own_public", SERVER_REC, char *msg, char *target
+ "message own_private", SERVER_REC, char *msg, char *target, char *orig_target
+ "message join", SERVER_REC, char *channel, char *nick, char *address
+ "message part", SERVER_REC, char *channel, char *nick, char *address, char *reason
+ "message quit", SERVER_REC, char *nick, char *address, char *reason
+ "message kick", SERVER_REC, char *channel, char *nick, char *kicker, char *address, char *reason
+ "message nick", SERVER_REC, char *newnick, char *oldnick, char *address
+ "message own_nick", SERVER_REC, char *newnick, char *oldnick, char *address
+ "message invite", SERVER_REC, char *channel, char *nick, char *address
+ "message topic", SERVER_REC, char *channel, char *topic, char *nick, char *address
+
+keyboard.c:
+ "keyinfo created", KEYINFO_REC
+ "keyinfo destroyed", KEYINFO_REC
+
+printtext.c:
+ "print text", TEXT_DEST_REC *dest, char *text, char *stripped
+
+themes.c:
+ "theme created", THEME_REC
+ "theme destroyed", THEME_REC
+
+window-activity.c:
+ "window hilight", WINDOW_REC
+ "window activity", WINDOW_REC, int old_level
+ "window item hilight", WI_ITEM_REC
+ "window item activity", WI_ITEM_REC, int old_lvel
+
+window-items.c:
+ "window item new", WINDOW_REC, WI_ITEM_REC
+ "window item remove", WINDOW_REC, WI_ITEM_REC
+ "window item changed", WINDOW_REC, WI_ITEM_REC
+ "window item server changed", WINDOW_REC, WI_ITEM_REC
+
+windows.c:
+ "window created", WINDOW_REC
+ "window destroyed", WINDOW_REC
+ "window changed", WINDOW_REC, WINDOW_REC old
+ "window changed automatic", WINDOW_REC
+ "window server changed", WINDOW_REC, SERVER_REC
+ "window refnum changed", WINDOW_REC, int old
+ "window name changed", WINDOW_REC
+ "window history changed", WINDOW_REC, char *oldname
+ "window level changed", WINDOW_REC
+
+FE IRC
+------
+
+fe-irc-messages.c:
+ "message irc op_public", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message irc own_wall", SERVER_REC, char *msg, char *target
+ "message irc own_action", SERVER_REC, char *msg, char *target
+ "message irc action", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message irc own_notice", SERVER_REC, char *msg, char *target
+ "message irc notice", SERVER_REC, char *msg, char *nick, char *address, char *target
+ "message irc own_ctcp", SERVER_REC, char *cmd, char *data, char *target
+ "message irc ctcp", SERVER_REC, char *msg, char *nick, char *address, char *target
+
+dcc/fe-dcc-chat-messages.c:
+ "message dcc own", DCC_REC *dcc, char *msg
+ "message dcc own_action", DCC_REC *dcc, char *msg
+ "message dcc own_ctcp", DCC_REC *dcc, char *cmd, char *data
+ "message dcc", DCC_REC *dcc, char *msg
+ "message dcc action", DCC_REC *dcc, char *msg
+ "message dcc ctcp", DCC_REC *dcc, char *cmd, char *data
+
+Text FE
+-------
+
+gui-printtext.c:
+ "beep"
+
+statusbar-items.c:
+ "mail counter"
diff --git a/apps/irssi/docs/special_vars.txt b/apps/irssi/docs/special_vars.txt
new file mode 100644 (file)
index 0000000..c56c452
--- /dev/null
@@ -0,0 +1,113 @@
+NOTE: This is just a slightly modified file taken from EPIC's help.
+
+Special Variables and Expandos
+
+Irssi supports a number of reserved, dynamic variables, sometimes
+referred to as expandos.  They are special in that the client is
+constantly updating their values automatically.  There are also
+numerous variable modifiers available.
+
+   Modifier          Description
+   $variable         A normal variable, expanding to the first match of:
+                     |  1) an internal SET variable
+                     |  2) an environment variable
+   $[num]variable    Expands to the variables value, with 'num' width.  If
+                     | the number is negative, the value is right-aligned.
+                     | The value is padded to meet the width with the
+                     | character given after number (default is space).
+                     | The value is truncated to specified width unless
+                     | '!' character precedes the number. If '.' character
+                     | precedes the number the value isn't padded, just
+                     | truncated.
+   $#variable        Expands to the number of words in $variable. If $variable
+                     | is omitted, it assumes $*
+   $@variable        Expands to the number of characters in $variable. if
+                     | $variable is omitted, it assumes $*
+   $($subvariable)   This is somewhat similar to a pointer, in that the
+                     | value of $subvar is taken as the name of the
+                     | variable to expand to.  Nesting is allowed.
+   ${expression}     Permits the value to be embedded in another string
+                     | unambiguously.
+   $!history!        Expands to a matching entry in the client's command
+                     | history, wildcards allowed.
+
+Whenever an alias is called, these expandos are set to the arguments passed
+to it.  If none of these expandos are used in the alias, or the $() form
+shown above, any arguments passed will automatically be appended to the last
+command in the alias.
+
+   Expando   Description
+   $*        expands to all arguments passed to an alias
+   $n        expands to argument 'n' passed to an alias (counting from zero)
+   $n-m      expands to arguments 'n' through 'm' passed to an alias
+   $n-       expands to all arguments from 'n' on passed to an alias
+   $-m       expands to all arguments up to 'm' passed to an alias
+   $~        expands to the last argument passed to an alias
+
+These variables are set and updated dynamically by the client.  The case of
+$A .. $Z is important.
+
+   Variable   Description
+   $,         last person who sent you a MSG
+   $.         last person to whom you sent a MSG
+   $:         last person to join a channel you are on
+   $;         last person to send a public message to a channel you are on
+   $A         text of your AWAY message, if any
+   $B         body of last MSG you sent
+   $C         current channel
+   $D         last person that NOTIFY detected a signon for
+   $E         idle time
+   $F         time client was started, $time() format
+   $H         current server numeric being processed
+   $I         channel you were last INVITEd to
+   $J         client version text string
+   $K         current value of CMDCHARS
+   $L         current contents of the input line
+   $M         modes of current channel, if any
+   $N         current nickname
+   $O         value of STATUS_OPER if you are an irc operator
+   $P         if you are a channel operator in $C, expands to a '@'
+   $Q         nickname of whomever you are QUERYing
+   $R         version of current server
+   $S         current server name
+   $T         target of current input (channel or nick of query)
+   $U         value of cutbuffer
+   $V         client release date (format YYYYMMDD)
+   $W         current working directory
+   $X         your /userhost $N address (user@host)
+   $Y         value of REALNAME
+   $Z         time of day (hh:mm, can be changed with /SET timestamp_format)
+   $$         a literal '$'
+
+   $versiontim          prints time of the irssi version in HHMM format
+   $sysname            system name (eg. Linux)
+   $sysrelease         system release (eg. 2.2.18)
+   $sysarch            system architecture (eg. i686)
+   $topic              channel topic
+   $usermode           user mode
+   $cumode             own channel user mode
+   $cumode_space       like $cumode, but gives space if there's no mode.
+   $tag                        server tag
+   $chatnet            chat network of server
+   $winref             window reference number
+   $winname            window name
+
+For example, assume you have the following alias:
+
+   alias blah msg $D Hi there!
+
+If /blah is passed any arguments, they will automatically be appended to the
+MSG text.  For example:
+
+   /blah oops                          /* command as entered */
+   "Hi there! oops"                    /* text sent to $D */
+
+Another useful form is ${}.  In general, variables can be embedded inside
+strings without problems, assuming the surrounding text could not be
+misinterpreted as part of the variable name.  This form guarantees that
+surrounding text will not affect the expression's return value.
+
+   /eval echo foo$Nfoo                 /* breaks, looks for $nfoo */
+   /eval echo foo${N}foo               /* ${N} returns current nickname */
+   fooYourNickfoo                      /* returned by above command */
+
index e0af3492013511476fad83598b33112b5c86f8b1..dd8f214891eca9f02a0a2640f3cb4ebd581bb182 100644 (file)
@@ -34,7 +34,8 @@
     <li>How can I save all texts in a window to file?</li>
     </ul></li>
 <li><a href="#c8">Logging</a></li>
-<li><a href="#c9">Irssi's settings</a></li>
+<li><a href="#c9">Proxies and IRC bouncers</a></li>
+<li><a href="#c10">Irssi's settings</a></li>
 </ol>
 
 <h3><a id="c1">1. For all the lazy people</a></h3>
@@ -83,19 +84,29 @@ to bot after joined to efnet/#irssi:</p>
 
 <pre>
      /CHANNEL ADD -auto #irssi ircnet
-     /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
                  #irssi efnet
 </pre>
 
+If you want lines containing your nick to hilight:
+
+<pre>
+     /HILIGHT nick
+</pre>
+
 <h3><a id="c2">2. Basic user interface usage</a></h3>
 
+<p>Windows can be scrolled up/down with PgUp and PgDown keys. If they don't
+work for you, use Meta-p and Meta-n keys. For jumping to beginning or end of
+the buffer, use /SB HOME and /SB END commands.</p>
+
 <p>By default, irssi uses "hidden windows" for everything. Hidden
 window is created every time you /JOIN a channel or /QUERY someone.
 There's several ways you can change between these windows:</p>
 
 <pre>
      Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
-     Meta-q .. Meta-p          - Jump directly between windows 11-20
+     Meta-q .. Meta-o          - Jump directly between windows 11-19
      /WINDOW &lt;number&gt;          - Jump to any window with specified number
      Ctrl-P, Ctrl-N            - Jump to previous / next window
 </pre>
@@ -117,10 +128,16 @@ want to use ALT instead of Windows key for it, use:</p>
      rxvt*modifier: alt
 </pre>
 
+<p>You could do this by changing the X key mappings:</p>
+
+<pre>
+    xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+</pre>
+
 <p>And how exactly do you set these X resources? For Debian, there's
 /etc/X11/Xresources/xterm file where you can put them and it's read 
 automatically when X starts. ~/.Xresources and ~/.Xdefaults files might also
-work. If you can't get anything else to work, just copy&paste those lines to
+work. If you can't get anything else to work, just copy&amp;paste those lines to
 ~/.Xresources and directly call "xrdb -merge ~/.Xresources" in some xterm.  
 The resources affect only the new xterms you start, not existing ones.</p>  
 
@@ -216,7 +233,7 @@ same network if the -auto server fails.</p>
 <p>And finally channels:</p>
 
 <pre>
-     /CHANNEL ADD -auto -bots *!*@bot@host.org -botcmd "/^msg $0 op pass"
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
                  #irssi efnet
      /CHANNEL ADD -auto #secret ircnet password
 </pre>
@@ -478,13 +495,99 @@ logs by adding date/time formats to the file name. The formats are in
 "man strftime" format. For example</p>
 
 <pre>
-     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
+     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
 </pre>
 
 <p>For logging only some specific channels or nicks, see /HELP log</p>
 
 
-<h3><a id="c9">9. Irssi's settings</a></h3>
+<h3><a id="c9">9. Proxies and IRC bouncers</a></h3>
+
+<p>Irssi supports connecting to IRC servers via a proxy. All proxies have
+these settings in common:</p>
+
+<pre>
+     /SET use_proxy ON
+     /SET proxy_address &lt;Proxy host address&gt;
+     /SET proxy_port &lt;Proxy port&gt;
+</pre>
+
+<p><strong>HTTP proxy</strong></p>
+
+<p>Use these settings with HTTP proxies:</p>
+
+<pre>
+     /SET -clear proxy_password
+     /EVAL SET proxy_string CONNECT %s:%d\n\n
+</pre>
+
+<p><strong>Irssi proxy</strong></p>
+
+<p>Irssi contains it's own proxy which you can build giving
+<strong>--with-proxy</strong> option to configure. You'll still need to run
+irssi in a screen to use it though.</p>
+
+<p>Irssi proxy is a bit different than most proxies, normally proxies create
+a new connection to IRC server when you connect to it, but with irssi proxy
+all the clients use the same IRC server connection (a bit like how screen -x
+works).</p>
+
+<p>Irssi proxy supports sharing multiple server connections in different
+ports, like you can share ircnet in port 2777 and efnet in port 2778.</p>
+
+<p>Usage in proxy side:</p>
+
+<pre>
+     /LOAD irc_proxy (/LOAD proxy in irssi 0.7.98.3 and older)
+     /SET irssiproxy_password &lt;password&gt;
+     /SET irssiproxy_ports &lt;ircnet&gt;=&lt;port&gt; ... (eg. ircnet=2777 efnet=2778)
+</pre>
+
+<p><strong>NOTE</strong>: you <strong>MUST</strong> add all the servers you
+are using to server and ircnet lists with /SERVER ADD and /IRCNET ADD.
+..Except if you really don't want to for some reason, and you only use
+one server connection, you may simply set:</p>
+
+<pre>
+     /SET irssiproxy_ports *=2777 (irssi 0.7.99 and later only)
+</pre>
+
+<p>Usage in client side:</p>
+
+<p>Just connect to the irssi proxy like it is a normal server with password
+specified in /SET irssiproxy_password. For example:</p>
+
+<pre>
+     /SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
+     /SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
+</pre>
+
+<p>Irssi proxy works fine with other IRC clients as well.</p>
+
+<p><strong>SOCKS</strong></p>
+
+Irssi can be compiled with socks support (<strong>--with-socks</strong>
+option to configure), but I don't really know how it works, if at all. /SET
+proxy settings don't have anything to do with socks however.
+
+<p><strong>Others</strong></p>
+
+<p>IRC bouncers usually work like IRC servers, and want a password. You can
+give it with:</p>
+
+<pre>
+     /SET proxy_password &lt;password&gt;
+</pre>
+
+<p>Irssi's default for connect string is</p>
+
+<pre>
+     /SET proxy_string CONNECT %s %d
+</pre>
+
+<p>which you can modify according to your bouncer's needs.</p>
+
+<h3><a id="c10">10. Irssi's settings</a></h3>
 
 <p>You probably don't like Irssi's default settings. I don't like them.
 But I'm still convinced that they're pretty good defaults. Here's some
@@ -603,7 +706,11 @@ of them you might want to change (the default value is shown):</p>
 
 <dt>/SET show_nickmode ON</dt>
   <dd>Show the nick's mode before nick in channels, ie. ops have
-  &lt;@nick&gt;, voices &lt;+nick&gt; and others &lt; nick&gt;</dd>
+  &lt;@nick&gt;, voices &lt;+nick&gt; and others &lt;&nbsp;nick&gt;</dd>
+
+<dt>/SET show_nickmode_empty ON</dt>
+  <dd>If the nick doesn't have a mode, use one space. ie. ON:
+  &lt;&nbsp;nick&gt;, OFF: &lt;nick&gt;</dd>
 
 <dt>/SET show_quit_once OFF</dt>
   <dd>Show quit message only once in some of the channel windows the
@@ -632,7 +739,7 @@ of them you might want to change (the default value is shown):</p>
   <dd>Show the number of mails in your mbox in status
   bar. The mbox file is taken from $MAIL environment setting. Only mbox
   format works for now.</dd>
-
+</dl>
 
 <p><strong>Nick completion</strong></p>
 
diff --git a/apps/irssi/docs/startup-HOWTO.txt b/apps/irssi/docs/startup-HOWTO.txt
new file mode 100644 (file)
index 0000000..cf98954
--- /dev/null
@@ -0,0 +1,598 @@
+Startup HOWTO
+
+  To new Irssi users (not to new IRC users ..)
+
+   Copyright (c) 2000-2001 by Timo Sirainen
+
+   Index with some FAQ questions that are answered in the chapter:
+    1. For all the lazy people
+    2. Basic user interface usage
+    3. Server and channel automation
+          + how do I automatically connect to servers at startup?
+          + how do I automatically join to channels at startup?
+    4. Setting up windows and automatically restoring them at startup
+    5. Status and msgs windows & message levels
+          + I want /WHOIS to print reply to current window
+          + I want all messages to go to one window, not create new
+            windows
+    6. How support for multiple servers works in irssi
+          + I connected to some server that doesn't respond and now irssi
+            keeps trying to reconnect to it again and again, how can I
+            stop it??
+          + I want to have own status and/or msgs window for each servers
+    7. /LASTLOG and jumping around in scrollback
+          + How can I save all texts in a window to file?
+    8. Logging
+    9. Proxies and IRC bouncers
+   10. Irssi's settings
+
+  1. For all the lazy people
+
+   These settings should give you pretty good defaults (the ones I use):
+
+   I don't like automatic query windows, I don't like status window, I do
+   like msgs window where all messages go:
+     /SET autocreate_own_query OFF
+     /SET autocreate_query_level DCCMSGS
+     /SET use_status_window OFF
+     /SET use_msgs_window ON
+
+   Disable automatic window closing when /PARTing channel or /UNQUERYing
+   query:
+     /SET autoclose_windows OFF
+     /SET reuse_unused_windows ON
+
+   And example how to add servers:
+
+   (openprojects network, identify with nickserv and wait for 2 seconds
+   before joining channels)
+     /IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -opn 2000" opn
+
+   Then add some servers to different networks (ircnet is already set up
+   for them), irc.kpnqwest.fi is used by default for IRCNet but if it
+   fails, irc.funet.fi is tried next:
+     /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+     /SERVER ADD -ircnet ircnet irc.funet.fi 6667
+     /SERVER ADD -auto -ircnet efnet efnet.cs.hut.fi 6667
+
+   Automatically join to channels after connected to server, send op
+   request to bot after joined to efnet/#irssi:
+     /CHANNEL ADD -auto #irssi ircnet
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
+                  #irssi efnet
+
+   If you want lines containing your nick to hilight:
+     /HILIGHT nick
+
+  2. Basic user interface usage
+
+   Windows can be scrolled up/down with PgUp and PgDown keys. If they
+   don't work for you, use Meta-p and Meta-n keys. For jumping to
+   beginning or end of the buffer, use /SB HOME and /SB END commands.
+
+   By default, irssi uses "hidden windows" for everything. Hidden window
+   is created every time you /JOIN a channel or /QUERY someone. There's
+   several ways you can change between these windows:
+     Meta-1, Meta-2, .. Meta-0 - Jump directly between windows 1-10
+     Meta-q .. Meta-o          - Jump directly between windows 11-19
+     /WINDOW <number>          - Jump to any window with specified number
+     Ctrl-P, Ctrl-N            - Jump to previous / next window
+
+   Clearly the easiest way is to use Meta-number keys. And what is the
+   Meta key? For some terminals, it's the same as ALT. If you have
+   Windows keyboard, it's probably the left Windows key. If they don't
+   work directly, you'll need to set a few X resources (NOTE: these work
+   with both xterm and rxvt):
+     XTerm*eightBitInput:   false
+     XTerm*metaSendsEscape: true
+
+   With rxvt, you can also specify which key acts as Meta key. So if you
+   want to use ALT instead of Windows key for it, use:
+     rxvt*modifier: alt
+
+   You could do this by changing the X key mappings:
+    xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+
+   And how exactly do you set these X resources? For Debian, there's
+   /etc/X11/Xresources/xterm file where you can put them and it's read
+   automatically when X starts. ~/.Xresources and ~/.Xdefaults files
+   might also work. If you can't get anything else to work, just
+   copy&paste those lines to ~/.Xresources and directly call "xrdb -merge
+   ~/.Xresources" in some xterm. The resources affect only the new xterms
+   you start, not existing ones.
+
+   Many windows SSH clients also don't allow usage of ALT. One excellent
+   client that does allow is putty, you can download it from
+   http://www.chiark.greenend.org.uk/~sgtatham/putty/.
+
+   Irssi also supports split windows, they've had some problems in past
+   but I think they should work pretty well now :) Here's some commands
+   related to them:
+     /WINDOW NEW                    - Create new split window
+     /WINDOW NEW HIDE               - Create new hidden window
+     /WINDOW CLOSE                  - Close split or hidden window
+
+     /WINDOW HIDE [<number>|<name>] - Make the split window hidden window
+     /WINDOW SHOW <number>|<name>   - Make the hidden window a split window
+
+     /WINDOW SHRINK [<lines>]       - Shrink the split window
+     /WINDOW GROW [<lines>]         - Grow the split window
+     /WINDOW BALANCE                - Balance the sizes of all split windows
+
+   By default, irssi uses "sticky windowing" for split windows. This
+   means that windows created inside one split window cannot be moved to
+   another split window without some effort. For example you could have
+   following window layout:
+     Split window 1: win#1 - Status window, win#2 - Messages window
+     Split window 2: win#3 - ircnet/#channel1, win#4 - ircnet/#channel2
+     Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
+
+   When you are in win#1 and press ALT-6, irssi jumps to split window #3
+   and moves the efnet/#channel2 the active window.
+
+   With non-sticky windowing the windows don't have any relationship with
+   split windows, pressing ALT-6 in win#1 moves win#6 to split window 1
+   and sets it active, except if win#6 was already visible in some other
+   split window irssi just changes to that split window. This it the way
+   windows work with ircii, if you prefer it you can set it with
+     /SET autostick_split_windows OFF
+
+   Each window can have multiple channels, queries and other "window
+   items" inside them. If you don't like windows at all, you disable
+   automatic creating of them with
+     /SET autocreate_windows OFF
+
+   If you want to group only some channels or queries in one window, use
+     /JOIN -window #channel
+     /QUERY -window nick
+
+  3. Server and channel automation
+
+   Irssi's multiple IRC network support is IMHO very good - at least
+   compared to other clients :) Even if you're only in one IRC network
+   you should group all your servers to be in the same IRC network as
+   this helps with reconnecting if your primary server breaks and is
+   probably useful in some other ways too :) For information how to
+   actually use irssi correctly with multiple servers see the chapter 6.
+
+   First you need to have your IRC network set, use /IRCNET command to
+   see if it's already there. If it isn't, use /IRCNET ADD yourircnet. To
+   make Irssi work properly with different IRC networks, you might need
+   to give some special settings to /IRCNET ADD, see manual.txt for more
+   information about them. Irssi defaults to IRCNet's behaviour.
+
+   After that you need to add your servers. For example:
+     /SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+     /SERVER ADD -auto -ircnet worknet irc.mycompany.com 6667 password
+
+   The -auto option specifies that this server is automatically connected
+   at startup. You don't need to make more than one server with -auto
+   option to one IRC network, other servers are automatically connected
+   in same network if the -auto server fails.
+
+   And finally channels:
+     /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass"
+                  #irssi efnet
+     /CHANNEL ADD -auto #secret ircnet password
+
+   -bots and -botcmd should be the only ones needing a bit of explaining.
+   They're used to send commands automatically to bot when channel is
+   joined, usually to get ops automatically. You can specify multiple bot
+   masks with -bots option separated with spaces (and remember to quote
+   the string then). The $0 in -botcmd specifies the first found bot in
+   the list. If you don't need the bot masks (ie. the bot is always with
+   the same nick, like chanserv) you can give only the -botcmd option and
+   the command is always sent.
+
+  4. Setting up windows and automatically restoring them at startup
+
+   First connect to all the servers, join the channels and create the
+   queries you want. If you want to move the windows or channels around
+   use commands:
+     /WINDOW MOVE LEFT/RIGHT/number    - move window elsewhere
+     /WINDOW ITEM MOVE <number>|<name> - move channel/query to another window
+
+   When everything looks the way you like, use /LAYOUT SAVE command (and
+   /SAVE, if you don't have autosaving enabled) and when you start irssi
+   next time, irssi remembers the positions of the channels, queries and
+   everything. This "remembering" doesn't mean that simply using /LAYOUT
+   SAVE would automatically make irssi reconnect to all servers and join
+   all channels, you'll need the /SERVER ADD -auto and /CHANNEL ADD -auto
+   commands to do that.
+
+   If you want to change the layout, you just rearrange the layout like
+   you want it and use /LAYOUT SAVE again. If you want to remove the
+   layout for some reason, use /LAYOUT RESET.
+
+  5. Status and msgs windows & message levels
+
+   By default, all the "extra messages" go to status window. This means
+   pretty much all messages that don't clearly belong to some channel or
+   query. Some people like it, some don't. If you want to remove it, use
+     /SET use_status_window OFF
+
+   This doesn't have any effect until you restart irssi. If you want to
+   remove it immediately, just /WINDOW CLOSE it.
+
+   Another common window is "messages window", where all private messages
+   go. By default it's disabled and query windows are created instead. To
+   make all private messages go to msgs window, say:
+     /SET use_msgs_window ON
+     /SET autocreate_query_level DCCMSGS  (or if you don't want queries to
+                                           dcc chats either, say NONE)
+
+   use_msgs_window either doesn't have any effect until restarting irssi.
+   To create it immediately say:
+     /WINDOW NEW HIDE     - create the window
+     /WINDOW NAME (msgs)  - name it to "(msgs)"
+     /WINDOW LEVEL MSGS   - make all private messages go to this window
+     /WINDOW MOVE 1       - move it to first window
+
+   Note that neither use_msgs_window nor use_status_window have any
+   effect at all if /LAYOUT SAVE has been used.
+
+   This brings us to message levels.. What are they? All messages that
+   irssi prints have one or more "message levels". Most common are PUBLIC
+   for public messages in channels, MSGS for private messages and CRAP
+   for all sorts of messages with no real classification. You can get a
+   whole list of levels with
+     /HELP levels
+
+   Status window has message level "ALL -MSGS", meaning that all
+   messages, except private messages, without more specific place go to
+   status window. The -MSGS is there so it doesn't conflict with messages
+   window.
+
+  6. How support for multiple servers works in irssi
+
+   ircii and several other clients support multiple servers by placing
+   the connection into some window. IRSSI DOES NOT. There is no required
+   relationship between window and server. You can connect to 10 servers
+   and manage them all in just one window, or join channel in each one of
+   them to one sigle window if you really want to. That being said,
+   here's how you do connect to new server without closing the old
+   connection:
+     /CONNECT irc.server.org
+
+   Instead of the /SERVER which disconnects the existing connection. To
+   see list of all active connections, use /SERVER without any
+   parameters. You should see a list of something like:
+     -!- IRCNet: irc.telia.fi:6667 (IRCNet)
+     -!- OPN: tolkien.openprojects.net:6667 (OPN)
+     -!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
+
+   Here you see that we're connected to IRCNet and OPN networks. The the
+   IRCNet at the beginning is called the "server tag" while the (IRCnet)
+   at the end shows the IRC network. Server tag specifies unique tag to
+   refer to the server, usually it's the same as the IRC network. When
+   the IRC network isn't known it's some part of the server name. When
+   there's multiple connections to same IRC network or server, irssi adds
+   a number after the tag so there could be ircnet, ircnet2, ircnet3 etc.
+
+   Server tags beginning with RECON- mean server reconnections. Above we
+   see that connection to server at 192.168.0.1 wasn't successful and
+   irssi will try to connect it again in 3 minutes.
+
+   To disconnect one of the servers, or to stop irssi from reconnecting,
+   use
+     /DISCONNECT ircnet   - disconnect server with tag "ircnet"
+     /DISCONNECT recon-1  - stop trying to reconnect to RECON-1 server
+     /RMRECONNS           - stop all server reconnections
+
+     /RECONNECT recon-1   - immediately try reconnecting back to RECON-1
+     /RECONNECT ALL       - immediately try reconnecting back to all
+                            servers in reconnection queue
+
+   Now that you're connected to all your servers, you'll have to know how
+   to specify which one of them you want to use. One way is to have an
+   empty window, like status or msgs window. In it, you can specify which
+   server to set active with
+     /WINDOW SERVER tag    - set server "tag" active
+     Ctrl-X                - set the next server in list active
+
+   When the server is active, you can use it normally. When there's
+   multiple connected servers, irssi adds [servertag] prefix to all
+   messages in non-channel/query messages so you'll know where it came
+   from.
+
+   Several commands also accept -servertag option to specify which server
+   it should use:
+     /MSG -tag nick message
+     /JOIN -tag #channel
+     /QUERY -tag nick
+
+   /MSG tab completion also automatically adds the -tag option when nick
+   isn't in active server.
+
+   Window's server can be made sticky. When sticky, it will never
+   automatically change to anything else, and if server gets
+   disconnected, the window won't have any active server. When the server
+   gets connected again, it is automatically set active in the window. To
+   set the window's server sticky use
+     /WINDOW SERVER -sticky tag
+
+   This is useful if you wish to have multiple status or msgs windows,
+   one for each server. Here's how to do them (repeat for each server)
+     /WINDOW NEW HIDE
+     /WINDOW NAME (status)
+     /WINDOW LEVEL ALL -MSGS
+     /WINDOW SERVER -sticky ircnet
+
+     /WINDOW NEW HIDE
+     /WINDOW NAME (msgs)
+     /WINDOW LEVEL MSGS
+     /WINDOW SERVER -sticky ircnet
+
+  7. /LASTLOG and jumping around in scrollback
+
+   /LASTLOG command can be used for searching texts in scrollback buffer.
+   Simplest usages are
+     /LASTLOG word     - print all lines with "word" in them
+     /LASTLOG word 10  - print last 10 occurances of "word"
+     /LASTLOG -topics  - print all topic changes
+
+   If there's more lines to be printed than 1000, irssi doesn't thinks
+   that you probably made some mistake and won't print them without
+   -force option. If you want to save the full lastlog to file, use
+     /LASTLOG -file ~/irc.log
+
+   With -file option you don't need -force even if there's more than 1000
+   lines. /LASTLOG has a lot of other options too, see /HELP lastlog for
+   details.
+
+   Once you've found the lines you were interested in, you might want to
+   check the discussion around them. Irssi has /SCROLLBACK (or alias /SB)
+   command for jumping around in scrollback buffer. Since /LASTLOG prints
+   the timestamp when the message was originally printed, you can use /SB
+   GOTO hh:mm to jump directly there. To get back to the bottom of
+   scrollback, use /SB END command.
+
+  8. Logging
+
+   Irssi can automatically log important messages when you're set away
+   (/AWAY reason). When you set yourself unaway (/AWAY), the new messages
+   in away log are printed to screen. You can configure it with:
+     /SET awaylog_level MSGS HILIGHT     - Specifies what messages to log
+     /SET awaylog_file ~/.irssi/away.log - Specifies the file to use
+
+   Easiest way to start logging with Irssi is to use autologging. With it
+   Irssi logs all channels and private messages to specified directory.
+   You can turn it on with
+     /SET autolog ON
+
+   By default it logs pretty much everything execept CTCPS or CRAP
+   (/WHOIS requests, etc). You can specify the logging level yourself
+   with
+     /SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
+
+   By default irssi logs to ~/irclogs/<servertag>/<target>.log. You can
+   change this with
+     /SET autolog_path ~/irclogs/$tag/$0.log (this is the default)
+
+   The path is automatically created if it doesn't exist. $0 specifies
+   the target (channel/nick). You can make irssi automatically rotate the
+   logs by adding date/time formats to the file name. The formats are in
+   "man strftime" format. For example
+     /SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
+
+   For logging only some specific channels or nicks, see /HELP log
+
+  9. Proxies and IRC bouncers
+
+   Irssi supports connecting to IRC servers via a proxy. All proxies have
+   these settings in common:
+     /SET use_proxy ON
+     /SET proxy_address <Proxy host address>
+     /SET proxy_port <Proxy port>
+
+   HTTP proxy
+
+   Use these settings with HTTP proxies:
+     /SET -clear proxy_password
+     /EVAL SET proxy_string CONNECT %s:%d\n\n
+
+   Irssi proxy
+
+   Irssi contains it's own proxy which you can build giving --with-proxy
+   option to configure. You'll still need to run irssi in a screen to use
+   it though.
+
+   Irssi proxy is a bit different than most proxies, normally proxies
+   create a new connection to IRC server when you connect to it, but with
+   irssi proxy all the clients use the same IRC server connection (a bit
+   like how screen -x works).
+
+   Irssi proxy supports sharing multiple server connections in different
+   ports, like you can share ircnet in port 2777 and efnet in port 2778.
+
+   Usage in proxy side:
+     /LOAD irc_proxy (/LOAD proxy in irssi 0.7.98.3 and older)
+     /SET irssiproxy_password <password>
+     /SET irssiproxy_ports <ircnet>=<port> ... (eg. ircnet=2777 efnet=2778)
+
+   NOTE: you MUST add all the servers you are using to server and ircnet
+   lists with /SERVER ADD and /IRCNET ADD. ..Except if you really don't
+   want to for some reason, and you only use one server connection, you
+   may simply set:
+     /SET irssiproxy_ports *=2777 (irssi 0.7.99 and later only)
+
+   Usage in client side:
+
+   Just connect to the irssi proxy like it is a normal server with
+   password specified in /SET irssiproxy_password. For example:
+     /SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
+     /SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
+
+   Irssi proxy works fine with other IRC clients as well.
+
+   SOCKS
+   Irssi can be compiled with socks support (--with-socks option to
+   configure), but I don't really know how it works, if at all. /SET
+   proxy settings don't have anything to do with socks however.
+
+   Others
+
+   IRC bouncers usually work like IRC servers, and want a password. You
+   can give it with:
+     /SET proxy_password <password>
+
+   Irssi's default for connect string is
+     /SET proxy_string CONNECT %s %d
+
+   which you can modify according to your bouncer's needs.
+
+  10. Irssi's settings
+
+   You probably don't like Irssi's default settings. I don't like them.
+   But I'm still convinced that they're pretty good defaults. Here's some
+   of them you might want to change (the default value is shown):
+
+   Queries
+
+   /SET autocreate_own_query ON
+          Should new query window be created when you send message to
+          someone (with /msg).
+
+   /SET autocreate_query_level MSGS
+          New query window should be created when receiving messages with
+          this level. MSGS, DCCMSGS and NOTICES levels work currently.
+          You can disable this with /SET -clear autocrate_query_level.
+
+   /SET autoclose_query 0
+          Query windows can be automatically closed after certain time of
+          inactivity. Queries with unread messages aren't closed and
+          active window is neither never closed. The value is given in
+          seconds.
+
+   Windows
+
+   /SET use_msgs_window OFF
+          Create messages window at startup. All private messages go to
+          this window. This only makes sense if you've disabled automatic
+          query windows. Message window can also be created manually with
+          /WINDOW LEVEL MSGS, /WINDOW NAME (msgs).
+
+   /SET use_status_window ON
+          Create status window at startup. All messages that don't really
+          have better place go here, like all /WHOIS replies etc. Status
+          window can also be created manually with /WINDOW LEVEL ALL
+          -MSGS, /WINDOW NAME (status).
+
+   /SET autocreate_windows ON
+          Should we create new windows for new window items or just place
+          everything in one window
+
+   /SET autoclose_windows ON
+          Should window be automatically closed when the last item in
+          them is removed (ie. /PART, /UNQUERY).
+
+   /SET reuse_unused_windows OFF
+          When finding where to place new window item (channel, query)
+          Irssi first tries to use already existing empty windows. If
+          this is set ON, new window will always be created for all
+          window items. This setting is ignored if autoclose_windows is
+          set ON.
+
+   /SET window_auto_change OFF
+          Should Irssi automatically change to automatically created
+          windows - usually queries when someone sends you a message. To
+          prevent accidentally sending text meant to some other
+          channel/nick, Irssi clears the input buffer when changing the
+          window. The text is still in scrollback buffer, you can get it
+          back with pressing arrow up key.
+
+   /SET print_active_channel OFF
+          When you keep more than one channel in same window, Irssi
+          prints the messages coming to active channel as "<nick> text"
+          and other channels as "<nick:channel> text". If this setting is
+          set ON, the messages to active channels are also printed in the
+          latter way.
+
+   /SET window_history OFF
+          Should command history be kept separate for each window.
+
+   User information
+
+   /SET nick
+          Your nick name
+
+   /SET alternate_nick
+          Your alternate nick.
+
+   /SET user_name
+          Your username, if you have ident enabled this doesn't affect
+          anything
+
+   /SET real_name
+          Your real name.
+
+   Server information
+
+   /SET skip_motd OFF
+          Should we hide server's MOTD (Message Of The Day).
+
+   /SET server_reconnect_time 300
+          Seconds to wait before connecting to same server again. Don't
+          set this too low since it usually doesn't help at all - if the
+          host is down, the few extra minutes of waiting won't hurt much.
+
+   /SET lag_max_before_disconnect 300
+          Maximum server lag in seconds before disconnecting and trying
+          to reconnect. This happens mostly only when network breaks
+          between you and IRC server.
+
+   Appearance
+
+   /SET timestamps ON
+          Show timestamps before each message.
+
+   /SET hide_text_style OFF
+          Hide all bolds, underlines, MIRC colors, etc.
+
+   /SET show_nickmode ON
+          Show the nick's mode before nick in channels, ie. ops have
+          <@nick>, voices <+nick> and others < nick>
+
+   /SET show_nickmode_empty ON
+          If the nick doesn't have a mode, use one space. ie. ON:
+          < nick>, OFF: <nick>
+
+   /SET show_quit_once OFF
+          Show quit message only once in some of the channel windows the
+          nick was in instead of in all windows.
+
+   /SET topicbar ON
+          Show the channel's topic in top of screen.
+
+   /SET lag_min_show 100
+          Show the server lag in status bar if it's bigger than this, the
+          unit is 1/100 of seconds (ie. the default value of 100 = 1
+          second).
+
+   /SET indent 10
+          When lines are longer than screen width they have to be split
+          to multiple lines. This specifies how much space to put at the
+          beginning of the line before the text begins. This can be
+          overridden in text formats with %| format.
+
+   /SET activity_hide_targets
+          If you don't want to see window activity in some certain
+          channels or queries, list them here. For example
+          "#boringchannel =bot1 =bot2". If any highlighted text or
+          message for you appears in that window, this setting is ignored
+          and the activity is shown.
+
+   /SET mail_counter ON
+          Show the number of mails in your mbox in status bar. The mbox
+          file is taken from $MAIL environment setting. Only mbox format
+          works for now.
+
+   Nick completion
+
+   /SET completion_auto OFF
+          Automatically complete the nick if line begins with start of
+          nick and the completion character. Learn to use the
+          tab-completion instead, it's a lot better ;)
+
+   /SET completion_char :
+          Completion character to use.
index 0a4104252d5dc05d66d5a0191aec6c9ca1891f0b..3076247c7fdd86a2d4ee9bc3d36b52688d1cc65c 100644 (file)
@@ -3,4 +3,4 @@ PERL_LDFLAGS="@PERL_LDFLAGS@"
 COMMON_LIBS="@COMMON_LIBS@"
 
 CHAT_MODULES="@CHAT_MODULES@"
-silc_MODULES="@silc_MODULES@"
+irc_MODULES="@irc_MODULES@"
diff --git a/apps/irssi/irssi-icon.png b/apps/irssi/irssi-icon.png
new file mode 100644 (file)
index 0000000..dd5efd2
Binary files /dev/null and b/apps/irssi/irssi-icon.png differ
diff --git a/apps/irssi/irssi-version.h.in b/apps/irssi/irssi-version.h.in
deleted file mode 100644 (file)
index 668993f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-/* automatically created by autogen.sh */
-#define IRSSI_VERSION "@VERSION@"
-#define IRSSI_VERSION_DATE "20010524"
index a3e9b9c0305563f3bfc2de2bbe439ec97a3f9a00..c15069cf39a83eec444c8e60ead794dc3974ee41 100644 (file)
@@ -4,213 +4,70 @@ Version:     @VERSION@
 Release:       1
 Vendor:        Timo Sirainen <tss@iki.fi>
 Summary:       Irssi is a IRC client
-Summary(pl):   Irssi - klient IRC
 Copyright:     GPL
 Group:                 Applications/Communications
-Group(pl):      Aplikacje/Komunikacja
 URL:           http://irssi.org/
 Source0:       http://irssi.org/irssi/files/%{name}-%{version}.tar.gz
 BuildRequires: glib-devel
 BuildRequires: ncurses-devel
-BuildRequires: imlib-devel
-BuildRequires: gtk+-devel
-BuildRequires: gnome-libs-devel
-BuildRequires: XFree86-devel
 BuildRoot:     /tmp/%{name}-%{version}-root
 
 %define                _sysconfdir     /etc
 %define                configure { CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ;  CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ;  FFLAGS="${FFLAGS:-%optflags}" ; export FFLAGS ; ./configure %{_target_platform}     --prefix=%{_prefix} --exec-prefix=%{_exec_prefix} --bindir=%{_bindir} --sbindir=%{_sbindir} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --localstatedir=%{_localstatedir} --sharedstatedir=%{_sharedstatedir} --mandir=%{_mandir} --infodir=%{_infodir} }
 
 %description
-Irssi is a textUI IRC client with IPv6 support 
-by Timo Sirainen <tss@iki.fi>.
-More information can be found at http://irssi.org/.
-
-%description -l pl
-Irssi jest tekstowym klientem IRC ze wsparciem dla IPv6.
-Napisany zosta³ przez Timo Strainen <tss@iki.fi>
-Wiêcej informacji mo¿na znale¶æ pod adresem
-http://irssi.org/
+Irssi is a modular IRC client that currently has only text
+mode user interface, but 80-90% of the code isn't text mode specific
+so other UI could be created pretty easily. Also, Irssi isn't really
+even IRC specific anymore, there's already a working SILC module
+available. Support for other protocols like ICQ could be create some day
+too.
 
-%package GNOME
-Summary:       GNOME version of irssi IRC client
-Summary(pl):   Wersja dla Â¶rodowiska GNOME klienta IRC irssi
-Group:         X11/Applications/Communications
-Group(pl):     X11/Aplikacje/Komunikacja
-Requires:      %{name} = %{version}
-
-%description GNOME
-Irssi is a GTK based (with GNOME) GUI IRC client with IPv6 support
-by Timo Sirainen <tss@iki.fi>.
 More information can be found at http://irssi.org/.
 
-%description GNOME -l pl
-Irssi jest graficznym klientem IRC ze wsparciem dla IPv6 pracuj±cym
-w Â¶rodowisku GNOME. Napisany zosta³ przez Timo Sirainen <tss@iki.fi>.
-Wiêcej informacji mo¿na znale¶æ pod adresem
-http://irssi.org/
-
 %prep
 %setup -q
 
 %build
-CPPFLAGS="-I/usr/X11R6/include"; export CPPFLAGS
-LDFLAGS="-s -L/usr/X11R6/lib"; export LDFLAGS
+export NOCONFIGURE=x
+./autogen.sh       
 %configure \
-       --with-gnome \
-       --with-gnome-panel \
        --with-imlib \
        --enable-ipv6 \
-       --with-textui=ncurses \
-       --without-socks \
-       --with-plugins
+       --with-textui \
+       --with-socks \
+        --with-bot \
+        --with-proxy \
+        --with-perl=yes \
+       --with-ncurses
 make
 
 %install
 rm -rf $RPM_BUILD_ROOT
-make DESTDIR=$RPM_BUILD_ROOT install
-strip --strip-unneeded $RPM_BUILD_ROOT%{_libdir}/irssi/plugins/lib*.so.*.*
-
-gzip -9fn AUTHORS ChangeLog README TODO NEWS
+make DESTDIR=$RPM_BUILD_ROOT PREFIX=$RPM_BUILD_ROOT/usr install
+mv $RPM_BUILD_ROOT/%{_datadir}/doc/irssi $RPM_BUILD_ROOT/%{_datadir}/doc/irssi-%version
+strip $RPM_BUILD_ROOT/%{_bindir}/*
+strip $RPM_BUILD_ROOT/%{_libdir}/irssi/modules/lib*.so*
+rm -f $RPM_BUILD_ROOT/%{_libdir}/perl5/5.6.0/i386-linux/perllocal.pod
 
 %clean
 rm -rf $RPM_BUILD_ROOT
 
 %files 
 %defattr (644,root,root,755)
-%doc {AUTHORS,ChangeLog,README,TODO,NEWS}.gz
-
-%attr(755,root,root) %{_bindir}/irssi
-%attr(755,root,root) %{_bindir}/irssi-text
-%attr(755,root,root) %{_bindir}/irssi-bot
+%doc %{_datadir}/doc/irssi-%version/
 
-%dir %{_sysconfdir}/irssi
-%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/irssi/*
+%attr(755,root,root) %{_bindir}/*
 
-%dir %{_libdir}/irssi
-%dir %{_libdir}/irssi/plugins
-%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.so.*.*
-%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.so
-#%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.la
-#%attr(755,root,root) %{_libdir}/irssi/plugins/lib*.a
+%config(noreplace) %verify(not size mtime md5) %{_sysconfdir}/irssi*
 
-%files GNOME
-%defattr (644,root,root,755)
-%attr(755,root,root) %{_bindir}/irssi
+%dir %{_libdir}
+%attr(755,root,root) %{_libdir}/irssi
+%attr(755,root,root) %{_libdir}/perl5
 
-%{_sysconfdir}/CORBA/servers/irssi.gnorba
-%{_datadir}/gnome/apps/Network/irssi.desktop
-%{_datadir}/gnome/help/irssi
-%{_datadir}/pixmaps/*
+%dir %{_datadir}/irssi
+%attr(755,root,root) %{_datadir}/irssi/*
 
-%define date    %(echo `LC_ALL="C" date +"%a %b %d %Y"`)
 %changelog
-* %{date} PLD Team <pld-list@pld.org.pl>
-All below listed persons can be reached on <cvs_login>@pld.org.pl
-
-$Log$
-Revision 1.1.1.1  2001/05/24 12:09:28  priikone
-       imported irssi.
-
-Revision 1.11  2001/03/29 14:38:28  cras
-http://irssi.org -> http://irssi.org/
-
-Revision 1.10  2000/06/02 01:55:01  cras
-Changed irssi's url to http://irssi.org/
-
-Revision 1.9  2000/04/17 09:37:05  kloczek
-- added pixmaps to %files (irssi have now own icon).
-
-Revision 1.8  2000/04/14 11:27:02  cras
-Sorry for a big update - I still don't have internet connection at home
-and this is what I've been doing a few weeks now.. :) You really shouldn't
-upgrade to this version without keeping a backup of the working one, since
-this will break everything and at least notify list is broken - probably
-something else too.
-
-* On the way to 0.8.0 .. Major rewriting/rearranging code. There's
-  some changes in behaviour because I'm trying to make Irssi a bit
-  more compatible with EPIC.
-
-* libPropList isn't needed anymore - I'm using my own configuration
-  library. This is mostly because different proplists worked a bit
-  differently everywhere and several people had problems with it.
-  It's also yet another extra library that you needed to compile
-  Irssi. New configuration library has several advantages:
-
-  You can add comments to configuration file and they also stay
-  there when it's saved.
-
-  It's not nearly as vulnerable as proplist. If some error occurs,
-  instead of just not reading anything it will try to continue if
-  possible. Also the error messages are written to irssi's text
-  window instead of stdout.
-
-  It can be managed more easily than proplist - setting/getting the
-  configuration is a lot more easier.
-
-* Coding style changes - I'm not using gint, gchar etc. anymore,
-  they're just extra pain when moving code to non-glib projects and
-  syntax hilighting doesn't work by default with most editors ;)
-
-  Indentation style was also changed to K&R because of some political
-  reasons ;) And I'm already starting to like it.. :) It forces me
-  to split code to different functions more often and the result is
-  that the code gets more readable.
-
-  And finally I'm also using nst' all over the place.
-
-+ /EVAL <commands> - Expand all the special variables from string and
-  run it. Commands can be split with ; character. See
-  docs/SPECIAL_VARS for more info.
-+ Aliases are parsed just like /EVAL - arguments are in $0..$9.
-+ Text formats are also parsed like /EVAL, arguments used to be in
-  $1..$9, now they're in $0..$8 so it messes up existing themes..
-+ /SET [key [value]] - no more the '=' character. Boolean values
-  also need to be changed with ON/OFF/TOGGLE values (not yes/no).
-  Settings aren't saved to disk until you use /SAVE.
-+ /TOGGLE <key> [ON/OFF] - same as /SET <key> TOGGLE
-
-Revision 1.7  2000/02/25 17:03:15  cras
-Irssi 0.7.27 released.
-
-Revision 1.6  2000/01/27 19:03:28  cras
-fixes by vkoivula@saunalahti.fi
-
-Revision 1.6  2000/01/25 00:00:00 vkoivula@saunalahti.fi
-- requires libProbList-devel changed to libPropList (problist.h is actually in
-  this package)
-- fixed filelist
-
-Revision 1.5  2000/01/13 02:13:20  kloczek
-- irssi.desktop now this is not applet but application description file -
-  place them in $(datadir)/gnome/apps/Network.
-
-Revision 1.4  1999/10/16 13:05:25  wiget
-- polish translation
-
-Revision 1.3  1999/09/13 16:50:25  wiget
-- fixed %%configure macro
-
-Revision 1.2  1999/09/04 11:42:33  wiget
-- new way to update Version: field in spec
-- new target for make 'make rpm'
-
-Revision 1.4  1999/09/03 09:36:24  wiget
-- updated to 0.7.16alpha-1
-
-Revision 1.3  1999/09/02 17:27:36  wiget
-- added BuildRequires rules
-
-Revision 1.2  1999/09/02 17:22:51  wiget
-- rewrite to PLD style coding:
--- correct Group and Group(pl)
--- %%changelog moved to end
--- splited to irssi and irssi-GNOME
--- added patch to allow 'make install DESTDIR=/some/dir'
--- added ./configure parameters
--- striped unneeded symbol from plugins
--- gziped docs
--- corrected %%files section
-
-- based at spec from tarball (by JT Traub <jtraub@dragoncat.net>)
+* Fri Aug 17 2001 - Joose Vettenranta <joose@iki.fi>
+  Created new spec file from spec file founded in irssi-0.7.98.3
diff --git a/apps/irssi/scripts/.cvsignore b/apps/irssi/scripts/.cvsignore
new file mode 100644 (file)
index 0000000..282522d
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/apps/irssi/scripts/Makefile.am b/apps/irssi/scripts/Makefile.am
new file mode 100644 (file)
index 0000000..7a5a7ab
--- /dev/null
@@ -0,0 +1,12 @@
+scriptdir = $(datadir)/irssi/scripts
+
+script_DATA = \
+       autoop.pl \
+       autorejoin.pl \
+       clones.pl \
+       hello.pl \
+       privmsg.pl \
+       realname.pl \
+       quitmsg.pl
+
+EXTRA_DIST = $(script_DATA)
diff --git a/apps/irssi/scripts/autoop.pl b/apps/irssi/scripts/autoop.pl
new file mode 100644 (file)
index 0000000..413f5e1
--- /dev/null
@@ -0,0 +1,80 @@
+# /AUTOOP <*|#channel> [<nickmasks>]
+
+use Irssi;
+use strict;
+
+my (%opnicks, %temp_opped);
+
+sub cmd_autoop {
+       my ($data) = @_;
+       my ($channel, $masks) = split(" ", $data, 2);
+
+       if ($channel eq "") {
+               if (!%opnicks) {
+                       Irssi::print("Usage: /AUTOOP <*|#channel> [<nickmasks>]");
+                       Irssi::print("No-one's being auto-opped currently.");
+                       return;
+               }
+
+               Irssi::print("Currently auto-opping in channels:");
+               foreach $channel (keys %opnicks) {
+                       $masks = $opnicks{$channel};
+
+                       if ($channel eq "*") {
+                               Irssi::print("All channels: $masks");
+                       } else {
+                               Irssi::print("$channel: $masks");
+                       }
+               }
+               return;
+       }
+
+       if ($masks eq "") {
+               $masks = "<no-one>";
+               delete $opnicks{$channel};
+       } else {
+               $opnicks{$channel} = $masks;
+       }
+       if ($channel eq "*") {
+               Irssi::print("Now auto-opping in all channels: $masks");
+       } else {
+               Irssi::print("$channel: Now auto-opping: $masks");
+       }
+}
+
+sub autoop {
+       my ($channel, $masks, @nicks) = @_;
+       my ($server, $nickrec);
+
+       $server = $channel->{server};
+       foreach $nickrec (@nicks) {
+               my $nick = $nickrec->{nick};
+               my $host = $nickrec->{host};
+
+                if (!$temp_opped{$nick} &&
+                   $server->masks_match($masks, $nick, $host)) {
+                       $channel->command("/op $nick");
+                       $temp_opped{$nick} = 1;
+               }
+       }
+}
+
+sub event_massjoin {
+       my ($channel, $nicks_list) = @_;
+       my @nicks = @{$nicks_list};
+
+       return if (!$channel->{chanop});
+
+       undef %temp_opped;
+
+       # channel specific
+       my $masks = $opnicks{$channel->{name}};
+       autoop($channel, $masks, @nicks) if ($masks);
+
+       # for all channels
+       $masks = $opnicks{"*"};
+       autoop($channel, $masks, @nicks) if ($masks);
+}
+
+Irssi::command_bind('autoop', 'cmd_autoop');
+Irssi::signal_add_last('massjoin', 'event_massjoin');
diff --git a/apps/irssi/scripts/autorejoin.pl b/apps/irssi/scripts/autorejoin.pl
new file mode 100644 (file)
index 0000000..bbbb0ed
--- /dev/null
@@ -0,0 +1,26 @@
+# automatically rejoin to channel after kicked
+
+# NOTE: I personally don't like this feature, in most channels I'm in it
+# will just result as ban. You've probably misunderstood the idea of /KICK
+# if you kick/get kicked all the time "just for fun" ...
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+sub event_rejoin_kick {
+       my ($server, $data) = @_;
+       my ($channel, $nick) = split(/ +/, $data);
+
+       return if ($server->{nick} ne $nick);
+
+       # check if channel has password
+       my $chanrec = $server->channel_find($channel);
+       my $password = $chanrec->{key} if ($chanrec);
+
+       # We have to use send_raw() because the channel record still
+       # exists and irssi won't even try to join to it with command()
+       $server->send_raw("JOIN $channel $password");
+}
+
+Irssi::signal_add('event kick', 'event_rejoin_kick');
diff --git a/apps/irssi/scripts/clones.pl b/apps/irssi/scripts/clones.pl
new file mode 100644 (file)
index 0000000..59c22c3
--- /dev/null
@@ -0,0 +1,43 @@
+# /CLONES - Display clones in the active channel
+# Modified by Roi Dayan. dejavo@punkass.com
+
+use strict;
+
+sub cmd_clones {
+  my ($data, $server, $channel) = @_;
+  my $min_show_count = ($data =~ /^[0-9]+$/) ? $data : 2;
+
+  if (!$channel || $channel->{type} ne "CHANNEL") {
+    Irssi::print("No active channel in window");
+    return;
+  }
+
+  my %hostnames = {};
+  my %hostnicks = {};
+  my @hosttmp = {};
+  foreach my $nick ($channel->nicks()) {
+    my @hosttmp = split(/\@/,$nick->{host});
+    $hostnames{$hosttmp[1]}++;
+    $hostnicks{$hosttmp[1]} = $hostnicks{$hosttmp[1]}.$hostnames{$hosttmp[1]}.". ".$nick->{nick}."!".$nick->{host}."\n";
+    $hostnicks{$hosttmp[1]} =~ s/^,//;
+#    $hostnicks{$hosttmp[1]} =~ s/\n$//;
+  }
+  
+  foreach my $nick (keys %hostnicks) {
+    $hostnicks{$nick} =~ s/\n$//;
+  }
+
+  my $count = 0;
+  foreach my $host (keys %hostnames) {
+    my $clones = $hostnames{$host};
+    if ($clones >= $min_show_count) {
+      $channel->print("Clones:") if ($count == 0);
+      $channel->print("$host: $clones $hostnicks{$host}");
+      $count++;
+    }
+  }
+
+  $channel->print("No clones in channel") if ($count == 0);
+}
+
+Irssi::command_bind('clones', 'cmd_clones');
diff --git a/apps/irssi/scripts/hello.pl b/apps/irssi/scripts/hello.pl
new file mode 100644 (file)
index 0000000..82adfe2
--- /dev/null
@@ -0,0 +1,12 @@
+# "Hello, world!" script :) /hello <nick> sends "Hello, world!" to <nick>
+
+use Irssi;
+use strict;
+
+sub cmd_hello {
+       my ($data, $server, $channel) = @_;
+
+       $server->command("/msg $data Hello, world!");
+}
+
+Irssi::command_bind('hello', 'cmd_hello');
diff --git a/apps/irssi/scripts/mail.pl b/apps/irssi/scripts/mail.pl
new file mode 100644 (file)
index 0000000..2d11a46
--- /dev/null
@@ -0,0 +1,83 @@
+# Mail counter statusbar item
+# for irssi 0.7.99 by Timo Sirainen
+#  /SET mail_ext_program - specify external mail checker program
+#  /SET mail_file - specifies mbox file location
+#  /SET mail_refresh_time - in seconds, how often to check for new mail
+
+use strict;
+use Irssi::TextUI;
+
+my $extprog;
+my ($last_refresh_time, $refresh_tag);
+
+# for mbox caching
+my ($last_size, $last_mtime, $last_mailcount);
+
+sub mbox_count {
+  my $mailfile = shift;
+
+  my @stat = stat($mailfile);
+  my $size = $stat[7];
+  my $mtime = $stat[9];
+
+  # if the file hasn't changed, get the count from cache
+  return $last_mailcount if ($last_size == $size && $last_mtime == $mtime);
+  $last_size = $size;
+  $last_mtime = $mtime;
+
+  my $count;
+  if ($extprog ne "") {
+    $count = `$extprog`;
+    chomp $count;
+  } else {
+    return 0 if (!open(F, $mailfile));
+
+    $count = 0;
+    while (<F>) {
+      $count++ if (/^From /);
+      $count-- if (/^Subject: .*FOLDER INTERNAL DATA/);
+    }
+    close(F);
+  }
+
+  $last_mailcount = $count;
+  return $count;
+}
+
+sub mail {
+  my ($item, $get_size_only) = @_;
+
+  my $count = mbox_count(Irssi::settings_get_str('mail_file'));
+  if ($count == 0) {
+    # no mail - don't print the [Mail: ] at all
+    if ($get_size_only) {
+      $item->{min_size} = $item->{max_size} = 0;
+    }
+  } else {
+    $item->default_handler($get_size_only, undef, $count, 1);
+  }
+}
+
+sub refresh_mail {
+  Irssi::statusbar_items_redraw('mail');
+}
+
+sub read_settings {
+  $extprog = Irssi::settings_get_str('mail_ext_program');
+  my $time = Irssi::settings_get_int('mail_refresh_time');
+  return if ($time == $last_refresh_time);
+
+  $last_refresh_time = $time;
+  Irssi::timeout_remove($refresh_tag) if ($refresh_tag);
+  $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_mail', undef);
+}
+
+Irssi::settings_add_str('misc', 'mail_ext_program', '');
+Irssi::settings_add_str('misc', 'mail_file', $ENV{'MAIL'});
+Irssi::settings_add_int('misc', 'mail_refresh_time', 60);
+
+Irssi::statusbar_item_register('mail', '{sb Mail: $0-}', 'mail');
+
+read_settings();
+Irssi::signal_add('setup changed', 'read_settings');
+mbox_count(Irssi::settings_get_str('mail_file'));
diff --git a/apps/irssi/scripts/mlock.pl b/apps/irssi/scripts/mlock.pl
new file mode 100644 (file)
index 0000000..35ad785
--- /dev/null
@@ -0,0 +1,119 @@
+# /MLOCK <channel> <mode>
+#
+# Locks the channel mode to <mode>, if someone else tries to change the mode
+# Irssi will automatically change it back. +k and +l are a bit special since
+# they require the parameter. If you omit the parameter, like setting the
+# mode to "+ntlk", Irssi will allow all +k and +l (or -lk) mode changes.
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+my %keep_channels;
+
+sub cmd_mlock {
+       my ($data, $server) = @_;
+       my ($channel, $mode) = split(/ /, $data, 2);
+
+       $keep_channels{$channel} = $mode;
+       mlock_check_mode($server, $channel);
+}
+
+sub mlock_check_mode {
+        my ($server, $channame) = @_;
+
+       my $channel = $server->channel_find($channame);
+       return if (!$channel || !$channel->{chanop});
+
+        my $keep_mode = $keep_channels{$channame};
+       return if (!$keep_mode);
+
+       # old channel mode
+       my ($oldmode, $oldkey, $oldlimit);
+       $oldmode = $channel->{mode};
+        $oldmode =~ s/^([^ ]*).*/\1/;
+       $oldkey = $channel->{key};
+       $oldlimit = $channel->{limit};
+
+       # get the new channel key/limit
+       my (@newmodes, $newkey, $limit);
+       @newmodes = split(/ /, $keep_mode); $keep_mode = $newmodes[0];
+       if ($keep_mode =~ /k/) {
+               if ($keep_mode =~ /k.*l/) {
+                        $newkey = $newmodes[1];
+                        $limit = $newmodes[2];
+               } elsif ($keep_mode =~ /l.*k/) {
+                       $limit = $newmodes[1];
+                        $newkey = $newmodes[2];
+               } else {
+                        $newkey = $newmodes[1];
+               }
+       } elsif ($keep_mode =~ /l/) {
+               $limit = $newmodes[1];
+       }
+
+       # check the differences
+       my %allmodes;
+       $keep_mode =~ s/^\+//;
+       for (my $n = 0; $n < length($keep_mode); $n++) {
+               my $modechar = substr($keep_mode, $n, 1);
+               $allmodes{$modechar} = '+';
+       }
+
+       for (my $n = 0; $n < length($oldmode); $n++) {
+               my $modechar = substr($oldmode, $n, 1);
+
+               if ($allmodes{$modechar} eq '+') {
+                       next if (($modechar eq "k" && $newkey ne $oldkey) ||
+                                ($modechar eq "l" && $limit != $oldlimit));
+                       delete $allmodes{$modechar};
+               } else {
+                       $allmodes{$modechar} = '-';
+               }
+       }
+
+       # create the mode change string
+       my ($modecmd, $extracmd);
+       foreach my $mode (keys %allmodes) {
+               Irssi::print("key = '$mode':".$allmodes{$mode});
+               if ($mode eq "k") {
+                       if ($allmodes{$mode} eq '+') {
+                               next if ($newkey eq "");
+                               if ($oldkey ne "") {
+                                       # we need to get rid of old key too
+                                       $modecmd .= "-k";
+                                       $extracmd .= " $oldkey";
+                               }
+                               $extracmd .= " $newkey";
+                       } else {
+                               $extracmd .= " $oldkey";
+                       }
+               }
+               if ($mode eq "l" && $allmodes{$mode} eq '+') {
+                       next if ($limit <= 0);
+                        $extracmd .= " $limit";
+               }
+               $modecmd .= $allmodes{$mode}.$mode;
+       }
+
+       if ($modecmd ne "") {
+               $channel->{server}->command("/mode $channame $modecmd$extracmd");
+       }
+}
+
+sub mlock_mode_changed {
+       my ($server, $data) = @_;
+       my ($channel, $mode) = split(/ /, $data, 2);
+
+       mlock_check_mode($server, $channel);
+}
+
+sub mlock_synced {
+       my $channel = $_[0];
+
+       mlock_check_mode($channel->{server}, $channel->{name});
+}
+
+Irssi::command_bind('mlock', 'cmd_mlock');
+Irssi::signal_add_last("event mode", "mlock_mode_changed");
+Irssi::signal_add("channel synced", "mlock_synced");
diff --git a/apps/irssi/scripts/privmsg.pl b/apps/irssi/scripts/privmsg.pl
new file mode 100644 (file)
index 0000000..b1d119c
--- /dev/null
@@ -0,0 +1,18 @@
+# listen PRIVMSGs - send a notice to yourself when your nick is meantioned
+
+use Irssi;
+use strict;
+
+sub event_privmsg {
+       my ($server, $data, $nick, $address) = @_;
+       my ($target, $text) = $data =~ /^(\S*)\s:(.*)/;
+
+       return if (!$server->ischannel($target));
+
+       my $mynick = $server->{nick};
+       return if ($text !~ /\b$mynick\b/);
+
+       $server->command("/notice $mynick In channel $target, $nick!$address said: $text");
+}
+
+Irssi::signal_add("event privmsg", "event_privmsg");
diff --git a/apps/irssi/scripts/quitmsg.pl b/apps/irssi/scripts/quitmsg.pl
new file mode 100644 (file)
index 0000000..981b15a
--- /dev/null
@@ -0,0 +1,35 @@
+# If quit message isn't given, quit with a random message
+# read from ~/.irssi/irssi.quit
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+my $quitfile = glob "~/.irssi/irssi.quit";
+
+sub cmd_quit {
+       my ($data, $server, $channel) = @_;
+       return if ($data ne "");
+
+       open (f, $quitfile) || return;
+       my $lines = 0; while(<f>) { $lines++; };
+
+       my $line = int(rand($lines))+1;
+
+       my $quitmsg;
+       seek(f, 0, 0); $. = 0;
+       while(<f>) {
+               next if ($. != $line);
+
+               chomp;
+               $quitmsg = $_;
+               last;
+       }
+       close(f);
+
+       foreach my $server (Irssi::servers) {
+               $server->command("/disconnect ".$server->{tag}." $quitmsg");
+       }
+}
+
+Irssi::command_bind('quit', 'cmd_quit');
diff --git a/apps/irssi/scripts/realname.pl b/apps/irssi/scripts/realname.pl
new file mode 100644 (file)
index 0000000..ccb1c6b
--- /dev/null
@@ -0,0 +1,34 @@
+# /RN - display real name of nick
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+
+sub cmd_realname {
+       my ($data, $server, $channel) = @_;
+
+       $server->send_raw("WHOIS :$data");
+
+       # ignore all whois replies except "No such nick" or the 
+       # first line of the WHOIS reply
+       $server->redirect_event($data, 2,
+                         "event 318", "event empty", -1,
+                         "event 402", "event 402", -1,
+                         "event 401", "event 401", 1,
+                         "event 311", "redir whois", 1,
+                         "event 301", "event empty", 1,
+                         "event 312", "event empty", 1,
+                         "event 313", "event empty", 1,
+                         "event 317", "event empty", 1,
+                         "event 319", "event empty", 1);
+}
+
+sub event_rn_whois {
+       my ($num, $nick, $user, $host, $empty, $realname) = split(/ +/, $_[1], 6);
+       $realname =~ s/^://;
+
+       Irssi::print("%_$nick%_ is $realname");
+}
+
+Irssi::command_bind('rn', 'cmd_realname');
+Irssi::signal_add('redir whois', 'event_rn_whois');
diff --git a/apps/irssi/silc.conf b/apps/irssi/silc.conf
new file mode 100644 (file)
index 0000000..3439e3c
--- /dev/null
@@ -0,0 +1,214 @@
+#
+# Configured servers
+#
+servers = (
+  { address = "silc.silcnet.org"; chatnet = SILCNet; port = 706; },
+  { address = "silc.ytti.fi"; chatnet = SILCNet; port = 706; },
+  { address = "silc.peelo.com"; chatnet = SILCNet; port = 706; },
+);
+
+#
+# Configured chat networks
+#
+chatnets = {
+  SILCNet = { type = "SILC"; };
+};
+
+#
+# Configured channels
+#
+channels = (
+  { name = "#silc"; chatnet = silcnet; autojoin = No; }
+);
+
+#
+# Your favorite aliases
+#
+aliases = {
+  JOIN = "join -window";
+  QUERY = "query -window";
+  LEAVE = "part";
+  BYE = "quit";
+  EXIT = "quit";
+  SIGNOFF = "quit";
+  DESCRIBE = "action";
+  DATE = "time";
+  HOST = "userhost";
+  LAST = "lastlog";
+  SAY = "msg *";
+  WHO = "users *";
+  WI = "whois";
+  WII = "whois $0 $0";
+  WW = "whowas";
+  W = "who";
+  N = "names";
+  M = "msg";
+  T = "topic";
+  C = "clear";
+  CL = "clear";
+  K = "kick";
+  KB = "kickban";
+  KN = "knockout";
+  BANS = "ban";
+  B = "ban";
+  MUB = "unban *";
+  UB = "unban";
+  IG = "ignore";
+  UNIG = "unignore";
+  SB = "scrollback";
+  WC = "window close";
+  WN = "window new hide";
+  GOTO = "sb goto";
+  CHAT = "dcc chat";
+  ADMIN = "info";
+  RUN = "SCRIPT LOAD";
+  UPTIME = "eval exec - expr `date +%s` - \\$F | awk '{print \"Irssi uptime: \"int(\\\\\\$1/3600/24)\"d \"int(\\\\\\$1/3600%24)\"h \"int(\\\\\\$1/60%60)\"m \"int(\\\\\\$1%60)\"s\" }'";
+  CALC = "exec - if which bc &>/dev/null\\; then echo '$*' | bc | awk '{print \"$*=\"$$1}'\\; else echo bc was not found\\; fi";
+};
+
+#
+# Configuration for statusbar and other bars that appear on the screen
+#
+statusbar = {
+  # formats:
+  # when using {templates}, the template is shown only if it's argument isn't
+  # empty unless no argument is given. for example {sb} is printed always,
+  # but {sb $T} is printed only if $T isn't empty.
+
+  items = {
+    # start/end text in statusbars
+    barstart = "{sbstart}";
+    barend = "{sbend}";
+
+    # treated "normally", you could change the time/user name to whatever
+    time = "{sb $Z}";
+    user = "{sb $cumode$N{sbmode $usermode}{sbaway $A}}";
+    topic = " $topic";
+
+    # treated specially .. window is printed with non-empty windows,
+    # window_empty is printed with empty windows
+    window = "{sb $winref:$T{sbmode $M}}";
+    window_empty = "{sb $winref{sbservertag $tag}}";
+    prompt = "{prompt $[.15]T}";
+    prompt_empty = "{prompt $winname}";
+
+    # all of these treated specially, they're only displayed when needed
+    lag = "{sb Lag: $0-}";
+    act = "{sb Act: $0-}";
+    more = "-- more --";
+  };
+
+  # there's two type of statusbars. root statusbars are either at the top
+  # of the screen or at the bottom of the screen. window statusbars are at
+  # the top/bottom of each split window in screen.
+  default = {
+    # the "default statusbar" to be displayed at the bottom of the window.
+    # contains all the normal items.
+    window = {
+      # window, root
+      type = "window";
+      # top, bottom
+      placement = "bottom";
+      # number
+      position = "1";
+      # active, inactive, always, never (disables the statusbar)
+      visible = "active";
+
+      # list of items in statusbar in the display order
+      items = {
+        barstart = { priority = "100"; };
+        time = { };
+        user = { };
+        window = { };
+        window_empty = { };
+        lag = { priority = "-1"; };
+        act = { priority = "10"; };
+        more = { priority = "-1"; alignment = "right"; };
+        barend = { priority = "100"; alignment = "right"; };
+      };
+    };
+
+    # statusbar to use in inactive split windows
+    window_inact = {
+      type = "window";
+      placement = "bottom";
+      position = "1";
+      visible = "inactive";
+      items = {
+        barstart = { priority = "100"; };
+        window = { };
+       window_empty = { };
+        more = { priority = "-1"; alignment = "right"; };
+        barend = { priority = "100"; alignment = "right"; };
+      };
+    };
+
+    # we treat input line as yet another statusbar :) It's possible to
+    # add other items before or after the input line item.
+    prompt = {
+      type = "root";
+      placement = "bottom";
+      # we want to be at the bottom always
+      position = "100";
+      visible = "always";
+      items = {
+        prompt = { priority = "-1"; };
+        prompt_empty = { priority = "-1"; };
+        # treated specially, this is the real input line.
+        input = { priority = "10"; };
+      };
+    };
+
+    # topicbar
+    topic = {
+      type = "root";
+      placement = "top";
+      position = "1";
+      visible = "never";
+      items = {
+        barstart = { priority = "100"; };
+        topic = { };
+        barend = { priority = "100"; alignment = "right"; };
+      };
+    };
+  };
+};
+
+#
+# Settings (can be changed with /SET command)
+#
+# You can set the default cipher, hash function and HMAC to be used
+# as setting as well.  You can set it here or use the /SET command.
+#
+# Available ciphers are (default: aes-256-cbc):
+#
+# aes-256-cbc, aes-192-cbc, aes-128-cbc,
+# twofish-256-cbc, twofish-192-cbc, twofish-128-cbc,
+# rc6-256-cbc, rc6-192-cbc, rc6-128-cbc, 
+# mars-256-cbc, mars-192-cbc, mars-128-cbc,
+# cast-256-cbc, cast-192-cbc and cast-128-cbc
+#
+# Available hash functions are (default: sha1):
+#
+# sha1 and md5
+#
+# Available HMAC's are (default: hmac-sha1-96):
+#
+# hmac-sha1-96, hmac-md5-96, hmac-sha1 and hmac-md5
+#
+settings = {
+  "server" = {
+    crypto_default_cipher = "aes-256-cbc";
+    crypto_default_hash = "sha1";
+    crypto_default_hmac = "hmac-sha1-96";
+  };
+  "fe-common/core" = {
+    autocreate_own_query = "no";
+    use_status_window = "no";
+    autoclose_windows = "no";
+    use_msgs_window = "no";
+    autocreate_windows = "no";
+    autocreate_query_level = "none";
+  };
+  "fe-text" = { indent = "8"; };
+};
index d8fc1899f0ec9a835ab0eba86291d4a7fee5b59b..a0dc6af1d8a363188f5af702176485a3a3776f4d 100644 (file)
@@ -2,7 +2,15 @@ if BUILD_TEXTUI
 TEXTUI=fe-text
 endif
 
+if BUILD_IRSSIBOT
+BOTUI=#
+endif
+
+if HAVE_PERL
+PERLDIR=perl
+endif
+
 noinst_HEADERS = \
        common.h
 
-SUBDIRS = lib-popt lib-config core silc fe-common $(TEXTUI)
+SUBDIRS = lib-popt lib-config core silc fe-common $(PERLDIR) $(TEXTUI) $(BOTUI)
index c79c5d9ba615ebe335a01ddcce5aea2cb2fe98b4..d6cc0421d7f296cb5d18916e707c41f2311c5213 100644 (file)
@@ -4,6 +4,12 @@
 #define IRSSI_AUTHOR "Timo Sirainen <tss@iki.fi>"
 #define IRSSI_WEBSITE "http://irssi.org/"
 
+#define IRSSI_DIR_SHORT "~/.silc"
+#define IRSSI_DIR_FULL "%s/.silc" /* %s == g_get_home_dir() */
+
+#define IRSSI_GLOBAL_CONFIG "silc.conf" /* config file name in /etc/ */
+#define IRSSI_HOME_CONFIG "silc.conf" /* config file name in ~/.irssi/ */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #  include <gmodule.h>
 #endif
 
-#include "core/memdebug.h"
-
-#define g_free_not_null(a) \
-       G_STMT_START { \
-         if (a) g_free(a); \
-       } G_STMT_END
-
-#define g_free_and_null(a) \
-       G_STMT_START { \
-         if (a) { g_free(a); (a) = NULL; } \
-       } G_STMT_END
-
+/* input functions */
 #define G_INPUT_READ   (1 << 0)
 #define G_INPUT_WRITE  (1 << 1)
 
@@ -65,8 +60,38 @@ int g_input_add(GIOChannel *source, int condition,
 int g_input_add_full(GIOChannel *source, int priority, int condition,
                     GInputFunction function, void *data);
 
+/* return full path for ~/.irssi */
+const char *get_irssi_dir(void);
+/* return full path for ~/.irssi/config */
+const char *get_irssi_config(void);
+
+/* max. size for %d */
 #define MAX_INT_STRLEN ((sizeof(int) * CHAR_BIT + 2) / 3 + 1)
 
+#define g_free_not_null(a) g_free(a)
+
+#define g_free_and_null(a) \
+       G_STMT_START { \
+         if (a) { g_free(a); (a) = NULL; } \
+       } G_STMT_END
+
+/* ctype.h isn't safe with chars, use our own instead */
+#define i_toupper(x) toupper((int) (unsigned char) (x))
+#define i_tolower(x) tolower((int) (unsigned char) (x))
+#define i_isalnum(x) isalnum((int) (unsigned char) (x))
+#define i_isalpha(x) isalpha((int) (unsigned char) (x))
+#define i_isascii(x) isascii((int) (unsigned char) (x))
+#define i_isblank(x) isblank((int) (unsigned char) (x))
+#define i_iscntrl(x) iscntrl((int) (unsigned char) (x))
+#define i_isdigit(x) isdigit((int) (unsigned char) (x))
+#define i_isgraph(x) isgraph((int) (unsigned char) (x))
+#define i_islower(x) islower((int) (unsigned char) (x))
+#define i_isprint(x) isprint((int) (unsigned char) (x))
+#define i_ispunct(x) ispunct((int) (unsigned char) (x))
+#define i_isspace(x) isspace((int) (unsigned char) (x))
+#define i_isupper(x) isupper((int) (unsigned char) (x))
+#define i_isxdigit(x) isxdigit((int) (unsigned char) (x))
+
 typedef struct _IPADDR IPADDR;
 typedef struct _CONFIG_REC CONFIG_REC;
 typedef struct _CONFIG_NODE CONFIG_NODE;
@@ -86,4 +111,6 @@ typedef struct _SERVER_CONNECT_REC SERVER_CONNECT_REC;
 typedef struct _SERVER_SETUP_REC SERVER_SETUP_REC;
 typedef struct _CHANNEL_SETUP_REC CHANNEL_SETUP_REC;
 
+typedef struct _WINDOW_REC WINDOW_REC;
+
 #endif
diff --git a/apps/irssi/src/core/.cvsignore b/apps/irssi/src/core/.cvsignore
new file mode 100644 (file)
index 0000000..8553e9e
--- /dev/null
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
diff --git a/apps/irssi/src/core/Makefile.am b/apps/irssi/src/core/Makefile.am
new file mode 100644 (file)
index 0000000..655d291
--- /dev/null
@@ -0,0 +1,96 @@
+noinst_LIBRARIES = libcore.a
+
+include $(top_srcdir)/Makefile.defines.in
+
+INCLUDES = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/core \
+       $(GLIB_CFLAGS) \
+       -DSYSCONFDIR=\""$(silc_etcdir)"\" \
+       -DMODULEDIR=\""$(silc_modulesdir)"\"
+
+libcore_a_SOURCES = \
+       args.c \
+       channels.c \
+       channels-setup.c \
+       commands.c \
+       chat-commands.c \
+       chat-protocols.c \
+       chatnets.c \
+       core.c \
+       expandos.c \
+        ignore.c \
+        levels.c \
+       line-split.c \
+       log.c \
+       log-away.c \
+       masks.c \
+       misc.c \
+       modules.c \
+       modules-load.c \
+       net-disconnect.c \
+       net-nonblock.c \
+       net-sendbuffer.c \
+       network.c \
+       nicklist.c \
+       nickmatch-cache.c \
+       pidwait.c \
+       queries.c \
+       rawlog.c \
+       servers.c \
+       servers-reconnect.c \
+       servers-setup.c \
+       session.c \
+       settings.c \
+       signals.c \
+       special-vars.c \
+       write-buffer.c
+
+structure_headers = \
+       channel-rec.h \
+       channel-setup-rec.h \
+       chatnet-rec.h \
+       query-rec.h \
+       server-rec.h \
+       server-setup-rec.h \
+       server-connect-rec.h \
+       window-item-rec.h
+
+noinst_HEADERS = \
+       args.h \
+       channels.h \
+       channels-setup.h \
+       commands.h \
+       chat-protocols.h \
+       chatnets.h \
+       core.h \
+       expandos.h \
+        ignore.h \
+        levels.h \
+       line-split.h \
+       log.h \
+       masks.h \
+       misc.h \
+       module.h \
+       modules.h \
+       modules-load.h \
+       net-disconnect.h \
+       net-nonblock.h \
+       net-sendbuffer.h \
+       network.h \
+       nick-rec.h \
+       nicklist.h \
+       nickmatch-cache.h \
+       pidwait.h \
+       queries.h \
+       rawlog.h \
+       servers.h \
+       servers-reconnect.h \
+       servers-setup.h \
+       session.h \
+       settings.h \
+       signals.h \
+       special-vars.h \
+       window-item-def.h \
+       write-buffer.h \
+       $(structure_headers)
diff --git a/apps/irssi/src/core/args.c b/apps/irssi/src/core/args.c
new file mode 100644 (file)
index 0000000..ab26ee1
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ args.c : small frontend to libPopt command line argument parser
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "args.h"
+
+GArray *iopt_tables = NULL;
+
+void args_register(struct poptOption *options)
+{
+       if (iopt_tables == NULL) {
+               iopt_tables = g_array_new(TRUE, TRUE,
+                                         sizeof(struct poptOption));
+       }
+
+       while (options->longName != NULL || options->shortName != '\0' ||
+              options->arg != NULL) {
+               g_array_append_val(iopt_tables, *options);
+               options = options+1;
+       }
+}
+
+void args_execute(int argc, char *argv[])
+{
+       poptContext con;
+       int nextopt;
+
+       if (iopt_tables == NULL)
+               return;
+
+       con = poptGetContext(PACKAGE, argc, argv,
+                            (struct poptOption *) (iopt_tables->data), 0);
+       poptReadDefaultConfig(con, TRUE);
+
+       while ((nextopt = poptGetNextOpt(con)) > 0) ;
+
+       if (nextopt != -1) {
+               printf("Error on option %s: %s.\n"
+                      "Run '%s --help' to see a full list of "
+                      "available command line options.\n",
+                      poptBadOption(con, 0), poptStrerror(nextopt), argv[0]);
+               exit(1);
+       }
+
+       g_array_free(iopt_tables, TRUE);
+       iopt_tables = NULL;
+
+        poptFreeContext(con);
+}
diff --git a/apps/irssi/src/core/args.h b/apps/irssi/src/core/args.h
new file mode 100644 (file)
index 0000000..4d1b82f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __ARGS_H
+#define __ARGS_H
+
+#ifdef HAVE_POPT_H
+#  include <popt.h>
+#else
+#  include "lib-popt/popt.h"
+#endif
+
+extern GArray *iopt_tables;
+
+void args_register(struct poptOption *options);
+void args_execute(int argc, char *argv[]);
+
+#endif
diff --git a/apps/irssi/src/core/channel-rec.h b/apps/irssi/src/core/channel-rec.h
new file mode 100644 (file)
index 0000000..f3ec5f8
--- /dev/null
@@ -0,0 +1,31 @@
+/* CHANNEL_REC definition, used for inheritance */
+
+#include "window-item-rec.h"
+
+char *topic;
+char *topic_by;
+time_t topic_time;
+
+GHashTable *nicks; /* list of nicks */
+NICK_REC *ownnick; /* our own nick */
+
+unsigned int no_modes:1; /* channel doesn't support modes */
+char *mode;
+int limit; /* user limit */
+char *key; /* password key */
+
+unsigned int chanop:1; /* You're a channel operator */
+unsigned int names_got:1; /* Received /NAMES list */
+unsigned int wholist:1; /* WHO list got */
+unsigned int synced:1; /* Channel synced - all queries done */
+
+unsigned int joined:1; /* Have we even received JOIN event for this channel? */
+unsigned int left:1; /* You just left the channel */
+unsigned int kicked:1; /* You just got kicked */
+unsigned int session_rejoin:1; /* This channel was joined with /UPGRADE */
+unsigned int destroying:1;
+
+/* Return the information needed to call SERVER_REC->channels_join() for
+   this channel. Usually just the channel name, but may contain also the
+   channel key. */
+char *(*get_join_data)(CHANNEL_REC *channel);
diff --git a/apps/irssi/src/core/channel-setup-rec.h b/apps/irssi/src/core/channel-setup-rec.h
new file mode 100644 (file)
index 0000000..a0b2897
--- /dev/null
@@ -0,0 +1,12 @@
+int type; /* module_get_uniq_id("CHANNEL SETUP", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+char *name;
+char *chatnet;
+char *password;
+
+char *botmasks;
+char *autosendcmd;
+
+unsigned int autojoin:1;
+GHashTable *module_data;
diff --git a/apps/irssi/src/core/channels-setup.c b/apps/irssi/src/core/channels-setup.c
new file mode 100644 (file)
index 0000000..ac9b1c2
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ channels-setup.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
+
+GSList *setupchannels;
+
+static void channel_setup_save(CHANNEL_SETUP_REC *channel)
+{
+       CONFIG_NODE *parentnode, *node;
+       int index;
+
+       index = g_slist_index(setupchannels, channel);
+
+       parentnode = iconfig_node_traverse("(channels", TRUE);
+       node = config_node_index(parentnode, index);
+       if (node == NULL)
+               node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+
+        iconfig_node_clear(node);
+       iconfig_node_set_str(node, "name", channel->name);
+       iconfig_node_set_str(node, "chatnet", channel->chatnet);
+       if (channel->autojoin)
+               iconfig_node_set_bool(node, "autojoin", TRUE);
+       iconfig_node_set_str(node, "password", channel->password);
+       iconfig_node_set_str(node, "botmasks", channel->botmasks);
+       iconfig_node_set_str(node, "autosendcmd", channel->autosendcmd);
+}
+
+void channel_setup_create(CHANNEL_SETUP_REC *channel)
+{
+       if (g_slist_find(setupchannels, channel) == NULL)
+               setupchannels = g_slist_append(setupchannels, channel);
+       channel_setup_save(channel);
+
+       signal_emit("channel setup created", 1, channel);
+}
+
+static void channel_config_remove(CHANNEL_SETUP_REC *channel)
+{
+       CONFIG_NODE *node;
+
+       node = iconfig_node_traverse("channels", FALSE);
+       if (node != NULL) iconfig_node_list_remove(node, g_slist_index(setupchannels, channel));
+}
+
+static void channel_setup_destroy(CHANNEL_SETUP_REC *channel)
+{
+       g_return_if_fail(channel != NULL);
+
+       setupchannels = g_slist_remove(setupchannels, channel);
+       signal_emit("channel setup destroyed", 1, channel);
+
+       g_free_not_null(channel->chatnet);
+       g_free_not_null(channel->password);
+       g_free_not_null(channel->botmasks);
+       g_free_not_null(channel->autosendcmd);
+       g_free(channel->name);
+       g_free(channel);
+}
+
+void channel_setup_remove(CHANNEL_SETUP_REC *channel)
+{
+        channel_config_remove(channel);
+        channel_setup_destroy(channel);
+}
+
+CHANNEL_SETUP_REC *channel_setup_find(const char *channel,
+                                     const char *chatnet)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(channel != NULL, NULL);
+
+       for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_SETUP_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, channel) == 0 &&
+                   channel_chatnet_match(rec->chatnet, chatnet))
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static CHANNEL_SETUP_REC *channel_setup_read(CONFIG_NODE *node)
+{
+       CHANNEL_SETUP_REC *rec;
+        CHATNET_REC *chatnetrec;
+       char *channel, *chatnet;
+
+       g_return_val_if_fail(node != NULL, NULL);
+
+       channel = config_node_get_str(node, "name", NULL);
+        chatnet = config_node_get_str(node, "chatnet", NULL);
+
+       chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
+       if (channel == NULL || chatnetrec == NULL) {
+               /* missing information.. */
+               return NULL;
+       }
+
+       rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
+       rec->type = module_get_uniq_id("CHANNEL SETUP", 0);
+       rec->chat_type = CHAT_PROTOCOL(chatnetrec)->id;
+       rec->autojoin = config_node_get_bool(node, "autojoin", FALSE);
+       rec->name = g_strdup(channel);
+       rec->chatnet = g_strdup(chatnetrec != NULL ? chatnetrec->name : chatnet);
+       rec->password = g_strdup(config_node_get_str(node, "password", NULL));
+       rec->botmasks = g_strdup(config_node_get_str(node, "botmasks", NULL));
+       rec->autosendcmd = g_strdup(config_node_get_str(node, "autosendcmd", NULL));
+
+       setupchannels = g_slist_append(setupchannels, rec);
+       signal_emit("channel setup created", 2, rec, node);
+       return rec;
+}
+
+static void channels_read_config(void)
+{
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+       while (setupchannels != NULL)
+               channel_setup_destroy(setupchannels->data);
+
+       /* Read channels */
+       node = iconfig_node_traverse("channels", FALSE);
+       if (node != NULL) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       channel_setup_read(tmp->data);
+       }
+}
+
+void channels_setup_init(void)
+{
+        setupchannels = NULL;
+       source_host_ok = FALSE;
+
+        signal_add("setup reread", (SIGNAL_FUNC) channels_read_config);
+        signal_add("irssi init read settings", (SIGNAL_FUNC) channels_read_config);
+}
+
+void channels_setup_deinit(void)
+{
+       while (setupchannels != NULL)
+               channel_setup_destroy(setupchannels->data);
+
+        signal_remove("setup reread", (SIGNAL_FUNC) channels_read_config);
+        signal_remove("irssi init read settings", (SIGNAL_FUNC) channels_read_config);
+}
diff --git a/apps/irssi/src/core/channels-setup.h b/apps/irssi/src/core/channels-setup.h
new file mode 100644 (file)
index 0000000..423bccb
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __CHANNELS_SETUP_H
+#define __CHANNELS_SETUP_H
+
+#include "modules.h"
+
+#define CHANNEL_SETUP(server) \
+       MODULE_CHECK_CAST(server, CHANNEL_SETUP_REC, type, "CHANNEL SETUP")
+
+#define IS_CHANNEL_SETUP(server) \
+       (CHANNEL_SETUP(server) ? TRUE : FALSE)
+
+struct _CHANNEL_SETUP_REC {
+#include "channel-setup-rec.h"
+};
+
+extern GSList *setupchannels;
+
+void channels_setup_init(void);
+void channels_setup_deinit(void);
+
+void channel_setup_create(CHANNEL_SETUP_REC *channel);
+void channel_setup_remove(CHANNEL_SETUP_REC *channel);
+
+CHANNEL_SETUP_REC *channel_setup_find(const char *channel,
+                                     const char *chatnet);
+
+#define channel_chatnet_match(rec, chatnet) \
+       ((rec) == NULL || (rec)[0] == '\0' || \
+        ((chatnet) != NULL && g_strcasecmp(rec, chatnet) == 0))
+
+#endif
diff --git a/apps/irssi/src/core/channels.c b/apps/irssi/src/core/channels.c
new file mode 100644 (file)
index 0000000..57288f7
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ channel.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "special-vars.h"
+
+#include "servers.h"
+#include "channels.h"
+#include "channels-setup.h"
+#include "nicklist.h"
+
+GSList *channels; /* List of all channels */
+
+static char *get_join_data(CHANNEL_REC *channel)
+{
+       return g_strdup(channel->name);
+}
+
+void channel_init(CHANNEL_REC *channel, int automatic)
+{
+       g_return_if_fail(channel != NULL);
+       g_return_if_fail(channel->name != NULL);
+
+       channels = g_slist_append(channels, channel);
+       if (channel->server != NULL) {
+               channel->server->channels =
+                       g_slist_append(channel->server->channels, channel);
+       }
+
+        MODULE_DATA_INIT(channel);
+       channel->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
+        channel->destroy = (void (*) (WI_ITEM_REC *)) channel_destroy;
+        channel->mode = g_strdup("");
+       channel->createtime = time(NULL);
+        channel->get_join_data = get_join_data;
+
+       signal_emit("channel created", 2, channel, GINT_TO_POINTER(automatic));
+}
+
+void channel_destroy(CHANNEL_REC *channel)
+{
+       g_return_if_fail(IS_CHANNEL(channel));
+
+       if (channel->destroying) return;
+       channel->destroying = TRUE;
+
+       channels = g_slist_remove(channels, channel);
+       if (channel->server != NULL)
+               channel->server->channels = g_slist_remove(channel->server->channels, channel);
+       signal_emit("channel destroyed", 1, channel);
+
+        MODULE_DATA_DEINIT(channel);
+       g_free_not_null(channel->hilight_color);
+       g_free_not_null(channel->topic);
+       g_free_not_null(channel->topic_by);
+       g_free_not_null(channel->key);
+       g_free(channel->mode);
+       g_free(channel->name);
+
+        channel->type = 0;
+       g_free(channel);
+}
+
+static CHANNEL_REC *channel_find_server(SERVER_REC *server,
+                                       const char *name)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(IS_SERVER(server), NULL);
+
+       if (server->channel_find_func != NULL) {
+               /* use the server specific channel find function */
+               return server->channel_find_func(server, name);
+       }
+
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *rec = tmp->data;
+
+               if (g_strcasecmp(name, rec->name) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+CHANNEL_REC *channel_find(SERVER_REC *server, const char *name)
+{
+       g_return_val_if_fail(server == NULL || IS_SERVER(server), NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+
+       if (server != NULL)
+               return channel_find_server(server, name);
+
+       /* find from any server */
+       return gslist_foreach_find(servers,
+                                  (FOREACH_FIND_FUNC) channel_find_server,
+                                  (void *) name);
+}
+
+static CHANNEL_REC *channel_find_servers(GSList *servers, const char *name)
+{
+       return gslist_foreach_find(servers,
+                                  (FOREACH_FIND_FUNC) channel_find_server,
+                                  (void *) name);
+}
+
+static GSList *servers_find_chatnet_except(SERVER_REC *server)
+{
+       GSList *tmp, *list;
+
+        list = NULL;
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *rec = tmp->data;
+
+               if (server != rec && rec->connrec->chatnet != NULL &&
+                   strcmp(server->connrec->chatnet,
+                          rec->connrec->chatnet) == 0) {
+                       /* chatnets match */
+                       list = g_slist_append(list, rec);
+               }
+       }
+
+        return list;
+}
+
+/* connected to server, autojoin to channels. */
+static void event_connected(SERVER_REC *server)
+{
+       GString *chans;
+       GSList *tmp, *chatnet_servers;
+
+       g_return_if_fail(SERVER(server));
+
+       if (server->connrec->reconnection ||
+           server->connrec->no_autojoin_channels)
+               return;
+
+       /* get list of servers in same chat network */
+       chatnet_servers = server->connrec->chatnet == NULL ? NULL:
+               servers_find_chatnet_except(server);
+
+       /* join to the channels marked with autojoin in setup */
+       chans = g_string_new(NULL);
+       for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_SETUP_REC *rec = tmp->data;
+
+               if (!rec->autojoin ||
+                   !channel_chatnet_match(rec->chatnet,
+                                          server->connrec->chatnet))
+                       continue;
+
+               /* check that we haven't already joined this channel in
+                  same chat network connection.. */
+                if (channel_find_servers(chatnet_servers, rec->name) == NULL)
+                       g_string_sprintfa(chans, "%s,", rec->name);
+       }
+        g_slist_free(chatnet_servers);
+
+       if (chans->len > 0) {
+               g_string_truncate(chans, chans->len-1);
+               server->channels_join(server, chans->str, TRUE);
+       }
+
+       g_string_free(chans, TRUE);
+}
+
+static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag)
+{
+       const char *flags = server->get_nick_flags();
+
+       return strchr(flags, flag) == NULL ||
+               (flag == flags[0] && nick->op) ||
+               (flag == flags[1] && (nick->voice || nick->halfop ||
+                                     nick->op)) ||
+               (flag == flags[2] && (nick->halfop || nick->op));
+}
+
+/* Send the auto send command to channel */
+void channel_send_autocommands(CHANNEL_REC *channel)
+{
+       CHANNEL_SETUP_REC *rec;
+       NICK_REC *nick;
+       char **bots, **bot;
+
+       g_return_if_fail(IS_CHANNEL(channel));
+
+       if (channel->session_rejoin)
+                return;
+
+       rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
+       if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
+               return;
+
+       if (rec->botmasks == NULL || !*rec->botmasks) {
+               /* just send the command. */
+               eval_special_string(rec->autosendcmd, "", channel->server, channel);
+               return;
+       }
+
+       /* find first available bot.. */
+       bots = g_strsplit(rec->botmasks, " ", -1);
+       for (bot = bots; *bot != NULL; bot++) {
+               const char *botnick = *bot;
+
+               if (*botnick == '\0')
+                        continue;
+
+               nick = nicklist_find_mask(channel,
+                                         channel->server->isnickflag(*botnick) ?
+                                         botnick+1 : botnick);
+               if (nick != NULL &&
+                   match_nick_flags(channel->server, nick, *botnick)) {
+                       eval_special_string(rec->autosendcmd, nick->nick,
+                                           channel->server, channel);
+                       break;
+               }
+       }
+       g_strfreev(bots);
+}
+
+void channels_init(void)
+{
+       channels_setup_init();
+
+       signal_add("event connected", (SIGNAL_FUNC) event_connected);
+}
+
+void channels_deinit(void)
+{
+       channels_setup_deinit();
+
+       signal_remove("event connected", (SIGNAL_FUNC) event_connected);
+       module_uniq_destroy("CHANNEL");
+}
diff --git a/apps/irssi/src/core/channels.h b/apps/irssi/src/core/channels.h
new file mode 100644 (file)
index 0000000..98b75ee
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __CHANNELS_H
+#define __CHANNELS_H
+
+#include "modules.h"
+
+/* Returns CHANNEL_REC if it's channel, NULL if it isn't. */
+#define CHANNEL(channel) \
+       MODULE_CHECK_CAST_MODULE(channel, CHANNEL_REC, type, \
+                             "WINDOW ITEM TYPE", "CHANNEL")
+
+#define IS_CHANNEL(channel) \
+       (CHANNEL(channel) ? TRUE : FALSE)
+
+#define STRUCT_SERVER_REC SERVER_REC
+struct _CHANNEL_REC {
+#include "channel-rec.h"
+};
+
+extern GSList *channels;
+
+/* Create new channel record */
+void channel_init(CHANNEL_REC *channel, int automatic);
+void channel_destroy(CHANNEL_REC *channel);
+
+/* find channel by name, if `server' is NULL, search from all servers */
+CHANNEL_REC *channel_find(SERVER_REC *server, const char *name);
+
+/* Send the auto send command to channel */
+void channel_send_autocommands(CHANNEL_REC *channel);
+
+void channels_init(void);
+void channels_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/chat-commands.c b/apps/irssi/src/core/chat-commands.c
new file mode 100644 (file)
index 0000000..8b1b373
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ chat-commands.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "network.h"
+#include "signals.h"
+#include "commands.h"
+#include "special-vars.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "servers-reconnect.h"
+#include "channels.h"
+#include "queries.h"
+#include "window-item-def.h"
+#include "rawlog.h"
+
+static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
+                                             char **rawlog_file)
+{
+        CHAT_PROTOCOL_REC *proto;
+       SERVER_CONNECT_REC *conn;
+       GHashTable *optlist;
+       char *addr, *portstr, *password, *nick, *chatnet, *host;
+       void *free_arg;
+
+       g_return_val_if_fail(data != NULL, NULL);
+
+       if (!cmd_get_params(data, &free_arg, 4 | PARAM_FLAG_OPTIONS,
+                           "connect", &optlist, &addr, &portstr,
+                           &password, &nick))
+               return NULL;
+       if (plus_addr != NULL) *plus_addr = *addr == '+';
+       if (*addr == '+') addr++;
+       if (*addr == '\0') {
+               signal_emit("error command", 1,
+                           GINT_TO_POINTER(CMDERR_NOT_ENOUGH_PARAMS));
+               cmd_params_free(free_arg);
+               return NULL;
+       }
+
+       if (strcmp(password, "-") == 0)
+               *password = '\0';
+
+        /* check if -<chatnet> option is used to specify chat protocol */
+       proto = chat_protocol_find_net(optlist);
+
+       /* connect to server */
+       chatnet = proto == NULL ? NULL :
+               g_hash_table_lookup(optlist, proto->chatnet);
+       conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
+                                 atoi(portstr), chatnet, password, nick);
+        if (proto == NULL)
+               proto = chat_protocol_find_id(conn->chat_type);
+
+       if (proto->not_initialized) {
+               /* trying to use protocol that isn't yet initialized */
+               signal_emit("chat protocol unknown", 1, proto->name);
+               server_connect_unref(conn);
+                cmd_params_free(free_arg);
+               return NULL;
+       }
+
+       if (g_hash_table_lookup(optlist, "6") != NULL)
+               conn->family = AF_INET6;
+       else if (g_hash_table_lookup(optlist, "4") != NULL)
+               conn->family = AF_INET;
+
+       if (g_hash_table_lookup(optlist, "!") != NULL)
+               conn->no_autojoin_channels = TRUE;
+
+       if (g_hash_table_lookup(optlist, "noproxy") != NULL)
+                g_free_and_null(conn->proxy);
+
+       *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog"));
+
+        host = g_hash_table_lookup(optlist, "host");
+       if (host != NULL && *host != '\0') {
+               IPADDR ip4, ip6;
+
+               if (net_gethostbyname(host, &ip4, &ip6) == 0)
+                        server_connect_own_ip_save(conn, &ip4, &ip6);
+       }
+
+       cmd_params_free(free_arg);
+        return conn;
+}
+
+/* SYNTAX: CONNECT [-4 | -6] [-ircnet <ircnet>] [-host <hostname>]
+                   <address>|<chatnet> [<port> [<password> [<nick>]]] */
+static void cmd_connect(const char *data)
+{
+       SERVER_CONNECT_REC *conn;
+       SERVER_REC *server;
+        char *rawlog_file;
+
+       conn = get_server_connect(data, NULL, &rawlog_file);
+       if (conn != NULL) {
+               server = CHAT_PROTOCOL(conn)->server_connect(conn);
+                server_connect_unref(conn);
+
+               if (server != NULL && rawlog_file != NULL)
+                       rawlog_open(server->rawlog, rawlog_file);
+
+               g_free(rawlog_file);
+       }
+}
+
+static RECONNECT_REC *find_reconnect_server(int chat_type,
+                                           const char *addr, int port)
+{
+       RECONNECT_REC *match, *last_proto_match;
+       GSList *tmp;
+        int count;
+
+       g_return_val_if_fail(addr != NULL, NULL);
+
+       /* check if there's a reconnection to the same host and maybe even
+          the same port */
+        match = last_proto_match = NULL; count = 0;
+       for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
+               RECONNECT_REC *rec = tmp->data;
+
+               if (rec->conn->chat_type == chat_type) {
+                       count++; last_proto_match = rec;
+                       if (g_strcasecmp(rec->conn->address, addr) == 0) {
+                               if (rec->conn->port == port)
+                                       return rec;
+                               match = rec;
+                       }
+               }
+       }
+
+       if (count == 1) {
+               /* only one reconnection with wanted protocol,
+                  we probably want to use it */
+                return last_proto_match;
+       }
+
+       return match;
+}
+
+static void update_reconnection(SERVER_CONNECT_REC *conn, SERVER_REC *server)
+{
+       SERVER_CONNECT_REC *oldconn;
+       RECONNECT_REC *recon;
+
+       if (server != NULL) {
+               oldconn = server->connrec;
+                server_connect_ref(oldconn);
+                reconnect_save_status(conn, server);
+       } else {
+               /* maybe we can reconnect some server from
+                  reconnection queue */
+               recon = find_reconnect_server(conn->chat_type,
+                                             conn->address, conn->port);
+               if (recon == NULL) return;
+
+               oldconn = recon->conn;
+                server_connect_ref(oldconn);
+               server_reconnect_destroy(recon);
+
+               conn->away_reason = g_strdup(oldconn->away_reason);
+               conn->channels = g_strdup(oldconn->channels);
+       }
+
+       conn->reconnection = TRUE;
+
+       if (conn->chatnet == NULL && oldconn->chatnet != NULL)
+               conn->chatnet = g_strdup(oldconn->chatnet);
+
+       server_connect_unref(oldconn);
+       if (server != NULL) {
+               signal_emit("command disconnect", 2,
+                           "* Changing server", server);
+       }
+}
+
+static void cmd_server(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       command_runsub("server", data, server, item);
+}
+
+static void sig_default_command_server(const char *data, SERVER_REC *server,
+                                      WI_ITEM_REC *item)
+{
+        signal_emit("command server connect", 3, data, server, item);
+}
+
+/* SYNTAX: SERVER [-4 | -6] [-ircnet <ircnet>] [-host <hostname>]
+                  [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
+static void cmd_server_connect(const char *data, SERVER_REC *server)
+{
+       SERVER_CONNECT_REC *conn;
+        char *rawlog_file;
+       int plus_addr;
+
+       g_return_if_fail(data != NULL);
+
+        /* create connection record */
+       conn = get_server_connect(data, &plus_addr, &rawlog_file);
+       if (conn != NULL) {
+               if (!plus_addr)
+                       update_reconnection(conn, server);
+               server = CHAT_PROTOCOL(conn)->server_connect(conn);
+               server_connect_unref(conn);
+
+               if (server != NULL && rawlog_file != NULL)
+                       rawlog_open(server->rawlog, rawlog_file);
+
+               g_free(rawlog_file);
+       }
+}
+
+/* SYNTAX: DISCONNECT *|<tag> [<message>] */
+static void cmd_disconnect(const char *data, SERVER_REC *server)
+{
+       char *tag, *msg;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &tag, &msg))
+               return;
+
+       if (*tag != '\0' && strcmp(tag, "*") != 0)
+               server = server_find_tag(tag);
+       if (server == NULL) cmd_param_error(CMDERR_NOT_CONNECTED);
+
+       if (*msg == '\0') msg = (char *) settings_get_str("quit_message");
+       signal_emit("server quit", 2, server, msg);
+
+       cmd_params_free(free_arg);
+       server_disconnect(server);
+}
+
+/* SYNTAX: QUIT [<message>] */
+static void cmd_quit(const char *data)
+{
+       GSList *tmp, *next;
+       const char *quitmsg;
+       char *str;
+
+       g_return_if_fail(data != NULL);
+
+       quitmsg = *data != '\0' ? data :
+               settings_get_str("quit_message");
+
+       /* disconnect from every server */
+       for (tmp = servers; tmp != NULL; tmp = next) {
+               next = tmp->next;
+
+               str = g_strdup_printf("* %s", quitmsg);
+               cmd_disconnect(str, tmp->data);
+               g_free(str);
+       }
+
+       signal_emit("gui exit", 0);
+}
+
+/* SYNTAX: JOIN [-invite] [-<server tag>] <channels> [<keys>] */
+static void cmd_join(const char *data, SERVER_REC *server)
+{
+       GHashTable *optlist;
+       char *channels;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "join", &optlist, &channels))
+               return;
+
+       /* -<server tag> */
+       server = cmd_options_get_server("join", optlist, server);
+       if (server == NULL || !server->connected)
+                cmd_param_error(CMDERR_NOT_CONNECTED);
+
+       if (g_hash_table_lookup(optlist, "invite"))
+               channels = server->last_invite;
+       else {
+               if (*channels == '\0')
+                       cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+       }
+
+       if (channels != NULL)
+               server->channels_join(server, channels, FALSE);
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */
+static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       GHashTable *optlist;
+       char *target, *origtarget, *msg;
+       void *free_arg;
+       int free_ret, target_type;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "msg", &optlist, &target, &msg))
+               return;
+       if (*target == '\0' || *msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       server = cmd_options_get_server("msg", optlist, SERVER(server));
+       if (server == NULL || !server->connected)
+               cmd_param_error(CMDERR_NOT_CONNECTED);
+
+        origtarget = target;
+       free_ret = FALSE;
+       if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0) {
+               target = parse_special(&target, server, item,
+                                      NULL, &free_ret, NULL, 0);
+               if (target != NULL && *target == '\0')
+                       target = NULL;
+       }
+
+       if (strcmp(target, "*") == 0) {
+                /* send to active channel/query */
+               if (item == NULL)
+                       cmd_param_error(CMDERR_NOT_JOINED);
+
+               target_type = IS_CHANNEL(item) ?
+                       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+               target = item->name;
+       }
+       else if (g_hash_table_lookup(optlist, "channel") != NULL)
+                target_type = SEND_TARGET_CHANNEL;
+       else if (g_hash_table_lookup(optlist, "nick") != NULL)
+               target_type = SEND_TARGET_NICK;
+       else {
+               /* Need to rely on server_ischannel(). If the protocol
+                  doesn't really know if it's channel or nick based on the
+                  name, it should just assume it's nick, because when typing
+                  text to channels it's always sent with /MSG -channel. */
+               target_type = server_ischannel(server, target) ?
+                       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+       }
+
+       if (target != NULL)
+               server->send_message(server, target, msg, target_type);
+
+       signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ?
+                   "message own_public" : "message own_private", 4,
+                   server, msg, target, origtarget);
+
+       if (free_ret && target != NULL) g_free(target);
+       cmd_params_free(free_arg);
+}
+
+static void cmd_foreach(const char *data, SERVER_REC *server,
+                       WI_ITEM_REC *item)
+{
+       command_runsub("foreach", data, server, item);
+}
+
+/* SYNTAX: FOREACH SERVER <command> */
+static void cmd_foreach_server(const char *data, SERVER_REC *server)
+{
+        GSList *list;
+
+       list = g_slist_copy(servers);
+       while (list != NULL) {
+               signal_emit("send command", 3, data, list->data, NULL);
+                list = g_slist_remove(list, list->data);
+       }
+}
+
+/* SYNTAX: FOREACH CHANNEL <command> */
+static void cmd_foreach_channel(const char *data)
+{
+        GSList *list;
+
+       list = g_slist_copy(channels);
+       while (list != NULL) {
+               CHANNEL_REC *rec = list->data;
+
+               signal_emit("send command", 3, data, rec->server, rec);
+                list = g_slist_remove(list, list->data);
+       }
+}
+
+/* SYNTAX: FOREACH QUERY <command> */
+static void cmd_foreach_query(const char *data)
+{
+        GSList *list;
+
+       list = g_slist_copy(queries);
+       while (list != NULL) {
+               QUERY_REC *rec = list->data;
+
+               signal_emit("send command", 3, data, rec->server, rec);
+                list = g_slist_remove(list, list->data);
+       }
+}
+
+void chat_commands_init(void)
+{
+       settings_add_str("misc", "quit_message", "leaving");
+
+       command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
+       command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
+       command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
+       command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
+       command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
+       command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
+       command_bind("msg", NULL, (SIGNAL_FUNC) cmd_msg);
+       command_bind("foreach", NULL, (SIGNAL_FUNC) cmd_foreach);
+       command_bind("foreach server", NULL, (SIGNAL_FUNC) cmd_foreach_server);
+       command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel);
+       command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query);
+
+        signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
+
+       command_set_options("connect", "4 6 !! +host noproxy -rawlog");
+       command_set_options("join", "invite");
+       command_set_options("msg", "channel nick");
+}
+
+void chat_commands_deinit(void)
+{
+       command_unbind("server", (SIGNAL_FUNC) cmd_server);
+       command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
+       command_unbind("connect", (SIGNAL_FUNC) cmd_connect);
+       command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
+       command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
+       command_unbind("join", (SIGNAL_FUNC) cmd_join);
+       command_unbind("msg", (SIGNAL_FUNC) cmd_msg);
+       command_unbind("foreach", (SIGNAL_FUNC) cmd_foreach);
+       command_unbind("foreach server", (SIGNAL_FUNC) cmd_foreach_server);
+       command_unbind("foreach channel", (SIGNAL_FUNC) cmd_foreach_channel);
+       command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query);
+
+        signal_remove("default command server", (SIGNAL_FUNC) sig_default_command_server);
+}
diff --git a/apps/irssi/src/core/chat-protocols.c b/apps/irssi/src/core/chat-protocols.c
new file mode 100644 (file)
index 0000000..2989598
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ chat-protocol.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "chat-protocols.h"
+
+#include "chatnets.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
+
+GSList *chat_protocols;
+
+static CHAT_PROTOCOL_REC *default_proto;
+
+void *chat_protocol_check_cast(void *object, int type_pos, const char *id)
+{
+       return object == NULL ||
+               chat_protocol_lookup(id) !=
+               G_STRUCT_MEMBER(int, object, type_pos) ? NULL : object;
+}
+
+/* Return the ID for the specified chat protocol. */
+int chat_protocol_lookup(const char *name)
+{
+       CHAT_PROTOCOL_REC *rec;
+
+       g_return_val_if_fail(name != NULL, -1);
+
+       rec = chat_protocol_find(name);
+       return rec == NULL ? -1 : rec->id;
+}
+
+CHAT_PROTOCOL_REC *chat_protocol_find(const char *name)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = chat_protocols; tmp != NULL; tmp = tmp->next) {
+               CHAT_PROTOCOL_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, name) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+CHAT_PROTOCOL_REC *chat_protocol_find_id(int id)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(id > 0, NULL);
+
+       for (tmp = chat_protocols; tmp != NULL; tmp = tmp->next) {
+               CHAT_PROTOCOL_REC *rec = tmp->data;
+
+               if (rec->id == id)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+CHAT_PROTOCOL_REC *chat_protocol_find_net(GHashTable *optlist)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(optlist != NULL, NULL);
+
+       for (tmp = chat_protocols; tmp != NULL; tmp = tmp->next) {
+               CHAT_PROTOCOL_REC *rec = tmp->data;
+
+               if (rec->chatnet != NULL &&
+                   g_hash_table_lookup(optlist, rec->chatnet) != NULL)
+                        return rec;
+       }
+
+       return NULL;
+}
+
+/* Register new chat protocol. */
+CHAT_PROTOCOL_REC *chat_protocol_register(CHAT_PROTOCOL_REC *rec)
+{
+       CHAT_PROTOCOL_REC *newrec;
+        int created;
+
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       newrec = chat_protocol_find(rec->name);
+        created = newrec == NULL;
+       if (newrec == NULL) {
+               newrec = g_new0(CHAT_PROTOCOL_REC, 1);
+               chat_protocols = g_slist_append(chat_protocols, newrec);
+       } else {
+               /* updating existing protocol */
+                g_free(newrec->name);
+       }
+
+       memcpy(newrec, rec, sizeof(CHAT_PROTOCOL_REC));
+       newrec->id = module_get_uniq_id_str("PROTOCOL", rec->name);
+       newrec->name = g_strdup(rec->name);
+
+       if (default_proto == NULL)
+                chat_protocol_set_default(newrec);
+
+        if (created)
+               signal_emit("chat protocol created", 1, newrec);
+        else
+               signal_emit("chat protocol updated", 1, newrec);
+        return newrec;
+}
+
+static void chat_protocol_destroy(CHAT_PROTOCOL_REC *rec)
+{
+       g_return_if_fail(rec != NULL);
+
+       chat_protocols = g_slist_remove(chat_protocols, rec);
+
+       if (default_proto == rec) {
+               chat_protocol_set_default(chat_protocols == NULL ? NULL :
+                                         chat_protocols->data);
+       }
+
+       signal_emit("chat protocol destroyed", 1, rec);
+
+       g_free(rec->name);
+       g_free(rec);
+}
+
+/* Unregister chat protocol. */
+void chat_protocol_unregister(const char *name)
+{
+       CHAT_PROTOCOL_REC *rec;
+
+       g_return_if_fail(name != NULL);
+
+       rec = chat_protocol_find(name);
+       if (rec != NULL) {
+               chat_protocol_destroy(rec);
+
+               /* there might still be references to this chat protocol -
+                  recreate it as a dummy protocol */
+               chat_protocol_get_unknown(name);
+       }
+}
+
+/* Default chat protocol to use */
+void chat_protocol_set_default(CHAT_PROTOCOL_REC *rec)
+{
+        default_proto = rec;
+}
+
+CHAT_PROTOCOL_REC *chat_protocol_get_default(void)
+{
+        return default_proto;
+}
+
+static CHATNET_REC *create_chatnet(void)
+{
+        return g_new0(CHATNET_REC, 1);
+}
+
+static SERVER_SETUP_REC *create_server_setup(void)
+{
+        return g_new0(SERVER_SETUP_REC, 1);
+}
+
+static CHANNEL_SETUP_REC *create_channel_setup(void)
+{
+        return g_new0(CHANNEL_SETUP_REC, 1);
+}
+
+static SERVER_CONNECT_REC *create_server_connect(void)
+{
+        return g_new0(SERVER_CONNECT_REC, 1);
+}
+
+static void destroy_server_connect(SERVER_CONNECT_REC *conn)
+{
+}
+
+/* Return "unknown chat protocol" record. Used when protocol name is
+   specified but it isn't registered yet. */
+CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name)
+{
+       CHAT_PROTOCOL_REC *rec, *newrec;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+        rec = chat_protocol_find(name);
+       if (rec != NULL)
+                return rec;
+
+       rec = g_new0(CHAT_PROTOCOL_REC, 1);
+        rec->not_initialized = TRUE;
+       rec->name = (char *) name;
+       rec->create_chatnet = create_chatnet;
+        rec->create_server_setup = create_server_setup;
+        rec->create_channel_setup = create_channel_setup;
+       rec->create_server_connect = create_server_connect;
+       rec->destroy_server_connect = destroy_server_connect;
+
+       newrec = chat_protocol_register(rec);
+       g_free(rec);
+        return newrec;
+}
+
+void chat_protocols_init(void)
+{
+       default_proto = NULL;
+       chat_protocols = NULL;
+}
+
+void chat_protocols_deinit(void)
+{
+       while (chat_protocols != NULL)
+                chat_protocol_destroy(chat_protocols->data);
+}
diff --git a/apps/irssi/src/core/chat-protocols.h b/apps/irssi/src/core/chat-protocols.h
new file mode 100644 (file)
index 0000000..eeb0075
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __CHAT_PROTOCOLS_H
+#define __CHAT_PROTOCOLS_H
+
+typedef struct {
+       int id;
+
+       unsigned int not_initialized:1;
+       unsigned int case_insensitive:1;
+
+       char *name;
+       char *fullname;
+       char *chatnet;
+
+        CHATNET_REC *(*create_chatnet) (void);
+       SERVER_SETUP_REC *(*create_server_setup) (void);
+        CHANNEL_SETUP_REC *(*create_channel_setup) (void);
+       SERVER_CONNECT_REC *(*create_server_connect) (void);
+        void (*destroy_server_connect) (SERVER_CONNECT_REC *);
+
+        SERVER_REC *(*server_connect) (SERVER_CONNECT_REC *);
+        CHANNEL_REC *(*channel_create) (SERVER_REC *, const char *, int);
+        QUERY_REC *(*query_create) (const char *, const char *, int);
+} CHAT_PROTOCOL_REC;
+
+extern GSList *chat_protocols;
+
+#define PROTO_CHECK_CAST(object, cast, type_field, id) \
+       ((cast *) chat_protocol_check_cast(object, \
+                               offsetof(cast, type_field), id))
+void *chat_protocol_check_cast(void *object, int type_pos, const char *id);
+
+#define CHAT_PROTOCOL(object) \
+       ((object) == NULL ? chat_protocol_get_default() : \
+       chat_protocol_find_id((object)->chat_type))
+
+/* Register new chat protocol. */
+CHAT_PROTOCOL_REC *chat_protocol_register(CHAT_PROTOCOL_REC *rec);
+
+/* Unregister chat protocol. */
+void chat_protocol_unregister(const char *name);
+
+/* Find functions */
+int chat_protocol_lookup(const char *name);
+CHAT_PROTOCOL_REC *chat_protocol_find(const char *name);
+CHAT_PROTOCOL_REC *chat_protocol_find_id(int id);
+CHAT_PROTOCOL_REC *chat_protocol_find_net(GHashTable *optlist);
+
+/* Default chat protocol to use */
+void chat_protocol_set_default(CHAT_PROTOCOL_REC *rec);
+CHAT_PROTOCOL_REC *chat_protocol_get_default(void);
+
+/* Return "unknown chat protocol" record. Used when protocol name is
+   specified but it isn't registered yet. */
+CHAT_PROTOCOL_REC *chat_protocol_get_unknown(const char *name);
+
+void chat_protocols_init(void);
+void chat_protocols_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/chatnet-rec.h b/apps/irssi/src/core/chatnet-rec.h
new file mode 100644 (file)
index 0000000..e3ed8aa
--- /dev/null
@@ -0,0 +1,12 @@
+int type; /* module_get_uniq_id("CHATNET", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+char *name;
+
+char *nick;
+char *username;
+char *realname;
+
+char *own_host; /* address to use when connecting this server */
+char *autosendcmd; /* command to send after connecting to this ircnet */
+IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
diff --git a/apps/irssi/src/core/chatnets.c b/apps/irssi/src/core/chatnets.c
new file mode 100644 (file)
index 0000000..8739ff7
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ chatnets.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "network.h"
+#include "signals.h"
+#include "special-vars.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+
+GSList *chatnets; /* list of available chat networks */
+
+static void chatnet_config_save(CHATNET_REC *chatnet)
+{
+       CONFIG_NODE *node;
+
+       node = iconfig_node_traverse("chatnets", TRUE);
+       node = config_node_section(node, chatnet->name, NODE_TYPE_BLOCK);
+       iconfig_node_clear(node);
+
+       iconfig_node_set_str(node, "type", chat_protocol_find_id(chatnet->chat_type)->name);
+       iconfig_node_set_str(node, "nick", chatnet->nick);
+       iconfig_node_set_str(node, "username", chatnet->username);
+       iconfig_node_set_str(node, "realname", chatnet->realname);
+       iconfig_node_set_str(node, "host", chatnet->own_host);
+       iconfig_node_set_str(node, "autosendcmd", chatnet->autosendcmd);
+
+        signal_emit("chatnet saved", 2, chatnet, node);
+}
+
+static void chatnet_config_remove(CHATNET_REC *chatnet)
+{
+       CONFIG_NODE *node;
+
+       node = iconfig_node_traverse("chatnets", FALSE);
+       if (node != NULL) iconfig_node_set_str(node, chatnet->name, NULL);
+}
+
+void chatnet_create(CHATNET_REC *chatnet)
+{
+       g_return_if_fail(chatnet != NULL);
+
+        chatnet->type = module_get_uniq_id("CHATNET", 0);
+       if (g_slist_find(chatnets, chatnet) == NULL)
+               chatnets = g_slist_append(chatnets, chatnet);
+
+       chatnet_config_save(chatnet);
+       signal_emit("chatnet created", 1, chatnet);
+}
+
+void chatnet_remove(CHATNET_REC *chatnet)
+{
+       g_return_if_fail(IS_CHATNET(chatnet));
+
+       signal_emit("chatnet removed", 1, chatnet);
+
+       chatnet_config_remove(chatnet);
+       chatnet_destroy(chatnet);
+}
+
+void chatnet_destroy(CHATNET_REC *chatnet)
+{
+       g_return_if_fail(IS_CHATNET(chatnet));
+
+       chatnets = g_slist_remove(chatnets, chatnet);
+       signal_emit("chatnet destroyed", 1, chatnet);
+
+       g_free_not_null(chatnet->nick);
+       g_free_not_null(chatnet->username);
+       g_free_not_null(chatnet->realname);
+       g_free_not_null(chatnet->own_host);
+       g_free_not_null(chatnet->autosendcmd);
+       g_free(chatnet->name);
+       g_free(chatnet);
+}
+
+/* Find the chat network by name */
+CHATNET_REC *chatnet_find(const char *name)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
+               CHATNET_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, name) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static void sig_connected(SERVER_REC *server)
+{
+       CHATNET_REC *rec;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       if (server->connrec->chatnet == NULL)
+               return;
+
+       rec = chatnet_find(server->connrec->chatnet);
+       if (rec != NULL && rec->autosendcmd)
+               eval_special_string(rec->autosendcmd, "", server, NULL);
+}
+
+static void chatnet_read(CONFIG_NODE *node)
+{
+        CHAT_PROTOCOL_REC *proto;
+       CHATNET_REC *rec;
+        char *type;
+
+       if (node == NULL || node->key == NULL)
+               return;
+
+       type = config_node_get_str(node, "type", NULL);
+       proto = type == NULL ? NULL : chat_protocol_find(type);
+       if (proto == NULL) {
+               proto = type == NULL ? chat_protocol_get_default() :
+                       chat_protocol_get_unknown(type);
+       }
+
+       if (type == NULL)
+               iconfig_node_set_str(node, "type", proto->name);
+
+       rec = proto->create_chatnet();
+       rec->type = module_get_uniq_id("CHATNET", 0);
+       rec->chat_type = proto->id;
+       rec->name = g_strdup(node->key);
+       rec->nick = g_strdup(config_node_get_str(node, "nick", NULL));
+       rec->username = g_strdup(config_node_get_str(node, "username", NULL));
+       rec->realname = g_strdup(config_node_get_str(node, "realname", NULL));
+       rec->own_host = g_strdup(config_node_get_str(node, "host", NULL));
+       rec->autosendcmd = g_strdup(config_node_get_str(node, "autosendcmd", NULL));
+
+       chatnets = g_slist_append(chatnets, rec);
+        signal_emit("chatnet read", 2, rec, node);
+}
+
+static void read_chatnets(void)
+{
+       CONFIG_NODE *node;
+        GSList *tmp;
+
+       while (chatnets != NULL)
+                chatnet_destroy(chatnets->data);
+
+       node = iconfig_node_traverse("chatnets", FALSE);
+       if (node != NULL) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                        chatnet_read(tmp->data);
+       }
+}
+
+void chatnets_init(void)
+{
+       chatnets = NULL;
+
+       signal_add_first("event connected", (SIGNAL_FUNC) sig_connected);
+       signal_add("setup reread", (SIGNAL_FUNC) read_chatnets);
+        signal_add_first("irssi init read settings", (SIGNAL_FUNC) read_chatnets);
+}
+
+void chatnets_deinit(void)
+{
+       module_uniq_destroy("CHATNET");
+
+       signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
+       signal_remove("setup reread", (SIGNAL_FUNC) read_chatnets);
+        signal_remove("irssi init read settings", (SIGNAL_FUNC) read_chatnets);
+}
diff --git a/apps/irssi/src/core/chatnets.h b/apps/irssi/src/core/chatnets.h
new file mode 100644 (file)
index 0000000..2b78f64
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __CHATNETS_H
+#define __CHATNETS_H
+
+#include "modules.h"
+
+/* Returns CHATNET_REC if it's chatnet, NULL if it isn't. */
+#define CHATNET(chatnet) \
+       MODULE_CHECK_CAST(chatnet, CHATNET_REC, type, "CHATNET")
+
+#define IS_CHATNET(chatnet) \
+       (CHATNET(chatnet) ? TRUE : FALSE)
+
+struct _CHATNET_REC {
+#include "chatnet-rec.h"
+};
+
+extern GSList *chatnets; /* list of available chat networks */
+
+/* add the chatnet to chat networks list */
+void chatnet_create(CHATNET_REC *chatnet);
+/* remove the chatnet from chat networks list */
+void chatnet_remove(CHATNET_REC *chatnet);
+/* destroy the chatnet structure. doesn't remove from config file */
+void chatnet_destroy(CHATNET_REC *chatnet);
+
+/* Find the chat network by name */
+CHATNET_REC *chatnet_find(const char *name);
+
+void chatnets_init(void);
+void chatnets_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/commands.c b/apps/irssi/src/core/commands.c
new file mode 100644 (file)
index 0000000..a96b5fa
--- /dev/null
@@ -0,0 +1,977 @@
+/*
+ commands.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "special-vars.h"
+#include "window-item-def.h"
+
+#include "servers.h"
+#include "channels.h"
+
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+GSList *commands;
+char *current_command;
+
+static int signal_default_command;
+
+static GSList *alias_runstack;
+
+COMMAND_REC *command_find(const char *cmd)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(cmd != NULL, NULL);
+
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->cmd, cmd) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static COMMAND_MODULE_REC *command_module_find(COMMAND_REC *rec,
+                                              const char *module)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(rec != NULL, NULL);
+       g_return_val_if_fail(module != NULL, NULL);
+
+       for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
+               COMMAND_MODULE_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, module) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static COMMAND_MODULE_REC *command_module_find_func(COMMAND_REC *rec,
+                                                   SIGNAL_FUNC func)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(rec != NULL, NULL);
+       g_return_val_if_fail(func != NULL, NULL);
+
+       for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
+               COMMAND_MODULE_REC *rec = tmp->data;
+
+                if (g_slist_find(rec->signals, (void *) func) != NULL)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+int command_have_sub(const char *command)
+{
+       GSList *tmp;
+       int len;
+
+       g_return_val_if_fail(command != NULL, FALSE);
+
+       /* find "command "s */
+        len = strlen(command);
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->cmd, command, len) == 0 &&
+                   rec->cmd[len] == ' ')
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static COMMAND_MODULE_REC *
+command_module_get(COMMAND_REC *rec, const char *module, int protocol)
+{
+        COMMAND_MODULE_REC *modrec;
+
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       modrec = command_module_find(rec, module);
+       if (modrec == NULL) {
+               modrec = g_new0(COMMAND_MODULE_REC, 1);
+               modrec->name = g_strdup(module);
+                modrec->protocol = -1;
+               rec->modules = g_slist_append(rec->modules, modrec);
+       }
+
+        if (protocol != -1)
+               modrec->protocol = protocol;
+
+        return modrec;
+}
+
+void command_bind_to(const char *module, int pos, const char *cmd,
+                    int protocol, const char *category, SIGNAL_FUNC func)
+{
+       COMMAND_REC *rec;
+        COMMAND_MODULE_REC *modrec;
+       char *str;
+
+       g_return_if_fail(module != NULL);
+       g_return_if_fail(cmd != NULL);
+
+       rec = command_find(cmd);
+       if (rec == NULL) {
+               rec = g_new0(COMMAND_REC, 1);
+               rec->cmd = g_strdup(cmd);
+               rec->category = category == NULL ? NULL : g_strdup(category);
+               commands = g_slist_append(commands, rec);
+       }
+        modrec = command_module_get(rec, module, protocol);
+
+        modrec->signals = g_slist_append(modrec->signals, (void *) func);
+
+       if (func != NULL) {
+               str = g_strconcat("command ", cmd, NULL);
+               signal_add_to(module, pos, str, func);
+               g_free(str);
+       }
+
+       signal_emit("commandlist new", 1, rec);
+}
+
+static void command_free(COMMAND_REC *rec)
+{
+       commands = g_slist_remove(commands, rec);
+       signal_emit("commandlist remove", 1, rec);
+
+       g_free_not_null(rec->category);
+       g_strfreev(rec->options);
+       g_free(rec->cmd);
+       g_free(rec);
+}
+
+static void command_module_free(COMMAND_MODULE_REC *modrec, COMMAND_REC *rec)
+{
+       rec->modules = g_slist_remove(rec->modules, modrec);
+
+       g_slist_free(modrec->signals);
+        g_free(modrec->name);
+        g_free_not_null(modrec->options);
+        g_free(modrec);
+}
+
+static void command_module_destroy(COMMAND_REC *rec,
+                                  COMMAND_MODULE_REC *modrec)
+{
+       GSList *tmp, *freelist;
+
+        command_module_free(modrec, rec);
+
+       /* command_set_options() might have added module declaration of it's
+          own without any signals .. check if they're the only ones left
+          and if so, destroy them. */
+        freelist = NULL;
+       for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
+               COMMAND_MODULE_REC *rec = tmp->data;
+
+               if (rec->signals == NULL)
+                       freelist = g_slist_append(freelist, rec);
+               else {
+                        g_slist_free(freelist);
+                        freelist = NULL;
+                       break;
+               }
+       }
+
+       g_slist_foreach(freelist, (GFunc) command_module_free, rec);
+       g_slist_free(freelist);
+
+       if (rec->modules == NULL)
+               command_free(rec);
+}
+
+void command_unbind(const char *cmd, SIGNAL_FUNC func)
+{
+       COMMAND_REC *rec;
+        COMMAND_MODULE_REC *modrec;
+       char *str;
+
+       g_return_if_fail(cmd != NULL);
+       g_return_if_fail(func != NULL);
+
+       rec = command_find(cmd);
+       if (rec != NULL) {
+               modrec = command_module_find_func(rec, func);
+               g_return_if_fail(modrec != NULL);
+
+               modrec->signals =
+                       g_slist_remove(modrec->signals, (void *) func);
+               if (modrec->signals == NULL)
+                       command_module_destroy(rec, modrec);
+       }
+
+       str = g_strconcat("command ", cmd, NULL);
+       signal_remove(str, func);
+       g_free(str);
+}
+
+/* Expand `cmd' - returns `cmd' if not found, NULL if more than one
+   match is found */
+static const char *command_expand(char *cmd)
+{
+       GSList *tmp;
+       const char *match;
+       int len, multiple;
+
+       g_return_val_if_fail(cmd != NULL, NULL);
+
+       multiple = FALSE;
+       match = NULL;
+       len = strlen(cmd);
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->cmd, cmd, len) == 0 &&
+                   strchr(rec->cmd+len, ' ') == NULL) {
+                       if (rec->cmd[len] == '\0') {
+                               /* full match */
+                               return rec->cmd;
+                       }
+
+                       if (match != NULL) {
+                               /* multiple matches, we still need to check
+                                  if there's some command left that is a
+                                  full match.. */
+                               multiple = TRUE;
+                       }
+
+                       /* check that this is the only match */
+                       match = rec->cmd;
+               }
+       }
+
+       if (multiple) {
+               signal_emit("error command", 2,
+                           GINT_TO_POINTER(CMDERR_AMBIGUOUS), cmd);
+               return NULL;
+       }
+
+       return match != NULL ? match : cmd;
+}
+
+void command_runsub(const char *cmd, const char *data,
+                   void *server, void *item)
+{
+       const char *newcmd;
+       char *orig, *subcmd, *defcmd, *args;
+
+       g_return_if_fail(data != NULL);
+
+        while (*data == ' ') data++;
+
+       if (*data == '\0') {
+                /* no subcommand given - list the subcommands */
+               signal_emit("list subcommands", 2, cmd);
+               return;
+       }
+
+       /* get command.. */
+       orig = subcmd = g_strdup_printf("command %s %s", cmd, data);
+       args = strchr(subcmd+8 + strlen(cmd)+1, ' ');
+       if (args != NULL) *args++ = '\0'; else args = "";
+       while (*args == ' ') args++;
+
+       /* check if this command can be expanded */
+       newcmd = command_expand(subcmd+8);
+       if (newcmd == NULL) {
+                /* ambiguous command */
+               g_free(orig);
+               return;
+       }
+
+       subcmd = g_strconcat("command ", newcmd, NULL);
+
+       g_strdown(subcmd);
+       if (!signal_emit(subcmd, 3, args, server, item)) {
+               defcmd = g_strdup_printf("default command %s", cmd);
+               if (!signal_emit(defcmd, 3, data, server, item)) {
+                       signal_emit("error command", 2,
+                                   GINT_TO_POINTER(CMDERR_UNKNOWN), subcmd+8);
+               }
+                g_free(defcmd);
+       }
+
+       g_free(subcmd);
+       g_free(orig);
+}
+
+static GSList *optlist_find(GSList *optlist, const char *option)
+{
+       while (optlist != NULL) {
+               char *name = optlist->data;
+               if (iscmdtype(*name)) name++;
+
+               if (g_strcasecmp(name, option) == 0)
+                       return optlist;
+
+               optlist = optlist->next;
+       }
+
+       return NULL;
+}
+
+int command_have_option(const char *cmd, const char *option)
+{
+       COMMAND_REC *rec;
+       char **tmp;
+
+       g_return_val_if_fail(cmd != NULL, FALSE);
+       g_return_val_if_fail(option != NULL, FALSE);
+
+        rec = command_find(cmd);
+       g_return_val_if_fail(rec != NULL, FALSE);
+
+       if (rec->options == NULL)
+               return FALSE;
+
+       for (tmp = rec->options; *tmp != NULL; tmp++) {
+               char *name = iscmdtype(**tmp) ? (*tmp)+1 : *tmp;
+
+               if (g_strcasecmp(name, option) == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void command_calc_options(COMMAND_REC *rec, const char *options)
+{
+       char **optlist, **tmp, *name, *str;
+       GSList *list, *oldopt;
+
+       optlist = g_strsplit(options, " ", -1);
+
+       if (rec->options == NULL) {
+                /* first call - use specified args directly */
+               rec->options = optlist;
+               return;
+       }
+
+       /* save old options to linked list */
+       list = NULL;
+       for (tmp = rec->options; *tmp != NULL; tmp++)
+                list = g_slist_append(list, g_strdup(*tmp));
+       g_strfreev(rec->options);
+
+       /* merge the options */
+       for (tmp = optlist; *tmp != NULL; tmp++) {
+               name = iscmdtype(**tmp) ? (*tmp)+1 : *tmp;
+
+               oldopt = optlist_find(list, name);
+               if (oldopt != NULL) {
+                        /* already specified - overwrite old defination */
+                       g_free(oldopt->data);
+                       oldopt->data = g_strdup(*tmp);
+               } else {
+                       /* new option, append to list */
+                        list = g_slist_append(list, g_strdup(*tmp));
+               }
+       }
+       g_strfreev(optlist);
+
+       /* linked list -> string[] */
+       str = gslist_to_string(list, " ");
+       rec->options = g_strsplit(str, " ", -1);
+        g_free(str);
+
+        g_slist_foreach(list, (GFunc) g_free, NULL);
+       g_slist_free(list);
+}
+
+/* recalculate options to command from options in all modules */
+static void command_update_options(COMMAND_REC *rec)
+{
+       GSList *tmp;
+
+       g_strfreev(rec->options);
+       rec->options = NULL;
+
+       for (tmp = rec->modules; tmp != NULL; tmp = tmp->next) {
+               COMMAND_MODULE_REC *modrec = tmp->data;
+
+               if (modrec->options != NULL)
+                       command_calc_options(rec, modrec->options);
+       }
+}
+
+void command_set_options_module(const char *module,
+                               const char *cmd, const char *options)
+{
+       COMMAND_REC *rec;
+       COMMAND_MODULE_REC *modrec;
+        int reload;
+
+       g_return_if_fail(module != NULL);
+       g_return_if_fail(cmd != NULL);
+       g_return_if_fail(options != NULL);
+
+        rec = command_find(cmd);
+       g_return_if_fail(rec != NULL);
+        modrec = command_module_get(rec, module, -1);
+
+       reload = modrec->options != NULL;
+        if (reload) {
+               /* options already set for the module ..
+                  we need to recalculate everything */
+               g_free(modrec->options);
+       }
+
+       modrec->options = g_strdup(options);
+
+        if (reload)
+               command_update_options(rec);
+        else
+               command_calc_options(rec, options);
+}
+
+char *cmd_get_param(char **data)
+{
+       char *pos;
+
+       g_return_val_if_fail(data != NULL, NULL);
+       g_return_val_if_fail(*data != NULL, NULL);
+
+       while (**data == ' ') (*data)++;
+       pos = *data;
+
+       while (**data != '\0' && **data != ' ') (*data)++;
+       if (**data == ' ') *(*data)++ = '\0';
+
+       return pos;
+}
+
+static char *cmd_get_quoted_param(char **data)
+{
+       char *pos, quote;
+
+       g_return_val_if_fail(data != NULL, NULL);
+       g_return_val_if_fail(*data != NULL, NULL);
+
+       while (**data == ' ') (*data)++;
+       if (**data != '\'' && **data != '"')
+               return cmd_get_param(data);
+
+       quote = **data; (*data)++;
+
+       pos = *data;
+       while (**data != '\0' && **data != quote) {
+               if (**data == '\\' && (*data)[1] != '\0')
+                        g_memmove(*data, (*data)+1, strlen(*data));
+               (*data)++;
+       }
+
+       if (**data != '\0') *(*data)++ = '\0';
+
+       return pos;
+}
+
+/* Find specified option from list of options - the `option' might be
+   shortened version of the full command. Returns index where the
+   option was found, -1 if not found or -2 if there was multiple matches. */
+static int option_find(char **array, const char *option)
+{
+       char **tmp;
+       int index, found, len, multiple;
+
+       g_return_val_if_fail(array != NULL, -1);
+       g_return_val_if_fail(option != NULL, -1);
+
+       len = strlen(option);
+
+       found = -1; index = 0; multiple = FALSE;
+       for (tmp = array; *tmp != NULL; tmp++, index++) {
+               const char *text = *tmp + iscmdtype(**tmp);
+
+               if (g_strncasecmp(text, option, len) == 0) {
+                       if (text[len] == '\0') {
+                               /* full match */
+                               return index;
+                       }
+
+                       if (found != -1) {
+                               /* multiple matches - we still need to check
+                                  if there's a full match left.. */
+                               multiple = TRUE;
+                       }
+
+                       /* partial match, check that it's the only one */
+                       found = index;
+               }
+       }
+
+       if (multiple)
+               return -2;
+
+       return found;
+}
+
+static int get_cmd_options(char **data, int ignore_unknown,
+                          const char *cmd, GHashTable *options)
+{
+       COMMAND_REC *rec;
+       char *option, *arg, **optlist;
+       int pos;
+
+       /* get option definations */
+       rec = cmd == NULL ? NULL : command_find(cmd);
+       optlist = rec == NULL ? NULL : rec->options;
+
+       option = NULL; pos = -1;
+       for (;;) {
+               if (**data == '-') {
+                       if (option != NULL && *optlist[pos] == '+') {
+                               /* required argument missing! */
+                                *data = optlist[pos] + 1;
+                               return CMDERR_OPTION_ARG_MISSING;
+                       }
+
+                       (*data)++;
+                       if (**data == '-' && i_isspace((*data)[1])) {
+                               /* -- option means end of options even
+                                  if next word starts with - */
+                               (*data)++;
+                               while (i_isspace(**data)) (*data)++;
+                               break;
+                       }
+
+                       if (**data == '\0')
+                               option = "-";
+                       else if (!i_isspace(**data))
+                               option = cmd_get_param(data);
+                       else {
+                               option = "-";
+                               (*data)++;
+                       }
+
+                       /* check if this option can have argument */
+                       pos = optlist == NULL ? -1 :
+                               option_find(optlist, option);
+
+                       if (pos == -1 && optlist != NULL &&
+                           is_numeric(option, '\0')) {
+                               /* check if we want -<number> option */
+                               pos = option_find(optlist, "#");
+                               if (pos != -1) {
+                                       g_hash_table_insert(options, "#",
+                                                           option);
+                                        pos = -3;
+                               }
+                       }
+
+                       if (pos == -1 && !ignore_unknown) {
+                               /* unknown option! */
+                                *data = option;
+                               return CMDERR_OPTION_UNKNOWN;
+                       }
+                       if (pos == -2 && !ignore_unknown) {
+                                /* multiple matches */
+                               *data = option;
+                               return CMDERR_OPTION_AMBIGUOUS;
+                       }
+                       if (pos >= 0) {
+                               /* if we used a shortcut of parameter, put
+                                  the whole parameter name in options table */
+                               option = optlist[pos] +
+                                       iscmdtype(*optlist[pos]);
+                       }
+                       if (options != NULL && pos != -3)
+                               g_hash_table_insert(options, option, "");
+
+                       if (pos < 0 || !iscmdtype(*optlist[pos]) ||
+                           *optlist[pos] == '!')
+                               option = NULL;
+
+                       while (i_isspace(**data)) (*data)++;
+                       continue;
+               }
+
+               if (option == NULL)
+                       break;
+
+               if (*optlist[pos] == '@' && !i_isdigit(**data))
+                       break; /* expected a numeric argument */
+
+               /* save the argument */
+               arg = cmd_get_quoted_param(data);
+               if (options != NULL) {
+                       g_hash_table_remove(options, option);
+                       g_hash_table_insert(options, option, arg);
+               }
+               option = NULL;
+
+               while (i_isspace(**data)) (*data)++;
+       }
+
+       return 0;
+}
+
+typedef struct {
+       char *data;
+        GHashTable *options;
+} CMD_TEMP_REC;
+
+static char *get_optional_channel(WI_ITEM_REC *active_item, char **data,
+                                 int require_name)
+{
+        CHANNEL_REC *chanrec;
+       char *tmp, *origtmp, *channel, *ret;
+
+       if (active_item == NULL) {
+                /* no active channel in window, channel required */
+               return cmd_get_param(data);
+       }
+
+       origtmp = tmp = g_strdup(*data);
+       channel = cmd_get_param(&tmp);
+
+       if (strcmp(channel, "*") == 0 && !require_name) {
+                /* "*" means active channel */
+               cmd_get_param(data);
+               ret = active_item->name;
+       } else if (!server_ischannel(active_item->server, channel)) {
+                /* we don't have channel parameter - use active channel */
+               ret = active_item->name;
+       } else {
+               /* Find the channel first and use it's name if found.
+                  This allows automatic !channel -> !XXXXXchannel replaces. */
+                channel = cmd_get_param(data);
+
+               chanrec = channel_find(active_item->server, channel);
+               ret = chanrec == NULL ? channel : chanrec->name;
+       }
+
+       g_free(origtmp);
+        return ret;
+}
+
+int cmd_get_params(const char *data, gpointer *free_me, int count, ...)
+{
+        WI_ITEM_REC *item;
+       CMD_TEMP_REC *rec;
+       GHashTable **opthash;
+       char **str, *arg, *datad;
+       va_list args;
+       int cnt, error, ignore_unknown, require_name;
+
+       g_return_val_if_fail(data != NULL, FALSE);
+
+       va_start(args, count);
+
+       rec = g_new0(CMD_TEMP_REC, 1);
+       rec->data = g_strdup(data);
+       *free_me = rec;
+
+        datad = rec->data;
+       error = FALSE;
+
+       item = (count & PARAM_FLAG_OPTCHAN) == 0 ? NULL:
+               (WI_ITEM_REC *) va_arg(args, WI_ITEM_REC *);
+
+       if (count & PARAM_FLAG_OPTIONS) {
+               arg = (char *) va_arg(args, char *);
+               opthash = (GHashTable **) va_arg(args, GHashTable **);
+
+               rec->options = *opthash =
+                       g_hash_table_new((GHashFunc) g_istr_hash,
+                                        (GCompareFunc) g_istr_equal);
+
+               ignore_unknown = count & PARAM_FLAG_UNKNOWN_OPTIONS;
+               error = get_cmd_options(&datad, ignore_unknown,
+                                       arg, *opthash);
+       }
+
+       if (!error) {
+               /* and now handle the string */
+               cnt = PARAM_WITHOUT_FLAGS(count);
+               if (count & PARAM_FLAG_OPTCHAN) {
+                       /* optional channel as first parameter */
+                        require_name = (count & PARAM_FLAG_OPTCHAN_NAME);
+                       arg = get_optional_channel(item, &datad, require_name);
+
+                       str = (char **) va_arg(args, char **);
+                       if (str != NULL) *str = arg;
+                       cnt--;
+               }
+
+               while (cnt-- > 0) {
+                       if (cnt == 0 && count & PARAM_FLAG_GETREST) {
+                               /* get rest */
+                               arg = datad;
+                       } else {
+                               arg = (count & PARAM_FLAG_NOQUOTES) ?
+                                       cmd_get_param(&datad) :
+                                       cmd_get_quoted_param(&datad);
+                       }
+
+                       str = (char **) va_arg(args, char **);
+                       if (str != NULL) *str = arg;
+               }
+       }
+       va_end(args);
+
+       if (error) {
+                signal_emit("error command", 2, GINT_TO_POINTER(error), datad);
+               signal_stop();
+
+                cmd_params_free(rec);
+               *free_me = NULL;
+       }
+
+       return !error;
+}
+
+void cmd_params_free(void *free_me)
+{
+       CMD_TEMP_REC *rec = free_me;
+
+       if (rec->options != NULL) g_hash_table_destroy(rec->options);
+       g_free(rec->data);
+       g_free(rec);
+}
+
+static void command_module_unbind_all(COMMAND_REC *rec,
+                                     COMMAND_MODULE_REC *modrec)
+{
+       GSList *tmp, *next;
+
+       for (tmp = modrec->signals; tmp != NULL; tmp = next) {
+               next = tmp->next;
+
+               command_unbind(rec->cmd, (SIGNAL_FUNC) tmp->data);
+       }
+
+       if (g_slist_find(commands, rec) != NULL) {
+               /* this module might have removed some options
+                  from command, update them. */
+               command_update_options(rec);
+       }
+}
+
+void commands_remove_module(const char *module)
+{
+       GSList *tmp, *next, *modlist;
+
+       g_return_if_fail(module != NULL);
+
+       for (tmp = commands; tmp != NULL; tmp = next) {
+               COMMAND_REC *rec = tmp->data;
+
+                next = tmp->next;
+               modlist = gslist_find_string(rec->modules, module);
+               if (modlist != NULL)
+                       command_module_unbind_all(rec, modlist->data);
+       }
+}
+
+static int cmd_protocol_match(COMMAND_REC *cmd, SERVER_REC *server)
+{
+       GSList *tmp;
+
+       for (tmp = cmd->modules; tmp != NULL; tmp = tmp->next) {
+               COMMAND_MODULE_REC *rec = tmp->data;
+
+               if (rec->protocol == -1) {
+                       /* at least one module accepts the command
+                          without specific protocol */
+                       return 1;
+               }
+
+               if (server != NULL && rec->protocol == server->chat_type) {
+                        /* matching protocol found */
+                        return 1;
+               }
+       }
+
+        return 0;
+}
+
+#define alias_runstack_push(alias) \
+       alias_runstack = g_slist_append(alias_runstack, alias)
+
+#define alias_runstack_pop(alias) \
+       alias_runstack = g_slist_remove(alias_runstack, alias)
+
+#define alias_runstack_find(alias) \
+        (gslist_find_icase_string(alias_runstack, alias) != NULL)
+
+static void parse_command(const char *command, int expand_aliases,
+                         SERVER_REC *server, void *item)
+{
+        COMMAND_REC *rec;
+       const char *alias, *newcmd;
+       char *cmd, *orig, *args, *oldcmd;
+
+       g_return_if_fail(command != NULL);
+
+       cmd = orig = g_strconcat("command ", command, NULL);
+       args = strchr(cmd+8, ' ');
+       if (args != NULL) *args++ = '\0'; else args = "";
+
+       /* check if there's an alias for command. Don't allow
+          recursive aliases */
+       alias = !expand_aliases || alias_runstack_find(cmd+8) ? NULL :
+               alias_find(cmd+8);
+       if (alias != NULL) {
+                alias_runstack_push(cmd+8);
+               eval_special_string(alias, args, server, item);
+                alias_runstack_pop(cmd+8);
+               g_free(orig);
+               return;
+       }
+
+       /* check if this command can be expanded */
+       newcmd = command_expand(cmd+8);
+       if (newcmd == NULL) {
+                /* ambiguous command */
+               g_free(orig);
+               return;
+       }
+
+       rec = command_find(newcmd);
+       if (rec != NULL && !cmd_protocol_match(rec, server)) {
+               g_free(orig);
+
+               signal_emit("error command", 2,
+                           GINT_TO_POINTER(server == NULL ?
+                                           CMDERR_NOT_CONNECTED :
+                                           CMDERR_ILLEGAL_PROTO));
+               return;
+       }
+
+       cmd = g_strconcat("command ", newcmd, NULL);
+       g_strdown(cmd);
+
+       oldcmd = current_command;
+       current_command = cmd+8;
+        if (server != NULL) server_ref(server);
+        if (!signal_emit(cmd, 3, args, server, item)) {
+               signal_emit_id(signal_default_command, 3,
+                              command, server, item);
+       }
+       if (server != NULL) {
+               if (server->connection_lost)
+                       server_disconnect(server);
+               server_unref(server);
+       }
+       current_command = oldcmd;
+
+       g_free(cmd);
+       g_free(orig);
+}
+
+static void event_command(const char *line, SERVER_REC *server, void *item)
+{
+       char *cmdchar;
+       int expand_aliases = TRUE;
+
+       g_return_if_fail(line != NULL);
+
+       if (*line == '\0') {
+               /* empty line, forget it. */
+                signal_stop();
+               return;
+       }
+
+       cmdchar = strchr(settings_get_str("cmdchars"), *line);
+       if (cmdchar != NULL && line[1] == ' ') {
+               /* "/ text" = same as sending "text" to active channel. */
+               line += 2;
+               cmdchar = NULL;
+       }
+       if (cmdchar == NULL) {
+               /* non-command - let someone else handle this */
+               signal_emit("send text", 3, line, server, item);
+               return;
+       }
+
+       /* same cmdchar twice ignores aliases ignores aliases */
+       line++;
+       if (*line == *cmdchar) {
+               line++;
+               expand_aliases = FALSE;
+       }
+
+       /* ^command hides the output - we'll do this at fe-common but
+          we have to skip the ^ char here.. */
+       if (*line == '^') line++;
+
+       parse_command(line, expand_aliases, server, item);
+}
+
+/* SYNTAX: EVAL <command(s)> */
+static void cmd_eval(const char *data, SERVER_REC *server, void *item)
+{
+       g_return_if_fail(data != NULL);
+
+       eval_special_string(data, "", server, item);
+}
+
+/* SYNTAX: CD <directory> */
+static void cmd_cd(const char *data)
+{
+       char *str;
+
+       g_return_if_fail(data != NULL);
+       if (*data == '\0') return;
+
+       str = convert_home(data);
+       chdir(str);
+       g_free(str);
+}
+
+void commands_init(void)
+{
+       commands = NULL;
+       current_command = NULL;
+       alias_runstack = NULL;
+
+       signal_default_command = signal_get_uniq_id("default command");
+
+       settings_add_str("misc", "cmdchars", "/");
+       signal_add("send command", (SIGNAL_FUNC) event_command);
+
+       command_bind("eval", NULL, (SIGNAL_FUNC) cmd_eval);
+       command_bind("cd", NULL, (SIGNAL_FUNC) cmd_cd);
+}
+
+void commands_deinit(void)
+{
+       g_free_not_null(current_command);
+
+       signal_remove("send command", (SIGNAL_FUNC) event_command);
+
+       command_unbind("eval", (SIGNAL_FUNC) cmd_eval);
+       command_unbind("cd", (SIGNAL_FUNC) cmd_cd);
+}
diff --git a/apps/irssi/src/core/commands.h b/apps/irssi/src/core/commands.h
new file mode 100644 (file)
index 0000000..3e55a2a
--- /dev/null
@@ -0,0 +1,155 @@
+#ifndef __COMMANDS_H
+#define __COMMANDS_H
+
+#include "signals.h"
+
+typedef struct {
+       char *name;
+       char *options;
+        int protocol; /* chat protocol required for this command */
+        GSList *signals;
+} COMMAND_MODULE_REC;
+
+typedef struct {
+        GSList *modules;
+       char *category;
+       char *cmd;
+       char **options; /* combined from modules[..]->options */
+} COMMAND_REC;
+
+enum {
+       CMDERR_OPTION_UNKNOWN = -3, /* unknown -option */
+       CMDERR_OPTION_AMBIGUOUS = -2, /* ambiguous -option */
+       CMDERR_OPTION_ARG_MISSING = -1, /* argument missing for -option */
+
+       CMDERR_UNKNOWN, /* unknown command */
+       CMDERR_AMBIGUOUS, /* ambiguous command */
+
+        CMDERR_ERRNO, /* get the error from errno */
+       CMDERR_NOT_ENOUGH_PARAMS, /* not enough parameters given */
+       CMDERR_NOT_CONNECTED, /* not connected to server */
+       CMDERR_NOT_JOINED, /* not joined to any channels in this window */
+       CMDERR_CHAN_NOT_FOUND, /* channel not found */
+       CMDERR_CHAN_NOT_SYNCED, /* channel not fully synchronized yet */
+       CMDERR_ILLEGAL_PROTO, /* requires different chat protocol than the active server */
+       CMDERR_NOT_GOOD_IDEA /* not good idea to do, -yes overrides this */
+};
+
+/* Return the full command for `alias' */
+#define alias_find(alias) \
+       iconfig_get_str("aliases", alias, NULL)
+
+/* Returning from command function with error */
+#define cmd_return_error(a) \
+       G_STMT_START { \
+         signal_emit("error command", 1, GINT_TO_POINTER(a)); \
+         signal_stop(); \
+         return; \
+       } G_STMT_END
+
+#define cmd_param_error(a) \
+       G_STMT_START { \
+         cmd_params_free(free_arg); \
+         cmd_return_error(a); \
+       } G_STMT_END
+
+extern GSList *commands;
+extern char *current_command; /* the command we're right now. */
+
+/* Bind command to specified function. */
+void command_bind_to(const char *module, int pos, const char *cmd,
+                     int protocol, const char *category, SIGNAL_FUNC func);
+#define command_bind(a, b, c) command_bind_to(MODULE_NAME, 1, a, -1, b, c)
+#define command_bind_first(a, b, c) command_bind_to(MODULE_NAME, 0, a, -1, b, c)
+#define command_bind_last(a, b, c) command_bind_to(MODULE_NAME, 2, a, -1, b, c)
+
+#define command_bind_proto(a, b, c, d) command_bind_to(MODULE_NAME, 1, a, b, c, d)
+#define command_bind_proto_first(a, b, c, d) command_bind_to(MODULE_NAME, 0, a, b, c, d)
+#define command_bind_proto_last(a, b, c, d) command_bind_to(MODULE_NAME, 2, a, b, c, d)
+
+void command_unbind(const char *cmd, SIGNAL_FUNC func);
+
+/* Run subcommand, `cmd' contains the base command, first word in `data'
+   contains the subcommand */
+void command_runsub(const char *cmd, const char *data,
+                   void *server, void *item);
+
+COMMAND_REC *command_find(const char *cmd);
+int command_have_sub(const char *command);
+
+/* Specify options that command can accept. `options' contains list of
+   options separated with space, each option can contain a special
+   char in front of it:
+
+   '!': no argument (default)
+   '-': optional argument
+   '+': argument required
+   '@': optional numeric argument
+
+   for example if options = "save -file +nick", you can use
+   /command -save -file [<filename>] -nick <nickname>
+
+   You can call this command multiple times for same command, options
+   will be merged. If there's any conflicts with option types, the last
+   call will override the previous */
+#define iscmdtype(c) \
+        ((c) == '!' || (c) == '-' || (c) == '+' || (c) == '@')
+void command_set_options_module(const char *module,
+                               const char *cmd, const char *options);
+#define command_set_options(cmd, options) \
+       command_set_options_module(MODULE_NAME, cmd, options)
+
+/* Returns TRUE if command has specified option. */
+int command_have_option(const char *cmd, const char *option);
+
+/* count can have these flags: */
+#define PARAM_WITHOUT_FLAGS(a) ((a) & 0x00000fff)
+/* don't check for quotes - "arg1 arg2" is NOT treated as one argument */
+#define PARAM_FLAG_NOQUOTES 0x00001000
+/* final argument gets all the rest of the arguments */
+#define PARAM_FLAG_GETREST 0x00002000
+/* command contains options - first you need to specify them with
+   command_set_options() function. Example:
+
+     -cmd requiredarg -noargcmd -cmd2 "another arg" -optnumarg rest of text
+
+   You would call this with:
+
+   // only once in init
+   command_set_options("mycmd", "+cmd noargcmd -cmd2 @optnumarg");
+
+   GHashTable *optlist;
+
+   cmd_get_params(data, &free_me, 1 | PARAM_FLAG_OPTIONS |
+                  PARAM_FLAG_GETREST, "mycmd", &optlist, &rest);
+
+   The optlist hash table is filled:
+
+   "cmd" = "requiredarg"
+   "noargcmd" = ""
+   "cmd2" = "another arg"
+   "optnumarg" = "" - this is because "rest" isn't a numeric value
+*/
+#define PARAM_FLAG_OPTIONS 0x00004000
+/* don't complain about unknown options */
+#define PARAM_FLAG_UNKNOWN_OPTIONS 0x00008000
+/* optional channel in first argument */
+#define PARAM_FLAG_OPTCHAN 0x00010000
+/* optional channel in first argument, but don't treat "*" as current channel */
+#define PARAM_FLAG_OPTCHAN_NAME 0x00030000
+
+char *cmd_get_param(char **data);
+/* get parameters from command - you should point free_me somewhere and
+   cmd_params_free() it after you don't use any of the parameters anymore.
+
+   Returns TRUE if all ok, FALSE if error occured. */
+int cmd_get_params(const char *data, gpointer *free_me, int count, ...);
+
+void cmd_params_free(void *free_me);
+
+void commands_remove_module(const char *module);
+
+void commands_init(void);
+void commands_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/core.c b/apps/irssi/src/core/core.c
new file mode 100644 (file)
index 0000000..f39c50d
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ core.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include <signal.h>
+
+#include "args.h"
+#include "pidwait.h"
+#include "misc.h"
+
+#include "net-disconnect.h"
+#include "net-sendbuffer.h"
+#include "signals.h"
+#include "settings.h"
+#include "session.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "chatnets.h"
+#include "commands.h"
+#include "expandos.h"
+#include "write-buffer.h"
+#include "log.h"
+#include "rawlog.h"
+#include "ignore.h"
+
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+#include "nickmatch-cache.h"
+
+#ifdef HAVE_SYS_RESOURCE_H
+#  include <sys/resource.h>
+   struct rlimit orig_core_rlimit;
+#endif
+
+void chat_commands_init(void);
+void chat_commands_deinit(void);
+
+void log_away_init(void);
+void log_away_deinit(void);
+
+int irssi_gui;
+int irssi_init_finished;
+
+static char *irssi_dir, *irssi_config_file;
+static GSList *dialog_type_queue, *dialog_text_queue;
+
+const char *get_irssi_dir(void)
+{
+        return irssi_dir;
+}
+
+/* return full path for ~/.irssi/config */
+const char *get_irssi_config(void)
+{
+        return irssi_config_file;
+}
+
+static void read_settings(void)
+{
+#ifndef WIN32
+       static int signals[] = {
+               SIGHUP, SIGINT, SIGQUIT, SIGTERM,
+               SIGALRM, SIGUSR1, SIGUSR2
+       };
+       static char *signames[] = {
+               "hup", "int", "quit", "term",
+               "alrm", "usr1", "usr2"
+       };
+
+       const char *ignores;
+       struct sigaction act;
+        int n;
+
+       ignores = settings_get_str("ignore_signals");
+
+       sigemptyset (&act.sa_mask);
+       act.sa_flags = 0;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               act.sa_handler = find_substr(ignores, signames[n]) ?
+                       SIG_IGN : SIG_DFL;
+               sigaction(signals[n], &act, NULL);
+       }
+
+#ifdef HAVE_SYS_RESOURCE_H
+       if (!settings_get_bool("override_coredump_limit"))
+               setrlimit(RLIMIT_CORE, &orig_core_rlimit);
+       else {
+               struct rlimit rlimit;
+
+                rlimit.rlim_cur = RLIM_INFINITY;
+                rlimit.rlim_max = RLIM_INFINITY;
+               if (setrlimit(RLIMIT_CORE, &rlimit) == -1)
+                        settings_set_bool("override_coredump_limit", FALSE);
+       }
+#endif
+#endif
+}
+
+static void sig_gui_dialog(const char *type, const char *text)
+{
+       dialog_type_queue = g_slist_append(dialog_type_queue, g_strdup(type));
+       dialog_text_queue = g_slist_append(dialog_text_queue, g_strdup(text));
+}
+
+static void sig_init_finished(void)
+{
+       GSList *type, *text;
+
+        signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+
+       /* send the dialog texts that were in queue before irssi
+          was initialized */
+       type = dialog_type_queue;
+        text = dialog_text_queue;
+       for (; text != NULL; text = text->next, type = type->next) {
+               signal_emit("gui dialog", 2, type->data, text->data);
+               g_free(type->data);
+                g_free(text->data);
+       }
+        g_slist_free(dialog_type_queue);
+        g_slist_free(dialog_text_queue);
+}
+
+void core_init_paths(int argc, char *argv[])
+{
+       static struct poptOption options[] = {
+               { "config", 0, POPT_ARG_STRING, NULL, 0, "Configuration file location (~/.irssi/config)", "PATH" },
+               { "home", 0, POPT_ARG_STRING, NULL, 0, "Irssi home dir location (~/.irssi)", "PATH" },
+               { NULL, '\0', 0, NULL }
+       };
+       char *str;
+       int n, len;
+
+       for (n = 1; n < argc; n++) {
+               if (strncmp(argv[n], "--home=", 7) == 0) {
+                        g_free_not_null(irssi_dir);
+                        irssi_dir = convert_home(argv[n]+7);
+                        len = strlen(irssi_dir);
+                       if (irssi_dir[len-1] == G_DIR_SEPARATOR)
+                               irssi_dir[len-1] = '\0';
+               } else if (strncmp(argv[n], "--config=", 9) == 0) {
+                        g_free_not_null(irssi_config_file);
+                       irssi_config_file = convert_home(argv[n]+9);
+               }
+       }
+
+       if (irssi_dir != NULL && !g_path_is_absolute(irssi_dir)) {
+               str = irssi_dir;
+               irssi_dir = g_strdup_printf("%s/%s", g_get_current_dir(), str);
+               g_free(str);
+       }
+
+       if (irssi_config_file != NULL &&
+           !g_path_is_absolute(irssi_config_file)) {
+               str = irssi_config_file;
+               irssi_config_file =
+                       g_strdup_printf("%s/%s", g_get_current_dir(), str);
+               g_free(str);
+       }
+
+       args_register(options);
+
+        if (irssi_dir == NULL)
+               irssi_dir = g_strdup_printf(IRSSI_DIR_FULL, g_get_home_dir());
+       if (irssi_config_file == NULL)
+               irssi_config_file = g_strdup_printf("%s/"IRSSI_HOME_CONFIG, irssi_dir);
+
+       session_set_binary(argv[0]);
+}
+
+static void sig_irssi_init_finished(void)
+{
+        irssi_init_finished = TRUE;
+}
+
+void core_init(int argc, char *argv[])
+{
+       dialog_type_queue = NULL;
+       dialog_text_queue = NULL;
+
+       modules_init();
+#ifndef WIN32
+       pidwait_init();
+#endif
+
+       net_disconnect_init();
+       net_sendbuffer_init();
+       signals_init();
+
+       signal_add_first("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
+       signal_add_first("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+
+       settings_init();
+       commands_init();
+       nickmatch_cache_init();
+        session_init();
+
+       chat_protocols_init();
+       chatnets_init();
+        expandos_init();
+       ignore_init();
+       servers_init();
+        write_buffer_init();
+       log_init();
+       log_away_init();
+       rawlog_init();
+
+       channels_init();
+       queries_init();
+       nicklist_init();
+
+       chat_commands_init();
+
+       settings_add_str("misc", "ignore_signals", "");
+       settings_add_bool("misc", "override_coredump_limit", TRUE);
+
+#ifdef HAVE_SYS_RESOURCE_H
+       getrlimit(RLIMIT_CORE, &orig_core_rlimit);
+#endif
+       read_settings();
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_add("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
+       settings_check();
+
+        module_register("core", "core");
+}
+
+void core_deinit(void)
+{
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished);
+
+       chat_commands_deinit();
+
+       nicklist_deinit();
+       queries_deinit();
+       channels_deinit();
+
+       rawlog_deinit();
+       log_away_deinit();
+       log_deinit();
+        write_buffer_deinit();
+       servers_deinit();
+       ignore_deinit();
+        expandos_deinit();
+       chatnets_deinit();
+       chat_protocols_deinit();
+
+        session_deinit();
+        nickmatch_cache_deinit();
+       commands_deinit();
+       settings_deinit();
+       signals_deinit();
+       net_sendbuffer_deinit();
+       net_disconnect_deinit();
+
+#ifndef WIN32
+       pidwait_deinit();
+#endif
+       modules_deinit();
+
+       g_free(irssi_dir);
+        g_free(irssi_config_file);
+}
diff --git a/apps/irssi/src/core/core.h b/apps/irssi/src/core/core.h
new file mode 100644 (file)
index 0000000..6b7ec62
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __IRSSI_CORE_H
+#define __IRSSI_CORE_H
+
+/* for determining what GUI is currently in use: */
+#define IRSSI_GUI_NONE 0
+#define IRSSI_GUI_TEXT 1
+#define IRSSI_GUI_GTK  2
+#define IRSSI_GUI_GNOME        3
+#define IRSSI_GUI_QT           4
+#define IRSSI_GUI_KDE          5
+
+extern int irssi_gui;
+extern int irssi_init_finished; /* TRUE after "irssi init finished" signal is sent */
+
+void core_init_paths(int argc, char *argv[]);
+
+void core_init(void);
+void core_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/expandos.c b/apps/irssi/src/core/expandos.c
new file mode 100644 (file)
index 0000000..777d50e
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ expandos.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "expandos.h"
+#include "settings.h"
+#include "commands.h"
+#include "misc.h"
+#include "irssi-version.h"
+
+#include "servers.h"
+#include "channels.h"
+#include "queries.h"
+#include "window-item-def.h"
+
+#ifdef HAVE_SYS_UTSNAME_H
+#  include <sys/utsname.h>
+#endif
+
+#define MAX_EXPANDO_SIGNALS 10
+
+typedef struct {
+       EXPANDO_FUNC func;
+
+        int signals;
+       int signal_ids[MAX_EXPANDO_SIGNALS];
+        int signal_args[MAX_EXPANDO_SIGNALS];
+} EXPANDO_REC;
+
+static int timer_tag;
+
+static EXPANDO_REC *char_expandos[255];
+static GHashTable *expandos;
+static time_t client_start_time;
+static char *last_sent_msg, *last_sent_msg_body;
+static char *last_privmsg_from, *last_public_from;
+static char *sysname, *sysrelease, *sysarch;
+
+static const char *timestamp_format;
+static int timestamp_seconds;
+static time_t last_timestamp;
+
+#define CHAR_EXPANDO(chr) \
+       (char_expandos[(int) (unsigned char) chr])
+
+/* Create expando - overrides any existing ones. */
+void expando_create(const char *key, EXPANDO_FUNC func, ...)
+{
+        EXPANDO_REC *rec;
+        const char *signal;
+       va_list va;
+
+       g_return_if_fail(key != NULL || *key == '\0');
+       g_return_if_fail(func != NULL);
+
+       if (key[1] != '\0')
+               rec = g_hash_table_lookup(expandos, key);
+       else {
+               /* single character expando */
+               rec = CHAR_EXPANDO(*key);
+       }
+
+       if (rec != NULL)
+               rec->signals = 0;
+       else {
+               rec = g_new0(EXPANDO_REC, 1);
+                if (key[1] != '\0')
+                       g_hash_table_insert(expandos, g_strdup(key), rec);
+               else
+                       char_expandos[(int) (unsigned char) *key] = rec;
+       }
+
+       rec->func = func;
+
+       va_start(va, func);
+       while ((signal = (const char *) va_arg(va, const char *)) != NULL)
+               expando_add_signal(key, signal, (int) va_arg(va, int));
+        va_end(va);
+}
+
+static EXPANDO_REC *expando_find(const char *key)
+{
+       if (key[1] != '\0')
+               return g_hash_table_lookup(expandos, key);
+        else
+               return CHAR_EXPANDO(*key);
+}
+
+/* Add new signal to expando */
+void expando_add_signal(const char *key, const char *signal, ExpandoArg arg)
+{
+       EXPANDO_REC *rec;
+
+       g_return_if_fail(key != NULL);
+       g_return_if_fail(signal != NULL);
+
+        rec = expando_find(key);
+        g_return_if_fail(rec != NULL);
+
+       if (arg == EXPANDO_NEVER) {
+                /* expando changes never */
+               rec->signals = -1;
+       } else if (rec->signals < MAX_EXPANDO_SIGNALS) {
+               g_return_if_fail(rec->signals != -1);
+
+               rec->signal_ids[rec->signals] = signal_get_uniq_id(signal);
+               rec->signal_args[rec->signals] = arg;
+                rec->signals++;
+       }
+}
+
+/* Destroy expando */
+void expando_destroy(const char *key, EXPANDO_FUNC func)
+{
+       gpointer origkey;
+        EXPANDO_REC *rec;
+
+       g_return_if_fail(key != NULL || *key == '\0');
+       g_return_if_fail(func != NULL);
+
+       if (key[1] == '\0') {
+               /* single character expando */
+               rec = CHAR_EXPANDO(*key);
+               if (rec != NULL && rec->func == func) {
+                       char_expandos[(int) (unsigned char) *key] = NULL;
+                       g_free(rec);
+               }
+       } else if (g_hash_table_lookup_extended(expandos, key, &origkey,
+                                               (gpointer *) &rec)) {
+               if (rec->func == func) {
+                       g_hash_table_remove(expandos, key);
+                       g_free(origkey);
+                       g_free(rec);
+               }
+       }
+}
+
+void expando_bind(const char *key, int funccount, SIGNAL_FUNC *funcs)
+{
+       SIGNAL_FUNC func;
+       EXPANDO_REC *rec;
+        int n, arg;
+
+       g_return_if_fail(key != NULL);
+       g_return_if_fail(funccount >= 1);
+       g_return_if_fail(funcs != NULL);
+       g_return_if_fail(funcs[0] != NULL);
+
+        rec = expando_find(key);
+       g_return_if_fail(rec != NULL);
+
+       if (rec->signals == 0) {
+               /* it's unknown when this expando changes..
+                  check it once in a second */
+                signal_add("expando timer", funcs[EXPANDO_ARG_NONE]);
+       }
+
+       for (n = 0; n < rec->signals; n++) {
+               arg = rec->signal_args[n];
+               func = arg < funccount ? funcs[arg] : NULL;
+               if (func == NULL) func = funcs[EXPANDO_ARG_NONE];
+
+               signal_add_to_id(MODULE_NAME, 1, rec->signal_ids[n], func);
+       }
+}
+
+void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs)
+{
+       SIGNAL_FUNC func;
+       EXPANDO_REC *rec;
+        int n, arg;
+
+       g_return_if_fail(key != NULL);
+       g_return_if_fail(funccount >= 1);
+       g_return_if_fail(funcs != NULL);
+       g_return_if_fail(funcs[0] != NULL);
+
+        rec = expando_find(key);
+       g_return_if_fail(rec != NULL);
+
+       if (rec->signals == 0) {
+               /* it's unknown when this expando changes..
+                  check it once in a second */
+                signal_remove("expando timer", funcs[EXPANDO_ARG_NONE]);
+       }
+
+       for (n = 0; n < rec->signals; n++) {
+               arg = rec->signal_args[n];
+               func = arg < funccount ? funcs[arg] : NULL;
+               if (func == NULL) func = funcs[EXPANDO_ARG_NONE];
+
+               signal_remove_id(rec->signal_ids[n], func);
+       }
+}
+
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *expando_get_signals(const char *key)
+{
+       EXPANDO_REC *rec;
+       int *signals;
+        int n;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       rec = expando_find(key);
+       if (rec == NULL || rec->signals < 0)
+                return NULL;
+
+       if (rec->signals == 0) {
+               /* it's unknown when this expando changes..
+                  check it once in a second */
+               signals = g_new(int, 3);
+               signals[0] = signal_get_uniq_id("expando timer");
+               signals[1] = EXPANDO_ARG_NONE;
+               signals[2] = -1;
+                return signals;
+       }
+
+        signals = g_new(int, rec->signals*2+1);
+       for (n = 0; n < rec->signals; n++) {
+                signals[n*2] = rec->signal_ids[n];
+                signals[n*2+1] = rec->signal_args[n];
+       }
+       signals[rec->signals*2] = -1;
+        return signals;
+}
+
+EXPANDO_FUNC expando_find_char(char chr)
+{
+       return CHAR_EXPANDO(chr) == NULL ? NULL :
+               CHAR_EXPANDO(chr)->func;
+}
+
+EXPANDO_FUNC expando_find_long(const char *key)
+{
+       EXPANDO_REC *rec = g_hash_table_lookup(expandos, key);
+       return rec == NULL ? NULL : rec->func;
+}
+
+/* last person who sent you a MSG */
+static char *expando_lastmsg(SERVER_REC *server, void *item, int *free_ret)
+{
+       return last_privmsg_from;
+}
+
+/* last person to whom you sent a MSG */
+static char *expando_lastmymsg(SERVER_REC *server, void *item, int *free_ret)
+{
+       return last_sent_msg;
+}
+
+/* last person to send a public message to a channel you are on */
+static char *expando_lastpublic(SERVER_REC *server, void *item, int *free_ret)
+{
+       return last_public_from;
+}
+
+/* text of your AWAY message, if any */
+static char *expando_awaymsg(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->away_reason;
+}
+
+/* body of last MSG you sent */
+static char *expando_lastmymsg_body(SERVER_REC *server, void *item, int *free_ret)
+{
+       return last_sent_msg_body;
+}
+
+/* current channel */
+static char *expando_channel(SERVER_REC *server, void *item, int *free_ret)
+{
+       return !IS_CHANNEL(item) ? NULL : CHANNEL(item)->name;
+}
+
+/* time client was started, $time() format */
+static char *expando_clientstarted(SERVER_REC *server, void *item, int *free_ret)
+{
+        *free_ret = TRUE;
+       return g_strdup_printf("%ld", (long) client_start_time);
+}
+
+/* channel you were last INVITEd to */
+static char *expando_last_invite(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->last_invite;
+}
+
+/* client version text string */
+static char *expando_version(SERVER_REC *server, void *item, int *free_ret)
+{
+       return IRSSI_VERSION;
+}
+
+/* current value of CMDCHARS */
+static char *expando_cmdchars(SERVER_REC *server, void *item, int *free_ret)
+{
+       return (char *) settings_get_str("cmdchars");
+}
+
+/* modes of current channel, if any */
+static char *expando_chanmode(SERVER_REC *server, void *item, int *free_ret)
+{
+       return !IS_CHANNEL(item) ? NULL : CHANNEL(item)->mode;
+}
+
+/* current nickname */
+static char *expando_nick(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->nick;
+}
+
+/* value of STATUS_OPER if you are an irc operator */
+static char *expando_statusoper(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL || !server->server_operator ? "" :
+               (char *) settings_get_str("STATUS_OPER");
+}
+
+/* if you are a channel operator in $C, expands to a '@' */
+static char *expando_chanop(SERVER_REC *server, void *item, int *free_ret)
+{
+       return IS_CHANNEL(item) && CHANNEL(item)->chanop ? "@" : "";
+}
+
+/* nickname of whomever you are QUERYing */
+static char *expando_query(SERVER_REC *server, void *item, int *free_ret)
+{
+       return !IS_QUERY(item) ? "" : QUERY(item)->name;
+}
+
+/* version of current server */
+static char *expando_serverversion(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->version;
+}
+
+/* target of current input (channel or QUERY nickname) */
+static char *expando_target(SERVER_REC *server, void *item, int *free_ret)
+{
+       return item == NULL ? "" : ((WI_ITEM_REC *) item)->name;
+}
+
+/* client release date (in YYYYMMDD format) */
+static char *expando_releasedate(SERVER_REC *server, void *item, int *free_ret)
+{
+        *free_ret = TRUE;
+       return g_strdup_printf("%d", IRSSI_VERSION_DATE);
+}
+
+/* client release time (in HHMM format) */
+static char *expando_releasetime(SERVER_REC *server, void *item, int *free_ret)
+{
+        *free_ret = TRUE;
+       return g_strdup_printf("%04d", IRSSI_VERSION_TIME);
+}
+
+/* current working directory */
+static char *expando_workdir(SERVER_REC *server, void *item, int *free_ret)
+{
+       *free_ret = TRUE;
+       return g_get_current_dir();
+}
+
+/* value of REALNAME */
+static char *expando_realname(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->connrec->realname;
+}
+
+/* time of day (hh:mm) */
+static char *expando_time(SERVER_REC *server, void *item, int *free_ret)
+{
+       time_t now;
+       struct tm *tm;
+        char str[256];
+
+        now = time(NULL);
+       tm = localtime(&now);
+
+       if (strftime(str, sizeof(str), timestamp_format, tm) == 0)
+                return "";
+
+       *free_ret = TRUE;
+        return g_strdup(str);
+}
+
+/* a literal '$' */
+static char *expando_dollar(SERVER_REC *server, void *item, int *free_ret)
+{
+       return "$";
+}
+
+/* system name */
+static char *expando_sysname(SERVER_REC *server, void *item, int *free_ret)
+{
+       return sysname;
+}
+
+/* system release */
+static char *expando_sysrelease(SERVER_REC *server, void *item, int *free_ret)
+{
+        return sysrelease;
+}
+
+/* system architecture */
+static char *expando_sysarch(SERVER_REC *server, void *item, int *free_ret)
+{
+        return sysarch;
+}
+
+/* Topic of active channel (or address of queried nick) */
+static char *expando_topic(SERVER_REC *server, void *item, int *free_ret)
+{
+       return IS_CHANNEL(item) ? CHANNEL(item)->topic :
+               IS_QUERY(item) ? QUERY(item)->address : "";
+}
+
+/* Server tag */
+static char *expando_servertag(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->tag;
+}
+
+/* Server chatnet */
+static char *expando_chatnet(SERVER_REC *server, void *item, int *free_ret)
+{
+       return server == NULL ? "" : server->connrec->chatnet;
+}
+
+static void sig_message_public(SERVER_REC *server, const char *msg,
+                              const char *nick, const char *address,
+                              const char *target)
+{
+       g_free_not_null(last_public_from);
+       last_public_from = g_strdup(nick);
+}
+
+static void sig_message_private(SERVER_REC *server, const char *msg,
+                               const char *nick, const char *address)
+{
+       g_free_not_null(last_privmsg_from);
+       last_privmsg_from = g_strdup(nick);
+}
+
+static void sig_message_own_private(SERVER_REC *server, const char *msg,
+                                   const char *target, const char *origtarget)
+{
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(msg != NULL);
+
+       if (target != NULL) {
+               if (target != last_sent_msg) {
+                       g_free_not_null(last_sent_msg);
+                       last_sent_msg = g_strdup(target);
+               }
+               g_free_not_null(last_sent_msg_body);
+               last_sent_msg_body = g_strdup(msg);
+       }
+}
+
+static int sig_timer(void)
+{
+       time_t now;
+       struct tm *tm;
+        int last_min;
+
+        signal_emit("expando timer", 0);
+
+        /* check if $Z has changed */
+       now = time(NULL);
+       if (last_timestamp != now) {
+               if (!timestamp_seconds && last_timestamp != 0) {
+                        /* assume it changes every minute */
+                       tm = localtime(&last_timestamp);
+                       last_min = tm->tm_min;
+
+                       tm = localtime(&now);
+                       if (tm->tm_min == last_min)
+                                return 1;
+               }
+
+                signal_emit("time changed", 0);
+               last_timestamp = now;
+       }
+
+        return 1;
+}
+
+static void read_settings(void)
+{
+       timestamp_format = settings_get_str("timestamp_format");
+       timestamp_seconds =
+               strstr(timestamp_format, "%r") != NULL ||
+               strstr(timestamp_format, "%s") != NULL ||
+               strstr(timestamp_format, "%S") != NULL ||
+               strstr(timestamp_format, "%X") != NULL ||
+               strstr(timestamp_format, "%T") != NULL;
+
+}
+
+void expandos_init(void)
+{
+#ifdef HAVE_SYS_UTSNAME_H
+       struct utsname un;
+#endif
+       settings_add_str("misc", "STATUS_OPER", "*");
+       settings_add_str("misc", "timestamp_format", "%H:%M");
+
+       client_start_time = time(NULL);
+       last_sent_msg = NULL; last_sent_msg_body = NULL;
+       last_privmsg_from = NULL; last_public_from = NULL;
+        last_timestamp = 0;
+
+        sysname = sysrelease = sysarch = NULL;
+#ifdef HAVE_SYS_UTSNAME_H
+       if (uname(&un) >= 0) {
+               sysname = g_strdup(un.sysname);
+               sysrelease = g_strdup(un.release);
+               sysarch = g_strdup(un.machine);
+       }
+#endif
+
+       memset(char_expandos, 0, sizeof(char_expandos));
+       expandos = g_hash_table_new((GHashFunc) g_str_hash,
+                                   (GCompareFunc) g_str_equal);
+
+       expando_create(",", expando_lastmsg,
+                      "message private", EXPANDO_ARG_SERVER, NULL);
+       expando_create(".", expando_lastmymsg,
+                      "command msg", EXPANDO_ARG_NONE, NULL);
+       expando_create(";", expando_lastpublic,
+                      "message public", EXPANDO_ARG_SERVER, NULL);
+       expando_create("A", expando_awaymsg,
+                      "away mode changed", EXPANDO_ARG_NONE, NULL);
+       expando_create("B", expando_lastmymsg_body,
+                      "command msg", EXPANDO_ARG_NONE, NULL);
+       expando_create("C", expando_channel,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window item changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("F", expando_clientstarted,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("I", expando_last_invite, NULL);
+       expando_create("J", expando_version,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("K", expando_cmdchars,
+                      "setup changed", EXPANDO_ARG_NONE, NULL);
+       expando_create("M", expando_chanmode,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window item changed", EXPANDO_ARG_WINDOW,
+                      "channel mode changed", EXPANDO_ARG_WINDOW_ITEM, NULL);
+       expando_create("N", expando_nick,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window server changed", EXPANDO_ARG_WINDOW,
+                       "server nick changed", EXPANDO_ARG_SERVER, NULL);
+       expando_create("O", expando_statusoper,
+                      "setup changed", EXPANDO_ARG_NONE,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window server changed", EXPANDO_ARG_WINDOW,
+                      "user mode changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("P", expando_chanop,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window item changed", EXPANDO_ARG_WINDOW,
+                      "nick mode changed", EXPANDO_ARG_WINDOW_ITEM, NULL);
+       expando_create("Q", expando_query,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window item changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("R", expando_serverversion,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window server changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("T", expando_target,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window item changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("V", expando_releasedate,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("versiontime", expando_releasetime,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("W", expando_workdir, NULL);
+       expando_create("Y", expando_realname,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window server changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("Z", expando_time,
+                      "time changed", EXPANDO_ARG_NONE, NULL);
+       expando_create("$", expando_dollar,
+                      "", EXPANDO_NEVER, NULL);
+
+       expando_create("sysname", expando_sysname,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("sysrelease", expando_sysrelease,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("sysarch", expando_sysarch,
+                      "", EXPANDO_NEVER, NULL);
+       expando_create("topic", expando_topic,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window item changed", EXPANDO_ARG_WINDOW,
+                      "channel topic changed", EXPANDO_ARG_WINDOW_ITEM,
+                      "query address changed", EXPANDO_ARG_WINDOW_ITEM, NULL);
+       expando_create("tag", expando_servertag,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window server changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("chatnet", expando_chatnet,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window server changed", EXPANDO_ARG_WINDOW, NULL);
+
+       read_settings();
+
+        timer_tag = g_timeout_add(500, (GSourceFunc) sig_timer, NULL);
+       signal_add("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_add("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_add_first("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void expandos_deinit(void)
+{
+       int n;
+
+       for (n = 0; n < sizeof(char_expandos)/sizeof(char_expandos[0]); n++)
+               g_free_not_null(char_expandos[n]);
+
+       expando_destroy("sysname", expando_sysname);
+       expando_destroy("sysrelease", expando_sysrelease);
+       expando_destroy("sysarch", expando_sysarch);
+       expando_destroy("topic", expando_topic);
+       expando_destroy("tag", expando_servertag);
+       expando_destroy("chatnet", expando_chatnet);
+
+        g_hash_table_destroy(expandos);
+
+       g_free_not_null(last_sent_msg); g_free_not_null(last_sent_msg_body);
+       g_free_not_null(last_privmsg_from); g_free_not_null(last_public_from);
+       g_free_not_null(sysname); g_free_not_null(sysrelease);
+        g_free_not_null(sysarch);
+
+        g_source_remove(timer_tag);
+       signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/core/expandos.h b/apps/irssi/src/core/expandos.h
new file mode 100644 (file)
index 0000000..cf05799
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __EXPANDOS_H
+#define __EXPANDOS_H
+
+#include "signals.h"
+
+/* first argument of signal must match to active .. */
+typedef enum {
+        EXPANDO_ARG_NONE = 1,
+        EXPANDO_ARG_SERVER,
+        EXPANDO_ARG_WINDOW,
+       EXPANDO_ARG_WINDOW_ITEM,
+
+       EXPANDO_NEVER /* special: expando never changes */
+} ExpandoArg;
+
+typedef char* (*EXPANDO_FUNC)
+       (SERVER_REC *server, void *item, int *free_ret);
+
+/* Create expando - overrides any existing ones.
+   ... = signal, type, ..., NULL - list of signals that might change the
+   value of this expando */
+void expando_create(const char *key, EXPANDO_FUNC func, ...);
+/* Add new signal to expando */
+void expando_add_signal(const char *key, const char *signal, ExpandoArg arg);
+/* Destroy expando */
+void expando_destroy(const char *key, EXPANDO_FUNC func);
+
+void expando_bind(const char *key, int funccount, SIGNAL_FUNC *funcs);
+void expando_unbind(const char *key, int funccount, SIGNAL_FUNC *funcs);
+
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *expando_get_signals(const char *key);
+
+/* internal: */
+EXPANDO_FUNC expando_find_char(char chr);
+EXPANDO_FUNC expando_find_long(const char *key);
+
+void expandos_init(void);
+void expandos_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/ignore.c b/apps/irssi/src/core/ignore.c
new file mode 100644 (file)
index 0000000..cebbc3b
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ ignore.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "levels.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "masks.h"
+#include "servers.h"
+#include "channels.h"
+#include "nicklist.h"
+#include "nickmatch-cache.h"
+
+#include "ignore.h"
+
+GSList *ignores;
+
+static NICKMATCH_REC *nickmatch;
+static int time_tag;
+
+/* check if `text' contains ignored nick at the start of the line. */
+static int ignore_check_replies_rec(IGNORE_REC *rec, CHANNEL_REC *channel,
+                                   const char *text)
+{
+       GSList *nicks, *tmp;
+
+       nicks = nicklist_find_multiple(channel, rec->mask);
+       if (nicks == NULL) return FALSE;
+
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+               NICK_REC *nick = tmp->data;
+
+               if (nick_match_msg(channel, text, nick->nick))
+                       return TRUE;
+       }
+       g_slist_free(nicks);
+
+       return FALSE;
+}
+
+static int ignore_check_replies(CHANNEL_REC *chanrec, const char *text)
+{
+       GSList *tmp;
+
+       if (text == NULL || chanrec == NULL)
+               return FALSE;
+
+        /* check reply ignores */
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               IGNORE_REC *rec = tmp->data;
+
+               if (rec->mask != NULL && rec->replies &&
+                   ignore_check_replies_rec(rec, chanrec, text))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int ignore_match_pattern(IGNORE_REC *rec, const char *text)
+{
+       if (rec->pattern == NULL)
+               return TRUE;
+
+        if (text == NULL)
+               return FALSE;
+
+       if (rec->regexp) {
+#ifdef HAVE_REGEX_H
+               return rec->regexp_compiled &&
+                       regexec(&rec->preg, text, 0, NULL, 0) == 0;
+#else
+                return FALSE;
+#endif
+       }
+
+       return rec->fullword ?
+               stristr_full(text, rec->pattern) != NULL :
+               stristr(text, rec->pattern) != NULL;
+}
+
+#define ignore_match_level(rec, level) \
+        ((level & (rec)->level) != 0)
+
+#define ignore_match_nickmask(rec, nick, nickmask) \
+       ((rec)->mask == NULL || \
+       (strchr((rec)->mask, '!') != NULL ? \
+               match_wildcards((rec)->mask, nickmask) : \
+               match_wildcards((rec)->mask, nick)))
+
+#define ignore_match_server(rec, server) \
+       ((rec)->servertag == NULL || \
+       g_strcasecmp((server)->tag, (rec)->servertag) == 0)
+
+#define ignore_match_channel(rec, channel) \
+       ((rec)->channels == NULL || ((channel) != NULL && \
+               strarray_find((rec)->channels, (channel)) != -1))
+
+static int ignore_check_without_mask(GSList *list, CHANNEL_REC *channel,
+                                    int level, const char *text)
+{
+       GSList *tmp;
+        int len, best_mask, best_match, best_patt;
+
+        best_mask = best_patt = -1; best_match = FALSE;
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               IGNORE_REC *rec = tmp->data;
+
+               if (ignore_match_level(rec, level) &&
+                   ignore_match_pattern(rec, text)) {
+                       len = rec->mask == NULL ? 0 : strlen(rec->mask);
+                       if (len > best_mask) {
+                               best_mask = len;
+                               best_match = !rec->exception;
+                       } else if (len == best_mask && rec->pattern != NULL) {
+                               len = strlen(rec->pattern);
+                               if (len > best_patt) {
+                                       best_patt = len;
+                                       best_match = !rec->exception;
+                               }
+                       }
+               }
+       }
+
+       if (best_match || (level & MSGLEVEL_PUBLIC) == 0)
+               return best_match;
+
+        return ignore_check_replies(channel, text);
+}
+
+int ignore_check(SERVER_REC *server, const char *nick, const char *host,
+                const char *channel, const char *text, int level)
+{
+       CHANNEL_REC *chanrec;
+       NICK_REC *nickrec;
+        IGNORE_REC *rec;
+       GSList *tmp, *list;
+        char *nickmask;
+        int len, best_mask, best_match, best_patt;
+
+       g_return_val_if_fail(server != NULL, 0);
+        if (nick == NULL) nick = "";
+
+       chanrec = server == NULL || channel == NULL ? NULL :
+               channel_find(server, channel);
+       if (chanrec != NULL && nick != NULL &&
+           (nickrec = nicklist_find(chanrec, nick)) != NULL) {
+                /* nick found - check only ignores in nickmatch cache */
+               if (nickrec->host == NULL)
+                       nicklist_set_host(chanrec, nickrec, host);
+
+               list = nickmatch_find(nickmatch, nickrec);
+               return ignore_check_without_mask(list, chanrec, level, text);
+       }
+
+       nickmask = g_strconcat(nick, "!", host, NULL);
+
+        best_mask = best_patt = -1; best_match = FALSE;
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               rec = tmp->data;
+
+               if (ignore_match_level(rec, level) &&
+                   ignore_match_server(rec, server) &&
+                   ignore_match_channel(rec, channel) &&
+                   ignore_match_nickmask(rec, nick, nickmask) &&
+                   ignore_match_pattern(rec, text)) {
+                       len = rec->mask == NULL ? 0 : strlen(rec->mask);
+                       if (len > best_mask) {
+                               best_mask = len;
+                               best_match = !rec->exception;
+                       } else if (len == best_mask && rec->pattern != NULL) {
+                               len = strlen(rec->pattern);
+                               if (len > best_patt) {
+                                       best_patt = len;
+                                       best_match = !rec->exception;
+                               }
+                       }
+               }
+       }
+        g_free(nickmask);
+
+       if (best_match || (level & MSGLEVEL_PUBLIC) == 0)
+               return best_match;
+
+        return ignore_check_replies(chanrec, text);
+}
+
+IGNORE_REC *ignore_find(const char *servertag, const char *mask,
+                       char **channels)
+{
+       GSList *tmp;
+       char **chan;
+       int ignore_servertag;
+
+       if (mask != NULL && (*mask == '\0' || strcmp(mask, "*") == 0))
+               mask = NULL;
+
+       ignore_servertag = servertag != NULL && strcmp(servertag, "*") == 0;
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               IGNORE_REC *rec = tmp->data;
+
+               if (!ignore_servertag) {
+                       if ((servertag == NULL && rec->servertag != NULL) ||
+                           (servertag != NULL && rec->servertag == NULL))
+                               continue;
+
+                       if (servertag != NULL && g_strcasecmp(servertag, rec->servertag) != 0)
+                               continue;
+               }
+
+               if ((rec->mask == NULL && mask != NULL) ||
+                   (rec->mask != NULL && mask == NULL)) continue;
+
+               if (rec->mask != NULL && g_strcasecmp(rec->mask, mask) != 0)
+                       continue;
+
+               if ((channels == NULL && rec->channels == NULL))
+                       return rec; /* no channels - ok */
+
+               if (channels != NULL && strcmp(*channels, "*") == 0)
+                       return rec; /* ignore channels */
+
+               if (channels == NULL || rec->channels == NULL)
+                       continue; /* other doesn't have channels */
+
+               if (strarray_length(channels) != strarray_length(rec->channels))
+                       continue; /* different amount of channels */
+
+               /* check that channels match */
+               for (chan = channels; *chan != NULL; chan++) {
+                       if (strarray_find(rec->channels, *chan) == -1)
+                                break;
+               }
+
+               if (*chan == NULL)
+                       return rec; /* channels ok */
+       }
+
+       return NULL;
+}
+
+static void ignore_set_config(IGNORE_REC *rec)
+{
+       CONFIG_NODE *node;
+       char *levelstr;
+
+       if (rec->level == 0 || rec->unignore_time > 0)
+               return;
+
+       node = iconfig_node_traverse("(ignores", TRUE);
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       if (rec->mask != NULL) iconfig_node_set_str(node, "mask", rec->mask);
+       if (rec->level) {
+               levelstr = bits2level(rec->level);
+               iconfig_node_set_str(node, "level", levelstr);
+               g_free(levelstr);
+       }
+       iconfig_node_set_str(node, "pattern", rec->pattern);
+       if (rec->exception) iconfig_node_set_bool(node, "exception", TRUE);
+       if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
+       if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
+       if (rec->replies) iconfig_node_set_bool(node, "replies", TRUE);
+
+       if (rec->channels != NULL && *rec->channels != NULL) {
+               node = config_node_section(node, "channels", NODE_TYPE_LIST);
+               iconfig_node_add_list(node, rec->channels);
+       }
+}
+
+static int ignore_index(IGNORE_REC *find)
+{
+       GSList *tmp;
+       int index;
+
+       index = 0;
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               IGNORE_REC *rec = tmp->data;
+
+               if (rec->servertag != NULL)
+                       continue;
+
+               if (rec == find)
+                       return index;
+               index++;
+       }
+
+       return -1;
+}
+
+static void ignore_remove_config(IGNORE_REC *rec)
+{
+       CONFIG_NODE *node;
+
+       node = iconfig_node_traverse("ignores", FALSE);
+       if (node != NULL) iconfig_node_list_remove(node, ignore_index(rec));
+}
+
+static void ignore_init_rec(IGNORE_REC *rec)
+{
+#ifdef HAVE_REGEX_H
+       rec->regexp_compiled = !rec->regexp || rec->pattern == NULL ? FALSE :
+               regcomp(&rec->preg, rec->pattern,
+                       REG_EXTENDED|REG_ICASE|REG_NOSUB) == 0;
+#endif
+}
+
+void ignore_add_rec(IGNORE_REC *rec)
+{
+       ignore_init_rec(rec);
+
+       ignores = g_slist_append(ignores, rec);
+       ignore_set_config(rec);
+
+       signal_emit("ignore created", 1, rec);
+       nickmatch_rebuild(nickmatch);
+}
+
+static void ignore_destroy(IGNORE_REC *rec, int send_signal)
+{
+       ignores = g_slist_remove(ignores, rec);
+       if (send_signal)
+               signal_emit("ignore destroyed", 1, rec);
+
+#ifdef HAVE_REGEX_H
+       if (rec->regexp_compiled) regfree(&rec->preg);
+#endif
+       if (rec->channels != NULL) g_strfreev(rec->channels);
+       g_free_not_null(rec->mask);
+       g_free_not_null(rec->servertag);
+       g_free_not_null(rec->pattern);
+       g_free(rec);
+
+       nickmatch_rebuild(nickmatch);
+}
+
+void ignore_update_rec(IGNORE_REC *rec)
+{
+       if (rec->level == 0) {
+               /* unignored everything */
+               ignore_remove_config(rec);
+               ignore_destroy(rec, TRUE);
+       } else {
+               /* unignore just some levels.. */
+               ignore_remove_config(rec);
+               ignores = g_slist_remove(ignores, rec);
+
+               ignores = g_slist_append(ignores, rec);
+               ignore_set_config(rec);
+
+               signal_emit("ignore changed", 1, rec);
+               nickmatch_rebuild(nickmatch);
+       }
+}
+
+static int unignore_timeout(void)
+{
+       GSList *tmp, *next;
+        time_t now;
+
+        now = time(NULL);
+       for (tmp = ignores; tmp != NULL; tmp = next) {
+               IGNORE_REC *rec = tmp->data;
+
+               next = tmp->next;
+               if (rec->unignore_time > 0 && now >= rec->unignore_time) {
+                       rec->level = 0;
+                       ignore_update_rec(rec);
+               }
+       }
+
+       return TRUE;
+}
+
+static void read_ignores(void)
+{
+       IGNORE_REC *rec;
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+       while (ignores != NULL)
+                ignore_destroy(ignores->data, FALSE);
+
+       node = iconfig_node_traverse("ignores", FALSE);
+       if (node == NULL) {
+               nickmatch_rebuild(nickmatch);
+               return;
+       }
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               if (node->type != NODE_TYPE_BLOCK)
+                       continue;
+
+               rec = g_new0(IGNORE_REC, 1);
+               ignores = g_slist_append(ignores, rec);
+
+               rec->mask = g_strdup(config_node_get_str(node, "mask", NULL));
+               rec->pattern = g_strdup(config_node_get_str(node, "pattern", NULL));
+               rec->level = level2bits(config_node_get_str(node, "level", ""));
+                rec->exception = config_node_get_bool(node, "exception", FALSE);
+               rec->regexp = config_node_get_bool(node, "regexp", FALSE);
+               rec->fullword = config_node_get_bool(node, "fullword", FALSE);
+               rec->replies = config_node_get_bool(node, "replies", FALSE);
+
+               node = config_node_section(node, "channels", -1);
+               if (node != NULL) rec->channels = config_node_get_list(node);
+
+               ignore_init_rec(rec);
+       }
+
+       nickmatch_rebuild(nickmatch);
+}
+
+static void ignore_nick_cache(GHashTable *list, CHANNEL_REC *channel,
+                             NICK_REC *nick)
+{
+       GSList *tmp, *matches;
+        char *nickmask;
+
+       if (nick->host == NULL)
+               return; /* don't check until host is known */
+
+        matches = NULL;
+       nickmask = g_strconcat(nick->nick, "!", nick->host, NULL);
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               IGNORE_REC *rec = tmp->data;
+
+               if (ignore_match_nickmask(rec, nick->nick, nickmask) &&
+                   ignore_match_server(rec, channel->server) &&
+                   ignore_match_channel(rec, channel->name))
+                       matches = g_slist_append(matches, rec);
+       }
+       g_free_not_null(nickmask);
+
+       if (matches == NULL)
+               g_hash_table_remove(list, nick);
+        else
+                g_hash_table_insert(list, nick, matches);
+}
+
+void ignore_init(void)
+{
+       ignores = NULL;
+       nickmatch = nickmatch_init(ignore_nick_cache);
+       time_tag = g_timeout_add(1000, (GSourceFunc) unignore_timeout, NULL);
+
+        read_ignores();
+        signal_add("setup reread", (SIGNAL_FUNC) read_ignores);
+}
+
+void ignore_deinit(void)
+{
+       g_source_remove(time_tag);
+       while (ignores != NULL)
+                ignore_destroy(ignores->data, TRUE);
+        nickmatch_deinit(nickmatch);
+
+       signal_remove("setup reread", (SIGNAL_FUNC) read_ignores);
+}
diff --git a/apps/irssi/src/core/ignore.h b/apps/irssi/src/core/ignore.h
new file mode 100644 (file)
index 0000000..4056062
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef __IGNORE_H
+#define __IGNORE_H
+
+#ifdef HAVE_REGEX_H
+#  include <regex.h>
+#endif
+
+typedef struct {
+       int level; /* ignore these levels */
+       char *mask; /* nick mask */
+       char *servertag; /* this is for autoignoring */
+       char **channels; /* ignore only in these channels */
+       char *pattern; /* text body must match this pattern */
+
+        time_t unignore_time; /* time in sec for temp ignores */
+
+       unsigned int exception:1; /* *don't* ignore */
+       unsigned int regexp:1;
+       unsigned int fullword:1;
+       unsigned int replies:1; /* ignore replies to nick in channel */
+#ifdef HAVE_REGEX_H
+       unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
+       regex_t preg;
+#endif
+} IGNORE_REC;
+
+extern GSList *ignores;
+
+int ignore_check(SERVER_REC *server, const char *nick, const char *host,
+                const char *channel, const char *text, int level);
+
+IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels);
+
+void ignore_add_rec(IGNORE_REC *rec);
+void ignore_update_rec(IGNORE_REC *rec);
+
+void ignore_init(void);
+void ignore_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/levels.c b/apps/irssi/src/core/levels.c
new file mode 100644 (file)
index 0000000..0caf58b
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ levels.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "levels.h"
+
+static const char *levels[] = {
+       "CRAP",
+       "MSGS",
+       "PUBLICS",
+       "NOTICES",
+       "SNOTES",
+       "CTCPS",
+       "ACTIONS",
+       "JOINS",
+       "PARTS",
+       "QUITS",
+       "KICKS",
+       "MODES",
+       "TOPICS",
+       "WALLOPS",
+       "INVITES",
+       "NICKS",
+       "DCC",
+       "DCCMSGS",
+       "CLIENTNOTICES",
+       "CLIENTCRAP",
+       "CLIENTERRORS",
+       "HILIGHTS",
+
+       "NOHILIGHT",
+       NULL
+};
+
+int level_get(const char *level)
+{
+       int n, len, match;
+
+       if (g_strcasecmp(level, "ALL") == 0 || strcmp(level, "*") == 0)
+               return MSGLEVEL_ALL;
+
+       if (g_strcasecmp(level, "NEVER") == 0)
+               return MSGLEVEL_NEVER;
+
+       len = strlen(level);
+       if (len == 0) return 0;
+
+       /* partial match allowed, as long as it's the only one that matches */
+       match = 0;
+       for (n = 0; levels[n] != NULL; n++) {
+               if (g_strncasecmp(levels[n], level, len) == 0) {
+                       if ((int)strlen(levels[n]) == len) {
+                               /* full match */
+                               return 1L << n;
+                       }
+                       if (match > 0) {
+                               /* ambiguous - abort */
+                               return 0;
+                       }
+                       match = 1L << n;
+               }
+       }
+
+       return match;
+}
+
+int level2bits(const char *level)
+{
+       char *orig, *str, *ptr;
+       int ret, singlelevel, negative;
+
+       g_return_val_if_fail(level != NULL, 0);
+
+       if (*level == '\0')
+               return 0;
+
+       orig = str = g_strdup(level);
+
+       ret = 0;
+       for (ptr = str; ; str++) {
+               if (*str == ' ')
+                       *str++ = '\0';
+               else if (*str != '\0')
+                       continue;
+
+               negative = *ptr == '-';
+               if (*ptr == '-' || *ptr == '+') ptr++;
+
+               singlelevel = level_get(ptr);
+               if (singlelevel != 0) {
+                       ret = !negative ? (ret | singlelevel) :
+                               (ret & ~singlelevel);
+               }
+
+                       while (*str == ' ') str++;
+               if (*str == '\0') break;
+
+                       ptr = str;
+       }
+       g_free(orig);
+
+       return ret;
+}
+
+char *bits2level(int bits)
+{
+       GString *str;
+       char *ret;
+       int n;
+
+       if (bits == 0)
+               return g_strdup("");
+
+       if (bits == MSGLEVEL_ALL)
+               return g_strdup("ALL");
+
+       str = g_string_new(NULL);
+       if (bits & MSGLEVEL_NEVER)
+               g_string_append(str, "NEVER ");
+
+       for (n = 0; levels[n] != NULL; n++) {
+               if (bits & (1L << n))
+                       g_string_sprintfa(str, "%s ", levels[n]);
+       }
+        if (str->len > 0)
+               g_string_truncate(str, str->len-1);
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+
+       return ret;
+}
+
+int combine_level(int dest, const char *src)
+{
+       char **list, **item, *itemname;
+       int itemlevel;
+
+       g_return_val_if_fail(src != NULL, dest);
+
+       list = g_strsplit(src, " ", -1);
+       for (item = list; *item != NULL; item++) {
+               itemname = *item + (**item == '+' || **item == '-' ? 1 : 0);
+                g_strup(itemname);
+               itemlevel = level_get(itemname);
+
+               if (strcmp(itemname, "NONE") == 0)
+                        dest = 0;
+               else if (**item == '-')
+                       dest &= ~(itemlevel);
+               else
+                       dest |= itemlevel;
+       }
+       g_strfreev(list);
+
+       return dest;
+}
diff --git a/apps/irssi/src/core/levels.h b/apps/irssi/src/core/levels.h
new file mode 100644 (file)
index 0000000..2d7288f
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef __LEVELS_H
+#define __LEVELS_H
+
+/* This is pretty much IRC specific, but I think it would be easier for
+   other chats to try to use these same levels instead of implementing too
+   difficult message leveling system (which might be done if really
+   needed..). */
+
+/* Message levels */
+#define MSGLEVEL_CRAP         0x0000001
+#define MSGLEVEL_MSGS         0x0000002
+#define MSGLEVEL_PUBLIC       0x0000004
+#define MSGLEVEL_NOTICES      0x0000008
+#define MSGLEVEL_SNOTES       0x0000010
+#define MSGLEVEL_CTCPS        0x0000020
+#define MSGLEVEL_ACTIONS      0x0000040
+#define MSGLEVEL_JOINS        0x0000080
+#define MSGLEVEL_PARTS        0x0000100
+#define MSGLEVEL_QUITS        0x0000200
+#define MSGLEVEL_KICKS        0x0000400
+#define MSGLEVEL_MODES        0x0000800
+#define MSGLEVEL_TOPICS       0x0001000
+#define MSGLEVEL_WALLOPS      0x0002000
+#define MSGLEVEL_INVITES      0x0004000
+#define MSGLEVEL_NICKS        0x0008000
+#define MSGLEVEL_DCC          0x0010000
+#define MSGLEVEL_DCCMSGS      0x0020000
+#define MSGLEVEL_CLIENTNOTICE 0x0040000
+#define MSGLEVEL_CLIENTCRAP   0x0080000
+#define MSGLEVEL_CLIENTERROR  0x0100000
+#define MSGLEVEL_HILIGHT      0x0200000
+
+#define MSGLEVEL_ALL          0x03fffff
+
+#define MSGLEVEL_NOHILIGHT    0x1000000 /* Don't highlight this message */
+#define MSGLEVEL_NO_ACT       0x2000000 /* Don't trigger channel activity */
+#define MSGLEVEL_NEVER        0x4000000 /* never ignore / never log */
+#define MSGLEVEL_LASTLOG      0x8000000 /* never ignore / never log */
+
+int level_get(const char *level);
+int level2bits(const char *level);
+char *bits2level(int bits);
+int combine_level(int dest, const char *src);
+
+#endif
diff --git a/apps/irssi/src/core/line-split.c b/apps/irssi/src/core/line-split.c
new file mode 100644 (file)
index 0000000..b7daf27
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ line-split.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "misc.h"
+
+/* Maximum line length - split to two lines if it's longer than this.
+
+   This is mostly to prevent excessive memory usage. Like if someone DCC
+   chats you, you both have very fast connections and the other side sends
+   you 100 megs of text without any line feeds -> irssi will (try to)
+   allocate 128M of memory for the line and will eventually crash when it
+   can't allocate any more memory. If the line is split at every 64k the
+   text buffer will free the old lines and the memory usage never gets
+   too high. */
+#define MAX_CHARS_IN_LINE 65536
+
+struct _LINEBUF_REC {
+        int len;
+       int alloc;
+       int remove;
+        char *str;
+};
+
+static void linebuf_append(LINEBUF_REC *rec, const char *data, int len)
+{
+       if (rec->len+len > rec->alloc) {
+               rec->alloc = nearest_power(rec->len+len);;
+               rec->str = rec->str == NULL ? g_malloc(rec->alloc) :
+                       g_realloc(rec->str, rec->alloc);
+       }
+
+       memcpy(rec->str + rec->len, data, len);
+       rec->len += len;
+}
+
+static char *linebuf_find(LINEBUF_REC *rec, char chr)
+{
+        int n;
+
+       for (n = 0; n < rec->len; n++)
+               if (rec->str[n] == chr) return rec->str+n;
+
+       return NULL;
+}
+
+static int remove_newline(LINEBUF_REC *rec)
+{
+       char *ptr;
+
+       ptr = linebuf_find(rec, '\n');
+       if (ptr == NULL) {
+               /* LF wasn't found, wait for more data.. */
+               if (rec->len < MAX_CHARS_IN_LINE)
+                       return 0;
+
+               /* line buffer is too big - force a newline. */
+                linebuf_append(rec, "\n", 1);
+               ptr = rec->str+rec->len-1;
+       }
+
+       rec->remove = (int) (ptr-rec->str)+1;
+       if (ptr != rec->str && ptr[-1] == '\r') {
+               /* remove CR too. */
+               ptr--;
+       }
+
+       *ptr = '\0';
+       return 1;
+}
+
+/* line-split `data'. Initially `*buffer' should contain NULL. */
+int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer)
+{
+       LINEBUF_REC *rec;
+
+       g_return_val_if_fail(data != NULL, -1);
+       g_return_val_if_fail(output != NULL, -1);
+       g_return_val_if_fail(buffer != NULL, -1);
+
+       if (*buffer == NULL)
+               *buffer = g_new0(LINEBUF_REC, 1);
+       rec = *buffer;
+
+       if (rec->remove > 0) {
+               rec->len -= rec->remove;
+               g_memmove(rec->str, rec->str+rec->remove, rec->len);
+               rec->remove = 0;
+       }
+
+       if (len > 0)
+               linebuf_append(rec, data, len);
+       else if (len < 0) {
+               /* connection closed.. */
+               if (rec->len == 0)
+                       return -1;
+
+               /* no new data got but still something in buffer.. */
+                len = 0;
+               if (linebuf_find(rec, '\n') == NULL) {
+                       /* connection closed and last line is missing \n ..
+                          just add it so we can see if it had
+                          anything useful.. */
+                       linebuf_append(rec, "\n", 1);
+               }
+       }
+
+       *output = rec->str;
+       return remove_newline(rec);
+}
+
+void line_split_free(LINEBUF_REC *buffer)
+{
+       if (buffer != NULL) {
+               if (buffer->str != NULL) g_free(buffer->str);
+               g_free(buffer);
+       }
+}
+
+/* Return 1 if there is no data in the buffer */
+int line_split_is_empty(LINEBUF_REC *buffer)
+{
+       return buffer->len == 0;
+}
diff --git a/apps/irssi/src/core/line-split.h b/apps/irssi/src/core/line-split.h
new file mode 100644 (file)
index 0000000..7c101dd
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __LINE_SPLIT_H
+#define __LINE_SPLIT_H
+
+/* line-split `data'. Initially `*buffer' should contain NULL. */
+int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer);
+void line_split_free(LINEBUF_REC *buffer);
+
+/* Return 1 if there is no data in the buffer */
+int line_split_is_empty(LINEBUF_REC *buffer);
+
+#endif
diff --git a/apps/irssi/src/core/log-away.c b/apps/irssi/src/core/log-away.c
new file mode 100644 (file)
index 0000000..724e4b4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ log-away.c : Awaylog handling
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "levels.h"
+#include "log.h"
+#include "servers.h"
+#include "settings.h"
+
+static LOG_REC *awaylog;
+static int away_filepos;
+static int away_msgs;
+
+static void sig_log_written(LOG_REC *log)
+{
+       if (log != awaylog) return;
+
+        away_msgs++;
+}
+
+static void awaylog_open(void)
+{
+       const char *fname, *levelstr;
+       LOG_REC *log;
+       int level;
+
+       fname = settings_get_str("awaylog_file");
+       levelstr = settings_get_str("awaylog_level");
+       if (*fname == '\0' || *levelstr == '\0') return;
+
+       level = level2bits(levelstr);
+       if (level == 0) return;
+
+       log = log_find(fname);
+       if (log != NULL && log->handle != -1)
+               return; /* already open */
+
+       if (log == NULL) {
+               log = log_create_rec(fname, level);
+               log->temp = TRUE;
+               log_update(log);
+       }
+
+       if (!log_start_logging(log)) {
+               /* creating log file failed? close it. */
+               log_close(log);
+               return;
+       }
+
+       awaylog = log;
+       away_filepos = lseek(log->handle, 0, SEEK_CUR);
+       away_msgs = 0;
+}
+
+static void awaylog_close(void)
+{
+       const char *fname;
+       LOG_REC *log;
+
+       fname = settings_get_str("awaylog_file");
+       if (*fname == '\0') return;
+
+       log = log_find(fname);
+       if (log == NULL || log->handle == -1) {
+               /* awaylog not open */
+               return;
+       }
+
+       if (awaylog == log) awaylog = NULL;
+
+       signal_emit("awaylog show", 3, log, GINT_TO_POINTER(away_msgs),
+                   GINT_TO_POINTER(away_filepos));
+       log_close(log);
+}
+
+static void sig_away_changed(SERVER_REC *server)
+{
+       if (server->usermode_away)
+               awaylog_open();
+       else
+                awaylog_close();
+}
+
+void log_away_init(void)
+{
+       awaylog = NULL;
+       away_filepos = 0;
+       away_msgs = 0;
+
+       settings_add_str("log", "awaylog_file", IRSSI_DIR_SHORT"/away.log");
+       settings_add_str("log", "awaylog_level", "msgs hilight");
+
+       signal_add("log written", (SIGNAL_FUNC) sig_log_written);
+       signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
+
+void log_away_deinit(void)
+{
+       signal_remove("log written", (SIGNAL_FUNC) sig_log_written);
+       signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed);
+}
diff --git a/apps/irssi/src/core/log.c b/apps/irssi/src/core/log.c
new file mode 100644 (file)
index 0000000..e843ffe
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ log.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "servers.h"
+#include "log.h"
+#include "write-buffer.h"
+
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#define DEFAULT_LOG_FILE_CREATE_MODE 600
+
+#ifdef HAVE_FCNTL
+static struct flock lock;
+#endif
+
+GSList *logs;
+
+static const char *log_item_types[] = {
+       "target",
+       "window",
+
+       NULL
+};
+
+const char *log_timestamp;
+static int log_file_create_mode;
+static int rotate_tag;
+
+static int log_item_str2type(const char *type)
+{
+       int n;
+
+       for (n = 0; log_item_types[n] != NULL; n++) {
+               if (g_strcasecmp(log_item_types[n], type) == 0)
+                       return n;
+       }
+
+       return -1;
+}
+
+static void log_write_timestamp(int handle, const char *format,
+                               const char *text, time_t stamp)
+{
+       struct tm *tm;
+       char str[256];
+
+       g_return_if_fail(format != NULL);
+       if (*format == '\0') return;
+
+       tm = localtime(&stamp);
+       if (strftime(str, sizeof(str), format, tm) > 0)
+               write_buffer(handle, str, strlen(str));
+       if (text != NULL) write_buffer(handle, text, strlen(text));
+}
+
+static char *log_filename(LOG_REC *log)
+{
+       char *str, fname[1024];
+       struct tm *tm;
+        size_t ret;
+       time_t now;
+
+       now = time(NULL);
+       tm = localtime(&now);
+
+       str = convert_home(log->fname);
+       ret = strftime(fname, sizeof(fname), str, tm);
+       g_free(str);
+
+       if (ret <= 0) {
+               g_warning("log_filename() : strftime() failed");
+                return NULL;
+       }
+
+       return g_strdup(fname);
+}
+
+int log_start_logging(LOG_REC *log)
+{
+       g_return_val_if_fail(log != NULL, FALSE);
+
+       if (log->handle != -1)
+               return TRUE;
+
+       /* Append/create log file */
+       g_free_not_null(log->real_fname);
+       log->real_fname = log_filename(log);
+       log->handle = log->real_fname == NULL ? -1 :
+               open(log->real_fname, O_WRONLY | O_APPEND | O_CREAT,
+                    log_file_create_mode);
+       if (log->handle == -1) {
+               signal_emit("log create failed", 1, log);
+               log->failed = TRUE;
+               return FALSE;
+       }
+#ifdef HAVE_FCNTL
+        memset(&lock, 0, sizeof(lock));
+       lock.l_type = F_WRLCK;
+       if (fcntl(log->handle, F_SETLK, &lock) == -1 && errno == EACCES) {
+               close(log->handle);
+               log->handle = -1;
+               signal_emit("log locked", 1, log);
+               log->failed = TRUE;
+               return FALSE;
+       }
+#endif
+       lseek(log->handle, 0, SEEK_END);
+
+       log->opened = log->last = time(NULL);
+       log_write_timestamp(log->handle,
+                           settings_get_str("log_open_string"),
+                           "\n", log->last);
+
+       signal_emit("log started", 1, log);
+       log->failed = FALSE;
+       return TRUE;
+}
+
+void log_stop_logging(LOG_REC *log)
+{
+       g_return_if_fail(log != NULL);
+
+       if (log->handle == -1)
+               return;
+
+       signal_emit("log stopped", 1, log);
+
+       log_write_timestamp(log->handle,
+                           settings_get_str("log_close_string"),
+                           "\n", time(NULL));
+
+#ifdef HAVE_FCNTL
+        memset(&lock, 0, sizeof(lock));
+       lock.l_type = F_UNLCK;
+       fcntl(log->handle, F_SETLK, &lock);
+#endif
+
+       write_buffer_flush();
+       close(log->handle);
+       log->handle = -1;
+}
+
+static void log_rotate_check(LOG_REC *log)
+{
+       char *new_fname, *dir;
+
+       g_return_if_fail(log != NULL);
+
+       if (log->handle == -1 || log->real_fname == NULL)
+               return;
+
+       new_fname = log_filename(log);
+       if (strcmp(new_fname, log->real_fname) != 0) {
+               /* rotate log */
+               log_stop_logging(log);
+               signal_emit("log rotated", 1, log);
+
+               dir = g_dirname(new_fname);
+               mkpath(dir, LOG_DIR_CREATE_MODE);
+               g_free(dir);
+
+               log_start_logging(log);
+       }
+       g_free(new_fname);
+}
+
+void log_write_rec(LOG_REC *log, const char *str, int level)
+{
+        char *colorstr;
+       struct tm *tm;
+       time_t now;
+       int hour, day;
+
+       g_return_if_fail(log != NULL);
+       g_return_if_fail(str != NULL);
+
+       if (log->handle == -1)
+               return;
+
+       now = time(NULL);
+       tm = localtime(&now);
+       hour = tm->tm_hour;
+       day = tm->tm_mday;
+
+       tm = localtime(&log->last);
+       day -= tm->tm_mday; /* tm breaks in log_rotate_check() .. */
+       if (tm->tm_hour != hour) {
+               /* hour changed, check if we need to rotate log file */
+                log_rotate_check(log);
+       }
+
+       if (day != 0) {
+               /* day changed */
+               log_write_timestamp(log->handle,
+                                   settings_get_str("log_day_changed"),
+                                   "\n", now);
+       }
+
+       log->last = now;
+
+       if (log->colorizer == NULL)
+               colorstr = NULL;
+        else
+                str = colorstr = log->colorizer(str);
+
+        if ((level & MSGLEVEL_LASTLOG) == 0)
+               log_write_timestamp(log->handle, log_timestamp, str, now);
+       else
+               write_buffer(log->handle, str, strlen(str));
+       write_buffer(log->handle, "\n", 1);
+
+       signal_emit("log written", 2, log, str);
+
+        g_free_not_null(colorstr);
+}
+
+LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
+                           const char *servertag)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(log != NULL, NULL);
+       g_return_val_if_fail(item != NULL, NULL);
+
+       for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
+               LOG_ITEM_REC *rec = tmp->data;
+
+               if (rec->type == type && g_strcasecmp(rec->name, item) == 0 &&
+                   (rec->servertag == NULL || (servertag != NULL &&
+                       g_strcasecmp(rec->servertag, servertag) == 0)))
+                       return rec;
+       }
+
+       return NULL;
+}
+
+void log_file_write(const char *server_tag, const char *item, int level,
+                   const char *str, int no_fallbacks)
+{
+       GSList *tmp, *fallbacks;
+       char *tmpstr;
+       int found;
+
+       g_return_if_fail(str != NULL);
+
+       if (logs == NULL)
+               return;
+
+       fallbacks = NULL; found = FALSE;
+
+       for (tmp = logs; tmp != NULL; tmp = tmp->next) {
+               LOG_REC *rec = tmp->data;
+
+               if (rec->handle == -1)
+                       continue; /* log not opened yet */
+
+               if ((level & rec->level) == 0)
+                       continue;
+
+               if (rec->items == NULL)
+                       fallbacks = g_slist_append(fallbacks, rec);
+               else if (item != NULL &&
+                        log_item_find(rec, LOG_ITEM_TARGET, item,
+                                      server_tag) != NULL)
+                       log_write_rec(rec, str, level);
+       }
+
+       if (!found && !no_fallbacks && fallbacks != NULL) {
+               /* not found from any items, so write it to all main logs */
+               tmpstr = (level & MSGLEVEL_PUBLIC) ?
+                       g_strconcat(item, ": ", str, NULL) :
+                       g_strdup(str);
+
+               for (tmp = fallbacks; tmp != NULL; tmp = tmp->next)
+                        log_write_rec(tmp->data, tmpstr, level);
+
+               g_free(tmpstr);
+       }
+        g_slist_free(fallbacks);
+}
+
+LOG_REC *log_find(const char *fname)
+{
+       GSList *tmp;
+
+       for (tmp = logs; tmp != NULL; tmp = tmp->next) {
+               LOG_REC *rec = tmp->data;
+
+               if (strcmp(rec->fname, fname) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static void log_items_update_config(LOG_REC *log, CONFIG_NODE *parent)
+{
+       GSList *tmp;
+       CONFIG_NODE *node;
+
+       parent = config_node_section(parent, "items", NODE_TYPE_LIST);
+       for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
+               LOG_ITEM_REC *rec = tmp->data;
+
+                node = config_node_section(parent, NULL, NODE_TYPE_BLOCK);
+               iconfig_node_set_str(node, "type", log_item_types[rec->type]);
+               iconfig_node_set_str(node, "name", rec->name);
+               iconfig_node_set_str(node, "server", rec->servertag);
+       }
+}
+
+static void log_update_config(LOG_REC *log)
+{
+       CONFIG_NODE *node;
+       char *levelstr;
+
+       if (log->temp)
+               return;
+
+       node = iconfig_node_traverse("logs", TRUE);
+       node = config_node_section(node, log->fname, NODE_TYPE_BLOCK);
+
+       if (log->autoopen)
+               iconfig_node_set_bool(node, "auto_open", TRUE);
+       else
+               iconfig_node_set_str(node, "auto_open", NULL);
+
+       levelstr = bits2level(log->level);
+       iconfig_node_set_str(node, "level", levelstr);
+       g_free(levelstr);
+
+       iconfig_node_set_str(node, "items", NULL);
+
+       if (log->items != NULL)
+               log_items_update_config(log, node);
+
+       signal_emit("log config save", 2, log, node);
+}
+
+static void log_remove_config(LOG_REC *log)
+{
+       iconfig_set_str("logs", log->fname, NULL);
+}
+
+LOG_REC *log_create_rec(const char *fname, int level)
+{
+       LOG_REC *rec;
+
+       g_return_val_if_fail(fname != NULL, NULL);
+
+       rec = log_find(fname);
+       if (rec == NULL) {
+               rec = g_new0(LOG_REC, 1);
+               rec->fname = g_strdup(fname);
+               rec->real_fname = log_filename(rec);
+               rec->handle = -1;
+       }
+
+       rec->level = level;
+       return rec;
+}
+
+void log_item_add(LOG_REC *log, int type, const char *name,
+                 const char *servertag)
+{
+       LOG_ITEM_REC *rec;
+
+       g_return_if_fail(log != NULL);
+       g_return_if_fail(name != NULL);
+
+       if (log_item_find(log, type, name, servertag))
+               return;
+
+       rec = g_new0(LOG_ITEM_REC, 1);
+       rec->type = type;
+       rec->name = g_strdup(name);
+       rec->servertag = g_strdup(servertag);
+
+       log->items = g_slist_append(log->items, rec);
+}
+
+void log_update(LOG_REC *log)
+{
+       g_return_if_fail(log != NULL);
+
+       if (log_find(log->fname) == NULL) {
+               logs = g_slist_append(logs, log);
+               log->handle = -1;
+       }
+
+       log_update_config(log);
+       signal_emit("log new", 1, log);
+}
+
+void log_item_destroy(LOG_REC *log, LOG_ITEM_REC *item)
+{
+       log->items = g_slist_remove(log->items, item);
+
+       g_free(item->name);
+       g_free_not_null(item->servertag);
+       g_free(item);
+}
+
+static void log_destroy(LOG_REC *log)
+{
+       g_return_if_fail(log != NULL);
+
+       if (log->handle != -1)
+               log_stop_logging(log);
+
+       logs = g_slist_remove(logs, log);
+       signal_emit("log remove", 1, log);
+
+       while (log->items != NULL)
+               log_item_destroy(log, log->items->data);
+       g_free(log->fname);
+       g_free_not_null(log->real_fname);
+       g_free(log);
+}
+
+void log_close(LOG_REC *log)
+{
+       g_return_if_fail(log != NULL);
+
+       log_remove_config(log);
+       log_destroy(log);
+}
+
+static int sig_rotate_check(void)
+{
+       static int last_hour = -1;
+       struct tm tm;
+       time_t now;
+
+       /* don't do anything until hour is changed */
+       now = time(NULL);
+       memcpy(&tm, localtime(&now), sizeof(tm));
+       if (tm.tm_hour != last_hour) {
+               last_hour = tm.tm_hour;
+               g_slist_foreach(logs, (GFunc) log_rotate_check, NULL);
+       }
+       return 1;
+}
+
+static void log_items_read_config(CONFIG_NODE *node, LOG_REC *log)
+{
+       LOG_ITEM_REC *rec;
+       GSList *tmp;
+       char *item;
+       int type;
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               if (node->type != NODE_TYPE_BLOCK)
+                       continue;
+
+               item = config_node_get_str(node, "name", NULL);
+               type = log_item_str2type(config_node_get_str(node, "type", NULL));
+               if (item == NULL || type == -1)
+                       continue;
+
+               rec = g_new0(LOG_ITEM_REC, 1);
+               rec->type = type;
+               rec->name = g_strdup(item);
+               rec->servertag = g_strdup(config_node_get_str(node, "server", NULL));
+
+               log->items = g_slist_append(log->items, rec);
+       }
+}
+
+static void log_read_config(void)
+{
+       CONFIG_NODE *node;
+       LOG_REC *log;
+       GSList *tmp, *next, *fnames;
+
+       /* close old logs, save list of open logs */
+       fnames = NULL;
+       for (tmp = logs; tmp != NULL; tmp = next) {
+               log = tmp->data;
+
+               next = tmp->next;
+               if (log->temp)
+                       continue;
+
+               if (log->handle != -1)
+                       fnames = g_slist_append(fnames, g_strdup(log->fname));
+               log_destroy(log);
+       }
+
+       node = iconfig_node_traverse("logs", FALSE);
+       if (node == NULL) return;
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               if (node->type != NODE_TYPE_BLOCK)
+                       continue;
+
+               log = g_new0(LOG_REC, 1);
+               logs = g_slist_append(logs, log);
+
+               log->handle = -1;
+               log->fname = g_strdup(node->key);
+               log->autoopen = config_node_get_bool(node, "auto_open", FALSE);
+               log->level = level2bits(config_node_get_str(node, "level", 0));
+
+               signal_emit("log config read", 2, log, node);
+
+               node = config_node_section(node, "items", -1);
+               if (node != NULL)
+                       log_items_read_config(node, log);
+
+               if (log->autoopen || gslist_find_string(fnames, log->fname))
+                       log_start_logging(log);
+       }
+
+       g_slist_foreach(fnames, (GFunc) g_free, NULL);
+       g_slist_free(fnames);
+}
+
+static void read_settings(void)
+{
+       log_timestamp = settings_get_str("log_timestamp");
+       log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
+}
+
+void log_init(void)
+{
+       rotate_tag = g_timeout_add(60000, (GSourceFunc) sig_rotate_check, NULL);
+       logs = NULL;
+
+       settings_add_int("log", "log_create_mode",
+                        DEFAULT_LOG_FILE_CREATE_MODE);
+       settings_add_str("log", "log_timestamp", "%H:%M ");
+       settings_add_str("log", "log_open_string",
+                        "--- Log opened %a %b %d %H:%M:%S %Y");
+       settings_add_str("log", "log_close_string",
+                        "--- Log closed %a %b %d %H:%M:%S %Y");
+       settings_add_str("log", "log_day_changed",
+                        "--- Day changed %a %b %d %Y");
+
+       read_settings();
+        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+        signal_add("setup reread", (SIGNAL_FUNC) log_read_config);
+        signal_add("irssi init finished", (SIGNAL_FUNC) log_read_config);
+}
+
+void log_deinit(void)
+{
+       g_source_remove(rotate_tag);
+
+       while (logs != NULL)
+               log_close(logs->data);
+
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+        signal_remove("setup reread", (SIGNAL_FUNC) log_read_config);
+        signal_remove("irssi init finished", (SIGNAL_FUNC) log_read_config);
+}
diff --git a/apps/irssi/src/core/log.h b/apps/irssi/src/core/log.h
new file mode 100644 (file)
index 0000000..6ada7c6
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef __LOG_H
+#define __LOG_H
+
+#define LOG_DIR_CREATE_MODE 0700
+
+enum {
+       LOG_ITEM_TARGET, /* channel, query, .. */
+       LOG_ITEM_WINDOW_REFNUM
+};
+
+typedef char *(*COLORIZE_FUNC)(const char *str);
+
+typedef struct {
+       int type;
+        char *name;
+       char *servertag;
+} LOG_ITEM_REC;
+
+typedef struct {
+       char *fname; /* file name, in strftime() format */
+       char *real_fname; /* the current expanded file name */
+       int handle; /* file handle */
+       time_t opened;
+
+       int level; /* log only these levels */
+       GSList *items; /* log only on these items */
+
+       time_t last; /* when last message was written */
+        COLORIZE_FUNC colorizer;
+
+       unsigned int autoopen:1; /* automatically start logging at startup */
+       unsigned int failed:1; /* opening log failed last time */
+       unsigned int temp:1; /* don't save this to config file */
+} LOG_REC;
+
+extern GSList *logs;
+
+/* Create log record - you still need to call log_update() to actually add it
+   into log list */
+LOG_REC *log_create_rec(const char *fname, int level);
+void log_update(LOG_REC *log);
+void log_close(LOG_REC *log);
+LOG_REC *log_find(const char *fname);
+
+void log_item_add(LOG_REC *log, int type, const char *name,
+                 const char *servertag);
+void log_item_destroy(LOG_REC *log, LOG_ITEM_REC *item);
+LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
+                           const char *servertag);
+
+void log_file_write(const char *server_tag, const char *item, int level,
+                   const char *str, int no_fallbacks);
+void log_write_rec(LOG_REC *log, const char *str, int level);
+
+int log_start_logging(LOG_REC *log);
+void log_stop_logging(LOG_REC *log);
+
+void log_init(void);
+void log_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/masks.c b/apps/irssi/src/core/masks.c
new file mode 100644 (file)
index 0000000..7014e43
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ masks.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "network.h"
+#include "misc.h"
+
+#include "servers.h"
+
+/* Returns TRUE if mask contains '!' ie. address should be checked too.
+   Also checks if mask contained any wildcards. */
+static int check_address(const char *mask, int *wildcards)
+{
+       int ret;
+
+       ret = FALSE;
+       while (*mask != '\0') {
+               if (*mask == '!') {
+                       if (*wildcards) return TRUE;
+                       ret = TRUE;
+               }
+
+               if (*mask == '?' || *mask == '*') {
+                       *wildcards = TRUE;
+                       if (ret) return TRUE;
+               }
+               mask++;
+       }
+
+       return ret;
+}
+
+static int check_mask(SERVER_REC *server, const char *mask,
+                     const char *str, int wildcards)
+{
+       if (server != NULL && server->mask_match_func != NULL) {
+               /* use server specified mask match function */
+               return server->mask_match_func(mask, str);
+       }
+
+       return wildcards ? match_wildcards(mask, str) :
+               g_strcasecmp(mask, str) == 0;
+}
+
+int mask_match(SERVER_REC *server, const char *mask,
+              const char *nick, const char *user, const char *host)
+{
+       char *str;
+       int ret, wildcards;
+
+       g_return_val_if_fail(server == NULL || IS_SERVER(server), FALSE);
+       g_return_val_if_fail(mask != NULL && nick != NULL &&
+                            nick != NULL && host != NULL, FALSE);
+
+       str = !check_address(mask, &wildcards) ? (char *) nick :
+               g_strdup_printf("%s!%s@%s", nick, user, host);
+       ret = check_mask(server, mask, str, wildcards);
+       if (str != nick) g_free(str);
+
+       return ret;
+}
+
+int mask_match_address(SERVER_REC *server, const char *mask,
+                      const char *nick, const char *address)
+{
+       char *str;
+       int ret, wildcards;
+
+       g_return_val_if_fail(server == NULL || IS_SERVER(server), FALSE);
+       g_return_val_if_fail(mask != NULL && nick != NULL, FALSE);
+       if (address == NULL) address = "";
+
+       str = !check_address(mask, &wildcards) ? (char *) nick :
+               g_strdup_printf("%s!%s", nick, address);
+       ret = check_mask(server, mask, str, wildcards);
+       if (str != nick) g_free(str);
+
+       return ret;
+}
+
+int masks_match(SERVER_REC *server, const char *masks,
+               const char *nick, const char *address)
+{
+       int (*mask_match_func)(const char *, const char *);
+       char **list, **tmp, *mask;
+       int found;
+
+       g_return_val_if_fail(server == NULL || IS_SERVER(server), FALSE);
+       g_return_val_if_fail(masks != NULL &&
+                            nick != NULL && address != NULL, FALSE);
+
+       if (*masks == '\0')
+                return FALSE;
+
+       mask_match_func = server != NULL && server->mask_match_func != NULL ?
+               server->mask_match_func : match_wildcards;
+
+       found = FALSE;
+       mask = g_strdup_printf("%s!%s", nick, address);
+       list = g_strsplit(masks, " ", -1);
+       for (tmp = list; *tmp != NULL; tmp++) {
+               if (g_strcasecmp(*tmp, nick) == 0) {
+                        found = TRUE;
+                       break;
+               }
+
+               if (mask_match_func(*tmp, mask)) {
+                       found = TRUE;
+                       break;
+               }
+       }
+       g_strfreev(list);
+       g_free(mask);
+
+       return found;
+}
diff --git a/apps/irssi/src/core/masks.h b/apps/irssi/src/core/masks.h
new file mode 100644 (file)
index 0000000..b6ce20d
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __MASKS_H
+#define __MASKS_H
+
+int mask_match(SERVER_REC *server, const char *mask,
+              const char *nick, const char *user, const char *host);
+int mask_match_address(SERVER_REC *server, const char *mask,
+                      const char *nick, const char *address);
+int masks_match(SERVER_REC *server, const char *masks,
+               const char *nick, const char *address);
+
+#endif
diff --git a/apps/irssi/src/core/misc.c b/apps/irssi/src/core/misc.c
new file mode 100644 (file)
index 0000000..d57c7f6
--- /dev/null
@@ -0,0 +1,772 @@
+/*
+ misc.c : irssi
+
+    Copyright (C) 1999 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "misc.h"
+#include "pidwait.h"
+
+#include <errno.h>
+#ifdef HAVE_REGEX_H
+#  include <regex.h>
+#endif
+
+typedef struct {
+       int condition;
+       GInputFunction function;
+        void *data;
+} IRSSI_INPUT_REC;
+
+static int irssi_io_invoke(GIOChannel *source, GIOCondition condition,
+                          void *data)
+{
+       IRSSI_INPUT_REC *rec = data;
+       int icond = 0;
+
+       if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               /* error, we have to call the function.. */
+               if (rec->condition & G_IO_IN)
+                       icond |= G_INPUT_READ;
+               else
+                       icond |= G_INPUT_WRITE;
+       }
+
+       if (condition & (G_IO_IN | G_IO_PRI))
+               icond |= G_INPUT_READ;
+       if (condition & G_IO_OUT)
+               icond |= G_INPUT_WRITE;
+
+       if (rec->condition & icond)
+               rec->function(rec->data, source, icond);
+
+       return TRUE;
+}
+
+int g_input_add_full(GIOChannel *source, int priority, int condition,
+                    GInputFunction function, void *data)
+{
+        IRSSI_INPUT_REC *rec;
+       unsigned int result;
+       GIOCondition cond;
+
+       rec = g_new(IRSSI_INPUT_REC, 1);
+       rec->condition = condition;
+       rec->function = function;
+       rec->data = data;
+
+       cond = (GIOCondition) (G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+       if (condition & G_INPUT_READ)
+               cond |= G_IO_IN|G_IO_PRI;
+       if (condition & G_INPUT_WRITE)
+               cond |= G_IO_OUT;
+
+       result = g_io_add_watch_full(source, priority, cond,
+                                    irssi_io_invoke, rec, g_free);
+
+       return result;
+}
+
+int g_input_add(GIOChannel *source, int condition,
+               GInputFunction function, void *data)
+{
+       return g_input_add_full(source, G_PRIORITY_DEFAULT, condition,
+                               function, data);
+}
+
+int g_timeval_cmp(const GTimeVal *tv1, const GTimeVal *tv2)
+{
+       if (tv1->tv_sec < tv2->tv_sec)
+               return -1;
+       if (tv1->tv_sec > tv2->tv_sec)
+               return 1;
+
+       return tv1->tv_usec < tv2->tv_usec ? -1 :
+               tv1->tv_usec > tv2->tv_usec ? 1 : 0;
+}
+
+long get_timeval_diff(const GTimeVal *tv1, const GTimeVal *tv2)
+{
+       long secs, usecs;
+
+       secs = tv1->tv_sec - tv2->tv_sec;
+       usecs = tv1->tv_usec - tv2->tv_usec;
+       if (usecs < 0) {
+               usecs += 1000000;
+               secs--;
+       }
+       usecs = usecs/1000 + secs * 1000;
+
+       return usecs;
+}
+
+int find_substr(const char *list, const char *item)
+{
+       const char *ptr;
+
+       g_return_val_if_fail(list != NULL, FALSE);
+       g_return_val_if_fail(item != NULL, FALSE);
+
+       if (*item == '\0')
+               return FALSE;
+
+       for (;;) {
+               while (i_isspace(*list)) list++;
+               if (*list == '\0') break;
+
+               ptr = strchr(list, ' ');
+               if (ptr == NULL) ptr = list+strlen(list);
+
+               if (g_strncasecmp(list, item, ptr-list) == 0 &&
+                   item[ptr-list] == '\0')
+                       return TRUE;
+
+               list = ptr;
+       }
+
+       return FALSE;
+}
+
+int strarray_length(char **array)
+{
+       int len;
+
+       g_return_val_if_fail(array != NULL, 0);
+
+       len = 0;
+       while (*array) {
+               len++;
+                array++;
+       }
+        return len;
+}
+
+int strarray_find(char **array, const char *item)
+{
+       char **tmp;
+       int index;
+
+       g_return_val_if_fail(array != NULL, 0);
+       g_return_val_if_fail(item != NULL, 0);
+
+       index = 0;
+       for (tmp = array; *tmp != NULL; tmp++, index++) {
+               if (g_strcasecmp(*tmp, item) == 0)
+                       return index;
+       }
+
+       return -1;
+}
+
+int execute(const char *cmd)
+{
+       char **args;
+#ifndef WIN32
+       int pid;
+#endif
+
+       g_return_val_if_fail(cmd != NULL, -1);
+
+#ifndef WIN32
+       pid = fork();
+       if (pid == -1) return FALSE;
+       if (pid != 0) {
+               pidwait_add(pid);
+               return pid;
+       }
+
+       args = g_strsplit(cmd, " ", -1);
+       execvp(args[0], args);
+       g_strfreev(args);
+
+       _exit(99);
+       return -1;
+#else
+       args = g_strsplit(cmd, " ", -1);
+       _spawnvp(_P_DETACH, args[0], args);
+       g_strfreev(args);
+       return 0;
+#endif
+}
+
+GSList *gslist_find_string(GSList *list, const char *key)
+{
+       for (list = list; list != NULL; list = list->next)
+               if (strcmp(list->data, key) == 0) return list;
+
+       return NULL;
+}
+
+GSList *gslist_find_icase_string(GSList *list, const char *key)
+{
+       for (list = list; list != NULL; list = list->next)
+               if (g_strcasecmp(list->data, key) == 0) return list;
+
+       return NULL;
+}
+
+void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data)
+{
+       void *ret;
+
+       while (list != NULL) {
+               ret = func(list->data, (void *) data);
+                if (ret != NULL) return ret;
+
+               list = list->next;
+       }
+
+       return NULL;
+}
+
+/* `list' contains pointer to structure with a char* to string. */
+char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
+{
+       GString *str;
+       char **data, *ret;
+
+       str = g_string_new(NULL);
+       while (list != NULL) {
+               data = G_STRUCT_MEMBER_P(list->data, offset);
+
+               if (str->len != 0) g_string_append(str, delimiter);
+               g_string_append(str, *data);
+               list = list->next;
+       }
+
+        ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+/* `list' contains char* */
+char *gslist_to_string(GSList *list, const char *delimiter)
+{
+       GString *str;
+       char *ret;
+
+       str = g_string_new(NULL);
+       while (list != NULL) {
+               if (str->len != 0) g_string_append(str, delimiter);
+               g_string_append(str, list->data);
+
+               list = list->next;
+       }
+
+        ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+void hash_save_key(char *key, void *value, GSList **list)
+{
+        *list = g_slist_append(*list, key);
+}
+
+/* save all keys in hash table to linked list - you shouldn't remove any
+   items while using this list, use g_slist_free() after you're done with it */
+GSList *hashtable_get_keys(GHashTable *hash)
+{
+       GSList *list;
+
+       list = NULL;
+       g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list);
+       return list;
+}
+
+GList *glist_find_string(GList *list, const char *key)
+{
+       for (list = list; list != NULL; list = list->next)
+               if (strcmp(list->data, key) == 0) return list;
+
+       return NULL;
+}
+
+GList *glist_find_icase_string(GList *list, const char *key)
+{
+       for (list = list; list != NULL; list = list->next)
+               if (g_strcasecmp(list->data, key) == 0) return list;
+
+       return NULL;
+}
+
+char *stristr(const char *data, const char *key)
+{
+       const char *max;
+       int keylen, datalen, pos;
+
+       keylen = strlen(key);
+       datalen = strlen(data);
+
+       if (keylen > datalen)
+               return NULL;
+       if (keylen == 0)
+               return (char *) data;
+
+       max = data+datalen-keylen;
+       pos = 0;
+       while (data <= max) {
+               if (key[pos] == '\0')
+                        return (char *) data;
+
+               if (i_toupper(data[pos]) == i_toupper(key[pos]))
+                       pos++;
+               else {
+                       data++;
+                        pos = 0;
+               }
+       }
+
+       return NULL;
+}
+
+#define isbound(c) \
+       ((unsigned char) (c) < 128 && \
+       (i_isspace(c) || i_ispunct(c)))
+
+char *strstr_full_case(const char *data, const char *key, int icase)
+{
+       const char *start, *max;
+       int keylen, datalen, pos, match;
+
+       keylen = strlen(key);
+       datalen = strlen(data);
+
+       if (keylen > datalen)
+               return NULL;
+       if (keylen == 0)
+               return (char *) data;
+
+       max = data+datalen-keylen;
+       start = data; pos = 0;
+       while (data <= max) {
+               if (key[pos] == '\0') {
+                       if (data[pos] != '\0' && !isbound(data[pos])) {
+                               data++;
+                               pos = 0;
+                                continue;
+                       }
+                       return (char *) data;
+               }
+
+               match = icase ? (i_toupper(data[pos]) == i_toupper(key[pos])) :
+                                data[pos] == key[pos];
+
+               if (match && (pos != 0 || data == start || isbound(data[-1])))
+                       pos++;
+               else {
+                       data++;
+                        pos = 0;
+               }
+       }
+
+       return NULL;
+}
+
+char *strstr_full(const char *data, const char *key)
+{
+        return strstr_full_case(data, key, FALSE);
+}
+
+char *stristr_full(const char *data, const char *key)
+{
+        return strstr_full_case(data, key, TRUE);
+}
+
+int regexp_match(const char *str, const char *regexp)
+{
+#ifdef HAVE_REGEX_H
+       regex_t preg;
+       int ret;
+
+       if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0)
+                return 0;
+
+       ret = regexec(&preg, str, 0, NULL, 0);
+       regfree(&preg);
+
+       return ret == 0;
+#else
+       return FALSE;
+#endif
+}
+
+/* Create the directory and all it's parent directories */
+int mkpath(const char *path, int mode)
+{
+       struct stat statbuf;
+       const char *p;
+       char *dir;
+
+       g_return_val_if_fail(path != NULL, -1);
+
+       p = g_path_skip_root((char *) path);
+       if (p == NULL) {
+               /* not a full path, maybe not what we wanted
+                  but continue anyway.. */
+                p = path;
+       }
+       for (;;) {
+               if (*p != G_DIR_SEPARATOR && *p != '\0') {
+                       p++;
+                       continue;
+               }
+
+               dir = g_strndup(path, (int) (p-path));
+               if (stat(dir, &statbuf) != 0) {
+#ifndef WIN32
+                       if (mkdir(dir, mode) == -1)
+#else
+                       if (_mkdir(dir) == -1)
+#endif
+                       {
+                               g_free(dir);
+                               return -1;
+                       }
+               }
+               g_free(dir);
+
+               if (*p++ == '\0')
+                       break;
+       }
+
+       return 0;
+}
+
+/* convert ~/ to $HOME */
+char *convert_home(const char *path)
+{
+       return *path == '~' && (*(path+1) == '/' || *(path+1) == '\0') ?
+               g_strconcat(g_get_home_dir(), path+1, NULL) :
+               g_strdup(path);
+}
+
+int g_istr_equal(gconstpointer v, gconstpointer v2)
+{
+       return g_strcasecmp((const char *) v, (const char *) v2) == 0;
+}
+
+int g_istr_cmp(gconstpointer v, gconstpointer v2)
+{
+       return g_strcasecmp((const char *) v, (const char *) v2);
+}
+
+/* a char* hash function from ASU */
+unsigned int g_istr_hash(gconstpointer v)
+{
+       const char *s = (const char *) v;
+       unsigned int h = 0, g;
+
+       while (*s != '\0') {
+               h = (h << 4) + i_toupper(*s);
+               if ((g = h & 0xf0000000UL)) {
+                       h = h ^ (g >> 24);
+                       h = h ^ g;
+               }
+               s++;
+       }
+
+       return h /* % M */;
+}
+
+/* Find `mask' from `data', you can use * and ? wildcards. */
+int match_wildcards(const char *cmask, const char *data)
+{
+       char *mask, *newmask, *p1, *p2;
+       int ret;
+
+       newmask = mask = g_strdup(cmask);
+       for (; *mask != '\0' && *data != '\0'; mask++) {
+               if (*mask != '*') {
+                       if (*mask != '?' && i_toupper(*mask) != i_toupper(*data))
+                               break;
+
+                       data++;
+                       continue;
+               }
+
+               while (*mask == '?' || *mask == '*') mask++;
+               if (*mask == '\0') {
+                       data += strlen(data);
+                       break;
+               }
+
+               p1 = strchr(mask, '*');
+               p2 = strchr(mask, '?');
+               if (p1 == NULL || (p2 < p1 && p2 != NULL)) p1 = p2;
+
+               if (p1 != NULL) *p1 = '\0';
+
+               data = stristr(data, mask);
+               if (data == NULL) break;
+
+               data += strlen(mask);
+               mask += strlen(mask)-1;
+
+               if (p1 != NULL) *p1 = p1 == p2 ? '?' : '*';
+       }
+
+       while (*mask == '*') mask++;
+
+       ret = data != NULL && *data == '\0' && *mask == '\0';
+       g_free(newmask);
+
+       return ret;
+}
+
+/* Return TRUE if all characters in `str' are numbers.
+   Stop when `end_char' is found from string. */
+int is_numeric(const char *str, char end_char)
+{
+       g_return_val_if_fail(str != NULL, FALSE);
+
+       if (*str == '\0' || *str == end_char)
+               return FALSE;
+
+       while (*str != '\0' && *str != end_char) {
+               if (!i_isdigit(*str)) return FALSE;
+               str++;
+       }
+
+       return TRUE;
+}
+
+/* replace all `from' chars in string to `to' chars. returns `str' */
+char *replace_chars(char *str, char from, char to)
+{
+       char *p;
+
+       for (p = str; *p != '\0'; p++) {
+               if (*p == from) *p = to;
+       }
+       return str;
+}
+
+int octal2dec(int octal)
+{
+       int dec, n;
+
+       dec = 0; n = 1;
+       while (octal != 0) {
+               dec += n*(octal%10);
+               octal /= 10; n *= 8;
+       }
+
+       return dec;
+}
+
+int dec2octal(int decimal)
+{
+       int octal, pos;
+
+       octal = 0; pos = 0;
+       while (decimal > 0) {
+               octal += (decimal & 7)*(pos == 0 ? 1 : pos);
+               decimal /= 8;
+               pos += 10;
+       }
+
+       return octal;
+}
+
+/* convert all low-ascii (<32) to ^<A..> combinations */
+char *show_lowascii(const char *channel)
+{
+       char *str, *p;
+
+       str = p = g_malloc(strlen(channel)*2+1);
+       while (*channel != '\0') {
+               if ((unsigned char) *channel >= 32)
+                       *p++ = *channel;
+               else {
+                       *p++ = '^';
+                       *p++ = *channel + 'A'-1;
+               }
+               channel++;
+       }
+       *p = '\0';
+
+       return str;
+}
+
+/* Get time in human readable form with localtime() + asctime() */
+char *my_asctime(time_t t)
+{
+       struct tm *tm;
+       char *str;
+        int len;
+
+       tm = localtime(&t);
+       str = g_strdup(asctime(tm));
+
+       len = strlen(str);
+       if (len > 0) str[len-1] = '\0';
+        return str;
+}
+
+/* Returns number of columns needed to print items.
+   save_column_widths is filled with length of each column. */
+int get_max_column_count(GSList *items, COLUMN_LEN_FUNC len_func,
+                        int max_width, int max_columns,
+                        int item_extra, int item_min_size,
+                        int **save_column_widths, int *rows)
+{
+        GSList *tmp;
+       int **columns, *columns_width, *columns_rows;
+       int item_pos, items_count;
+       int ret, len, max_len, n, col;
+
+       items_count = g_slist_length(items);
+       if (items_count == 0) {
+               *save_column_widths = NULL;
+                *rows = 0;
+               return 0;
+       }
+
+       len = max_width/(item_extra+item_min_size);
+        if (len <= 0) len = 1;
+       if (max_columns <= 0 || len < max_columns)
+                max_columns = len;
+
+       columns = g_new0(int *, max_columns);
+       columns_width = g_new0(int, max_columns);
+       columns_rows = g_new0(int, max_columns);
+
+       for (n = 1; n < max_columns; n++) {
+               columns[n] = g_new0(int, n+1);
+               columns_rows[n] = items_count <= n+1 ? 1 :
+                        (items_count+n)/(n+1);
+       }
+
+       /* for each possible column count, save the column widths and
+          find the biggest column count that fits to screen. */
+        item_pos = 0; max_len = 0;
+       for (tmp = items; tmp != NULL; tmp = tmp->next) {
+               len = item_extra+len_func(tmp->data);
+               if (max_len < len)
+                       max_len = len;
+
+               for (n = 1; n < max_columns; n++) {
+                       if (columns_width[n] > max_width)
+                               continue; /* too wide */
+
+                       col = item_pos/columns_rows[n];
+                       if (columns[n][col] < len) {
+                               columns_width[n] += len-columns[n][col];
+                                columns[n][col] = len;
+                       }
+               }
+
+                item_pos++;
+       }
+
+       for (n = max_columns-1; n >= 1; n--) {
+               if (columns_width[n] <= max_width &&
+                   columns[n][n] > 0)
+                        break;
+       }
+        ret = n+1;
+
+       *save_column_widths = g_new(int, ret);
+       if (ret == 1) {
+                **save_column_widths = max_len;
+                *rows = 1;
+       } else {
+               memcpy(*save_column_widths, columns[ret-1], sizeof(int)*ret);
+               *rows = columns_rows[ret-1];
+       }
+
+       for (n = 1; n < max_columns; n++)
+                g_free(columns[n]);
+       g_free(columns_width);
+       g_free(columns_rows);
+       g_free(columns);
+
+        return ret;
+}
+
+/* Return a column sorted copy of a list. */
+GSList *columns_sort_list(GSList *list, int rows)
+{
+        GSList *tmp, *sorted;
+       int row, skip;
+
+       if (list == NULL || rows == 0)
+                return list;
+
+       sorted = NULL;
+
+       for (row = 0; row < rows; row++) {
+                tmp = g_slist_nth(list, row);
+                skip = 1;
+               for (; tmp != NULL; tmp = tmp->next) {
+                       if (--skip == 0) {
+                                skip = rows;
+                               sorted = g_slist_append(sorted, tmp->data);
+                       }
+               }
+       }
+
+       g_return_val_if_fail(g_slist_length(sorted) ==
+                            g_slist_length(list), sorted);
+        return sorted;
+}
+
+/* Expand escape string, the first character in data should be the
+   one after '\'. Returns the expanded character or -1 if error. */
+int expand_escape(const char **data)
+{
+        char digit[4];
+
+       switch (**data) {
+       case 't':
+               return '\t';
+       case 'r':
+               return '\r';
+       case 'n':
+               return '\n';
+       case 'e':
+               return 27; /* ESC */
+
+       case 'x':
+                /* hex digit */
+               if (!isxdigit((*data)[1]) || !isxdigit((*data)[2]))
+                       return -1;
+
+               digit[0] = (*data)[1];
+               digit[1] = (*data)[2];
+                digit[2] = '\0';
+               *data += 2;
+               return strtol(digit, NULL, 16);
+       case 'c':
+                /* control character (\cA = ^A) */
+                (*data)++;
+               return i_toupper(**data) - 64;
+       default:
+               if (!i_isdigit(**data))
+                       return -1;
+
+                /* octal */
+                digit[0] = (*data)[0];
+                digit[1] = (*data)[1];
+               digit[2] = (*data)[2];
+                digit[3] = '\0';
+               *data += 2;
+               return strtol(digit, NULL, 8);
+       }
+}
diff --git a/apps/irssi/src/core/misc.h b/apps/irssi/src/core/misc.h
new file mode 100644 (file)
index 0000000..804cffc
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef __MISC_H
+#define __MISC_H
+
+/* `str' should be type char[MAX_INT_STRLEN] */
+#define ltoa(str, num) \
+       g_snprintf(str, sizeof(str), "%d", num)
+
+typedef void* (*FOREACH_FIND_FUNC) (void *item, void *data);
+typedef int (*COLUMN_LEN_FUNC)(void *data);
+
+static inline int nearest_power(int num)
+{
+       int n = 1;
+
+       while (n < num) n <<= 1;
+       return n;
+}
+
+/* Returns 1 if tv1 > tv2, -1 if tv2 > tv1 or 0 if they're equal. */
+int g_timeval_cmp(const GTimeVal *tv1, const GTimeVal *tv2);
+/* Returns "tv1 - tv2", returns the result in milliseconds. Note that
+   if the difference is too large, the result might be invalid. */
+long get_timeval_diff(const GTimeVal *tv1, const GTimeVal *tv2);
+
+/* find `item' from a space separated `list' */
+int find_substr(const char *list, const char *item);
+/* return how many items `array' has */
+int strarray_length(char **array);
+/* return index of `item' in `array' or -1 if not found */
+int strarray_find(char **array, const char *item);
+
+int execute(const char *cmd); /* returns pid or -1 = error */
+
+GSList *gslist_find_string(GSList *list, const char *key);
+GSList *gslist_find_icase_string(GSList *list, const char *key);
+GList *glist_find_string(GList *list, const char *key);
+GList *glist_find_icase_string(GList *list, const char *key);
+
+void *gslist_foreach_find(GSList *list, FOREACH_FIND_FUNC func, const void *data);
+
+/* `list' contains pointer to structure with a char* to string. */
+char *gslistptr_to_string(GSList *list, int offset, const char *delimiter);
+/* `list' contains char* */
+char *gslist_to_string(GSList *list, const char *delimiter);
+
+/* save all keys in hash table to linked list - you shouldn't remove any
+   items while using this list, use g_slist_free() after you're done with it */
+GSList *hashtable_get_keys(GHashTable *hash);
+
+/* strstr() with case-ignoring */
+char *stristr(const char *data, const char *key);
+
+/* like strstr(), but matches only for full words.
+   `icase' specifies if match is case sensitive */
+char *strstr_full_case(const char *data, const char *key, int icase);
+char *strstr_full(const char *data, const char *key);
+char *stristr_full(const char *data, const char *key);
+
+/* easy way to check if regexp matches */
+int regexp_match(const char *str, const char *regexp);
+
+/* Create the directory and all it's parent directories */
+int mkpath(const char *path, int mode);
+/* convert ~/ to $HOME */
+char *convert_home(const char *path);
+
+/* Case-insensitive string hash functions */
+int g_istr_equal(gconstpointer v, gconstpointer v2);
+unsigned int g_istr_hash(gconstpointer v);
+
+/* Case-insensitive GCompareFunc func */
+int g_istr_cmp(gconstpointer v, gconstpointer v2);
+
+/* Find `mask' from `data', you can use * and ? wildcards. */
+int match_wildcards(const char *mask, const char *data);
+
+/* Return TRUE if all characters in `str' are numbers.
+   Stop when `end_char' is found from string. */
+int is_numeric(const char *str, char end_char);
+
+/* replace all `from' chars in string to `to' chars. returns `str' */
+char *replace_chars(char *str, char from, char to);
+
+/* octal <-> decimal conversions */
+int octal2dec(int octal);
+int dec2octal(int decimal);
+
+/* convert all low-ascii (<32) to ^<A..> combinations */
+char *show_lowascii(const char *channel);
+
+/* Get time in human readable form with localtime() + asctime() */
+char *my_asctime(time_t t);
+
+/* Returns number of columns needed to print items.
+   save_column_widths is filled with length of each column. */
+int get_max_column_count(GSList *items, COLUMN_LEN_FUNC len_func,
+                        int max_width, int max_columns,
+                        int item_extra, int item_min_size,
+                        int **save_column_widths, int *rows);
+
+/* Return a column sorted copy of a list. */
+GSList *columns_sort_list(GSList *list, int rows);
+
+/* Expand escape string, the first character in data should be the
+   one after '\'. Returns the expanded character or -1 if error. */
+int expand_escape(const char **data);
+
+#endif
diff --git a/apps/irssi/src/core/module.h b/apps/irssi/src/core/module.h
new file mode 100644 (file)
index 0000000..8978438
--- /dev/null
@@ -0,0 +1,3 @@
+#include "common.h"
+
+#define MODULE_NAME "core"
diff --git a/apps/irssi/src/core/modules-load.c b/apps/irssi/src/core/modules-load.c
new file mode 100644 (file)
index 0000000..e7e7809
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ modules-load.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "modules-load.h"
+#include "signals.h"
+
+#include "settings.h"
+#include "commands.h"
+#include "misc.h"
+
+#ifdef HAVE_GMODULE
+
+/* Returns the module name without path, "lib" prefix or ".so" suffix */
+static char *module_get_name(const char *path, int *start, int *end)
+{
+       const char *name;
+       char *module_name, *ptr;
+
+        name = NULL;
+       if (*path == '~' || g_path_is_absolute(path)) {
+               name = strrchr(path, G_DIR_SEPARATOR);
+                if (name != NULL) name++;
+       }
+
+       if (name == NULL)
+               name = path;
+
+       if (strncmp(name, "lib", 3) == 0)
+               name += 3;
+
+       module_name = g_strdup(name);
+       ptr = strchr(module_name, '.');
+       if (ptr != NULL) *ptr = '\0';
+
+       *start = (int) (name-path);
+       *end = *start + (ptr == NULL ? strlen(name) :
+                        (int) (ptr-module_name));
+
+       return module_name;
+}
+
+/* Returns the root module name for given submodule (eg. perl_core -> perl) */
+static char *module_get_root(const char *name, char **prefixes)
+{
+       int len;
+
+       /* skip any of the prefixes.. */
+       while (*prefixes != NULL) {
+                len = strlen(*prefixes);
+               if (strncmp(name, *prefixes, len) == 0 && name[len] == '_') {
+                       name += len+1;
+                        break;
+               }
+               prefixes++;
+       }
+
+       /* skip the _core part */
+        len = strlen(name);
+       if (len > 5 && strcmp(name+len-5, "_core") == 0)
+               return g_strndup(name, len-5);
+
+        return g_strdup(name);
+}
+
+/* Returns the sub module name for given submodule (eg. perl_core -> core) */
+static char *module_get_sub(const char *name, const char *root)
+{
+       int rootlen, namelen;
+
+        namelen = strlen(name);
+       rootlen = strlen(root);
+        g_return_val_if_fail(namelen >= rootlen, g_strdup(name));
+
+       if (strncmp(name, root, rootlen) == 0 &&
+           strcmp(name+rootlen, "_core") == 0)
+                return g_strdup("core");
+
+       if (namelen+1 > rootlen && name[namelen-rootlen-1] == '_' &&
+           strcmp(name+namelen-rootlen, root) == 0)
+                return g_strndup(name, namelen-rootlen-1);
+
+        return g_strdup(name);
+}
+
+static GModule *module_open(const char *name, int *found)
+{
+       struct stat statbuf;
+       GModule *module;
+       char *path, *str;
+
+       if (g_path_is_absolute(name) || *name == '~' ||
+           (*name == '.' && name[1] == G_DIR_SEPARATOR))
+               path = g_strdup(name);
+       else {
+               /* first try from home dir */
+               str = g_strdup_printf("%s/modules", get_irssi_dir());
+               path = g_module_build_path(str, name);
+               g_free(str);
+
+               if (stat(path, &statbuf) == 0) {
+                       module = g_module_open(path, (GModuleFlags) 0);
+                       g_free(path);
+                       *found = TRUE;
+                       return module;
+               }
+
+               /* module not found from home dir, try global module dir */
+               g_free(path);
+               path = g_module_build_path(MODULEDIR, name);
+       }
+
+       *found = stat(path, &statbuf) == 0;
+       module = g_module_open(path, (GModuleFlags) 0);
+       g_free(path);
+       return module;
+}
+
+static char *module_get_func(const char *rootmodule, const char *submodule,
+                            const char *function)
+{
+       if (strcmp(submodule, "core") == 0)
+               return g_strconcat(rootmodule, "_core_", function, NULL);
+
+       if (strcmp(rootmodule, submodule) == 0)
+               return g_strconcat(rootmodule, "_", function, NULL);
+
+       return g_strconcat(submodule, "_", rootmodule, "_", function, NULL);
+}
+
+#define module_error(error, text, rootmodule, submodule) \
+       signal_emit("module error", 4, GINT_TO_POINTER(error), text, \
+                   rootmodule, submodule)
+
+/* Returns 1 if ok, 0 if error in module and
+   -1 if module wasn't found */
+static int module_load_name(const char *path, const char *rootmodule,
+                           const char *submodule, int silent)
+{
+       void (*module_init) (void);
+       void (*module_deinit) (void);
+       GModule *gmodule;
+        MODULE_REC *module;
+       MODULE_FILE_REC *rec;
+       char *initfunc, *deinitfunc;
+        int found;
+
+       gmodule = module_open(path, &found);
+       if (gmodule == NULL) {
+               if (!silent || found) {
+                       module_error(MODULE_ERROR_LOAD, g_module_error(),
+                                    rootmodule, submodule);
+               }
+               return found ? 0 : -1;
+       }
+
+       /* get the module's init() and deinit() functions */
+       initfunc = module_get_func(rootmodule, submodule, "init");
+       deinitfunc = module_get_func(rootmodule, submodule, "deinit");
+       found = g_module_symbol(gmodule, initfunc, (gpointer *) &module_init) &&
+               g_module_symbol(gmodule, deinitfunc, (gpointer *) &module_deinit);
+       g_free(initfunc);
+       g_free(deinitfunc);
+
+       if (!found) {
+               module_error(MODULE_ERROR_INVALID, NULL,
+                            rootmodule, submodule);
+               g_module_close(gmodule);
+               return 0;
+       }
+
+       /* Call the module's init() function - it should register itself
+          with module_register() function, abort if it doesn't. */
+       module_init();
+
+       module = module_find(rootmodule);
+       rec = module == NULL ? NULL :
+                strcmp(rootmodule, submodule) == 0 ?
+               module_file_find(module, "core") :
+               module_file_find(module, submodule);
+       if (rec == NULL) {
+               rec = module_register_full(rootmodule, submodule, NULL);
+               rec->gmodule = gmodule;
+               module_file_unload(rec);
+
+               module_error(MODULE_ERROR_INVALID, NULL,
+                            rootmodule, submodule);
+                return 0;
+       }
+
+        rec->module_deinit = module_deinit;
+       rec->gmodule = gmodule;
+        rec->initialized = TRUE;
+
+       settings_check_module(rec->defined_module_name);
+
+       signal_emit("module loaded", 2, rec->root, rec);
+       return 1;
+}
+
+static int module_load_prefixes(const char *path, const char *module,
+                               int start, int end, char **prefixes)
+{
+        GString *realpath;
+        int status, ok;
+
+        /* load module_core */
+       realpath = g_string_new(path);
+       g_string_insert(realpath, end, "_core");
+
+       /* Don't print the error message the first time, since the module
+          may not have the core part at all. */
+       status = module_load_name(realpath->str, module, "core", TRUE);
+        ok = status > 0;
+
+       if (prefixes != NULL) {
+               /* load all the "prefix modules", like the fe-common, irc,
+                  etc. part of the module */
+               while (*prefixes != NULL) {
+                        g_string_assign(realpath, path);
+                       g_string_insert_c(realpath, start, '_');
+                       g_string_insert(realpath, start, *prefixes);
+
+                       status = module_load_name(realpath->str, module,
+                                                 *prefixes, TRUE);
+                       if (status > 0)
+                               ok = TRUE;
+
+                        prefixes++;
+               }
+       }
+
+       if (!ok) {
+                /* error loading module, print the error message */
+               g_string_assign(realpath, path);
+               g_string_insert(realpath, end, "_core");
+               module_load_name(realpath->str, module, "core", FALSE);
+       }
+
+       g_string_free(realpath, TRUE);
+        return ok;
+}
+
+static int module_load_full(const char *path, const char *rootmodule,
+                           const char *submodule, int start, int end,
+                           char **prefixes)
+{
+       MODULE_REC *module;
+        int status, try_prefixes;
+
+       if (!g_module_supported())
+               return FALSE;
+
+       module = module_find(rootmodule);
+       if (module != NULL && (strcmp(submodule, rootmodule) == 0 ||
+                              module_file_find(module, submodule) != NULL)) {
+                /* module is already loaded */
+               module_error(MODULE_ERROR_ALREADY_LOADED, NULL,
+                            rootmodule, submodule);
+                return FALSE;
+       }
+
+       /* check if the given module exists.. */
+       try_prefixes = strcmp(rootmodule, submodule) == 0;
+       status = module_load_name(path, rootmodule, submodule, try_prefixes);
+       if (status == -1 && try_prefixes) {
+               /* nope, try loading the module_core,
+                  fe_module, etc. */
+               status = module_load_prefixes(path, rootmodule,
+                                             start, end, prefixes);
+       }
+
+       return status > 0;
+}
+
+/* Load module - automatically tries to load also the related non-core
+   modules given in `prefixes' (like irc, fe, fe_text, ..) */
+int module_load(const char *path, char **prefixes)
+{
+       char *exppath, *name, *submodule, *rootmodule;
+        int start, end, ret;
+
+       g_return_val_if_fail(path != NULL, FALSE);
+
+       exppath = convert_home(path);
+
+       name = module_get_name(exppath, &start, &end);
+       rootmodule = module_get_root(name, prefixes);
+       submodule = module_get_sub(name, rootmodule);
+       g_free(name);
+
+       ret = module_load_full(exppath, rootmodule, submodule,
+                              start, end, prefixes);
+
+       g_free(rootmodule);
+       g_free(submodule);
+        g_free(exppath);
+        return ret;
+}
+
+/* Load a sub module. */
+int module_load_sub(const char *path, const char *submodule, char **prefixes)
+{
+        GString *full_path;
+       char *exppath, *name, *rootmodule;
+        int start, end, ret;
+
+       g_return_val_if_fail(path != NULL, FALSE);
+       g_return_val_if_fail(submodule != NULL, FALSE);
+
+        exppath = convert_home(path);
+
+       name = module_get_name(exppath, &start, &end);
+       rootmodule = module_get_root(name, prefixes);
+       g_free(name);
+
+        full_path = g_string_new(exppath);
+       if (strcmp(submodule, "core") == 0)
+               g_string_insert(full_path, end, "_core");
+       else {
+               g_string_insert_c(full_path, start, '_');
+               g_string_insert(full_path, start, submodule);
+       }
+
+       ret = module_load_full(full_path->str, rootmodule, submodule,
+                              start, end, NULL);
+
+       g_string_free(full_path, TRUE);
+       g_free(rootmodule);
+       g_free(exppath);
+        return ret;
+}
+
+static void module_file_deinit_gmodule(MODULE_FILE_REC *file)
+{
+       /* call the module's deinit() function */
+        if (file->module_deinit != NULL)
+               file->module_deinit();
+
+       if (file->defined_module_name != NULL) {
+               settings_remove_module(file->defined_module_name);
+               commands_remove_module(file->defined_module_name);
+               signals_remove_module(file->defined_module_name);
+       }
+
+       g_module_close(file->gmodule);
+}
+
+void module_file_unload(MODULE_FILE_REC *file)
+{
+       MODULE_REC *root;
+
+        root = file->root;
+       root->files = g_slist_remove(root->files, file);
+
+        if (file->initialized)
+               signal_emit("module unloaded", 2, file->root, file);
+
+       if (file->gmodule != NULL)
+                module_file_deinit_gmodule(file);
+
+       g_free(file->name);
+       g_free(file->defined_module_name);
+       g_free(file);
+
+       if (root->files == NULL && g_slist_find(modules, root) != NULL)
+                module_unload(root);
+}
+
+void module_unload(MODULE_REC *module)
+{
+       g_return_if_fail(module != NULL);
+
+       modules = g_slist_remove(modules, module);
+
+       signal_emit("module unloaded", 1, module);
+
+       while (module->files != NULL)
+                module_file_unload(module->files->data);
+
+        g_free(module->name);
+       g_free(module);
+}
+
+#else /* !HAVE_GMODULE - modules are not supported */
+
+int module_load(const char *path, char **prefixes)
+{
+        return FALSE;
+}
+
+void module_unload(MODULE_REC *module)
+{
+}
+
+#endif
diff --git a/apps/irssi/src/core/modules-load.h b/apps/irssi/src/core/modules-load.h
new file mode 100644 (file)
index 0000000..42e1438
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __MODULES_LOAD_H
+#define __MODULES_LOAD_H
+
+#include "modules.h"
+
+/* Load module - automatically tries to load also the related non-core
+   modules given in `prefixes' (like irc, fe, fe_text, ..) */
+int module_load(const char *path, char **prefixes);
+
+/* Load a sub module. */
+int module_load_sub(const char *path, const char *submodule, char **prefixes);
+
+void module_unload(MODULE_REC *module);
+void module_file_unload(MODULE_FILE_REC *file);
+
+#endif
diff --git a/apps/irssi/src/core/modules.c b/apps/irssi/src/core/modules.c
new file mode 100644 (file)
index 0000000..dc37318
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ modules.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+
+GSList *modules;
+
+static GHashTable *uniqids, *uniqstrids;
+static GHashTable *idlookup, *stridlookup;
+static int next_uniq_id;
+
+void *module_check_cast(void *object, int type_pos, const char *id)
+{
+       return object == NULL || module_find_id(id,
+               G_STRUCT_MEMBER(int, object, type_pos)) == -1 ? NULL : object;
+}
+
+void *module_check_cast_module(void *object, int type_pos,
+                              const char *module, const char *id)
+{
+       const char *str;
+
+       if (object == NULL)
+               return NULL;
+
+       str = module_find_id_str(module,
+                                G_STRUCT_MEMBER(int, object, type_pos));
+       return str == NULL || strcmp(str, id) != 0 ? NULL : object;
+}
+
+/* return unique number across all modules for `id' */
+int module_get_uniq_id(const char *module, int id)
+{
+        GHashTable *ids;
+       gpointer origkey, uniqid, idp;
+       int ret;
+
+       g_return_val_if_fail(module != NULL, -1);
+
+       ids = g_hash_table_lookup(idlookup, module);
+       if (ids == NULL) {
+               /* new module */
+               ids = g_hash_table_new((GHashFunc) g_direct_hash,
+                                      (GCompareFunc) g_direct_equal);
+               g_hash_table_insert(idlookup, g_strdup(module), ids);
+       }
+
+       idp = GINT_TO_POINTER(id);
+       if (!g_hash_table_lookup_extended(ids, idp, &origkey, &uniqid)) {
+               /* not found */
+               ret = next_uniq_id++;
+                g_hash_table_insert(ids, idp, GINT_TO_POINTER(ret));
+                g_hash_table_insert(uniqids, GINT_TO_POINTER(ret), idp);
+       } else {
+                ret = GPOINTER_TO_INT(uniqid);
+       }
+
+       return ret;
+}
+
+/* return unique number across all modules for `id' */
+int module_get_uniq_id_str(const char *module, const char *id)
+{
+        GHashTable *ids;
+       gpointer origkey, uniqid;
+       int ret;
+
+       g_return_val_if_fail(module != NULL, -1);
+
+       ids = g_hash_table_lookup(stridlookup, module);
+       if (ids == NULL) {
+               /* new module */
+               ids = g_hash_table_new((GHashFunc) g_str_hash,
+                                      (GCompareFunc) g_str_equal);
+               g_hash_table_insert(stridlookup, g_strdup(module), ids);
+       }
+
+       if (!g_hash_table_lookup_extended(ids, id, &origkey, &uniqid)) {
+               /* not found */
+               char *saveid;
+
+               saveid = g_strdup(id);
+               ret = next_uniq_id++;
+                g_hash_table_insert(ids, saveid, GINT_TO_POINTER(ret));
+                g_hash_table_insert(uniqstrids, GINT_TO_POINTER(ret), saveid);
+       } else {
+                ret = GPOINTER_TO_INT(uniqid);
+       }
+
+       return ret;
+}
+
+/* returns the original module specific id, -1 = not found */
+int module_find_id(const char *module, int uniqid)
+{
+       GHashTable *idlist;
+       gpointer origkey, id;
+       int ret;
+
+       g_return_val_if_fail(module != NULL, -1);
+
+       if (!g_hash_table_lookup_extended(uniqids, GINT_TO_POINTER(uniqid),
+                                         &origkey, &id))
+               return -1;
+
+       /* check that module matches */
+       idlist = g_hash_table_lookup(idlookup, module);
+       if (idlist == NULL)
+               return -1;
+
+       ret = GPOINTER_TO_INT(id);
+       if (!g_hash_table_lookup_extended(idlist, id, &origkey, &id) ||
+           GPOINTER_TO_INT(id) != uniqid)
+               ret = -1;
+
+       return ret;
+}
+
+/* returns the original module specific id, NULL = not found */
+const char *module_find_id_str(const char *module, int uniqid)
+{
+       GHashTable *idlist;
+       gpointer origkey, id;
+       const char *ret;
+
+       g_return_val_if_fail(module != NULL, NULL);
+
+       if (!g_hash_table_lookup_extended(uniqstrids, GINT_TO_POINTER(uniqid),
+                                         &origkey, &id))
+               return NULL;
+
+       /* check that module matches */
+       idlist = g_hash_table_lookup(stridlookup, module);
+       if (idlist == NULL)
+               return NULL;
+
+       ret = id;
+       if (!g_hash_table_lookup_extended(idlist, id, &origkey, &id) ||
+           GPOINTER_TO_INT(id) != uniqid)
+               ret = NULL;
+
+       return ret;
+}
+
+static void uniq_destroy(gpointer key, gpointer value)
+{
+        g_hash_table_remove(uniqids, value);
+}
+
+static void uniq_destroy_str(gpointer key, gpointer value)
+{
+        g_hash_table_remove(uniqstrids, value);
+        g_free(key);
+}
+
+/* Destroy unique IDs from `module'. This function is automatically called
+   when module is destroyed with module's name as the parameter. */
+void module_uniq_destroy(const char *module)
+{
+       GHashTable *idlist;
+       gpointer key;
+
+       if (g_hash_table_lookup_extended(idlookup, module, &key,
+                                        (gpointer *) &idlist)) {
+               g_hash_table_remove(idlookup, key);
+               g_free(key);
+
+               g_hash_table_foreach(idlist, (GHFunc) uniq_destroy, NULL);
+               g_hash_table_destroy(idlist);
+       }
+
+       if (g_hash_table_lookup_extended(stridlookup, module, &key,
+                                        (gpointer *) &idlist)) {
+               g_hash_table_remove(stridlookup, key);
+               g_free(key);
+
+               g_hash_table_foreach(idlist, (GHFunc) uniq_destroy_str, NULL);
+               g_hash_table_destroy(idlist);
+       }
+}
+
+/* Register a new module. The `name' is the root module name, `submodule'
+   specifies the current module to be registered (eg. "perl", "fe").
+   The module is registered as statically loaded by default. */
+MODULE_FILE_REC *module_register_full(const char *name, const char *submodule,
+                                     const char *defined_module_name)
+{
+       MODULE_REC *module;
+        MODULE_FILE_REC *file;
+
+       module = module_find(name);
+       if (module == NULL) {
+               module = g_new0(MODULE_REC, 1);
+               module->name = g_strdup(name);
+
+                modules = g_slist_append(modules, module);
+       }
+
+       file = module_file_find(module, submodule);
+       if (file != NULL)
+               return file;
+
+       file = g_new0(MODULE_FILE_REC, 1);
+       file->root = module;
+       file->name = g_strdup(submodule);
+        file->defined_module_name = g_strdup(defined_module_name);
+
+       module->files = g_slist_append(module->files, file);
+        return file;
+}
+
+MODULE_REC *module_find(const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+               MODULE_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, name) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = module->files; tmp != NULL; tmp = tmp->next) {
+               MODULE_FILE_REC *rec = tmp->data;
+
+               if (strcmp(rec->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+static void uniq_get_modules(char *key, void *value, GSList **list)
+{
+        *list = g_slist_append(*list, g_strdup(key));
+}
+
+void modules_init(void)
+{
+       modules = NULL;
+
+       idlookup = g_hash_table_new((GHashFunc) g_str_hash,
+                                   (GCompareFunc) g_str_equal);
+       uniqids = g_hash_table_new((GHashFunc) g_direct_hash,
+                                  (GCompareFunc) g_direct_equal);
+
+       stridlookup = g_hash_table_new((GHashFunc) g_str_hash,
+                                      (GCompareFunc) g_str_equal);
+       uniqstrids = g_hash_table_new((GHashFunc) g_direct_hash,
+                                     (GCompareFunc) g_direct_equal);
+       next_uniq_id = 0;
+}
+
+void modules_deinit(void)
+{
+       GSList *list;
+
+       list = NULL;
+       g_hash_table_foreach(idlookup, (GHFunc) uniq_get_modules, &list);
+       g_hash_table_foreach(stridlookup, (GHFunc) uniq_get_modules, &list);
+
+       while (list != NULL) {
+               module_uniq_destroy(list->data);
+               g_free(list->data);
+               list = g_slist_remove(list, list->data);
+       }
+
+       g_hash_table_destroy(idlookup);
+       g_hash_table_destroy(stridlookup);
+       g_hash_table_destroy(uniqids);
+       g_hash_table_destroy(uniqstrids);
+}
diff --git a/apps/irssi/src/core/modules.h b/apps/irssi/src/core/modules.h
new file mode 100644 (file)
index 0000000..75a77c7
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef __MODULES_H
+#define __MODULES_H
+
+#define MODULE_DATA_INIT(rec) \
+        (rec)->module_data = g_hash_table_new(g_str_hash, g_str_equal)
+
+#define MODULE_DATA_DEINIT(rec) \
+        g_hash_table_destroy((rec)->module_data)
+
+#define MODULE_DATA_SET(rec, data) \
+       g_hash_table_insert((rec)->module_data, MODULE_NAME, data)
+
+#define MODULE_DATA_UNSET(rec) \
+       g_hash_table_remove((rec)->module_data, MODULE_NAME)
+
+#define MODULE_DATA(rec) \
+       g_hash_table_lookup((rec)->module_data, MODULE_NAME)
+
+
+#ifdef HAVE_GMODULE
+#  define MODULE_IS_STATIC(rec) \
+        ((rec)->gmodule == NULL)
+#else
+#  define MODULE_IS_STATIC(rec) TRUE
+#endif
+
+enum {
+       MODULE_ERROR_ALREADY_LOADED,
+       MODULE_ERROR_LOAD,
+       MODULE_ERROR_INVALID
+};
+
+typedef struct _MODULE_REC MODULE_REC;
+
+typedef struct {
+       MODULE_REC *root;
+       char *name;
+        char *defined_module_name;
+       void (*module_deinit) (void);
+
+#ifdef HAVE_GMODULE
+       GModule *gmodule; /* static, if NULL */
+#endif
+       unsigned int initialized:1;
+} MODULE_FILE_REC;
+
+struct _MODULE_REC {
+       char *name;
+        GSList *files; /* list of modules that belong to this root module */
+};
+
+extern GSList *modules;
+
+/* Register a new module. The `name' is the root module name, `submodule'
+   specifies the current module to be registered (eg. "perl", "fe").
+   The module is registered as statically loaded by default. */
+MODULE_FILE_REC *module_register_full(const char *name, const char *submodule,
+                                     const char *defined_module_name);
+#define module_register(name, submodule) \
+        module_register_full(name, submodule, MODULE_NAME)
+
+MODULE_REC *module_find(const char *name);
+MODULE_FILE_REC *module_file_find(MODULE_REC *module, const char *name);
+
+#define MODULE_CHECK_CAST(object, cast, type_field, id) \
+       ((cast *) module_check_cast(object, offsetof(cast, type_field), id))
+#define MODULE_CHECK_CAST_MODULE(object, cast, type_field, module, id) \
+       ((cast *) module_check_cast_module(object, \
+                               offsetof(cast, type_field), module, id))
+void *module_check_cast(void *object, int type_pos, const char *id);
+void *module_check_cast_module(void *object, int type_pos,
+                              const char *module, const char *id);
+
+/* return unique number across all modules for `id' */
+int module_get_uniq_id(const char *module, int id);
+/* return unique number across all modules for `id'. */
+int module_get_uniq_id_str(const char *module, const char *id);
+
+/* returns the original module specific id, -1 = not found */
+int module_find_id(const char *module, int uniqid);
+/* returns the original module specific id, NULL = not found */
+const char *module_find_id_str(const char *module, int uniqid);
+
+/* Destroy unique IDs from `module'. This function is automatically called
+   when module is destroyed with module's name as the parameter. */
+void module_uniq_destroy(const char *module);
+
+void modules_init(void);
+void modules_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/net-disconnect.c b/apps/irssi/src/core/net-disconnect.c
new file mode 100644 (file)
index 0000000..f232f1f
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ net-disconnect.c : 
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "network.h"
+
+/* when quitting, wait for max. 5 seconds before forcing to close the socket */
+#define MAX_QUIT_CLOSE_WAIT 5
+
+/* wait for max. 2 minutes for other side to close the socket */
+#define MAX_CLOSE_WAIT (60*2)
+
+typedef struct {
+       time_t created;
+       GIOChannel *handle;
+       int tag;
+} NET_DISCONNECT_REC;
+
+static GSList *disconnects;
+
+static int timeout_tag;
+
+static void net_disconnect_remove(NET_DISCONNECT_REC *rec)
+{
+       disconnects = g_slist_remove(disconnects, rec);
+
+       g_source_remove(rec->tag);
+        net_disconnect(rec->handle);
+       g_free(rec);
+}
+
+static void sig_disconnect(NET_DISCONNECT_REC *rec)
+{
+       char buf[512];
+       int count, ret;
+
+       /* check if there's any data waiting in socket. read max. 5kB so
+          if server just keeps sending us stuff we won't get stuck */
+       count = 0;
+       do {
+               ret = net_receive(rec->handle, buf, sizeof(buf));
+               if (ret == -1) {
+                       /* socket was closed */
+                       net_disconnect_remove(rec);
+               }
+                count++;
+       } while (ret == sizeof(buf) && count < 10);
+}
+
+static int sig_timeout_disconnect(void)
+{
+       NET_DISCONNECT_REC *rec;
+       GSList *tmp, *next;
+       time_t now;
+
+       /* check if we've waited enough for sockets to close themselves */
+       now = time(NULL);
+       for (tmp = disconnects; tmp != NULL; tmp = next) {
+               rec = tmp->data;
+               next = tmp->next;
+
+               if (rec->created+MAX_CLOSE_WAIT <= now)
+                       net_disconnect_remove(rec);
+       }
+
+       if (disconnects == NULL) {
+               /* no more sockets in disconnect queue, stop calling this
+                  function */
+               timeout_tag = -1;
+       }
+       return disconnects != NULL;
+}
+
+/* Try to let the other side close the connection, if it still isn't
+   disconnected after certain amount of time, close it ourself */
+void net_disconnect_later(GIOChannel *handle)
+{
+       NET_DISCONNECT_REC *rec;
+
+       rec = g_new(NET_DISCONNECT_REC, 1);
+       rec->created = time(NULL);
+       rec->handle = handle;
+       rec->tag = g_input_add(handle, G_INPUT_READ,
+                              (GInputFunction) sig_disconnect, rec);
+
+       if (timeout_tag == -1) {
+               timeout_tag = g_timeout_add(10000, (GSourceFunc)
+                                           sig_timeout_disconnect, NULL);
+       }
+
+       disconnects = g_slist_append(disconnects, rec);
+}
+
+void net_disconnect_init(void)
+{
+       disconnects = NULL;
+       timeout_tag = -1;
+}
+
+void net_disconnect_deinit(void)
+{
+#ifndef WIN32
+       NET_DISCONNECT_REC *rec;
+       time_t now, max;
+       int first, fd;
+       struct timeval tv;
+       fd_set set;
+
+       /* give the sockets a chance to disconnect themselves.. */
+       max = time(NULL)+MAX_QUIT_CLOSE_WAIT;
+       first = 1;
+       while (disconnects != NULL) {
+               rec = disconnects->data;
+
+               now = time(NULL);
+               if (rec->created+MAX_QUIT_CLOSE_WAIT <= now || max <= now) {
+                       /* this one has waited enough */
+                       net_disconnect_remove(rec);
+                       continue;
+               }
+
+                fd = g_io_channel_unix_get_fd(rec->handle);
+               FD_ZERO(&set);
+               FD_SET(fd, &set);
+               tv.tv_sec = first ? 0 : max-now;
+               tv.tv_usec = first ? 100000 : 0;
+               if (select(fd+1, &set, NULL, NULL, &tv) > 0 &&
+                   FD_ISSET(fd, &set)) {
+                       /* data coming .. check if we can close the handle */
+                       sig_disconnect(rec);
+               } else if (first) {
+                       /* Display the text when we have already waited
+                          for a while */
+                       printf("Please wait, waiting for servers to close "
+                              "connections..\n");
+                       fflush(stdout);
+
+                       first = 0;
+               }
+       }
+#endif
+}
diff --git a/apps/irssi/src/core/net-disconnect.h b/apps/irssi/src/core/net-disconnect.h
new file mode 100644 (file)
index 0000000..a1ca064
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __NET_DISCONNECT_H
+#define __NET_DISCONNECT_H
+
+void net_disconnect_init(void);
+void net_disconnect_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/net-nonblock.c b/apps/irssi/src/core/net-nonblock.c
new file mode 100644 (file)
index 0000000..7392f42
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ net-nonblock.c : Nonblocking net_connect()
+
+    Copyright (C) 1998-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+
+#include <signal.h>
+
+#include "pidwait.h"
+#include "net-nonblock.h"
+
+typedef struct {
+       NET_CALLBACK func;
+       void *data;
+
+       GIOChannel *pipes[2];
+       int port;
+       IPADDR *my_ip;
+       int tag;
+} SIMPLE_THREAD_REC;
+
+#define is_fatal_error(err) \
+       (err != 0 && err != G_IO_ERROR_AGAIN && errno != EINTR)
+
+static int g_io_channel_write_block(GIOChannel *channel, void *data, int len)
+{
+        unsigned int ret;
+       int err, sent;
+
+       sent = 0;
+       do {
+               err = g_io_channel_write(channel, (char *) data + sent,
+                                        len-sent, &ret);
+                sent += ret;
+       } while (sent < len && !is_fatal_error(err));
+
+       return err != 0 ? -1 : 0;
+}
+
+static int g_io_channel_read_block(GIOChannel *channel, void *data, int len)
+{
+       time_t maxwait;
+        unsigned int ret;
+       int err, received;
+
+       maxwait = time(NULL)+2;
+       received = 0;
+       do {
+               err = g_io_channel_read(channel, (char *) data + received,
+                                       len-received, &ret);
+               received += ret;
+       } while (received < len && time(NULL) < maxwait &&
+                (ret != 0 || !is_fatal_error(err)));
+
+       return received < len ? -1 : 0;
+}
+
+/* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is
+   written to pipe when found PID of the resolver child is returned */
+int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe)
+{
+       RESOLVED_IP_REC rec;
+       const char *errorstr;
+#ifndef WIN32
+       int pid;
+#endif
+
+       g_return_val_if_fail(addr != NULL, FALSE);
+
+#ifndef WIN32
+       pid = fork();
+       if (pid > 0) {
+               /* parent */
+               pidwait_add(pid);
+               return pid;
+       }
+
+       if (pid != 0) {
+               /* failed! */
+               g_warning("net_connect_thread(): fork() failed! "
+                         "Using blocking resolving");
+       }
+#endif
+
+       /* child */
+        memset(&rec, 0, sizeof(rec));
+       rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6);
+       if (rec.error == 0) {
+               errorstr = NULL;
+       } else {
+               errorstr = net_gethosterror(rec.error);
+               rec.errlen = errorstr == NULL ? 0 : strlen(errorstr)+1;
+       }
+
+        g_io_channel_write_block(pipe, &rec, sizeof(rec));
+       if (rec.errlen != 0)
+               g_io_channel_write_block(pipe, (void *) errorstr, rec.errlen);
+
+#ifndef WIN32
+       if (pid == 0)
+               _exit(99);
+#endif
+
+       /* we used blocking lookup */
+       return 0;
+}
+
+/* get the resolved IP address */
+int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec)
+{
+       rec->error = -1;
+       rec->errorstr = NULL;
+
+#ifndef WIN32
+       fcntl(g_io_channel_unix_get_fd(pipe), F_SETFL, O_NONBLOCK);
+#endif
+
+       /* get ip+error */
+       if (g_io_channel_read_block(pipe, rec, sizeof(*rec)) == -1) {
+               rec->errorstr = g_strdup_printf("Host name lookup: %s",
+                                               g_strerror(errno));
+               return -1;
+       }
+
+       if (rec->error) {
+               /* read error string, if we can't read everything for some
+                  reason, just ignore it. */
+               rec->errorstr = g_malloc0(rec->errlen+1);
+                g_io_channel_read_block(pipe, rec->errorstr, rec->errlen);
+       }
+
+       return 0;
+}
+
+/* Get host name, call func when finished */
+int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data)
+{
+       /* FIXME: not implemented */
+       return FALSE;
+}
+
+/* Kill the resolver child */
+void net_disconnect_nonblock(int pid)
+{
+       g_return_if_fail(pid > 0);
+
+#ifndef WIN32
+       kill(pid, SIGKILL);
+#endif
+}
+
+static void simple_init(SIMPLE_THREAD_REC *rec, GIOChannel *handle)
+{
+       g_return_if_fail(rec != NULL);
+
+       g_source_remove(rec->tag);
+
+       if (net_geterror(handle) != 0) {
+               /* failed */
+               g_io_channel_close(handle);
+                g_io_channel_unref(handle);
+               handle = NULL;
+       }
+
+       rec->func(handle, rec->data);
+       g_free(rec);
+}
+
+static void simple_readpipe(SIMPLE_THREAD_REC *rec, GIOChannel *pipe)
+{
+       RESOLVED_IP_REC iprec;
+       GIOChannel *handle;
+        IPADDR *ip;
+
+       g_return_if_fail(rec != NULL);
+
+       g_source_remove(rec->tag);
+
+       net_gethostbyname_return(pipe, &iprec);
+       g_free_not_null(iprec.errorstr);
+
+       g_io_channel_close(rec->pipes[0]);
+       g_io_channel_unref(rec->pipes[0]);
+       g_io_channel_close(rec->pipes[1]);
+       g_io_channel_unref(rec->pipes[1]);
+
+       ip = iprec.ip4.family != 0 ? &iprec.ip4 : &iprec.ip6;
+       handle = iprec.error == -1 ? NULL :
+               net_connect_ip(ip, rec->port, rec->my_ip);
+
+       g_free_not_null(rec->my_ip);
+
+       if (handle == NULL) {
+               /* failed */
+               rec->func(NULL, rec->data);
+               g_free(rec);
+               return;
+       }
+
+       rec->tag = g_input_add(handle, G_INPUT_READ | G_INPUT_WRITE,
+                              (GInputFunction) simple_init, rec);
+}
+
+/* Connect to server, call func when finished */
+int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip,
+                        NET_CALLBACK func, void *data)
+{
+       SIMPLE_THREAD_REC *rec;
+       int fd[2];
+
+       g_return_val_if_fail(server != NULL, FALSE);
+       g_return_val_if_fail(func != NULL, FALSE);
+
+       if (pipe(fd) != 0) {
+               g_warning("net_connect_nonblock(): pipe() failed.");
+               return FALSE;
+       }
+
+       rec = g_new0(SIMPLE_THREAD_REC, 1);
+       rec->port = port;
+       if (my_ip != NULL) {
+               rec->my_ip = g_malloc(sizeof(IPADDR));
+               memcpy(rec->my_ip, my_ip, sizeof(IPADDR));
+       }
+       rec->func = func;
+       rec->data = data;
+       rec->pipes[0] = g_io_channel_unix_new(fd[0]);
+       rec->pipes[1] = g_io_channel_unix_new(fd[1]);
+
+       /* start nonblocking host name lookup */
+       net_gethostbyname_nonblock(server, rec->pipes[1]);
+       rec->tag = g_input_add(rec->pipes[0], G_INPUT_READ,
+                              (GInputFunction) simple_readpipe, rec);
+
+       return TRUE;
+}
diff --git a/apps/irssi/src/core/net-nonblock.h b/apps/irssi/src/core/net-nonblock.h
new file mode 100644 (file)
index 0000000..a0e5cdd
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef __NET_NONBLOCK_H
+#define __NET_NONBLOCK_H
+
+#include "network.h"
+
+typedef struct {
+       IPADDR ip4, ip6; /* resolved ip addresses */
+       int error; /* error, 0 = no error, -1 = error: */
+       int errlen; /* error text length */
+       char *errorstr; /* error string - dynamically allocated, you'll
+                          need to free() it yourself unless it's NULL */
+} RESOLVED_IP_REC;
+
+typedef struct {
+        int namelen;
+       char *name;
+
+       int error;
+       int errlen;
+       char *errorstr;
+} RESOLVED_NAME_REC;
+
+typedef void (*NET_CALLBACK) (GIOChannel *, void *);
+typedef void (*NET_HOST_CALLBACK) (RESOLVED_NAME_REC *, void *);
+
+/* nonblocking gethostbyname(), PID of the resolver child is returned. */
+int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe);
+/* Get host's name, call func when finished */
+int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data);
+/* get the resolved IP address. returns -1 if some error occured with read() */
+int net_gethostbyname_return(GIOChannel *pipe, RESOLVED_IP_REC *rec);
+
+/* Connect to server, call func when finished */
+int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip,
+                        NET_CALLBACK func, void *data);
+/* Kill the resolver child */
+void net_disconnect_nonblock(int pid);
+
+#endif
diff --git a/apps/irssi/src/core/net-sendbuffer.c b/apps/irssi/src/core/net-sendbuffer.c
new file mode 100644 (file)
index 0000000..fd1039d
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ net-sendbuffer.c : Buffered send()
+
+    Copyright (C) 1998-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+
+#include "network.h"
+#include "net-sendbuffer.h"
+
+struct _NET_SENDBUF_REC {
+       GIOChannel *handle;
+
+        int send_tag;
+       int bufsize;
+       int bufpos;
+       char *buffer; /* Buffer is NULL until it's actually needed. */
+};
+
+static GSList *buffers;
+
+/* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE
+   is used */
+NET_SENDBUF_REC *net_sendbuffer_create(GIOChannel *handle, int bufsize)
+{
+       NET_SENDBUF_REC *rec;
+
+       g_return_val_if_fail(handle != NULL, NULL);
+
+       rec = g_new0(NET_SENDBUF_REC, 1);
+        rec->send_tag = -1;
+       rec->handle = handle;
+       rec->bufsize = bufsize > 0 ? bufsize : DEFAULT_BUFFER_SIZE;
+
+       buffers = g_slist_append(buffers, rec);
+       return rec;
+}
+
+/* Destroy the buffer. `close' specifies if socket handle should be closed. */
+void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close)
+{
+       buffers = g_slist_remove(buffers, rec);
+
+        if (rec->send_tag != -1) g_source_remove(rec->send_tag);
+       if (close) net_disconnect(rec->handle);
+       g_free_not_null(rec->buffer);
+       g_free(rec);
+}
+
+/* Transmit all data from buffer - return TRUE if the whole buffer was sent */
+static int buffer_send(NET_SENDBUF_REC *rec)
+{
+       int ret;
+
+       ret = net_transmit(rec->handle, rec->buffer, rec->bufpos);
+       if (ret < 0 || rec->bufpos == ret) {
+               /* error/all sent - don't try to send it anymore */
+                g_free_and_null(rec->buffer);
+               return TRUE;
+       }
+
+       if (ret > 0) {
+                rec->bufpos -= ret;
+               g_memmove(rec->buffer, rec->buffer+ret, rec->bufpos);
+       }
+       return FALSE;
+}
+
+static void sig_sendbuffer(NET_SENDBUF_REC *rec)
+{
+       if (rec->buffer != NULL) {
+               if (!buffer_send(rec))
+                        return;
+       }
+
+       g_source_remove(rec->send_tag);
+       rec->send_tag = -1;
+}
+
+/* Add `data' to transmit buffer - return FALSE if buffer is full */
+static int buffer_add(NET_SENDBUF_REC *rec, const void *data, int size)
+{
+       if (rec->buffer == NULL) {
+               rec->buffer = g_malloc(rec->bufsize);
+               rec->bufpos = 0;
+       }
+
+       if (rec->bufpos+size > rec->bufsize)
+               return FALSE;
+
+       memcpy(rec->buffer+rec->bufpos, data, size);
+       rec->bufpos += size;
+       return TRUE;
+}
+
+/* Send data, if all of it couldn't be sent immediately, it will be resent
+   automatically after a while. Returns -1 if some unrecoverable error
+   occured. */
+int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size)
+{
+       int ret;
+
+       g_return_val_if_fail(rec != NULL, -1);
+       g_return_val_if_fail(data != NULL, -1);
+       if (size <= 0) return 0;
+
+       if (rec->buffer == NULL) {
+                /* nothing in buffer - transmit immediately */
+               ret = net_transmit(rec->handle, data, size);
+               if (ret < 0) return -1;
+               size -= ret;
+               data = ((const char *) data) + ret;
+       }
+
+       if (size <= 0)
+               return 0;
+
+       /* everything couldn't be sent. */
+       if (rec->send_tag == -1) {
+               rec->send_tag =
+                       g_input_add(rec->handle, G_INPUT_WRITE,
+                                   (GInputFunction) sig_sendbuffer, rec);
+       }
+
+       return buffer_add(rec, data, size) ? 0 : -1;
+}
+
+/* Flush the buffer, blocks until finished. */
+void net_sendbuffer_flush(NET_SENDBUF_REC *rec)
+{
+       int handle;
+
+       if (rec->buffer == NULL)
+               return;
+
+        /* set the socket blocking while doing this */
+       handle = g_io_channel_unix_get_fd(rec->handle);
+#ifndef WIN32
+       fcntl(handle, F_SETFL, 0);
+#endif
+       while (!buffer_send(rec)) ;
+#ifndef WIN32
+       fcntl(handle, F_SETFL, O_NONBLOCK);
+#endif
+}
+
+/* Returns the socket handle */
+GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec)
+{
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       return rec->handle;
+}
+
+void net_sendbuffer_init(void)
+{
+       buffers = NULL;
+}
+
+void net_sendbuffer_deinit(void)
+{
+}
diff --git a/apps/irssi/src/core/net-sendbuffer.h b/apps/irssi/src/core/net-sendbuffer.h
new file mode 100644 (file)
index 0000000..d2388b4
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __NET_SENDBUFFER_H
+#define __NET_SENDBUFFER_H
+
+#define DEFAULT_BUFFER_SIZE 8192
+
+/* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE
+   is used */
+NET_SENDBUF_REC *net_sendbuffer_create(GIOChannel *handle, int bufsize);
+/* Destroy the buffer. `close' specifies if socket handle should be closed. */
+void net_sendbuffer_destroy(NET_SENDBUF_REC *rec, int close);
+
+/* Send data, if all of it couldn't be sent immediately, it will be resent
+   automatically after a while. Returns -1 if some unrecoverable error
+   occured. */
+int net_sendbuffer_send(NET_SENDBUF_REC *rec, const void *data, int size);
+
+/* Flush the buffer, blocks until finished. */
+void net_sendbuffer_flush(NET_SENDBUF_REC *rec);
+
+/* Returns the socket handle */
+GIOChannel *net_sendbuffer_handle(NET_SENDBUF_REC *rec);
+
+void net_sendbuffer_init(void);
+void net_sendbuffer_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/network.c b/apps/irssi/src/core/network.c
new file mode 100644 (file)
index 0000000..6be8711
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ network.c : Network stuff
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "network.h"
+
+#ifndef INADDR_NONE
+#  define INADDR_NONE INADDR_BROADCAST
+#endif
+
+union sockaddr_union {
+       struct sockaddr sa;
+       struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+       struct sockaddr_in6 sin6;
+#endif
+};
+
+#ifdef HAVE_IPV6
+#  define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
+       sizeof(so.sin6) : sizeof(so.sin))
+#else
+#  define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
+#endif
+
+#ifdef WIN32
+#  define g_io_channel_new(handle) g_io_channel_win32_new_stream_socket(handle)
+#else
+#  define g_io_channel_new(handle) g_io_channel_unix_new(handle)
+#endif
+
+/* Cygwin need this, don't know others.. */
+/*#define BLOCKING_SOCKETS 1*/
+
+int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
+{
+       if (ip1->family != ip2->family)
+               return 0;
+
+#ifdef HAVE_IPV6
+       if (ip1->family == AF_INET6)
+               return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
+#endif
+
+       return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
+}
+
+
+/* copy IP to sockaddr */
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
+{
+       if (ip == NULL) {
+#ifdef HAVE_IPV6
+               so->sin6.sin6_family = AF_INET6;
+               so->sin6.sin6_addr = in6addr_any;
+#else
+               so->sin.sin_family = AF_INET;
+               so->sin.sin_addr.s_addr = INADDR_ANY;
+#endif
+               return;
+       }
+
+       so->sin.sin_family = ip->family;
+#ifdef HAVE_IPV6
+       if (ip->family == AF_INET6)
+               memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
+       else
+#endif
+               memcpy(&so->sin.sin_addr, &ip->ip, 4);
+}
+
+void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
+{
+       ip->family = so->sin.sin_family;
+
+#ifdef HAVE_IPV6
+       if (ip->family == AF_INET6)
+               memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
+       else
+#endif
+               memcpy(&ip->ip, &so->sin.sin_addr, 4);
+}
+
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void sin_set_port(union sockaddr_union *so, int port)
+{
+#ifdef HAVE_IPV6
+       if (so->sin.sin_family == AF_INET6)
+                so->sin6.sin6_port = htons(port);
+       else
+#endif
+               so->sin.sin_port = htons((unsigned short)port);
+}
+
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+int sin_get_port(union sockaddr_union *so)
+{
+#ifdef HAVE_IPV6
+       if (so->sin.sin_family == AF_INET6)
+               return ntohs(so->sin6.sin6_port);
+#endif
+       return ntohs(so->sin.sin_port);
+}
+
+/* Connect to socket */
+GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip)
+{
+       IPADDR ip4, ip6, *ip;
+        int family;
+
+       g_return_val_if_fail(addr != NULL, NULL);
+
+        family = my_ip == NULL ? 0 : my_ip->family;
+       if (net_gethostbyname(addr, &ip4, &ip6) == -1)
+               return NULL;
+
+       if (my_ip == NULL) {
+                /* prefer IPv4 addresses */
+               ip = ip4.family != 0 ? &ip4 : &ip6;
+       } else if (IPADDR_IS_V6(my_ip)) {
+                /* my_ip is IPv6 address, use it if possible */
+               if (ip6.family != 0)
+                       ip = &ip6;
+               else {
+                       my_ip = NULL;
+                        ip = &ip4;
+               }
+       } else {
+                /* my_ip is IPv4 address, use it if possible */
+               if (ip4.family != 0)
+                       ip = &ip4;
+               else {
+                       my_ip = NULL;
+                        ip = &ip6;
+               }
+       }
+
+       return net_connect_ip(ip, port, my_ip);
+}
+
+/* Connect to socket with ip address */
+GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
+{
+       union sockaddr_union so;
+       int handle, ret, opt = 1;
+
+       if (my_ip != NULL && ip->family != my_ip->family) {
+               g_warning("net_connect_ip(): ip->family != my_ip->family");
+                my_ip = NULL;
+       }
+
+       /* create the socket */
+       memset(&so, 0, sizeof(so));
+        so.sin.sin_family = ip->family;
+       handle = socket(ip->family, SOCK_STREAM, 0);
+
+       if (handle == -1)
+               return NULL;
+
+       /* set socket options */
+#ifndef WIN32
+       fcntl(handle, F_SETFL, O_NONBLOCK);
+#endif
+       setsockopt(handle, SOL_SOCKET, SO_REUSEADDR,
+                  (char *) &opt, sizeof(opt));
+       setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE,
+                  (char *) &opt, sizeof(opt));
+
+       /* set our own address */
+       if (my_ip != NULL) {
+               sin_set_ip(&so, my_ip);
+               if (bind(handle, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
+                       /* failed, set it back to INADDR_ANY */
+                       sin_set_ip(&so, NULL);
+                       bind(handle, &so.sa, SIZEOF_SOCKADDR(so));
+               }
+       }
+
+       /* connect */
+       sin_set_ip(&so, ip);
+       sin_set_port(&so, port);
+       ret = connect(handle, &so.sa, SIZEOF_SOCKADDR(so));
+
+#ifndef WIN32
+       if (ret < 0 && errno != EINPROGRESS)
+#else
+       if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
+#endif
+       {
+               close(handle);
+               return NULL;
+       }
+
+       return g_io_channel_new(handle);
+}
+
+/* Disconnect socket */
+void net_disconnect(GIOChannel *handle)
+{
+       g_return_if_fail(handle != NULL);
+
+       g_io_channel_close(handle);
+       g_io_channel_unref(handle);
+}
+
+/* Listen for connections on a socket. if `my_ip' is NULL, listen in any
+   address. */
+GIOChannel *net_listen(IPADDR *my_ip, int *port)
+{
+       union sockaddr_union so;
+       int ret, handle, opt = 1;
+       socklen_t len;
+
+       g_return_val_if_fail(port != NULL, NULL);
+
+       memset(&so, 0, sizeof(so));
+       sin_set_port(&so, *port);
+       sin_set_ip(&so, my_ip);
+
+       /* create the socket */
+       handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
+#ifdef HAVE_IPV6
+       if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
+               /* IPv6 is not supported by OS */
+               so.sin.sin_family = AF_INET;
+               so.sin.sin_addr.s_addr = INADDR_ANY;
+
+               handle = socket(AF_INET, SOCK_STREAM, 0);
+       }
+#endif
+       if (handle == -1)
+               return NULL;
+
+       /* set socket options */
+#ifndef WIN32
+       fcntl(handle, F_SETFL, O_NONBLOCK);
+#endif
+       setsockopt(handle, SOL_SOCKET, SO_REUSEADDR,
+                  (char *) &opt, sizeof(opt));
+       setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE,
+                  (char *) &opt, sizeof(opt));
+
+       /* specify the address/port we want to listen in */
+       ret = bind(handle, &so.sa, SIZEOF_SOCKADDR(so));
+       if (ret >= 0) {
+               /* get the actual port we started listen */
+               len = SIZEOF_SOCKADDR(so);
+               ret = getsockname(handle, &so.sa, &len);
+               if (ret >= 0) {
+                       *port = sin_get_port(&so);
+
+                       /* start listening */
+                       if (listen(handle, 1) >= 0)
+                                return g_io_channel_new(handle);
+               }
+
+       }
+
+        /* error */
+       close(handle);
+       return NULL;
+}
+
+/* Accept a connection on a socket */
+GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port)
+{
+       union sockaddr_union so;
+       int ret;
+       socklen_t addrlen;
+
+       g_return_val_if_fail(handle != NULL, NULL);
+
+       addrlen = sizeof(so);
+       ret = accept(g_io_channel_unix_get_fd(handle), &so.sa, &addrlen);
+
+       if (ret < 0)
+               return NULL;
+
+       if (addr != NULL) sin_get_ip(&so, addr);
+       if (port != NULL) *port = sin_get_port(&so);
+
+#ifndef WIN32
+       fcntl(ret, F_SETFL, O_NONBLOCK);
+#endif
+       return g_io_channel_new(ret);
+}
+
+/* Read data from socket, return number of bytes read, -1 = error */
+int net_receive(GIOChannel *handle, char *buf, int len)
+{
+        unsigned int ret;
+       int err;
+
+       g_return_val_if_fail(handle != NULL, -1);
+       g_return_val_if_fail(buf != NULL, -1);
+
+       err = g_io_channel_read(handle, buf, len, &ret);
+       if (err == 0 && ret == 0)
+               return -1; /* disconnected */
+
+       if (err == G_IO_ERROR_AGAIN || (err != 0 && errno == EINTR))
+               return 0; /* no bytes received */
+
+       return err == 0 ? (int)ret : -1;
+}
+
+/* Transmit data, return number of bytes sent, -1 = error */
+int net_transmit(GIOChannel *handle, const char *data, int len)
+{
+        unsigned int ret;
+       int err;
+
+       g_return_val_if_fail(handle != NULL, -1);
+       g_return_val_if_fail(data != NULL, -1);
+
+       err = g_io_channel_write(handle, (char *) data, len, &ret);
+       if (err == G_IO_ERROR_AGAIN ||
+           (err != 0 && (errno == EINTR || errno == EPIPE)))
+               return 0;
+
+       return err == 0 ? (int)ret : -1;
+}
+
+/* Get socket address/port */
+int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
+{
+       union sockaddr_union so;
+       socklen_t addrlen;
+
+       g_return_val_if_fail(handle != NULL, -1);
+       g_return_val_if_fail(addr != NULL, -1);
+
+       addrlen = sizeof(so);
+       if (getsockname(g_io_channel_unix_get_fd(handle),
+                       (struct sockaddr *) &so, &addrlen) == -1)
+               return -1;
+
+        sin_get_ip(&so, addr);
+       if (port) *port = sin_get_port(&so);
+
+       return 0;
+}
+
+/* Get IP addresses for host, both IPv4 and IPv6 if possible.
+   If ip->family is 0, the address wasn't found.
+   Returns 0 = ok, others = error code for net_gethosterror() */
+int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
+{
+#ifdef HAVE_IPV6
+       union sockaddr_union *so;
+       struct addrinfo hints, *ai, *ailist;
+       int ret, count;
+#else
+       struct hostent *hp;
+#endif
+
+       g_return_val_if_fail(addr != NULL, -1);
+
+       memset(ip4, 0, sizeof(IPADDR));
+       memset(ip6, 0, sizeof(IPADDR));
+
+#ifdef HAVE_IPV6
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_socktype = SOCK_STREAM;
+
+       /* save error to host_error for later use */
+       ret = getaddrinfo(addr, NULL, &hints, &ailist);
+       if (ret != 0)
+               return ret;
+
+        count = 0;
+       for (ai = ailist; ai != NULL && count < 2; ai = ai->ai_next) {
+               so = (union sockaddr_union *) ai->ai_addr;
+
+               if (ai->ai_family == AF_INET6 && ip6->family == 0) {
+                       sin_get_ip(so, ip6);
+                        count++;
+               } else if (ai->ai_family == AF_INET && ip4->family == 0) {
+                       sin_get_ip(so, ip4);
+                        count++;
+               }
+       }
+       freeaddrinfo(ailist);
+       return count > 0 ? 0 : 1;
+#else
+       hp = gethostbyname(addr);
+       if (hp == NULL)
+                return -1;
+               //return h_errno;
+
+       ip4->family = AF_INET;
+       memcpy(&ip4->ip, hp->h_addr, 4);
+
+       return 0;
+#endif
+}
+
+/* Get name for host, *name should be g_free()'d unless it's NULL.
+   Return values are the same as with net_gethostbyname() */
+int net_gethostbyaddr(IPADDR *ip, char **name)
+{
+#ifdef HAVE_IPV6
+       struct addrinfo req, *ai;
+       int host_error;
+#else
+       struct hostent *hp;
+#endif
+       char ipname[MAX_IP_LEN];
+
+       g_return_val_if_fail(ip != NULL, -1);
+       g_return_val_if_fail(name != NULL, -1);
+
+       net_ip2host(ip, ipname);
+
+       *name = NULL;
+#ifdef HAVE_IPV6
+       memset(&req, 0, sizeof(struct addrinfo));
+       req.ai_socktype = SOCK_STREAM;
+       req.ai_flags = AI_CANONNAME;
+
+       /* save error to host_error for later use */
+       host_error = getaddrinfo(ipname, NULL, &req, &ai);
+       if (host_error != 0)
+               return host_error;
+       *name = g_strdup(ai->ai_canonname);
+
+       freeaddrinfo(ai);
+#else
+       hp = gethostbyaddr(ipname, strlen(ipname), AF_INET);
+       if (hp == NULL) return -1;
+
+       *name = g_strdup(hp->h_name);
+#endif
+
+       return 0;
+}
+
+int net_ip2host(IPADDR *ip, char *host)
+{
+#ifdef HAVE_IPV6
+       if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
+               return -1;
+#else
+       unsigned long ip4;
+
+       ip4 = ntohl(ip->ip.s_addr);
+       g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
+                  (ip4 & 0xff000000UL) >> 24,
+                  (ip4 & 0x00ff0000) >> 16,
+                  (ip4 & 0x0000ff00) >> 8,
+                  (ip4 & 0x000000ff));
+#endif
+       return 0;
+}
+
+int net_host2ip(const char *host, IPADDR *ip)
+{
+       unsigned long addr;
+
+#ifdef HAVE_IPV6
+       if (strchr(host, ':') != NULL) {
+               /* IPv6 */
+               ip->family = AF_INET6;
+               if (inet_pton(AF_INET6, host, &ip->ip) == 0)
+                       return -1;
+       } else
+#endif
+       {
+               /* IPv4 */
+               ip->family = AF_INET;
+#ifdef HAVE_INET_ATON
+               if (inet_aton(host, &ip->ip.s_addr) == 0)
+                       return -1;
+#else
+               addr = inet_addr(host);
+               if (addr == INADDR_NONE)
+                       return -1;
+
+               memcpy(&ip->ip, &addr, 4);
+#endif
+       }
+
+       return 0;
+}
+
+/* Get socket error */
+int net_geterror(GIOChannel *handle)
+{
+       int data;
+       socklen_t len = sizeof(data);
+
+       if (getsockopt(g_io_channel_unix_get_fd(handle),
+                      SOL_SOCKET, SO_ERROR, (void *) &data, &len) == -1)
+               return -1;
+
+       return data;
+}
+
+/* get error of net_gethostname() */
+const char *net_gethosterror(int error)
+{
+#ifdef HAVE_IPV6
+       g_return_val_if_fail(error != 0, NULL);
+
+       if (error == 1) {
+               /* getnameinfo() failed ..
+                  FIXME: does strerror return the right error message? */
+               return g_strerror(errno);
+       }
+
+       return gai_strerror(error);
+#else
+       switch (error) {
+       case HOST_NOT_FOUND:
+               return "Host not found";
+       case NO_ADDRESS:
+               return "No IP address found for name";
+       case NO_RECOVERY:
+               return "A non-recovable name server error occurred";
+       case TRY_AGAIN:
+               return "A temporary error on an authoritative name server";
+       }
+
+       /* unknown error */
+       return NULL;
+#endif
+}
+
+/* return TRUE if host lookup failed because it didn't exist (ie. not
+   some error with name server) */
+int net_hosterror_notfound(int error)
+{
+#ifdef HAVE_IPV6
+       return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
+#else
+       return error == HOST_NOT_FOUND || error == NO_ADDRESS;
+#endif
+}
+
+/* Get name of TCP service */
+char *net_getservbyport(int port)
+{
+       struct servent *entry;
+
+       entry = getservbyport(htons((unsigned short) port), "tcp");
+       return entry == NULL ? NULL : entry->s_name;
+}
+
+int is_ipv4_address(const char *host)
+{
+       while (*host != '\0') {
+               if (*host != '.' && !i_isdigit(*host))
+                       return 0;
+                host++;
+       }
+
+       return 1;
+}
+
+int is_ipv6_address(const char *host)
+{
+       while (*host != '\0') {
+               if (*host != ':' && !isxdigit(*host))
+                       return 0;
+                host++;
+       }
+
+       return 1;
+}
diff --git a/apps/irssi/src/core/network.h b/apps/irssi/src/core/network.h
new file mode 100644 (file)
index 0000000..4c25740
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef __NETWORK_H
+#define __NETWORK_H
+
+#ifdef HAVE_SOCKS_H
+#include <socks.h>
+#endif
+
+#include <sys/types.h>
+#ifndef WIN32
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <netdb.h>
+#  include <arpa/inet.h>
+#endif
+
+#ifndef AF_INET6
+#  ifdef PF_INET6
+#    define AF_INET6 PF_INET6
+#  else
+#    define AF_INET6 10
+#  endif
+#endif
+
+struct _IPADDR {
+       unsigned short family;
+#ifdef HAVE_IPV6
+       struct in6_addr ip;
+#else
+       struct in_addr ip;
+#endif
+};
+
+/* maxmimum string length of IP address */
+#ifdef HAVE_IPV6
+#  define MAX_IP_LEN INET6_ADDRSTRLEN
+#else
+#  define MAX_IP_LEN 20
+#endif
+
+#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
+
+/* returns 1 if IPADDRs are the same */
+int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
+
+/* Connect to socket */
+GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
+/* Connect to socket with ip address */
+GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
+/* Disconnect socket */
+void net_disconnect(GIOChannel *handle);
+/* Try to let the other side close the connection, if it still isn't
+   disconnected after certain amount of time, close it ourself */
+void net_disconnect_later(GIOChannel *handle);
+
+/* Listen for connections on a socket */
+GIOChannel *net_listen(IPADDR *my_ip, int *port);
+/* Accept a connection on a socket */
+GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port);
+
+/* Read data from socket, return number of bytes read, -1 = error */
+int net_receive(GIOChannel *handle, char *buf, int len);
+/* Transmit data, return number of bytes sent, -1 = error */
+int net_transmit(GIOChannel *handle, const char *data, int len);
+
+/* Get IP addresses for host, both IPv4 and IPv6 if possible.
+   If ip->family is 0, the address wasn't found.
+   Returns 0 = ok, others = error code for net_gethosterror() */
+int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6);
+/* Get name for host, *name should be g_free()'d unless it's NULL.
+   Return values are the same as with net_gethostbyname() */
+int net_gethostbyaddr(IPADDR *ip, char **name);
+/* get error of net_gethostname() */
+const char *net_gethosterror(int error);
+/* return TRUE if host lookup failed because it didn't exist (ie. not
+   some error with name server) */
+int net_hosterror_notfound(int error);
+
+/* Get socket address/port */
+int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port);
+
+/* IPADDR -> char* translation. `host' must be at least MAX_IP_LEN bytes */
+int net_ip2host(IPADDR *ip, char *host);
+/* char* -> IPADDR translation. */
+int net_host2ip(const char *host, IPADDR *ip);
+
+/* Get socket error */
+int net_geterror(GIOChannel *handle);
+
+/* Get name of TCP service */
+char *net_getservbyport(int port);
+
+int is_ipv4_address(const char *host);
+int is_ipv6_address(const char *host);
+
+#endif
diff --git a/apps/irssi/src/core/nick-rec.h b/apps/irssi/src/core/nick-rec.h
new file mode 100644 (file)
index 0000000..d0dbce3
--- /dev/null
@@ -0,0 +1,27 @@
+/* NICK_REC definition, used for inheritance */
+
+int type; /* module_get_uniq_id("NICK", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+time_t last_check; /* last time gone was checked */
+
+char *nick;
+char *host;
+char *realname;
+int hops;
+
+/* status in server */
+unsigned int gone:1;
+unsigned int serverop:1;
+
+/* status in channel */
+unsigned int send_massjoin:1; /* Waiting to be sent in massjoin signal */
+unsigned int op:1;
+unsigned int halfop:1;
+unsigned int voice:1;
+
+/*GHashTable *module_data;*/
+
+void *unique_id; /* unique ID to use for comparing if one nick is in another channels,
+                   or NULL = nicks are unique, just keep comparing them. */
+NICK_REC *next; /* support for multiple identically named nicks */
diff --git a/apps/irssi/src/core/nicklist.c b/apps/irssi/src/core/nicklist.c
new file mode 100644 (file)
index 0000000..b7e8540
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ nicklist.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "servers.h"
+#include "channels.h"
+#include "nicklist.h"
+#include "masks.h"
+
+#define isalnumhigh(a) \
+        (i_isalnum(a) || (unsigned char) (a) >= 128)
+
+static void nick_hash_add(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       NICK_REC *list;
+
+       nick->next = NULL;
+
+       list = g_hash_table_lookup(channel->nicks, nick->nick);
+        if (list == NULL)
+               g_hash_table_insert(channel->nicks, nick->nick, nick);
+       else {
+                /* multiple nicks with same name */
+               while (list->next != NULL)
+                       list = list->next;
+               list->next = nick;
+       }
+
+       if (nick == channel->ownnick) {
+                /* move our own nick to beginning of the nick list.. */
+               nicklist_set_own(channel, nick);
+       }
+}
+
+static void nick_hash_remove(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       NICK_REC *list;
+
+       list = g_hash_table_lookup(channel->nicks, nick->nick);
+       if (list == NULL)
+               return;
+
+       if (list == nick || list->next == NULL) {
+               g_hash_table_remove(channel->nicks, nick->nick);
+               if (list->next != NULL) {
+                       g_hash_table_insert(channel->nicks, nick->next->nick,
+                                           nick->next);
+               }
+       } else {
+               while (list->next != nick)
+                       list = list->next;
+               list->next = nick->next;
+       }
+}
+
+/* Add new nick to list */
+void nicklist_insert(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       /*MODULE_DATA_INIT(nick);*/
+
+       nick->type = module_get_uniq_id("NICK", 0);
+        nick->chat_type = channel->chat_type;
+
+        nick_hash_add(channel, nick);
+       signal_emit("nicklist new", 2, channel, nick);
+}
+
+/* Set host address for nick */
+void nicklist_set_host(CHANNEL_REC *channel, NICK_REC *nick, const char *host)
+{
+        g_return_if_fail(channel != NULL);
+        g_return_if_fail(nick != NULL);
+       g_return_if_fail(host != NULL);
+
+        g_free_not_null(nick->host);
+       nick->host = g_strdup(host);
+
+        signal_emit("nicklist host changed", 2, channel, nick);
+}
+
+static void nicklist_destroy(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       signal_emit("nicklist remove", 2, channel, nick);
+
+        /*MODULE_DATA_DEINIT(nick);*/
+       g_free(nick->nick);
+       g_free_not_null(nick->realname);
+       g_free_not_null(nick->host);
+       g_free(nick);
+}
+
+/* Remove nick from list */
+void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       g_return_if_fail(IS_CHANNEL(channel));
+       g_return_if_fail(nick != NULL);
+
+        nick_hash_remove(channel, nick);
+       nicklist_destroy(channel, nick);
+}
+
+static void nicklist_rename_list(SERVER_REC *server, void *new_nick_id,
+                                const char *old_nick, const char *new_nick,
+                                GSList *nicks)
+{
+       CHANNEL_REC *channel;
+       NICK_REC *nickrec;
+       GSList *tmp;
+
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
+               channel = tmp->data;
+               nickrec = tmp->next->data;
+
+               /* remove old nick from hash table */
+                nick_hash_remove(channel, nickrec);
+
+               if (new_nick_id != NULL)
+                       nickrec->unique_id = new_nick_id;
+
+               g_free(nickrec->nick);
+               nickrec->nick = g_strdup(new_nick);
+
+               /* add new nick to hash table */
+                nick_hash_add(channel, nickrec);
+
+               signal_emit("nicklist changed", 3, channel, nickrec, old_nick);
+       }
+       g_slist_free(nicks);
+}
+
+void nicklist_rename(SERVER_REC *server, const char *old_nick,
+                    const char *new_nick)
+{
+       nicklist_rename_list(server, NULL, old_nick, new_nick,
+                            nicklist_get_same(server, old_nick));
+}
+
+void nicklist_rename_unique(SERVER_REC *server,
+                           void *old_nick_id, const char *old_nick,
+                           void *new_nick_id, const char *new_nick)
+{
+       nicklist_rename_list(server, new_nick_id, old_nick, new_nick,
+                            nicklist_get_same_unique(server, old_nick_id));
+}
+
+static NICK_REC *nicklist_find_wildcards(CHANNEL_REC *channel,
+                                        const char *mask)
+{
+       GSList *nicks, *tmp;
+       NICK_REC *nick;
+
+       nicks = nicklist_getnicks(channel);
+       nick = NULL;
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+               nick = tmp->data;
+
+               if (mask_match_address(channel->server, mask,
+                                      nick->nick, nick->host))
+                       break;
+       }
+       g_slist_free(nicks);
+       return tmp == NULL ? NULL : nick;
+}
+
+GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask)
+{
+       GSList *nicks, *tmp, *next;
+
+       g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+       g_return_val_if_fail(mask != NULL, NULL);
+
+       nicks = nicklist_getnicks(channel);
+       for (tmp = nicks; tmp != NULL; tmp = next) {
+               NICK_REC *nick = tmp->data;
+
+               next = tmp->next;
+               if (!mask_match_address(channel->server, mask,
+                                       nick->nick, nick->host))
+                        nicks = g_slist_remove(nicks, tmp->data);
+       }
+
+       return nicks;
+}
+
+/* Find nick */
+NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *nick)
+{
+       g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+       g_return_val_if_fail(nick != NULL, NULL);
+
+       return g_hash_table_lookup(channel->nicks, nick);
+}
+
+NICK_REC *nicklist_find_unique(CHANNEL_REC *channel, const char *nick,
+                              void *id)
+{
+       NICK_REC *rec;
+
+       g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+       g_return_val_if_fail(nick != NULL, NULL);
+
+       rec = g_hash_table_lookup(channel->nicks, nick);
+       while (rec != NULL && rec->unique_id != id)
+                rec = rec->next;
+
+        return rec;
+}
+
+/* Find nick mask, wildcards allowed */
+NICK_REC *nicklist_find_mask(CHANNEL_REC *channel, const char *mask)
+{
+       NICK_REC *nickrec;
+       char *nick, *host;
+
+       g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+       g_return_val_if_fail(mask != NULL, NULL);
+
+       nick = g_strdup(mask);
+       host = strchr(nick, '!');
+       if (host != NULL) *host++ = '\0';
+
+       if (strchr(nick, '*') || strchr(nick, '?')) {
+               g_free(nick);
+               return nicklist_find_wildcards(channel, mask);
+       }
+
+       nickrec = g_hash_table_lookup(channel->nicks, nick);
+
+       if (host != NULL) {
+               while (nickrec != NULL) {
+                       if (nickrec->host != NULL &&
+                           match_wildcards(host, nickrec->host))
+                               break; /* match */
+                       nickrec = nickrec->next;
+               }
+       }
+       g_free(nick);
+       return nickrec;
+}
+
+static void get_nicks_hash(gpointer key, NICK_REC *rec, GSList **list)
+{
+       while (rec != NULL) {
+               *list = g_slist_append(*list, rec);
+                rec = rec->next;
+       }
+}
+
+/* Get list of nicks */
+GSList *nicklist_getnicks(CHANNEL_REC *channel)
+{
+       GSList *list;
+
+       g_return_val_if_fail(IS_CHANNEL(channel), NULL);
+
+       list = NULL;
+       g_hash_table_foreach(channel->nicks, (GHFunc) get_nicks_hash, &list);
+       return list;
+}
+
+typedef struct {
+        CHANNEL_REC *channel;
+       const char *nick;
+       GSList *list;
+} NICKLIST_GET_SAME_REC;
+
+static void get_nicks_same_hash(gpointer key, NICK_REC *nick,
+                               NICKLIST_GET_SAME_REC *rec)
+{
+       while (nick != NULL) {
+               if (g_strcasecmp(nick->nick, rec->nick) == 0) {
+                       rec->list = g_slist_append(rec->list, rec->channel);
+                       rec->list = g_slist_append(rec->list, nick);
+               }
+
+               nick = nick->next;
+       }
+}
+
+GSList *nicklist_get_same(SERVER_REC *server, const char *nick)
+{
+       NICKLIST_GET_SAME_REC rec;
+       GSList *tmp;
+
+       g_return_val_if_fail(IS_SERVER(server), NULL);
+
+       rec.nick = nick;
+       rec.list = NULL;
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               rec.channel = tmp->data;
+               g_hash_table_foreach(rec.channel->nicks,
+                                    (GHFunc) get_nicks_same_hash, &rec);
+       }
+       return rec.list;
+}
+
+typedef struct {
+       CHANNEL_REC *channel;
+        void *id;
+       GSList *list;
+} NICKLIST_GET_SAME_UNIQUE_REC;
+
+static void get_nicks_same_hash_unique(gpointer key, NICK_REC *nick,
+                                      NICKLIST_GET_SAME_UNIQUE_REC *rec)
+{
+       while (nick != NULL) {
+               if (nick->unique_id == rec->id) {
+                       rec->list = g_slist_append(rec->list, rec->channel);
+                       rec->list = g_slist_append(rec->list, nick);
+                        break;
+               }
+
+                nick = nick->next;
+       }
+}
+
+GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
+{
+       NICKLIST_GET_SAME_UNIQUE_REC rec;
+       GSList *tmp;
+
+       g_return_val_if_fail(IS_SERVER(server), NULL);
+       g_return_val_if_fail(id != NULL, NULL);
+
+        rec.id = id;
+       rec.list = NULL;
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               rec.channel = tmp->data;
+               g_hash_table_foreach(rec.channel->nicks,
+                                    (GHFunc) get_nicks_same_hash_unique,
+                                    &rec);
+       }
+       return rec.list;
+}
+
+/* nick record comparision for sort functions */
+int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
+{
+       int status1, status2;
+       
+       if (p1 == NULL) return -1;
+       if (p2 == NULL) return 1;
+
+       /* we assign each status (op, halfop, voice, normal) a number
+        * and compare them. this is easier than 100,000 if's and
+        * returns :-)
+        * -- yath */
+
+       if (p1->op)
+               status1 = 4;
+       else if (p1->halfop)
+               status1 = 3;
+       else if (p1->voice)
+               status1 = 2;
+       else
+               status1 = 1;
+
+       if (p2->op)
+               status2 = 4;
+       else if (p2->halfop)
+               status2 = 3;
+       else if (p2->voice)
+               status2 = 2;
+       else
+               status2 = 1;
+       
+       if (status1 < status2)
+               return 1;
+       else if (status1 > status2)
+               return -1;
+       
+       return g_strcasecmp(p1->nick, p2->nick);
+}
+
+static void nicklist_update_flags_list(SERVER_REC *server, int gone,
+                                      int serverop, GSList *nicks)
+{
+       GSList *tmp;
+       CHANNEL_REC *channel;
+       NICK_REC *rec;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
+               channel = tmp->data;
+               rec = tmp->next->data;
+
+               rec->last_check = time(NULL);
+
+               if (gone != -1 && (int)rec->gone != gone) {
+                       rec->gone = gone;
+                       signal_emit("nicklist gone changed", 2, channel, rec);
+               }
+
+               if (serverop != -1 && (int)rec->serverop != serverop) {
+                       rec->serverop = serverop;
+                       signal_emit("nicklist serverop changed", 2, channel, rec);
+               }
+       }
+       g_slist_free(nicks);
+}
+
+void nicklist_update_flags(SERVER_REC *server, const char *nick,
+                          int gone, int serverop)
+{
+       nicklist_update_flags_list(server, gone, serverop,
+                                  nicklist_get_same(server, nick));
+}
+
+void nicklist_update_flags_unique(SERVER_REC *server, void *id,
+                                 int gone, int serverop)
+{
+       nicklist_update_flags_list(server, gone, serverop,
+                                  nicklist_get_same_unique(server, id));
+}
+
+/* Specify which nick in channel is ours */
+void nicklist_set_own(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       NICK_REC *first, *next;
+
+        channel->ownnick = nick;
+
+       /* move our nick in the list to first, makes some things easier
+          (like handling multiple identical nicks in fe-messages.c) */
+       first = g_hash_table_lookup(channel->nicks, nick->nick);
+       if (first->next == NULL)
+               return;
+
+       next = nick->next;
+       nick->next = first;
+
+       while (first->next != nick)
+                first = first->next;
+       first->next = next;
+
+        g_hash_table_insert(channel->nicks, nick->nick, nick);
+}
+
+static void sig_channel_created(CHANNEL_REC *channel)
+{
+       g_return_if_fail(IS_CHANNEL(channel));
+
+       channel->nicks = g_hash_table_new((GHashFunc) g_istr_hash,
+                                         (GCompareFunc) g_istr_equal);
+}
+
+static void nicklist_remove_hash(gpointer key, NICK_REC *nick,
+                                CHANNEL_REC *channel)
+{
+       NICK_REC *next;
+
+       while (nick != NULL) {
+                next = nick->next;
+               nicklist_destroy(channel, nick);
+                nick = next;
+       }
+}
+
+static void sig_channel_destroyed(CHANNEL_REC *channel)
+{
+       g_return_if_fail(IS_CHANNEL(channel));
+
+       g_hash_table_foreach(channel->nicks,
+                            (GHFunc) nicklist_remove_hash, channel);
+       g_hash_table_destroy(channel->nicks);
+}
+
+static NICK_REC *nick_nfind(CHANNEL_REC *channel, const char *nick, int len)
+{
+        NICK_REC *rec;
+       char *tmpnick;
+
+       tmpnick = g_strndup(nick, len);
+       rec = g_hash_table_lookup(channel->nicks, tmpnick);
+
+       if (rec != NULL) {
+               /* if there's multiple, get the one with identical case */
+               while (rec->next != NULL) {
+                       if (strcmp(rec->nick, tmpnick) == 0)
+                               break;
+                        rec = rec->next;
+               }
+       }
+
+        g_free(tmpnick);
+       return rec;
+}
+
+/* Check is `msg' is meant for `nick'. */
+int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick)
+{
+       const char *msgstart, *orignick;
+       int len, fullmatch;
+
+       g_return_val_if_fail(nick != NULL, FALSE);
+       g_return_val_if_fail(msg != NULL, FALSE);
+
+       if (channel != NULL && channel->server->nick_match_msg != NULL)
+               return channel->server->nick_match_msg(msg, nick);
+
+       /* first check for identical match */
+       len = strlen(nick);
+       if (g_strncasecmp(msg, nick, len) == 0 && !isalnumhigh((int) msg[len]))
+               return TRUE;
+
+       orignick = nick;
+       for (;;) {
+               nick = orignick;
+               msgstart = msg;
+                fullmatch = TRUE;
+
+               /* check if it matches for alphanumeric parts of nick */
+               while (*nick != '\0' && *msg != '\0') {
+                       if (i_toupper(*nick) == i_toupper(*msg)) {
+                               /* total match */
+                               msg++;
+                       } else if (i_isalnum(*msg) && !i_isalnum(*nick)) {
+                               /* some strange char in your nick, pass it */
+                                fullmatch = FALSE;
+                       } else
+                               break;
+
+                       nick++;
+               }
+
+               if (msg != msgstart && !isalnumhigh(*msg)) {
+                       /* at least some of the chars in line matched the
+                          nick, and msg continue with non-alphanum character,
+                          this might be for us.. */
+                       if (*nick != '\0') {
+                               /* remove the rest of the non-alphanum chars
+                                  from nick and check if it then matches. */
+                                fullmatch = FALSE;
+                               while (*nick != '\0' && !i_isalnum(*nick))
+                                       nick++;
+                       }
+
+                       if (*nick == '\0') {
+                               /* yes, match! */
+                                break;
+                       }
+               }
+
+               /* no match. check if this is a message to multiple people
+                  (like nick1,nick2: text) */
+               while (*msg != '\0' && *msg != ' ' && *msg != ',') msg++;
+
+               if (*msg != ',') {
+                        nick = orignick;
+                       break;
+               }
+
+                msg++;
+       }
+
+       if (*nick != '\0')
+               return FALSE; /* didn't match */
+
+       if (fullmatch)
+               return TRUE; /* matched without fuzzyness */
+
+       /* matched with some fuzzyness .. check if there's an exact match
+          for some other nick in the same channel. */
+        return nick_nfind(channel, msgstart, (int) (msg-msgstart)) == NULL;
+}
+
+void nicklist_init(void)
+{
+       signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
+       signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+}
+
+void nicklist_deinit(void)
+{
+       signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
+       signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+
+       module_uniq_destroy("NICK");
+}
diff --git a/apps/irssi/src/core/nicklist.h b/apps/irssi/src/core/nicklist.h
new file mode 100644 (file)
index 0000000..a968350
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __NICKLIST_H
+#define __NICKLIST_H
+
+/* Returns NICK_REC if it's nick, NULL if it isn't. */
+#define NICK(server) \
+       MODULE_CHECK_CAST(server, NICK_REC, type, "NICK")
+
+#define IS_NICK(server) \
+       (NICK(server) ? TRUE : FALSE)
+
+struct _NICK_REC {
+#include "nick-rec.h"
+};
+
+/* Add new nick to list */
+void nicklist_insert(CHANNEL_REC *channel, NICK_REC *nick);
+/* Set host address for nick */
+void nicklist_set_host(CHANNEL_REC *channel, NICK_REC *nick, const char *host);
+/* Remove nick from list */
+void nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick);
+/* Change nick */
+void nicklist_rename(SERVER_REC *server, const char *old_nick,
+                    const char *new_nick);
+void nicklist_rename_unique(SERVER_REC *server,
+                           void *old_nick_id, const char *old_nick,
+                           void *new_nick_id, const char *new_nick);
+
+/* Find nick */
+NICK_REC *nicklist_find(CHANNEL_REC *channel, const char *nick);
+NICK_REC *nicklist_find_unique(CHANNEL_REC *channel, const char *nick,
+                              void *id);
+/* Find nick mask, wildcards allowed */
+NICK_REC *nicklist_find_mask(CHANNEL_REC *channel, const char *mask);
+/* Get list of nicks that match the mask */
+GSList *nicklist_find_multiple(CHANNEL_REC *channel, const char *mask);
+/* Get list of nicks */
+GSList *nicklist_getnicks(CHANNEL_REC *channel);
+/* Get all the nick records of `nick'. Returns channel, nick, channel, ... */
+GSList *nicklist_get_same(SERVER_REC *server, const char *nick);
+GSList *nicklist_get_same_unique(SERVER_REC *server, void *id);
+
+/* Update specified nick's status in server. */
+void nicklist_update_flags(SERVER_REC *server, const char *nick,
+                          int gone, int ircop);
+void nicklist_update_flags_unique(SERVER_REC *server, void *id,
+                          int gone, int ircop);
+
+/* Specify which nick in channel is ours */
+void nicklist_set_own(CHANNEL_REC *channel, NICK_REC *nick);
+
+/* Nick record comparision for sort functions */
+int nicklist_compare(NICK_REC *p1, NICK_REC *p2);
+
+/* Check is `msg' is meant for `nick'. */
+int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick);
+
+void nicklist_init(void);
+void nicklist_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/nickmatch-cache.c b/apps/irssi/src/core/nickmatch-cache.c
new file mode 100644 (file)
index 0000000..6605a2f
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ nickmatch-cache.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+
+#include "channels.h"
+#include "nicklist.h"
+
+#include "nickmatch-cache.h"
+
+static GSList *lists;
+
+NICKMATCH_REC *nickmatch_init(NICKMATCH_REBUILD_FUNC func)
+{
+       NICKMATCH_REC *rec;
+
+       rec = g_new0(NICKMATCH_REC, 1);
+       rec->func = func;
+
+       lists = g_slist_append(lists, rec);
+        return rec;
+}
+
+void nickmatch_deinit(NICKMATCH_REC *rec)
+{
+       lists = g_slist_remove(lists, rec);
+
+        g_hash_table_destroy(rec->nicks);
+        g_free(rec);
+}
+
+static void nickmatch_check_channel(CHANNEL_REC *channel, NICKMATCH_REC *rec)
+{
+       GSList *nicks, *tmp;
+
+       nicks = nicklist_getnicks(channel);
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+               NICK_REC *nick = tmp->data;
+
+               rec->func(rec->nicks, channel, nick);
+       }
+        g_slist_free(nicks);
+}
+
+void nickmatch_rebuild(NICKMATCH_REC *rec)
+{
+       if (rec->nicks != NULL)
+               g_hash_table_destroy(rec->nicks);
+
+       rec->nicks = g_hash_table_new((GHashFunc) g_direct_hash,
+                                     (GCompareFunc) g_direct_equal);
+
+       g_slist_foreach(channels, (GFunc) nickmatch_check_channel, rec);
+}
+
+static void sig_nick_new(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       GSList *tmp;
+
+        g_return_if_fail(channel != NULL);
+       g_return_if_fail(nick != NULL);
+
+       for (tmp = lists; tmp != NULL; tmp = tmp->next) {
+               NICKMATCH_REC *rec = tmp->data;
+
+               rec->func(rec->nicks, channel, nick);
+       }
+}
+
+static void sig_nick_remove(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       GSList *tmp;
+
+        g_return_if_fail(channel != NULL);
+       g_return_if_fail(nick != NULL);
+
+       for (tmp = lists; tmp != NULL; tmp = tmp->next) {
+               NICKMATCH_REC *rec = tmp->data;
+
+                g_hash_table_remove(rec->nicks, nick);
+       }
+}
+
+void nickmatch_cache_init(void)
+{
+       lists = NULL;
+        signal_add("nicklist new", (SIGNAL_FUNC) sig_nick_new);
+        signal_add("nicklist changed", (SIGNAL_FUNC) sig_nick_new);
+        signal_add("nicklist host changed", (SIGNAL_FUNC) sig_nick_new);
+        signal_add("nicklist remove", (SIGNAL_FUNC) sig_nick_remove);
+}
+
+void nickmatch_cache_deinit(void)
+{
+       g_slist_foreach(lists, (GFunc) nickmatch_deinit, NULL);
+        g_slist_free(lists);
+
+       signal_remove("nicklist new", (SIGNAL_FUNC) sig_nick_new);
+        signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nick_new);
+        signal_remove("nicklist host changed", (SIGNAL_FUNC) sig_nick_new);
+        signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nick_remove);
+}
diff --git a/apps/irssi/src/core/nickmatch-cache.h b/apps/irssi/src/core/nickmatch-cache.h
new file mode 100644 (file)
index 0000000..c4140a4
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __NICKMATCH_CACHE_H
+#define __NICKMATCH_CACHE_H
+
+typedef void (*NICKMATCH_REBUILD_FUNC) (GHashTable *list,
+                                       CHANNEL_REC *channel, NICK_REC *nick);
+
+typedef struct {
+        GHashTable *nicks;
+       NICKMATCH_REBUILD_FUNC func;
+} NICKMATCH_REC;
+
+NICKMATCH_REC *nickmatch_init(NICKMATCH_REBUILD_FUNC func);
+void nickmatch_deinit(NICKMATCH_REC *rec);
+
+/* Calls rebuild function for all nicks in all channels.
+   This must be called soon after nickmatch_init(), before any nicklist
+   signals get sent. */
+void nickmatch_rebuild(NICKMATCH_REC *rec);
+
+#define nickmatch_find(rec, nick) \
+        g_hash_table_lookup((rec)->nicks, nick)
+
+void nickmatch_cache_init(void);
+void nickmatch_cache_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/pidwait.c b/apps/irssi/src/core/pidwait.c
new file mode 100644 (file)
index 0000000..bb48d0d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ pidwait.c :
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "modules.h"
+
+#include <sys/wait.h>
+
+static GSList *pids;
+
+static unsigned int childcheck_tag;
+static int signal_pidwait;
+
+/* add a pid to wait list */
+void pidwait_add(int pid)
+{
+       pids = g_slist_append(pids, GINT_TO_POINTER(pid));
+}
+
+/* remove pid from wait list */
+void pidwait_remove(int pid)
+{
+       pids = g_slist_remove(pids, GINT_TO_POINTER(pid));
+}
+
+/* return list of pids that are being waited.
+   don't free the return value. */
+GSList *pidwait_get_pids(void)
+{
+        return pids;
+}
+
+static int child_check(void)
+{
+       GSList *tmp, *next;
+       int status;
+
+       /* wait for each pid.. */
+       for (tmp = pids; tmp != NULL; tmp = next) {
+               int pid = GPOINTER_TO_INT(tmp->data);
+
+               next = tmp->next;
+               if (waitpid(pid, &status, WNOHANG) > 0) {
+                       /* process terminated, remove from list */
+                       signal_emit_id(signal_pidwait, 2, tmp->data,
+                                      GINT_TO_POINTER(status));
+                       pids = g_slist_remove(pids, tmp->data);
+               }
+       }
+       return 1;
+}
+
+void pidwait_init(void)
+{
+       pids = NULL;
+       childcheck_tag = g_timeout_add(1000, (GSourceFunc) child_check, NULL);
+
+       signal_pidwait = signal_get_uniq_id("pidwait");
+}
+
+void pidwait_deinit(void)
+{
+       g_slist_free(pids);
+
+       g_source_remove(childcheck_tag);
+}
diff --git a/apps/irssi/src/core/pidwait.h b/apps/irssi/src/core/pidwait.h
new file mode 100644 (file)
index 0000000..ae1ba01
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __PIDWAIT_H
+#define __PIDWAIT_H
+
+void pidwait_init(void);
+void pidwait_deinit(void);
+
+/* add a pid to wait list */
+void pidwait_add(int pid);
+/* remove pid from wait list */
+void pidwait_remove(int pid);
+
+/* return list of pids that are being waited.
+   don't free the return value. */
+GSList *pidwait_get_pids(void);
+
+#endif
diff --git a/apps/irssi/src/core/queries.c b/apps/irssi/src/core/queries.c
new file mode 100644 (file)
index 0000000..cc16ad1
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ queries.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "servers.h"
+#include "queries.h"
+
+GSList *queries;
+
+void query_init(QUERY_REC *query, int automatic)
+{
+       g_return_if_fail(query != NULL);
+       g_return_if_fail(query->name != NULL);
+
+       queries = g_slist_append(queries, query);
+
+        MODULE_DATA_INIT(query);
+       query->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
+        query->destroy = (void (*) (WI_ITEM_REC *)) query_destroy;
+       if (query->server_tag != NULL) {
+               query->server = server_find_tag(query->server_tag);
+               if (query->server != NULL) {
+                       query->server->queries =
+                               g_slist_append(query->server->queries, query);
+               }
+       }
+
+       signal_emit("query created", 2, query, GINT_TO_POINTER(automatic));
+}
+
+void query_destroy(QUERY_REC *query)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+        if (query->destroying) return;
+       query->destroying = TRUE;
+
+       queries = g_slist_remove(queries, query);
+       if (query->server != NULL) {
+               query->server->queries =
+                       g_slist_remove(query->server->queries, query);
+       }
+       signal_emit("query destroyed", 1, query);
+
+        MODULE_DATA_DEINIT(query);
+       g_free_not_null(query->hilight_color);
+        g_free_not_null(query->server_tag);
+        g_free_not_null(query->address);
+       g_free(query->name);
+
+        query->type = 0;
+       g_free(query);
+}
+
+static QUERY_REC *query_find_server(SERVER_REC *server, const char *nick)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(IS_SERVER(server), NULL);
+
+       if (server->query_find_func != NULL) {
+               /* use the server specific query find function */
+               return server->query_find_func(server, nick);
+       }
+
+       for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, nick) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+QUERY_REC *query_find(SERVER_REC *server, const char *nick)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(server == NULL || IS_SERVER(server), NULL);
+       g_return_val_if_fail(nick != NULL, NULL);
+
+       if (server != NULL)
+               return query_find_server(server, nick);
+
+       for (tmp = queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, nick) == 0)
+                       return rec;
+       }
+
+        return NULL;
+}
+
+void query_change_nick(QUERY_REC *query, const char *nick)
+{
+       char *oldnick;
+
+       g_return_if_fail(IS_QUERY(query));
+
+        oldnick = query->name;
+       query->name = g_strdup(nick);
+       signal_emit("query nick changed", 2, query, oldnick);
+        g_free(oldnick);
+}
+
+void query_change_address(QUERY_REC *query, const char *address)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+        g_free_not_null(query->address);
+       query->address = g_strdup(address);
+       signal_emit("query address changed", 1, query);
+}
+
+void query_change_server(QUERY_REC *query, SERVER_REC *server)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+       if (query->server != NULL) {
+               query->server->queries =
+                        g_slist_remove(query->server->queries, query);
+       }
+       if (server != NULL)
+                server->queries = g_slist_append(server->queries, query);
+
+       query->server = server;
+       signal_emit("query server changed", 1, query);
+}
+
+void queries_init(void)
+{
+}
+
+void queries_deinit(void)
+{
+       module_uniq_destroy("QUERY");
+}
diff --git a/apps/irssi/src/core/queries.h b/apps/irssi/src/core/queries.h
new file mode 100644 (file)
index 0000000..77ef9c3
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __QUERIES_H
+#define __QUERIES_H
+
+#include "modules.h"
+
+/* Returns QUERY_REC if it's query, NULL if it isn't. */
+#define QUERY(query) \
+       MODULE_CHECK_CAST_MODULE(query, QUERY_REC, type, \
+                             "WINDOW ITEM TYPE", "QUERY")
+
+#define IS_QUERY(query) \
+       (QUERY(query) ? TRUE : FALSE)
+
+#define STRUCT_SERVER_REC SERVER_REC
+struct _QUERY_REC {
+#include "query-rec.h"
+};
+
+extern GSList *queries;
+
+void query_init(QUERY_REC *query, int automatic);
+void query_destroy(QUERY_REC *query);
+
+/* Find query by name, if `server' is NULL, search from all servers */
+QUERY_REC *query_find(SERVER_REC *server, const char *nick);
+
+void query_change_nick(QUERY_REC *query, const char *nick);
+void query_change_address(QUERY_REC *query, const char *address);
+void query_change_server(QUERY_REC *query, SERVER_REC *server);
+
+void queries_init(void);
+void queries_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/query-rec.h b/apps/irssi/src/core/query-rec.h
new file mode 100644 (file)
index 0000000..12ef491
--- /dev/null
@@ -0,0 +1,9 @@
+/* QUERY_REC definition, used for inheritance */
+
+#include "window-item-rec.h"
+
+char *address;
+char *server_tag;
+unsigned int unwanted:1; /* TRUE if the other side closed or
+                           some error occured (DCC chats!) */
+unsigned int destroying:1;
diff --git a/apps/irssi/src/core/rawlog.c b/apps/irssi/src/core/rawlog.c
new file mode 100644 (file)
index 0000000..d605ccb
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ rawlog.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "rawlog.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "write-buffer.h"
+#include "settings.h"
+
+#include "servers.h"
+
+static int rawlog_lines;
+static int signal_rawlog;
+static int log_file_create_mode;
+
+RAWLOG_REC *rawlog_create(void)
+{
+       RAWLOG_REC *rec;
+
+       rec = g_new0(RAWLOG_REC, 1);
+        return rec;
+}
+
+void rawlog_destroy(RAWLOG_REC *rawlog)
+{
+       g_return_if_fail(rawlog != NULL);
+
+       g_slist_foreach(rawlog->lines, (GFunc) g_free, NULL);
+       g_slist_free(rawlog->lines);
+
+       if (rawlog->logging) {
+               write_buffer_flush();
+               close(rawlog->handle);
+       }
+       g_free(rawlog);
+}
+
+/* NOTE! str must be dynamically allocated and must not be freed after! */
+static void rawlog_add(RAWLOG_REC *rawlog, char *str)
+{
+       if (rawlog->nlines < rawlog_lines || rawlog_lines <= 2)
+               rawlog->nlines++;
+       else {
+               g_free(rawlog->lines->data);
+               rawlog->lines = g_slist_remove(rawlog->lines,
+                                              rawlog->lines->data);
+       }
+
+       if (rawlog->logging) {
+               write_buffer(rawlog->handle, str, strlen(str));
+               write_buffer(rawlog->handle, "\n", 1);
+       }
+
+       rawlog->lines = g_slist_append(rawlog->lines, str);
+       signal_emit_id(signal_rawlog, 2, rawlog, str);
+}
+
+void rawlog_input(RAWLOG_REC *rawlog, const char *str)
+{
+       g_return_if_fail(rawlog != NULL);
+       g_return_if_fail(str != NULL);
+
+       rawlog_add(rawlog, g_strdup_printf(">> %s", str));
+}
+
+void rawlog_output(RAWLOG_REC *rawlog, const char *str)
+{
+       g_return_if_fail(rawlog != NULL);
+       g_return_if_fail(str != NULL);
+
+       rawlog_add(rawlog, g_strdup_printf("<< %s", str));
+}
+
+void rawlog_redirect(RAWLOG_REC *rawlog, const char *str)
+{
+       g_return_if_fail(rawlog != NULL);
+       g_return_if_fail(str != NULL);
+
+       rawlog_add(rawlog, g_strdup_printf("--> %s", str));
+}
+
+static void rawlog_dump(RAWLOG_REC *rawlog, int f)
+{
+       GSList *tmp;
+
+       for (tmp = rawlog->lines; tmp != NULL; tmp = tmp->next) {
+               write(f, tmp->data, strlen((char *) tmp->data));
+               write(f, "\n", 1);
+       }
+}
+
+void rawlog_open(RAWLOG_REC *rawlog, const char *fname)
+{
+       char *path;
+
+        g_return_if_fail(rawlog != NULL);
+       g_return_if_fail(fname != NULL);
+
+       if (rawlog->logging)
+               return;
+
+       path = convert_home(fname);
+       rawlog->handle = open(path, O_WRONLY | O_APPEND | O_CREAT,
+                             log_file_create_mode);
+       g_free(path);
+
+       rawlog_dump(rawlog, rawlog->handle);
+       rawlog->logging = rawlog->handle != -1;
+}
+
+void rawlog_close(RAWLOG_REC *rawlog)
+{
+       if (rawlog->logging) {
+               write_buffer_flush();
+               close(rawlog->handle);
+               rawlog->logging = 0;
+       }
+}
+
+void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
+{
+       char *path;
+       int f;
+
+       path = convert_home(fname);
+       f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
+       g_free(path);
+
+       rawlog_dump(rawlog, f);
+       close(f);
+}
+
+void rawlog_set_size(int lines)
+{
+       rawlog_lines = lines;
+}
+
+static void read_settings(void)
+{
+       rawlog_set_size(settings_get_int("rawlog_lines"));
+       log_file_create_mode = octal2dec(settings_get_int("log_create_mode"));
+}
+
+static void cmd_rawlog(const char *data, SERVER_REC *server, void *item)
+{
+       command_runsub("rawlog", data, server, item);
+}
+
+/* SYNTAX: RAWLOG SAVE <file> */
+static void cmd_rawlog_save(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+       if (server == NULL || server->rawlog == NULL)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+       rawlog_save(server->rawlog, data);
+}
+
+/* SYNTAX: RAWLOG OPEN <file> */
+static void cmd_rawlog_open(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+       if (server == NULL || server->rawlog == NULL)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+       rawlog_open(server->rawlog, data);
+}
+
+/* SYNTAX: RAWLOG CLOSE */
+static void cmd_rawlog_close(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+       if (server == NULL || server->rawlog == NULL)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       rawlog_close(server->rawlog);
+}
+
+void rawlog_init(void)
+{
+       signal_rawlog = signal_get_uniq_id("rawlog");
+
+       settings_add_int("history", "rawlog_lines", 200);
+       read_settings();
+
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_bind("rawlog", NULL, (SIGNAL_FUNC) cmd_rawlog);
+       command_bind("rawlog save", NULL, (SIGNAL_FUNC) cmd_rawlog_save);
+       command_bind("rawlog open", NULL, (SIGNAL_FUNC) cmd_rawlog_open);
+       command_bind("rawlog close", NULL, (SIGNAL_FUNC) cmd_rawlog_close);
+}
+
+void rawlog_deinit(void)
+{
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_unbind("rawlog", (SIGNAL_FUNC) cmd_rawlog);
+       command_unbind("rawlog save", (SIGNAL_FUNC) cmd_rawlog_save);
+       command_unbind("rawlog open", (SIGNAL_FUNC) cmd_rawlog_open);
+       command_unbind("rawlog close", (SIGNAL_FUNC) cmd_rawlog_close);
+}
diff --git a/apps/irssi/src/core/rawlog.h b/apps/irssi/src/core/rawlog.h
new file mode 100644 (file)
index 0000000..9e460a8
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __RAWLOG_H
+#define __RAWLOG_H
+
+struct _RAWLOG_REC {
+       int logging;
+       int handle;
+
+        int nlines;
+       GSList *lines;
+};
+
+RAWLOG_REC *rawlog_create(void);
+void rawlog_destroy(RAWLOG_REC *rawlog);
+
+void rawlog_input(RAWLOG_REC *rawlog, const char *str);
+void rawlog_output(RAWLOG_REC *rawlog, const char *str);
+void rawlog_redirect(RAWLOG_REC *rawlog, const char *str);
+
+void rawlog_set_size(int lines);
+
+void rawlog_open(RAWLOG_REC *rawlog, const char *fname);
+void rawlog_close(RAWLOG_REC *rawlog);
+void rawlog_save(RAWLOG_REC *rawlog, const char *fname);
+
+void rawlog_init(void);
+void rawlog_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/server-connect-rec.h b/apps/irssi/src/core/server-connect-rec.h
new file mode 100644 (file)
index 0000000..a59880e
--- /dev/null
@@ -0,0 +1,30 @@
+/* SERVER_CONNECT_REC definition, used for inheritance */
+
+int type; /* module_get_uniq_id("SERVER CONNECT", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+int refcount;
+
+/* if we're connecting via proxy, or just NULLs */
+char *proxy;
+int proxy_port;
+char *proxy_string, *proxy_string_after, *proxy_password;
+
+unsigned short family; /* 0 = don't care, AF_INET or AF_INET6 */
+char *tag; /* try to keep this tag when connected to server */
+char *address;
+int port;
+char *chatnet;
+
+IPADDR *own_ip4, *own_ip6;
+
+char *password;
+char *nick;
+char *username;
+char *realname;
+
+/* when reconnecting, the old server status */
+unsigned int reconnection:1; /* we're trying to reconnect */
+unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
+char *channels;
+char *away_reason;
diff --git a/apps/irssi/src/core/server-rec.h b/apps/irssi/src/core/server-rec.h
new file mode 100644 (file)
index 0000000..0003f69
--- /dev/null
@@ -0,0 +1,73 @@
+/* SERVER_REC definition, used for inheritance */
+
+int type; /* module_get_uniq_id("SERVER", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+int refcount;
+
+STRUCT_SERVER_CONNECT_REC *connrec;
+time_t connect_time; /* connection time */
+time_t real_connect_time; /* time when server replied that we really are connected */
+
+char *tag; /* tag name for addressing server */
+char *nick; /* current nick */
+
+unsigned int connected:1; /* Connected to server */
+unsigned int disconnected:1; /* Disconnected, waiting for refcount to drop zero */
+unsigned int connection_lost:1; /* Connection lost unintentionally */
+unsigned int session_reconnect:1; /* Connected to this server with /UPGRADE */
+unsigned int no_reconnect:1; /* Don't reconnect to server */
+
+NET_SENDBUF_REC *handle;
+int readtag; /* input tag */
+
+/* for net_connect_nonblock() */
+GIOChannel *connect_pipe[2];
+int connect_tag;
+int connect_pid;
+
+RAWLOG_REC *rawlog;
+LINEBUF_REC *buffer; /* receive buffer */
+GHashTable *module_data;
+
+char *version; /* server version */
+char *away_reason;
+char *last_invite; /* channel where you were last invited */
+unsigned int server_operator:1;
+unsigned int usermode_away:1;
+unsigned int banned:1; /* not allowed to connect to this server */
+unsigned int dns_error:1; /* DNS said the host doesn't exist */
+
+GTimeVal lag_sent; /* 0 or time when last lag query was sent to server */
+time_t lag_last_check; /* last time we checked lag */
+int lag; /* server lag in milliseconds */
+
+GSList *channels;
+GSList *queries;
+
+/* -- support for multiple server types -- */
+
+/* -- must not be NULL: -- */
+/* join to a number of channels, channels are specified in `data' separated
+   with commas. there can exist other information after first space like
+   channel keys etc. */
+void (*channels_join)(SERVER_REC *server, const char *data, int automatic);
+/* returns true if `flag' indicates a nick flag (op/voice/halfop) */
+int (*isnickflag)(char flag);
+/* returns true if `data' indicates a channel */
+int (*ischannel)(SERVER_REC *server, const char *data);
+/* returns all nick flag characters in order op, voice, halfop. If some
+   of them aren't supported '\0' can be used. */
+const char *(*get_nick_flags)(void);
+/* send public or private message to server */
+void (*send_message)(SERVER_REC *server, const char *target,
+                    const char *msg, int target_type);
+
+/* -- Default implementations are used if NULL -- */
+CHANNEL_REC *(*channel_find_func)(SERVER_REC *server, const char *name);
+QUERY_REC *(*query_find_func)(SERVER_REC *server, const char *nick);
+int (*mask_match_func)(const char *mask, const char *data);
+/* returns true if `msg' was meant for `nick' */
+int (*nick_match_msg)(const char *nick, const char *msg);
+
+#undef STRUCT_SERVER_CONNECT_REC
diff --git a/apps/irssi/src/core/server-setup-rec.h b/apps/irssi/src/core/server-setup-rec.h
new file mode 100644 (file)
index 0000000..f94ec9a
--- /dev/null
@@ -0,0 +1,22 @@
+int type; /* module_get_uniq_id("SERVER SETUP", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+
+char *chatnet;
+
+unsigned short family; /* 0 = default, AF_INET or AF_INET6 */
+char *address;
+int port;
+char *password;
+
+char *own_host; /* address to use when connecting this server */
+IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
+
+time_t last_connect; /* to avoid reconnecting too fast.. */
+
+unsigned int autoconnect:1;
+unsigned int no_proxy:1;
+unsigned int last_failed:1; /* if last connection attempt failed */
+unsigned int banned:1; /* if we're banned from this server */
+unsigned int dns_error:1; /* DNS said the host doesn't exist */
+
+GHashTable *module_data;
diff --git a/apps/irssi/src/core/servers-reconnect.c b/apps/irssi/src/core/servers-reconnect.c
new file mode 100644 (file)
index 0000000..e51a3fa
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ servers-reconnect.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "commands.h"
+#include "network.h"
+#include "signals.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "servers-reconnect.h"
+
+#include "settings.h"
+
+GSList *reconnects;
+static int last_reconnect_tag;
+static int reconnect_timeout_tag;
+static int reconnect_time;
+
+void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server)
+{
+        g_free_not_null(conn->tag);
+       conn->tag = g_strdup(server->tag);
+
+       g_free_not_null(conn->away_reason);
+       conn->away_reason = !server->usermode_away ? NULL :
+               g_strdup(server->away_reason);
+
+       signal_emit("server reconnect save status", 2, conn, server);
+}
+
+static void server_reconnect_add(SERVER_CONNECT_REC *conn,
+                                time_t next_connect)
+{
+       RECONNECT_REC *rec;
+
+       g_return_if_fail(IS_SERVER_CONNECT(conn));
+
+       rec = g_new(RECONNECT_REC, 1);
+       rec->tag = ++last_reconnect_tag;
+       rec->next_connect = next_connect;
+
+       rec->conn = conn;
+       server_connect_ref(conn);
+
+       reconnects = g_slist_append(reconnects, rec);
+}
+
+void server_reconnect_destroy(RECONNECT_REC *rec)
+{
+       g_return_if_fail(rec != NULL);
+
+       reconnects = g_slist_remove(reconnects, rec);
+
+       signal_emit("server reconnect remove", 1, rec);
+       server_connect_unref(rec->conn);
+       g_free(rec);
+
+       if (reconnects == NULL)
+           last_reconnect_tag = 0;
+}
+
+static int server_reconnect_timeout(void)
+{
+       SERVER_CONNECT_REC *conn;
+       GSList *list, *tmp;
+       time_t now;
+
+       /* If server_connect() removes the next reconnection in queue,
+          we're screwed. I don't think this should happen anymore, but just
+          to be sure we don't crash, do this safely. */
+       list = g_slist_copy(reconnects);
+       now = time(NULL);
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               RECONNECT_REC *rec = tmp->data;
+
+               if (g_slist_find(reconnects, rec) == NULL)
+                       continue;
+
+               if (rec->next_connect <= now) {
+                       conn = rec->conn;
+                       server_connect_ref(conn);
+                       server_reconnect_destroy(rec);
+                       CHAT_PROTOCOL(conn)->server_connect(conn);
+                       server_connect_unref(conn);
+               }
+       }
+
+       g_slist_free(list);
+       return 1;
+}
+
+static void sserver_connect(SERVER_SETUP_REC *rec, SERVER_CONNECT_REC *conn)
+{
+        conn->family = rec->family;
+       conn->address = g_strdup(rec->address);
+       if (conn->port == 0) conn->port = rec->port;
+
+       server_setup_fill_reconn(conn, rec);
+       server_reconnect_add(conn, rec->last_connect+reconnect_time);
+       server_connect_unref(conn);
+}
+
+static SERVER_CONNECT_REC *
+server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
+{
+       SERVER_CONNECT_REC *dest;
+
+        dest = NULL;
+       signal_emit("server connect copy", 2, &dest, src);
+       g_return_val_if_fail(dest != NULL, NULL);
+
+        server_connect_ref(dest);
+       dest->type = module_get_uniq_id("SERVER CONNECT", 0);
+       dest->reconnection = src->reconnection;
+       dest->proxy = g_strdup(src->proxy);
+        dest->proxy_port = src->proxy_port;
+       dest->proxy_string = g_strdup(src->proxy_string);
+       dest->proxy_string_after = g_strdup(src->proxy_string_after);
+       dest->proxy_password = g_strdup(src->proxy_password);
+
+       dest->tag = g_strdup(src->tag);
+
+       if (connect_info) {
+                dest->family = src->family;
+               dest->address = g_strdup(src->address);
+               dest->port = src->port;
+               dest->password = g_strdup(src->password);
+       }
+
+       dest->chatnet = g_strdup(src->chatnet);
+       dest->nick = g_strdup(src->nick);
+       dest->username = g_strdup(src->username);
+       dest->realname = g_strdup(src->realname);
+
+       if (src->own_ip4 != NULL) {
+               dest->own_ip4 = g_new(IPADDR, 1);
+               memcpy(dest->own_ip4, src->own_ip4, sizeof(IPADDR));
+       }
+       if (src->own_ip6 != NULL) {
+               dest->own_ip6 = g_new(IPADDR, 1);
+               memcpy(dest->own_ip6, src->own_ip6, sizeof(IPADDR));
+       }
+
+       dest->channels = g_strdup(src->channels);
+       dest->away_reason = g_strdup(src->away_reason);
+
+       return dest;
+}
+
+#define server_should_reconnect(server) \
+       ((server)->connection_lost && !(server)->no_reconnect && \
+       ((server)->connrec->chatnet != NULL || \
+               (!(server)->banned && !(server)->dns_error)))
+
+#define sserver_connect_ok(rec, net) \
+       (!(rec)->banned && !(rec)->dns_error && (rec)->chatnet != NULL && \
+       g_strcasecmp((rec)->chatnet, (net)) == 0)
+
+static void sig_reconnect(SERVER_REC *server)
+{
+       SERVER_CONNECT_REC *conn;
+       SERVER_SETUP_REC *sserver;
+       GSList *tmp;
+       int use_next, through;
+       time_t now;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       if (reconnect_time == -1 || !server_should_reconnect(server))
+               return;
+
+       conn = server_connect_copy_skeleton(server->connrec, FALSE);
+        g_return_if_fail(conn != NULL);
+
+       /* save the server status */
+       if (server->connected) {
+               conn->reconnection = TRUE;
+
+                reconnect_save_status(conn, server);
+       }
+
+       sserver = server_setup_find(server->connrec->address,
+                                   server->connrec->port,
+                                   server->connrec->chatnet);
+
+       if (sserver != NULL) {
+               /* save the last connection time/status */
+               sserver->last_connect = server->connect_time == 0 ?
+                       time(NULL) : server->connect_time;
+               sserver->last_failed = !server->connected;
+               if (server->banned) sserver->banned = TRUE;
+                if (server->dns_error) sserver->dns_error = TRUE;
+       }
+
+       if (sserver == NULL || conn->chatnet == NULL) {
+               /* not in any chatnet, just reconnect back to same server */
+                conn->family = server->connrec->family;
+               conn->address = g_strdup(server->connrec->address);
+               conn->port = server->connrec->port;
+               conn->password = g_strdup(server->connrec->password);
+
+               server_reconnect_add(conn, (server->connect_time == 0 ? time(NULL) :
+                                           server->connect_time) + reconnect_time);
+               server_connect_unref(conn);
+               return;
+       }
+
+       /* always try to first connect to the first on the list where we
+          haven't got unsuccessful connection attempts for the past half
+          an hour. */
+
+       now = time(NULL);
+       for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+               SERVER_SETUP_REC *rec = tmp->data;
+
+               if (sserver_connect_ok(rec, conn->chatnet) &&
+                   (!rec->last_connect || !rec->last_failed ||
+                    rec->last_connect < now-FAILED_RECONNECT_WAIT)) {
+                       if (rec == sserver)
+                                conn->port = server->connrec->port;
+                       sserver_connect(rec, conn);
+                       return;
+               }
+       }
+
+       /* just try the next server in list */
+       use_next = through = FALSE;
+       for (tmp = setupservers; tmp != NULL; ) {
+               SERVER_SETUP_REC *rec = tmp->data;
+
+               if (!use_next && server->connrec->port == rec->port &&
+                   g_strcasecmp(rec->address, server->connrec->address) == 0)
+                       use_next = TRUE;
+               else if (use_next && sserver_connect_ok(rec, conn->chatnet)) {
+                       if (rec == sserver)
+                                conn->port = server->connrec->port;
+                       sserver_connect(rec, conn);
+                       break;
+               }
+
+               if (tmp->next != NULL) {
+                       tmp = tmp->next;
+                       continue;
+               }
+
+               if (through) {
+                       /* shouldn't happen unless there's no servers in
+                          this chatnet in setup.. */
+                       server_connect_unref(conn);
+                       break;
+               }
+
+               tmp = setupservers;
+               use_next = through = TRUE;
+       }
+}
+
+static void sig_connected(SERVER_REC *server)
+{
+       g_return_if_fail(IS_SERVER(server));
+       if (!server->connrec->reconnection)
+               return;
+
+       if (server->connrec->channels != NULL)
+               server->channels_join(server, server->connrec->channels, TRUE);
+}
+
+/* Remove all servers from reconnect list */
+/* SYNTAX: RMRECONNS */
+static void cmd_rmreconns(void)
+{
+       while (reconnects != NULL)
+               server_reconnect_destroy(reconnects->data);
+}
+
+static RECONNECT_REC *reconnect_find_tag(int tag)
+{
+       GSList *tmp;
+
+       for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
+               RECONNECT_REC *rec = tmp->data;
+
+               if (rec->tag == tag)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static void reconnect_all(void)
+{
+       GSList *list;
+       SERVER_CONNECT_REC *conn;
+       RECONNECT_REC *rec;
+
+       /* first move reconnects to another list so if server_connect()
+          fails and goes to reconnection list again, we won't get stuck
+          here forever */
+       list = NULL;
+       while (reconnects != NULL) {
+               rec = reconnects->data;
+
+               list = g_slist_append(list, rec->conn);
+                server_connect_ref(rec->conn);
+               server_reconnect_destroy(rec);
+       }
+
+
+       while (list != NULL) {
+               conn = list->data;
+
+               CHAT_PROTOCOL(conn)->server_connect(conn);
+                server_connect_unref(conn);
+                list = g_slist_remove(list, conn);
+       }
+}
+
+/* SYNTAX: RECONNECT <tag> */
+static void cmd_reconnect(const char *data, SERVER_REC *server)
+{
+       SERVER_CONNECT_REC *conn;
+       RECONNECT_REC *rec;
+       int tag;
+
+       if (*data == '\0' && server != NULL) {
+               /* reconnect back to same server */
+               conn = server_connect_copy_skeleton(server->connrec, TRUE);
+
+               if (server->connected)
+                       reconnect_save_status(conn, server);
+               signal_emit("command disconnect", 2, "* Reconnecting", server);
+
+               conn->reconnection = TRUE;
+               CHAT_PROTOCOL(conn)->server_connect(conn);
+               server_connect_unref(conn);
+                return;
+       }
+
+       if (g_strcasecmp(data, "all") == 0) {
+               /* reconnect all servers in reconnect queue */
+                reconnect_all();
+                return;
+       }
+
+       if (*data == '\0') {
+               /* reconnect to first server in reconnection list */
+               if (reconnects == NULL)
+                       cmd_return_error(CMDERR_NOT_CONNECTED);
+                rec = reconnects->data;
+       } else {
+               if (g_strncasecmp(data, "RECON-", 6) == 0)
+                       data += 6;
+
+               tag = atoi(data);
+               rec = tag <= 0 ? NULL : reconnect_find_tag(tag);
+
+               if (rec == NULL) {
+                       signal_emit("server reconnect not found", 1, data);
+                        return;
+               }
+       }
+
+       conn = rec->conn;
+       server_connect_ref(conn);
+       server_reconnect_destroy(rec);
+       CHAT_PROTOCOL(conn)->server_connect(conn);
+       server_connect_unref(conn);
+}
+
+static void cmd_disconnect(const char *data, SERVER_REC *server)
+{
+       RECONNECT_REC *rec;
+
+       if (g_strncasecmp(data, "RECON-", 6) != 0)
+               return; /* handle only reconnection removing */
+
+       rec = reconnect_find_tag(atoi(data+6));
+
+       if (rec == NULL)
+               signal_emit("server reconnect not found", 1, data);
+       else
+               server_reconnect_destroy(rec);
+       signal_stop();
+}
+
+static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
+{
+       GSList *tmp, *next;
+
+       for (tmp = reconnects; tmp != NULL; tmp = next) {
+               RECONNECT_REC *rec = tmp->data;
+
+                next = tmp->next;
+                if (rec->conn->chat_type == proto->id)
+                       server_reconnect_destroy(rec);
+       }
+}
+
+static void read_settings(void)
+{
+       reconnect_time = settings_get_int("server_reconnect_time");
+}
+
+void servers_reconnect_init(void)
+{
+       settings_add_int("server", "server_reconnect_time", 300);
+
+       reconnects = NULL;
+       last_reconnect_tag = 0;
+
+       reconnect_timeout_tag = g_timeout_add(1000, (GSourceFunc) server_reconnect_timeout, NULL);
+       read_settings();
+
+       signal_add("server connect failed", (SIGNAL_FUNC) sig_reconnect);
+       signal_add("server disconnected", (SIGNAL_FUNC) sig_reconnect);
+       signal_add("event connected", (SIGNAL_FUNC) sig_connected);
+       signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_bind("rmreconns", NULL, (SIGNAL_FUNC) cmd_rmreconns);
+       command_bind("reconnect", NULL, (SIGNAL_FUNC) cmd_reconnect);
+       command_bind_first("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
+}
+
+void servers_reconnect_deinit(void)
+{
+       g_source_remove(reconnect_timeout_tag);
+
+       signal_remove("server connect failed", (SIGNAL_FUNC) sig_reconnect);
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_reconnect);
+       signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
+       signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_unbind("rmreconns", (SIGNAL_FUNC) cmd_rmreconns);
+       command_unbind("reconnect", (SIGNAL_FUNC) cmd_reconnect);
+       command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
+}
diff --git a/apps/irssi/src/core/servers-reconnect.h b/apps/irssi/src/core/servers-reconnect.h
new file mode 100644 (file)
index 0000000..835d58d
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __SERVER_RECONNECT_H
+#define __SERVER_RECONNECT_H
+
+/* wait for half an hour before trying to reconnect to host where last
+   connection failed */
+#define FAILED_RECONNECT_WAIT (60*30)
+
+typedef struct {
+        int tag;
+       time_t next_connect;
+
+       SERVER_CONNECT_REC *conn;
+} RECONNECT_REC;
+
+extern GSList *reconnects;
+
+void reconnect_save_status(SERVER_CONNECT_REC *conn, SERVER_REC *server);
+void server_reconnect_destroy(RECONNECT_REC *rec);
+
+void servers_reconnect_init(void);
+void servers_reconnect_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/servers-setup.c b/apps/irssi/src/core/servers-setup.c
new file mode 100644 (file)
index 0000000..99f9f57
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ servers-setup.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "network.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "servers-setup.h"
+
+GSList *setupservers;
+
+char *old_source_host;
+int source_host_ok; /* Use source_host_ip .. */
+IPADDR *source_host_ip4, *source_host_ip6; /* Resolved address */
+
+static void save_ips(IPADDR *ip4, IPADDR *ip6,
+                    IPADDR **save_ip4, IPADDR **save_ip6)
+{
+       if (ip4->family == 0)
+               g_free_and_null(*save_ip4);
+       else {
+                if (*save_ip4 == NULL)
+                       *save_ip4 = g_new(IPADDR, 1);
+               memcpy(*save_ip4, ip4, sizeof(IPADDR));
+       }
+
+       if (ip6->family == 0)
+               g_free_and_null(*save_ip6);
+       else {
+                if (*save_ip6 == NULL)
+                       *save_ip6 = g_new(IPADDR, 1);
+               memcpy(*save_ip6, ip6, sizeof(IPADDR));
+       }
+}
+
+static void get_source_host_ip(void)
+{
+        const char *hostname;
+       IPADDR ip4, ip6;
+
+       if (source_host_ok)
+               return;
+
+       /* FIXME: This will block! */
+        hostname = settings_get_str("hostname");
+       source_host_ok = *hostname != '\0' &&
+               net_gethostbyname(hostname, &ip4, &ip6) == 0;
+
+       if (source_host_ok)
+               save_ips(&ip4, &ip6, &source_host_ip4, &source_host_ip6);
+       else {
+                g_free_and_null(source_host_ip4);
+                g_free_and_null(source_host_ip6);
+       }
+}
+
+static void conn_set_ip(SERVER_CONNECT_REC *conn, const char *own_host,
+                       IPADDR **own_ip4, IPADDR **own_ip6)
+{
+       IPADDR ip4, ip6;
+
+       if (*own_ip4 == NULL && *own_ip6 == NULL) {
+               /* resolve the IP */
+               if (net_gethostbyname(own_host, &ip4, &ip6) == 0)
+                        save_ips(&ip4, &ip6, own_ip4, own_ip6);
+       }
+
+       server_connect_own_ip_save(conn, *own_ip4, *own_ip6);
+}
+
+/* Fill information to connection from server setup record */
+void server_setup_fill_reconn(SERVER_CONNECT_REC *conn,
+                             SERVER_SETUP_REC *sserver)
+{
+       g_return_if_fail(IS_SERVER_CONNECT(conn));
+       g_return_if_fail(IS_SERVER_SETUP(sserver));
+
+       if (sserver->own_host != NULL) {
+               conn_set_ip(conn, sserver->own_host,
+                           &sserver->own_ip4, &sserver->own_ip6);
+       }
+
+       if (sserver->chatnet != NULL && conn->chatnet == NULL)
+               conn->chatnet = g_strdup(sserver->chatnet);
+
+       if (sserver->password != NULL && conn->password == NULL)
+               conn->password = g_strdup(sserver->password);
+
+       signal_emit("server setup fill reconn", 2, conn, sserver);
+}
+
+static void server_setup_fill(SERVER_CONNECT_REC *conn,
+                             const char *address, int port)
+{
+       g_return_if_fail(conn != NULL);
+       g_return_if_fail(address != NULL);
+
+       conn->type = module_get_uniq_id("SERVER CONNECT", 0);
+
+       conn->address = g_strdup(address);
+       if (port > 0) conn->port = port;
+
+       if (!conn->nick) conn->nick = g_strdup(settings_get_str("nick"));
+       conn->username = g_strdup(settings_get_str("user_name"));
+       conn->realname = g_strdup(settings_get_str("real_name"));
+
+       /* proxy settings */
+       if (settings_get_bool("use_proxy")) {
+               conn->proxy = g_strdup(settings_get_str("proxy_address"));
+               conn->proxy_port = settings_get_int("proxy_port");
+               conn->proxy_string = g_strdup(settings_get_str("proxy_string"));
+               conn->proxy_string_after = g_strdup(settings_get_str("proxy_string_after"));
+               conn->proxy_password = g_strdup(settings_get_str("proxy_password"));
+       }
+
+       /* source IP */
+       if (source_host_ip4 != NULL) {
+               conn->own_ip4 = g_new(IPADDR, 1);
+               memcpy(conn->own_ip4, source_host_ip4, sizeof(IPADDR));
+       }
+       if (source_host_ip6 != NULL) {
+               conn->own_ip6 = g_new(IPADDR, 1);
+               memcpy(conn->own_ip6, source_host_ip6, sizeof(IPADDR));
+       }
+
+       signal_emit("server setup fill connect", 1, conn);
+}
+
+static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
+                                    SERVER_SETUP_REC *sserver)
+{
+       g_return_if_fail(IS_SERVER_CONNECT(conn));
+       g_return_if_fail(IS_SERVER_SETUP(sserver));
+
+       sserver->last_connect = time(NULL);
+
+        if (sserver->no_proxy)
+               g_free_and_null(conn->proxy);
+
+       if (sserver->family != 0 && conn->family == 0)
+                conn->family = sserver->family;
+       if (sserver->port > 0 && conn->port <= 0)
+               conn->port = sserver->port;
+       server_setup_fill_reconn(conn, sserver);
+
+       signal_emit("server setup fill server", 2, conn, sserver);
+}
+
+static void server_setup_fill_chatnet(SERVER_CONNECT_REC *conn,
+                                     CHATNET_REC *chatnet)
+{
+       g_return_if_fail(IS_SERVER_CONNECT(conn));
+       g_return_if_fail(IS_CHATNET(chatnet));
+
+       if (chatnet->nick != NULL) {
+               g_free(conn->nick);
+               conn->nick = g_strdup(chatnet->nick);;
+       }
+       if (chatnet->username != NULL) {
+                g_free(conn->username);
+               conn->username = g_strdup(chatnet->username);;
+       }
+       if (chatnet->realname != NULL) {
+                g_free(conn->realname);
+               conn->realname = g_strdup(chatnet->realname);;
+       }
+       if (chatnet->own_host != NULL) {
+               conn_set_ip(conn, chatnet->own_host,
+                           &chatnet->own_ip4, &chatnet->own_ip6);
+       }
+
+       signal_emit("server setup fill chatnet", 2, conn, chatnet);
+}
+
+static SERVER_CONNECT_REC *
+create_addr_conn(int chat_type, const char *address, int port,
+                const char *chatnet, const char *password,
+                const char *nick)
+{
+        CHAT_PROTOCOL_REC *proto;
+       SERVER_CONNECT_REC *conn;
+       SERVER_SETUP_REC *sserver;
+       CHATNET_REC *chatnetrec;
+
+       g_return_val_if_fail(address != NULL, NULL);
+
+       sserver = server_setup_find(address, port, chatnet);
+       if (sserver != NULL) {
+               if (chat_type < 0)
+                       chat_type = sserver->chat_type;
+               else if (chat_type != sserver->chat_type)
+                        sserver = NULL;
+       }
+
+       proto = chat_type >= 0 ? chat_protocol_find_id(chat_type) :
+                chat_protocol_get_default();
+
+       conn = proto->create_server_connect();
+       server_connect_ref(conn);
+
+       conn->chat_type = proto->id;
+        if (chatnet != NULL && *chatnet != '\0')
+               conn->chatnet = g_strdup(chatnet);
+
+       /* fill in the defaults */
+       server_setup_fill(conn, address, port);
+
+       /* fill the rest from chat network settings */
+       chatnetrec = chatnet != NULL ? chatnet_find(chatnet) :
+               (sserver == NULL || sserver->chatnet == NULL ? NULL :
+                chatnet_find(sserver->chatnet));
+       if (chatnetrec != NULL)
+               server_setup_fill_chatnet(conn, chatnetrec);
+
+       /* fill the information from setup */
+       if (sserver != NULL)
+               server_setup_fill_server(conn, sserver);
+
+       /* nick / password given in command line overrides all settings */
+       if (password && *password) {
+               g_free_not_null(conn->password);
+               conn->password = g_strdup(password);
+       }
+       if (nick && *nick) {
+               g_free_not_null(conn->nick);
+               conn->nick = g_strdup(nick);
+       }
+
+       return conn;
+}
+
+/* Connect to server where last connect succeeded (or we haven't tried to
+   connect yet). If there's no such server, connect to server where we
+   haven't connected for the longest time */
+static SERVER_CONNECT_REC *
+create_chatnet_conn(const char *dest, int port,
+                   const char *password, const char *nick)
+{
+       SERVER_SETUP_REC *bestrec;
+       GSList *tmp;
+       time_t now, besttime;
+
+       now = time(NULL);
+       bestrec = NULL; besttime = now;
+       for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+               SERVER_SETUP_REC *rec = tmp->data;
+
+               if (rec->chatnet == NULL ||
+                   g_strcasecmp(rec->chatnet, dest) != 0)
+                       continue;
+
+               if (!rec->last_failed) {
+                       bestrec = rec;
+                       break;
+               }
+
+               if (bestrec == NULL || besttime > rec->last_connect) {
+                       bestrec = rec;
+                       besttime = rec->last_connect;
+               }
+       }
+
+       return bestrec == NULL ? NULL :
+               create_addr_conn(bestrec->chat_type, bestrec->address, 0,
+                                dest, NULL, nick);
+}
+
+/* Create server connection record. `dest' is required, rest can be NULL.
+   `dest' is either a server address or chat network */
+SERVER_CONNECT_REC *
+server_create_conn(int chat_type, const char *dest, int port,
+                  const char *chatnet, const char *password,
+                  const char *nick)
+{
+       SERVER_CONNECT_REC *rec;
+        CHATNET_REC *chatrec;
+
+       g_return_val_if_fail(dest != NULL, NULL);
+
+        chatrec = chatnet_find(dest);
+       if (chatrec != NULL) {
+               rec = create_chatnet_conn(chatrec->name, port, password, nick);
+               if (rec != NULL)
+                       return rec;
+       }
+
+       chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
+       if (chatrec != NULL)
+               chatnet = chatrec->name;
+
+       return create_addr_conn(chat_type, dest, port,
+                               chatnet, password, nick);
+}
+
+/* Find matching server from setup. Try to find record with a same port,
+   but fallback to any server with the same address. */
+SERVER_SETUP_REC *server_setup_find(const char *address, int port,
+                                   const char *chatnet)
+{
+       SERVER_SETUP_REC *server;
+       GSList *tmp;
+
+       g_return_val_if_fail(address != NULL, NULL);
+
+       server = NULL;
+       for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+               SERVER_SETUP_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->address, address) == 0 &&
+                   (chatnet == NULL || rec->chatnet == NULL ||
+                    g_strcasecmp(rec->chatnet, chatnet) == 0)) {
+                       server = rec;
+                       if (rec->port == port)
+                               break;
+               }
+       }
+
+       return server;
+}
+
+/* Find matching server from setup. Ports must match or NULL is returned. */
+SERVER_SETUP_REC *server_setup_find_port(const char *address, int port)
+{
+       SERVER_SETUP_REC *rec;
+
+       rec = server_setup_find(address, port, NULL);
+       return rec == NULL || rec->port != port ? NULL : rec;
+}
+
+static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
+{
+       SERVER_SETUP_REC *rec;
+        CHATNET_REC *chatnetrec;
+       char *server, *chatnet, *family;
+       int port;
+
+       g_return_val_if_fail(node != NULL, NULL);
+
+       server = config_node_get_str(node, "address", NULL);
+       if (server == NULL)
+               return NULL;
+
+       port = config_node_get_int(node, "port", 0);
+       if (server_setup_find_port(server, port) != NULL) {
+               /* already exists - don't let it get there twice or
+                  server reconnects will screw up! */
+               return NULL;
+       }
+
+       rec = NULL;
+       chatnet = config_node_get_str(node, "chatnet", NULL);
+
+       chatnetrec = chatnet == NULL ? NULL : chatnet_find(chatnet);
+       if (chatnetrec == NULL && chatnet != NULL) {
+                /* chat network not found, create it. */
+               chatnetrec = chat_protocol_get_default()->create_chatnet();
+               chatnetrec->chat_type = chat_protocol_get_default()->id;
+               chatnetrec->name = g_strdup(chatnet);
+               chatnet_create(chatnetrec);
+       }
+
+        family = config_node_get_str(node, "family", "");
+
+       rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup();
+       rec->type = module_get_uniq_id("SERVER SETUP", 0);
+        rec->chat_type = CHAT_PROTOCOL(chatnetrec)->id;
+       rec->chatnet = chatnetrec == NULL ? NULL : g_strdup(chatnetrec->name);
+       rec->family = g_strcasecmp(family, "inet6") == 0 ? AF_INET6 :
+               (g_strcasecmp(family, "inet") == 0 ? AF_INET : 0);
+       rec->address = g_strdup(server);
+       rec->password = g_strdup(config_node_get_str(node, "password", NULL));
+       rec->port = port;
+       rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
+       rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
+       rec->own_host = g_strdup(config_node_get_str(node, "own_host", NULL));
+
+       signal_emit("server setup read", 2, rec, node);
+
+       setupservers = g_slist_append(setupservers, rec);
+       return rec;
+}
+
+static void server_setup_save(SERVER_SETUP_REC *rec)
+{
+       CONFIG_NODE *parentnode, *node;
+       int index;
+
+       index = g_slist_index(setupservers, rec);
+
+       parentnode = iconfig_node_traverse("(servers", TRUE);
+       node = config_node_index(parentnode, index);
+       if (node == NULL)
+               node = config_node_section(parentnode, NULL, NODE_TYPE_BLOCK);
+
+        iconfig_node_clear(node);
+       iconfig_node_set_str(node, "address", rec->address);
+       iconfig_node_set_str(node, "chatnet", rec->chatnet);
+
+       iconfig_node_set_int(node, "port", rec->port);
+       iconfig_node_set_str(node, "password", rec->password);
+       iconfig_node_set_str(node, "own_host", rec->own_host);
+
+       iconfig_node_set_str(node, "family",
+                            rec->family == AF_INET6 ? "inet6" :
+                            rec->family == AF_INET ? "inet" : NULL);
+
+       if (rec->autoconnect)
+               iconfig_node_set_bool(node, "autoconnect", TRUE);
+       if (rec->no_proxy)
+               iconfig_node_set_bool(node, "no_proxy", TRUE);
+
+       signal_emit("server setup saved", 2, rec, node);
+}
+
+static void server_setup_remove_config(SERVER_SETUP_REC *rec)
+{
+       CONFIG_NODE *node;
+       int index;
+
+       node = iconfig_node_traverse("servers", FALSE);
+       if (node != NULL) {
+               index = g_slist_index(setupservers, rec);
+               iconfig_node_list_remove(node, index);
+       }
+}
+
+static void server_setup_destroy(SERVER_SETUP_REC *rec)
+{
+       setupservers = g_slist_remove(setupservers, rec);
+       signal_emit("server setup destroyed", 1, rec);
+
+       g_free_not_null(rec->own_host);
+       g_free_not_null(rec->own_ip4);
+       g_free_not_null(rec->own_ip6);
+       g_free_not_null(rec->chatnet);
+       g_free_not_null(rec->password);
+       g_free(rec->address);
+       g_free(rec);
+}
+
+void server_setup_add(SERVER_SETUP_REC *rec)
+{
+       rec->type = module_get_uniq_id("SERVER SETUP", 0);
+       if (g_slist_find(setupservers, rec) == NULL)
+               setupservers = g_slist_append(setupservers, rec);
+       server_setup_save(rec);
+}
+
+void server_setup_remove(SERVER_SETUP_REC *rec)
+{
+       server_setup_remove_config(rec);
+       server_setup_destroy(rec);
+}
+
+static void read_servers(void)
+{
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+       while (setupservers != NULL)
+               server_setup_destroy(setupservers->data);
+
+       /* Read servers */
+       node = iconfig_node_traverse("servers", FALSE);
+       if (node != NULL) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       server_setup_read(tmp->data);
+       }
+}
+
+static void read_settings(void)
+{
+       if (old_source_host == NULL ||
+           strcmp(old_source_host, settings_get_str("hostname")) != 0) {
+                g_free_not_null(old_source_host);
+               old_source_host = g_strdup(settings_get_str("hostname"));
+
+               source_host_ok = FALSE;
+               get_source_host_ip();
+       }
+}
+
+void servers_setup_init(void)
+{
+       settings_add_str("server", "hostname", "");
+
+       settings_add_str("server", "nick", NULL);
+       settings_add_str("server", "user_name", NULL);
+       settings_add_str("server", "real_name", NULL);
+
+       settings_add_bool("proxy", "use_proxy", FALSE);
+       settings_add_str("proxy", "proxy_address", "");
+       settings_add_int("proxy", "proxy_port", 6667);
+       settings_add_str("proxy", "proxy_string", "CONNECT %s %d");
+       settings_add_str("proxy", "proxy_string_after", "");
+       settings_add_str("proxy", "proxy_password", "");
+
+        setupservers = NULL;
+       source_host_ip4 = source_host_ip6 = NULL;
+        old_source_host = NULL;
+       read_settings();
+
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_add("setup reread", (SIGNAL_FUNC) read_servers);
+        signal_add("irssi init read settings", (SIGNAL_FUNC) read_servers);
+}
+
+void servers_setup_deinit(void)
+{
+       g_free_not_null(source_host_ip4);
+       g_free_not_null(source_host_ip6);
+       g_free_not_null(old_source_host);
+
+       while (setupservers != NULL)
+               server_setup_destroy(setupservers->data);
+
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_remove("setup reread", (SIGNAL_FUNC) read_servers);
+        signal_remove("irssi init read settings", (SIGNAL_FUNC) read_servers);
+
+       module_uniq_destroy("SERVER SETUP");
+}
diff --git a/apps/irssi/src/core/servers-setup.h b/apps/irssi/src/core/servers-setup.h
new file mode 100644 (file)
index 0000000..d0101bc
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef __SERVERS_SETUP_H
+#define __SERVERS_SETUP_H
+
+#include "modules.h"
+
+#define SERVER_SETUP(server) \
+       MODULE_CHECK_CAST(server, SERVER_SETUP_REC, type, "SERVER SETUP")
+
+#define IS_SERVER_SETUP(server) \
+       (SERVER_SETUP(server) ? TRUE : FALSE)
+
+/* servers */
+struct _SERVER_SETUP_REC {
+#include "server-setup-rec.h"
+};
+
+extern GSList *setupservers;
+
+extern IPADDR *source_host_ip4, *source_host_ip6; /* Resolved address */
+extern int source_host_ok; /* Use source_host_ip .. */
+
+/* Fill reconnection specific information to connection
+   from server setup record */
+void server_setup_fill_reconn(SERVER_CONNECT_REC *conn,
+                             SERVER_SETUP_REC *sserver);
+
+/* Create server connection record. `dest' is required, rest can be NULL.
+   `dest' is either a server address or chat network */
+SERVER_CONNECT_REC *
+server_create_conn(int chat_type, const char *dest, int port,
+                  const char *chatnet, const char *password,
+                  const char *nick);
+
+/* Find matching server from setup. Try to find record with a same port,
+   but fallback to any server with the same address. */
+SERVER_SETUP_REC *server_setup_find(const char *address, int port,
+                                   const char *chatnet);
+/* Find matching server from setup. Ports must match or NULL is returned. */
+SERVER_SETUP_REC *server_setup_find_port(const char *address, int port);
+
+void server_setup_add(SERVER_SETUP_REC *rec);
+void server_setup_remove(SERVER_SETUP_REC *rec);
+
+void servers_setup_init(void);
+void servers_setup_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/servers.c b/apps/irssi/src/core/servers.c
new file mode 100644 (file)
index 0000000..499f1f9
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ server.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "line-split.h"
+#include "net-nonblock.h"
+#include "net-sendbuffer.h"
+#include "misc.h"
+#include "rawlog.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "servers-reconnect.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "queries.h"
+
+GSList *servers, *lookup_servers;
+
+/* connection to server failed */
+void server_connect_failed(SERVER_REC *server, const char *msg)
+{
+       g_return_if_fail(IS_SERVER(server));
+
+       lookup_servers = g_slist_remove(lookup_servers, server);
+
+       signal_emit("server connect failed", 2, server, msg);
+
+       if (server->connect_tag != -1) {
+               g_source_remove(server->connect_tag);
+               server->connect_tag = -1;
+       }
+       if (server->handle != NULL) {
+               net_sendbuffer_destroy(server->handle, TRUE);
+               server->handle = NULL;
+       }
+
+       if (server->connect_pipe[0] != NULL) {
+               g_io_channel_close(server->connect_pipe[0]);
+               g_io_channel_unref(server->connect_pipe[0]);
+               g_io_channel_close(server->connect_pipe[1]);
+               g_io_channel_unref(server->connect_pipe[1]);
+               server->connect_pipe[0] = NULL;
+               server->connect_pipe[1] = NULL;
+       }
+
+       server_unref(server);
+}
+
+/* generate tag from server's address */
+static char *server_create_address_tag(const char *address)
+{
+       const char *start, *end;
+
+       g_return_val_if_fail(address != NULL, NULL);
+
+       /* try to generate a reasonable server tag */
+       if (strchr(address, '.') == NULL) {
+               start = end = NULL;
+       } else if (g_strncasecmp(address, "irc", 3) == 0 ||
+           g_strncasecmp(address, "chat", 4) == 0) {
+               /* irc-2.cs.hut.fi -> hut, chat.bt.net -> bt */
+               end = strrchr(address, '.');
+               start = end-1;
+               while (start > address && *start != '.') start--;
+       } else {
+               /* efnet.cs.hut.fi -> efnet */
+               end = strchr(address, '.');
+               start = end;
+       }
+
+       if (start == end) start = address; else start++;
+       if (end == NULL) end = address + strlen(address);
+
+       return g_strndup(start, (int) (end-start));
+}
+
+/* create unique tag for server. prefer ircnet's name or
+   generate it from server's address */
+static char *server_create_tag(SERVER_CONNECT_REC *conn)
+{
+       GString *str;
+       char *tag;
+       int num;
+
+       g_return_val_if_fail(IS_SERVER_CONNECT(conn), NULL);
+
+       tag = conn->chatnet != NULL && *conn->chatnet != '\0' ?
+               g_strdup(conn->chatnet) :
+               server_create_address_tag(conn->address);
+
+       if (conn->tag != NULL && server_find_tag(conn->tag) == NULL &&
+           strncmp(conn->tag, tag, strlen(tag)) == 0) {
+               /* use the existing tag if it begins with the same ID -
+                  this is useful when you have several connections to
+                  same server and you want to keep the same tags with
+                  the servers (or it would cause problems when rejoining
+                  /LAYOUT SAVEd channels). */
+               g_free(tag);
+               return g_strdup(conn->tag);
+       }
+
+
+       /* then just append numbers after tag until unused is found.. */
+       str = g_string_new(tag);
+       for (num = 2; server_find_tag(str->str) != NULL; num++)
+               g_string_sprintf(str, "%s%d", tag, num);
+       g_free(tag);
+
+       tag = str->str;
+       g_string_free(str, FALSE);
+       return tag;
+}
+
+/* Connection to server finished, fill the rest of the fields */
+void server_connect_finished(SERVER_REC *server)
+{
+       server->connect_time = time(NULL);
+
+       servers = g_slist_append(servers, server);
+       signal_emit("server connected", 1, server);
+}
+
+static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
+{
+       int error;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       error = net_geterror(handle);
+       if (error != 0) {
+               server->connection_lost = TRUE;
+               server_connect_failed(server, g_strerror(error));
+               return;
+       }
+
+       lookup_servers = g_slist_remove(lookup_servers, server);
+       g_source_remove(server->connect_tag);
+       server->connect_tag = -1;
+
+       server_connect_finished(server);
+}
+
+static void server_connect_callback_readpipe(SERVER_REC *server)
+{
+       SERVER_CONNECT_REC *conn;
+       RESOLVED_IP_REC iprec;
+       GIOChannel *handle;
+        IPADDR *ip, *own_ip;
+       const char *errormsg;
+        int port;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       g_source_remove(server->connect_tag);
+       server->connect_tag = -1;
+
+       net_gethostbyname_return(server->connect_pipe[0], &iprec);
+
+       g_io_channel_close(server->connect_pipe[0]);
+       g_io_channel_unref(server->connect_pipe[0]);
+       g_io_channel_close(server->connect_pipe[1]);
+       g_io_channel_unref(server->connect_pipe[1]);
+
+       server->connect_pipe[0] = NULL;
+       server->connect_pipe[1] = NULL;
+
+       /* figure out if we should use IPv4 or v6 address */
+       ip = iprec.error != 0 ? NULL : iprec.ip6.family == 0 ||
+               (server->connrec->family == AF_INET && iprec.ip4.family != 0) ?
+               &iprec.ip4 : &iprec.ip6;
+       if (iprec.ip4.family != 0 && server->connrec->family == 0 &&
+           !settings_get_bool("resolve_prefer_ipv6"))
+                ip = &iprec.ip4;
+
+        conn = server->connrec;
+       port = conn->proxy != NULL ? conn->proxy_port : conn->port;
+       own_ip = ip == NULL ? NULL :
+               (IPADDR_IS_V6(ip) ? conn->own_ip6 : conn->own_ip4);
+
+       handle = NULL;
+       if (ip != NULL) {
+               signal_emit("server connecting", 2, server, ip);
+                if (server->handle == NULL)
+                       handle = net_connect_ip(ip, port, own_ip);
+               else
+                        handle = net_sendbuffer_handle(server->handle);
+       }
+
+       if (handle == NULL) {
+               /* failed */
+               if (iprec.error != 0 && net_hosterror_notfound(iprec.error)) {
+                       /* IP wasn't found for the host, don't try to reconnect
+                          back to this server */
+                       server->dns_error = TRUE;
+               }
+
+               if (iprec.error == 0) {
+                       /* connect() failed */
+                       errormsg = g_strerror(errno);
+               } else {
+                       /* gethostbyname() failed */
+                       errormsg = iprec.errorstr != NULL ? iprec.errorstr :
+                               "Host lookup failed";
+               }
+               server->connection_lost = TRUE;
+               server_connect_failed(server, errormsg);
+               g_free_not_null(iprec.errorstr);
+               return;
+       }
+
+        if (server->handle == NULL)
+               server->handle = net_sendbuffer_create(handle, 0);
+       server->connect_tag =
+               g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
+                           (GInputFunction) server_connect_callback_init,
+                           server);
+}
+
+/* initializes server record but doesn't start connecting */
+void server_connect_init(SERVER_REC *server)
+{
+       g_return_if_fail(server != NULL);
+
+       MODULE_DATA_INIT(server);
+       server->type = module_get_uniq_id("SERVER", 0);
+       server_ref(server);
+
+       server->nick = g_strdup(server->connrec->nick);
+       if (server->connrec->username == NULL || *server->connrec->username == '\0') {
+               g_free_not_null(server->connrec->username);
+
+               server->connrec->username = g_get_user_name();
+               if (*server->connrec->username == '\0') server->connrec->username = "-";
+               server->connrec->username = g_strdup(server->connrec->username);
+       }
+       if (server->connrec->realname == NULL || *server->connrec->realname == '\0') {
+               g_free_not_null(server->connrec->realname);
+
+               server->connrec->realname = g_get_real_name();
+               if (*server->connrec->realname == '\0') server->connrec->realname = "-";
+               server->connrec->realname = g_strdup(server->connrec->realname);
+       }
+
+       server->tag = server_create_tag(server->connrec);
+}
+
+/* starts connecting to server */
+int server_start_connect(SERVER_REC *server)
+{
+       const char *connect_address;
+        int fd[2];
+
+       g_return_val_if_fail(server != NULL, FALSE);
+       if (server->connrec->port <= 0) return FALSE;
+
+       server_connect_init(server);
+
+       if (pipe(fd) != 0) {
+               g_warning("server_connect(): pipe() failed.");
+                g_free(server->tag);
+               g_free(server->nick);
+               return FALSE;
+       }
+
+        server->connect_pipe[0] = g_io_channel_unix_new(fd[0]);
+       server->connect_pipe[1] = g_io_channel_unix_new(fd[1]);
+
+       connect_address = server->connrec->proxy != NULL ?
+               server->connrec->proxy : server->connrec->address;
+       server->connect_pid =
+               net_gethostbyname_nonblock(connect_address,
+                                          server->connect_pipe[1]);
+       server->connect_tag =
+               g_input_add(server->connect_pipe[0], G_INPUT_READ,
+                           (GInputFunction) server_connect_callback_readpipe,
+                           server);
+       server->rawlog = rawlog_create();
+
+       lookup_servers = g_slist_append(lookup_servers, server);
+
+       signal_emit("server looking", 1, server);
+       return TRUE;
+}
+
+static int server_remove_channels(SERVER_REC *server)
+{
+       GSList *tmp;
+       int found;
+
+       g_return_val_if_fail(server != NULL, FALSE);
+
+       found = FALSE;
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *channel = tmp->data;
+
+               channel->server = NULL;
+               channel_destroy(channel);
+               found = TRUE;
+       }
+
+       while (server->queries != NULL)
+               query_change_server(server->queries->data, NULL);
+
+       g_slist_free(server->channels);
+       g_slist_free(server->queries);
+
+       return found;
+}
+
+void server_disconnect(SERVER_REC *server)
+{
+       int chans;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       if (server->disconnected)
+               return;
+
+       if (server->connect_tag != -1) {
+               /* still connecting to server.. */
+               if (server->connect_pid != -1)
+                       net_disconnect_nonblock(server->connect_pid);
+               server_connect_failed(server, NULL);
+               return;
+       }
+
+       servers = g_slist_remove(servers, server);
+
+       server->disconnected = TRUE;
+       signal_emit("server disconnected", 1, server);
+
+       /* close all channels */
+       chans = server_remove_channels(server);
+
+       if (server->handle != NULL) {
+               if (!chans || server->connection_lost)
+                       net_sendbuffer_destroy(server->handle, TRUE);
+               else {
+                       /* we were on some channels, try to let the server
+                          disconnect so that our quit message is guaranteed
+                          to get displayed */
+                       net_disconnect_later(net_sendbuffer_handle(server->handle));
+                       net_sendbuffer_destroy(server->handle, FALSE);
+               }
+               server->handle = NULL;
+       }
+
+       if (server->readtag > 0) {
+               g_source_remove(server->readtag);
+               server->readtag = -1;
+       }
+
+       server_unref(server);
+}
+
+void server_ref(SERVER_REC *server)
+{
+       g_return_if_fail(IS_SERVER(server));
+
+       server->refcount++;
+}
+
+int server_unref(SERVER_REC *server)
+{
+       g_return_val_if_fail(IS_SERVER(server), FALSE);
+
+       if (--server->refcount > 0)
+               return TRUE;
+
+       if (g_slist_find(servers, server) != NULL) {
+               g_warning("Non-referenced server wasn't disconnected");
+               server_disconnect(server);
+               return TRUE;
+       }
+
+        MODULE_DATA_DEINIT(server);
+       server_connect_unref(server->connrec);
+       if (server->rawlog != NULL) rawlog_destroy(server->rawlog);
+       if (server->buffer != NULL) line_split_free(server->buffer);
+       g_free(server->version);
+       g_free(server->away_reason);
+       g_free(server->nick);
+       g_free(server->tag);
+
+       server->type = 0;
+       g_free(server);
+        return FALSE;
+}
+
+SERVER_REC *server_find_tag(const char *tag)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(tag != NULL, NULL);
+       if (*tag == '\0') return NULL;
+
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *server = tmp->data;
+
+               if (g_strcasecmp(server->tag, tag) == 0)
+                       return server;
+       }
+
+       for (tmp = lookup_servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *server = tmp->data;
+
+               if (g_strcasecmp(server->tag, tag) == 0)
+                       return server;
+       }
+
+       return NULL;
+}
+
+SERVER_REC *server_find_chatnet(const char *chatnet)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(chatnet != NULL, NULL);
+       if (*chatnet == '\0') return NULL;
+
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *server = tmp->data;
+
+               if (server->connrec->chatnet != NULL &&
+                   g_strcasecmp(server->connrec->chatnet, chatnet) == 0)
+                       return server;
+       }
+
+       return NULL;
+}
+
+void server_connect_ref(SERVER_CONNECT_REC *conn)
+{
+        conn->refcount++;
+}
+
+void server_connect_unref(SERVER_CONNECT_REC *conn)
+{
+       g_return_if_fail(IS_SERVER_CONNECT(conn));
+
+       if (--conn->refcount > 0)
+               return;
+       if (conn->refcount < 0) {
+               g_warning("Connection '%s' refcount = %d",
+                         conn->tag, conn->refcount);
+       }
+
+        CHAT_PROTOCOL(conn)->destroy_server_connect(conn);
+
+       g_free_not_null(conn->proxy);
+       g_free_not_null(conn->proxy_string);
+       g_free_not_null(conn->proxy_string_after);
+       g_free_not_null(conn->proxy_password);
+
+       g_free_not_null(conn->tag);
+       g_free_not_null(conn->address);
+       g_free_not_null(conn->chatnet);
+
+       g_free_not_null(conn->own_ip4);
+       g_free_not_null(conn->own_ip6);
+
+        g_free_not_null(conn->password);
+        g_free_not_null(conn->nick);
+        g_free_not_null(conn->username);
+       g_free_not_null(conn->realname);
+
+       g_free_not_null(conn->channels);
+        g_free_not_null(conn->away_reason);
+
+        conn->type = 0;
+       g_free(conn);
+}
+
+void server_change_nick(SERVER_REC *server, const char *nick)
+{
+       g_free(server->nick);
+       server->nick = g_strdup(nick);
+
+       signal_emit("server nick changed", 1, server);
+}
+
+/* Update own IPv4 and IPv6 records */
+void server_connect_own_ip_save(SERVER_CONNECT_REC *conn,
+                               IPADDR *ip4, IPADDR *ip6)
+{
+       if (ip4 == NULL || ip4->family == 0)
+               g_free_and_null(conn->own_ip4);
+       if (ip6 == NULL || ip6->family == 0)
+               g_free_and_null(conn->own_ip6);
+
+       if (ip4 != NULL && ip4->family != 0) {
+               /* IPv4 address was found */
+               if (conn->own_ip4 == NULL)
+                       conn->own_ip4 = g_new0(IPADDR, 1);
+               memcpy(conn->own_ip4, ip4, sizeof(IPADDR));
+       }
+
+       if (ip6 != NULL && ip6->family != 0) {
+               /* IPv6 address was found */
+               if (conn->own_ip6 == NULL)
+                       conn->own_ip6 = g_new0(IPADDR, 1);
+               memcpy(conn->own_ip6, ip6, sizeof(IPADDR));
+       }
+}
+
+/* `optlist' should contain only one unknown key - the server tag.
+   returns NULL if there was unknown -option */
+SERVER_REC *cmd_options_get_server(const char *cmd,
+                                  GHashTable *optlist,
+                                  SERVER_REC *defserver)
+{
+       SERVER_REC *server;
+       GSList *list, *tmp, *next;
+
+       /* get all the options, then remove the known ones. there should
+          be only one left - the server tag. */
+       list = hashtable_get_keys(optlist);
+       if (cmd != NULL) {
+               for (tmp = list; tmp != NULL; tmp = next) {
+                       char *option = tmp->data;
+                       next = tmp->next;
+
+                       if (command_have_option(cmd, option))
+                               list = g_slist_remove(list, option);
+               }
+       }
+
+       if (list == NULL)
+               return defserver;
+
+       server = server_find_tag(list->data);
+       if (server == NULL || list->next != NULL) {
+               /* unknown option (not server tag) */
+               signal_emit("error command", 2,
+                           GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN),
+                           server == NULL ? list->data : list->next->data);
+               signal_stop();
+
+               server = NULL;
+       }
+
+       g_slist_free(list);
+       return server;
+}
+
+static void disconnect_servers(GSList *servers, int chat_type)
+{
+       GSList *tmp, *next;
+
+       for (tmp = servers; tmp != NULL; tmp = next) {
+               SERVER_REC *rec = tmp->data;
+
+                next = tmp->next;
+                if (rec->chat_type == chat_type)
+                       server_disconnect(rec);
+       }
+}
+
+static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
+{
+        disconnect_servers(servers, proto->id);
+        disconnect_servers(lookup_servers, proto->id);
+}
+
+void servers_init(void)
+{
+       settings_add_bool("server", "resolve_prefer_ipv6", FALSE);
+       lookup_servers = servers = NULL;
+
+       signal_add("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
+
+       servers_reconnect_init();
+       servers_setup_init();
+}
+
+void servers_deinit(void)
+{
+       signal_remove("chat protocol deinit", (SIGNAL_FUNC) sig_chat_protocol_deinit);
+
+       servers_setup_deinit();
+       servers_reconnect_deinit();
+
+       module_uniq_destroy("SERVER");
+       module_uniq_destroy("SERVER CONNECT");
+}
diff --git a/apps/irssi/src/core/servers.h b/apps/irssi/src/core/servers.h
new file mode 100644 (file)
index 0000000..dddde26
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef __SERVERS_H
+#define __SERVERS_H
+
+#include "modules.h"
+
+/* Returns SERVER_REC if it's server, NULL if it isn't. */
+#define SERVER(server) \
+       MODULE_CHECK_CAST(server, SERVER_REC, type, "SERVER")
+
+/* Returns SERVER_CONNECT_REC if it's server connection, NULL if it isn't. */
+#define SERVER_CONNECT(conn) \
+       MODULE_CHECK_CAST(conn, SERVER_CONNECT_REC, type, "SERVER CONNECT")
+
+#define IS_SERVER(server) \
+       (SERVER(server) ? TRUE : FALSE)
+
+#define IS_SERVER_CONNECT(conn) \
+       (SERVER_CONNECT(conn) ? TRUE : FALSE)
+
+#define server_ischannel(server, channel) \
+        (server)->ischannel(server, channel)
+
+/* all strings should be either NULL or dynamically allocated */
+/* address and nick are mandatory, rest are optional */
+struct _SERVER_CONNECT_REC {
+#include "server-connect-rec.h"
+};
+
+#define STRUCT_SERVER_CONNECT_REC SERVER_CONNECT_REC
+struct _SERVER_REC {
+#include "server-rec.h"
+};
+
+#define SEND_TARGET_CHANNEL    0
+#define SEND_TARGET_NICK       1
+
+extern GSList *servers, *lookup_servers;
+
+void servers_init(void);
+void servers_deinit(void);
+
+/* Disconnect from server */
+void server_disconnect(SERVER_REC *server);
+
+void server_ref(SERVER_REC *server);
+int server_unref(SERVER_REC *server);
+
+SERVER_REC *server_find_tag(const char *tag);
+SERVER_REC *server_find_chatnet(const char *chatnet);
+
+/* starts connecting to server */
+int server_start_connect(SERVER_REC *server);
+void server_connect_ref(SERVER_CONNECT_REC *conn);
+void server_connect_unref(SERVER_CONNECT_REC *conn);
+
+/* initializes server record but doesn't start connecting */
+void server_connect_init(SERVER_REC *server);
+/* Connection to server finished, fill the rest of the fields */
+void server_connect_finished(SERVER_REC *server);
+/* connection to server failed */
+void server_connect_failed(SERVER_REC *server, const char *msg);
+
+/* Change your nick */
+void server_change_nick(SERVER_REC *server, const char *nick);
+
+/* Update own IPv4 and IPv6 records */
+void server_connect_own_ip_save(SERVER_CONNECT_REC *conn,
+                               IPADDR *ip4, IPADDR *ip6);
+
+/* `optlist' should contain only one unknown key - the server tag.
+   returns NULL if there was unknown -option */
+SERVER_REC *cmd_options_get_server(const char *cmd,
+                                  GHashTable *optlist,
+                                  SERVER_REC *defserver);
+
+#endif
diff --git a/apps/irssi/src/core/session.c b/apps/irssi/src/core/session.c
new file mode 100644 (file)
index 0000000..0248f13
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ session.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "args.h"
+#include "net-sendbuffer.h"
+#include "pidwait.h"
+#include "lib-config/iconfig.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "nicklist.h"
+
+static char *session_file;
+static char *irssi_binary;
+
+static char **session_args;
+
+void session_set_binary(const char *path)
+{
+       char **paths, **tmp;
+        char *str;
+
+       g_free_and_null(irssi_binary);
+
+       if (g_path_is_absolute(path)) {
+                /* full path - easy */
+               irssi_binary = g_strdup(path);
+                return;
+       }
+
+       if (strchr(path, G_DIR_SEPARATOR) != NULL) {
+               /* relative path */
+                str = g_get_current_dir();
+               irssi_binary = g_strconcat(str, G_DIR_SEPARATOR_S, path, NULL);
+               g_free(str);
+                return;
+       }
+
+       /* we'll need to find it from path. */
+       str = g_getenv("PATH");
+       if (str == NULL) return;
+
+       paths = g_strsplit(str, ":", -1);
+       for (tmp = paths; *tmp != NULL; tmp++) {
+                str = g_strconcat(*tmp, G_DIR_SEPARATOR_S, path, NULL);
+               if (access(str, X_OK) == 0) {
+                       irssi_binary = str;
+                        break;
+               }
+                g_free(str);
+       }
+       g_strfreev(paths);
+}
+
+void session_upgrade(void)
+{
+       if (session_args == NULL)
+                return;
+
+       execvp(session_args[0], session_args);
+       fprintf(stderr, "exec failed: %s: %s\n",
+               session_args[0], g_strerror(errno));
+}
+
+/* SYNTAX: UPGRADE [<irssi binary path>] */
+static void cmd_upgrade(const char *data)
+{
+       CONFIG_REC *session;
+       char *session_file, *str;
+
+       if (*data == '\0')
+               data = irssi_binary;
+       if (data == NULL)
+                cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       /* save the session */
+        session_file = g_strdup_printf("%s/session", get_irssi_dir());
+       session = config_open(session_file, 0600);
+        unlink(session_file);
+
+       signal_emit("session save", 1, session);
+        config_write(session, NULL, -1);
+        config_close(session);
+
+       /* data may contain some other program as well, like
+          /UPGRADE /usr/bin/screen irssi */
+       str = g_strdup_printf("%s --noconnect --session=%s --home=%s --config=%s",
+                             data, session_file, get_irssi_dir(), get_irssi_config());
+        session_args = g_strsplit(str, " ", -1);
+        g_free(str);
+
+       signal_emit("gui exit", 0);
+}
+
+static void session_save_nick(CHANNEL_REC *channel, NICK_REC *nick,
+                             CONFIG_REC *config, CONFIG_NODE *node)
+{
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       config_node_set_str(config, node, "nick", nick->nick);
+       config_node_set_bool(config, node, "op", nick->op);
+       config_node_set_bool(config, node, "halfop", nick->halfop);
+       config_node_set_bool(config, node, "voice", nick->voice);
+
+       signal_emit("session save nick", 4, channel, nick, config, node);
+}
+
+static void session_save_channel_nicks(CHANNEL_REC *channel, CONFIG_REC *config,
+                                      CONFIG_NODE *node)
+{
+       GSList *tmp, *nicks;
+
+       node = config_node_section(node, "nicks", NODE_TYPE_LIST);
+        nicks = nicklist_getnicks(channel);
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next)
+               session_save_nick(channel, tmp->data, config, node);
+        g_slist_free(nicks);
+}
+
+static void session_save_channel(CHANNEL_REC *channel, CONFIG_REC *config,
+                                CONFIG_NODE *node)
+{
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       config_node_set_str(config, node, "name", channel->name);
+       config_node_set_str(config, node, "topic", channel->topic);
+       config_node_set_str(config, node, "key", channel->key);
+
+       signal_emit("session save channel", 3, channel, config, node);
+}
+
+static void session_save_server_channels(SERVER_REC *server,
+                                        CONFIG_REC *config,
+                                        CONFIG_NODE *node)
+{
+       GSList *tmp;
+
+       /* save channels */
+        node = config_node_section(node, "channels", NODE_TYPE_LIST);
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next)
+               session_save_channel(tmp->data, config, node);
+}
+
+static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
+                               CONFIG_NODE *node)
+{
+       int handle;
+
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+       config_node_set_str(config, node, "chat_type",
+                           chat_protocol_find_id(server->chat_type)->name);
+       config_node_set_str(config, node, "address", server->connrec->address);
+       config_node_set_int(config, node, "port", server->connrec->port);
+       config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
+       config_node_set_str(config, node, "password", server->connrec->password);
+       config_node_set_str(config, node, "nick", server->nick);
+
+       handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
+       config_node_set_int(config, node, "handle", handle);
+
+       signal_emit("session save server", 3, server, config, node);
+
+       /* fake the server disconnection */
+       g_io_channel_unref(net_sendbuffer_handle(server->handle));
+       net_sendbuffer_destroy(server->handle, FALSE);
+       server->handle = NULL;
+
+       server->connection_lost = TRUE;
+        server->no_reconnect = TRUE;
+        server_disconnect(server);
+}
+
+static void session_restore_channel_nicks(CHANNEL_REC *channel,
+                                         CONFIG_NODE *node)
+{
+       GSList *tmp;
+
+       /* restore nicks */
+       node = config_node_section(node, "nicks", -1);
+       if (node != NULL && node->type == NODE_TYPE_LIST) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp)) {
+                       signal_emit("session restore nick", 2,
+                                   channel, tmp->data);
+               }
+       }
+}
+
+static void session_restore_channel(SERVER_REC *server, CONFIG_NODE *node)
+{
+        CHANNEL_REC *channel;
+       const char *name;
+
+       name = config_node_get_str(node, "name", NULL);
+       if (name == NULL)
+               return;
+
+       channel = CHAT_PROTOCOL(server)->channel_create(server, name, TRUE);
+       channel->topic = g_strdup(config_node_get_str(node, "topic", NULL));
+        channel->key = g_strdup(config_node_get_str(node, "key", NULL));
+        channel->session_rejoin = TRUE;
+
+       signal_emit("session restore channel", 2, channel, node);
+}
+
+static void session_restore_server_channels(SERVER_REC *server,
+                                           CONFIG_NODE *node)
+{
+       GSList *tmp;
+
+       /* restore channels */
+       node = config_node_section(node, "channels", -1);
+       if (node != NULL && node->type == NODE_TYPE_LIST) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       session_restore_channel(server, tmp->data);
+       }
+}
+
+static void session_restore_server(CONFIG_NODE *node)
+{
+       CHAT_PROTOCOL_REC *proto;
+       SERVER_CONNECT_REC *conn;
+       SERVER_REC *server;
+       const char *chat_type, *address, *chatnet, *password, *nick;
+        int port, handle;
+
+        chat_type = config_node_get_str(node, "chat_type", NULL);
+       address = config_node_get_str(node, "address", NULL);
+       port = config_node_get_int(node, "port", 0);
+       chatnet = config_node_get_str(node, "chatnet", NULL);
+       password = config_node_get_str(node, "password", NULL);
+       nick = config_node_get_str(node, "nick", NULL);
+       handle = config_node_get_int(node, "handle", -1);
+
+       if (chat_type == NULL || address == NULL || nick == NULL || handle < 0)
+               return;
+
+       proto = chat_protocol_find(chat_type);
+       if (proto == NULL || proto->not_initialized) {
+               if (handle < 0) close(handle);
+               return;
+       }
+
+       conn = server_create_conn(proto->id, address, port,
+                                 chatnet, password, nick);
+       if (conn != NULL) {
+               conn->reconnection = TRUE;
+
+               server = proto->server_connect(conn);
+                server->handle = net_sendbuffer_create(g_io_channel_unix_new(handle), 0);
+               server->session_reconnect = TRUE;
+
+               signal_emit("session restore server", 2, server, node);
+       }
+}
+
+static void sig_session_save(CONFIG_REC *config)
+{
+       CONFIG_NODE *node;
+       GSList *tmp;
+        GString *str;
+
+        /* save servers */
+       node = config_node_traverse(config, "(servers", TRUE);
+       while (servers != NULL)
+               session_save_server(servers->data, config, node);
+
+       /* save pids */
+        str = g_string_new(NULL);
+       for (tmp = pidwait_get_pids(); tmp != NULL; tmp = tmp->next)
+                g_string_sprintfa(str, "%d ", GPOINTER_TO_INT(tmp->data));
+        config_node_set_str(config, config->mainnode, "pids", str->str);
+        g_string_free(str, TRUE);
+}
+
+static void sig_session_restore(CONFIG_REC *config)
+{
+       CONFIG_NODE *node;
+        GSList *tmp;
+        char **pids, **pid;
+
+        /* restore servers */
+       node = config_node_traverse(config, "(servers", FALSE);
+       if (node != NULL) {
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       session_restore_server(tmp->data);
+       }
+
+       /* restore pids (so we don't leave zombies) */
+       pids = g_strsplit(config_node_get_str(config->mainnode, "pids", ""), " ", -1);
+       for (pid = pids; *pid != NULL; pid++)
+                pidwait_add(atoi(*pid));
+        g_strfreev(pids);
+}
+
+static void sig_init_finished(void)
+{
+       CONFIG_REC *session;
+
+       if (session_file == NULL)
+               return;
+
+       session = config_open(session_file, -1);
+       if (session == NULL)
+               return;
+
+       config_parse(session);
+        signal_emit("session restore", 1, session);
+       config_close(session);
+
+       unlink(session_file);
+       session_file = NULL;
+}
+
+void session_init(void)
+{
+       static struct poptOption options[] = {
+               { "session", 0, POPT_ARG_STRING, &session_file, 0, "Used by /UPGRADE command", "PATH" },
+               { NULL, '\0', 0, NULL }
+       };
+
+        session_file = NULL;
+       args_register(options);
+
+       command_bind("upgrade", NULL, (SIGNAL_FUNC) cmd_upgrade);
+
+       signal_add("session save", (SIGNAL_FUNC) sig_session_save);
+       signal_add("session restore", (SIGNAL_FUNC) sig_session_restore);
+       signal_add("session save server", (SIGNAL_FUNC) session_save_server_channels);
+       signal_add("session restore server", (SIGNAL_FUNC) session_restore_server_channels);
+       signal_add("session save channel", (SIGNAL_FUNC) session_save_channel_nicks);
+       signal_add("session restore channel", (SIGNAL_FUNC) session_restore_channel_nicks);
+       signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+}
+
+void session_deinit(void)
+{
+       g_free_not_null(irssi_binary);
+
+        command_unbind("upgrade", (SIGNAL_FUNC) cmd_upgrade);
+
+       signal_remove("session save", (SIGNAL_FUNC) sig_session_save);
+       signal_remove("session restore", (SIGNAL_FUNC) sig_session_restore);
+       signal_remove("session save server", (SIGNAL_FUNC) session_save_server_channels);
+       signal_remove("session restore server", (SIGNAL_FUNC) session_restore_server_channels);
+       signal_remove("session save channel", (SIGNAL_FUNC) session_save_channel_nicks);
+       signal_remove("session restore channel", (SIGNAL_FUNC) session_restore_channel_nicks);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+}
diff --git a/apps/irssi/src/core/session.h b/apps/irssi/src/core/session.h
new file mode 100644 (file)
index 0000000..5788b5f
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __SESSION_H
+#define __SESSION_H
+
+void session_set_binary(const char *path);
+void session_upgrade(void);
+
+void session_init(void);
+void session_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/settings.c b/apps/irssi/src/core/settings.c
new file mode 100644 (file)
index 0000000..d99757e
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ settings.c : Irssi settings
+
+    Copyright (C) 1999 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+
+#include "lib-config/iconfig.h"
+#include "settings.h"
+#include "default-config.h"
+
+#include <signal.h>
+
+CONFIG_REC *mainconfig;
+
+static GString *last_errors;
+static GSList *last_invalid_modules;
+static int fe_initialized;
+static int config_changed; /* FIXME: remove after .98 (unless needed again) */
+
+static GHashTable *settings;
+static int timeout_tag;
+
+static int config_last_modifycounter;
+static time_t config_last_mtime;
+static long config_last_size;
+static unsigned int config_last_checksum;
+
+static SETTINGS_REC *settings_find(const char *key)
+{
+       SETTINGS_REC *rec;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       rec = g_hash_table_lookup(settings, key);
+       if (rec == NULL) {
+               g_warning("settings_get_default_str(%s) : "
+                         "unknown setting", key);
+               return NULL;
+       }
+
+       return rec;
+}
+
+const char *settings_get_str(const char *key)
+{
+       SETTINGS_REC *rec;
+       CONFIG_NODE *setnode, *node;
+
+       rec = settings_find(key);
+        g_return_val_if_fail(rec != NULL, NULL);
+
+       setnode = iconfig_node_traverse("settings", FALSE);
+       if (setnode == NULL)
+               return rec->def;
+
+       node = config_node_section(setnode, rec->module, -1);
+       return node == NULL ? rec->def :
+               config_node_get_str(node, key, rec->def);
+}
+
+int settings_get_int(const char *key)
+{
+       SETTINGS_REC *rec;
+       CONFIG_NODE *setnode, *node;
+        int def;
+
+       rec = settings_find(key);
+        g_return_val_if_fail(rec != NULL, 0);
+        def = GPOINTER_TO_INT(rec->def);
+
+       setnode = iconfig_node_traverse("settings", FALSE);
+       if (setnode == NULL)
+               return def;
+
+       node = config_node_section(setnode, rec->module, -1);
+       return node == NULL ? def :
+               config_node_get_int(node, key, def);
+}
+
+int settings_get_bool(const char *key)
+{
+       SETTINGS_REC *rec;
+       CONFIG_NODE *setnode, *node;
+        int def;
+
+       rec = settings_find(key);
+        g_return_val_if_fail(rec != NULL, 0);
+        def = GPOINTER_TO_INT(rec->def);
+
+       setnode = iconfig_node_traverse("settings", FALSE);
+       if (setnode == NULL)
+               return def;
+
+       node = config_node_section(setnode, rec->module, -1);
+       return node == NULL ? def :
+               config_node_get_bool(node, key, def);
+}
+
+void settings_add_str_module(const char *module, const char *section,
+                            const char *key, const char *def)
+{
+       SETTINGS_REC *rec;
+
+       g_return_if_fail(key != NULL);
+       g_return_if_fail(section != NULL);
+
+       rec = g_hash_table_lookup(settings, key);
+       g_return_if_fail(rec == NULL);
+
+       rec = g_new0(SETTINGS_REC, 1);
+        rec->module = g_strdup(module);
+       rec->key = g_strdup(key);
+       rec->section = g_strdup(section);
+       rec->def = def == NULL ? NULL : g_strdup(def);
+
+       g_hash_table_insert(settings, rec->key, rec);
+}
+
+void settings_add_int_module(const char *module, const char *section,
+                            const char *key, int def)
+{
+       SETTINGS_REC *rec;
+
+       g_return_if_fail(key != NULL);
+       g_return_if_fail(section != NULL);
+
+       rec = g_hash_table_lookup(settings, key);
+       g_return_if_fail(rec == NULL);
+
+       rec = g_new0(SETTINGS_REC, 1);
+        rec->module = g_strdup(module);
+       rec->type = SETTING_TYPE_INT;
+       rec->key = g_strdup(key);
+       rec->section = g_strdup(section);
+       rec->def = GINT_TO_POINTER(def);
+
+       g_hash_table_insert(settings, rec->key, rec);
+}
+
+void settings_add_bool_module(const char *module, const char *section,
+                             const char *key, int def)
+{
+       SETTINGS_REC *rec;
+
+       g_return_if_fail(key != NULL);
+       g_return_if_fail(section != NULL);
+
+       rec = g_hash_table_lookup(settings, key);
+       g_return_if_fail(rec == NULL);
+
+       rec = g_new0(SETTINGS_REC, 1);
+        rec->module = g_strdup(module);
+       rec->type = SETTING_TYPE_BOOLEAN;
+       rec->key = g_strdup(key);
+       rec->section = g_strdup(section);
+       rec->def = GINT_TO_POINTER(def);
+
+       g_hash_table_insert(settings, rec->key, rec);
+}
+
+static void settings_destroy(SETTINGS_REC *rec)
+{
+       if (rec->type == SETTING_TYPE_STRING)
+               g_free_not_null(rec->def);
+        g_free(rec->module);
+        g_free(rec->section);
+        g_free(rec->key);
+       g_free(rec);
+}
+
+void settings_remove(const char *key)
+{
+       SETTINGS_REC *rec;
+
+       g_return_if_fail(key != NULL);
+
+       rec = g_hash_table_lookup(settings, key);
+       if (rec == NULL) return;
+
+       g_hash_table_remove(settings, key);
+       settings_destroy(rec);
+}
+
+static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
+                               const char *module)
+{
+       if (strcmp(rec->module, module) == 0) {
+               settings_destroy(rec);
+                return TRUE;
+       }
+
+        return FALSE;
+}
+
+void settings_remove_module(const char *module)
+{
+       g_hash_table_foreach_remove(settings,
+                                   (GHRFunc) settings_remove_hash,
+                                   (void *) module);
+}
+
+static CONFIG_NODE *settings_get_node(const char *key)
+{
+       SETTINGS_REC *rec;
+        CONFIG_NODE *node;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       rec = g_hash_table_lookup(settings, key);
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       node = iconfig_node_traverse("settings", TRUE);
+       return config_node_section(node, rec->module, NODE_TYPE_BLOCK);
+}
+
+void settings_set_str(const char *key, const char *value)
+{
+        iconfig_node_set_str(settings_get_node(key), key, value);
+}
+
+void settings_set_int(const char *key, int value)
+{
+        iconfig_node_set_int(settings_get_node(key), key, value);
+}
+
+void settings_set_bool(const char *key, int value)
+{
+        iconfig_node_set_bool(settings_get_node(key), key, value);
+}
+
+int settings_get_type(const char *key)
+{
+       SETTINGS_REC *rec;
+
+       g_return_val_if_fail(key != NULL, -1);
+
+       rec = g_hash_table_lookup(settings, key);
+       return rec == NULL ? -1 : rec->type;
+}
+
+/* Get the record of the setting */
+SETTINGS_REC *settings_get_record(const char *key)
+{
+       g_return_val_if_fail(key != NULL, NULL);
+
+       return g_hash_table_lookup(settings, key);
+}
+
+static void sig_init_finished(void)
+{
+       fe_initialized = TRUE;
+       if (last_errors != NULL) {
+               signal_emit("settings errors", 1, last_errors->str);
+               g_string_free(last_errors, TRUE);
+       }
+
+       if (config_changed) {
+               /* some backwards compatibility changes were made to
+                  config file, reload it */
+               signal_emit("setup changed", 0);
+       }
+}
+
+static void settings_clean_invalid_module(const char *module)
+{
+        CONFIG_NODE *node;
+        SETTINGS_REC *set;
+       GSList *tmp, *next;
+
+       node = iconfig_node_traverse("settings", FALSE);
+       if (node == NULL) return;
+
+       node = config_node_section(node, module, -1);
+       if (node == NULL) return;
+
+       for (tmp = config_node_first(node->value); tmp != NULL; tmp = next) {
+               CONFIG_NODE *subnode = tmp->data;
+                next = config_node_next(tmp);
+
+               set = g_hash_table_lookup(settings, subnode->key);
+               if (set == NULL || strcmp(set->module, module) != 0)
+                        iconfig_node_remove(node, subnode);
+       }
+}
+
+/* remove all invalid settings from config file. works only with the
+   modules that have already called settings_check() */
+void settings_clean_invalid(void)
+{
+       while (last_invalid_modules != NULL) {
+               char *module = last_invalid_modules->data;
+
+                settings_clean_invalid_module(module);
+
+                g_free(module);
+               last_invalid_modules =
+                       g_slist_remove(last_invalid_modules, module);
+       }
+}
+
+/* verify that all settings in config file for `module' are actually found
+   from /SET list */
+void settings_check_module(const char *module)
+{
+        SETTINGS_REC *set;
+       CONFIG_NODE *node;
+        GString *errors;
+       GSList *tmp;
+        int count;
+
+        g_return_if_fail(module != NULL);
+
+       node = iconfig_node_traverse("settings", FALSE);
+       node = node == NULL ? NULL : config_node_section(node, module, -1);
+       if (node == NULL) return;
+
+        errors = g_string_new(NULL);
+       g_string_sprintf(errors, "Unknown settings in configuration "
+                        "file for module %s:", module);
+
+        count = 0;
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               set = g_hash_table_lookup(settings, node->key);
+               if (set == NULL || strcmp(set->module, module) != 0) {
+                       g_string_sprintfa(errors, " %s", node->key);
+                        count++;
+               }
+       }
+       if (count > 0) {
+               if (gslist_find_icase_string(last_invalid_modules,
+                                            module) == NULL) {
+                        /* mark this module having invalid settings */
+                       last_invalid_modules =
+                               g_slist_append(last_invalid_modules,
+                                              g_strdup(module));
+               }
+               if (fe_initialized)
+                        signal_emit("settings errors", 1, errors->str);
+               else {
+                       if (last_errors == NULL)
+                               last_errors = g_string_new(NULL);
+                       else
+                               g_string_append_c(last_errors, '\n');
+                        g_string_append(last_errors, errors->str);
+               }
+       }
+        g_string_free(errors, TRUE);
+}
+
+static int settings_compare(SETTINGS_REC *v1, SETTINGS_REC *v2)
+{
+       return strcmp(v1->section, v2->section);
+}
+
+static void settings_hash_get(const char *key, SETTINGS_REC *rec,
+                             GSList **list)
+{
+       *list = g_slist_insert_sorted(*list, rec,
+                                     (GCompareFunc) settings_compare);
+}
+
+GSList *settings_get_sorted(void)
+{
+       GSList *list;
+
+       list = NULL;
+       g_hash_table_foreach(settings, (GHFunc) settings_hash_get, &list);
+       return list;
+}
+
+void sig_term(int n)
+{
+       /* if we get SIGTERM after this, just die instead of coming back here. */
+       signal(SIGTERM, SIG_DFL);
+
+       /* quit from all servers too.. */
+       signal_emit("command quit", 1, "");
+
+       /* and die */
+       raise(SIGTERM);
+}
+
+/* Yes, this is my own stupid checksum generator, some "real" algorithm
+   would be nice but would just take more space without much real benefit */
+static unsigned int file_checksum(const char *fname)
+{
+        char buf[512];
+        int f, ret, n;
+       unsigned int checksum = 0;
+
+       f = open(fname, O_RDONLY);
+       if (f == -1) return 0;
+
+        n = 0;
+       while ((ret = read(f, buf, sizeof(buf))) > 0) {
+               while (ret-- > 0)
+                       checksum += buf[ret] << ((n++ & 3)*8);
+       }
+       close(f);
+       return checksum;
+}
+
+static void irssi_config_save_state(const char *fname)
+{
+       struct stat statbuf;
+
+       g_return_if_fail(fname != NULL);
+
+       if (stat(fname, &statbuf) != 0)
+               return;
+
+       /* save modify time, file size and checksum */
+       config_last_mtime = statbuf.st_mtime;
+       config_last_size = statbuf.st_size;
+       config_last_checksum = file_checksum(fname);
+}
+
+int irssi_config_is_changed(const char *fname)
+{
+       struct stat statbuf;
+
+       if (fname == NULL)
+               fname = mainconfig->fname;
+
+       if (stat(fname, &statbuf) != 0)
+               return FALSE;
+
+       return config_last_mtime != statbuf.st_mtime &&
+               (config_last_size != statbuf.st_size ||
+                config_last_checksum != file_checksum(fname));
+}
+
+static CONFIG_REC *parse_configfile(const char *fname)
+{
+       CONFIG_REC *config;
+       struct stat statbuf;
+        const char *path;
+       char *str;
+
+       if (fname == NULL)
+               fname = get_irssi_config();
+
+       if (stat(fname, &statbuf) == 0)
+               path = fname;
+       else {
+               /* user configuration file not found, use the default one
+                  from sysconfdir */
+                path = SYSCONFDIR"/"IRSSI_GLOBAL_CONFIG;
+               if (stat(path, &statbuf) != 0) {
+                       /* no configuration file in sysconfdir ..
+                          use the build-in configuration */
+                        path = NULL;
+               }
+       }
+
+       config = config_open(path, -1);
+       if (config == NULL) {
+               str = g_strdup_printf("Error opening configuration file %s: %s",
+                                     path, g_strerror(errno));
+               signal_emit("gui dialog", 2, "error", str);
+                g_free(str);
+
+               config = config_open(NULL, -1);
+       }
+
+        if (path != NULL)
+               config_parse(config);
+        else
+               config_parse_data(config, default_config, "internal");
+
+       config_change_file_name(config, fname, 0660);
+        irssi_config_save_state(fname);
+       return config;
+}
+
+static void init_configfile(void)
+{
+       struct stat statbuf;
+       char *str;
+
+       if (stat(get_irssi_dir(), &statbuf) != 0) {
+               /* ~/.irssi not found, create it. */
+               if (mkpath(get_irssi_dir(), 0700) != 0) {
+                       g_error("Couldn't create %s directory", get_irssi_dir());
+               }
+       } else if (!S_ISDIR(statbuf.st_mode)) {
+               g_error("%s is not a directory.\n"
+                       "You should remove it with command: rm %s",
+                       get_irssi_dir(), get_irssi_dir());
+       }
+
+       mainconfig = parse_configfile(NULL);
+       config_last_modifycounter = mainconfig->modifycounter;
+
+       /* any errors? */
+       if (config_last_error(mainconfig) != NULL) {
+               str = g_strdup_printf("Ignored errors in configuration file:\n%s",
+                                     config_last_error(mainconfig));
+               signal_emit("gui dialog", 2, "error", str);
+                g_free(str);
+       }
+
+       signal(SIGTERM, sig_term);
+}
+
+int settings_reread(const char *fname)
+{
+       CONFIG_REC *tempconfig;
+       char *str;
+
+       str = fname == NULL ? NULL : convert_home(fname);
+       tempconfig = parse_configfile(str);
+        g_free_not_null(str);
+
+       if (tempconfig == NULL) {
+               signal_emit("gui dialog", 2, "error", g_strerror(errno));
+               return FALSE;
+       }
+
+       if (config_last_error(tempconfig) != NULL) {
+               str = g_strdup_printf("Errors in configuration file:\n%s",
+                                     config_last_error(tempconfig));
+               signal_emit("gui dialog", 2, "error", str);
+               g_free(str);
+
+               config_close(tempconfig);
+                return FALSE;
+       }
+
+       config_close(mainconfig);
+       mainconfig = tempconfig;
+       config_last_modifycounter = mainconfig->modifycounter;
+
+       signal_emit("setup changed", 0);
+       signal_emit("setup reread", 1, mainconfig->fname);
+        return TRUE;
+}
+
+int settings_save(const char *fname, int autosave)
+{
+       char *str;
+       int error;
+
+       if (fname == NULL)
+               fname = mainconfig->fname;
+
+       error = config_write(mainconfig, fname, 0660) != 0;
+       irssi_config_save_state(fname);
+       config_last_modifycounter = mainconfig->modifycounter;
+       if (error) {
+               str = g_strdup_printf("Couldn't save configuration file: %s",
+                                     config_last_error(mainconfig));
+               signal_emit("gui dialog", 2, "error", str);
+               g_free(str);
+       }
+       signal_emit("setup saved", 2, fname, GINT_TO_POINTER(autosave));
+        return !error;
+}
+
+static int sig_autosave(void)
+{
+       char *fname, *str;
+
+       if (!settings_get_bool("settings_autosave") ||
+           config_last_modifycounter == mainconfig->modifycounter)
+               return 1;
+
+       if (!irssi_config_is_changed(NULL))
+               settings_save(NULL, TRUE);
+       else {
+               fname = g_strconcat(mainconfig->fname, ".autosave", NULL);
+               str = g_strdup_printf("Configuration file was modified "
+                                     "while irssi was running. Saving "
+                                     "configuration to file '%s' instead. "
+                                     "Use /SAVE or /RELOAD to get rid of "
+                                     "this message.", fname);
+               signal_emit("gui dialog", 2, "warning", str);
+               g_free(str);
+
+                settings_save(fname, TRUE);
+               g_free(fname);
+       }
+
+        return 1;
+}
+
+void settings_init(void)
+{
+       settings = g_hash_table_new((GHashFunc) g_istr_hash,
+                                   (GCompareFunc) g_istr_equal);
+
+       last_errors = NULL;
+        last_invalid_modules = NULL;
+       fe_initialized = FALSE;
+        config_changed = FALSE;
+
+       config_last_mtime = 0;
+       config_last_modifycounter = 0;
+       init_configfile();
+
+       settings_add_bool("misc", "settings_autosave", TRUE);
+       timeout_tag = g_timeout_add(1000*60*60, (GSourceFunc) sig_autosave, NULL);
+       signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+       signal_add("gui exit", (SIGNAL_FUNC) sig_autosave);
+}
+
+static void settings_hash_free(const char *key, SETTINGS_REC *rec)
+{
+       settings_destroy(rec);
+}
+
+void settings_deinit(void)
+{
+        g_source_remove(timeout_tag);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
+       signal_remove("gui exit", (SIGNAL_FUNC) sig_autosave);
+
+       g_slist_foreach(last_invalid_modules, (GFunc) g_free, NULL);
+       g_slist_free(last_invalid_modules);
+
+       g_hash_table_foreach(settings, (GHFunc) settings_hash_free, NULL);
+       g_hash_table_destroy(settings);
+
+       if (mainconfig != NULL) config_close(mainconfig);
+}
diff --git a/apps/irssi/src/core/settings.h b/apps/irssi/src/core/settings.h
new file mode 100644 (file)
index 0000000..e0df975
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef __SETTINGS_H
+#define __SETTINGS_H
+
+enum {
+       SETTING_TYPE_STRING,
+       SETTING_TYPE_INT,
+       SETTING_TYPE_BOOLEAN
+};
+
+typedef struct {
+        char *module;
+       int type;
+       char *key;
+       char *section;
+       void *def;
+} SETTINGS_REC;
+
+/* macros for handling the default Irssi configuration */
+#define iconfig_get_str(a, b, c) config_get_str(mainconfig, a, b, c)
+#define iconfig_get_int(a, b, c) config_get_int(mainconfig, a, b, c)
+#define iconfig_get_bool(a, b, c) config_get_bool(mainconfig, a, b, c)
+#define iconfig_list_find(a, b, c, d) config_list_find(mainconfig, a, b, c, d)
+
+#define iconfig_set_str(a, b, c) config_set_str(mainconfig, a, b, c)
+#define iconfig_set_int(a, b, c) config_set_int(mainconfig, a, b, c)
+#define iconfig_set_bool(a, b, c) config_set_bool(mainconfig, a, b, c)
+
+#define iconfig_node_traverse(a, b) config_node_traverse(mainconfig, a, b)
+#define iconfig_node_set_str(a, b, c) config_node_set_str(mainconfig, a, b, c)
+#define iconfig_node_set_int(a, b, c) config_node_set_int(mainconfig, a, b, c)
+#define iconfig_node_set_bool(a, b, c) config_node_set_bool(mainconfig, a, b, c)
+#define iconfig_node_list_remove(a, b) config_node_list_remove(mainconfig, a, b)
+#define iconfig_node_remove(a, b) config_node_remove(mainconfig, a, b)
+#define iconfig_node_clear(a) config_node_clear(mainconfig, a)
+#define iconfig_node_add_list(a, b) config_node_add_list(mainconfig, a, b)
+
+extern CONFIG_REC *mainconfig;
+extern const char *default_config;
+
+/* Functions for handling the "settings" node of Irssi configuration */
+const char *settings_get_str(const char *key);
+int settings_get_int(const char *key);
+int settings_get_bool(const char *key);
+
+/* Functions to add/remove settings */
+void settings_add_str_module(const char *module, const char *section,
+                            const char *key, const char *def);
+void settings_add_int_module(const char *module, const char *section,
+                            const char *key, int def);
+void settings_add_bool_module(const char *module, const char *section,
+                             const char *key, int def);
+void settings_remove(const char *key);
+void settings_remove_module(const char *module);
+
+#define settings_add_str(section, key, def) \
+       settings_add_str_module(MODULE_NAME, section, key, def)
+#define settings_add_int(section, key, def) \
+       settings_add_int_module(MODULE_NAME, section, key, def)
+#define settings_add_bool(section, key, def) \
+       settings_add_bool_module(MODULE_NAME, section, key, def)
+
+void settings_set_str(const char *key, const char *value);
+void settings_set_int(const char *key, int value);
+void settings_set_bool(const char *key, int value);
+
+/* Get the type (SETTING_TYPE_xxx) of `key' */
+int settings_get_type(const char *key);
+/* Get all settings sorted by section. Free the result with g_slist_free() */
+GSList *settings_get_sorted(void);
+/* Get the record of the setting */
+SETTINGS_REC *settings_get_record(const char *key);
+
+/* verify that all settings in config file for `module' are actually found
+   from /SET list */
+void settings_check_module(const char *module);
+#define settings_check() settings_check_module(MODULE_NAME)
+
+/* remove all invalid settings from config file. works only with the
+   modules that have already called settings_check() */
+void settings_clean_invalid(void);
+
+/* if `fname' is NULL, the default is used */
+int settings_reread(const char *fname);
+int settings_save(const char *fname, int autosave);
+int irssi_config_is_changed(const char *fname);
+
+void settings_init(void);
+void settings_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/core/signals.c b/apps/irssi/src/core/signals.c
new file mode 100644 (file)
index 0000000..92064e8
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ signals.c : irssi
+
+    Copyright (C) 1999 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "../common.h"
+#include "signals.h"
+#include "modules.h"
+
+#define SIGNAL_LISTS 3
+
+typedef struct {
+       int id; /* signal id */
+        int refcount;
+
+       int emitting; /* signal is being emitted */
+       int stop_emit; /* this signal was stopped */
+
+       GPtrArray *modulelist[SIGNAL_LISTS]; /* list of what signals belong
+                                               to which module */
+       GPtrArray *siglist[SIGNAL_LISTS]; /* signal lists */
+} SIGNAL_REC;
+
+#define signal_is_emitlist_empty(a) \
+       (!(a)->siglist[0] && !(a)->siglist[1] && !(a)->siglist[2])
+
+static GMemChunk *signals_chunk;
+static GHashTable *signals;
+static SIGNAL_REC *current_emitted_signal;
+
+#define signal_ref(signal) ++(signal)->refcount
+#define signal_unref(rec) (signal_unref_full(rec, TRUE))
+
+static int signal_unref_full(SIGNAL_REC *rec, int remove_hash)
+{
+       if (rec->refcount == 0) {
+               g_error("signal_unref(%s) : BUG - reference counter == 0",
+                       signal_get_id_str(rec->id));
+       }
+
+       if (--rec->refcount != 0)
+               return FALSE;
+
+       /* remove whole signal from memory */
+       if (!signal_is_emitlist_empty(rec)) {
+               g_error("signal_unref(%s) : BUG - emitlist wasn't empty",
+                       signal_get_id_str(rec->id));
+       }
+
+       if (remove_hash)
+               g_hash_table_remove(signals, GINT_TO_POINTER(rec->id));
+       g_mem_chunk_free(signals_chunk, rec);
+       return TRUE;
+}
+
+static void signal_unref_count(SIGNAL_REC *rec, int count)
+{
+       while (count-- > 0)
+                signal_unref(rec);
+}
+
+void signal_add_to(const char *module, int pos,
+                  const char *signal, SIGNAL_FUNC func)
+{
+       g_return_if_fail(signal != NULL);
+
+       signal_add_to_id(module, pos, signal_get_uniq_id(signal), func);
+}
+
+/* bind a signal */
+void signal_add_to_id(const char *module, int pos,
+                     int signal_id, SIGNAL_FUNC func)
+{
+       SIGNAL_REC *rec;
+
+       g_return_if_fail(signal_id >= 0);
+       g_return_if_fail(func != NULL);
+       g_return_if_fail(pos >= 0 && pos < SIGNAL_LISTS);
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec == NULL) {
+               rec = g_mem_chunk_alloc0(signals_chunk);
+               rec->id = signal_id;
+               g_hash_table_insert(signals, GINT_TO_POINTER(signal_id), rec);
+       }
+
+       if (rec->siglist[pos] == NULL) {
+               rec->siglist[pos] = g_ptr_array_new();
+               rec->modulelist[pos] = g_ptr_array_new();
+       }
+
+       g_ptr_array_add(rec->siglist[pos], (void *) func);
+       g_ptr_array_add(rec->modulelist[pos], (void *) module);
+
+        signal_ref(rec);
+}
+
+static int signal_list_find(GPtrArray *array, void *data)
+{
+       unsigned int n;
+
+       for (n = 0; n < array->len; n++) {
+               if (g_ptr_array_index(array, n) == data)
+                       return n;
+       }
+
+       return -1;
+}
+
+static void signal_list_free(SIGNAL_REC *rec, int list)
+{
+       g_ptr_array_free(rec->siglist[list], TRUE);
+       g_ptr_array_free(rec->modulelist[list], TRUE);
+       rec->siglist[list] = NULL;
+       rec->modulelist[list] = NULL;
+}
+
+/* Returns TRUE if the whole signal is removed after this remove */
+static void signal_remove_from_list(SIGNAL_REC *rec, int list, int index)
+{
+       g_ptr_array_remove_index(rec->siglist[list], index);
+       g_ptr_array_remove_index(rec->modulelist[list], index);
+
+       if (rec->siglist[list]->len == 0)
+               signal_list_free(rec, list);
+
+       signal_unref(rec);
+}
+
+/* Remove signal from emit lists */
+static int signal_remove_from_lists(SIGNAL_REC *rec, SIGNAL_FUNC func)
+{
+       int n, index;
+
+       for (n = 0; n < SIGNAL_LISTS; n++) {
+               if (rec->siglist[n] == NULL)
+                       continue;
+
+               index = signal_list_find(rec->siglist[n], (void *) func);
+               if (index != -1) {
+                       /* remove the function from emit list */
+                       signal_remove_from_list(rec, n, index);
+                       return 1;
+               }
+       }
+
+        return 0;
+}
+
+void signal_remove_id(int signal_id, SIGNAL_FUNC func)
+{
+       SIGNAL_REC *rec;
+
+       g_return_if_fail(signal_id >= 0);
+       g_return_if_fail(func != NULL);
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+        if (rec != NULL)
+               signal_remove_from_lists(rec, func);
+}
+
+/* unbind signal */
+void signal_remove(const char *signal, SIGNAL_FUNC func)
+{
+       g_return_if_fail(signal != NULL);
+
+       signal_remove_id(signal_get_uniq_id(signal), func);
+}
+
+static int signal_emit_real(SIGNAL_REC *rec, int params, va_list va)
+{
+       gconstpointer arglist[SIGNAL_MAX_ARGUMENTS];
+        SIGNAL_REC *prev_emitted_signal;
+        SIGNAL_FUNC func;
+       int n, index, stopped, stop_emit_count;
+
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS; n++)
+               arglist[n] = n >= params ? NULL : va_arg(va, gconstpointer);
+
+       /* signal_stop_by_name("signal"); signal_emit("signal", ...);
+          fails if we compare rec->stop_emit against 0. */
+       stop_emit_count = rec->stop_emit;
+
+        signal_ref(rec);
+
+       stopped = FALSE;
+       rec->emitting++;
+       for (n = 0; n < SIGNAL_LISTS; n++) {
+               /* run signals in emit lists */
+               if (rec->siglist[n] == NULL)
+                       continue;
+
+               for (index = rec->siglist[n]->len-1; index >= 0; index--) {
+                       func = (SIGNAL_FUNC) g_ptr_array_index(rec->siglist[n], index);
+
+                       prev_emitted_signal = current_emitted_signal;
+                       current_emitted_signal = rec;
+#if SIGNAL_MAX_ARGUMENTS != 6
+#  error SIGNAL_MAX_ARGUMENTS changed - update code
+#endif
+                       func(arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5]);
+                       current_emitted_signal = prev_emitted_signal;
+
+                       if (rec->stop_emit != stop_emit_count) {
+                               stopped = TRUE;
+                               rec->stop_emit--;
+                               n = SIGNAL_LISTS;
+                               break;
+                       }
+               }
+       }
+       rec->emitting--;
+
+       if (!rec->emitting) {
+               if (rec->stop_emit != 0) {
+                       /* signal_stop() used too many times */
+                        rec->stop_emit = 0;
+               }
+       }
+
+        signal_unref(rec);
+       return stopped;
+}
+
+int signal_emit(const char *signal, int params, ...)
+{
+       SIGNAL_REC *rec;
+       va_list va;
+       int signal_id;
+
+       g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
+
+       signal_id = signal_get_uniq_id(signal);
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec != NULL) {
+               va_start(va, params);
+               signal_emit_real(rec, params, va);
+               va_end(va);
+       }
+
+       return rec != NULL;
+}
+
+int signal_emit_id(int signal_id, int params, ...)
+{
+       SIGNAL_REC *rec;
+       va_list va;
+
+       g_return_val_if_fail(signal_id >= 0, FALSE);
+       g_return_val_if_fail(params >= 0 && params <= SIGNAL_MAX_ARGUMENTS, FALSE);
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec != NULL) {
+               va_start(va, params);
+               signal_emit_real(rec, params, va);
+               va_end(va);
+       }
+
+       return rec != NULL;
+}
+
+/* stop the current ongoing signal emission */
+void signal_stop(void)
+{
+       SIGNAL_REC *rec;
+
+       rec = current_emitted_signal;
+       if (rec == NULL)
+               g_warning("signal_stop() : no signals are being emitted currently");
+       else if (rec->emitting > rec->stop_emit)
+               rec->stop_emit++;
+}
+
+/* stop ongoing signal emission by signal name */
+void signal_stop_by_name(const char *signal)
+{
+       SIGNAL_REC *rec;
+       int signal_id;
+
+       signal_id = signal_get_uniq_id(signal);
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       if (rec == NULL)
+               g_warning("signal_stop_by_name() : unknown signal \"%s\"", signal);
+       else if (rec->emitting > rec->stop_emit)
+               rec->stop_emit++;
+}
+
+/* return the name of the signal that is currently being emitted */
+const char *signal_get_emitted(void)
+{
+       return signal_get_id_str(signal_get_emitted_id());
+}
+
+/* return the ID of the signal that is currently being emitted */
+int signal_get_emitted_id(void)
+{
+       SIGNAL_REC *rec;
+
+       rec = current_emitted_signal;
+        g_return_val_if_fail(rec != NULL, -1);
+       return rec->id;
+}
+
+/* return TRUE if specified signal was stopped */
+int signal_is_stopped(int signal_id)
+{
+       SIGNAL_REC *rec;
+
+       rec = g_hash_table_lookup(signals, GINT_TO_POINTER(signal_id));
+       g_return_val_if_fail(rec != NULL, FALSE);
+
+        return rec->emitting <= rec->stop_emit;
+}
+
+static void signal_remove_module(void *signal, SIGNAL_REC *rec,
+                                const char *module)
+{
+        unsigned int index;
+       int list;
+
+       for (list = 0; list < SIGNAL_LISTS; list++) {
+               if (rec->modulelist[list] == NULL)
+                       continue;
+
+               for (index = rec->modulelist[list]->len; index > 0; index--)
+                       if (g_strcasecmp(g_ptr_array_index(rec->modulelist[list], index-1), module) == 0)
+                               signal_remove_from_list(rec, list, index-1);
+       }
+}
+
+static void signal_foreach_ref(void *signal, SIGNAL_REC *rec)
+{
+        signal_ref(rec);
+}
+
+static int signal_foreach_unref(void *signal, SIGNAL_REC *rec)
+{
+        return signal_unref_full(rec, FALSE);
+}
+
+/* remove all signals that belong to `module' */
+void signals_remove_module(const char *module)
+{
+       g_return_if_fail(module != NULL);
+
+       g_hash_table_foreach(signals, (GHFunc) signal_foreach_ref, NULL);
+       g_hash_table_foreach(signals, (GHFunc) signal_remove_module, (void *) module);
+       g_hash_table_foreach_remove(signals, (GHRFunc) signal_foreach_unref, NULL);
+}
+
+void signals_init(void)
+{
+       signals_chunk = g_mem_chunk_new("signals", sizeof(SIGNAL_REC),
+                                       sizeof(SIGNAL_REC)*200, G_ALLOC_AND_FREE);
+       signals = g_hash_table_new((GHashFunc) g_direct_hash, (GCompareFunc) g_direct_equal);
+}
+
+static void signal_free(void *key, SIGNAL_REC *rec)
+{
+       int n;
+
+        signal_ref(rec);
+
+       for (n = 0; n < SIGNAL_LISTS; n++) {
+               if (rec->siglist[n] != NULL) {
+                       signal_unref_count(rec, rec->siglist[n]->len);
+                        signal_list_free(rec, n);
+               }
+       }
+
+       if (!signal_unref_full(rec, FALSE)) {
+               g_error("signal_free(%s) : BUG - signal still has %d references",
+                       signal_get_id_str(rec->id), rec->refcount);
+       }
+}
+
+void signals_deinit(void)
+{
+       g_hash_table_foreach(signals, (GHFunc) signal_free, NULL);
+       g_hash_table_destroy(signals);
+
+       module_uniq_destroy("signals");
+       g_mem_chunk_destroy(signals_chunk);
+}
diff --git a/apps/irssi/src/core/signals.h b/apps/irssi/src/core/signals.h
new file mode 100644 (file)
index 0000000..795f732
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef __SIGNAL_H
+#define __SIGNAL_H
+
+#define SIGNAL_MAX_ARGUMENTS 6
+typedef void (*SIGNAL_FUNC) (gconstpointer, gconstpointer,
+                            gconstpointer, gconstpointer,
+                            gconstpointer, gconstpointer);
+
+void signals_init(void);
+void signals_deinit(void);
+
+/* signal name -> ID */
+#define signal_get_uniq_id(signal) \
+        module_get_uniq_id_str("signals", signal)
+/* signal ID -> name */
+#define signal_get_id_str(signal_id) \
+       module_find_id_str("signals", signal_id)
+
+/* bind a signal */
+void signal_add_to(const char *module, int pos,
+                  const char *signal, SIGNAL_FUNC func);
+void signal_add_to_id(const char *module, int pos,
+                     int signal, SIGNAL_FUNC func);
+#define signal_add(a, b) signal_add_to(MODULE_NAME, 1, a, b)
+#define signal_add_first(a, b) signal_add_to(MODULE_NAME, 0, a, b)
+#define signal_add_last(a, b) signal_add_to(MODULE_NAME, 2, a, b)
+
+/* unbind signal */
+void signal_remove(const char *signal, SIGNAL_FUNC func);
+void signal_remove_id(int signal_id, SIGNAL_FUNC func);
+
+/* emit signal */
+int signal_emit(const char *signal, int params, ...);
+int signal_emit_id(int signal_id, int params, ...);
+
+/* stop the current ongoing signal emission */
+void signal_stop(void);
+/* stop ongoing signal emission by signal name */
+void signal_stop_by_name(const char *signal);
+
+/* return the name of the signal that is currently being emitted */
+const char *signal_get_emitted(void);
+/* return the ID of the signal that is currently being emitted */
+int signal_get_emitted_id(void);
+/* return TRUE if specified signal was stopped */
+int signal_is_stopped(int signal_id);
+
+/* remove all signals that belong to `module' */
+void signals_remove_module(const char *module);
+
+#endif
diff --git a/apps/irssi/src/core/special-vars.c b/apps/irssi/src/core/special-vars.c
new file mode 100644 (file)
index 0000000..4b1f535
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ special-vars.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "special-vars.h"
+#include "expandos.h"
+#include "settings.h"
+#include "servers.h"
+#include "misc.h"
+
+#define ALIGN_RIGHT 0x01
+#define ALIGN_CUT   0x02
+#define ALIGN_PAD   0x04
+
+#define isvarchar(c) \
+        (i_isalnum(c) || (c) == '_')
+
+#define isarg(c) \
+       (i_isdigit(c) || (c) == '*' || (c) == '~' || (c) == '-')
+
+static SPECIAL_HISTORY_FUNC history_func = NULL;
+
+static char *get_argument(char **cmd, char **arglist)
+{
+       GString *str;
+       char *ret;
+       int max, arg, argcount;
+
+       arg = 0;
+       max = -1;
+
+       argcount = arglist == NULL ? 0 : strarray_length(arglist);
+
+       if (**cmd == '*') {
+               /* get all arguments */
+       } else if (**cmd == '~') {
+               /* get last argument */
+               arg = max = argcount-1;
+       } else {
+               if (i_isdigit(**cmd)) {
+                       /* first argument */
+                       arg = max = (**cmd)-'0';
+                       (*cmd)++;
+               }
+
+               if (**cmd == '-') {
+                       /* get more than one argument */
+                       (*cmd)++;
+                       if (!i_isdigit(**cmd))
+                               max = -1; /* get all the rest */
+                       else {
+                               max = (**cmd)-'0';
+                               (*cmd)++;
+                       }
+               }
+               (*cmd)--;
+       }
+
+       str = g_string_new(NULL);
+       while (arg >= 0 && arg < argcount && (arg <= max || max == -1)) {
+               g_string_append(str, arglist[arg]);
+               g_string_append_c(str, ' ');
+               arg++;
+       }
+       if (str->len > 0) g_string_truncate(str, str->len-1);
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+static char *get_internal_setting(const char *key, int type, int *free_ret)
+{
+       switch (type) {
+       case SETTING_TYPE_BOOLEAN:
+               return settings_get_bool(key) ? "yes" : "no";
+       case SETTING_TYPE_INT:
+               *free_ret = TRUE;
+               return g_strdup_printf("%d", settings_get_int(key));
+       case SETTING_TYPE_STRING:
+               return (char *) settings_get_str(key);
+       }
+
+       return NULL;
+}
+
+static char *get_long_variable_value(const char *key, SERVER_REC *server,
+                                    void *item, int *free_ret)
+{
+       EXPANDO_FUNC func;
+       char *ret;
+       int type;
+
+       *free_ret = FALSE;
+
+       /* expando? */
+        func = expando_find_long(key);
+       if (func != NULL)
+               return func(server, item, free_ret);
+
+       /* internal setting? */
+       type = settings_get_type(key);
+       if (type != -1)
+               return get_internal_setting(key, type, free_ret);
+
+       /* environment variable? */
+       ret = g_getenv(key);
+       if (ret != NULL)
+               return ret;
+
+       return NULL;
+}
+
+static char *get_long_variable(char **cmd, SERVER_REC *server,
+                              void *item, int *free_ret, int getname)
+{
+       char *start, *var, *ret;
+
+       /* get variable name */
+       start = *cmd;
+       while (isvarchar((*cmd)[1])) (*cmd)++;
+
+       var = g_strndup(start, (int) (*cmd-start)+1);
+       if (getname) {
+               *free_ret = TRUE;
+                return var;
+       }
+       ret = get_long_variable_value(var, server, item, free_ret);
+       g_free(var);
+       return ret;
+}
+
+/* return the value of the variable found from `cmd'.
+   if 'getname' is TRUE, return the name of the variable instead it's value */
+static char *get_variable(char **cmd, SERVER_REC *server, void *item,
+                         char **arglist, int *free_ret, int *arg_used,
+                         int getname)
+{
+       EXPANDO_FUNC func;
+
+       if (isarg(**cmd)) {
+               /* argument */
+               *free_ret = TRUE;
+               if (arg_used != NULL) *arg_used = TRUE;
+               return getname ? g_strdup_printf("%c", **cmd) :
+                       get_argument(cmd, arglist);
+       }
+
+       if (i_isalpha(**cmd) && isvarchar((*cmd)[1])) {
+               /* long variable name.. */
+               return get_long_variable(cmd, server, item, free_ret, getname);
+       }
+
+       /* single character variable. */
+       if (getname) {
+               *free_ret = TRUE;
+                return g_strdup_printf("%c", **cmd);
+       }
+       *free_ret = FALSE;
+       func = expando_find_char(**cmd);
+       return func == NULL ? NULL : func(server, item, free_ret);
+}
+
+static char *get_history(char **cmd, void *item, int *free_ret)
+{
+       char *start, *text, *ret;
+
+       /* get variable name */
+       start = ++(*cmd);
+       while (**cmd != '\0' && **cmd != '!') (*cmd)++;
+
+       if (history_func == NULL)
+               ret = NULL;
+       else {
+               text = g_strndup(start, (int) (*cmd-start));
+               ret = history_func(text, item, free_ret);
+               g_free(text);
+       }
+
+       if (**cmd == '\0') (*cmd)--;
+       return ret;
+}
+
+static char *get_special_value(char **cmd, SERVER_REC *server, void *item,
+                              char **arglist, int *free_ret, int *arg_used,
+                              int flags)
+{
+       char command, *value, *p;
+       int len;
+
+       if ((flags & PARSE_FLAG_ONLY_ARGS) && !isarg(**cmd)) {
+               *free_ret = TRUE;
+               return g_strdup_printf("$%c", **cmd);
+       }
+
+       if (**cmd == '!') {
+               /* find text from command history */
+               if (flags & PARSE_FLAG_GETNAME)
+                       return "!";
+
+               return get_history(cmd, item, free_ret);
+       }
+
+       command = 0;
+       if (**cmd == '#' || **cmd == '@') {
+                command = **cmd;
+               if ((*cmd)[1] != '\0')
+                       (*cmd)++;
+               else {
+                       /* default to $* */
+                       char *temp_cmd = "*";
+
+                       if (flags & PARSE_FLAG_GETNAME)
+                                return "*";
+
+                       *free_ret = TRUE;
+                       return get_argument(&temp_cmd, arglist);
+               }
+       }
+
+       value = get_variable(cmd, server, item, arglist, free_ret,
+                            arg_used, flags & PARSE_FLAG_GETNAME);
+
+       if (flags & PARSE_FLAG_GETNAME)
+               return value;
+
+       if (command == '#') {
+               /* number of words */
+               if (value == NULL || *value == '\0') {
+                       if (value != NULL && *free_ret) {
+                               g_free(value);
+                               *free_ret = FALSE;
+                       }
+                       return "0";
+               }
+
+               len = 1;
+               for (p = value; *p != '\0'; p++) {
+                       if (*p == ' ' && (p[1] != ' ' && p[1] != '\0'))
+                               len++;
+               }
+                if (*free_ret) g_free(value);
+
+               *free_ret = TRUE;
+               return g_strdup_printf("%d", len);
+       }
+
+       if (command == '@') {
+               /* number of characters */
+               if (value == NULL) return "0";
+
+               len = strlen(value);
+                if (*free_ret) g_free(value);
+
+               *free_ret = TRUE;
+               return g_strdup_printf("%d", len);
+       }
+
+       return value;
+}
+
+/* get alignment arguments (inside the []) */
+static int get_alignment_args(char **data, int *align, int *flags, char *pad)
+{
+       char *str;
+
+       *align = 0;
+       *flags = ALIGN_CUT|ALIGN_PAD;
+        *pad = ' ';
+
+       /* '!' = don't cut, '-' = right padding */
+       str = *data;
+       while (*str != '\0' && *str != ']' && !i_isdigit(*str)) {
+               if (*str == '!')
+                       *flags &= ~ALIGN_CUT;
+               else if (*str == '-')
+                       *flags |= ALIGN_RIGHT;
+               else if (*str == '.')
+                         *flags &= ~ALIGN_PAD;
+               str++;
+       }
+       if (!i_isdigit(*str))
+               return FALSE; /* expecting number */
+
+       /* get the alignment size */
+       while (i_isdigit(*str)) {
+               *align = (*align) * 10 + (*str-'0');
+               str++;
+       }
+
+       /* get the pad character */
+       while (*str != '\0' && *str != ']') {
+               *pad = *str;
+               str++;
+       }
+
+       if (*str++ != ']') return FALSE;
+
+       *data = str;
+       return TRUE;
+}
+
+/* return the aligned text */
+static char *get_alignment(const char *text, int align, int flags, char pad)
+{
+       GString *str;
+       char *ret;
+
+       g_return_val_if_fail(text != NULL, NULL);
+
+       str = g_string_new(text);
+
+       /* cut */
+       if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
+               g_string_truncate(str, align);
+
+       /* add pad characters */
+       if (flags & ALIGN_PAD) {
+               while (str->len < align) {
+                       if (flags & ALIGN_RIGHT)
+                               g_string_prepend_c(str, pad);
+                       else
+                               g_string_append_c(str, pad);
+               }
+       }
+
+       ret = str->str;
+        g_string_free(str, FALSE);
+       return ret;
+}
+
+/* Parse and expand text after '$' character. return value has to be
+   g_free()'d if `free_ret' is TRUE. */
+char *parse_special(char **cmd, SERVER_REC *server, void *item,
+                   char **arglist, int *free_ret, int *arg_used, int flags)
+{
+       static char **nested_orig_cmd = NULL; /* FIXME: KLUDGE! */
+       char command, *value;
+
+       char align_pad;
+       int align, align_flags;
+
+       char *nest_value;
+       int brackets, nest_free;
+
+       *free_ret = FALSE;
+
+       command = **cmd; (*cmd)++;
+       switch (command) {
+       case '[':
+               /* alignment */
+               if (!get_alignment_args(cmd, &align, &align_flags,
+                                       &align_pad) || **cmd == '\0') {
+                       (*cmd)--;
+                       return NULL;
+               }
+               break;
+       default:
+               command = 0;
+               (*cmd)--;
+       }
+
+       nest_free = FALSE; nest_value = NULL;
+       if (**cmd == '(') {
+               /* subvariable */
+               int toplevel = nested_orig_cmd == NULL;
+
+               if (toplevel) nested_orig_cmd = cmd;
+               (*cmd)++;
+               if (**cmd != '$') {
+                       /* ... */
+                       nest_value = *cmd;
+               } else {
+                       (*cmd)++;
+                       nest_value = parse_special(cmd, server, item, arglist,
+                                                  &nest_free, arg_used,
+                                                  flags);
+               }
+
+               while ((*nested_orig_cmd)[1] != '\0') {
+                       (*nested_orig_cmd)++;
+                       if (**nested_orig_cmd == ')')
+                               break;
+               }
+               cmd = &nest_value;
+
+                if (toplevel) nested_orig_cmd = NULL;
+       }
+
+       if (**cmd != '{')
+               brackets = FALSE;
+       else {
+               /* special value is inside {...} (foo${test}bar -> fooXXXbar) */
+               (*cmd)++;
+               brackets = TRUE;
+       }
+
+       value = get_special_value(cmd, server, item, arglist,
+                                 free_ret, arg_used, flags);
+       if (**cmd == '\0')
+               g_error("parse_special() : buffer overflow!");
+
+       if (value != NULL && *value != '\0' && (flags & PARSE_FLAG_ISSET_ANY))
+               *arg_used = TRUE;
+
+       if (brackets) {
+               while (**cmd != '}' && (*cmd)[1] != '\0')
+                       (*cmd)++;
+       }
+
+       if (nest_free) g_free(nest_value);
+
+       if (command == '[' && (flags & PARSE_FLAG_GETNAME) == 0) {
+               /* alignment */
+               char *p;
+
+               if (value == NULL) return "";
+
+               p = get_alignment(value, align, align_flags, align_pad);
+               if (*free_ret) g_free(value);
+
+               *free_ret = TRUE;
+               return p;
+       }
+
+       return value;
+}
+
+static void gstring_append_escaped(GString *str, const char *text, int flags)
+{
+       char esc[4], *escpos;
+       
+       escpos = esc;
+       if (flags & PARSE_FLAG_ESCAPE_VARS)
+               *escpos++ = '%';
+       if (flags & PARSE_FLAG_ESCAPE_THEME) {
+               *escpos++ = '{';
+               *escpos++ = '}';
+       }
+
+       if (escpos == esc) {
+               g_string_append(str, text);
+               return;
+       }
+
+       *escpos = '\0'; 
+       while (*text != '\0') {
+               for (escpos = esc; *escpos != '\0'; escpos++) {
+                       if (*text == *escpos) {
+                               g_string_append_c(str, '%');
+                               break;
+                       }
+               }
+               g_string_append_c(str, *text);
+               text++;
+       }
+}
+
+/* parse the whole string. $ and \ chars are replaced */
+char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
+                          const char *data, int *arg_used, int flags)
+{
+       char code, **arglist, *ret;
+       GString *str;
+       int need_free, chr;
+
+       g_return_val_if_fail(cmd != NULL, NULL);
+       g_return_val_if_fail(data != NULL, NULL);
+
+       /* create the argument list */
+       arglist = g_strsplit(data, " ", -1);
+
+       if (arg_used != NULL) *arg_used = FALSE;
+       code = 0;
+       str = g_string_new(NULL);
+       while (*cmd != '\0') {
+               if (code == '\\') {
+                       if (*cmd == ';')
+                               g_string_append_c(str, ';');
+                       else {
+                               chr = expand_escape(&cmd);
+                               g_string_append_c(str, chr != -1 ? chr : *cmd);
+                       }
+                       code = 0;
+               } else if (code == '$') {
+                       char *ret;
+
+                       ret = parse_special((char **) &cmd, server, item,
+                                           arglist, &need_free, arg_used,
+                                           flags);
+                       if (ret != NULL) {
+                                gstring_append_escaped(str, ret, flags);
+                               if (need_free) g_free(ret);
+                       }
+                       code = 0;
+               } else {
+                       if (*cmd == '\\' || *cmd == '$')
+                               code = *cmd;
+                       else
+                               g_string_append_c(str, *cmd);
+               }
+
+                cmd++;
+       }
+       g_strfreev(arglist);
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+#define is_split_char(str, start) \
+       ((str)[0] == ';' && ((start) == (str) || \
+               ((str)[-1] != '\\' && (str)[-1] != '$')))
+
+/* execute the commands in string - commands can be split with ';' */
+void eval_special_string(const char *cmd, const char *data,
+                        SERVER_REC *server, void *item)
+{
+       const char *cmdchars;
+       char *orig, *str, *start, *ret;
+       int arg_used, arg_used_ever;
+       GSList *commands;
+
+       commands = NULL;
+       arg_used_ever = FALSE;
+       cmdchars = settings_get_str("cmdchars");
+
+       /* get a list of all the commands to run */
+       orig = start = str = g_strdup(cmd);
+       do {
+               if (is_split_char(str, start)) {
+                       *str++ = '\0';
+                        while (*str == ' ') str++;
+               } else if (*str != '\0') {
+                       str++;
+                       continue;
+               }
+
+               ret = parse_special_string(start, server, item,
+                                          data, &arg_used, 0);
+               if (*ret != '\0') {
+                       if (arg_used) arg_used_ever = TRUE;
+
+                       if (strchr(cmdchars, *ret) == NULL) {
+                               /* no command char - let's put it there.. */
+                               char *old = ret;
+
+                               ret = g_strdup_printf("%c%s", *cmdchars, old);
+                               g_free(old);
+                       }
+                       commands = g_slist_append(commands, ret);
+               }
+               start = str;
+       } while (*start != '\0');
+
+       /* run the command, if no arguments were ever used, append all of them
+          after each command */
+       while (commands != NULL) {
+               ret = commands->data;
+
+               if (!arg_used_ever && *data != '\0') {
+                       char *old = ret;
+
+                       ret = g_strconcat(old, " ", data, NULL);
+                       g_free(old);
+               }
+
+                if (server != NULL)
+                       server_ref(server);
+               signal_emit("send command", 3, ret, server, item);
+
+               if (server != NULL && !server_unref(server)) {
+                        /* the server was destroyed */
+                       server = NULL;
+                        item = NULL;
+               }
+
+               /* FIXME: window item would need reference counting as well,
+                  eg. "/EVAL win close;say hello" wouldn't work now.. */
+
+               g_free(ret);
+               commands = g_slist_remove(commands, commands->data);
+       }
+       g_free(orig);
+}
+
+void special_history_func_set(SPECIAL_HISTORY_FUNC func)
+{
+       history_func = func;
+}
+
+static void update_signals_hash(GHashTable **hash, int *signals)
+{
+       void *signal_id;
+        int arg_type;
+
+       if (*hash == NULL) {
+               *hash = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+       }
+
+       while (*signals != -1) {
+                signal_id = GINT_TO_POINTER(*signals);
+               arg_type = GPOINTER_TO_INT(g_hash_table_lookup(*hash, signal_id));
+               if (arg_type != 0 && arg_type != signals[1]) {
+                       /* same signal is used for different purposes ..
+                          not sure if this should ever happen, but change
+                          the argument type to none so it will at least
+                          work. */
+                       arg_type = EXPANDO_ARG_NONE;
+               }
+
+               if (arg_type == 0) arg_type = signals[1];
+               g_hash_table_insert(*hash, signal_id,
+                                   GINT_TO_POINTER(arg_type));
+               signals += 2;
+       }
+}
+
+static void get_signal_hash(void *signal_id, void *arg_type, int **pos)
+{
+       (*pos)[0] = GPOINTER_TO_INT(signal_id);
+        (*pos)[1] = GPOINTER_TO_INT(arg_type);
+        (*pos) += 2;
+}
+
+static int *get_signals_list(GHashTable *hash)
+{
+       int *signals, *pos;
+
+       if (hash == NULL) {
+               /* no expandos in text - never needs updating */
+               return NULL;
+       }
+
+        pos = signals = g_new(int, g_hash_table_size(hash)*2 + 1);
+       g_hash_table_foreach(hash, (GHFunc) get_signal_hash, &pos);
+        *pos = -1;
+
+       g_hash_table_destroy(hash);
+        return signals;
+
+}
+
+#define TASK_BIND              1
+#define TASK_UNBIND            2
+#define TASK_GET_SIGNALS       3
+
+static int *special_vars_signals_task(const char *text, int funccount,
+                                     SIGNAL_FUNC *funcs, int task)
+{
+        GHashTable *signals;
+       char *expando;
+       int need_free, *expando_signals;
+
+        signals = NULL;
+       while (*text != '\0') {
+               if (*text == '\\' && text[1] != '\0') {
+                        /* escape */
+                       text += 2;
+               } else if (*text == '$' && text[1] != '\0') {
+                        /* expando */
+                       text++;
+                       expando = parse_special((char **) &text, NULL, NULL,
+                                               NULL, &need_free, NULL,
+                                               PARSE_FLAG_GETNAME);
+                       if (expando == NULL)
+                               continue;
+
+                       switch (task) {
+                       case TASK_BIND:
+                               expando_bind(expando, funccount, funcs);
+                               break;
+                       case TASK_UNBIND:
+                               expando_unbind(expando, funccount, funcs);
+                               break;
+                       case TASK_GET_SIGNALS:
+                               expando_signals = expando_get_signals(expando);
+                               if (expando_signals != NULL) {
+                                       update_signals_hash(&signals,
+                                                           expando_signals);
+                                        g_free(expando_signals);
+                               }
+                               break;
+                       }
+                       if (need_free) g_free(expando);
+               } else {
+                        /* just a char */
+                       text++;
+               }
+       }
+
+       if (task == TASK_GET_SIGNALS)
+                return get_signals_list(signals);
+
+        return NULL;
+}
+
+void special_vars_add_signals(const char *text,
+                             int funccount, SIGNAL_FUNC *funcs)
+{
+        special_vars_signals_task(text, funccount, funcs, TASK_BIND);
+}
+
+void special_vars_remove_signals(const char *text,
+                                int funccount, SIGNAL_FUNC *funcs)
+{
+        special_vars_signals_task(text, funccount, funcs, TASK_UNBIND);
+}
+
+int *special_vars_get_signals(const char *text)
+{
+       return special_vars_signals_task(text, 0, NULL, TASK_GET_SIGNALS);
+}
diff --git a/apps/irssi/src/core/special-vars.h b/apps/irssi/src/core/special-vars.h
new file mode 100644 (file)
index 0000000..11262da
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef __SPECIAL_VARS_H
+#define __SPECIAL_VARS_H
+
+#include "signals.h"
+
+#define PARSE_FLAG_GETNAME     0x01 /* return argument name instead of it's value */
+#define PARSE_FLAG_ISSET_ANY   0x02 /* arg_used field specifies that at least one of the $variables was non-empty */
+#define PARSE_FLAG_ESCAPE_VARS  0x04 /* if any arguments/variables contain % chars, escape them with another % */
+#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */
+#define PARSE_FLAG_ONLY_ARGS   0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */
+
+typedef char* (*SPECIAL_HISTORY_FUNC)
+       (const char *text, void *item, int *free_ret);
+
+/* Parse and expand text after '$' character. return value has to be
+   g_free()'d if `free_ret' is TRUE. */
+char *parse_special(char **cmd, SERVER_REC *server, void *item,
+                   char **arglist, int *free_ret, int *arg_used, int flags);
+
+/* parse the whole string. $ and \ chars are replaced */
+char *parse_special_string(const char *cmd, SERVER_REC *server, void *item,
+                          const char *data, int *arg_used, int flags);
+
+/* execute the commands in string - commands can be split with ';' */
+void eval_special_string(const char *cmd, const char *data,
+                        SERVER_REC *server, void *item);
+
+void special_history_func_set(SPECIAL_HISTORY_FUNC func);
+
+void special_vars_add_signals(const char *text,
+                             int funccount, SIGNAL_FUNC *funcs);
+void special_vars_remove_signals(const char *text,
+                                int funccount, SIGNAL_FUNC *funcs);
+/* Returns [<signal id>, EXPANDO_ARG_xxx, <signal id>, ..., -1] */
+int *special_vars_get_signals(const char *text);
+
+#endif
diff --git a/apps/irssi/src/core/window-item-def.h b/apps/irssi/src/core/window-item-def.h
new file mode 100644 (file)
index 0000000..4364c66
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __WINDOW_ITEM_DEF_H
+#define __WINDOW_ITEM_DEF_H
+
+#define STRUCT_SERVER_REC SERVER_REC
+struct _WI_ITEM_REC {
+#include "window-item-rec.h"
+};
+
+#endif
diff --git a/apps/irssi/src/core/window-item-rec.h b/apps/irssi/src/core/window-item-rec.h
new file mode 100644 (file)
index 0000000..eeb465f
--- /dev/null
@@ -0,0 +1,17 @@
+/* WI_ITEM_REC definition, used for inheritance */
+
+int type; /* module_get_uniq_id("CHANNEL/QUERY/xxx", 0) */
+int chat_type; /* chat_protocol_lookup(xx) */
+GHashTable *module_data;
+
+void *window;
+STRUCT_SERVER_REC *server;
+char *name;
+
+time_t createtime;
+int data_level;
+char *hilight_color;
+
+void (*destroy)(WI_ITEM_REC *item);
+
+#undef STRUCT_SERVER_REC
diff --git a/apps/irssi/src/core/write-buffer.c b/apps/irssi/src/core/write-buffer.c
new file mode 100644 (file)
index 0000000..1c3eef8
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ write-buffer.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "settings.h"
+#include "write-buffer.h"
+
+#define BUFFER_BLOCK_SIZE 2048
+
+typedef struct {
+       char *active_block;
+        int active_block_pos;
+
+       GSList *blocks;
+} BUFFER_REC;
+
+static GSList *empty_blocks;
+static GHashTable *buffers;
+static int block_count;
+
+static int write_buffer_max_blocks;
+static int timeout_tag;
+
+static void write_buffer_new_block(BUFFER_REC *rec)
+{
+       char *block;
+
+       if (empty_blocks == NULL)
+               block = g_malloc(BUFFER_BLOCK_SIZE);
+       else {
+               block = empty_blocks->data;
+                empty_blocks = g_slist_remove(empty_blocks, block);
+       }
+
+        block_count++;
+       rec->active_block = block;
+        rec->active_block_pos = 0;
+       rec->blocks = g_slist_append(rec->blocks, block);
+}
+
+int write_buffer(int handle, const void *data, int size)
+{
+       BUFFER_REC *rec;
+        const char *cdata = data;
+       int next_size;
+
+       if (write_buffer_max_blocks <= 0) {
+               /* no write buffer */
+                return write(handle, data, size);
+       }
+
+       if (size <= 0)
+               return size;
+
+       rec = g_hash_table_lookup(buffers, GINT_TO_POINTER(handle));
+       if (rec == NULL) {
+               rec = g_new0(BUFFER_REC, 1);
+                write_buffer_new_block(rec);
+               g_hash_table_insert(buffers, GINT_TO_POINTER(handle), rec);
+       }
+
+       while (size > 0) {
+                if (rec->active_block_pos == BUFFER_BLOCK_SIZE)
+                       write_buffer_new_block(rec);
+
+               next_size = size < BUFFER_BLOCK_SIZE-rec->active_block_pos ?
+                       size : BUFFER_BLOCK_SIZE-rec->active_block_pos;
+               memcpy(rec->active_block+rec->active_block_pos,
+                      cdata, next_size);
+
+               rec->active_block_pos += next_size;
+               cdata += next_size;
+                size -= next_size;
+       }
+
+       if (block_count > write_buffer_max_blocks)
+                write_buffer_flush();
+
+        return size;
+}
+
+static int write_buffer_flush_rec(void *handlep, BUFFER_REC *rec)
+{
+       GSList *tmp;
+        int handle, size;
+
+        handle = GPOINTER_TO_INT(handlep);
+       for (tmp = rec->blocks; tmp != NULL; tmp = tmp->next) {
+               size = tmp->data != rec->active_block ? BUFFER_BLOCK_SIZE :
+                       rec->active_block_pos;
+               write(handle, tmp->data, size);
+       }
+
+        empty_blocks = g_slist_concat(empty_blocks, rec->blocks);
+       g_free(rec);
+        return TRUE;
+}
+
+void write_buffer_flush(void)
+{
+       g_slist_foreach(empty_blocks, (GFunc) g_free, NULL);
+       g_slist_free(empty_blocks);
+        empty_blocks = NULL;
+
+       g_hash_table_foreach_remove(buffers,
+                                   (GHRFunc) write_buffer_flush_rec, NULL);
+        block_count = 0;
+}
+
+static int flush_timeout(void)
+{
+       write_buffer_flush();
+        return 1;
+}
+
+static void read_settings(void)
+{
+       int msecs;
+
+       write_buffer_flush();
+
+       write_buffer_max_blocks = settings_get_int("write_buffer_kb") *
+               1024/BUFFER_BLOCK_SIZE;
+
+       if (settings_get_int("write_buffer_mins") > 0) {
+                msecs = settings_get_int("write_buffer_mins")*60*1000;
+               if (timeout_tag == -1) {
+                       timeout_tag = g_timeout_add(msecs,
+                                                   (GSourceFunc) flush_timeout,
+                                                   NULL);
+               }
+       } else if (timeout_tag != -1) {
+               g_source_remove(timeout_tag);
+                timeout_tag = -1;
+       }
+}
+
+static void cmd_flushbuffer(void)
+{
+        write_buffer_flush();
+}
+
+void write_buffer_init(void)
+{
+       settings_add_int("misc", "write_buffer_mins", 0);
+       settings_add_int("misc", "write_buffer_kb", 0);
+
+       buffers = g_hash_table_new((GHashFunc) g_direct_hash,
+                                  (GCompareFunc) g_direct_equal);
+
+        empty_blocks = NULL;
+        block_count = 0;
+
+       timeout_tag = -1;
+       read_settings();
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+        command_bind("flushbuffer", NULL, (SIGNAL_FUNC) cmd_flushbuffer);
+}
+
+void write_buffer_deinit(void)
+{
+       if (timeout_tag != -1)
+               g_source_remove(timeout_tag);
+
+        write_buffer_flush();
+        g_hash_table_destroy(buffers);
+
+       g_slist_foreach(empty_blocks, (GFunc) g_free, NULL);
+        g_slist_free(empty_blocks);
+
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+       command_unbind("flushbuffer",  (SIGNAL_FUNC) cmd_flushbuffer);
+}
diff --git a/apps/irssi/src/core/write-buffer.h b/apps/irssi/src/core/write-buffer.h
new file mode 100644 (file)
index 0000000..ef52744
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __WRITE_BUFFER_H
+#define __WRITE_BUFFER_H
+
+int write_buffer(int handle, const void *data, int size);
+void write_buffer_flush(void);
+
+void write_buffer_init(void);
+void write_buffer_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/.cvsignore b/apps/irssi/src/fe-common/core/.cvsignore
new file mode 100644 (file)
index 0000000..8553e9e
--- /dev/null
@@ -0,0 +1,8 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
diff --git a/apps/irssi/src/fe-common/core/Makefile.am b/apps/irssi/src/fe-common/core/Makefile.am
new file mode 100644 (file)
index 0000000..43bb1cd
--- /dev/null
@@ -0,0 +1,63 @@
+noinst_LIBRARIES = libfe_common_core.a
+
+include $(top_srcdir)/Makefile.defines.in
+
+INCLUDES = \
+       -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \
+       $(GLIB_CFLAGS) \
+       -DHELPDIR=\""$(silc_helpdir)"\" \
+       -DTHEMESDIR=\""$(silc_etcdir)"\"
+
+libfe_common_core_a_SOURCES = \
+       autorun.c \
+       chat-completion.c \
+       command-history.c \
+       completion.c \
+       fe-channels.c \
+       fe-common-core.c \
+       fe-core-commands.c \
+       fe-exec.c \
+       fe-expandos.c \
+       fe-help.c \
+       fe-ignore.c \
+       fe-ignore-messages.c \
+       fe-log.c \
+       fe-messages.c \
+       fe-modules.c \
+       fe-queries.c \
+       fe-server.c \
+       fe-settings.c \
+       formats.c \
+       hilight-text.c \
+       keyboard.c \
+       module-formats.c \
+       printtext.c \
+       themes.c \
+       translation.c \
+       window-activity.c \
+       window-commands.c \
+       window-items.c \
+       windows-layout.c \
+       fe-windows.c
+
+noinst_HEADERS = \
+       autorun.h \
+       command-history.h \
+       chat-completion.h \
+       completion.h \
+       fe-channels.h \
+       fe-common-core.h \
+       fe-exec.h \
+       fe-messages.h \
+       fe-queries.h \
+       formats.h \
+       hilight-text.h \
+       keyboard.h \
+       module-formats.h \
+       module.h \
+       printtext.h \
+       themes.h \
+       translation.h \
+       window-items.h \
+       windows-layout.h \
+       fe-windows.h
diff --git a/apps/irssi/src/fe-common/core/autorun.c b/apps/irssi/src/fe-common/core/autorun.c
new file mode 100644 (file)
index 0000000..9c1050d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ autorun.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "line-split.h"
+#include "special-vars.h"
+
+#include "fe-windows.h"
+
+void autorun_startup(void)
+{
+       char tmpbuf[1024], *str, *path;
+       LINEBUF_REC *buffer = NULL;
+       int f, ret, recvlen;
+
+       /* open ~/.irssi/startup and run all commands in it */
+       path = g_strdup_printf("%s/startup", get_irssi_dir());
+       f = open(path, O_RDONLY);
+       g_free(path);
+       if (f == -1) {
+               /* file not found */
+               return;
+       }
+
+       do {
+               recvlen = read(f, tmpbuf, sizeof(tmpbuf));
+
+               ret = line_split(tmpbuf, recvlen, &str, &buffer);
+               if (ret > 0) {
+                       eval_special_string(str, "",
+                                           active_win->active_server,
+                                           active_win->active);
+               }
+       } while (ret > 0);
+       line_split_free(buffer);
+
+       close(f);
+}
diff --git a/apps/irssi/src/fe-common/core/autorun.h b/apps/irssi/src/fe-common/core/autorun.h
new file mode 100644 (file)
index 0000000..59d8e84
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __AUTORUN_H
+#define __AUTORUN_H
+
+void autorun_startup(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/chat-completion.c b/apps/irssi/src/fe-common/core/chat-completion.c
new file mode 100644 (file)
index 0000000..a3d92cc
--- /dev/null
@@ -0,0 +1,955 @@
+/*
+ chat-completion.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "chatnets.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "channels-setup.h"
+#include "queries.h"
+#include "nicklist.h"
+
+#include "completion.h"
+#include "window-items.h"
+
+static int keep_privates_count, keep_publics_count;
+static int completion_lowercase;
+static const char *completion_char, *cmdchars;
+static GSList *global_lastmsgs;
+static int completion_auto, completion_strict;
+
+#define SERVER_LAST_MSG_ADD(server, nick) \
+       last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \
+                    nick, TRUE, keep_privates_count)
+
+#define CHANNEL_LAST_MSG_ADD(channel, nick, own) \
+       last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \
+                    nick, own, keep_publics_count)
+
+static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick)
+{
+       while (list != NULL) {
+               LAST_MSG_REC *rec = list->data;
+
+               if (g_strcasecmp(rec->nick, nick) == 0)
+                       return rec;
+               list = list->next;
+       }
+
+       return NULL;
+}
+
+static void last_msg_dec_owns(GSList *list)
+{
+       LAST_MSG_REC *rec;
+
+       while (list != NULL) {
+               rec = list->data;
+               if (rec->own) rec->own--;
+
+               list = list->next;
+       }
+}
+
+static void last_msg_add(GSList **list, const char *nick, int own, int max)
+{
+       LAST_MSG_REC *rec;
+
+       rec = last_msg_find(*list, nick);
+       if (rec != NULL) {
+               /* msg already exists, update it */
+               *list = g_slist_remove(*list, rec);
+               if (own)
+                       rec->own = max;
+               else if (rec->own)
+                        rec->own--;
+       } else {
+               rec = g_new(LAST_MSG_REC, 1);
+               rec->nick = g_strdup(nick);
+
+               if ((int)g_slist_length(*list) == max) {
+                       *list = g_slist_remove(*list,
+                                              g_slist_last(*list)->data);
+               }
+
+               rec->own = own ? max : 0;
+       }
+       rec->time = time(NULL);
+
+        last_msg_dec_owns(*list);
+
+       *list = g_slist_prepend(*list, rec);
+}
+
+static void last_msg_destroy(GSList **list, LAST_MSG_REC *rec)
+{
+       *list = g_slist_remove(*list, rec);
+
+       g_free(rec->nick);
+       g_free(rec);
+}
+
+void completion_last_message_add(const char *nick)
+{
+       g_return_if_fail(nick != NULL);
+
+       last_msg_add(&global_lastmsgs, nick, TRUE, keep_privates_count);
+}
+
+void completion_last_message_remove(const char *nick)
+{
+       LAST_MSG_REC *rec;
+
+       g_return_if_fail(nick != NULL);
+
+       rec = last_msg_find(global_lastmsgs, nick);
+        if (rec != NULL) last_msg_destroy(&global_lastmsgs, rec);
+}
+
+void completion_last_message_rename(const char *oldnick, const char *newnick)
+{
+       LAST_MSG_REC *rec;
+
+       g_return_if_fail(oldnick != NULL);
+       g_return_if_fail(newnick != NULL);
+
+       rec = last_msg_find(global_lastmsgs, oldnick);
+       if (rec != NULL) {
+               g_free(rec->nick);
+                rec->nick = g_strdup(newnick);
+       }
+}
+
+static void sig_message_public(SERVER_REC *server, const char *msg,
+                              const char *nick, const char *address,
+                              const char *target)
+{
+       CHANNEL_REC *channel;
+        int own;
+
+       channel = channel_find(server, target);
+       if (channel != NULL) {
+                own = nick_match_msg(channel, msg, server->nick);
+               CHANNEL_LAST_MSG_ADD(channel, nick, own);
+       }
+}
+
+static void sig_message_join(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *address)
+{
+       CHANNEL_REC *chanrec;
+
+       chanrec = channel_find(server, channel);
+       if (chanrec != NULL)
+               CHANNEL_LAST_MSG_ADD(chanrec, nick, FALSE);
+}
+
+static void sig_message_private(SERVER_REC *server, const char *msg,
+                               const char *nick, const char *address)
+{
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(nick != NULL);
+
+       SERVER_LAST_MSG_ADD(server, nick);
+}
+
+static void sig_message_own_public(SERVER_REC *server, const char *msg,
+                                  const char *target, const char *origtarget)
+{
+       CHANNEL_REC *channel;
+       NICK_REC *nick;
+       char *p, *msgnick;
+
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(msg != NULL);
+        if (target == NULL) return;
+
+        channel = channel_find(server, target);
+       if (channel == NULL)
+               return;
+
+       /* channel msg - if first word in line is nick,
+          add it to lastmsgs */
+       p = strchr(msg, ' ');
+       if (p != NULL && p != msg) {
+               msgnick = g_strndup(msg, (int) (p-msg));
+               nick = nicklist_find(channel, msgnick);
+               if (nick == NULL && msgnick[1] != '\0') {
+                       /* probably ':' or ',' or some other
+                          char after nick, try without it */
+                       msgnick[strlen(msgnick)-1] = '\0';
+                       nick = nicklist_find(channel, msgnick);
+               }
+                g_free(msgnick);
+               if (nick != NULL && nick != channel->ownnick)
+                       CHANNEL_LAST_MSG_ADD(channel, nick->nick, TRUE);
+       }
+}
+
+static void sig_message_own_private(SERVER_REC *server, const char *msg,
+                                   const char *target, const char *origtarget)
+{
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(target != NULL);
+
+       if (target != NULL && query_find(server, target) == NULL)
+               SERVER_LAST_MSG_ADD(server, target);
+}
+
+static void sig_nick_removed(CHANNEL_REC *channel, NICK_REC *nick)
+{
+        MODULE_CHANNEL_REC *mchannel;
+       LAST_MSG_REC *rec;
+
+        mchannel = MODULE_DATA(channel);
+       rec = last_msg_find(mchannel->lastmsgs, nick->nick);
+       if (rec != NULL) last_msg_destroy(&mchannel->lastmsgs, rec);
+}
+
+static void sig_nick_changed(CHANNEL_REC *channel, NICK_REC *nick,
+                            const char *oldnick)
+{
+        MODULE_CHANNEL_REC *mchannel;
+       LAST_MSG_REC *rec;
+
+        mchannel = MODULE_DATA(channel);
+       rec = last_msg_find(mchannel->lastmsgs, oldnick);
+       if (rec != NULL) {
+               g_free(rec->nick);
+               rec->nick = g_strdup(nick->nick);
+       }
+}
+
+static int last_msg_cmp(LAST_MSG_REC *m1, LAST_MSG_REC *m2)
+{
+       return m1->time < m2->time ? 1 : -1;
+}
+
+/* Complete /MSG from specified server, or from
+   global_lastmsgs if server is NULL */
+static void completion_msg_server(GSList **list, SERVER_REC *server,
+                                 const char *nick, const char *prefix)
+{
+       LAST_MSG_REC *msg;
+       GSList *tmp;
+       int len;
+
+       g_return_if_fail(nick != NULL);
+
+       len = strlen(nick);
+       tmp = server == NULL ? global_lastmsgs :
+               ((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs;
+       for (; tmp != NULL; tmp = tmp->next) {
+               LAST_MSG_REC *rec = tmp->data;
+
+               if (len != 0 && g_strncasecmp(rec->nick, nick, len) != 0)
+                       continue;
+
+               msg = g_new(LAST_MSG_REC, 1);
+               msg->time = rec->time;
+               msg->nick = prefix == NULL || *prefix == '\0' ?
+                       g_strdup(rec->nick) :
+                       g_strconcat(prefix, " ", rec->nick, NULL);
+               *list = g_slist_insert_sorted(*list, msg,
+                                             (GCompareFunc) last_msg_cmp);
+       }
+}
+
+/* convert list of LAST_MSG_REC's to list of char* nicks. */
+static GList *convert_msglist(GSList *msglist)
+{
+       GList *list;
+
+       list = NULL;
+       while (msglist != NULL) {
+               LAST_MSG_REC *rec = msglist->data;
+
+                list = g_list_append(list, rec->nick);
+               msglist = g_slist_remove(msglist, rec);
+               g_free(rec);
+       }
+
+       return list;
+}
+
+/* Complete /MSG - if `find_server' is NULL, complete nicks from all servers */
+static GList *completion_msg(SERVER_REC *win_server,
+                            SERVER_REC *find_server,
+                            const char *nick, const char *prefix)
+{
+       GSList *tmp, *list;
+       char *newprefix;
+
+       g_return_val_if_fail(nick != NULL, NULL);
+       if (servers == NULL) return NULL;
+
+       list = NULL;
+       if (find_server != NULL) {
+               completion_msg_server(&list, find_server, nick, prefix);
+               return convert_msglist(list);
+       }
+
+       completion_msg_server(&list, NULL, nick, prefix);
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *rec = tmp->data;
+
+               if (rec == win_server)
+                       newprefix = g_strdup(prefix);
+               else {
+                       newprefix = prefix == NULL ?
+                               g_strdup_printf("-%s", rec->tag) :
+                               g_strdup_printf("%s -%s", prefix, rec->tag);
+               }
+
+               completion_msg_server(&list, rec, nick, newprefix);
+               g_free_not_null(newprefix);
+       }
+
+       return convert_msglist(list);
+}
+
+static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
+                                  const char *nick, const char *suffix)
+{
+        MODULE_CHANNEL_REC *mchannel;
+       GSList *tmp;
+        GList *ownlist;
+       char *str;
+       int len;
+
+       /* go through the last x nicks who have said something in the channel.
+          nicks of all the "own messages" are placed before others */
+        ownlist = NULL;
+       len = strlen(nick);
+        mchannel = MODULE_DATA(channel);
+       for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) {
+               LAST_MSG_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->nick, nick, len) == 0 &&
+                   glist_find_icase_string(*outlist, rec->nick) == NULL) {
+                       str = g_strconcat(rec->nick, suffix, NULL);
+                       if (completion_lowercase) g_strdown(str);
+                       if (rec->own)
+                               ownlist = g_list_append(ownlist, str);
+                        else
+                               *outlist = g_list_append(*outlist, str);
+               }
+       }
+
+        *outlist = g_list_concat(ownlist, *outlist);
+}
+
+static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
+                                        const char *nick,
+                                        const char *suffix)
+{
+       GSList *nicks, *tmp;
+       GList *list;
+       char *tnick, *str, *in, *out;
+       int len, str_len, tmplen;
+
+       g_return_val_if_fail(channel != NULL, NULL);
+
+       list = NULL;
+
+       /* get all nicks from current channel, strip non alnum chars,
+          compare again and add to completion list on matching */
+       len = strlen(nick);
+       nicks = nicklist_getnicks(channel);
+
+       str_len = 80; str = g_malloc(str_len+1);
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+               NICK_REC *rec = tmp->data;
+
+                tmplen = strlen(rec->nick);
+               if (tmplen > str_len) {
+                        str_len = tmplen*2;
+                        str = g_realloc(str, str_len+1);
+               }
+
+               /* remove non alnum chars from nick */
+               in = rec->nick; out = str;
+               while (*in != '\0') {
+                       if (i_isalnum(*in))
+                               *out++ = *in;
+                        in++;
+               }
+                *out = '\0';
+
+               /* add to list if 'cleaned' nick matches */
+               if (g_strncasecmp(str, nick, len) == 0) {
+                       tnick = g_strconcat(rec->nick, suffix, NULL);
+                       if (completion_lowercase)
+                               g_strdown(tnick);
+
+                       if (glist_find_icase_string(list, tnick) == NULL)
+                               list = g_list_append(list, tnick);
+                       else
+                                g_free(tnick);
+               }
+
+       }
+        g_free(str);
+       g_slist_free(nicks);
+
+       return list;
+}
+
+static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
+                                      const char *suffix)
+{
+       GSList *nicks, *tmp;
+       GList *list;
+       char *str;
+       int len;
+
+       g_return_val_if_fail(channel != NULL, NULL);
+       g_return_val_if_fail(nick != NULL, NULL);
+       if (*nick == '\0') return NULL;
+
+       if (suffix != NULL && *suffix == '\0')
+               suffix = NULL;
+
+       /* put first the nicks who have recently said something */
+       list = NULL;
+       complete_from_nicklist(&list, channel, nick, suffix);
+
+       /* and add the rest of the nicks too */
+       len = strlen(nick);
+       nicks = nicklist_getnicks(channel);
+       for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+               NICK_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->nick, nick, len) == 0 &&
+                   rec != channel->ownnick) {
+                       str = g_strconcat(rec->nick, suffix, NULL);
+                       if (completion_lowercase)
+                               g_strdown(str);
+                        if (glist_find_icase_string(list, str) == NULL)
+                               list = g_list_append(list, str);
+                       else
+                                g_free(str);
+               }
+       }
+       g_slist_free(nicks);
+
+       /* remove non alphanum chars from nick and search again in case
+          list is still NULL ("foo<tab>" would match "_foo_" f.e.) */
+       if (!completion_strict)
+               list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix));
+       return list;
+}
+
+/* append all strings in list2 to list1 that already aren't there and
+   free list2 */
+static GList *completion_joinlist(GList *list1, GList *list2)
+{
+       GList *old;
+
+       old = list2;
+       while (list2 != NULL) {
+               if (!glist_find_icase_string(list1, list2->data))
+                       list1 = g_list_append(list1, list2->data);
+               else
+                       g_free(list2->data);
+
+               list2 = list2->next;
+       }
+
+       g_list_free(old);
+       return list1;
+}
+
+GList *completion_get_channels(SERVER_REC *server, const char *word)
+{
+       GList *list;
+       GSList *tmp;
+       int len;
+
+       g_return_val_if_fail(word != NULL, NULL);
+       g_return_val_if_fail(*word != '\0', NULL);
+
+       len = strlen(word);
+       list = NULL;
+
+       /* first get the joined channels */
+       tmp = server == NULL ? NULL : server->channels;
+       for (; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->name, word, len) == 0)
+                       list = g_list_append(list, g_strdup(rec->name));
+       }
+
+       /* get channels from setup */
+       for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_SETUP_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->name, word, len) == 0 &&
+                   glist_find_icase_string(list, rec->name) == NULL)
+                       list = g_list_append(list, g_strdup(rec->name));
+
+       }
+
+       return list;
+}
+
+static void complete_window_nicks(GList **list, WINDOW_REC *window,
+                                  const char *word, const char *linestart)
+{
+        CHANNEL_REC *channel;
+        GList *tmplist;
+        GSList *tmp;
+        const char *nicksuffix;
+
+        nicksuffix = *linestart != '\0' ? NULL : completion_char;
+
+        channel = CHANNEL(window->active);
+
+        /* first the active channel */
+        if (channel != NULL) {
+                tmplist = completion_channel_nicks(channel, word, nicksuffix);
+                *list = completion_joinlist(*list, tmplist);
+        }
+
+        if (nicksuffix != NULL) {
+                /* completing nick at the start of line - probably answering
+                   to some other nick, don't even try to complete from
+                   non-active channels */
+                return;
+        }
+
+        /* then the rest */
+        for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+                channel = CHANNEL(tmp->data);
+                if (channel != NULL && tmp->data != window->active) {
+                        tmplist = completion_channel_nicks(channel, word,
+                                                           nicksuffix);
+                        *list = completion_joinlist(*list, tmplist);
+                }
+        }
+}
+
+static void sig_complete_word(GList **list, WINDOW_REC *window,
+                             const char *word, const char *linestart,
+                             int *want_space)
+{
+       SERVER_REC *server;
+       CHANNEL_REC *channel;
+       QUERY_REC *query;
+       char *prefix;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(window != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(linestart != NULL);
+
+       server = window->active_server;
+       if (server == NULL && servers != NULL)
+               server = servers->data;
+
+       if (server != NULL && server_ischannel(server, word)) {
+               /* probably completing a channel name */
+               *list = completion_get_channels(window->active_server, word);
+                return;
+       }
+
+       server = window->active_server;
+       if (server == NULL || !server->connected)
+               return;
+
+       if (*linestart == '\0' && *word == '\0') {
+               /* pressed TAB at the start of line - add /MSG */
+                prefix = g_strdup_printf("%cmsg", *cmdchars);
+               *list = completion_msg(server, NULL, "", prefix);
+               if (*list == NULL)
+                       *list = g_list_append(*list, g_strdup(prefix));
+               g_free(prefix);
+
+               signal_stop();
+               return;
+       }
+
+       channel = CHANNEL(window->active);
+       query = QUERY(window->active);
+       if (channel == NULL && query != NULL &&
+           g_strncasecmp(word, query->name, strlen(word)) == 0) {
+               /* completion in query */
+                *list = g_list_append(*list, g_strdup(query->name));
+       } else if (channel != NULL) {
+               /* nick completion .. we could also be completing a nick
+                  after /MSG from nicks in channel */
+               complete_window_nicks(list, window, word, linestart);
+       }
+
+       if (*list != NULL) signal_stop();
+}
+
+static SERVER_REC *line_get_server(const char *line)
+{
+       SERVER_REC *server;
+       char *tag, *ptr;
+
+       g_return_val_if_fail(line != NULL, NULL);
+       if (*line != '-') return NULL;
+
+       /* -option found - should be server tag */
+       tag = g_strdup(line+1);
+       ptr = strchr(tag, ' ');
+       if (ptr != NULL) *ptr = '\0';
+
+       server = server_find_tag(tag);
+
+       g_free(tag);
+       return server;
+}
+
+static void sig_complete_msg(GList **list, WINDOW_REC *window,
+                            const char *word, const char *line,
+                            int *want_space)
+{
+       SERVER_REC *server, *msgserver;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       server = window->active_server;
+       if (server == NULL || !server->connected)
+               return;
+
+       msgserver = line_get_server(line);
+       *list = completion_msg(server, msgserver, word, NULL);
+       if (*list != NULL) signal_stop();
+}
+
+static void sig_erase_complete_msg(WINDOW_REC *window, const char *word,
+                                  const char *line)
+{
+       SERVER_REC *server;
+       MODULE_SERVER_REC *mserver;
+        GSList *tmp;
+
+       server = line_get_server(line);
+       if (server == NULL){
+               server = window->active_server;
+               if (server == NULL)
+                        return;
+       }
+
+       if (*word == '\0')
+               return;
+
+        /* check from global list */
+       completion_last_message_remove(word);
+
+       /* check from server specific list */
+       if (server != NULL) {
+               mserver = MODULE_DATA(server);
+               for (tmp = mserver->lastmsgs; tmp != NULL; tmp = tmp->next) {
+                       LAST_MSG_REC *rec = tmp->data;
+
+                       if (g_strcasecmp(rec->nick, word) == 0) {
+                               last_msg_destroy(&mserver->lastmsgs, rec);
+                                break;
+                       }
+               }
+
+       }
+}
+
+GList *completion_get_chatnets(const char *word)
+{
+       GList *list;
+       GSList *tmp;
+       int len;
+
+       g_return_val_if_fail(word != NULL, NULL);
+
+       len = strlen(word);
+       list = NULL;
+
+       for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
+               CHATNET_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->name, word, len) == 0)
+                       list = g_list_append(list, g_strdup(rec->name));
+       }
+
+       return list;
+}
+
+GList *completion_get_servers(const char *word)
+{
+       GList *list;
+       GSList *tmp;
+       int len;
+
+       g_return_val_if_fail(word != NULL, NULL);
+
+       len = strlen(word);
+       list = NULL;
+
+       for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+               SERVER_SETUP_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->address, word, len) == 0) 
+                       list = g_list_append(list, g_strdup(rec->address));
+       }
+
+       return list;
+}
+
+static void sig_complete_connect(GList **list, WINDOW_REC *window,
+                                const char *word, const char *line, 
+                                int *want_space)
+{
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+
+       *list = completion_get_chatnets(word);
+       *list = g_list_concat(*list, completion_get_servers(word));
+       if (*list != NULL) signal_stop();
+}
+
+static void sig_complete_topic(GList **list, WINDOW_REC *window,
+                              const char *word, const char *line,
+                              int *want_space)
+{
+       const char *topic;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+
+       if (*word == '\0' && IS_CHANNEL(window->active)) {
+               topic = CHANNEL(window->active)->topic;
+               if (topic != NULL) {
+                       *list = g_list_append(NULL, g_strdup(topic));
+                        signal_stop();
+               }
+       }
+}
+
+/* expand \n, \t and \\ */
+static char *expand_escapes(const char *line, SERVER_REC *server,
+                           WI_ITEM_REC *item)
+{
+       char *ptr, *ret;
+        int chr;
+
+       ret = ptr = g_malloc(strlen(line)+1);
+       for (; *line != '\0'; line++) {
+               if (*line != '\\') {
+                       *ptr++ = *line;
+                       continue;
+               }
+
+               line++;
+               if (*line == '\0') {
+                       *ptr++ = '\\';
+                       break;
+               }
+
+               chr = expand_escape(&line);
+               if (chr == '\r' || chr == '\n') {
+                       /* newline .. we need to send another "send text"
+                          event to handle it (or actually the text before
+                          the newline..) */
+                       if (ret != ptr) {
+                               *ptr = '\0';
+                               signal_emit("send text", 3, ret, server, item);
+                               ptr = ret;
+                       }
+               } else if (chr != -1) {
+                        /* escaping went ok */
+                       *ptr++ = chr;
+               } else {
+                        /* unknown escape, add it as-is */
+                       *ptr++ = '\\';
+                       *ptr++ = *line;
+               }
+       }
+
+       *ptr = '\0';
+       return ret;
+}
+
+static char *auto_complete(CHANNEL_REC *channel, const char *line)
+{
+       GList *comp;
+       const char *p;
+        char *nick, *ret;
+
+       p = strstr(line, completion_char);
+       if (p == NULL)
+               return NULL;
+
+        nick = g_strndup(line, (int) (p-line));
+
+        ret = NULL;
+       if (nicklist_find(channel, nick) == NULL) {
+                /* not an exact match, use the first possible completion */
+               comp = completion_channel_nicks(channel, nick, NULL);
+               if (comp != NULL) {
+                       ret = g_strconcat(comp->data, p, NULL);
+                       g_list_foreach(comp, (GFunc) g_free, NULL);
+                       g_list_free(comp);
+               }
+       }
+
+       g_free(nick);
+
+        return ret;
+}
+
+static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       char *line, *str;
+
+       g_return_if_fail(data != NULL);
+       if (item == NULL) return;
+
+       line = settings_get_bool("expand_escapes") ?
+               expand_escapes(data, server, item) : g_strdup(data);
+
+       /* check for automatic nick completion */
+       if (completion_auto && IS_CHANNEL(item)) {
+               str = auto_complete(CHANNEL(item), line);
+               if (str != NULL) {
+                       g_free(line);
+                        line = str;
+               }
+       }
+
+       str = g_strdup_printf(IS_CHANNEL(item) ? "-channel %s %s" :
+                             IS_QUERY(item) ? "-nick %s %s" : "%s %s",
+                             item->name, line);
+
+       signal_emit("command msg", 3, str, server, item);
+
+       g_free(str);
+       g_free(line);
+
+       signal_stop();
+}
+
+static void sig_server_disconnected(SERVER_REC *server)
+{
+       MODULE_SERVER_REC *mserver;
+
+       g_return_if_fail(server != NULL);
+
+        mserver = MODULE_DATA(server);
+       while (mserver->lastmsgs)
+                last_msg_destroy(&mserver->lastmsgs, mserver->lastmsgs->data);
+}
+
+static void sig_channel_destroyed(CHANNEL_REC *channel)
+{
+       MODULE_CHANNEL_REC *mchannel;
+
+       g_return_if_fail(channel != NULL);
+
+        mchannel = MODULE_DATA(channel);
+       while (mchannel->lastmsgs != NULL) {
+               last_msg_destroy(&mchannel->lastmsgs,
+                                mchannel->lastmsgs->data);
+       }
+}
+
+static void read_settings(void)
+{
+       keep_privates_count = settings_get_int("completion_keep_privates");
+       keep_publics_count = settings_get_int("completion_keep_publics");
+       completion_lowercase = settings_get_bool("completion_nicks_lowercase");
+       completion_char = settings_get_str("completion_char");
+       cmdchars = settings_get_str("cmdchars");
+       completion_auto = settings_get_bool("completion_auto");
+       completion_strict = settings_get_bool("completion_strict");
+
+       if (*completion_char == '\0') {
+                /* this would break.. */
+               completion_auto = FALSE;
+       }
+}
+
+void chat_completion_init(void)
+{
+       settings_add_str("completion", "completion_char", ":");
+       settings_add_bool("completion", "completion_auto", FALSE);
+       settings_add_int("completion", "completion_keep_publics", 50);
+       settings_add_int("completion", "completion_keep_privates", 10);
+       settings_add_bool("completion", "expand_escapes", FALSE);
+       settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
+       settings_add_bool("completion", "completion_strict", FALSE);
+
+       read_settings();
+       signal_add("complete word", (SIGNAL_FUNC) sig_complete_word);
+       signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
+       signal_add("complete command query", (SIGNAL_FUNC) sig_complete_msg);
+       signal_add("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
+       signal_add("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
+       signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
+       signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
+       signal_add("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
+       signal_add("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_add("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_add("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
+       signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_add("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
+       signal_add("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
+       signal_add("send text", (SIGNAL_FUNC) event_text);
+       signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void chat_completion_deinit(void)
+{
+       while (global_lastmsgs != NULL)
+               last_msg_destroy(&global_lastmsgs, global_lastmsgs->data);
+
+       signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
+       signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
+       signal_remove("complete command query", (SIGNAL_FUNC) sig_complete_msg);
+       signal_remove("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
+       signal_remove("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
+       signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
+       signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect);
+       signal_remove("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
+       signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
+       signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
+       signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
+       signal_remove("send text", (SIGNAL_FUNC) event_text);
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/chat-completion.h b/apps/irssi/src/fe-common/core/chat-completion.h
new file mode 100644 (file)
index 0000000..3cb70ca
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __CHAT_COMPLETION_H
+#define __CHAT_COMPLETION_H
+
+void completion_last_message_add(const char *nick);
+void completion_last_message_remove(const char *nick);
+void completion_last_message_rename(const char *oldnick, const char *newnick);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/command-history.c b/apps/irssi/src/fe-common/core/command-history.c
new file mode 100644 (file)
index 0000000..70f6859
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ command-history.c : irssi
+
+    Copyright (C) 1999 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "special-vars.h"
+#include "settings.h"
+
+#include "fe-windows.h"
+#include "window-items.h"
+
+#include "command-history.h"
+
+/* command history */
+static HISTORY_REC *global_history;
+static int window_history;
+static GSList *histories;
+
+void command_history_add(HISTORY_REC *history, const char *text)
+{
+       GList *link;
+
+       g_return_if_fail(history != NULL);
+       g_return_if_fail(text != NULL);
+
+       link = g_list_last(history->list);
+       if (link != NULL && strcmp(link->data, text) == 0)
+         return; /* same as previous entry */
+
+       if (settings_get_int("max_command_history") < 1 || 
+           history->lines < settings_get_int("max_command_history"))
+               history->lines++;
+       else {
+               link = history->list;
+               g_free(link->data);
+               history->list = g_list_remove_link(history->list, link);
+               g_list_free_1(link);
+       }
+
+       history->list = g_list_append(history->list, g_strdup(text));
+}
+
+HISTORY_REC *command_history_find(HISTORY_REC *history)
+{
+       GSList *tmp;
+       tmp = g_slist_find(histories, history);
+
+       if (tmp == NULL)
+               return NULL;
+       else
+               return tmp->data;
+}
+
+HISTORY_REC *command_history_find_name(const char *name)
+{
+       GSList *tmp;
+
+       if (name == NULL)
+               return NULL;
+
+       for (tmp = histories; tmp != NULL; tmp = tmp->next) {
+               HISTORY_REC *rec = tmp->data;
+               
+               if (rec->name != NULL && g_strcasecmp(rec->name, name) == 0)
+                       return rec;
+       }
+       
+       return NULL;
+}
+
+HISTORY_REC *command_history_current(WINDOW_REC *window)
+{
+       HISTORY_REC *rec;
+
+       if (window == NULL)
+               return global_history;
+
+       if (window_history)
+               return window->history;
+
+       rec = command_history_find_name(window->history_name);
+       if (rec != NULL)
+               return rec;
+
+       return global_history;
+}
+
+const char *command_history_prev(WINDOW_REC *window, const char *text)
+{
+       HISTORY_REC *history;
+       GList *pos;
+
+       history = command_history_current(window);
+       pos = history->pos;
+
+       if (pos != NULL) {
+               history->pos = history->pos->prev;
+               if (history->pos == NULL)
+                        history->over_counter++;
+       } else {
+               history->pos = g_list_last(history->list);
+       }
+
+       if (*text != '\0' &&
+           (pos == NULL || strcmp(pos->data, text) != 0)) {
+               /* save the old entry to history */
+               command_history_add(history, text);
+       }
+
+       return history->pos == NULL ? "" : history->pos->data;
+}
+
+const char *command_history_next(WINDOW_REC *window, const char *text)
+{
+       HISTORY_REC *history;
+       GList *pos;
+
+       history = command_history_current(window);
+       pos = history->pos; 
+
+       if (pos != NULL)
+               history->pos = history->pos->next;
+       else if (history->over_counter > 0) {
+               history->over_counter--;
+               history->pos = history->list;
+       }
+
+       if (*text != '\0' &&
+           (pos == NULL || strcmp(pos->data, text) != 0)) {
+               /* save the old entry to history */
+               command_history_add(history, text);
+       }
+       return history->pos == NULL ? "" : history->pos->data;
+}
+
+void command_history_clear_pos_func(HISTORY_REC *history, gpointer user_data)
+{
+       history->over_counter = 0;
+       history->pos = NULL;
+}
+
+void command_history_clear_pos(WINDOW_REC *window)
+{
+       g_slist_foreach(histories, 
+                      (GFunc) command_history_clear_pos_func, NULL);
+}
+
+HISTORY_REC *command_history_create(const char *name)
+{
+       HISTORY_REC *rec;
+       
+       rec = g_new0(HISTORY_REC, 1);
+       
+       if (name != NULL)
+               rec->name = g_strdup(name);
+
+       histories = g_slist_append(histories, rec);
+       
+       return rec;
+}
+
+void command_history_destroy(HISTORY_REC *history)
+{
+       g_return_if_fail(history != NULL);
+
+       /* history->refcount should be 0 here, or somthing is wrong... */
+       g_return_if_fail(history->refcount == 0);
+
+       histories = g_slist_remove(histories, history);
+
+       g_list_foreach(history->list, (GFunc) g_free, NULL);
+       g_list_free(history->list);
+
+       g_free_not_null(history->name);
+       g_free(history);
+}
+
+void command_history_link(const char *name)
+{
+       HISTORY_REC *rec;
+       rec = command_history_find_name(name);
+
+       if (rec == NULL)
+               rec = command_history_create(name);
+
+       rec->refcount++;
+}
+
+void command_history_unlink(const char *name)
+{
+       HISTORY_REC *rec;
+       rec = command_history_find_name(name);
+
+       if (rec == NULL)
+               return;
+
+       if (--(rec->refcount) <= 0)
+               command_history_destroy(rec);
+}
+
+static void sig_window_created(WINDOW_REC *window, int automatic)
+{
+       window->history = command_history_create(NULL);
+}
+
+static void sig_window_destroyed(WINDOW_REC *window)
+{
+       command_history_unlink(window->history_name);
+       command_history_destroy(window->history);
+       g_free_not_null(window->history_name);
+}
+
+static void sig_window_history_changed(WINDOW_REC *window, const char *oldname)
+{
+       command_history_link(window->history_name);
+       command_history_unlink(oldname);
+}
+
+static char *special_history_func(const char *text, void *item, int *free_ret)
+{
+       WINDOW_REC *window;
+       HISTORY_REC *history;
+       GList *tmp;
+        char *findtext, *ret;
+
+       window = item == NULL ? active_win : window_item_window(item);
+
+       findtext = g_strdup_printf("*%s*", text);
+       ret = NULL;
+
+       history = command_history_current(window);
+       for (tmp = history->list; tmp != NULL; tmp = tmp->next) {
+               const char *line = tmp->data;
+
+               if (match_wildcards(findtext, line)) {
+                       *free_ret = TRUE;
+                        ret = g_strdup(line);
+               }
+       }
+       g_free(findtext);
+
+       return ret;
+}
+
+static void read_settings(void)
+{
+       window_history = settings_get_bool("window_history");
+}
+
+void command_history_init(void)
+{
+       settings_add_int("history", "max_command_history", 100);
+       settings_add_bool("history", "window_history", FALSE);
+
+       special_history_func_set(special_history_func);
+
+       global_history = command_history_create(NULL);
+
+       read_settings();
+       signal_add("window created", (SIGNAL_FUNC) sig_window_created);
+       signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_add("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void command_history_deinit(void)
+{
+       signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
+       signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_remove("window history changed", (SIGNAL_FUNC) sig_window_history_changed);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_history_destroy(global_history);
+}
diff --git a/apps/irssi/src/fe-common/core/command-history.h b/apps/irssi/src/fe-common/core/command-history.h
new file mode 100644 (file)
index 0000000..2ca312e
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef __COMMAND_HISTORY_H
+#define __COMMAND_HISTORY_H
+
+#include "common.h"
+
+typedef struct {
+       char *name;
+
+       GList *list, *pos;
+       int lines, over_counter;
+
+       int refcount;
+} HISTORY_REC;
+
+HISTORY_REC *command_history_find(HISTORY_REC *history);
+HISTORY_REC *command_history_find_name(const char *name);
+
+HISTORY_REC *command_history_current(WINDOW_REC *window);
+
+void command_history_init(void);
+void command_history_deinit(void);
+
+void command_history_add(HISTORY_REC *window, const char *text);
+
+const char *command_history_prev(WINDOW_REC *window, const char *text);
+const char *command_history_next(WINDOW_REC *window, const char *text);
+
+void command_history_clear_pos(WINDOW_REC *window);
+
+HISTORY_REC *command_history_create(const char *name);
+void command_history_destroy(HISTORY_REC *history);
+void command_history_link(const char *name);
+void command_history_unlink(const char *name);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/completion.c b/apps/irssi/src/fe-common/core/completion.c
new file mode 100644 (file)
index 0000000..f10fbb6
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ completion.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "completion.h"
+
+#define wordreplace_find(word) \
+       iconfig_list_find("replaces", "text", word, "replace")
+
+#define completion_find(completion) \
+       iconfig_list_find("completions", "short", completion, "long")
+
+static GList *complist; /* list of commands we're currently completing */
+static char *last_line;
+static int last_want_space, last_line_pos;
+
+#define isseparator_notspace(c) \
+        ((c) == ',')
+
+#define isseparator(c) \
+       (i_isspace(c) || isseparator_notspace(c))
+
+void chat_completion_init(void);
+void chat_completion_deinit(void);
+
+/* Return whole word at specified position in string */
+char *get_word_at(const char *str, int pos, char **startpos)
+{
+       const char *start, *end;
+
+       g_return_val_if_fail(str != NULL, NULL);
+       g_return_val_if_fail(pos >= 0, NULL);
+
+       /* get previous word if char at `pos' is space */
+       start = str+pos;
+       while (start > str && isseparator(start[-1])) start--;
+
+       end = start;
+       while (start > str && !isseparator(start[-1])) start--;
+       while (*end != '\0' && !isseparator(*end)) end++;
+       while (*end != '\0' && isseparator_notspace(*end)) end++;
+
+       *startpos = (char *) start;
+       return g_strndup(start, (int) (end-start));
+}
+
+/* automatic word completion - called when space/enter is pressed */
+char *auto_word_complete(const char *line, int *pos)
+{
+       GString *result;
+       const char *replace;
+       char *word, *wordstart, *ret;
+       int startpos;
+
+       g_return_val_if_fail(line != NULL, NULL);
+       g_return_val_if_fail(pos != NULL, NULL);
+
+       word = get_word_at(line, *pos, &wordstart);
+       startpos = (int) (wordstart-line);
+
+       result = g_string_new(line);
+       g_string_erase(result, startpos, strlen(word));
+
+       /* check for words in autocompletion list */
+       replace = wordreplace_find(word);
+       if (replace == NULL) {
+               ret = NULL;
+               g_string_free(result, TRUE);
+       } else {
+               *pos = startpos+strlen(replace);
+
+               g_string_insert(result, startpos, replace);
+               ret = result->str;
+               g_string_free(result, FALSE);
+       }
+
+       g_free(word);
+       return ret;
+}
+
+static void free_completions(void)
+{
+       complist = g_list_first(complist);
+
+       g_list_foreach(complist, (GFunc) g_free, NULL);
+       g_list_free(complist);
+        complist = NULL;
+
+       g_free_and_null(last_line);
+}
+
+/* manual word completion - called when TAB is pressed */
+char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase)
+{
+       static int startpos = 0, wordlen = 0;
+        int old_startpos, old_wordlen;
+
+       GString *result;
+       char *word, *wordstart, *linestart, *ret;
+       int continue_complete, want_space;
+
+       g_return_val_if_fail(line != NULL, NULL);
+       g_return_val_if_fail(pos != NULL, NULL);
+
+       continue_complete = complist != NULL && *pos == last_line_pos &&
+               strcmp(line, last_line) == 0;
+
+       old_startpos = startpos;
+       old_wordlen = wordlen;
+
+       if (!erase && continue_complete) {
+               word = NULL;
+                linestart = NULL;
+       } else {
+               /* get the word we want to complete */
+               word = get_word_at(line, *pos, &wordstart);
+               startpos = (int) (wordstart-line);
+               wordlen = strlen(word);
+
+               /* get the start of line until the word we're completing */
+               if (isseparator(*line)) {
+                       /* empty space at the start of line */
+                       if (wordstart == line)
+                               wordstart += strlen(wordstart);
+               } else {
+                       while (wordstart > line && isseparator(wordstart[-1]))
+                               wordstart--;
+               }
+               linestart = g_strndup(line, (int) (wordstart-line));
+
+               /* completions usually add space after the word, that makes
+                  things a bit harder. When continuing a completion
+                  "/msg nick1 "<tab> we have to cycle to nick2, etc.
+                  BUT if we start completion with "/msg "<tab>, we don't
+                  want to complete the /msg word, but instead complete empty
+                  word with /msg being in linestart. */
+               if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
+                   (*linestart == '\0' || wordstart[-1] != ' ')) {
+                       char *old;
+
+                        old = linestart;
+                       linestart = *linestart == '\0' ?
+                               g_strdup(word) :
+                               g_strconcat(linestart, " ", word, NULL);
+                       g_free(old);
+
+                       g_free(word);
+                       word = g_strdup("");
+                       startpos = strlen(linestart)+1;
+                       wordlen = 0;
+               }
+
+       }
+
+       if (erase) {
+               signal_emit("complete erase", 3, window, word, linestart);
+
+               if (!continue_complete)
+                        return NULL;
+
+                /* jump to next completion */
+               word = NULL;
+               linestart = NULL;
+                startpos = old_startpos;
+               wordlen = old_wordlen;
+       }
+
+       if (continue_complete) {
+               /* complete from old list */
+               complist = complist->next != NULL ? complist->next :
+                       g_list_first(complist);
+               want_space = last_want_space;
+       } else {
+               /* get new completion list */
+               free_completions();
+
+               want_space = TRUE;
+               signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
+               last_want_space = want_space;
+       }
+
+       g_free(linestart);
+       g_free(word);
+
+       if (complist == NULL)
+               return NULL;
+
+       /* word completed */
+       *pos = startpos+strlen(complist->data);
+
+       /* replace the word in line - we need to return
+          a full new line */
+       result = g_string_new(line);
+       g_string_erase(result, startpos, wordlen);
+       g_string_insert(result, startpos, complist->data);
+
+       if (want_space) {
+               if (!isseparator(result->str[*pos]))
+                       g_string_insert_c(result, *pos, ' ');
+               (*pos)++;
+       }
+
+       wordlen = strlen(complist->data);
+       last_line_pos = *pos;
+       g_free_not_null(last_line);
+       last_line = g_strdup(result->str);
+
+       ret = result->str;
+       g_string_free(result, FALSE);
+       return ret;
+}
+
+#define IS_CURRENT_DIR(dir) \
+        ((dir)[0] == '.' && ((dir)[1] == '\0' || (dir)[1] == G_DIR_SEPARATOR))
+
+#define USE_DEFAULT_PATH(path, default_path) \
+       ((!g_path_is_absolute(path) || IS_CURRENT_DIR(path)) && \
+        default_path != NULL)
+
+GList *list_add_file(GList *list, const char *name, const char *default_path)
+{
+       struct stat statbuf;
+       char *fname;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       fname = convert_home(name);
+       if (USE_DEFAULT_PATH(fname, default_path)) {
+                g_free(fname);
+               fname = g_strconcat(default_path, G_DIR_SEPARATOR_S,
+                                   name, NULL);
+       }
+       if (stat(fname, &statbuf) == 0) {
+               list = g_list_append(list, !S_ISDIR(statbuf.st_mode) ? g_strdup(name) :
+                                    g_strconcat(name, G_DIR_SEPARATOR_S, NULL));
+       }
+
+        g_free(fname);
+       return list;
+}
+
+GList *filename_complete(const char *path, const char *default_path)
+{
+        GList *list;
+       DIR *dirp;
+       struct dirent *dp;
+       char *realpath, *dir, *basename, *name;
+       int len;
+
+       g_return_val_if_fail(path != NULL, NULL);
+
+       list = NULL;
+
+       /* get directory part of the path - expand ~/ */
+       realpath = convert_home(path);
+       if (USE_DEFAULT_PATH(realpath, default_path)) {
+                g_free(realpath);
+               realpath = g_strconcat(default_path, G_DIR_SEPARATOR_S,
+                                      path, NULL);
+       }
+
+       /* open directory for reading */
+       dir = g_dirname(realpath);
+       dirp = opendir(dir);
+       g_free(dir);
+        g_free(realpath);
+
+       if (dirp == NULL)
+               return NULL;
+
+       dir = g_dirname(path);
+       if (*dir == G_DIR_SEPARATOR && dir[1] == '\0') {
+                /* completing file in root directory */
+               *dir = '\0';
+       } else if (IS_CURRENT_DIR(dir) && !IS_CURRENT_DIR(path)) {
+               /* completing file in default_path
+                  (path not set, and leave it that way) */
+               g_free_and_null(dir);
+       }
+
+       basename = g_basename(path);
+       len = strlen(basename);
+
+       /* add all files in directory to completion list */
+       while ((dp = readdir(dirp)) != NULL) {
+               if (dp->d_name[0] == '.') {
+                       if (dp->d_name[1] == '\0' ||
+                           (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
+                               continue; /* skip . and .. */
+
+                       if (basename[0] != '.')
+                               continue;
+               }
+
+               if (len == 0 || strncmp(dp->d_name, basename, len) == 0) {
+                       name = dir == NULL ? g_strdup(dp->d_name) :
+                               g_strdup_printf("%s"G_DIR_SEPARATOR_S"%s", dir, dp->d_name);
+                       list = list_add_file(list, name, default_path);
+                       g_free(name);
+               }
+       }
+       closedir(dirp);
+
+       g_free_not_null(dir);
+        return list;
+}
+
+static GList *completion_get_settings(const char *key)
+{
+       GList *complist;
+       GSList *tmp, *sets;
+       int len;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       sets = settings_get_sorted();
+
+       len = strlen(key);
+       complist = NULL;
+       for (tmp = sets; tmp != NULL; tmp = tmp->next) {
+               SETTINGS_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->key, key, len) == 0)
+                       complist = g_list_insert_sorted(complist, g_strdup(rec->key), (GCompareFunc) g_istr_cmp);
+       }
+       g_slist_free(sets);
+       return complist;
+}
+
+static GList *completion_get_bool_settings(const char *key)
+{
+       GList *complist;
+       GSList *tmp, *sets;
+       int len;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       sets = settings_get_sorted();
+
+       len = strlen(key);
+       complist = NULL;
+       for (tmp = sets; tmp != NULL; tmp = tmp->next) {
+               SETTINGS_REC *rec = tmp->data;
+
+               if (rec->type == SETTING_TYPE_BOOLEAN &&
+                   g_strncasecmp(rec->key, key, len) == 0)
+                       complist = g_list_insert_sorted(complist, g_strdup(rec->key), (GCompareFunc) g_istr_cmp);
+       }
+       g_slist_free(sets);
+       return complist;
+}
+
+static GList *completion_get_aliases(const char *alias, char cmdchar)
+{
+       CONFIG_NODE *node;
+       GList *complist;
+       GSList *tmp;
+       char *word;
+       int len;
+
+       g_return_val_if_fail(alias != NULL, NULL);
+
+       /* get list of aliases from mainconfig */
+       node = iconfig_node_traverse("aliases", FALSE);
+       tmp = node == NULL ? NULL : config_node_first(node->value);
+
+       len = strlen(alias);
+       complist = NULL;
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               CONFIG_NODE *node = tmp->data;
+
+               if (node->type != NODE_TYPE_KEY)
+                       continue;
+
+               if (g_strncasecmp(node->key, alias, len) == 0) {
+                       word = g_strdup_printf("%c%s", cmdchar, node->key);
+                       /* add matching alias to completion list, aliases will
+                          be appended after command completions and kept in
+                          uppercase to show it's an alias */
+                       if (glist_find_icase_string(complist, word) == NULL)
+                               complist = g_list_insert_sorted(complist, word, (GCompareFunc) g_istr_cmp);
+                       else
+                               g_free(word);
+               }
+       }
+       return complist;
+}
+
+static GList *completion_get_commands(const char *cmd, char cmdchar)
+{
+       GList *complist;
+       GSList *tmp;
+       char *word;
+       int len;
+
+       g_return_val_if_fail(cmd != NULL, NULL);
+
+       len = strlen(cmd);
+       complist = NULL;
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               if (strchr(rec->cmd, ' ') != NULL)
+                       continue;
+
+               if (g_strncasecmp(rec->cmd, cmd, len) == 0) {
+                       word = cmdchar == '\0' ? g_strdup(rec->cmd) :
+                               g_strdup_printf("%c%s", cmdchar, rec->cmd);
+                       if (glist_find_icase_string(complist, word) == NULL)
+                               complist = g_list_insert_sorted(complist, word, (GCompareFunc) g_istr_cmp);
+                       else
+                               g_free(word);
+               }
+       }
+       return complist;
+}
+
+static GList *completion_get_subcommands(const char *cmd)
+{
+       GList *complist;
+       GSList *tmp;
+       char *spacepos;
+       int len, skip;
+
+       g_return_val_if_fail(cmd != NULL, NULL);
+
+       /* get the number of chars to skip at the start of command. */
+       spacepos = strrchr(cmd, ' ');
+       skip = spacepos == NULL ? strlen(cmd)+1 :
+               ((int) (spacepos-cmd) + 1);
+
+       len = strlen(cmd);
+       complist = NULL;
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               if ((int)strlen(rec->cmd) < len)
+                       continue;
+
+               if (strchr(rec->cmd+len, ' ') != NULL)
+                       continue;
+
+               if (g_strncasecmp(rec->cmd, cmd, len) == 0)
+                       complist = g_list_insert_sorted(complist, g_strdup(rec->cmd+skip), (GCompareFunc) g_istr_cmp);
+       }
+       return complist;
+}
+
+GList *completion_get_options(const char *cmd, const char *option)
+{
+       COMMAND_REC *rec;
+       GList *list;
+       char **tmp;
+       int len;
+
+       g_return_val_if_fail(cmd != NULL, NULL);
+       g_return_val_if_fail(option != NULL, NULL);
+
+       rec = command_find(cmd);
+       if (rec == NULL || rec->options == NULL) return NULL;
+
+       list = NULL;
+       len = strlen(option);
+       for (tmp = rec->options; *tmp != NULL; tmp++) {
+               const char *optname = *tmp + iscmdtype(**tmp);
+
+               if (len == 0 || g_strncasecmp(optname, option, len) == 0)
+                        list = g_list_append(list, g_strconcat("-", optname, NULL));
+       }
+
+       return list;
+}
+
+/* split the line to command and arguments */
+static char *line_get_command(const char *line, char **args, int aliases)
+{
+       const char *ptr, *cmdargs;
+       char *cmd, *checkcmd;
+
+       g_return_val_if_fail(line != NULL, NULL);
+       g_return_val_if_fail(args != NULL, NULL);
+
+       cmd = checkcmd = NULL; *args = "";
+       cmdargs = NULL; ptr = line;
+
+       do {
+               ptr = strchr(ptr, ' ');
+               if (ptr == NULL) {
+                       checkcmd = g_strdup(line);
+                       cmdargs = "";
+               } else {
+                       checkcmd = g_strndup(line, (int) (ptr-line));
+
+                       while (i_isspace(*ptr)) ptr++;
+                       cmdargs = ptr;
+               }
+
+               if (aliases ? !alias_find(checkcmd) :
+                   !command_find(checkcmd)) {
+                       /* not found, use the previous */
+                       g_free(checkcmd);
+                       break;
+               }
+
+               /* found, check if it has subcommands */
+               g_free_not_null(cmd);
+               if (!aliases)
+                       cmd = checkcmd;
+               else {
+                        cmd = g_strdup(alias_find(checkcmd));
+                       g_free(checkcmd);
+               }
+               *args = (char *) cmdargs;
+       } while (ptr != NULL);
+
+        if (cmd != NULL)
+               g_strdown(cmd);
+       return cmd;
+}
+
+static char *expand_aliases(const char *line)
+{
+        char *cmd, *args, *ret;
+
+       g_return_val_if_fail(line != NULL, NULL);
+
+       cmd = line_get_command(line, &args, TRUE);
+       if (cmd == NULL) return g_strdup(line);
+       if (*args == '\0') return cmd;
+
+       ret = g_strconcat(cmd, " ", args, NULL);
+       g_free(cmd);
+       return ret;
+}
+
+static void sig_complete_word(GList **list, WINDOW_REC *window,
+                             const char *word, const char *linestart,
+                             int *want_space)
+{
+       const char *newword, *cmdchars;
+       char *signal, *cmd, *args, *line;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(linestart != NULL);
+
+       /* check against "completion words" list */
+       newword = completion_find(word);
+       if (newword != NULL) {
+               *list = g_list_append(*list, g_strdup(newword));
+
+               signal_stop();
+               return;
+       }
+
+       /* command completion? */
+       cmdchars = settings_get_str("cmdchars");
+       if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) {
+               /* complete /command */
+               *list = completion_get_commands(word+1, *word);
+
+               /* complete aliases, too */
+               *list = g_list_concat(*list,
+                                     completion_get_aliases(word+1, *word));
+
+               if (*list != NULL) signal_stop();
+               return;
+       }
+
+       /* check only for /command completions from now on */
+       if (*linestart == '\0')
+               return;
+
+        cmdchars = strchr(cmdchars, *linestart);
+       if (cmdchars == NULL) return;
+
+        /* check if there's aliases */
+       line = linestart[1] == *cmdchars ? g_strdup(linestart+2) :
+               expand_aliases(linestart+1);
+
+       cmd = line_get_command(line, &args, FALSE);
+       if (cmd == NULL) {
+               g_free(line);
+               return;
+       }
+
+       /* we're completing -option? */
+       if (*word == '-') {
+               *list = completion_get_options(cmd, word+1);
+               g_free(cmd);
+               g_free(line);
+               return;
+       }
+
+       /* complete parameters */
+       signal = g_strconcat("complete command ", cmd, NULL);
+       signal_emit(signal, 5, list, window, word, args, want_space);
+
+       if (command_have_sub(line)) {
+               /* complete subcommand */
+               g_free(cmd);
+               cmd = g_strconcat(line, " ", word, NULL);
+               *list = g_list_concat(completion_get_subcommands(cmd), *list);
+
+               if (*list != NULL) signal_stop();
+       }
+
+       g_free(signal);
+       g_free(cmd);
+
+       g_free(line);
+}
+
+static void sig_complete_erase(WINDOW_REC *window, const char *word,
+                              const char *linestart)
+{
+       const char *cmdchars;
+        char *line, *cmd, *args, *signal;
+
+       if (*linestart == '\0')
+               return;
+
+        /* we only want to check for commands */
+       cmdchars = settings_get_str("cmdchars");
+        cmdchars = strchr(cmdchars, *linestart);
+       if (cmdchars == NULL)
+               return;
+
+        /* check if there's aliases */
+       line = linestart[1] == *cmdchars ? g_strdup(linestart+2) :
+               expand_aliases(linestart+1);
+
+       cmd = line_get_command(line, &args, FALSE);
+       if (cmd == NULL) {
+               g_free(line);
+               return;
+       }
+
+       signal = g_strconcat("complete erase command ", cmd, NULL);
+       signal_emit(signal, 3, window, word, args);
+
+        g_free(signal);
+       g_free(cmd);
+       g_free(line);
+}
+
+static void sig_complete_set(GList **list, WINDOW_REC *window,
+                            const char *word, const char *line, int *want_space)
+{
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       if (*line != '\0') return;
+
+       *list = completion_get_settings(word);
+       if (*list != NULL) signal_stop();
+}
+
+static void sig_complete_toggle(GList **list, WINDOW_REC *window,
+                               const char *word, const char *line, int *want_space)
+{
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       if (*line != '\0') return;
+
+       *list = completion_get_bool_settings(word);
+       if (*list != NULL) signal_stop();
+}
+
+/* first argument of command is file name - complete it */
+static void sig_complete_filename(GList **list, WINDOW_REC *window,
+                                 const char *word, const char *line, int *want_space)
+{
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       if (*line != '\0') return;
+
+       *list = filename_complete(word, NULL);
+       if (*list != NULL) {
+               *want_space = FALSE;
+               signal_stop();
+       }
+}
+
+/* first argument of command is .. command :) (/HELP command) */
+static void sig_complete_command(GList **list, WINDOW_REC *window,
+                                 const char *word, const char *line, int *want_space)
+{
+       char *cmd;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       if (*line == '\0') {
+               /* complete base command */
+               *list = completion_get_commands(word, '\0');
+       } else if (command_have_sub(line)) {
+               /* complete subcommand */
+                cmd = g_strconcat(line, " ", word, NULL);
+               *list = completion_get_subcommands(cmd);
+               g_free(cmd);
+       }
+
+       if (*list != NULL) signal_stop();
+}
+
+void completion_init(void)
+{
+       complist = NULL;
+       last_line = NULL; last_line_pos = -1;
+
+       chat_completion_init();
+
+       signal_add_first("complete word", (SIGNAL_FUNC) sig_complete_word);
+       signal_add_first("complete erase", (SIGNAL_FUNC) sig_complete_erase);
+       signal_add("complete command set", (SIGNAL_FUNC) sig_complete_set);
+       signal_add("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
+       signal_add("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
+       signal_add("complete command save", (SIGNAL_FUNC) sig_complete_filename);
+       signal_add("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
+       signal_add("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
+       signal_add("complete command rawlog save", (SIGNAL_FUNC) sig_complete_filename);
+       signal_add("complete command help", (SIGNAL_FUNC) sig_complete_command);
+}
+
+void completion_deinit(void)
+{
+        free_completions();
+
+       chat_completion_deinit();
+
+       signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
+       signal_remove("complete erase", (SIGNAL_FUNC) sig_complete_erase);
+       signal_remove("complete command set", (SIGNAL_FUNC) sig_complete_set);
+       signal_remove("complete command toggle", (SIGNAL_FUNC) sig_complete_toggle);
+       signal_remove("complete command cat", (SIGNAL_FUNC) sig_complete_filename);
+       signal_remove("complete command save", (SIGNAL_FUNC) sig_complete_filename);
+       signal_remove("complete command reload", (SIGNAL_FUNC) sig_complete_filename);
+       signal_remove("complete command rawlog open", (SIGNAL_FUNC) sig_complete_filename);
+       signal_remove("complete command rawlog save", (SIGNAL_FUNC) sig_complete_filename);
+       signal_remove("complete command help", (SIGNAL_FUNC) sig_complete_command);
+}
+
+
diff --git a/apps/irssi/src/fe-common/core/completion.h b/apps/irssi/src/fe-common/core/completion.h
new file mode 100644 (file)
index 0000000..9a8b32c
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __COMPLETION_H
+#define __COMPLETION_H
+
+#include "window-items.h"
+
+/* automatic word completion - called when space/enter is pressed */
+char *auto_word_complete(const char *line, int *pos);
+/* manual word completion - called when TAB is pressed. if erase is TRUE,
+   the word is removed from completion list entirely (if possible) and
+   next completion is used */
+char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase);
+
+GList *filename_complete(const char *path, const char *default_path);
+
+void completion_init(void);
+void completion_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-channels.c b/apps/irssi/src/fe-common/core/fe-channels.c
new file mode 100644 (file)
index 0000000..fe41ea3
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ fe-channels.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "channels.h"
+#include "channels-setup.h"
+#include "nicklist.h"
+
+#include "fe-windows.h"
+#include "fe-channels.h"
+#include "window-items.h"
+#include "printtext.h"
+
+static void signal_channel_created(CHANNEL_REC *channel, void *automatic)
+{
+       if (window_item_window(channel) == NULL) {
+               window_item_create((WI_ITEM_REC *) channel,
+                                  GPOINTER_TO_INT(automatic));
+       }
+}
+
+static void signal_channel_created_curwin(CHANNEL_REC *channel)
+{
+       g_return_if_fail(channel != NULL);
+
+       window_item_add(active_win, (WI_ITEM_REC *) channel, FALSE);
+}
+
+static void signal_channel_destroyed(CHANNEL_REC *channel)
+{
+       WINDOW_REC *window;
+
+       g_return_if_fail(channel != NULL);
+
+       window = window_item_window((WI_ITEM_REC *) channel);
+       if (window == NULL)
+               return;
+
+       window_item_destroy((WI_ITEM_REC *) channel);
+
+       if (channel->joined && !channel->left &&
+           channel->server != NULL) {
+               /* kicked out from channel */
+               window_bind_add(window, channel->server->tag,
+                               channel->name);
+       } else if (!channel->joined || channel->left)
+               window_auto_destroy(window);
+}
+
+static void sig_disconnected(SERVER_REC *server)
+{
+       WINDOW_REC *window;
+       GSList *tmp;
+
+       g_return_if_fail(IS_SERVER(server));
+
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *channel = tmp->data;
+
+               window = window_item_window((WI_ITEM_REC *) channel);
+               window_bind_add(window, server->tag, channel->name);
+       }
+}
+
+static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item)
+{
+       g_return_if_fail(window != NULL);
+       if (item == NULL) return;
+
+       if (g_slist_length(window->items) > 1 && IS_CHANNEL(item)) {
+               printformat(item->server, item->name, MSGLEVEL_CLIENTNOTICE,
+                           TXT_TALKING_IN, item->name);
+                signal_stop();
+       }
+}
+
+static void cmd_wjoin_pre(const char *data)
+{
+       GHashTable *optlist;
+       char *nick;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "join", &optlist, &nick))
+                return;
+
+       if (g_hash_table_lookup(optlist, "window") != NULL) {
+               signal_add("channel created",
+                          (SIGNAL_FUNC) signal_channel_created_curwin);
+        }
+       cmd_params_free(free_arg);
+}
+
+static void cmd_join(const char *data, SERVER_REC *server)
+{
+       WINDOW_REC *window;
+        CHANNEL_REC *channel;
+
+        if (strchr(data, ' ') != NULL || strchr(data, ',') != NULL)
+                return;
+
+        channel = channel_find(server, data);
+       if (channel == NULL)
+               return;
+
+        window = window_item_window(channel);
+
+       if (window == active_win) {
+               /* channel is in active window, set it active */
+               window_item_set_active(active_win,
+                                      (WI_ITEM_REC *) channel);
+       } else {
+               /* notify user how to move the channel to active
+                  window. this was used to be done automatically
+                  but it just confused everyone who did it
+                  accidentally */
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_CHANNEL_MOVE_NOTIFY, channel->name,
+                                  window->refnum);
+       }
+}
+
+static void cmd_wjoin_post(const char *data)
+{
+       GHashTable *optlist;
+       char *nick;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "join", &optlist, &nick))
+               return;
+
+       if (g_hash_table_lookup(optlist, "window") != NULL) {
+               signal_remove("channel created",
+                          (SIGNAL_FUNC) signal_channel_created_curwin);
+       }
+       cmd_params_free(free_arg);
+}
+
+static void cmd_channel_list_joined(void)
+{
+       CHANNEL_REC *channel;
+       GString *nicks;
+       GSList *nicklist, *tmp, *ntmp;
+
+       if (channels == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_IN_CHANNELS);
+               return;
+       }
+
+       /* print active channel */
+       channel = CHANNEL(active_win->active);
+       if (channel != NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CURRENT_CHANNEL, channel->name);
+
+       /* print list of all channels, their modes, server tags and nicks */
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_HEADER);
+       for (tmp = channels; tmp != NULL; tmp = tmp->next) {
+               channel = tmp->data;
+
+               nicklist = nicklist_getnicks(channel);
+               nicks = g_string_new(NULL);
+               for (ntmp = nicklist; ntmp != NULL; ntmp = ntmp->next) {
+                       NICK_REC *rec = ntmp->data;
+
+                       g_string_sprintfa(nicks, "%s ", rec->nick);
+               }
+
+               if (nicks->len > 1) g_string_truncate(nicks, nicks->len-1);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_LINE,
+                           channel->name, channel->mode, channel->server->tag, nicks->str);
+
+               g_slist_free(nicklist);
+               g_string_free(nicks, TRUE);
+       }
+}
+
+/* SYNTAX: CHANNEL LIST */
+static void cmd_channel_list(void)
+{
+       GString *str;
+       GSList *tmp;
+
+       str = g_string_new(NULL);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_HEADER);
+       for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_SETUP_REC *rec = tmp->data;
+
+               g_string_truncate(str, 0);
+               if (rec->autojoin)
+                       g_string_append(str, "autojoin, ");
+               if (rec->botmasks != NULL && *rec->botmasks != '\0')
+                       g_string_sprintfa(str, "bots: %s, ", rec->botmasks);
+               if (rec->autosendcmd != NULL && *rec->autosendcmd != '\0')
+                       g_string_sprintfa(str, "botcmd: %s, ", rec->autosendcmd);
+
+               if (str->len > 2) g_string_truncate(str, str->len-2);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_LINE,
+                           rec->name, rec->chatnet == NULL ? "" : rec->chatnet,
+                           rec->password == NULL ? "" : rec->password, str->str);
+       }
+       g_string_free(str, TRUE);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_FOOTER);
+}
+
+static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       if (*data == '\0')
+               cmd_channel_list_joined();
+       else if (server != NULL && server_ischannel(server, data)) {
+               signal_emit("command join", 3, data, server, item);
+       } else {
+               command_runsub("channel", data, server, item);
+       }
+}
+
+/* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
+                       <channel> <chatnet> [<password>] */
+static void cmd_channel_add(const char *data)
+{
+       GHashTable *optlist;
+        CHATNET_REC *chatnetrec;
+       CHANNEL_SETUP_REC *rec;
+       char *botarg, *botcmdarg, *chatnet, *channel, *password;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
+                           "channel add", &optlist, &channel, &chatnet, &password))
+               return;
+
+       if (*chatnet == '\0' || *channel == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       chatnetrec = chatnet_find(chatnet);
+       if (chatnetrec == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_UNKNOWN_CHATNET, chatnet);
+               cmd_params_free(free_arg);
+                return;
+       }
+
+       botarg = g_hash_table_lookup(optlist, "bots");
+       botcmdarg = g_hash_table_lookup(optlist, "botcmd");
+
+       rec = channel_setup_find(channel, chatnet);
+       if (rec == NULL) {
+               rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
+               rec->name = g_strdup(channel);
+               rec->chatnet = g_strdup(chatnet);
+       } else {
+               if (g_hash_table_lookup(optlist, "bots")) g_free_and_null(rec->botmasks);
+               if (g_hash_table_lookup(optlist, "botcmd")) g_free_and_null(rec->autosendcmd);
+               if (*password != '\0') g_free_and_null(rec->password);
+       }
+       if (g_hash_table_lookup(optlist, "auto")) rec->autojoin = TRUE;
+       if (g_hash_table_lookup(optlist, "noauto")) rec->autojoin = FALSE;
+       if (botarg != NULL && *botarg != '\0') rec->botmasks = g_strdup(botarg);
+       if (botcmdarg != NULL && *botcmdarg != '\0') rec->autosendcmd = g_strdup(botcmdarg);
+       if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
+
+       signal_emit("channel add fill", 2, rec, optlist);
+
+       channel_setup_create(rec);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_CHANSETUP_ADDED, channel, chatnet);
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: CHANNEL REMOVE <channel> <chatnet> */
+static void cmd_channel_remove(const char *data)
+{
+       CHANNEL_SETUP_REC *rec;
+       char *chatnet, *channel;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 2, &channel, &chatnet))
+               return;
+       if (*chatnet == '\0' || *channel == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       rec = channel_setup_find(channel, chatnet);
+       if (rec == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_NOT_FOUND, channel, chatnet);
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_REMOVED, channel, chatnet);
+               channel_setup_remove(rec);
+       }
+       cmd_params_free(free_arg);
+}
+
+static int get_nick_length(void *data)
+{
+        return strlen(((NICK_REC *) data)->nick);
+}
+
+static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
+{
+        WINDOW_REC *window;
+       TEXT_DEST_REC dest;
+       GString *str;
+       GSList *tmp;
+        char *format, *stripped, *prefix_format;
+       char *linebuf, nickmode[2] = { 0, 0 };
+       int *columns, cols, rows, last_col_rows, col, row, max_width;
+        int item_extra, linebuf_size, formatnum;
+
+       window = window_find_closest(channel->server, channel->name,
+                                    MSGLEVEL_CLIENTCRAP);
+        max_width = window->width;
+
+        /* get the length of item extra stuff ("[ ] ") */
+       format = format_get_text(MODULE_NAME, NULL,
+                                channel->server, channel->name,
+                                TXT_NAMES_NICK, " ", "");
+       stripped = strip_codes(format);
+       item_extra = strlen(stripped);
+        g_free(stripped);
+       g_free(format);
+
+       if (settings_get_int("names_max_width") > 0 &&
+           settings_get_int("names_max_width") < max_width)
+               max_width = settings_get_int("names_max_width");
+
+        /* remove width of the timestamp from max_width */
+       format_create_dest(&dest, channel->server, channel->name,
+                          MSGLEVEL_CLIENTCRAP, NULL);
+       format = format_get_line_start(current_theme, &dest, time(NULL));
+       if (format != NULL) {
+               stripped = strip_codes(format);
+               max_width -= strlen(stripped);
+               g_free(stripped);
+               g_free(format);
+       }
+
+        /* remove width of the prefix from max_width */
+       prefix_format = format_get_text(MODULE_NAME, NULL,
+                                       channel->server, channel->name,
+                                       TXT_NAMES_PREFIX, channel->name);
+       if (prefix_format != NULL) {
+               stripped = strip_codes(prefix_format);
+               max_width -= strlen(stripped);
+               g_free(stripped);
+       }
+
+       if (max_width <= 0) {
+               /* we should always have at least some space .. if we
+                  really don't, it won't show properly anyway. */
+               max_width = 10;
+       }
+
+       /* calculate columns */
+       cols = get_max_column_count(nicklist, get_nick_length, max_width,
+                                   settings_get_int("names_max_columns"),
+                                   item_extra, 3, &columns, &rows);
+       nicklist = columns_sort_list(nicklist, rows);
+
+        /* rows in last column */
+       last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
+       if (last_col_rows == 0)
+                last_col_rows = rows;
+
+       str = g_string_new(prefix_format);
+       linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
+
+        col = 0; row = 0;
+       for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
+               NICK_REC *rec = tmp->data;
+
+               if (rec->op)
+                       nickmode[0] = '@';
+               else if (rec->halfop)
+                       nickmode[0] = '%';
+               else if (rec->voice)
+                       nickmode[0] = '+';
+               else
+                       nickmode[0] = ' ';
+               
+               if (linebuf_size < columns[col]-item_extra+1) {
+                       linebuf_size = (columns[col]-item_extra+1)*2;
+                        linebuf = g_realloc(linebuf, linebuf_size);
+               }
+               memset(linebuf, ' ', columns[col]-item_extra);
+               linebuf[columns[col]-item_extra] = '\0';
+               memcpy(linebuf, rec->nick, strlen(rec->nick));
+
+               formatnum = rec->op ? TXT_NAMES_NICK_OP :
+                       rec->halfop ? TXT_NAMES_NICK_HALFOP :
+                       rec->voice ? TXT_NAMES_NICK_VOICE :
+                        TXT_NAMES_NICK;
+               format = format_get_text(MODULE_NAME, NULL,
+                                        channel->server, channel->name,
+                                        formatnum, nickmode, linebuf);
+               g_string_append(str, format);
+               g_free(format);
+
+               if (++col == cols) {
+                       printtext(channel->server, channel->name,
+                                 MSGLEVEL_CLIENTCRAP, "%s", str->str);
+                       g_string_truncate(str, 0);
+                       if (prefix_format != NULL)
+                                g_string_assign(str, prefix_format);
+                       col = 0; row++;
+
+                       if (row == last_col_rows)
+                                cols--;
+               }
+       }
+
+       if (str->len != 0) {
+               printtext(channel->server, channel->name,
+                         MSGLEVEL_CLIENTCRAP, "%s", str->str);
+       }
+
+       g_slist_free(nicklist);
+       g_string_free(str, TRUE);
+       g_free_not_null(columns);
+       g_free_not_null(prefix_format);
+       g_free(linebuf);
+}
+
+void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
+{
+       NICK_REC *nick;
+       GSList *tmp, *nicklist, *sorted;
+       int nicks, normal, voices, halfops, ops;
+
+       nicks = normal = voices = halfops = ops = 0;
+       nicklist = nicklist_getnicks(channel);
+       sorted = NULL;
+
+       /* sort the nicklist */
+       for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
+               nick = tmp->data;
+
+               nicks++;
+               if (nick->op) {
+                       ops++;
+                       if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0)
+                                continue;
+               } else if (nick->halfop) {
+                       halfops++;
+                       if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0)
+                               continue;
+               } else if (nick->voice) {
+                       voices++;
+                       if ((flags & CHANNEL_NICKLIST_FLAG_VOICES) == 0)
+                               continue;
+               } else {
+                       normal++;
+                       if ((flags & CHANNEL_NICKLIST_FLAG_NORMAL) == 0)
+                               continue;
+               }
+
+               sorted = g_slist_insert_sorted(sorted, nick, (GCompareFunc)
+                                              nicklist_compare);
+       }
+       g_slist_free(nicklist);
+
+       /* display the nicks */
+        if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) {
+               printformat(channel->server, channel->name,
+                           MSGLEVEL_CRAP, TXT_NAMES, channel->name, "");
+               display_sorted_nicks(channel, sorted);
+       }
+       g_slist_free(sorted);
+
+       printformat(channel->server, channel->name,
+                   MSGLEVEL_CRAP, TXT_ENDOFNAMES,
+                   channel->name, nicks, ops, halfops, voices, normal);
+}
+
+/* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [<channels> | **] */
+static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       CHANNEL_REC *chanrec;
+       GHashTable *optlist;
+        GString *unknowns;
+       char *channel, **channels, **tmp;
+        int flags;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+       if (!IS_SERVER(server) || !server->connected)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "names", &optlist, &channel))
+               return;
+
+       if (strcmp(channel, "*") == 0 || *channel == '\0') {
+               if (!IS_CHANNEL(item))
+                        cmd_param_error(CMDERR_NOT_JOINED);
+
+               channel = item->name;
+       }
+
+       flags = 0;
+       if (g_hash_table_lookup(optlist, "ops") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_OPS;
+       if (g_hash_table_lookup(optlist, "halfops") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_HALFOPS;
+       if (g_hash_table_lookup(optlist, "voices") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_VOICES;
+       if (g_hash_table_lookup(optlist, "normal") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_NORMAL;
+       if (g_hash_table_lookup(optlist, "count") != NULL)
+               flags |= CHANNEL_NICKLIST_FLAG_COUNT;
+
+        if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL;
+
+        unknowns = g_string_new(NULL);
+
+       channels = g_strsplit(channel, ",", -1);
+       for (tmp = channels; *tmp != NULL; tmp++) {
+               chanrec = channel_find(server, *tmp);
+               if (chanrec == NULL)
+                       g_string_sprintfa(unknowns, "%s,", *tmp);
+               else {
+                       fe_channels_nicklist(chanrec, flags);
+                       signal_stop();
+               }
+       }
+       g_strfreev(channels);
+
+       if (unknowns->len > 1)
+                g_string_truncate(unknowns, unknowns->len-1);
+
+       if (unknowns->len > 0 && strcmp(channel, unknowns->str) != 0)
+                signal_emit("command names", 3, unknowns->str, server, item);
+        g_string_free(unknowns, TRUE);
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: CYCLE [<channel>] [<message>] */
+static void cmd_cycle(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       CHANNEL_REC *chanrec;
+       char *channame, *msg, *joindata;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+       if (!IS_SERVER(server) || !server->connected)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN,
+                           item, &channame, &msg))
+               return;
+       if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       chanrec = channel_find(server, channame);
+       if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
+
+       joindata = chanrec->get_join_data(chanrec);
+       window_bind_add(window_item_window(chanrec),
+                       chanrec->server->tag, chanrec->name);
+        channel_destroy(chanrec);
+
+       server->channels_join(server, joindata, FALSE);
+       g_free(joindata);
+
+       cmd_params_free(free_arg);
+}
+
+void fe_channels_init(void)
+{
+       settings_add_bool("lookandfeel", "autoclose_windows", TRUE);
+       settings_add_int("lookandfeel", "names_max_columns", 6);
+       settings_add_int("lookandfeel", "names_max_width", 0);
+
+       signal_add("channel created", (SIGNAL_FUNC) signal_channel_created);
+       signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
+       signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
+       signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+
+       command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_wjoin_pre);
+       command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
+       command_bind_last("join", NULL, (SIGNAL_FUNC) cmd_wjoin_post);
+       command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel);
+       command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add);
+       command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove);
+       command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list);
+       command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
+       command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
+
+       command_set_options("channel add", "auto noauto -bots -botcmd");
+       command_set_options("names", "count ops halfops voices normal");
+       command_set_options("join", "window");
+}
+
+void fe_channels_deinit(void)
+{
+       signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created);
+       signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
+       signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+
+       command_unbind("join", (SIGNAL_FUNC) cmd_wjoin_pre);
+       command_unbind("join", (SIGNAL_FUNC) cmd_join);
+       command_unbind("join", (SIGNAL_FUNC) cmd_wjoin_post);
+       command_unbind("channel", (SIGNAL_FUNC) cmd_channel);
+       command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add);
+       command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove);
+       command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list);
+       command_unbind("names", (SIGNAL_FUNC) cmd_names);
+       command_unbind("cycle", (SIGNAL_FUNC) cmd_cycle);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-channels.h b/apps/irssi/src/fe-common/core/fe-channels.h
new file mode 100644 (file)
index 0000000..e9a03a1
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __FE_CHANNELS_H
+#define __FE_CHANNELS_H
+
+#define CHANNEL_NICKLIST_FLAG_OPS       0x01
+#define CHANNEL_NICKLIST_FLAG_HALFOPS   0x02
+#define CHANNEL_NICKLIST_FLAG_VOICES    0x04
+#define CHANNEL_NICKLIST_FLAG_NORMAL    0x08
+#define CHANNEL_NICKLIST_FLAG_ALL       0x0f
+#define CHANNEL_NICKLIST_FLAG_COUNT     0x10
+
+void fe_channels_nicklist(CHANNEL_REC *channel, int flags);
+
+void fe_channels_init(void);
+void fe_channels_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-common-core.c b/apps/irssi/src/fe-common/core/fe-common-core.c
new file mode 100644 (file)
index 0000000..fb0e0fe
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ fe-common-core.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "args.h"
+#include "misc.h"
+#include "levels.h"
+#include "settings.h"
+#include "irssi-version.h"
+
+#include "servers.h"
+#include "channels.h"
+#include "servers-setup.h"
+
+#include "autorun.h"
+#include "fe-queries.h"
+#include "hilight-text.h"
+#include "command-history.h"
+#include "completion.h"
+#include "keyboard.h"
+#include "printtext.h"
+#include "formats.h"
+#include "themes.h"
+#include "translation.h"
+#include "fe-channels.h"
+#include "fe-windows.h"
+#include "window-items.h"
+#include "windows-layout.h"
+
+#include <signal.h>
+
+static char *autocon_server;
+static char *autocon_password;
+static int autocon_port;
+static int no_autoconnect;
+static char *cmdline_nick;
+static char *cmdline_hostname;
+
+void fe_core_log_init(void);
+void fe_core_log_deinit(void);
+
+void fe_exec_init(void);
+void fe_exec_deinit(void);
+
+void fe_expandos_init(void);
+void fe_expandos_deinit(void);
+
+void fe_help_init(void);
+void fe_help_deinit(void);
+
+void fe_ignore_init(void);
+void fe_ignore_deinit(void);
+
+void fe_ignore_messages_init(void);
+void fe_ignore_messages_deinit(void);
+
+void fe_log_init(void);
+void fe_log_deinit(void);
+
+void fe_messages_init(void);
+void fe_messages_deinit(void);
+
+void fe_modules_init(void);
+void fe_modules_deinit(void);
+
+void fe_server_init(void);
+void fe_server_deinit(void);
+
+void fe_settings_init(void);
+void fe_settings_deinit(void);
+
+void window_activity_init(void);
+void window_activity_deinit(void);
+
+void window_commands_init(void);
+void window_commands_deinit(void);
+
+void fe_core_commands_init(void);
+void fe_core_commands_deinit(void);
+
+static void print_version(void)
+{
+       printf(PACKAGE" " IRSSI_VERSION" (%d %04d)\n",
+              IRSSI_VERSION_DATE, IRSSI_VERSION_TIME);
+        exit(0);
+}
+
+static void sig_connected(SERVER_REC *server)
+{
+       MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1));
+}
+
+static void sig_disconnected(SERVER_REC *server)
+{
+       g_free(MODULE_DATA(server));
+       MODULE_DATA_UNSET(server);
+}
+
+static void sig_channel_created(CHANNEL_REC *channel)
+{
+       MODULE_DATA_SET(channel, g_new0(MODULE_CHANNEL_REC, 1));
+}
+
+static void sig_channel_destroyed(CHANNEL_REC *channel)
+{
+       g_free(MODULE_DATA(channel));
+       MODULE_DATA_UNSET(channel);
+}
+
+void fe_common_core_init(void)
+{
+       static struct poptOption version_options[] = {
+               { NULL, '\0', POPT_ARG_CALLBACK, (void *)&print_version, '\0', NULL },
+               { "version", 'v', POPT_ARG_NONE, NULL, 0, "Display irssi version" },
+               { NULL, '\0', 0, NULL }
+       };
+
+       static struct poptOption options[] = {
+               { NULL, '\0', POPT_ARG_INCLUDE_TABLE, version_options, 0, NULL, NULL },
+               POPT_AUTOHELP
+               { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server/ircnet", "SERVER" },
+               { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "PASSWORD" },
+               { "port", 'p', POPT_ARG_INT, &autocon_port, 0, "Autoconnect port", "PORT" },
+               { "noconnect", '!', POPT_ARG_NONE, &no_autoconnect, 0, "Disable autoconnecting", NULL },
+               { "nick", 'n', POPT_ARG_STRING, &cmdline_nick, 0, "Specify nick to use", NULL },
+               { "hostname", 'h', POPT_ARG_STRING, &cmdline_hostname, 0, "Specify host name to use", NULL },
+               { NULL, '\0', 0, NULL }
+       };
+
+       autocon_server = NULL;
+       autocon_password = NULL;
+       autocon_port = 6667;
+       no_autoconnect = FALSE;
+       cmdline_nick = NULL;
+       cmdline_hostname = NULL;
+       args_register(options);
+
+       settings_add_bool("lookandfeel", "timestamps", TRUE);
+       settings_add_str("lookandfeel", "timestamp_level", "ALL");
+       settings_add_int("lookandfeel", "timestamp_timeout", 0);
+
+       settings_add_bool("lookandfeel", "bell_beeps", FALSE);
+       settings_add_str("lookandfeel", "beep_msg_level", "");
+       settings_add_bool("lookandfeel", "beep_when_window_active", TRUE);
+       settings_add_bool("lookandfeel", "beep_when_away", TRUE);
+
+       settings_add_bool("lookandfeel", "hide_text_style", FALSE);
+       settings_add_bool("lookandfeel", "hide_mirc_colors", FALSE);
+       settings_add_bool("lookandfeel", "hide_server_tags", FALSE);
+
+       settings_add_bool("lookandfeel", "use_status_window", TRUE);
+       settings_add_bool("lookandfeel", "use_msgs_window", FALSE);
+
+       themes_init();
+        theme_register(fecommon_core_formats);
+
+       command_history_init();
+       completion_init();
+       keyboard_init();
+       printtext_init();
+       formats_init();
+#ifndef WIN32
+        fe_exec_init();
+#endif
+        fe_expandos_init();
+       fe_help_init();
+       fe_ignore_init();
+       fe_log_init();
+       fe_modules_init();
+       fe_server_init();
+       fe_settings_init();
+       translation_init();
+       windows_init();
+       window_activity_init();
+       window_commands_init();
+       window_items_init();
+       windows_layout_init();
+       fe_core_commands_init();
+
+        fe_channels_init();
+        fe_queries_init();
+
+       fe_messages_init();
+       hilight_text_init();
+       fe_ignore_messages_init();
+
+       settings_check();
+
+        signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
+        signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+        signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
+        signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+
+       module_register("core", "fe");
+}
+
+void fe_common_core_deinit(void)
+{
+       hilight_text_deinit();
+       command_history_deinit();
+       completion_deinit();
+       keyboard_deinit();
+       printtext_deinit();
+       formats_deinit();
+#ifndef WIN32
+        fe_exec_deinit();
+#endif
+        fe_expandos_deinit();
+       fe_help_deinit();
+       fe_ignore_deinit();
+       fe_log_deinit();
+       fe_modules_deinit();
+       fe_server_deinit();
+       fe_settings_deinit();
+       translation_deinit();
+       windows_deinit();
+       window_activity_deinit();
+       window_commands_deinit();
+       window_items_deinit();
+       windows_layout_deinit();
+       fe_core_commands_deinit();
+
+        fe_channels_deinit();
+        fe_queries_deinit();
+
+       fe_messages_deinit();
+       fe_ignore_messages_init();
+
+        theme_unregister();
+       themes_deinit();
+
+        signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
+        signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+        signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
+        signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+}
+
+void glog_func(const char *log_domain, GLogLevelFlags log_level,
+              const char *message)
+{
+       const char *reason;
+
+       switch (log_level) {
+       case G_LOG_LEVEL_WARNING:
+                reason = "warning";
+                break;
+       case G_LOG_LEVEL_CRITICAL:
+                reason = "critical";
+               break;
+       default:
+               reason = "error";
+                break;
+       }
+
+       if (windows == NULL)
+               fprintf(stderr, "GLib %s: %s", reason, message);
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_GLIB_ERROR, reason, message);
+       }
+}
+
+static void create_windows(void)
+{
+       WINDOW_REC *window;
+
+       windows_layout_restore();
+       if (windows != NULL)
+               return;
+
+       if (settings_get_bool("use_status_window")) {
+               window = window_create(NULL, TRUE);
+               window_set_name(window, "(status)");
+               window_set_level(window, MSGLEVEL_ALL ^
+                                (settings_get_bool("use_msgs_window") ?
+                                 (MSGLEVEL_MSGS|MSGLEVEL_DCCMSGS) : 0));
+                window_set_immortal(window, TRUE);
+       }
+
+       if (settings_get_bool("use_msgs_window")) {
+               window = window_create(NULL, TRUE);
+               window_set_name(window, "(msgs)");
+               window_set_level(window, MSGLEVEL_MSGS|MSGLEVEL_DCCMSGS);
+                window_set_immortal(window, TRUE);
+       }
+
+       if (windows == NULL) {
+               /* we have to have at least one window.. */
+                window = window_create(NULL, TRUE);
+       }
+}
+
+static void autoconnect_servers(void)
+{
+       GSList *tmp, *chatnets;
+       char *str;
+
+       if (autocon_server != NULL) {
+               /* connect to specified server */
+               str = g_strdup_printf(autocon_password == NULL ? "%s %d" : "%s %d %s",
+                                     autocon_server, autocon_port, autocon_password);
+               signal_emit("command connect", 1, str);
+               g_free(str);
+               return;
+       }
+
+       if (no_autoconnect) {
+               /* don't autoconnect */
+               return;
+       }
+
+       /* connect to autoconnect servers */
+       chatnets = NULL;
+       for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
+               SERVER_SETUP_REC *rec = tmp->data;
+
+               if (rec->autoconnect &&
+                   (rec->chatnet == NULL ||
+                    gslist_find_icase_string(chatnets, rec->chatnet) == NULL)) {
+                       if (rec->chatnet != NULL)
+                               chatnets = g_slist_append(chatnets, rec->chatnet);
+
+                       str = g_strdup_printf("%s %d", rec->address, rec->port);
+                       signal_emit("command connect", 1, str);
+                       g_free(str);
+               }
+       }
+
+       g_slist_free(chatnets);
+}
+
+void fe_common_core_finish_init(void)
+{
+       int setup_changed;
+
+       signal_emit("irssi init read settings", 0);
+
+#ifdef SIGPIPE
+       signal(SIGPIPE, SIG_IGN);
+#endif
+
+        setup_changed = FALSE;
+       if (cmdline_nick != NULL) {
+               /* override nick found from setup */
+               settings_set_str("nick", cmdline_nick);
+               setup_changed = TRUE;
+       }
+
+       if (cmdline_hostname != NULL) {
+               /* override host name found from setup */
+               settings_set_str("hostname", cmdline_hostname);
+               setup_changed = TRUE;
+       }
+
+       create_windows();
+
+        /* _after_ windows are created.. */
+       g_log_set_handler(G_LOG_DOMAIN,
+                         (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
+                                           G_LOG_LEVEL_WARNING),
+                         (GLogFunc) glog_func, NULL);
+
+       if (setup_changed)
+                signal_emit("setup changed", 0);
+
+        autorun_startup();
+       autoconnect_servers();
+}
diff --git a/apps/irssi/src/fe-common/core/fe-common-core.h b/apps/irssi/src/fe-common/core/fe-common-core.h
new file mode 100644 (file)
index 0000000..1c12047
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __FE_COMMON_CORE_H
+#define __FE_COMMON_CORE_H
+
+void fe_common_core_init(void);
+void fe_common_core_deinit(void);
+void fe_common_core_finish_init(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-core-commands.c b/apps/irssi/src/fe-common/core/fe-core-commands.c
new file mode 100644 (file)
index 0000000..cf7938e
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ fe-core-commands.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "line-split.h"
+#include "settings.h"
+#include "irssi-version.h"
+#include "servers.h"
+
+#include "fe-windows.h"
+#include "printtext.h"
+
+#define PASTE_CHECK_SPEED 200 /* 0.2 sec */
+
+static int ret_texts[] = {
+       TXT_OPTION_UNKNOWN,
+       TXT_OPTION_AMBIGUOUS,
+       TXT_OPTION_MISSING_ARG,
+       TXT_COMMAND_UNKNOWN,
+       TXT_COMMAND_AMBIGUOUS,
+        -1,
+       TXT_NOT_ENOUGH_PARAMS,
+       TXT_NOT_CONNECTED,
+       TXT_NOT_JOINED,
+       TXT_CHAN_NOT_FOUND,
+       TXT_CHAN_NOT_SYNCED,
+        TXT_ILLEGAL_PROTO,
+       TXT_NOT_GOOD_IDEA
+};
+
+/* keep the whole command line here temporarily. we need it in
+   "default command" event handler, but there we don't know if the start of
+   the line had one or two command chars, and which one.. */
+static const char *current_cmdline;
+static int hide_output;
+
+static GTimeVal time_command_last, time_command_now;
+static int last_command_cmd, command_cmd;
+
+/* SYNTAX: ECHO [-current] [-window <name>] [-level <level>] <text> */
+static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item)
+{
+        WINDOW_REC *window;
+       GHashTable *optlist;
+       char *msg, *levelstr, *winname;
+       void *free_arg;
+       int level;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_GETREST, "echo", &optlist, &msg))
+               return;
+
+        levelstr = g_hash_table_lookup(optlist, "level");
+       level = levelstr == NULL ? 0 :
+               level2bits(g_hash_table_lookup(optlist, "level"));
+       if (level == 0) level = MSGLEVEL_CRAP;
+
+       winname = g_hash_table_lookup(optlist, "window");
+       window = winname == NULL ? NULL :
+               is_numeric(winname, '\0') ?
+               window_find_refnum(atoi(winname)) :
+               window_find_item(NULL, winname);
+       if (window == NULL) window = active_win;
+
+       printtext_window(window, level, "%s", msg);
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: VERSION */
+static void cmd_version(char *data)
+{
+       char time[10];
+
+       g_return_if_fail(data != NULL);
+
+       if (*data == '\0') {
+                g_snprintf(time, sizeof(time), "%04d", IRSSI_VERSION_TIME);
+               printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                         "Client: "PACKAGE" " IRSSI_VERSION" (%d %s)",
+                         IRSSI_VERSION_DATE, time);
+       }
+}
+
+/* SYNTAX: CAT <file> */
+static void cmd_cat(const char *data)
+{
+       LINEBUF_REC *buffer = NULL;
+       char *fname, *fposstr;
+       char tmpbuf[1024], *str;
+       void *free_arg;
+       int f, ret, recvlen, fpos;
+
+       if (!cmd_get_params(data, &free_arg, 2, &fname, &fposstr))
+               return;
+
+       fname = convert_home(fname);
+       fpos = atoi(fposstr);
+        cmd_params_free(free_arg);
+
+       f = open(fname, O_RDONLY);
+       g_free(fname);
+
+       if (f == -1) {
+               /* file not found */
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                         "%s", g_strerror(errno));
+               return;
+       }
+
+        lseek(f, fpos, SEEK_SET);
+       do {
+               recvlen = read(f, tmpbuf, sizeof(tmpbuf));
+
+               ret = line_split(tmpbuf, recvlen, &str, &buffer);
+               if (ret > 0)
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s", str);
+       } while (ret > 0);
+       line_split_free(buffer);
+
+       close(f);
+}
+
+/* SYNTAX: BEEP */
+static void cmd_beep(void)
+{
+        signal_emit("beep", 0);
+}
+
+static void cmd_nick(const char *data, SERVER_REC *server)
+{
+       g_return_if_fail(data != NULL);
+
+       if (*data != '\0') return;
+       if (server == NULL || !server->connected)
+               cmd_return_error(CMDERR_NOT_CONNECTED);
+
+       /* display current nick */
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_YOUR_NICK, server->nick);
+       signal_stop();
+}
+
+static void cmd_join(const char *data, SERVER_REC *server)
+{
+       GHashTable *optlist;
+       char *channels;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                           "join", &optlist, &channels))
+               return;
+
+       server = cmd_options_get_server("join", optlist, server);
+       if (g_hash_table_lookup(optlist, "invite") &&
+           server != NULL && server->last_invite == NULL) {
+                /* ..all this trouble just to print this error message */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED);
+               signal_stop();
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static void sig_stop(void)
+{
+       signal_stop();
+}
+
+static void event_command(const char *data)
+{
+       const char *cmdchar;
+
+       if (*data == '\0') {
+               /* empty line, forget it. */
+                signal_stop();
+               return;
+       }
+
+       /* save current command line */
+       current_cmdline = data;
+
+        /* for detecting if we're pasting text */
+       time_command_last = time_command_now;
+       last_command_cmd = command_cmd;
+
+       g_get_current_time(&time_command_now);
+       command_cmd = strchr(settings_get_str("cmdchars"), *data) != NULL;
+
+       /* /^command hides the output of the command */
+       cmdchar = strchr(settings_get_str("cmdchars"), *data);
+       if (cmdchar != NULL && (data[1] == '^' ||
+                               (data[1] == *cmdchar && data[2] == '^'))) {
+                hide_output = TRUE;
+               signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
+               signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
+               signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
+       }
+}
+
+static void event_command_last(const char *data)
+{
+       if (hide_output) {
+               hide_output = FALSE;
+               signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
+               signal_remove("print format", (SIGNAL_FUNC) sig_stop);
+               signal_remove("print text", (SIGNAL_FUNC) sig_stop);
+       }
+}
+
+static void event_default_command(const char *data, void *server,
+                                 WI_ITEM_REC *item)
+{
+       const char *cmdchars, *ptr;
+       char *cmd, *p;
+       long diff;
+
+       cmdchars = settings_get_str("cmdchars");
+
+       ptr = data;
+       while (*ptr != '\0' && *ptr != ' ') {
+               if (strchr(cmdchars, *ptr)) {
+                       /* command character inside command .. we probably
+                          want to send this text to channel. for example
+                          when pasting a path /usr/bin/xxx. */
+                       signal_emit("send text", 3, current_cmdline, server, item);
+                       return;
+               }
+               ptr++;
+       }
+
+       /* maybe we're copy+pasting text? check how long it was since the
+          last line */
+       diff = get_timeval_diff(&time_command_now, &time_command_last);
+       if (item != NULL && !last_command_cmd && diff < PASTE_CHECK_SPEED) {
+               signal_emit("send text", 3, current_cmdline, active_win->active_server, active_win->active);
+               command_cmd = FALSE;
+               return;
+       }
+
+       /* get the command part of the line, send "error command" signal */
+       cmd = g_strdup(data);
+       p = strchr(cmd, ' ');
+       if (p != NULL) *p = '\0';
+
+       signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_UNKNOWN), cmd);
+
+       g_free(cmd);
+}
+
+static void event_cmderror(void *errorp, const char *arg)
+{
+       int error;
+
+       error = GPOINTER_TO_INT(errorp);
+       if (error == CMDERR_ERRNO) {
+                /* errno is special */
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno));
+       } else {
+                /* others */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[error + -CMDERR_OPTION_UNKNOWN], arg);
+       }
+}
+
+static void event_list_subcommands(const char *command)
+{
+        GSList *tmp;
+        GString *str;
+       int len;
+
+       str = g_string_new(NULL);
+
+        len = strlen(command);
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->cmd, command, len) == 0 &&
+                   rec->cmd[len] == ' ' &&
+                   strchr(rec->cmd+len+1, ' ') == NULL) {
+                        g_string_sprintfa(str, "%s ", rec->cmd+len+1);
+               }
+       }
+
+       if (str->len != 0) {
+               g_string_truncate(str, str->len-1);
+                printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str->str);
+       }
+
+        g_string_free(str, TRUE);
+}
+
+void fe_core_commands_init(void)
+{
+       hide_output = FALSE;
+
+       command_cmd = FALSE;
+       memset(&time_command_now, 0, sizeof(GTimeVal));
+
+       command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo);
+       command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
+       command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
+       command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
+       command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
+       command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join);
+
+       signal_add("send command", (SIGNAL_FUNC) event_command);
+       signal_add_last("send command", (SIGNAL_FUNC) event_command_last);
+       signal_add("default command", (SIGNAL_FUNC) event_default_command);
+       signal_add("error command", (SIGNAL_FUNC) event_cmderror);
+       signal_add("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
+
+       command_set_options("echo", "current +level +window");
+}
+
+void fe_core_commands_deinit(void)
+{
+       command_unbind("echo", (SIGNAL_FUNC) cmd_echo);
+       command_unbind("version", (SIGNAL_FUNC) cmd_version);
+       command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
+       command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
+       command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
+       command_unbind("join", (SIGNAL_FUNC) cmd_join);
+
+       signal_remove("send command", (SIGNAL_FUNC) event_command);
+       signal_remove("send command", (SIGNAL_FUNC) event_command_last);
+       signal_remove("default command", (SIGNAL_FUNC) event_default_command);
+       signal_remove("error command", (SIGNAL_FUNC) event_cmderror);
+       signal_remove("list subcommands", (SIGNAL_FUNC) event_list_subcommands);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-exec.c b/apps/irssi/src/fe-common/core/fe-exec.c
new file mode 100644 (file)
index 0000000..8097263
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ fe-exec.c : irssi
+
+    Copyright (C) 2000-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "pidwait.h"
+#include "line-split.h"
+#include "net-sendbuffer.h"
+#include "misc.h"
+#include "levels.h"
+
+#include "printtext.h"
+#include "fe-exec.h"
+#include "fe-windows.h"
+#include "window-items.h"
+
+#include <signal.h>
+#include <sys/wait.h>
+
+GSList *processes;
+static int signal_exec_input;
+
+static void exec_wi_destroy(EXEC_WI_REC *rec)
+{
+        g_return_if_fail(rec != NULL);
+
+       if (rec->destroying) return;
+        rec->destroying = TRUE;
+
+       if (window_item_window((WI_ITEM_REC *) rec) != NULL)
+               window_item_destroy((WI_ITEM_REC *) rec);
+
+       MODULE_DATA_DEINIT(rec);
+       g_free(rec->name);
+        g_free(rec);
+}
+
+static EXEC_WI_REC *exec_wi_create(WINDOW_REC *window, PROCESS_REC *rec)
+{
+       EXEC_WI_REC *item;
+
+        g_return_val_if_fail(window != NULL, NULL);
+        g_return_val_if_fail(rec != NULL, NULL);
+
+       item = g_new0(EXEC_WI_REC, 1);
+       item->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "EXEC");
+        item->destroy = (void (*) (WI_ITEM_REC *)) exec_wi_destroy;
+       item->name = rec->name != NULL ?
+               g_strdup_printf("%%%s", rec->name) :
+               g_strdup_printf("%%%d", rec->id);
+
+       item->createtime = time(NULL);
+        item->process = rec;
+
+       MODULE_DATA_INIT(item);
+       window_item_add(window, (WI_ITEM_REC *) item, FALSE);
+        return item;
+}
+
+static int process_get_new_id(void)
+{
+        PROCESS_REC *rec;
+       GSList *tmp;
+       int id;
+
+       id = 0;
+       tmp = processes;
+       while (tmp != NULL) {
+               rec = tmp->data;
+
+               if (id != rec->id) {
+                       tmp = tmp->next;
+                       continue;
+               }
+
+               id++;
+               tmp = processes;
+       }
+
+       return id;
+}
+
+static PROCESS_REC *process_find_pid(int pid)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(pid > 0, NULL);
+
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               PROCESS_REC *rec = tmp->data;
+
+               if (rec->pid == pid)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static PROCESS_REC *process_find_id(int id, int verbose)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(id != -1, NULL);
+
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               PROCESS_REC *rec = tmp->data;
+
+               if (rec->id == id)
+                       return rec;
+       }
+
+       if (verbose) {
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                         "Unknown process id: %d", id);
+       }
+
+       return NULL;
+}
+
+static PROCESS_REC *process_find(const char *name, int verbose)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(name != NULL, NULL);
+
+       if (*name == '%' && is_numeric(name+1, 0))
+                return process_find_id(atoi(name+1), verbose);
+
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               PROCESS_REC *rec = tmp->data;
+
+               if (rec->name != NULL && strcmp(rec->name, name) == 0)
+                       return rec;
+       }
+
+       if (verbose) {
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                         "Unknown process name: %s", name);
+       }
+
+       return NULL;
+}
+
+static void process_destroy(PROCESS_REC *rec, int status)
+{
+       processes = g_slist_remove(processes, rec);
+
+       signal_emit("exec remove", 2, rec, GINT_TO_POINTER(status));
+
+       if (rec->read_tag != -1)
+               g_source_remove(rec->read_tag);
+       if (rec->target_item != NULL)
+                exec_wi_destroy(rec->target_item);
+
+       line_split_free(rec->databuf);
+        g_io_channel_close(rec->in);
+        g_io_channel_unref(rec->in);
+        net_sendbuffer_destroy(rec->out, TRUE);
+
+       g_free_not_null(rec->name);
+       g_free_not_null(rec->target);
+        g_free(rec->args);
+        g_free(rec);
+}
+
+static void processes_killall(int signum)
+{
+       GSList *tmp;
+
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               PROCESS_REC *rec = tmp->data;
+
+               kill(rec->pid, signum);
+       }
+}
+
+static int signal_name_to_id(const char *name)
+{
+       /* check only the few most common signals, too much job to check
+          them all. if we sometimes want more, procps-sources/proc/sig.c
+          would be useful for copypasting */
+       if (g_strcasecmp(name, "hup") == 0)
+                return SIGHUP;
+       if (g_strcasecmp(name, "int") == 0)
+                return SIGINT;
+       if (g_strcasecmp(name, "term") == 0)
+                return SIGTERM;
+       if (g_strcasecmp(name, "kill") == 0)
+                return SIGKILL;
+       if (g_strcasecmp(name, "usr1") == 0)
+                return SIGUSR1;
+       if (g_strcasecmp(name, "usr2") == 0)
+                return SIGUSR2;
+        return -1;
+}
+
+/* `optlist' should contain only one unknown key - the server tag.
+   returns NULL if there was unknown -option */
+static int cmd_options_get_signal(const char *cmd,
+                                 GHashTable *optlist)
+{
+       GSList *list, *tmp, *next;
+       char *signame;
+        int signum;
+
+       /* get all the options, then remove the known ones. there should
+          be only one left - the signal */
+       list = hashtable_get_keys(optlist);
+       if (cmd != NULL) {
+               for (tmp = list; tmp != NULL; tmp = next) {
+                       char *option = tmp->data;
+                       next = tmp->next;
+
+                       if (command_have_option(cmd, option))
+                               list = g_slist_remove(list, option);
+               }
+       }
+
+       if (list == NULL)
+               return -1;
+
+       signame = list->data;
+       signum = -1;
+
+       signum = is_numeric(signame, 0) ? atol(signame) :
+               signal_name_to_id(signame);
+
+       if (signum == -1 || list->next != NULL) {
+               /* unknown option (not a signal) */
+               signal_emit("error command", 2,
+                           GINT_TO_POINTER(CMDERR_OPTION_UNKNOWN),
+                           signum == -1 ? list->data : list->next->data);
+               signal_stop();
+                return -2;
+       }
+
+       g_slist_free(list);
+       return signum;
+}
+
+static void exec_show_list(void)
+{
+       GSList *tmp;
+
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               PROCESS_REC *rec = tmp->data;
+
+               printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                         "%d (%s): %s", rec->id, rec->name, rec->args);
+       }
+}
+
+static void process_exec(PROCESS_REC *rec, const char *cmd)
+{
+       const char *shell_args[4] = { "/bin/sh", "-c", NULL, NULL };
+        char **args;
+       int in[2], out[2];
+        int n;
+
+       if (pipe(in) == -1)
+                return;
+       if (pipe(out) == -1)
+               return;
+
+       shell_args[2] = cmd;
+       rec->pid = fork();
+       if (rec->pid == -1) {
+                /* error */
+               close(in[0]); close(in[1]);
+                close(out[0]); close(out[1]);
+               return;
+       }
+
+       if (rec->pid != 0) {
+               /* parent process */
+                GIOChannel *outio = g_io_channel_unix_new(in[1]);
+
+               rec->in = g_io_channel_unix_new(out[0]);
+               rec->out = net_sendbuffer_create(outio, 0);
+
+                close(out[1]);
+               close(in[0]);
+               pidwait_add(rec->pid);
+                return;
+       }
+
+       /* child process, try to clean up everything */
+       setsid();
+       setuid(getuid());
+       setgid(getgid());
+       signal(SIGINT, SIG_IGN);
+       signal(SIGQUIT, SIG_DFL);
+
+       putenv("TERM=tty");
+
+       /* set stdin, stdout and stderr */
+        dup2(in[0], STDIN_FILENO);
+        dup2(out[1], STDOUT_FILENO);
+       dup2(out[1], STDERR_FILENO);
+
+        /* don't let child see our files */
+       for (n = 3; n < 256; n++)
+                close(n);
+
+       if (rec->shell) {
+               execvp(shell_args[0], (char **) shell_args);
+
+               fprintf(stderr, "Exec: /bin/sh: %s\n", g_strerror(errno));
+       } else {
+               args = g_strsplit(cmd, " ", -1);
+                execvp(args[0], args);
+
+               fprintf(stderr, "Exec: %s: %s\n", args[0], g_strerror(errno));
+       }
+
+       _exit(-1);
+}
+
+static void sig_exec_input_reader(PROCESS_REC *rec)
+{
+        char tmpbuf[512], *str;
+        unsigned int recvlen;
+       int err, ret;
+
+       g_return_if_fail(rec != NULL);
+
+       recvlen = 0;
+       err = g_io_channel_read(rec->in, tmpbuf,
+                               sizeof(tmpbuf), &recvlen);
+       if (err != 0 && err != G_IO_ERROR_AGAIN && errno != EINTR)
+               recvlen = -1;
+
+       do {
+               ret = line_split(tmpbuf, recvlen, &str, &rec->databuf);
+               if (ret == -1) {
+                       /* link to terminal closed? */
+                       g_source_remove(rec->read_tag);
+                        rec->read_tag = -1;
+                       break;
+               }
+
+               if (ret > 0) {
+                       signal_emit_id(signal_exec_input, 2, rec, str);
+                        if (recvlen > 0) recvlen = 0;
+               }
+       } while (ret > 0);
+}
+
+static void handle_exec(const char *args, GHashTable *optlist,
+                       WI_ITEM_REC *item)
+{
+       PROCESS_REC *rec;
+        char *target, *level;
+       int notice, signum, interactive;
+
+       /* check that there's no unknown options. we allowed them
+          because signals can be used as options, but there should be
+          only one unknown option: the signal name/number. */
+       signum = cmd_options_get_signal("exec", optlist);
+       if (signum == -2)
+                return;
+
+       if (*args == '\0') {
+               exec_show_list();
+                return;
+       }
+
+       target = NULL;
+       notice = FALSE;
+
+       if (g_hash_table_lookup(optlist, "in") != NULL) {
+               rec = process_find(g_hash_table_lookup(optlist, "in"), TRUE);
+               if (rec != NULL) {
+                       net_sendbuffer_send(rec->out, args, strlen(args));
+                       net_sendbuffer_send(rec->out, "\n", 1);
+               }
+               return;
+       }
+
+       /* check if args is a process ID or name. if it's ID but not found,
+          complain about it and fail immediately */
+       rec = process_find(args, *args == '%');
+       if (*args == '%' && rec == NULL)
+               return;
+
+        /* common options */
+       if (g_hash_table_lookup(optlist, "out") != NULL) {
+                /* redirect output to active channel/query */
+               if (item == NULL)
+                       cmd_return_error(CMDERR_NOT_JOINED);
+                target = item->name;
+       } else if (g_hash_table_lookup(optlist, "msg") != NULL) {
+                /* redirect output to /msg <nick> */
+               target = g_hash_table_lookup(optlist, "msg");
+       } else if (g_hash_table_lookup(optlist, "notice") != NULL) {
+               target = g_hash_table_lookup(optlist, "notice");
+                notice = TRUE;
+       }
+
+        /* options that require process ID/name as argument */
+       if (rec == NULL &&
+           (signum != -1 || g_hash_table_lookup(optlist, "close") != NULL)) {
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                         "Unknown process name: %s", args);
+               return;
+       }
+       if (g_hash_table_lookup(optlist, "close") != NULL) {
+               /* forcibly close the process */
+                process_destroy(rec, -1);
+                return;
+       }
+
+       if (signum != -1) {
+               /* send a signal to process */
+                kill(rec->pid, signum);
+                return;
+       }
+
+        interactive = g_hash_table_lookup(optlist, "interactive") != NULL;
+       if (*args == '%') {
+               /* do something to already existing process */
+               char *name;
+
+               if (target != NULL) {
+                        /* redirect output to target */
+                       g_free_and_null(rec->target);
+                       rec->target = g_strdup(target);
+                        rec->notice = notice;
+               }
+
+                name = g_hash_table_lookup(optlist, "name");
+               if (name != NULL) {
+                       /* change window name */
+                       g_free_not_null(rec->name);
+                       rec->name = *name == '\0' ? NULL : g_strdup(name);
+               } else if (target == NULL &&
+                          (rec->target_item == NULL || interactive)) {
+                       /* no parameters given,
+                          redirect output to the active window */
+                       g_free_and_null(rec->target);
+                       rec->target_win = active_win;
+
+                       if (rec->target_item != NULL) {
+                               exec_wi_destroy(rec->target_item);
+                                rec->target_item = NULL;
+                       }
+
+                       if (interactive) {
+                               rec->target_item =
+                                       exec_wi_create(active_win, rec);
+                       }
+               }
+                return;
+       }
+
+        /* starting a new process */
+       rec = g_new0(PROCESS_REC, 1);
+       rec->pid = -1;
+        rec->shell = g_hash_table_lookup(optlist, "nosh") == NULL;
+
+       process_exec(rec, args);
+       if (rec->pid == -1) {
+                /* pipe() or fork() failed */
+               g_free(rec);
+               cmd_return_error(CMDERR_ERRNO);
+       }
+
+        rec->id = process_get_new_id();
+       rec->target = g_strdup(target);
+       rec->target_win = active_win;
+        rec->args = g_strdup(args);
+       rec->notice = notice;
+        rec->silent = g_hash_table_lookup(optlist, "-") != NULL;
+       rec->name = g_strdup(g_hash_table_lookup(optlist, "name"));
+
+       level = g_hash_table_lookup(optlist, "level");
+       rec->level = level == NULL ? MSGLEVEL_CLIENTCRAP : level2bits(level);
+
+       rec->read_tag = g_input_add(rec->in, G_INPUT_READ,
+                                   (GInputFunction) sig_exec_input_reader,
+                                   rec);
+       processes = g_slist_append(processes, rec);
+
+       if (rec->target == NULL && interactive)
+               rec->target_item = exec_wi_create(active_win, rec);
+
+       signal_emit("exec new", 1, rec);
+}
+
+/* SYNTAX: EXEC [-] [-nosh] [-out | -msg <target> | -notice <target>]
+               [-name <name>] <cmd line>
+          EXEC -out | -window | -msg <target> | -notice <target> |
+               -close | -<signal> <id>
+          EXEC -in <id> <text to send to process> */
+static void cmd_exec(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       GHashTable *optlist;
+        char *args;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                          PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
+                          "exec", &optlist, &args)) {
+               handle_exec(args, optlist, item);
+               cmd_params_free(free_arg);
+       }
+}
+
+static void sig_pidwait(void *pid, void *statusp)
+{
+       PROCESS_REC *rec;
+        char *str;
+       int status = GPOINTER_TO_INT(statusp);
+
+        rec = process_find_pid(GPOINTER_TO_INT(pid));
+       if (rec == NULL) return;
+
+       /* process exited - print the last line if
+          there wasn't a newline at end. */
+       if (line_split("\n", 1, &str, &rec->databuf) > 0 && *str != '\0')
+               signal_emit_id(signal_exec_input, 2, rec, str);
+
+       if (!rec->silent) {
+               if (WIFSIGNALED(status)) {
+                       status = WTERMSIG(status);
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                                 "process %d (%s) terminated with signal %d (%s)",
+                                 rec->id, rec->args,
+                                 status, g_strsignal(status));
+               } else {
+                        status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                                 "process %d (%s) terminated with return code %d",
+                                 rec->id, rec->args, status);
+               }
+       }
+       process_destroy(rec, status);
+}
+
+static void sig_exec_input(PROCESS_REC *rec, const char *text)
+{
+       WI_ITEM_REC *item;
+       SERVER_REC *server;
+        char *str;
+
+        item = NULL;
+       server = NULL;
+
+       if (rec->target != NULL) {
+               item = window_item_find(NULL, rec->target);
+               server = item != NULL ? item->server :
+                       active_win->active_server;
+
+                str = g_strconcat(rec->target, " ", text, NULL);
+               signal_emit(rec->notice ? "command notice" : "command msg",
+                           3, str, server, item);
+                g_free(str);
+       } else if (rec->target_item != NULL) {
+               printtext(NULL, rec->target_item->name,
+                         rec->level, "%s", text);
+       } else {
+               printtext_window(rec->target_win, rec->level, "%s", text);
+       }
+}
+
+static void sig_window_destroyed(WINDOW_REC *window)
+{
+       GSList *tmp;
+
+       /* window is being closed, if there's any /exec targets for it,
+          change them to active window. */
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               PROCESS_REC *rec = tmp->data;
+
+               if (rec->target_win == window)
+                       rec->target_win = active_win;
+       }
+}
+
+static void event_text(const char *data, SERVER_REC *server, EXEC_WI_REC *item)
+{
+       if (!IS_EXEC_WI(item)) return;
+
+       net_sendbuffer_send(item->process->out, data, strlen(data));
+       net_sendbuffer_send(item->process->out, "\n", 1);
+        signal_stop();
+}
+
+void fe_exec_init(void)
+{
+       command_bind("exec", NULL, (SIGNAL_FUNC) cmd_exec);
+       command_set_options("exec", "!- interactive nosh +name out +msg +notice +in window close +level");
+
+        signal_exec_input = signal_get_uniq_id("exec input");
+        signal_add("pidwait", (SIGNAL_FUNC) sig_pidwait);
+        signal_add("exec input", (SIGNAL_FUNC) sig_exec_input);
+        signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_add_first("send text", (SIGNAL_FUNC) event_text);
+}
+
+void fe_exec_deinit(void)
+{
+       if (processes != NULL) {
+               processes_killall(SIGTERM);
+               sleep(1);
+               processes_killall(SIGKILL);
+
+               while (processes != NULL)
+                       process_destroy(processes->data, -1);
+       }
+
+       command_unbind("exec", (SIGNAL_FUNC) cmd_exec);
+
+        signal_remove("pidwait", (SIGNAL_FUNC) sig_pidwait);
+        signal_remove("exec input", (SIGNAL_FUNC) sig_exec_input);
+        signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed);
+       signal_remove("send text", (SIGNAL_FUNC) event_text);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-exec.h b/apps/irssi/src/fe-common/core/fe-exec.h
new file mode 100644 (file)
index 0000000..bebd1f8
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef __FE_EXEC_H
+#define __FE_EXEC_H
+
+#include "fe-windows.h"
+
+#define EXEC_WI(query) \
+       MODULE_CHECK_CAST_MODULE(query, EXEC_WI_REC, type, \
+                             "WINDOW ITEM TYPE", "EXEC")
+
+#define IS_EXEC_WI(query) \
+       (EXEC_WI(query) ? TRUE : FALSE)
+
+typedef struct PROCESS_REC PROCESS_REC;
+
+#define STRUCT_SERVER_REC void
+typedef struct {
+#include "window-item-rec.h"
+       PROCESS_REC *process;
+       unsigned int destroying:1;
+} EXEC_WI_REC;
+
+struct PROCESS_REC {
+        int id;
+       char *name;
+        char *args;
+
+       int pid;
+       GIOChannel *in;
+        NET_SENDBUF_REC *out;
+        LINEBUF_REC *databuf;
+       int read_tag;
+
+        int level; /* what level to use when printing the text */
+        char *target; /* send text with /msg <target> ... */
+       WINDOW_REC *target_win; /* print text to this window */
+        EXEC_WI_REC *target_item; /* print text to this exec window item */
+
+       unsigned int shell:1; /* start the program via /bin/sh */
+       unsigned int notice:1; /* send text with /notice, not /msg if target is set */
+       unsigned int silent:1; /* don't print "process exited with level xx" */
+};
+
+extern GSList *processes;
+
+void fe_exec_init(void);
+void fe_exec_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-expandos.c b/apps/irssi/src/fe-common/core/fe-expandos.c
new file mode 100644 (file)
index 0000000..c14ac7c
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ fe-expandos.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "expandos.h"
+#include "fe-windows.h"
+
+/* Window ref# */
+static char *expando_winref(SERVER_REC *server, void *item, int *free_ret)
+{
+       if (active_win == NULL)
+               return "";
+
+        *free_ret = TRUE;
+       return g_strdup_printf("%d", active_win->refnum);
+}
+
+/* Window name */
+static char *expando_winname(SERVER_REC *server, void *item, int *free_ret)
+{
+       if (active_win == NULL)
+               return "";
+
+       return active_win->name;
+}
+
+void fe_expandos_init(void)
+{
+       expando_create("winref", expando_winref,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window refnum changed", EXPANDO_ARG_WINDOW, NULL);
+       expando_create("winname", expando_winname,
+                      "window changed", EXPANDO_ARG_NONE,
+                      "window name changed", EXPANDO_ARG_WINDOW, NULL);
+}
+
+void fe_expandos_deinit(void)
+{
+       expando_destroy("winref", expando_winref);
+       expando_destroy("winname", expando_winname);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-help.c b/apps/irssi/src/fe-common/core/fe-help.c
new file mode 100644 (file)
index 0000000..fa90473
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ fe-help.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "line-split.h"
+#include "settings.h"
+
+#include "printtext.h"
+#include "formats.h"
+
+static int commands_equal(COMMAND_REC *rec, COMMAND_REC *rec2)
+{
+       if (rec->category == NULL && rec2->category != NULL)
+               return -1;
+       if (rec2->category == NULL && rec->category != NULL)
+               return 1;
+
+       return strcmp(rec->cmd, rec2->cmd);
+}
+
+static int get_cmd_length(void *data)
+{
+        return strlen(((COMMAND_REC *) data)->cmd);
+}
+
+static void help_category(GSList *cmdlist, int items)
+{
+        WINDOW_REC *window;
+       TEXT_DEST_REC dest;
+       GString *str;
+       GSList *tmp;
+       int *columns, cols, rows, col, row, last_col_rows, max_width;
+       char *linebuf, *format, *stripped;
+
+       window = window_find_closest(NULL, NULL, MSGLEVEL_CLIENTCRAP);
+        max_width = window->width;
+
+        /* remove width of timestamp from max_width */
+       format_create_dest(&dest, NULL, NULL, MSGLEVEL_CLIENTCRAP, NULL);
+       format = format_get_line_start(current_theme, &dest, time(NULL));
+       if (format != NULL) {
+               stripped = strip_codes(format);
+               max_width -= strlen(stripped);
+               g_free(stripped);
+               g_free(format);
+       }
+
+        /* calculate columns */
+       cols = get_max_column_count(cmdlist, get_cmd_length,
+                                   max_width, 6, 1, 3, &columns, &rows);
+       cmdlist = columns_sort_list(cmdlist, rows);
+
+        /* rows in last column */
+       last_col_rows = rows-(cols*rows-g_slist_length(cmdlist));
+       if (last_col_rows == 0)
+                last_col_rows = rows;
+
+       str = g_string_new(NULL);
+       linebuf = g_malloc(max_width+1);
+
+        col = 0; row = 0;
+       for (tmp = cmdlist; tmp != NULL; tmp = tmp->next) {
+               COMMAND_REC *rec = tmp->data;
+
+               memset(linebuf, ' ', columns[col]);
+               linebuf[columns[col]] = '\0';
+               memcpy(linebuf, rec->cmd, strlen(rec->cmd));
+               g_string_append(str, linebuf);
+
+               if (++col == cols) {
+                       printtext(NULL, NULL,
+                                 MSGLEVEL_CLIENTCRAP, "%s", str->str);
+                       g_string_truncate(str, 0);
+                       col = 0; row++;
+
+                       if (row == last_col_rows)
+                                cols--;
+               }
+       }
+       if (str->len != 0)
+               printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s", str->str);
+
+       g_slist_free(cmdlist);
+       g_string_free(str, TRUE);
+       g_free(columns);
+       g_free(linebuf);
+}
+
+static int show_help_file(const char *file)
+{
+        const char *helppath;
+       char tmpbuf[1024], *str, *path;
+       LINEBUF_REC *buffer = NULL;
+       int f, ret, recvlen;
+
+        helppath = settings_get_str("help_path");
+
+       /* helpdir/command or helpdir/category/command */
+       path = g_strdup_printf("%s/%s", helppath, file);
+       f = open(path, O_RDONLY);
+       g_free(path);
+
+       if (f == -1)
+               return FALSE;
+
+       /* just print to screen whatever is in the file */
+       do {
+               recvlen = read(f, tmpbuf, sizeof(tmpbuf));
+
+               ret = line_split(tmpbuf, recvlen, &str, &buffer);
+               if (ret > 0) {
+                       str = g_strconcat("%|", str, NULL);
+                       printtext_string(NULL, NULL, MSGLEVEL_CLIENTCRAP, str);
+                       g_free(str);
+               }
+       }
+       while (ret > 0);
+       line_split_free(buffer);
+
+       close(f);
+       return TRUE;
+}
+
+static void show_help(const char *data)
+{
+       COMMAND_REC *rec, *last;
+       GSList *tmp, *cmdlist;
+       int items, findlen;
+       int header, found, fullmatch;
+
+       g_return_if_fail(data != NULL);
+
+       /* sort the commands list */
+       commands = g_slist_sort(commands, (GCompareFunc) commands_equal);
+
+       /* print command, sort by category */
+       cmdlist = NULL; last = NULL; header = FALSE; fullmatch = FALSE;
+       items = 0; findlen = strlen(data); found = FALSE;
+       for (tmp = commands; tmp != NULL; last = rec, tmp = tmp->next) {
+               rec = tmp->data;
+
+               if (last != NULL && rec->category != NULL &&
+                   (last->category == NULL ||
+                    strcmp(rec->category, last->category) != 0)) {
+                       /* category changed */
+                       if (items > 0) {
+                               if (!header) {
+                                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "Irssi commands:");
+                                       header = TRUE;
+                               }
+                               if (last->category != NULL) {
+                                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "");
+                                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s:", last->category);
+                               }
+                               help_category(cmdlist, items);
+                       }
+
+                       g_slist_free(cmdlist); cmdlist = NULL;
+                       items = 0;
+               }
+
+               if (last != NULL && g_strcasecmp(rec->cmd, last->cmd) == 0)
+                       continue; /* don't display same command twice */
+
+               if ((int)strlen(rec->cmd) >= findlen &&
+                   g_strncasecmp(rec->cmd, data, findlen) == 0) {
+                       if (rec->cmd[findlen] == '\0') {
+                               fullmatch = TRUE;
+                               found = TRUE;
+                               break;
+                       }
+                       else if (strchr(rec->cmd+findlen+1, ' ') == NULL) {
+                               /* not a subcommand (and matches the query) */
+                               items++;
+                               cmdlist = g_slist_append(cmdlist, rec);
+                               found = TRUE;
+                       }
+               }
+       }
+
+       if ((!found || fullmatch) && !show_help_file(data)) {
+               printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                         "No help for %s", data);
+       }
+
+       if (*data != '\0' && data[strlen(data)-1] != ' ' &&
+           command_have_sub(data)) {
+               char *cmd;
+
+               cmd = g_strconcat(data, " ", NULL);
+               show_help(cmd);
+               g_free(cmd);
+       }
+
+       if (items != 0) {
+               /* display the last category */
+               if (!header) {
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                                 "Irssi commands:");
+                       header = TRUE;
+               }
+
+               if (last->category != NULL) {
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "");
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                                 "%s:", last->category);
+               }
+               help_category(cmdlist, items);
+               g_slist_free(cmdlist);
+       }
+}
+
+/* SYNTAX: HELP [<command>] */
+static void cmd_help(const char *data)
+{
+       char *cmd, *ptr;
+
+       cmd = g_strdup(data);
+       ptr = cmd+strlen(cmd);
+       while (ptr[-1] == ' ') ptr--; *ptr = '\0';
+
+       show_help(cmd);
+        g_free(cmd);
+}
+
+void fe_help_init(void)
+{
+        settings_add_str("misc", "help_path", HELPDIR);
+       command_bind("help", NULL, (SIGNAL_FUNC) cmd_help);
+}
+
+void fe_help_deinit(void)
+{
+       command_unbind("help", (SIGNAL_FUNC) cmd_help);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-ignore-messages.c b/apps/irssi/src/fe-common/core/fe-ignore-messages.c
new file mode 100644 (file)
index 0000000..770f4a4
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ fe-ignore-messages.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "levels.h"
+#include "ignore.h"
+
+static void sig_message_public(SERVER_REC *server, const char *msg,
+                              const char *nick, const char *address,
+                              const char *target)
+{
+       if (ignore_check(server, nick, address, target, msg, MSGLEVEL_PUBLIC))
+               signal_stop();
+}
+
+static void sig_message_private(SERVER_REC *server, const char *msg,
+                               const char *nick, const char *address)
+{
+       if (ignore_check(server, nick, address, NULL, msg, MSGLEVEL_MSGS))
+               signal_stop();
+}
+
+static void sig_message_join(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *address)
+{
+       if (ignore_check(server, nick, address, channel, NULL, MSGLEVEL_JOINS))
+               signal_stop();
+}
+
+static void sig_message_part(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *address,
+                            const char *reason)
+{
+       if (ignore_check(server, nick, address, channel, NULL, MSGLEVEL_PARTS))
+               signal_stop();
+}
+
+static void sig_message_quit(SERVER_REC *server, const char *nick,
+                            const char *address, const char *reason)
+{
+       if (ignore_check(server, nick, address, NULL, reason, MSGLEVEL_QUITS))
+               signal_stop();
+}
+
+static void sig_message_kick(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *kicker,
+                            const char *address, const char *reason)
+{
+       if (ignore_check(server, kicker, address,
+                        channel, reason, MSGLEVEL_KICKS))
+               signal_stop();
+}
+
+static void sig_message_nick(SERVER_REC *server, const char *newnick,
+                            const char *oldnick, const char *address)
+{
+       if (ignore_check(server, oldnick, address,
+                        NULL, NULL, MSGLEVEL_NICKS) ||
+           ignore_check(server, newnick, address,
+                        NULL, NULL, MSGLEVEL_NICKS))
+               signal_stop();
+}
+
+static void sig_message_own_nick(SERVER_REC *server, const char *newnick,
+                                const char *oldnick, const char *address)
+{
+       if (ignore_check(server, oldnick, address, NULL, NULL, MSGLEVEL_NICKS))
+               signal_stop();
+}
+
+static void sig_message_invite(SERVER_REC *server, const char *channel,
+                              const char *nick, const char *address)
+{
+       if (*channel == '\0' ||
+           ignore_check(server, nick, address,
+                        channel, NULL, MSGLEVEL_INVITES))
+               signal_stop();
+}
+
+static void sig_message_topic(SERVER_REC *server, const char *channel,
+                             const char *topic,
+                             const char *nick, const char *address)
+{
+       if (ignore_check(server, nick, address,
+                        channel, topic, MSGLEVEL_TOPICS))
+               signal_stop();
+}
+
+void fe_ignore_messages_init(void)
+{
+       signal_add_first("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_add_first("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_add_first("message part", (SIGNAL_FUNC) sig_message_part);
+       signal_add_first("message quit", (SIGNAL_FUNC) sig_message_quit);
+       signal_add_first("message kick", (SIGNAL_FUNC) sig_message_kick);
+       signal_add_first("message nick", (SIGNAL_FUNC) sig_message_nick);
+       signal_add_first("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
+       signal_add_first("message invite", (SIGNAL_FUNC) sig_message_invite);
+       signal_add_first("message topic", (SIGNAL_FUNC) sig_message_topic);
+}
+
+void fe_ignore_messages_deinit(void)
+{
+       signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_remove("message part", (SIGNAL_FUNC) sig_message_part);
+       signal_remove("message quit", (SIGNAL_FUNC) sig_message_quit);
+       signal_remove("message kick", (SIGNAL_FUNC) sig_message_kick);
+       signal_remove("message nick", (SIGNAL_FUNC) sig_message_nick);
+       signal_remove("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
+       signal_remove("message invite", (SIGNAL_FUNC) sig_message_invite);
+       signal_remove("message topic", (SIGNAL_FUNC) sig_message_topic);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-ignore.c b/apps/irssi/src/fe-common/core/fe-ignore.c
new file mode 100644 (file)
index 0000000..55a8826
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ fe-ignore.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+
+#include "servers.h"
+#include "ignore.h"
+#include "printtext.h"
+
+static char *ignore_get_key(IGNORE_REC *rec)
+{
+       char *chans, *ret;
+
+       if (rec->channels == NULL)
+               return g_strdup(rec->mask != NULL ? rec->mask : "*" );
+
+       chans = g_strjoinv(",", rec->channels);
+       if (rec->mask == NULL) return chans;
+
+       ret = g_strdup_printf("%s %s", rec->mask, chans);
+       g_free(chans);
+       return ret;
+}
+
+static void ignore_print(int index, IGNORE_REC *rec)
+{
+       GString *options;
+       char *key, *levels;
+
+       key = ignore_get_key(rec);
+       levels = bits2level(rec->level);
+
+       options = g_string_new(NULL);
+       if (rec->exception) g_string_sprintfa(options, "-except ");
+       if (rec->regexp) {
+               g_string_sprintfa(options, "-regexp ");
+#ifdef HAVE_REGEX_H
+               if (!rec->regexp_compiled)
+                       g_string_sprintfa(options, "[INVALID!] ");
+#endif
+       }
+       if (rec->fullword) g_string_sprintfa(options, "-full ");
+       if (rec->replies) g_string_sprintfa(options, "-replies ");
+       if (rec->pattern != NULL)
+               g_string_sprintfa(options, "-pattern %s ", rec->pattern);
+
+       if (options->len > 1) g_string_truncate(options, options->len-1);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                   TXT_IGNORE_LINE, index,
+                   key != NULL ? key : "",
+                   levels != NULL ? levels : "", options->str);
+       g_string_free(options, TRUE);
+        g_free(key);
+       g_free(levels);
+}
+
+static void cmd_ignore_show(void)
+{
+       GSList *tmp;
+       int index;
+
+       if (ignores == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                           TXT_IGNORE_NO_IGNORES);
+                return;
+       }
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_HEADER);
+       index = 1;
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next, index++) {
+               IGNORE_REC *rec = tmp->data;
+
+               ignore_print(index, rec);
+       }
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_IGNORE_FOOTER);
+}
+
+/* SYNTAX: IGNORE [-regexp | -full] [-pattern <pattern>] [-except] [-replies]
+                  [-channels <channel>] [-time <secs>] <mask> [<levels>]
+           IGNORE [-regexp | -full] [-pattern <pattern>] [-except] [-replies]
+                 [-time <secs>] <channels> [<levels>] */
+static void cmd_ignore(const char *data)
+{
+       GHashTable *optlist;
+       IGNORE_REC *rec;
+       char *patternarg, *chanarg, *mask, *levels, *timestr;
+       char **channels;
+       void *free_arg;
+       int new_ignore;
+
+       if (*data == '\0') {
+               cmd_ignore_show();
+               return;
+       }
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_GETREST,
+                           "ignore", &optlist, &mask, &levels))
+               return;
+
+       patternarg = g_hash_table_lookup(optlist, "pattern");
+        chanarg = g_hash_table_lookup(optlist, "channels");
+
+       if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+        if (*levels == '\0') levels = "ALL";
+
+       if (active_win->active_server != NULL &&
+           server_ischannel(active_win->active_server, mask)) {
+               chanarg = mask;
+               mask = NULL;
+       }
+       channels = (chanarg == NULL || *chanarg == '\0') ? NULL :
+               g_strsplit(replace_chars(chanarg, ',', ' '), " ", -1);
+
+       rec = ignore_find(NULL, mask, channels);
+       new_ignore = rec == NULL;
+
+       if (rec == NULL) {
+               rec = g_new0(IGNORE_REC, 1);
+
+               rec->mask = mask == NULL || *mask == '\0' ||
+                       strcmp(mask, "*") == 0 ? NULL : g_strdup(mask);
+               rec->channels = channels;
+       } else {
+                g_free_and_null(rec->pattern);
+               g_strfreev(channels);
+       }
+
+       rec->level = combine_level(rec->level, levels);
+
+       if (new_ignore && rec->level == 0) {
+               /* tried to unignore levels from nonexisting ignore */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_IGNORE_NOT_FOUND, rec->mask);
+               g_free(rec->mask);
+               g_strfreev(rec->channels);
+               g_free(rec);
+               cmd_params_free(free_arg);
+                return;
+       }
+
+       rec->pattern = (patternarg == NULL || *patternarg == '\0') ?
+               NULL : g_strdup(patternarg);
+       rec->exception = g_hash_table_lookup(optlist, "except") != NULL;
+       rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
+       rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
+       rec->replies = g_hash_table_lookup(optlist, "replies") != NULL;
+       timestr = g_hash_table_lookup(optlist, "time");
+        if (timestr != NULL)
+               rec->unignore_time = time(NULL)+atoi(timestr);
+
+       if (new_ignore)
+               ignore_add_rec(rec);
+       else
+               ignore_update_rec(rec);
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: UNIGNORE <id>|<mask> */
+static void cmd_unignore(const char *data)
+{
+       IGNORE_REC *rec;
+       GSList *tmp;
+        char *mask;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1, &mask))
+               return;
+
+       if (*mask == '\0')
+                cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       if (is_numeric(mask, ' ')) {
+               /* with index number */
+               tmp = g_slist_nth(ignores, atoi(mask)-1);
+               rec = tmp == NULL ? NULL : tmp->data;
+       } else {
+               /* with mask */
+               const char *chans[2] = { "*", NULL };
+
+               if (active_win->active_server != NULL &&
+                   server_ischannel(active_win->active_server, mask)) {
+                       chans[0] = mask;
+                       mask = NULL;
+               }
+               rec = ignore_find("*", mask, (char **) chans);
+       }
+
+       if (rec != NULL) {
+               rec->level = 0;
+               ignore_update_rec(rec);
+       } else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_IGNORE_NOT_FOUND, mask);
+       }
+       cmd_params_free(free_arg);
+}
+
+static void sig_ignore_created(IGNORE_REC *rec)
+{
+       char *key, *levels;
+
+       key = ignore_get_key(rec);
+       levels = bits2level(rec->level);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_IGNORED, key, levels);
+       g_free(key);
+       g_free(levels);
+}
+
+static void sig_ignore_destroyed(IGNORE_REC *rec)
+{
+       char *key;
+
+       key = ignore_get_key(rec);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_UNIGNORED, key);
+       g_free(key);
+}
+
+void fe_ignore_init(void)
+{
+       command_bind("ignore", NULL, (SIGNAL_FUNC) cmd_ignore);
+       command_bind("unignore", NULL, (SIGNAL_FUNC) cmd_unignore);
+
+       signal_add("ignore destroyed", (SIGNAL_FUNC) sig_ignore_destroyed);
+       signal_add("ignore created", (SIGNAL_FUNC) sig_ignore_created);
+       signal_add("ignore changed", (SIGNAL_FUNC) sig_ignore_created);
+
+       command_set_options("ignore", "regexp full except replies -time -pattern -channels");
+}
+
+void fe_ignore_deinit(void)
+{
+       command_unbind("ignore", (SIGNAL_FUNC) cmd_ignore);
+       command_unbind("unignore", (SIGNAL_FUNC) cmd_unignore);
+
+       signal_remove("ignore destroyed", (SIGNAL_FUNC) sig_ignore_destroyed);
+       signal_remove("ignore created", (SIGNAL_FUNC) sig_ignore_created);
+       signal_remove("ignore changed", (SIGNAL_FUNC) sig_ignore_created);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-log.c b/apps/irssi/src/fe-common/core/fe-log.c
new file mode 100644 (file)
index 0000000..e7b4ae5
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ fe-log.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "chat-protocols.h"
+#include "servers.h"
+#include "levels.h"
+#include "misc.h"
+#include "log.h"
+#include "special-vars.h"
+#include "settings.h"
+#include "lib-config/iconfig.h"
+
+#include "fe-windows.h"
+#include "window-items.h"
+#include "formats.h"
+#include "themes.h"
+#include "printtext.h"
+
+/* close autologs after 5 minutes of inactivity */
+#define AUTOLOG_INACTIVITY_CLOSE (60*5)
+
+static int autolog_level;
+static int autoremove_tag;
+static const char *autolog_path;
+
+static THEME_REC *log_theme;
+static int skip_next_printtext;
+static const char *log_theme_name;
+
+static char *log_colorizer_strip(const char *str)
+{
+        return strip_codes(str);
+}
+
+static void log_add_targets(LOG_REC *log, const char *targets, const char *tag)
+{
+       char **tmp, **items;
+
+        g_return_if_fail(log != NULL);
+        g_return_if_fail(targets != NULL);
+
+       items = g_strsplit(targets, " ", -1);
+
+       for (tmp = items; *tmp != NULL; tmp++)
+               log_item_add(log, LOG_ITEM_TARGET, *tmp, tag);
+
+       g_strfreev(items);
+}
+
+/* SYNTAX: LOG OPEN [-noopen] [-autoopen] [-window] [-<server tag>]
+                    [-targets <targets>] [-colors]
+                   <fname> [<levels>] */
+static void cmd_log_open(const char *data)
+{
+        SERVER_REC *server;
+        GHashTable *optlist;
+       char *targetarg, *fname, *levels, *servertag;
+       void *free_arg;
+       char window[MAX_INT_STRLEN];
+       LOG_REC *log;
+       int level;
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_OPTIONS,
+                           "log open", &optlist, &fname, &levels))
+               return;
+       if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       level = level2bits(levels);
+       log = log_create_rec(fname, level != 0 ? level : MSGLEVEL_ALL);
+
+       /* -<server tag> */
+       server = cmd_options_get_server("log open", optlist, NULL);
+       servertag = server == NULL ? NULL : server->tag;
+
+       if (g_hash_table_lookup(optlist, "window")) {
+               /* log by window ref# */
+               targetarg = g_hash_table_lookup(optlist, "targets");
+               if (targetarg == NULL || !is_numeric(targetarg, '\0')) {
+                       ltoa(window, active_win->refnum);
+                       targetarg = window;
+               }
+               log_item_add(log, LOG_ITEM_WINDOW_REFNUM, targetarg,
+                            servertag);
+       } else {
+               targetarg = g_hash_table_lookup(optlist, "targets");
+               if (targetarg != NULL && *targetarg != '\0')
+                       log_add_targets(log, targetarg, servertag);
+       }
+
+       if (g_hash_table_lookup(optlist, "autoopen"))
+               log->autoopen = TRUE;
+
+       if (g_hash_table_lookup(optlist, "colors") == NULL)
+               log->colorizer = log_colorizer_strip;
+
+       log_update(log);
+
+       if (log->handle == -1 && g_hash_table_lookup(optlist, "noopen") == NULL) {
+               /* start logging */
+               if (log_start_logging(log)) {
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                                   TXT_LOG_OPENED, fname);
+               } else {
+                       log_close(log);
+               }
+       }
+
+        cmd_params_free(free_arg);
+}
+
+static LOG_REC *log_find_from_data(const char *data)
+{
+       GSList *tmp;
+
+       if (!is_numeric(data, ' '))
+               return log_find(data);
+
+       /* with index number */
+       tmp = g_slist_nth(logs, atoi(data)-1);
+       return tmp == NULL ? NULL : tmp->data;
+}
+
+/* SYNTAX: LOG CLOSE <id>|<file> */
+static void cmd_log_close(const char *data)
+{
+       LOG_REC *log;
+
+       log = log_find_from_data(data);
+       if (log == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
+       else {
+               log_close(log);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
+       }
+}
+
+/* SYNTAX: LOG START <id>|<file> */
+static void cmd_log_start(const char *data)
+{
+       LOG_REC *log;
+
+       log = log_find_from_data(data);
+       if (log != NULL) {
+               log_start_logging(log);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, data);
+       }
+}
+
+/* SYNTAX: LOG STOP <id>|<file> */
+static void cmd_log_stop(const char *data)
+{
+       LOG_REC *log;
+
+       log = log_find_from_data(data);
+       if (log == NULL || log->handle == -1)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_LOG_NOT_OPEN, data);
+       else {
+               log_stop_logging(log);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, data);
+       }
+}
+
+static char *log_items_get_list(LOG_REC *log)
+{
+       GSList *tmp;
+       GString *str;
+       char *ret;
+
+       g_return_val_if_fail(log != NULL, NULL);
+       g_return_val_if_fail(log->items != NULL, NULL);
+
+       str = g_string_new(NULL);
+       for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
+               LOG_ITEM_REC *rec = tmp->data;
+
+                g_string_sprintfa(str, "%s, ", rec->name);
+       }
+       g_string_truncate(str, str->len-2);
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+static void cmd_log_list(void)
+{
+       GSList *tmp;
+       char *levelstr, *items;
+       int index;
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_HEADER);
+       for (tmp = logs, index = 1; tmp != NULL; tmp = tmp->next, index++) {
+               LOG_REC *rec = tmp->data;
+
+               levelstr = bits2level(rec->level);
+               items = rec->items == NULL ? NULL :
+                        log_items_get_list(rec);
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST,
+                           index, rec->fname, items != NULL ? items : "",
+                           levelstr, rec->autoopen ? " -autoopen" : "");
+
+               g_free_not_null(items);
+               g_free(levelstr);
+       }
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_LOG_LIST_FOOTER);
+}
+
+static void cmd_log(const char *data, SERVER_REC *server, void *item)
+{
+       if (*data == '\0')
+               cmd_log_list();
+       else
+               command_runsub("log", data, server, item);
+}
+
+static LOG_REC *logs_find_item(int type, const char *item,
+                              const char *servertag, LOG_ITEM_REC **ret_item)
+{
+       LOG_ITEM_REC *logitem;
+       GSList *tmp;
+
+       for (tmp = logs; tmp != NULL; tmp = tmp->next) {
+               LOG_REC *log = tmp->data;
+
+               logitem = log_item_find(log, type, item, servertag);
+               if (logitem != NULL) {
+                       if (ret_item != NULL) *ret_item = logitem;
+                       return log;
+               }
+       }
+
+       return NULL;
+}
+
+/* SYNTAX: WINDOW LOG on|off|toggle [<filename>] */
+static void cmd_window_log(const char *data)
+{
+       LOG_REC *log;
+       char *set, *fname, window[MAX_INT_STRLEN];
+       void *free_arg;
+       int open_log, close_log;
+
+       if (!cmd_get_params(data, &free_arg, 2, &set, &fname))
+               return;
+
+        ltoa(window, active_win->refnum);
+       log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
+
+        open_log = close_log = FALSE;
+       if (g_strcasecmp(set, "ON") == 0)
+               open_log = TRUE;
+       else if (g_strcasecmp(set, "OFF") == 0) {
+               close_log = TRUE;
+       } else if (g_strcasecmp(set, "TOGGLE") == 0) {
+                open_log = log == NULL;
+                close_log = log != NULL;
+       } else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
+               cmd_params_free(free_arg);
+               return;
+       }
+
+       if (open_log && log == NULL) {
+               /* irc.log.<windowname> or irc.log.Window<ref#> */
+               fname = *fname != '\0' ? g_strdup(fname) :
+                       g_strdup_printf("~/irc.log.%s%s",
+                                       active_win->name != NULL ? active_win->name : "Window",
+                                       active_win->name != NULL ? "" : window);
+               log = log_create_rec(fname, MSGLEVEL_ALL);
+               log->colorizer = log_colorizer_strip;
+                log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
+               log_update(log);
+               g_free(fname);
+       }
+
+       if (open_log && log != NULL) {
+               log_start_logging(log);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_OPENED, log->fname);
+       } else if (close_log && log != NULL && log->handle != -1) {
+               log_stop_logging(log);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_CLOSED, log->fname);
+       }
+
+        cmd_params_free(free_arg);
+}
+
+/* Create log file entry to window, but don't start logging */
+/* SYNTAX: WINDOW LOGFILE <file> */
+static void cmd_window_logfile(const char *data)
+{
+       LOG_REC *log;
+       char window[MAX_INT_STRLEN];
+
+        ltoa(window, active_win->refnum);
+       log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, window, NULL, NULL);
+
+       if (log != NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE_LOGGING);
+               return;
+       }
+
+       log = log_create_rec(data, MSGLEVEL_ALL);
+       log->colorizer = log_colorizer_strip;
+       log_item_add(log, LOG_ITEM_WINDOW_REFNUM, window, NULL);
+       log_update(log);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_WINDOWLOG_FILE, data);
+}
+
+/* window's refnum changed - update the logs to log the new window refnum */
+static void sig_window_refnum_changed(WINDOW_REC *window, gpointer old_refnum)
+{
+       char winnum[MAX_INT_STRLEN];
+       LOG_REC *log;
+       LOG_ITEM_REC *item;
+
+        ltoa(winnum, GPOINTER_TO_INT(old_refnum));
+       log = logs_find_item(LOG_ITEM_WINDOW_REFNUM, winnum, NULL, &item);
+
+       if (log != NULL) {
+               ltoa(winnum, window->refnum);
+
+               g_free(item->name);
+               item->name = g_strdup(winnum);
+       }
+}
+
+static void sig_server_disconnected(SERVER_REC *server)
+{
+        LOG_ITEM_REC *logitem;
+       GSList *tmp, *next;
+
+       for (tmp = logs; tmp != NULL; tmp = next) {
+               LOG_REC *log = tmp->data;
+               next = tmp->next;
+
+               if (!log->temp || log->items == NULL)
+                        continue;
+
+               logitem = log->items->data;
+               if (logitem->type == LOG_ITEM_TARGET &&
+                   logitem->servertag != NULL &&
+                   g_strcasecmp(logitem->servertag, server->tag) == 0 &&
+                   server_ischannel(server, logitem->name)) /* kludge again.. so we won't close dcc chats */
+                       log_close(log);
+       }
+}
+
+static void autologs_close_all(void)
+{
+       GSList *tmp, *next;
+
+       for (tmp = logs; tmp != NULL; tmp = next) {
+               LOG_REC *rec = tmp->data;
+
+               next = tmp->next;
+               if (rec->temp) log_close(rec);
+       }
+}
+
+/* '%' -> '%%', '/' -> '_' */
+static char *escape_target(const char *target)
+{
+       char *str, *p;
+
+       p = str = g_malloc(strlen(target)*2+1);
+       while (*target != '\0') {
+               if (*target == '/')
+                       *p++ = '_';
+               else {
+                       if (*target == '%')
+                               *p++ = '%';
+                       *p++ = *target;
+               }
+
+                target++;
+       }
+       *p = '\0';
+
+        return str;
+}
+
+static void autolog_open(SERVER_REC *server, const char *server_tag,
+                        const char *target)
+{
+       LOG_REC *log;
+       char *fname, *dir, *fixed_target;
+
+       log = logs_find_item(LOG_ITEM_TARGET, target, server_tag, NULL);
+       if (log != NULL && !log->failed) {
+               log_start_logging(log);
+               return;
+       }
+
+       /* '/' -> '_' - don't even accidentally try to log to
+          #../../../file if you happen to join to such channel..
+
+          '%' -> '%%' - so strftime() won't mess with them */
+       fixed_target = escape_target(target);
+       if (CHAT_PROTOCOL(server)->case_insensitive)
+               g_strdown(fixed_target);
+
+       fname = parse_special_string(autolog_path, server, NULL,
+                                    fixed_target, NULL, 0);
+       g_free(fixed_target);
+
+       if (log_find(fname) == NULL) {
+               log = log_create_rec(fname, autolog_level);
+                if (!settings_get_bool("autolog_colors"))
+                       log->colorizer = log_colorizer_strip;
+               log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
+
+               dir = g_dirname(log->real_fname);
+               mkpath(dir, LOG_DIR_CREATE_MODE);
+               g_free(dir);
+
+               log->temp = TRUE;
+               log_update(log);
+               log_start_logging(log);
+       }
+       g_free(fname);
+}
+
+static void autolog_open_check(SERVER_REC *server, const char *server_tag,
+                              const char *target, int level)
+{
+       char **targets, **tmp;
+
+       /* FIXME: kind of a kludge, but we don't want to reopen logs when
+          we're parting the channel with /WINDOW CLOSE.. Maybe a small
+          timeout would be nice instead of immediately closing the log file
+          after "window item destroyed" */
+       if (level == MSGLEVEL_PARTS ||
+           (autolog_level & level) == 0 || target == NULL || *target == '\0')
+               return;
+
+       /* there can be multiple targets separated with comma */
+       targets = g_strsplit(target, ",", -1);
+       for (tmp = targets; *tmp != NULL; tmp++)
+               autolog_open(server, server_tag, *tmp);
+       g_strfreev(targets);
+}
+
+static void log_single_line(WINDOW_REC *window, const char *server_tag,
+                           const char *target, int level, const char *text)
+{
+       char windownum[MAX_INT_STRLEN];
+       char **targets, **tmp;
+       LOG_REC *log;
+
+       /* save to log created with /WINDOW LOG */
+       ltoa(windownum, window->refnum);
+       log = logs_find_item(LOG_ITEM_WINDOW_REFNUM,
+                            windownum, NULL, NULL);
+       if (log != NULL) {
+               log_write_rec(log, text, level);
+       }
+
+       if (target == NULL)
+               log_file_write(server_tag, NULL, level, text, FALSE);
+       else {
+               /* there can be multiple items separated with comma */
+               targets = g_strsplit(target, ",", -1);
+               for (tmp = targets; *tmp != NULL; tmp++)
+                       log_file_write(server_tag, *tmp, level, text, FALSE);
+               g_strfreev(targets);
+       }
+}
+
+static void log_line(TEXT_DEST_REC *dest, const char *text)
+{
+       char **lines, **tmp;
+
+       if (dest->level == MSGLEVEL_NEVER)
+               return;
+
+       /* let autolog open the log records */
+       autolog_open_check(dest->server, dest->server_tag,
+                          dest->target, dest->level);
+
+       if (logs == NULL)
+               return;
+
+       /* text may contain one or more lines, log wants to eat them one
+          line at a time */
+       lines = g_strsplit(text, "\n", -1);
+       for (tmp = lines; *tmp != NULL; tmp++)
+               log_single_line(dest->window, dest->server_tag,
+                               dest->target, dest->level, *tmp);
+       g_strfreev(lines);
+}
+
+static void sig_printtext(TEXT_DEST_REC *dest, const char *text,
+                         const char *stripped)
+{
+       if (skip_next_printtext) {
+               skip_next_printtext = FALSE;
+               return;
+       }
+
+       log_line(dest, text);
+}
+
+static void sig_print_format(THEME_REC *theme, const char *module,
+                            TEXT_DEST_REC *dest, void *formatnum, char **args)
+{
+       char *str, *linestart, *tmp;
+
+       if (log_theme == NULL) {
+               /* theme isn't loaded for some reason (/reload destroys it),
+                  reload it. */
+               log_theme = theme_load(log_theme_name);
+               if (log_theme == NULL) return;
+       }
+
+       if (theme == log_theme)
+               return;
+
+       str = format_get_text_theme_charargs(log_theme, module, dest,
+                                            GPOINTER_TO_INT(formatnum), args);
+       skip_next_printtext = TRUE;
+
+       if (*str != '\0') {
+                /* add the line start format */
+               linestart = format_get_level_tag(log_theme, dest);
+                tmp = str;
+               str = format_add_linestart(tmp, linestart);
+               g_free_not_null(linestart);
+               g_free(tmp);
+
+               /* strip colors from text, log it. */
+               log_line(dest, str);
+       }
+       g_free(str);
+
+}
+
+static int sig_autoremove(void)
+{
+       SERVER_REC *server;
+       LOG_ITEM_REC *logitem;
+       GSList *tmp, *next;
+       time_t removetime;
+
+        removetime = time(NULL)-AUTOLOG_INACTIVITY_CLOSE;
+       for (tmp = logs; tmp != NULL; tmp = next) {
+               LOG_REC *log = tmp->data;
+
+               next = tmp->next;
+
+               if (!log->temp || log->last > removetime || log->items == NULL)
+                        continue;
+
+               /* Close only logs with private messages */
+               logitem = log->items->data;
+               if (logitem->servertag == NULL)
+                       continue;
+
+               server = server_find_tag(logitem->servertag);
+               if (logitem->type == LOG_ITEM_TARGET &&
+                   server != NULL && !server_ischannel(server, logitem->name))
+                       log_close(log);
+       }
+       return 1;
+}
+
+static void sig_window_item_destroy(WINDOW_REC *window, WI_ITEM_REC *item)
+{
+       LOG_REC *log;
+
+       log = logs_find_item(LOG_ITEM_TARGET, item->name,
+                            item->server == NULL ? NULL :
+                            item->server->tag, NULL);
+       if (log != NULL && log->temp)
+               log_close(log);
+}
+
+static void sig_log_locked(LOG_REC *log)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                   TXT_LOG_LOCKED, log->fname);
+}
+
+static void sig_log_create_failed(LOG_REC *log)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                   TXT_LOG_CREATE_FAILED,
+                   log->real_fname, g_strerror(errno));
+}
+
+static void sig_log_new(LOG_REC *log)
+{
+       if (!settings_get_bool("awaylog_colors") &&
+           strcmp(log->fname, settings_get_str("awaylog_file")) == 0)
+                log->colorizer = log_colorizer_strip;
+}
+
+static void sig_log_config_read(LOG_REC *log, CONFIG_NODE *node)
+{
+        if (!config_node_get_bool(node, "colors", FALSE))
+               log->colorizer = log_colorizer_strip;
+}
+
+static void sig_log_config_save(LOG_REC *log, CONFIG_NODE *node)
+{
+        if (log->colorizer == NULL)
+               iconfig_node_set_bool(node, "colors", TRUE);
+        else
+               iconfig_node_set_str(node, "colors", NULL);
+}
+
+static void sig_awaylog_show(LOG_REC *log, gpointer pmsgs, gpointer pfilepos)
+{
+       char *str;
+       int msgs, filepos;
+
+       msgs = GPOINTER_TO_INT(pmsgs);
+       filepos = GPOINTER_TO_INT(pfilepos);
+
+       if (msgs == 0)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_NO_AWAY_MSGS, log->fname);
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOG_AWAY_MSGS, log->fname, msgs);
+
+                str = g_strdup_printf("\"%s\" %d", log->fname, filepos);
+               signal_emit("command cat", 1, str);
+               g_free(str);
+       }
+}
+
+static void sig_theme_destroyed(THEME_REC *theme)
+{
+       if (theme == log_theme)
+               log_theme = NULL;
+}
+
+static void read_settings(void)
+{
+       int old_autolog = autolog_level;
+
+       autolog_path = settings_get_str("autolog_path");
+       autolog_level = !settings_get_bool("autolog") ? 0 :
+               level2bits(settings_get_str("autolog_level"));
+
+       if (old_autolog && !autolog_level)
+               autologs_close_all();
+
+       /* write to log files with different theme? */
+       if (log_theme_name != NULL)
+               signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+       log_theme_name = settings_get_str("log_theme");
+       if (*log_theme_name == '\0')
+               log_theme_name = NULL;
+       else
+               signal_add("print format", (SIGNAL_FUNC) sig_print_format);
+
+       log_theme = log_theme_name == NULL ? NULL :
+               theme_load(log_theme_name);
+}
+
+void fe_log_init(void)
+{
+       autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL);
+       skip_next_printtext = FALSE;
+
+       settings_add_bool("log", "awaylog_colors", TRUE);
+        settings_add_bool("log", "autolog", FALSE);
+       settings_add_bool("log", "autolog_colors", FALSE);
+        settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
+       settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
+        settings_add_str("log", "log_theme", "");
+
+       autolog_level = 0;
+       log_theme_name = NULL;
+       read_settings();
+
+       command_bind("log", NULL, (SIGNAL_FUNC) cmd_log);
+       command_bind("log open", NULL, (SIGNAL_FUNC) cmd_log_open);
+       command_bind("log close", NULL, (SIGNAL_FUNC) cmd_log_close);
+       command_bind("log start", NULL, (SIGNAL_FUNC) cmd_log_start);
+       command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop);
+       command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log);
+       command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile);
+       signal_add_first("print text", (SIGNAL_FUNC) sig_printtext);
+       signal_add("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
+       signal_add("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
+       signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_add("log locked", (SIGNAL_FUNC) sig_log_locked);
+       signal_add("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
+       signal_add("log new", (SIGNAL_FUNC) sig_log_new);
+       signal_add("log config read", (SIGNAL_FUNC) sig_log_config_read);
+       signal_add("log config save", (SIGNAL_FUNC) sig_log_config_save);
+       signal_add("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
+       signal_add("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_set_options("log open", "noopen autoopen -targets window colors");
+}
+
+void fe_log_deinit(void)
+{
+       g_source_remove(autoremove_tag);
+       if (log_theme_name != NULL)
+                signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+
+       command_unbind("log", (SIGNAL_FUNC) cmd_log);
+       command_unbind("log open", (SIGNAL_FUNC) cmd_log_open);
+       command_unbind("log close", (SIGNAL_FUNC) cmd_log_close);
+       command_unbind("log start", (SIGNAL_FUNC) cmd_log_start);
+       command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop);
+       command_unbind("window log", (SIGNAL_FUNC) cmd_window_log);
+       command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile);
+       signal_remove("print text", (SIGNAL_FUNC) sig_printtext);
+       signal_remove("window item destroy", (SIGNAL_FUNC) sig_window_item_destroy);
+       signal_remove("window refnum changed", (SIGNAL_FUNC) sig_window_refnum_changed);
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked);
+       signal_remove("log create failed", (SIGNAL_FUNC) sig_log_create_failed);
+       signal_remove("log new", (SIGNAL_FUNC) sig_log_new);
+       signal_remove("log config read", (SIGNAL_FUNC) sig_log_config_read);
+       signal_remove("log config save", (SIGNAL_FUNC) sig_log_config_save);
+       signal_remove("awaylog show", (SIGNAL_FUNC) sig_awaylog_show);
+       signal_remove("theme destroyed", (SIGNAL_FUNC) sig_theme_destroyed);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-messages.c b/apps/irssi/src/fe-common/core/fe-messages.c
new file mode 100644 (file)
index 0000000..4b4e4db
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ fe-messages.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "special-vars.h"
+#include "settings.h"
+
+#include "servers.h"
+#include "channels.h"
+#include "nicklist.h"
+#include "ignore.h"
+
+#include "window-items.h"
+#include "fe-queries.h"
+#include "hilight-text.h"
+#include "printtext.h"
+
+#define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c))
+
+static GHashTable *printnicks;
+
+/* convert _underlined_ and *bold* words (and phrases) to use real
+   underlining or bolding */
+char *expand_emphasis(WI_ITEM_REC *item, const char *text)
+{
+       GString *str;
+       char *ret;
+       int pos;
+
+        g_return_val_if_fail(text != NULL, NULL);
+
+       str = g_string_new(text);
+
+       for (pos = 0; pos < str->len; pos++) {
+               char type, *bgn, *end;
+
+               bgn = str->str + pos;
+
+               if (*bgn == '*') 
+                       type = 2; /* bold */
+               else if (*bgn == '_') 
+                       type = 31; /* underlined */
+               else
+                       continue;
+
+               /* check that the beginning marker starts a word, and
+                  that the matching end marker ends a word */
+               if ((pos > 0 && !i_isspace(bgn[-1])) || !ishighalnum(bgn[1]))
+                       continue;
+               if ((end = strchr(bgn+1, *bgn)) == NULL)
+                       continue;
+               if (!ishighalnum(end[-1]) || ishighalnum(end[1]) ||
+                   end[1] == type || end[1] == '*' || end[1] == '_')
+                       continue;
+
+               if (IS_CHANNEL(item)) {
+                       /* check that this isn't a _nick_, we don't want to
+                          use emphasis on them. */
+                       int found;
+                        char c;
+
+                       c = end[1];
+                        end[1] = '\0';
+                        found = nicklist_find(CHANNEL(item), bgn) != NULL;
+                       end[1] = c;
+                       if (found) continue;
+               }
+
+               /* allow only *word* emphasis, not *multiple words* */
+               if (!settings_get_bool("emphasis_multiword")) {
+                       char *c;
+                       for (c = bgn+1; c != end; c++) {
+                               if (!ishighalnum(*c))
+                                       break;
+                       }
+                       if (c != end) continue;
+               }
+
+               if (settings_get_bool("emphasis_replace")) {
+                       *bgn = *end = type;
+                        pos += (end-bgn);
+               } else {
+                       g_string_insert_c(str, pos, type);
+                        pos += (end - bgn) + 2;
+                       g_string_insert_c(str, pos++, type);
+               }
+       }
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+static char *channel_get_nickmode_rec(NICK_REC *nickrec)
+{
+        char *emptystr;
+
+       if (!settings_get_bool("show_nickmode"))
+                return "";
+
+        emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
+
+       return nickrec == NULL ? emptystr :
+               nickrec->op ? "@" :
+               nickrec->halfop ? "%" :
+               nickrec->voice ? "+" :
+               emptystr;
+}
+
+char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick)
+{
+       g_return_val_if_fail(nick != NULL, NULL);
+
+        return channel_get_nickmode_rec(channel == NULL ? NULL :
+                                       nicklist_find(channel, nick));
+}
+
+static void sig_message_public(SERVER_REC *server, const char *msg,
+                              const char *nick, const char *address,
+                              const char *target, NICK_REC *nickrec)
+{
+       CHANNEL_REC *chanrec;
+       const char *nickmode, *printnick;
+       int for_me, print_channel, level;
+       char *color, *freemsg = NULL;
+
+       /* NOTE: this may return NULL if some channel is just closed with
+          /WINDOW CLOSE and server still sends the few last messages */
+       chanrec = channel_find(server, target);
+       if (nickrec == NULL && chanrec != NULL)
+                nickrec = nicklist_find(chanrec, nick);
+
+       for_me = nick_match_msg(chanrec, msg, server->nick);
+       color = for_me ? NULL :
+               hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
+
+       print_channel = chanrec == NULL ||
+               !window_item_is_active((WI_ITEM_REC *) chanrec);
+       if (!print_channel && settings_get_bool("print_active_channel") &&
+           window_item_window((WI_ITEM_REC *) chanrec)->items->next != NULL)
+               print_channel = TRUE;
+
+       level = MSGLEVEL_PUBLIC;
+       if (for_me || color != NULL)
+               level |= MSGLEVEL_HILIGHT;
+
+       if (settings_get_bool("emphasis"))
+               msg = freemsg = expand_emphasis((WI_ITEM_REC *) chanrec, msg);
+
+       /* get nick mode & nick what to print the msg with
+          (in case there's multiple identical nicks) */
+       nickmode = channel_get_nickmode_rec(nickrec);
+       printnick = nickrec == NULL ? nick :
+               g_hash_table_lookup(printnicks, nickrec);
+       if (printnick == NULL)
+               printnick = nick;
+
+       if (!print_channel) {
+               /* message to active channel in window */
+               if (color != NULL) {
+                       /* highlighted nick */
+                       printformat(server, target, level,
+                                   TXT_PUBMSG_HILIGHT,
+                                   color, printnick, msg, nickmode);
+               } else {
+                       printformat(server, target, level,
+                                   for_me ? TXT_PUBMSG_ME : TXT_PUBMSG,
+                                   printnick, msg, nickmode);
+               }
+       } else {
+               /* message to not existing/active channel */
+               if (color != NULL) {
+                       /* highlighted nick */
+                       printformat(server, target, level,
+                                   TXT_PUBMSG_HILIGHT_CHANNEL,
+                                   color, printnick, target, msg, nickmode);
+               } else {
+                       printformat(server, target, level,
+                                   for_me ? TXT_PUBMSG_ME_CHANNEL :
+                                   TXT_PUBMSG_CHANNEL,
+                                   printnick, target, msg, nickmode);
+               }
+       }
+
+        g_free_not_null(freemsg);
+       g_free_not_null(color);
+}
+
+static void sig_message_private(SERVER_REC *server, const char *msg,
+                               const char *nick, const char *address)
+{
+       QUERY_REC *query;
+        char *freemsg = NULL;
+
+       query = query_find(server, nick);
+
+       if (settings_get_bool("emphasis"))
+               msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
+
+       printformat(server, nick, MSGLEVEL_MSGS,
+                   query == NULL ? TXT_MSG_PRIVATE :
+                   TXT_MSG_PRIVATE_QUERY, nick, address, msg);
+
+       g_free_not_null(freemsg);
+}
+
+static void print_own_channel_message(SERVER_REC *server, CHANNEL_REC *channel,
+                                     const char *target, const char *msg)
+{
+       WINDOW_REC *window;
+       const char *nickmode;
+        char *freemsg = NULL;
+       int print_channel;
+
+       nickmode = channel_get_nickmode(channel, server->nick);
+
+       window = channel == NULL ? NULL :
+               window_item_window((WI_ITEM_REC *) channel);
+
+       print_channel = window == NULL ||
+               window->active != (WI_ITEM_REC *) channel;
+
+       if (!print_channel && settings_get_bool("print_active_channel") &&
+           window != NULL && g_slist_length(window->items) > 1)
+               print_channel = TRUE;
+
+       if (settings_get_bool("emphasis"))
+               msg = freemsg = expand_emphasis((WI_ITEM_REC *) channel, msg);
+
+       if (!print_channel) {
+               printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
+                           TXT_OWN_MSG, server->nick, msg, nickmode);
+       } else {
+               printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
+                           TXT_OWN_MSG_CHANNEL, server->nick, target, msg, nickmode);
+       }
+
+       g_free_not_null(freemsg);
+}
+
+static void sig_message_own_public(SERVER_REC *server, const char *msg,
+                                  const char *target)
+{
+       CHANNEL_REC *channel;
+
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(msg != NULL);
+
+       channel = channel_find(server, target);
+       print_own_channel_message(server, channel, target, msg);
+}
+
+static void sig_message_own_private(SERVER_REC *server, const char *msg,
+                                   const char *target, const char *origtarget)
+{
+       QUERY_REC *query;
+        char *freemsg = NULL;
+
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(msg != NULL);
+
+       if (target == NULL) {
+               /* this should only happen if some special target failed and
+                  we should display some error message. currently the special
+                  targets are only ',' and '.'. */
+               g_return_if_fail(strcmp(origtarget, ",") == 0 ||
+                                strcmp(origtarget, ".") == 0);
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           *origtarget == ',' ? TXT_NO_MSGS_GOT :
+                           TXT_NO_MSGS_SENT);
+               signal_stop();
+               return;
+       }
+
+       query = privmsg_get_query(server, target, TRUE, MSGLEVEL_MSGS);
+
+       if (settings_get_bool("emphasis"))
+               msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
+
+       printformat(server, target,
+                   MSGLEVEL_MSGS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
+                   query == NULL ? TXT_OWN_MSG_PRIVATE :
+                   TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick);
+
+       g_free_not_null(freemsg);
+}
+
+static void sig_message_join(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *address)
+{
+       printformat(server, channel, MSGLEVEL_JOINS,
+                   TXT_JOIN, nick, address, channel);
+}
+
+static void sig_message_part(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *address,
+                            const char *reason)
+{
+       printformat(server, channel, MSGLEVEL_PARTS,
+                   TXT_PART, nick, address, channel, reason);
+}
+
+static void sig_message_quit(SERVER_REC *server, const char *nick,
+                            const char *address, const char *reason)
+{
+       WINDOW_REC *window;
+       GString *chans;
+       GSList *tmp, *windows;
+       char *print_channel;
+       int once, count;
+
+       if (ignore_check(server, nick, address, NULL, reason, MSGLEVEL_QUITS))
+               return;
+
+       print_channel = NULL;
+       once = settings_get_bool("show_quit_once");
+
+       count = 0; windows = NULL;
+       chans = g_string_new(NULL);
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *rec = tmp->data;
+
+               if (!nicklist_find(rec, nick))
+                       continue;
+
+               if (ignore_check(server, nick, address, rec->name,
+                                reason, MSGLEVEL_QUITS)) {
+                       count++;
+                       continue;
+               }
+
+               if (print_channel == NULL ||
+                   active_win->active == (WI_ITEM_REC *) rec)
+                       print_channel = rec->name;
+
+               if (once)
+                       g_string_sprintfa(chans, "%s,", rec->name);
+               else {
+                       window = window_item_window((WI_ITEM_REC *) rec);
+                       if (g_slist_find(windows, window) == NULL) {
+                               windows = g_slist_append(windows, window);
+                               printformat(server, rec->name, MSGLEVEL_QUITS,
+                                           TXT_QUIT, nick, address, reason,
+                                           rec->name);
+                       }
+               }
+               count++;
+       }
+       g_slist_free(windows);
+
+       if (!once) {
+               /* check if you had query with the nick and
+                  display the quit there too */
+               QUERY_REC *query = query_find(server, nick);
+               if (query != NULL) {
+                       printformat(server, nick, MSGLEVEL_QUITS,
+                                   TXT_QUIT, nick, address, reason, "");
+               }
+       }
+
+       if (once || count == 0) {
+               if (chans->len > 0)
+                       g_string_truncate(chans, chans->len-1);
+               printformat(server, print_channel, MSGLEVEL_QUITS,
+                           count <= 1 ? TXT_QUIT : TXT_QUIT_ONCE,
+                           nick, address, reason, chans->str);
+       }
+       g_string_free(chans, TRUE);
+}
+
+static void sig_message_kick(SERVER_REC *server, const char *channel,
+                            const char *nick, const char *kicker,
+                            const char *address, const char *reason)
+{
+       printformat(server, channel, MSGLEVEL_KICKS,
+                   TXT_KICK, nick, channel, kicker, reason);
+}
+
+static void print_nick_change_channel(SERVER_REC *server, const char *channel,
+                                     const char *newnick, const char *oldnick,
+                                     const char *address,
+                                     int ownnick)
+{
+       int level;
+
+       if (ignore_check(server, oldnick, address,
+                        channel, newnick, MSGLEVEL_NICKS))
+               return;
+
+       level = MSGLEVEL_NICKS;
+        if (ownnick) level |= MSGLEVEL_NO_ACT;
+
+       printformat(server, channel, level,
+                   ownnick ? TXT_YOUR_NICK_CHANGED : TXT_NICK_CHANGED,
+                   oldnick, newnick, channel);
+}
+
+static void print_nick_change(SERVER_REC *server, const char *newnick,
+                             const char *oldnick, const char *address,
+                             int ownnick)
+{
+       GSList *tmp, *windows;
+       int msgprint;
+
+       msgprint = FALSE;
+
+       /* Print to each channel/query where the nick is.
+          Don't print more than once to the same window. */
+       windows = NULL;
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               CHANNEL_REC *channel = tmp->data;
+               WINDOW_REC *window =
+                       window_item_window((WI_ITEM_REC *) channel);
+
+               if (nicklist_find(channel, newnick) == NULL ||
+                   g_slist_find(windows, window) != NULL)
+                       continue;
+
+               windows = g_slist_append(windows, window);
+               print_nick_change_channel(server, channel->name, newnick,
+                                         oldnick, address, ownnick);
+               msgprint = TRUE;
+       }
+
+       g_slist_free(windows);
+
+       if (!msgprint && ownnick) {
+               printformat(server, NULL, MSGLEVEL_NICKS,
+                           TXT_YOUR_NICK_CHANGED, oldnick, newnick, "");
+       }
+}
+
+static void sig_message_nick(SERVER_REC *server, const char *newnick,
+                            const char *oldnick, const char *address)
+{
+       print_nick_change(server, newnick, oldnick, address, FALSE);
+}
+
+static void sig_message_own_nick(SERVER_REC *server, const char *newnick,
+                                const char *oldnick, const char *address)
+{
+        if (!settings_get_bool("show_own_nickchange_once"))
+               print_nick_change(server, newnick, oldnick, address, TRUE);
+       else {
+               printformat(server, NULL, MSGLEVEL_NICKS,
+                           TXT_YOUR_NICK_CHANGED, oldnick, newnick, "");
+       }
+}
+
+static void sig_message_invite(SERVER_REC *server, const char *channel,
+                              const char *nick, const char *address)
+{
+       char *str;
+
+       str = show_lowascii(channel);
+       printformat(server, NULL, MSGLEVEL_INVITES,
+                   TXT_INVITE, nick, str);
+       g_free(str);
+}
+
+static void sig_message_topic(SERVER_REC *server, const char *channel,
+                             const char *topic,
+                             const char *nick, const char *address)
+{
+       printformat(server, channel, MSGLEVEL_TOPICS,
+                   *topic != '\0' ? TXT_NEW_TOPIC : TXT_TOPIC_UNSET,
+                   nick, channel, topic);
+}
+
+static int printnick_exists(NICK_REC *first, NICK_REC *ignore,
+                           const char *nick)
+{
+       char *printnick;
+
+       while (first != NULL) {
+               if (first != ignore) {
+                       printnick = g_hash_table_lookup(printnicks, first);
+                       if (printnick != NULL && strcmp(printnick, nick) == 0)
+                               return TRUE;
+               }
+
+               first = first->next;
+       }
+
+        return FALSE;
+}
+
+static NICK_REC *printnick_find_original(NICK_REC *nick)
+{
+       while (nick != NULL) {
+               if (g_hash_table_lookup(printnicks, nick) == NULL)
+                        return nick;
+
+               nick = nick->next;
+       }
+
+       return NULL;
+}
+
+static void sig_nicklist_new(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       NICK_REC *firstnick;
+       GString *newnick;
+       char *nickhost, *p;
+       int n;
+
+       if (nick->host == NULL)
+                return;
+
+       firstnick = g_hash_table_lookup(channel->nicks, nick->nick);
+       if (firstnick->next == NULL)
+               return;
+
+       if (nick == channel->ownnick) {
+               /* own nick is being added, might be a nick change and
+                  someone else having the original nick already in use.. */
+               nick = printnick_find_original(firstnick->next);
+               if (nick == NULL)
+                        return; /* nope, we have it */
+       }
+
+       /* identical nick already exists, have to change it somehow.. */
+       p = strchr(nick->host, '@');
+       if (p == NULL) p = nick->host; else p++;
+
+       nickhost = g_strdup_printf("%s@%s", nick->nick, p);
+       p = strchr(nickhost+strlen(nick->nick), '.');
+       if (p != NULL) *p = '\0';
+
+       if (!printnick_exists(firstnick, nick, nickhost)) {
+                /* use nick@host */
+               g_hash_table_insert(printnicks, nick, nickhost);
+                return;
+       }
+
+       newnick = g_string_new(NULL);
+        n = 2;
+       do {
+               g_string_sprintf(newnick, "%s%d", nickhost, n);
+                n++;
+       } while (printnick_exists(firstnick, nick, newnick->str));
+
+       g_hash_table_insert(printnicks, nick, newnick->str);
+       g_string_free(newnick, FALSE);
+        g_free(nickhost);
+}
+
+static void sig_nicklist_remove(CHANNEL_REC *channel, NICK_REC *nick)
+{
+       char *nickname;
+
+       nickname = g_hash_table_lookup(printnicks, nick);
+       if (nickname != NULL) {
+                g_free(nickname);
+               g_hash_table_remove(printnicks, nick);
+       }
+}
+
+static void sig_nicklist_changed(CHANNEL_REC *channel, NICK_REC *nick)
+{
+        sig_nicklist_remove(channel, nick);
+        sig_nicklist_new(channel, nick);
+}
+
+static void sig_channel_joined(CHANNEL_REC *channel)
+{
+        NICK_REC *nick;
+       char *nickname;
+
+       /* channel->ownnick is set at this point - check if our own nick
+          has been changed, if it was set it back to the original nick and
+          change the previous original to something else */
+
+        nickname = g_hash_table_lookup(printnicks, channel->ownnick);
+       if (nickname == NULL)
+               return;
+
+        g_free(nickname);
+       g_hash_table_remove(printnicks, channel->ownnick);
+
+        /* our own nick is guaranteed to be the first in list */
+        nick = channel->ownnick->next;
+       while (nick != NULL) {
+               if (g_hash_table_lookup(printnicks, nick) == NULL) {
+                       sig_nicklist_new(channel, nick);
+                        break;
+               }
+                nick = nick->next;
+       }
+}
+
+static void g_hash_free_value(void *key, void *value)
+{
+        g_free(value);
+}
+
+void fe_messages_init(void)
+{
+       printnicks = g_hash_table_new((GHashFunc) g_direct_hash,
+                                     (GCompareFunc) g_direct_equal);
+
+       settings_add_bool("lookandfeel", "emphasis", TRUE);
+       settings_add_bool("lookandfeel", "emphasis_replace", FALSE);
+       settings_add_bool("lookandfeel", "emphasis_multiword", FALSE);
+       settings_add_bool("lookandfeel", "show_nickmode", TRUE);
+       settings_add_bool("lookandfeel", "show_nickmode_empty", TRUE);
+       settings_add_bool("lookandfeel", "print_active_channel", FALSE);
+       settings_add_bool("lookandfeel", "show_quit_once", FALSE);
+       settings_add_bool("lookandfeel", "show_own_nickchange_once", FALSE);
+
+       signal_add_last("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_add_last("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_add_last("message own_public", (SIGNAL_FUNC) sig_message_own_public);
+       signal_add_last("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_add_last("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_add_last("message part", (SIGNAL_FUNC) sig_message_part);
+       signal_add_last("message quit", (SIGNAL_FUNC) sig_message_quit);
+       signal_add_last("message kick", (SIGNAL_FUNC) sig_message_kick);
+       signal_add_last("message nick", (SIGNAL_FUNC) sig_message_nick);
+       signal_add_last("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
+       signal_add_last("message invite", (SIGNAL_FUNC) sig_message_invite);
+       signal_add_last("message topic", (SIGNAL_FUNC) sig_message_topic);
+
+       signal_add("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
+       signal_add("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
+       signal_add("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed);
+       signal_add("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new);
+       signal_add("channel joined", (SIGNAL_FUNC) sig_channel_joined);
+}
+
+void fe_messages_deinit(void)
+{
+        g_hash_table_foreach(printnicks, (GHFunc) g_hash_free_value, NULL);
+       g_hash_table_destroy(printnicks);
+
+       signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
+       signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
+       signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
+       signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
+       signal_remove("message part", (SIGNAL_FUNC) sig_message_part);
+       signal_remove("message quit", (SIGNAL_FUNC) sig_message_quit);
+       signal_remove("message kick", (SIGNAL_FUNC) sig_message_kick);
+       signal_remove("message nick", (SIGNAL_FUNC) sig_message_nick);
+       signal_remove("message own_nick", (SIGNAL_FUNC) sig_message_own_nick);
+       signal_remove("message invite", (SIGNAL_FUNC) sig_message_invite);
+       signal_remove("message topic", (SIGNAL_FUNC) sig_message_topic);
+
+       signal_remove("nicklist new", (SIGNAL_FUNC) sig_nicklist_new);
+       signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nicklist_remove);
+       signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nicklist_changed);
+       signal_remove("nicklist host changed", (SIGNAL_FUNC) sig_nicklist_new);
+       signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-messages.h b/apps/irssi/src/fe-common/core/fe-messages.h
new file mode 100644 (file)
index 0000000..afe7644
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __FE_MESSAGES_H
+#define __FE_MESSAGES_H
+
+/* convert _underlined_ and *bold* words (and phrases) to use real
+   underlining or bolding */
+char *expand_emphasis(WI_ITEM_REC *item, const char *text);
+
+char *channel_get_nickmode(CHANNEL_REC *channel, const char *nick);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-modules.c b/apps/irssi/src/fe-common/core/fe-modules.c
new file mode 100644 (file)
index 0000000..5bbcf0b
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ fe-common-core.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "modules.h"
+#include "modules-load.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "chat-protocols.h"
+
+#include "printtext.h"
+
+#ifdef HAVE_GMODULE
+
+static void sig_module_error(void *number, const char *data,
+                            const char *rootmodule, const char *submodule)
+{
+       switch (GPOINTER_TO_INT(number)) {
+       case MODULE_ERROR_ALREADY_LOADED:
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_MODULE_ALREADY_LOADED, rootmodule, submodule);
+               break;
+       case MODULE_ERROR_LOAD:
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_MODULE_LOAD_ERROR, rootmodule, submodule, data);
+               break;
+       case MODULE_ERROR_INVALID:
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_MODULE_INVALID, rootmodule, submodule);
+               break;
+       }
+}
+
+static void sig_module_loaded(MODULE_REC *module, MODULE_FILE_REC *file)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_MODULE_LOADED, module->name, file->name);
+}
+
+static void sig_module_unloaded(MODULE_REC *module, MODULE_FILE_REC *file)
+{
+       if (file != NULL && file->gmodule != NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_MODULE_UNLOADED, module->name, file->name);
+       }
+}
+
+static int module_list_sub(MODULE_REC *module, int mark_type,
+                          GString *submodules)
+{
+       GSList *tmp;
+        int all_dynamic, dynamic;
+
+       g_string_truncate(submodules, 0);
+
+        all_dynamic = -1;
+       for (tmp = module->files; tmp != NULL; tmp = tmp->next) {
+               MODULE_FILE_REC *file = tmp->data;
+
+               /* if there's dynamic and static modules mixed, we'll need
+                  to specify them separately */
+               if (!mark_type) {
+                       dynamic = file->gmodule != NULL;
+                       if (all_dynamic != -1 && all_dynamic != dynamic) {
+                               return module_list_sub(module, TRUE,
+                                                      submodules);
+                       }
+                       all_dynamic = dynamic;
+               }
+
+               if (submodules->len > 0)
+                       g_string_append_c(submodules, ' ');
+               g_string_append(submodules, file->name);
+               if (mark_type) {
+                       g_string_append(submodules, file->gmodule == NULL ?
+                                       " (static)" : " (dynamic)");
+               }
+       }
+
+        return all_dynamic;
+}
+
+static void cmd_load_list(void)
+{
+       GSList *tmp;
+       GString *submodules;
+        const char *type;
+        int dynamic;
+
+        submodules = g_string_new(NULL);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_HEADER);
+       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+               MODULE_REC *rec = tmp->data;
+
+                dynamic = module_list_sub(rec, FALSE, submodules);
+               type = dynamic == -1 ? "mixed" :
+                       dynamic ? "dynamic" : "static";
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                           TXT_MODULE_LINE, rec->name, type, submodules->str);
+       }
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_MODULE_FOOTER);
+
+       g_string_free(submodules, TRUE);
+}
+
+static char **module_prefixes_get(void)
+{
+        GSList *tmp;
+        char **list, *name;
+        int count;
+
+       list = g_new(char *, 2 + 2*g_slist_length(chat_protocols));
+       list[0] = "fe";
+
+       count = 1;
+       for (tmp = chat_protocols; tmp != NULL; tmp = tmp->next) {
+               CHAT_PROTOCOL_REC *rec = tmp->data;
+
+               name = g_strdup(rec->name);
+                g_strdown(name);
+
+               list[count++] = name;
+                list[count++] = g_strconcat("fe_", name, NULL);
+       }
+       list[count] = NULL;
+
+        return list;
+}
+
+static void module_prefixes_free(char **list)
+{
+       char **pos = list+1;
+
+       while (*pos != NULL) {
+                g_free(*pos);
+                pos++;
+       }
+        g_free(list);
+}
+
+/* SYNTAX: LOAD <module> [<submodule>] */
+static void cmd_load(const char *data)
+{
+        char *rootmodule, *submodule;
+       char **module_prefixes;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule))
+               return;
+
+       if (*rootmodule == '\0')
+               cmd_load_list();
+       else {
+               module_prefixes = module_prefixes_get();
+               if (*submodule == '\0')
+                       module_load(rootmodule, module_prefixes);
+               else {
+                       module_load_sub(rootmodule, submodule,
+                                       module_prefixes);
+               }
+                module_prefixes_free(module_prefixes);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: UNLOAD <module> [<submodule>] */
+static void cmd_unload(const char *data)
+{
+       MODULE_REC *module;
+        MODULE_FILE_REC *file;
+        char *rootmodule, *submodule;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule))
+               return;
+       if (*rootmodule == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       module = module_find(rootmodule);
+       if (module != NULL) {
+               if (*submodule == '\0')
+                       module_unload(module);
+               else {
+                       file = module_file_find(module, submodule);
+                        if (file != NULL)
+                               module_file_unload(file);
+                       else
+                                module = NULL;
+               }
+       }
+
+       if (module == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                            TXT_MODULE_NOT_LOADED, rootmodule, submodule);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+void fe_modules_init(void)
+{
+       signal_add("module error", (SIGNAL_FUNC) sig_module_error);
+       signal_add("module loaded", (SIGNAL_FUNC) sig_module_loaded);
+       signal_add("module unloaded", (SIGNAL_FUNC) sig_module_unloaded);
+
+       command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
+       command_bind("unload", NULL, (SIGNAL_FUNC) cmd_unload);
+}
+
+void fe_modules_deinit(void)
+{
+       signal_remove("module error", (SIGNAL_FUNC) sig_module_error);
+       signal_remove("module loaded", (SIGNAL_FUNC) sig_module_loaded);
+       signal_remove("module unloaded", (SIGNAL_FUNC) sig_module_unloaded);
+
+       command_unbind("load", (SIGNAL_FUNC) cmd_load);
+       command_unbind("unload", (SIGNAL_FUNC) cmd_unload);
+}
+
+#else /* !HAVE_GMODULE */
+
+static void cmd_load(const char *data)
+{
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                 "Dynamic modules loading not supported");
+}
+
+void fe_modules_init(void)
+{
+       command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
+}
+
+void fe_modules_deinit(void)
+{
+       command_unbind("load", (SIGNAL_FUNC) cmd_load);
+}
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-queries.c b/apps/irssi/src/fe-common/core/fe-queries.c
new file mode 100644 (file)
index 0000000..44d7ef0
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ fe-queries.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "queries.h"
+
+#include "fe-windows.h"
+#include "window-items.h"
+#include "printtext.h"
+
+static int queryclose_tag, query_auto_close, querycreate_level;
+
+/* Return query where to put the private message. */
+QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick,
+                            int own, int level)
+{
+       QUERY_REC *query;
+
+       g_return_val_if_fail(IS_SERVER(server), NULL);
+        g_return_val_if_fail(nick != NULL, NULL);
+
+       query = query_find(server, nick);
+       if (query == NULL && (querycreate_level & level) != 0 &&
+           (!own || settings_get_bool("autocreate_own_query"))) {
+               query = CHAT_PROTOCOL(server)->
+                       query_create(server->tag, nick, TRUE);
+       }
+
+       return query;
+}
+
+static void signal_query_created(QUERY_REC *query, gpointer automatic)
+{
+        TEXT_DEST_REC dest;
+
+       g_return_if_fail(IS_QUERY(query));
+
+       if (window_item_window(query) == NULL) {
+               window_item_create((WI_ITEM_REC *) query,
+                                  GPOINTER_TO_INT(automatic));
+       }
+
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest, TXT_QUERY_START,
+                        query->name, query->server_tag);
+}
+
+static void signal_query_created_curwin(QUERY_REC *query)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+       window_item_add(active_win, (WI_ITEM_REC *) query, FALSE);
+}
+
+static void signal_query_destroyed(QUERY_REC *query)
+{
+       WINDOW_REC *window;
+        TEXT_DEST_REC dest;
+
+       g_return_if_fail(IS_QUERY(query));
+
+       window = window_item_window((WI_ITEM_REC *) query);
+       if (window == NULL)
+               return;
+
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest, TXT_QUERY_STOP, query->name);
+
+       window_item_destroy((WI_ITEM_REC *) query);
+
+       if (!query->unwanted)
+               window_auto_destroy(window);
+}
+
+static void signal_query_server_changed(QUERY_REC *query)
+{
+       WINDOW_REC *window;
+
+       g_return_if_fail(query != NULL);
+
+       window = window_item_window((WI_ITEM_REC *) query);
+       if (window->active == (WI_ITEM_REC *) query)
+               window_change_server(window, query->server);
+}
+
+static void signal_query_nick_changed(QUERY_REC *query, const char *oldnick)
+{
+        TEXT_DEST_REC dest;
+
+       g_return_if_fail(query != NULL);
+
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest,  TXT_NICK_CHANGED, oldnick,
+                        query->name, query->name);
+
+       signal_emit("window item changed", 2,
+                   window_item_window((WI_ITEM_REC *) query), query);
+}
+
+static void signal_window_item_server_changed(WINDOW_REC *window,
+                                             QUERY_REC *query)
+{
+       if (IS_QUERY(query)) {
+               g_free_and_null(query->server_tag);
+                if (query->server != NULL)
+                       query->server_tag = g_strdup(query->server->tag);
+       }
+}
+
+static void sig_server_connected(SERVER_REC *server)
+{
+       GSList *tmp;
+
+       if (!IS_SERVER(server))
+               return;
+
+       /* check if there's any queries without server */
+       for (tmp = queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               if (rec->server == NULL &&
+                   (rec->server_tag == NULL ||
+                    g_strcasecmp(rec->server_tag, server->tag) == 0)) {
+                       window_item_change_server((WI_ITEM_REC *) rec, server);
+                       server->queries = g_slist_append(server->queries, rec);
+               }
+       }
+}
+
+static void cmd_window_server(const char *data)
+{
+       SERVER_REC *server;
+        QUERY_REC *query;
+        TEXT_DEST_REC dest;
+
+       g_return_if_fail(data != NULL);
+
+       server = server_find_tag(data);
+        query = QUERY(active_win->active);
+       if (server == NULL || query == NULL)
+               return;
+
+       /* /WINDOW SERVER used in a query window */
+       format_create_dest_tag(&dest, query->server, query->server_tag,
+                              query->name, MSGLEVEL_CLIENTNOTICE, NULL);
+       printformat_dest(&dest, TXT_QUERY_SERVER_CHANGED,
+                        query->name, server->tag);
+
+       query_change_server(query, server);
+       signal_stop();
+}
+
+/* SYNTAX: UNQUERY [<nick>] */
+static void cmd_unquery(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       QUERY_REC *query;
+
+       g_return_if_fail(data != NULL);
+
+       if (*data == '\0') {
+               /* remove current query */
+               query = QUERY(item);
+               if (query == NULL) return;
+       } else {
+               query = query_find(server, data);
+               if (query == NULL) {
+                       printformat(server, NULL, MSGLEVEL_CLIENTERROR,
+                                   TXT_NO_QUERY, data);
+                       return;
+               }
+       }
+
+       query_destroy(query);
+}
+
+/* SYNTAX: QUERY [-window] [-<server tag>] <nick> [<message>] */
+static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       GHashTable *optlist;
+       QUERY_REC *query;
+       char *nick, *msg;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
+                           PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS,
+                           "query", &optlist, &nick, &msg))
+               return;
+
+       if (*nick == '\0') {
+               /* remove current query */
+               cmd_unquery("", server, item);
+               cmd_params_free(free_arg);
+                return;
+       }
+
+       server = cmd_options_get_server("query", optlist, server);
+       if (server == NULL) {
+               cmd_params_free(free_arg);
+                return;
+       }
+
+       if (*nick != '=' && (server == NULL || !server->connected))
+               cmd_param_error(CMDERR_NOT_CONNECTED);
+
+       if (g_hash_table_lookup(optlist, "window") != NULL) {
+               signal_add("query created",
+                          (SIGNAL_FUNC) signal_query_created_curwin);
+       }
+
+       query = query_find(server, nick);
+       if (query == NULL)
+               query = CHAT_PROTOCOL(server)->
+                       query_create(server->tag, nick, FALSE);
+       else {
+               /* query already exists */
+               WINDOW_REC *window = window_item_window(query);
+
+               if (window == active_win) {
+                        /* query is in active window, set it active */
+                       window_item_set_active(active_win,
+                                              (WI_ITEM_REC *) query);
+               } else {
+                       /* notify user how to move the query to active
+                          window. this was used to be done automatically
+                          but it just confused everyone who did it
+                          accidentally */
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_QUERY_MOVE_NOTIFY, query->name,
+                                          window->refnum);
+               }
+       }
+
+       if (g_hash_table_lookup(optlist, "window") != NULL) {
+               signal_remove("query created",
+                             (SIGNAL_FUNC) signal_query_created_curwin);
+       }
+
+       if (*msg != '\0') {
+                msg = g_strdup_printf("-nick %s %s", nick, msg);
+               signal_emit("command msg", 3, msg, server, query);
+                g_free(msg);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static int window_has_query(WINDOW_REC *window)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(window != NULL, FALSE);
+
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+               if (IS_QUERY(tmp->data))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void sig_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
+{
+       if (query_auto_close <= 0)
+               return;
+
+       /* reset the window's last_line timestamp so that query doesn't get
+          closed immediately after switched to the window, or after changed
+          to some other window from it */
+       if (window != NULL && window_has_query(window))
+               window->last_line = time(NULL);
+       if (old_window != NULL && window_has_query(old_window))
+               old_window->last_line = time(NULL);
+}
+
+static int sig_query_autoclose(void)
+{
+       WINDOW_REC *window;
+       GSList *tmp, *next;
+       time_t now;
+
+       now = time(NULL);
+       for (tmp = queries; tmp != NULL; tmp = next) {
+               QUERY_REC *rec = tmp->data;
+
+               next = tmp->next;
+               window = window_item_window((WI_ITEM_REC *) rec);
+               if (window != active_win && rec->data_level == 0 &&
+                   now-window->last_line > query_auto_close)
+                       query_destroy(rec);
+       }
+        return 1;
+}
+
+static void sig_message_private(SERVER_REC *server, const char *msg,
+                               const char *nick, const char *address)
+{
+       /* create query window if needed */
+       privmsg_get_query(server, nick, FALSE, MSGLEVEL_MSGS);
+}
+
+static void read_settings(void)
+{
+       querycreate_level = level2bits(settings_get_str("autocreate_query_level"));
+       query_auto_close = settings_get_int("autoclose_query");
+       if (query_auto_close > 0 && queryclose_tag == -1)
+               queryclose_tag = g_timeout_add(5000, (GSourceFunc) sig_query_autoclose, NULL);
+       else if (query_auto_close <= 0 && queryclose_tag != -1) {
+               g_source_remove(queryclose_tag);
+               queryclose_tag = -1;
+       }
+}
+
+void fe_queries_init(void)
+{
+       settings_add_str("lookandfeel", "autocreate_query_level", "MSGS DCCMSGS");
+       settings_add_bool("lookandfeel", "autocreate_own_query", TRUE);
+       settings_add_int("lookandfeel", "autoclose_query", 0);
+
+       queryclose_tag = -1;
+       read_settings();
+
+       signal_add("query created", (SIGNAL_FUNC) signal_query_created);
+       signal_add("query destroyed", (SIGNAL_FUNC) signal_query_destroyed);
+       signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
+       signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
+        signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
+       signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
+       signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
+       signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_bind("query", NULL, (SIGNAL_FUNC) cmd_query);
+       command_bind("unquery", NULL, (SIGNAL_FUNC) cmd_unquery);
+       command_bind("window server", NULL, (SIGNAL_FUNC) cmd_window_server);
+
+       command_set_options("query", "window");
+}
+
+void fe_queries_deinit(void)
+{
+       if (queryclose_tag != -1) g_source_remove(queryclose_tag);
+
+       signal_remove("query created", (SIGNAL_FUNC) signal_query_created);
+       signal_remove("query destroyed", (SIGNAL_FUNC) signal_query_destroyed);
+       signal_remove("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
+       signal_remove("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
+        signal_remove("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
+       signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
+       signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
+       signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_unbind("query", (SIGNAL_FUNC) cmd_query);
+       command_unbind("unquery", (SIGNAL_FUNC) cmd_unquery);
+       command_unbind("window server", (SIGNAL_FUNC) cmd_window_server);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-queries.h b/apps/irssi/src/fe-common/core/fe-queries.h
new file mode 100644 (file)
index 0000000..6db9cb4
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FE_QUERIES_H
+#define __FE_QUERIES_H
+
+#include "queries.h"
+
+/* Return query where to put the private message. */
+QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick,
+                            int own, int level);
+
+void fe_queries_init(void);
+void fe_queries_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/fe-server.c b/apps/irssi/src/fe-common/core/fe-server.c
new file mode 100644 (file)
index 0000000..d8f9d13
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ fe-server.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "network.h"
+#include "levels.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "servers-setup.h"
+#include "servers-reconnect.h"
+
+#include "module-formats.h"
+#include "printtext.h"
+
+static void print_servers(void)
+{
+       GSList *tmp;
+
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *rec = tmp->data;
+
+               printformat(NULL, NULL, MSGLEVEL_CRAP, TXT_SERVER_LIST,
+                           rec->tag, rec->connrec->address, rec->connrec->port,
+                           rec->connrec->chatnet == NULL ? "" : rec->connrec->chatnet, rec->connrec->nick);
+       }
+}
+
+static void print_lookup_servers(void)
+{
+       GSList *tmp;
+       for (tmp = lookup_servers; tmp != NULL; tmp = tmp->next) {
+               SERVER_REC *rec = tmp->data;
+
+               printformat(NULL, NULL, MSGLEVEL_CRAP, TXT_SERVER_LOOKUP_LIST,
+                           rec->tag, rec->connrec->address, rec->connrec->port,
+                           rec->connrec->chatnet == NULL ? "" : rec->connrec->chatnet, rec->connrec->nick);
+       }
+}
+
+static void print_reconnects(void)
+{
+       GSList *tmp;
+       char *tag, *next_connect;
+       int left;
+
+       for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
+               RECONNECT_REC *rec = tmp->data;
+               SERVER_CONNECT_REC *conn = rec->conn;
+
+               tag = g_strdup_printf("RECON-%d", rec->tag);
+               left = rec->next_connect-time(NULL);
+               next_connect = g_strdup_printf("%02d:%02d", left/60, left%60);
+               printformat(NULL, NULL, MSGLEVEL_CRAP, TXT_SERVER_RECONNECT_LIST,
+                           tag, conn->address, conn->port,
+                           conn->chatnet == NULL ? "" : conn->chatnet,
+                           conn->nick, next_connect);
+               g_free(next_connect);
+               g_free(tag);
+       }
+}
+
+static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist)
+{
+       CHAT_PROTOCOL_REC *rec;
+        SERVER_SETUP_REC *server;
+        char *chatnet;
+
+       rec = chat_protocol_find_net(optlist);
+       if (rec == NULL)
+                rec = chat_protocol_get_default();
+       else {
+               chatnet = g_hash_table_lookup(optlist, rec->chatnet);
+               if (chatnet_find(chatnet) == NULL) {
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                                   TXT_UNKNOWN_CHATNET, chatnet);
+                       return NULL;
+               }
+       }
+
+        server = rec->create_server_setup();
+        server->chat_type = rec->id;
+       return server;
+}
+
+static void cmd_server_add(const char *data)
+{
+        GHashTable *optlist;
+       SERVER_SETUP_REC *rec;
+       char *addr, *portstr, *password, *value;
+       void *free_arg;
+       int port;
+
+       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
+                           "server add", &optlist, &addr, &portstr, &password))
+               return;
+
+       if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+       port = *portstr == '\0' ? 6667 : atoi(portstr);
+
+       rec = server_setup_find_port(addr, port);
+       if (rec == NULL) {
+               rec = create_server_setup(optlist);
+               if (rec == NULL) {
+                       cmd_params_free(free_arg);
+                       return;
+               }
+               rec->address = g_strdup(addr);
+               rec->port = port;
+       } else {
+               value = g_hash_table_lookup(optlist, "port");
+               if (value != NULL && *value != '\0') rec->port = atoi(value);
+
+               if (*password != '\0') g_free_and_null(rec->password);
+               if (g_hash_table_lookup(optlist, "host")) {
+                       g_free_and_null(rec->own_host);
+                       rec->own_ip4 = rec->own_ip6 = NULL;
+               }
+       }
+
+       if (g_hash_table_lookup(optlist, "6"))
+               rec->family = AF_INET6;
+        else if (g_hash_table_lookup(optlist, "4"))
+               rec->family = AF_INET;
+
+       if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
+       if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
+       if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE;
+       if (g_hash_table_lookup(optlist, "noproxy")) rec->no_proxy = TRUE;
+
+       if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
+       value = g_hash_table_lookup(optlist, "host");
+       if (value != NULL && *value != '\0') {
+               rec->own_host = g_strdup(value);
+               rec->own_ip4 = rec->own_ip6 = NULL;
+       }
+
+       signal_emit("server add fill", 2, rec, optlist);
+
+       server_setup_add(rec);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_SETUPSERVER_ADDED, addr, port);
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: SERVER REMOVE <address> [<port>] */
+static void cmd_server_remove(const char *data)
+{
+       SERVER_SETUP_REC *rec;
+       char *addr, *port;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 2, &addr, &port))
+               return;
+       if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+        if (*port == '\0')
+               rec = server_setup_find(addr, -1, NULL);
+       else
+               rec = server_setup_find_port(addr, atoi(port));
+
+       if (rec == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_SETUPSERVER_NOT_FOUND, addr, port);
+       else {
+               server_setup_remove(rec);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_SETUPSERVER_REMOVED, addr, port);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static void cmd_server(const char *data)
+{
+       if (*data != '\0')
+               return;
+
+       if (servers == NULL && lookup_servers == NULL &&
+           reconnects == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_NO_CONNECTED_SERVERS);
+       } else {
+               print_servers();
+               print_lookup_servers();
+               print_reconnects();
+       }
+
+        signal_stop();
+}
+
+static void cmd_server_connect(const char *data)
+{
+       GHashTable *optlist;
+       char *addr;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "connect", &optlist, &addr))
+               return;
+
+       if (*addr == '\0' || strcmp(addr, "+") == 0)
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+       if (*addr == '+') window_create(NULL, FALSE);
+
+       cmd_params_free(free_arg);
+}
+
+static void sig_server_looking(SERVER_REC *server)
+{
+       g_return_if_fail(server != NULL);
+
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_LOOKING_UP, server->connrec->address);
+}
+
+static void sig_server_connecting(SERVER_REC *server, IPADDR *ip)
+{
+       char ipaddr[MAX_IP_LEN];
+
+       g_return_if_fail(server != NULL);
+       g_return_if_fail(ip != NULL);
+
+       net_ip2host(ip, ipaddr);
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONNECTING,
+                   server->connrec->address, ipaddr, server->connrec->port);
+}
+
+static void sig_server_connected(SERVER_REC *server)
+{
+       g_return_if_fail(server != NULL);
+
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_CONNECTION_ESTABLISHED, server->connrec->address);
+}
+
+static void sig_connect_failed(SERVER_REC *server, gchar *msg)
+{
+       g_return_if_fail(server != NULL);
+
+       if (msg == NULL) {
+               /* no message so this wasn't unexpected fail - send
+                  connection_lost message instead */
+               printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONNECTION_LOST, server->connrec->address);
+       } else {
+               printformat(server, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_CANT_CONNECT, server->connrec->address, server->connrec->port, msg);
+       }
+}
+
+static void sig_server_disconnected(SERVER_REC *server)
+{
+       g_return_if_fail(server != NULL);
+
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_CONNECTION_LOST, server->connrec->address);
+}
+
+static void sig_server_quit(SERVER_REC *server, const char *msg)
+{
+       g_return_if_fail(server != NULL);
+
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_SERVER_QUIT, server->connrec->address, msg);
+}
+
+static void sig_server_lag_disconnected(SERVER_REC *server)
+{
+       g_return_if_fail(server != NULL);
+
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_LAG_DISCONNECTED, server->connrec->address,
+                   time(NULL)-server->lag_sent.tv_sec);
+}
+
+static void sig_server_reconnect_removed(RECONNECT_REC *reconnect)
+{
+       g_return_if_fail(reconnect != NULL);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_RECONNECT_REMOVED, reconnect->conn->address, reconnect->conn->port,
+                   reconnect->conn->chatnet == NULL ? "" : reconnect->conn->chatnet);
+}
+
+static void sig_server_reconnect_not_found(const char *tag)
+{
+       g_return_if_fail(tag != NULL);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_RECONNECT_NOT_FOUND, tag);
+}
+
+static void sig_chat_protocol_unknown(const char *protocol)
+{
+       g_return_if_fail(protocol != NULL);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                    TXT_UNKNOWN_CHAT_PROTOCOL, protocol);
+}
+
+void fe_server_init(void)
+{
+       command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
+       command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
+       command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add);
+       command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
+       command_set_options("server add", "4 6 auto noauto proxy noproxy -host -port");
+
+       signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
+       signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
+       signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
+       signal_add("server connect failed", (SIGNAL_FUNC) sig_connect_failed);
+       signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
+
+       signal_add("server lag disconnect", (SIGNAL_FUNC) sig_server_lag_disconnected);
+       signal_add("server reconnect remove", (SIGNAL_FUNC) sig_server_reconnect_removed);
+       signal_add("server reconnect not found", (SIGNAL_FUNC) sig_server_reconnect_not_found);
+
+       signal_add("chat protocol unknown", (SIGNAL_FUNC) sig_chat_protocol_unknown);
+}
+
+void fe_server_deinit(void)
+{
+       command_unbind("server", (SIGNAL_FUNC) cmd_server);
+       command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
+       command_unbind("server add", (SIGNAL_FUNC) cmd_server_add);
+       command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove);
+
+       signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking);
+       signal_remove("server connecting", (SIGNAL_FUNC) sig_server_connecting);
+       signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
+       signal_remove("server connect failed", (SIGNAL_FUNC) sig_connect_failed);
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
+
+       signal_remove("server lag disconnect", (SIGNAL_FUNC) sig_server_lag_disconnected);
+       signal_remove("server reconnect remove", (SIGNAL_FUNC) sig_server_reconnect_removed);
+       signal_remove("server reconnect not found", (SIGNAL_FUNC) sig_server_reconnect_not_found);
+
+       signal_remove("chat protocol unknown", (SIGNAL_FUNC) sig_chat_protocol_unknown);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-settings.c b/apps/irssi/src/fe-common/core/fe-settings.c
new file mode 100644 (file)
index 0000000..382a09f
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ fe-settings.c : irssi
+
+    Copyright (C) 1999 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "servers.h"
+#include "misc.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "levels.h"
+#include "printtext.h"
+#include "keyboard.h"
+
+static void set_print(SETTINGS_REC *rec)
+{
+       const char *value;
+       char value_int[MAX_INT_STRLEN];
+
+       switch (rec->type) {
+       case SETTING_TYPE_BOOLEAN:
+               value = settings_get_bool(rec->key) ? "ON" : "OFF";
+               break;
+       case SETTING_TYPE_INT:
+               ltoa(value_int, settings_get_int(rec->key));
+               value = value_int;
+               break;
+       case SETTING_TYPE_STRING:
+               value = settings_get_str(rec->key);
+               break;
+       default:
+               value = "";
+       }
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_SET_ITEM,
+                   rec->key, value);
+}
+
+static void set_boolean(const char *key, const char *value)
+{
+       if (g_strcasecmp(value, "ON") == 0)
+               settings_set_bool(key, TRUE);
+       else if (g_strcasecmp(value, "OFF") == 0)
+               settings_set_bool(key, FALSE);
+       else if (g_strcasecmp(value, "TOGGLE") == 0)
+               settings_set_bool(key, !settings_get_bool(key));
+       else
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_NOT_TOGGLE);
+}
+
+/* SYNTAX: SET [-clear] [<key> [<value>]] */
+static void cmd_set(char *data)
+{
+        GHashTable *optlist;
+       GSList *sets, *tmp;
+       const char *last_section;
+       char *key, *value;
+       void *free_arg;
+       int found, clear;
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
+                           "set", &optlist, &key, &value))
+               return;
+
+       clear = g_hash_table_lookup(optlist, "clear") != NULL;
+
+       last_section = ""; found = 0;
+       sets = settings_get_sorted();
+       for (tmp = sets; tmp != NULL; tmp = tmp->next) {
+               SETTINGS_REC *rec = tmp->data;
+
+               if (((clear || *value != '\0') && g_strcasecmp(rec->key, key) != 0) ||
+                   (*value == '\0' && *key != '\0' && stristr(rec->key, key) == NULL))
+                       continue;
+
+               if (strcmp(last_section, rec->section) != 0) {
+                       /* print section */
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                                   TXT_SET_TITLE, rec->section);
+                       last_section = rec->section;
+               }
+
+               if (clear || *value != '\0') {
+                       /* change the setting */
+                       switch (rec->type) {
+                       case SETTING_TYPE_BOOLEAN:
+                                if (clear)
+                                       settings_set_bool(key, FALSE);
+                                else
+                                       set_boolean(key, value);
+                               break;
+                       case SETTING_TYPE_INT:
+                               settings_set_int(key, clear ? 0 : atoi(value));
+                               break;
+                       case SETTING_TYPE_STRING:
+                               settings_set_str(key, clear ? "" : value);
+                               break;
+                       }
+                       signal_emit("setup changed", 0);
+               }
+
+                set_print(rec);
+               found = TRUE;
+
+               if (clear || *value != '\0')
+                       break;
+       }
+       g_slist_free(sets);
+
+        if (!found) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_SET_UNKNOWN, key);
+        }
+
+        cmd_params_free(free_arg);
+}
+
+/* SYNTAX: TOGGLE <key> [on|off|toggle] */
+static void cmd_toggle(const char *data)
+{
+       char *key, *value;
+       void *free_arg;
+       int type;
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &key, &value))
+               return;
+
+        if (*key == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       type = settings_get_type(key);
+        if (type == -1)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_UNKNOWN, key);
+       else if (type != SETTING_TYPE_BOOLEAN)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_SET_NOT_BOOLEAN, key);
+       else {
+               set_boolean(key, *value != '\0' ? value : "TOGGLE");
+                set_print(settings_get_record(key));
+               signal_emit("setup changed", 0);
+       }
+
+        cmd_params_free(free_arg);
+}
+
+static int config_key_compare(CONFIG_NODE *node1, CONFIG_NODE *node2)
+{
+       return g_strcasecmp(node1->key, node2->key);
+}
+
+static void show_aliases(const char *alias)
+{
+       CONFIG_NODE *node;
+       GSList *tmp, *list;
+       int aliaslen;
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_HEADER);
+
+       node = iconfig_node_traverse("aliases", FALSE);
+       tmp = node == NULL ? NULL : config_node_first(node->value);
+
+       /* first get the list of aliases sorted */
+       list = NULL;
+       aliaslen = strlen(alias);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               CONFIG_NODE *node = tmp->data;
+
+               if (node->type != NODE_TYPE_KEY)
+                       continue;
+
+               if (aliaslen != 0 && g_strncasecmp(node->key, alias, aliaslen) != 0)
+                       continue;
+
+               list = g_slist_insert_sorted(list, node, (GCompareFunc) config_key_compare);
+       }
+
+       /* print the aliases */
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               CONFIG_NODE *node = tmp->data;
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_LINE,
+                           node->key, node->value);
+       }
+       g_slist_free(list);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_ALIASLIST_FOOTER);
+}
+
+static void alias_remove(const char *alias)
+{
+       if (iconfig_get_str("aliases", alias, NULL) == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_ALIAS_NOT_FOUND, alias);
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_ALIAS_REMOVED, alias);
+               iconfig_set_str("aliases", alias, NULL);
+       }
+}
+
+/* SYNTAX: ALIAS [[-]<alias> [<command>]] */
+static void cmd_alias(const char *data)
+{
+       char *alias, *value;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST, &alias, &value))
+               return;
+
+       if (*alias == '-') {
+               if (alias[1] != '\0') alias_remove(alias+1);
+       } else if (*alias == '\0' || *value == '\0')
+               show_aliases(alias);
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_ALIAS_ADDED, alias);
+               iconfig_set_str("aliases", alias, value);
+       }
+        cmd_params_free(free_arg);
+}
+
+/* SYNTAX: UNALIAS <alias> */
+static void cmd_unalias(const char *data)
+{
+       g_return_if_fail(data != NULL);
+       if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       alias_remove(data);
+}
+
+/* SYNTAX: RELOAD [<file>] */
+static void cmd_reload(const char *data)
+{
+       const char *fname;
+
+       fname = *data == '\0' ? get_irssi_config() : data;
+
+       if (settings_reread(fname)) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONFIG_RELOADED, fname);
+       }
+}
+
+static void settings_save_fe(const char *fname)
+{
+       if (settings_save(fname, FALSE /* not autosaved */)) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONFIG_SAVED, fname);
+       }
+}
+
+static void settings_save_confirm(const char *line, char *fname)
+{
+       if (i_toupper(line[0]) == 'Y')
+               settings_save_fe(fname);
+       g_free(fname);
+}
+
+/* SYNTAX: SAVE [<file>] */
+static void cmd_save(const char *data)
+{
+        GHashTable *optlist;
+       char *format, *fname;
+        void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "save", &optlist, &fname))
+               return;
+
+       if (*fname == '\0')
+               fname = mainconfig->fname;
+
+       if (!irssi_config_is_changed(fname))
+               settings_save_fe(fname);
+       else {
+                /* config file modified outside irssi */
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                           TXT_CONFIG_MODIFIED, fname);
+
+               format = format_get_text(MODULE_NAME, NULL, NULL, NULL,
+                                        TXT_OVERWRITE_CONFIG);
+               keyboard_entry_redirect((SIGNAL_FUNC) settings_save_confirm,
+                                       format, 0, g_strdup(fname));
+               g_free(format);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static void settings_clean_confirm(const char *line)
+{
+       if (i_toupper(line[0]) == 'Y')
+                settings_clean_invalid();
+}
+
+static void sig_settings_errors(const char *msg)
+{
+        printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", msg);
+       keyboard_entry_redirect((SIGNAL_FUNC) settings_clean_confirm,
+                               "Remove unknown settings from config file (Y/n)?",
+                               0, NULL);
+}
+
+void fe_settings_init(void)
+{
+       command_bind("set", NULL, (SIGNAL_FUNC) cmd_set);
+       command_bind("toggle", NULL, (SIGNAL_FUNC) cmd_toggle);
+       command_bind("alias", NULL, (SIGNAL_FUNC) cmd_alias);
+       command_bind("unalias", NULL, (SIGNAL_FUNC) cmd_unalias);
+       command_bind("reload", NULL, (SIGNAL_FUNC) cmd_reload);
+       command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
+       command_set_options("set", "clear");
+
+        signal_add("settings errors", (SIGNAL_FUNC) sig_settings_errors);
+}
+
+void fe_settings_deinit(void)
+{
+       command_unbind("set", (SIGNAL_FUNC) cmd_set);
+       command_unbind("toggle", (SIGNAL_FUNC) cmd_toggle);
+       command_unbind("alias", (SIGNAL_FUNC) cmd_alias);
+       command_unbind("unalias", (SIGNAL_FUNC) cmd_unalias);
+       command_unbind("reload", (SIGNAL_FUNC) cmd_reload);
+       command_unbind("save", (SIGNAL_FUNC) cmd_save);
+
+       signal_remove("settings errors", (SIGNAL_FUNC) sig_settings_errors);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-windows.c b/apps/irssi/src/fe-common/core/fe-windows.c
new file mode 100644 (file)
index 0000000..d3f068e
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ windows.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "servers.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "levels.h"
+
+#include "printtext.h"
+#include "fe-windows.h"
+#include "window-items.h"
+
+GSList *windows; /* first in the list is the active window,
+                    next is the last active, etc. */
+WINDOW_REC *active_win;
+
+static int daytag;
+static int daycheck; /* 0 = don't check, 1 = time is 00:00, check,
+                        2 = time is 00:00, already checked */
+
+static int window_get_new_refnum(void)
+{
+       WINDOW_REC *win;
+       GSList *tmp;
+       int refnum;
+
+       refnum = 1;
+       tmp = windows;
+       while (tmp != NULL) {
+               win = tmp->data;
+
+               if (refnum != win->refnum) {
+                       tmp = tmp->next;
+                       continue;
+               }
+
+               refnum++;
+               tmp = windows;
+       }
+
+       return refnum;
+}
+
+WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
+{
+       WINDOW_REC *rec;
+
+       rec = g_new0(WINDOW_REC, 1);
+       rec->refnum = window_get_new_refnum();
+
+       windows = g_slist_prepend(windows, rec);
+       signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic));
+
+       if (item != NULL) window_item_add(rec, item, automatic);
+       if (windows->next == NULL || !automatic || settings_get_bool("window_auto_change")) {
+               if (automatic && windows->next != NULL)
+                       signal_emit("window changed automatic", 1, rec);
+               window_set_active(rec);
+       }
+       return rec;
+}
+
+/* removed_refnum was removed from the windows list, pack the windows so
+   there won't be any holes. If there is any holes after removed_refnum,
+   leave the windows behind it alone. */
+static void windows_pack(int removed_refnum)
+{
+       WINDOW_REC *window;
+       int refnum;
+
+       for (refnum = removed_refnum+1;; refnum++) {
+               window = window_find_refnum(refnum);
+               if (window == NULL || window->sticky_refnum)
+                       break;
+
+               window_set_refnum(window, refnum-1);
+       }
+}
+
+void window_destroy(WINDOW_REC *window)
+{
+       g_return_if_fail(window != NULL);
+
+       if (window->destroying) return;
+       window->destroying = TRUE;
+       windows = g_slist_remove(windows, window);
+
+       if (active_win == window && windows != NULL) {
+                active_win = NULL; /* it's corrupted */
+               window_set_active(windows->data);
+       }
+
+       while (window->items != NULL)
+               window_item_destroy(window->items->data);
+
+        if (settings_get_bool("windows_auto_renumber"))
+               windows_pack(window->refnum);
+
+       signal_emit("window destroyed", 1, window);
+
+       while (window->bound_items != NULL)
+                window_bind_destroy(window, window->bound_items->data);
+
+       g_free_not_null(window->hilight_color);
+       g_free_not_null(window->servertag);
+       g_free_not_null(window->theme_name);
+       g_free_not_null(window->name);
+       g_free(window);
+}
+
+void window_auto_destroy(WINDOW_REC *window)
+{
+       if (settings_get_bool("autoclose_windows") && windows->next != NULL &&
+           window->items == NULL && window->bound_items == NULL &&
+           window->level == 0 && !window->immortal)
+                window_destroy(window);
+}
+
+void window_set_active(WINDOW_REC *window)
+{
+       WINDOW_REC *old_window;
+
+       if (window == active_win)
+               return;
+
+       old_window = active_win;
+       active_win = window;
+       if (active_win != NULL) {
+               windows = g_slist_remove(windows, active_win);
+               windows = g_slist_prepend(windows, active_win);
+       }
+
+        if (active_win != NULL)
+               signal_emit("window changed", 2, active_win, old_window);
+}
+
+void window_change_server(WINDOW_REC *window, void *server)
+{
+       window->active_server = server;
+       signal_emit("window server changed", 2, window, server);
+}
+
+void window_set_refnum(WINDOW_REC *window, int refnum)
+{
+       GSList *tmp;
+       int old_refnum;
+
+       g_return_if_fail(window != NULL);
+       g_return_if_fail(refnum >= 1);
+       if (window->refnum == refnum) return;
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->refnum == refnum) {
+                       rec->refnum = window->refnum;
+                        signal_emit("window refnum changed", 2, rec, GINT_TO_POINTER(refnum));
+                       break;
+               }
+       }
+
+       old_refnum = window->refnum;
+       window->refnum = refnum;
+       signal_emit("window refnum changed", 2, window, GINT_TO_POINTER(old_refnum));
+}
+
+void window_set_name(WINDOW_REC *window, const char *name)
+{
+       g_free_not_null(window->name);
+       window->name = g_strdup(name);
+
+       signal_emit("window name changed", 1, window);
+}
+
+void window_set_history(WINDOW_REC *window, const char *name)
+{
+       char *oldname;
+       oldname = window->history_name;
+
+       if (name == NULL || *name == '\0')
+               window->history_name = NULL;
+       else
+               window->history_name = g_strdup(name);
+
+       signal_emit("window history changed", 1, window, oldname);
+
+       g_free_not_null(oldname);
+}
+
+void window_set_level(WINDOW_REC *window, int level)
+{
+       g_return_if_fail(window != NULL);
+
+       window->level = level;
+        signal_emit("window level changed", 1, window);
+}
+
+void window_set_immortal(WINDOW_REC *window, int immortal)
+{
+       g_return_if_fail(window != NULL);
+
+       window->immortal = immortal;
+        signal_emit("window immortal changed", 1, window);
+}
+
+/* return active item's name, or if none is active, window's name */
+char *window_get_active_name(WINDOW_REC *window)
+{
+       g_return_val_if_fail(window != NULL, NULL);
+
+       if (window->active != NULL)
+               return window->active->name;
+
+       return window->name;
+}
+
+WINDOW_REC *window_find_level(void *server, int level)
+{
+       WINDOW_REC *match;
+       GSList *tmp;
+
+       match = NULL;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if ((server == NULL || rec->active_server == server) &&
+                   (rec->level & level)) {
+                       if (server == NULL || rec->active_server == server)
+                               return rec;
+                       match = rec;
+               }
+       }
+
+       return match;
+}
+
+WINDOW_REC *window_find_closest(void *server, const char *name, int level)
+{
+       WINDOW_REC *window;
+       WI_ITEM_REC *item;
+
+       /* match by name */
+       item = name == NULL ? NULL :
+               window_item_find(server, name);
+       if (item != NULL)
+                return window_item_window(item);
+
+       /* match by level */
+       if (level != MSGLEVEL_HILIGHT)
+               level &= ~(MSGLEVEL_HILIGHT | MSGLEVEL_NOHILIGHT);
+       window = window_find_level(server, level);
+       if (window != NULL) return window;
+
+       /* match by level - ignore server */
+       window = window_find_level(NULL, level);
+       if (window != NULL) return window;
+
+       /* fallback to active */
+       return active_win;
+}
+
+WINDOW_REC *window_find_refnum(int refnum)
+{
+       GSList *tmp;
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->refnum == refnum)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+WINDOW_REC *window_find_name(const char *name)
+{
+       GSList *tmp;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->name != NULL && g_strcasecmp(rec->name, name) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+WINDOW_REC *window_find_item(SERVER_REC *server, const char *name)
+{
+       WINDOW_REC *rec;
+       WI_ITEM_REC *item;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       rec = window_find_name(name);
+       if (rec != NULL) return rec;
+
+       item = server == NULL ? NULL :
+               window_item_find(server, name);
+       if (item == NULL && server == NULL) {
+               /* not found from the active server - any server? */
+               item = window_item_find(NULL, name);
+       }
+
+       if (item == NULL) {
+               char *chan;
+
+               /* still nothing? maybe user just left the # in front of
+                  channel, try again with it.. */
+               chan = g_strdup_printf("#%s", name);
+               item = server == NULL ? NULL :
+                       window_item_find(server, chan);
+               if (item == NULL) item = window_item_find(NULL, chan);
+               g_free(chan);
+       }
+
+       if (item == NULL)
+               return 0;
+
+       return window_item_window(item);
+}
+
+int window_refnum_prev(int refnum, int wrap)
+{
+       GSList *tmp;
+       int prev, max;
+
+       max = prev = -1;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->refnum < refnum && (prev == -1 || rec->refnum > prev))
+                       prev = rec->refnum;
+               if (wrap && (max == -1 || rec->refnum > max))
+                       max = rec->refnum;
+       }
+
+       return prev != -1 ? prev : max;
+}
+
+int window_refnum_next(int refnum, int wrap)
+{
+       GSList *tmp;
+       int min, next;
+
+       min = next = -1;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->refnum > refnum && (next == -1 || rec->refnum < next))
+                       next = rec->refnum;
+               if (wrap && (min == -1 || rec->refnum < min))
+                       min = rec->refnum;
+       }
+
+       return next != -1 ? next : min;
+}
+
+int windows_refnum_last(void)
+{
+       GSList *tmp;
+       int max;
+
+       max = -1;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->refnum > max)
+                       max = rec->refnum;
+       }
+
+       return max;
+}
+
+int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2)
+{
+       return w1->refnum < w2->refnum ? -1 : 1;
+}
+
+GSList *windows_get_sorted(void)
+{
+       GSList *tmp, *sorted;
+
+        sorted = NULL;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               sorted = g_slist_insert_sorted(sorted, rec, (GCompareFunc)
+                                              window_refnum_cmp);
+       }
+
+        return sorted;
+}
+
+/* Add a new bind to window - if duplicate is found it's returned */
+WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
+                                const char *name)
+{
+       WINDOW_BIND_REC *rec;
+
+        g_return_val_if_fail(window != NULL, NULL);
+        g_return_val_if_fail(servertag != NULL, NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+
+       rec = window_bind_find(window, servertag, name);
+       if (rec != NULL)
+               return rec;
+
+       rec = g_new0(WINDOW_BIND_REC, 1);
+        rec->name = g_strdup(name);
+        rec->servertag = g_strdup(servertag);
+
+       window->bound_items = g_slist_append(window->bound_items, rec);
+        return rec;
+}
+
+void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec)
+{
+       g_return_if_fail(window != NULL);
+        g_return_if_fail(rec != NULL);
+
+       window->bound_items = g_slist_remove(window->bound_items, rec);
+
+        g_free(rec->servertag);
+        g_free(rec->name);
+        g_free(rec);
+}
+
+WINDOW_BIND_REC *window_bind_find(WINDOW_REC *window, const char *servertag,
+                                 const char *name)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(window != NULL, NULL);
+        g_return_val_if_fail(servertag != NULL, NULL);
+        g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) {
+               WINDOW_BIND_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, name) == 0 &&
+                   g_strcasecmp(rec->servertag, servertag) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+void window_bind_remove_unsticky(WINDOW_REC *window)
+{
+       GSList *tmp, *next;
+
+       for (tmp = window->bound_items; tmp != NULL; tmp = next) {
+               WINDOW_BIND_REC *rec = tmp->data;
+
+               next = tmp->next;
+               if (!rec->sticky)
+                        window_bind_destroy(window, rec);
+       }
+}
+
+static void sig_server_looking(SERVER_REC *server)
+{
+       GSList *tmp;
+
+       g_return_if_fail(server != NULL);
+
+       /* Try to keep some server assigned to windows..
+          Also change active window's server if the window is empty */
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if ((rec->servertag == NULL ||
+                    g_strcasecmp(rec->servertag, server->tag) == 0) &&
+                   (rec->active_server == NULL ||
+                    (rec == active_win && rec->items == NULL)))
+                       window_change_server(rec, server);
+       }
+}
+
+static void sig_server_disconnected(SERVER_REC *server)
+{
+       GSList *tmp;
+        SERVER_REC *new_server;
+
+       g_return_if_fail(server != NULL);
+
+       new_server = servers == NULL ? NULL : servers->data;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->active_server == server) {
+                       window_change_server(rec, rec->servertag != NULL ?
+                                            NULL : new_server);
+               }
+       }
+}
+
+static void window_print_daychange(WINDOW_REC *window, struct tm *tm)
+{
+        THEME_REC *theme;
+        TEXT_DEST_REC dest;
+       char *format, str[256];
+
+       theme = active_win->theme != NULL ? active_win->theme : current_theme;
+       format_create_dest(&dest, NULL, NULL, MSGLEVEL_NEVER, window);
+       format = format_get_text_theme(theme, MODULE_NAME, &dest,
+                                      TXT_DAYCHANGE);
+       if (strftime(str, sizeof(str), format, tm) <= 0)
+                str[0] = '\0';
+       g_free(format);
+
+       printtext_string_window(window, MSGLEVEL_NEVER, str);
+}
+
+static void sig_print_text(void)
+{
+       GSList *tmp;
+       time_t t;
+       struct tm *tm;
+
+       t = time(NULL);
+       tm = localtime(&t);
+       if (tm->tm_hour != 0 || tm->tm_min != 0)
+               return;
+
+       daycheck = 2;
+       signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
+
+       /* day changed, print notice about it to every window */
+       for (tmp = windows; tmp != NULL; tmp = tmp->next)
+               window_print_daychange(tmp->data, tm);
+}
+
+static int sig_check_daychange(void)
+{
+       time_t t;
+       struct tm *tm;
+
+       t = time(NULL);
+       tm = localtime(&t);
+
+       if (daycheck == 1 && tm->tm_hour == 0 && tm->tm_min == 0) {
+               sig_print_text();
+               return TRUE;
+       }
+
+       if (tm->tm_hour != 23 || tm->tm_min != 59) {
+               daycheck = 0;
+               return TRUE;
+       }
+
+       /* time is 23:59 */
+       if (daycheck == 0) {
+               daycheck = 1;
+               signal_add("print text", (SIGNAL_FUNC) sig_print_text);
+       }
+       return TRUE;
+}
+
+static void read_settings(void)
+{
+       if (daytag != -1) {
+               g_source_remove(daytag);
+               daytag = -1;
+       }
+
+       if (settings_get_bool("timestamps"))
+               daytag = g_timeout_add(30000, (GSourceFunc) sig_check_daychange, NULL);
+}
+
+void windows_init(void)
+{
+       active_win = NULL;
+       daycheck = 0; daytag = -1;
+       settings_add_bool("lookandfeel", "window_auto_change", FALSE);
+       settings_add_bool("lookandfeel", "windows_auto_renumber", TRUE);
+
+       read_settings();
+       signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
+       signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_add("server connect failed", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void windows_deinit(void)
+{
+       if (daytag != -1) g_source_remove(daytag);
+       if (daycheck == 1) signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
+
+       signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking);
+       signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/fe-windows.h b/apps/irssi/src/fe-common/core/fe-windows.h
new file mode 100644 (file)
index 0000000..9173459
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef __WINDOWS_H
+#define __WINDOWS_H
+
+#include "window-item-def.h"
+#include "command-history.h"
+
+enum {
+        DATA_LEVEL_NONE = 0,
+       DATA_LEVEL_TEXT,
+       DATA_LEVEL_MSG,
+        DATA_LEVEL_HILIGHT
+};
+
+typedef struct {
+       char *servertag;
+        char *name;
+       unsigned int sticky:1;
+} WINDOW_BIND_REC;
+
+struct _WINDOW_REC {
+       int refnum;
+       char *name;
+
+        int width, height;
+
+       GSList *items;
+       WI_ITEM_REC *active;
+       SERVER_REC *active_server;
+        char *servertag; /* active_server must be either NULL or have this tag (unless there's items in this window) */
+
+       int level; /* message level */
+       GSList *bound_items; /* list of WINDOW_BIND_RECs */
+
+       unsigned int immortal:1;
+       unsigned int sticky_refnum:1;
+       unsigned int destroying:1;
+
+       /* window-specific command line history */
+       HISTORY_REC *history;
+       char *history_name;
+
+       int data_level; /* current data level */
+       char *hilight_color; /* current hilight color in %format */
+
+       time_t last_timestamp; /* When was last timestamp printed */
+       time_t last_line; /* When was last line printed */
+
+        char *theme_name; /* active theme in window, NULL = default */
+       void *theme; /* THEME_REC */
+
+       void *gui_data;
+};
+
+extern GSList *windows;
+extern WINDOW_REC *active_win;
+
+WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic);
+void window_destroy(WINDOW_REC *window);
+
+void window_auto_destroy(WINDOW_REC *window);
+
+void window_set_active(WINDOW_REC *window);
+void window_change_server(WINDOW_REC *window, void *server);
+
+void window_set_refnum(WINDOW_REC *window, int refnum);
+void window_set_name(WINDOW_REC *window, const char *name);
+void window_set_history(WINDOW_REC *window, const char *name);
+void window_set_level(WINDOW_REC *window, int level);
+void window_set_immortal(WINDOW_REC *window, int immortal);
+
+/* return active item's name, or if none is active, window's name */
+char *window_get_active_name(WINDOW_REC *window);
+
+WINDOW_REC *window_find_level(void *server, int level);
+WINDOW_REC *window_find_closest(void *server, const char *name, int level);
+WINDOW_REC *window_find_refnum(int refnum);
+WINDOW_REC *window_find_name(const char *name);
+WINDOW_REC *window_find_item(SERVER_REC *server, const char *name);
+
+int window_refnum_prev(int refnum, int wrap);
+int window_refnum_next(int refnum, int wrap);
+int windows_refnum_last(void);
+
+int window_refnum_cmp(WINDOW_REC *w1, WINDOW_REC *w2);
+GSList *windows_get_sorted(void);
+
+/* Add a new bind to window - if duplicate is found it's returned */
+WINDOW_BIND_REC *window_bind_add(WINDOW_REC *window, const char *servertag,
+                                const char *name);
+void window_bind_destroy(WINDOW_REC *window, WINDOW_BIND_REC *rec);
+
+WINDOW_BIND_REC *window_bind_find(WINDOW_REC *window, const char *servertag,
+                                 const char *name);
+void window_bind_remove_unsticky(WINDOW_REC *window);
+
+void windows_init(void);
+void windows_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/formats.c b/apps/irssi/src/fe-common/core/formats.c
new file mode 100644 (file)
index 0000000..b4d2e7e
--- /dev/null
@@ -0,0 +1,1075 @@
+/*
+ formats.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "special-vars.h"
+#include "settings.h"
+
+#include "levels.h"
+#include "servers.h"
+
+#include "fe-windows.h"
+#include "window-items.h"
+#include "formats.h"
+#include "themes.h"
+#include "translation.h"
+
+static const char *format_backs = "04261537";
+static const char *format_fores = "kbgcrmyw";
+static const char *format_boldfores = "KBGCRMYW";
+
+static int signal_gui_print_text;
+static int hide_text_style, hide_server_tags, hide_mirc_colors;
+
+static int timestamp_level;
+static int timestamp_timeout;
+
+int format_find_tag(const char *module, const char *tag)
+{
+       FORMAT_REC *formats;
+       int n;
+
+       formats = g_hash_table_lookup(default_formats, module);
+       if (formats == NULL)
+               return -1;
+
+       for (n = 0; formats[n].def != NULL; n++) {
+               if (formats[n].tag != NULL &&
+                   g_strcasecmp(formats[n].tag, tag) == 0)
+                       return n;
+       }
+
+       return -1;
+}
+
+static void format_expand_code(const char **format, GString *out, int *flags)
+{
+       int set;
+
+       if (flags == NULL) {
+                /* flags are being ignored - skip the code */
+               while (**format != ']')
+                       (*format)++;
+                return;
+       }
+
+        set = TRUE;
+       (*format)++;
+       while (**format != ']' && **format != '\0') {
+               if (**format == '+')
+                       set = TRUE;
+               else if (**format == '-')
+                       set = FALSE;
+               else switch (**format) {
+               case 'i':
+                       /* indent function */
+                       (*format)++;
+                       if (**format == '=')
+                               (*format)++;
+
+                       g_string_append_c(out, 4);
+                       g_string_append_c(out, FORMAT_STYLE_INDENT_FUNC);
+                       while (**format != ']' && **format != '\0' &&
+                              **format != ',') {
+                               g_string_append_c(out, **format);
+                               (*format)++;
+                       }
+                       g_string_append_c(out, ',');
+                        (*format)--;
+                       break;
+               case 's':
+               case 'S':
+                       *flags |= !set ? PRINT_FLAG_UNSET_LINE_START :
+                               **format == 's' ? PRINT_FLAG_SET_LINE_START :
+                               PRINT_FLAG_SET_LINE_START_IRSSI;
+                       break;
+               case 't':
+                       *flags |= set ? PRINT_FLAG_SET_TIMESTAMP :
+                                PRINT_FLAG_UNSET_TIMESTAMP;
+                        break;
+               case 'T':
+                       *flags |= set ? PRINT_FLAG_SET_SERVERTAG :
+                                PRINT_FLAG_UNSET_SERVERTAG;
+                        break;
+               }
+
+               (*format)++;
+       }
+}
+
+int format_expand_styles(GString *out, const char **format, int *flags)
+{
+       char *p, fmt;
+
+        fmt = **format;
+       switch (fmt) {
+       case '{':
+       case '}':
+       case '%':
+                /* escaped char */
+               g_string_append_c(out, fmt);
+               break;
+       case 'U':
+               /* Underline on/off */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_UNDERLINE);
+               break;
+       case '9':
+       case '_':
+               /* bold on/off */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_BOLD);
+               break;
+       case '8':
+               /* reverse */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_REVERSE);
+               break;
+       case ':':
+               /* Newline */
+               g_string_append_c(out, '\n');
+               break;
+       case '|':
+               /* Indent here */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_INDENT);
+               break;
+       case 'F':
+               /* blink */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_BLINK);
+               break;
+       case 'n':
+       case 'N':
+               /* default color */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
+               break;
+       case '>':
+                /* clear to end of line */
+               g_string_append_c(out, 4);
+               g_string_append_c(out, FORMAT_STYLE_CLRTOEOL);
+               break;
+       case '[':
+               /* code */
+                format_expand_code(format, out, flags);
+               break;
+       default:
+               /* check if it's a background color */
+               p = strchr(format_backs, fmt);
+               if (p != NULL) {
+                       g_string_append_c(out, 4);
+                       g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
+                       g_string_append_c(out, (char) ((int) (p-format_backs)+'0'));
+                       break;
+               }
+
+               /* check if it's a foreground color */
+               if (fmt == 'p') fmt = 'm';
+               p = strchr(format_fores, fmt);
+               if (p != NULL) {
+                       g_string_append_c(out, 4);
+                       g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
+                       g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
+                       break;
+               }
+
+               /* check if it's a bold foreground color */
+               if (fmt == 'P') fmt = 'M';
+               p = strchr(format_boldfores, fmt);
+               if (p != NULL) {
+                       g_string_append_c(out, 4);
+                       g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
+                       g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
+                       break;
+               }
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+void format_read_arglist(va_list va, FORMAT_REC *format,
+                        char **arglist, int arglist_size,
+                        char *buffer, int buffer_size)
+{
+       int num, len, bufpos;
+
+        g_return_if_fail(format->params < arglist_size);
+
+       bufpos = 0;
+        arglist[format->params] = NULL;
+       for (num = 0; num < format->params; num++) {
+               switch (format->paramtypes[num]) {
+               case FORMAT_STRING:
+                       arglist[num] = (char *) va_arg(va, char *);
+                       if (arglist[num] == NULL) {
+                               g_warning("format_read_arglist() : parameter %d is NULL", num);
+                               arglist[num] = "";
+                       }
+                       break;
+               case FORMAT_INT: {
+                       int d = (int) va_arg(va, int);
+
+                       if (bufpos >= buffer_size) {
+                               arglist[num] = "";
+                               break;
+                       }
+
+                       arglist[num] = buffer+bufpos;
+                       len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
+                                        "%d", d);
+                       bufpos += len+1;
+                       break;
+               }
+               case FORMAT_LONG: {
+                       long l = (long) va_arg(va, long);
+
+                       if (bufpos >= buffer_size) {
+                               arglist[num] = "";
+                               break;
+                       }
+
+                       arglist[num] = buffer+bufpos;
+                       len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
+                                        "%ld", l);
+                        bufpos += len+1;
+                       break;
+               }
+               case FORMAT_FLOAT: {
+                       double f = (double) va_arg(va, double);
+
+                       if (bufpos >= buffer_size) {
+                               arglist[num] = "";
+                               break;
+                       }
+
+                       arglist[num] = buffer+bufpos;
+                       len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
+                                        "%0.2f", f);
+                       bufpos += len+1;
+                       break;
+               }
+               }
+       }
+}
+
+void format_create_dest(TEXT_DEST_REC *dest,
+                       void *server, const char *target,
+                       int level, WINDOW_REC *window)
+{
+        format_create_dest_tag(dest, server, NULL, target, level, window);
+}
+
+void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
+                           const char *server_tag, const char *target,
+                           int level, WINDOW_REC *window)
+{
+        memset(dest, 0, sizeof(TEXT_DEST_REC));
+
+       dest->server = server;
+       dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag;
+       dest->target = target;
+       dest->level = level;
+       dest->window = window != NULL ? window :
+               window_find_closest(server, target, level);
+}
+
+/* Return length of text part in string (ie. without % codes) */
+int format_get_length(const char *str)
+{
+        GString *tmp;
+       int len;
+
+        g_return_val_if_fail(str != NULL, 0);
+
+        tmp = g_string_new(NULL);
+       len = 0;
+       while (*str != '\0') {
+               if (*str == '%' && str[1] != '\0') {
+                       str++;
+                       if (*str != '%' &&
+                           format_expand_styles(tmp, &str, NULL)) {
+                                str++;
+                               continue;
+                       }
+
+                       /* %% or unknown %code, written as-is */
+                       if (*str != '%')
+                               len++;
+               }
+
+                len++;
+               str++;
+       }
+
+       g_string_free(tmp, TRUE);
+        return len;
+}
+
+/* Return how many characters in `str' must be skipped before `len'
+   characters of text is skipped. Like strip_real_length(), except this
+   handles %codes. */
+int format_real_length(const char *str, int len)
+{
+       GString *tmp;
+       const char *start;
+
+        g_return_val_if_fail(str != NULL, 0);
+        g_return_val_if_fail(len >= 0, 0);
+
+        start = str;
+        tmp = g_string_new(NULL);
+       while (*str != '\0' && len > 0) {
+               if (*str == '%' && str[1] != '\0') {
+                       str++;
+                       if (*str != '%' &&
+                           format_expand_styles(tmp, &str, NULL)) {
+                                str++;
+                               continue;
+                       }
+
+                       /* %% or unknown %code, written as-is */
+                       if (*str != '%') {
+                               if (--len == 0)
+                                        break;
+                       }
+               }
+
+                len--;
+               str++;
+       }
+
+       g_string_free(tmp, TRUE);
+        return (int) (str-start);
+}
+
+char *format_string_expand(const char *text, int *flags)
+{
+       GString *out;
+       char code, *ret;
+
+        g_return_val_if_fail(text != NULL, NULL);
+
+       out = g_string_new(NULL);
+
+       if (flags != NULL) *flags = 0;
+       code = 0;
+       while (*text != '\0') {
+               if (code == '%') {
+                       /* color code */
+                       if (!format_expand_styles(out, &text, flags)) {
+                               g_string_append_c(out, '%');
+                               g_string_append_c(out, '%');
+                               g_string_append_c(out, *text);
+                       }
+                       code = 0;
+               } else {
+                       if (*text == '%')
+                               code = *text;
+                       else
+                               g_string_append_c(out, *text);
+               }
+
+               text++;
+       }
+
+       ret = out->str;
+       g_string_free(out, FALSE);
+       return ret;
+}
+
+static char *format_get_text_args(TEXT_DEST_REC *dest,
+                                 const char *text, char **arglist)
+{
+       GString *out;
+       char code, *ret;
+       int need_free;
+
+       out = g_string_new(NULL);
+
+       code = 0;
+       while (*text != '\0') {
+               if (code == '%') {
+                       /* color code */
+                       if (!format_expand_styles(out, &text, &dest->flags)) {
+                               g_string_append_c(out, '%');
+                               g_string_append_c(out, '%');
+                               g_string_append_c(out, *text);
+                       }
+                       code = 0;
+               } else if (code == '$') {
+                       /* argument */
+                       char *ret;
+
+                       ret = parse_special((char **) &text, dest->server,
+                                           dest->target == NULL ? NULL :
+                                           window_item_find(dest->server, dest->target),
+                                           arglist, &need_free, NULL, 0);
+
+                       if (ret != NULL) {
+                               /* string shouldn't end with \003 or it could
+                                  mess up the next one or two characters */
+                                int diff;
+                               int len = strlen(ret);
+                               while (len > 0 && ret[len-1] == 3) len--;
+                               diff = strlen(ret)-len;
+
+                               g_string_append(out, ret);
+                               if (diff > 0)
+                                       g_string_truncate(out, out->len-diff);
+                               if (need_free) g_free(ret);
+                       }
+                       code = 0;
+               } else {
+                       if (*text == '%' || *text == '$')
+                               code = *text;
+                       else
+                               g_string_append_c(out, *text);
+               }
+
+               text++;
+       }
+
+       ret = out->str;
+       g_string_free(out, FALSE);
+       return ret;
+}
+
+char *format_get_text_theme(THEME_REC *theme, const char *module,
+                           TEXT_DEST_REC *dest, int formatnum, ...)
+{
+       va_list va;
+       char *str;
+
+       if (theme == NULL)
+               theme = window_get_theme(dest->window);
+
+       va_start(va, formatnum);
+       str = format_get_text_theme_args(theme, module, dest, formatnum, va);
+       va_end(va);
+
+       return str;
+}
+
+char *format_get_text_theme_args(THEME_REC *theme, const char *module,
+                                TEXT_DEST_REC *dest, int formatnum,
+                                va_list va)
+{
+       char *arglist[MAX_FORMAT_PARAMS];
+       char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
+       FORMAT_REC *formats;
+
+       formats = g_hash_table_lookup(default_formats, module);
+       format_read_arglist(va, &formats[formatnum],
+                           arglist, sizeof(arglist)/sizeof(char *),
+                           buffer, sizeof(buffer));
+
+       return format_get_text_theme_charargs(theme, module, dest,
+                                             formatnum, arglist);
+}
+
+char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
+                                    TEXT_DEST_REC *dest, int formatnum,
+                                    char **args)
+{
+       MODULE_THEME_REC *module_theme;
+       char *text;
+
+       module_theme = g_hash_table_lookup(theme->modules, module);
+       if (module_theme == NULL)
+               return NULL;
+
+        text = module_theme->expanded_formats[formatnum];
+       return format_get_text_args(dest, text, args);
+}
+
+char *format_get_text(const char *module, WINDOW_REC *window,
+                     void *server, const char *target,
+                     int formatnum, ...)
+{
+       TEXT_DEST_REC dest;
+       THEME_REC *theme;
+       va_list va;
+       char *str;
+
+       format_create_dest(&dest, server, target, 0, window);
+       theme = window_get_theme(dest.window);
+
+       va_start(va, formatnum);
+       str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
+       va_end(va);
+
+       return str;
+}
+
+/* add `linestart' to start of each line in `text'. `text' may contain
+   multiple lines separated with \n. */
+char *format_add_linestart(const char *text, const char *linestart)
+{
+       GString *str;
+       char *ret;
+
+       if (linestart == NULL)
+               return g_strdup(text);
+
+       if (strchr(text, '\n') == NULL)
+               return g_strconcat(linestart, text, NULL);
+
+       str = g_string_new(linestart);
+       while (*text != '\0') {
+               g_string_append_c(str, *text);
+               if (*text == '\n')
+                       g_string_append(str, linestart);
+               text++;
+       }
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+        return ret;
+}
+
+#define LINE_START_IRSSI_LEVEL \
+       (MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE)
+
+#define NOT_LINE_START_LEVEL \
+       (MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \
+       MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \
+       MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS)
+
+/* return the "-!- " text at the start of the line */
+char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
+{
+       int format;
+
+        /* check for flags if we want to override defaults */
+       if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
+               return NULL;
+
+       if (dest->flags & PRINT_FLAG_SET_LINE_START)
+               format = TXT_LINE_START;
+        else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
+               format = TXT_LINE_START_IRSSI;
+       else {
+                /* use defaults */
+               if (dest->level & LINE_START_IRSSI_LEVEL)
+                       format = TXT_LINE_START_IRSSI;
+               else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
+                       format = TXT_LINE_START;
+               else
+                       return NULL;
+       }
+
+       return format_get_text_theme(theme, MODULE_NAME, dest, format);
+}
+
+static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
+{
+        char *format, str[256];
+       struct tm *tm;
+       int diff;
+
+       if ((timestamp_level & dest->level) == 0)
+               return NULL;
+
+        /* check for flags if we want to override defaults */
+       if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
+               return NULL;
+
+       if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
+           (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
+                return NULL;
+
+
+       if (timestamp_timeout > 0) {
+               diff = t - dest->window->last_timestamp;
+               dest->window->last_timestamp = t;
+               if (diff < timestamp_timeout)
+                       return NULL;
+       }
+
+       tm = localtime(&t);
+       format = format_get_text_theme(theme, MODULE_NAME, dest,
+                                      TXT_TIMESTAMP);
+       if (strftime(str, sizeof(str), format, tm) <= 0)
+                str[0] = '\0';
+       g_free(format);
+       return g_strdup(str);
+}
+
+static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
+{
+       int count = 0;
+
+       if (dest->server_tag == NULL || hide_server_tags)
+                return NULL;
+
+        /* check for flags if we want to override defaults */
+       if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
+               return NULL;
+
+       if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) {
+               if (dest->window->active != NULL &&
+                   dest->window->active->server == dest->server)
+                       return NULL;
+
+               if (servers != NULL) {
+                       count++;
+                       if (servers->next != NULL)
+                               count++;
+               }
+               if (count < 2 && lookup_servers != NULL) {
+                       count++;
+                       if (lookup_servers->next != NULL)
+                               count++;
+               }
+
+               if (count < 2)
+                        return NULL;
+       }
+
+       return format_get_text_theme(theme, MODULE_NAME, dest,
+                                    TXT_SERVERTAG, dest->server_tag);
+}
+
+char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
+{
+       char *timestamp, *servertag;
+       char *linestart;
+
+       timestamp = get_timestamp(theme, dest, t);
+       servertag = get_server_tag(theme, dest);
+
+       if (timestamp == NULL && servertag == NULL)
+               return NULL;
+
+       linestart = g_strconcat(timestamp != NULL ? timestamp : "",
+                               servertag, NULL);
+
+       g_free_not_null(timestamp);
+       g_free_not_null(servertag);
+       return linestart;
+}
+
+void format_newline(WINDOW_REC *window)
+{
+       g_return_if_fail(window != NULL);
+
+       signal_emit_id(signal_gui_print_text, 6, window,
+                      GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
+                      GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
+                      "", GINT_TO_POINTER(-1));
+}
+
+/* parse ANSI color string */
+static const char *get_ansi_color(THEME_REC *theme, const char *str,
+                                 int *fg_ret, int *bg_ret, int *flags_ret)
+{
+       static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+       const char *start;
+       int fg, bg, flags, num;
+
+       if (*str != '[')
+               return str;
+       start = str++;
+
+       fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret;
+       bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret;
+       flags = flags_ret == NULL ? 0 : *flags_ret;
+
+       num = 0;
+       for (;; str++) {
+               if (*str == '\0') return start;
+
+               if (i_isdigit(*str)) {
+                       num = num*10 + (*str-'0');
+                       continue;
+               }
+
+               if (*str != ';' && *str != 'm')
+                       return start;
+
+               switch (num) {
+               case 0:
+                       /* reset colors back to default */
+                       fg = theme->default_color;
+                       bg = -1;
+                       flags &= ~GUI_PRINT_FLAG_INDENT;
+                       break;
+               case 1:
+                       /* hilight */
+                       flags |= GUI_PRINT_FLAG_BOLD;
+                       break;
+               case 5:
+                       /* blink */
+                       flags |= GUI_PRINT_FLAG_BLINK;
+                       break;
+               case 7:
+                       /* reverse */
+                       flags |= GUI_PRINT_FLAG_REVERSE;
+                       break;
+               default:
+                       if (num >= 30 && num <= 37)
+                               fg = (fg & 0xf8) | ansitab[num-30];
+                       if (num >= 40 && num <= 47) {
+                               if (bg == -1) bg = 0;
+                               bg = (bg & 0xf8) | ansitab[num-40];
+                       }
+                       break;
+               }
+               num = 0;
+
+               if (*str == 'm') {
+                       if (fg_ret != NULL) *fg_ret = fg;
+                       if (bg_ret != NULL) *bg_ret = bg;
+                       if (flags_ret != NULL) *flags_ret = flags;
+
+                       str++;
+                       break;
+               }
+       }
+
+       return str;
+}
+
+/* parse MIRC color string */
+static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
+{
+       int fg, bg;
+
+       fg = fg_ret == NULL ? -1 : *fg_ret;
+       bg = bg_ret == NULL ? -1 : *bg_ret;
+
+       if (!i_isdigit(**str) && **str != ',') {
+               fg = -1;
+               bg = -1;
+       } else {
+               /* foreground color */
+               if (**str != ',') {
+                       fg = **str-'0';
+                        (*str)++;
+                       if (i_isdigit(**str)) {
+                               fg = fg*10 + (**str-'0');
+                               (*str)++;
+                       }
+               }
+               if (**str == ',') {
+                       /* background color */
+                       (*str)++;
+                       if (!i_isdigit(**str))
+                               bg = -1;
+                       else {
+                               bg = **str-'0';
+                               (*str)++;
+                               if (i_isdigit(**str)) {
+                                       bg = bg*10 + (**str-'0');
+                                       (*str)++;
+                               }
+                       }
+               }
+       }
+
+       if (fg_ret) *fg_ret = fg;
+       if (bg_ret) *bg_ret = bg;
+}
+
+#define IS_COLOR_CODE(c) \
+       ((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \
+       (c) == 15 || (c) == 22 || (c) == 27 || (c) == 31)
+
+/* Return how many characters in `str' must be skipped before `len'
+   characters of text is skipped. */
+int strip_real_length(const char *str, int len,
+                     int *last_color_pos, int *last_color_len)
+{
+       const char *start = str;
+
+        if (last_color_pos != NULL)
+               *last_color_pos = -1;
+       if (last_color_len != NULL)
+               *last_color_len = -1;
+
+       while (*str != '\0') {
+               if (*str == 3) {
+                       const char *mircstart = str;
+
+                       if (last_color_pos != NULL)
+                               *last_color_pos = (int) (str-start);
+                        str++;
+                       get_mirc_color(&str, NULL, NULL);
+                        if (last_color_len != NULL)
+                               *last_color_len = (int) (str-mircstart);
+
+               } else if (*str == 4 && str[1] != '\0') {
+                       if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
+                               if (last_color_pos != NULL)
+                                       *last_color_pos = (int) (str-start);
+                                if (last_color_len != NULL)
+                                        *last_color_len = 3;
+                               str++;
+                       } else if (str[1] == FORMAT_STYLE_DEFAULTS) {
+                               if (last_color_pos != NULL)
+                                       *last_color_pos = (int) (str-start);
+                                if (last_color_len != NULL)
+                                        *last_color_len = 2;
+                       }
+                        str += 2;
+               } else {
+                       if (!IS_COLOR_CODE(*str)) {
+                               if (len-- == 0)
+                                       break;
+                       }
+                       str++;
+               }
+       }
+
+       return (int) (str-start);
+}
+
+char *strip_codes(const char *input)
+{
+        const char *p;
+        char *str, *out;
+
+        out = str = g_strdup(input);
+        for (p = input; *p != '\0'; p++) {
+                if (*p == 3) {
+                        p++;  
+
+                        /* mirc color */
+                        get_mirc_color(&p, NULL, NULL);
+                        p--;
+                        continue;
+                }
+
+                if (*p == 4 && p[1] != '\0') {
+                        if (p[1] >= FORMAT_STYLE_SPECIAL) {
+                                p++;
+                                continue;
+                        }
+
+                        /* irssi color */
+                        if (p[2] != '\0') {
+                                p += 2;
+                                continue;
+                        }
+               }
+
+               if (*p == 27 && p[1] != '\0')
+                       p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
+
+                if (!IS_COLOR_CODE(*p))
+                        *out++ = *p;   
+        }
+
+        *out = '\0';
+        return str; 
+}
+
+/* send a fully parsed text string for GUI to print */
+void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
+{
+       char *dup, *str, *ptr, type;
+       int fgcolor, bgcolor;
+       int flags;
+
+       dup = str = g_strdup(text);
+
+       flags = 0; fgcolor = -1; bgcolor = -1;
+       while (*str != '\0') {
+                type = '\0';
+               for (ptr = str; *ptr != '\0'; ptr++) {
+                       if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
+                               type = *ptr;
+                               *ptr++ = '\0';
+                               break;
+                       }
+
+                       *ptr = (char) translation_in[(int) (unsigned char) *ptr];
+               }
+
+               if (type == 7) {
+                       /* bell */
+                       if (settings_get_bool("bell_beeps"))
+                                signal_emit("beep", 0);
+               } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
+                        /* clear to end of line */
+                       flags |= GUI_PRINT_FLAG_CLRTOEOL;
+               }
+
+               if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
+                        /* send the text to gui handler */
+                       signal_emit_id(signal_gui_print_text, 6, dest->window,
+                                      GINT_TO_POINTER(fgcolor),
+                                      GINT_TO_POINTER(bgcolor),
+                                      GINT_TO_POINTER(flags), str,
+                                      dest->level);
+                       flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
+               }
+
+               if (type == '\n')
+                       format_newline(dest->window);
+
+               if (*ptr == '\0')
+                       break;
+
+               switch (type)
+               {
+               case 2:
+                       /* bold */
+                       if (!hide_text_style)
+                               flags ^= GUI_PRINT_FLAG_BOLD;
+                       break;
+               case 3:
+                       /* MIRC color */
+                       get_mirc_color((const char **) &ptr,
+                                       hide_mirc_colors || hide_text_style ? NULL : &fgcolor,
+                                       hide_mirc_colors || hide_text_style ? NULL : &bgcolor);
+                       if (!hide_mirc_colors && !hide_text_style)
+                               flags |= GUI_PRINT_FLAG_MIRC_COLOR;
+                       break;
+               case 4:
+                       /* user specific colors */
+                       flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
+                       switch (*ptr) {
+                       case FORMAT_STYLE_BLINK:
+                               flags ^= GUI_PRINT_FLAG_BLINK;
+                               break;
+                       case FORMAT_STYLE_UNDERLINE:
+                               flags ^= GUI_PRINT_FLAG_UNDERLINE;
+                               break;
+                       case FORMAT_STYLE_BOLD:
+                               flags ^= GUI_PRINT_FLAG_BOLD;
+                               break;
+                       case FORMAT_STYLE_REVERSE:
+                               flags ^= GUI_PRINT_FLAG_REVERSE;
+                               break;
+                       case FORMAT_STYLE_INDENT:
+                               flags |= GUI_PRINT_FLAG_INDENT;
+                               break;
+                       case FORMAT_STYLE_INDENT_FUNC: {
+                               const char *start = ptr;
+                               while (*ptr != ',' && *ptr != '\0')
+                                       ptr++;
+                               if (*ptr != '\0') *ptr++ = '\0';
+                               signal_emit_id(signal_gui_print_text, 6,
+                                              dest->window, NULL, NULL,
+                                              GINT_TO_POINTER(GUI_PRINT_FLAG_INDENT_FUNC),
+                                              str, start, dest->level);
+                               break;
+                       }
+                       case FORMAT_STYLE_DEFAULTS:
+                               fgcolor = bgcolor = -1;
+                               flags &= GUI_PRINT_FLAG_INDENT;
+                               break;
+                       case FORMAT_STYLE_CLRTOEOL:
+                                break;
+                       default:
+                               if (*ptr != FORMAT_COLOR_NOCHANGE) {
+                                       fgcolor = (unsigned char) *ptr-'0';
+                                       if (fgcolor <= 7)
+                                               flags &= ~GUI_PRINT_FLAG_BOLD;
+                                       else {
+                                               /* bold */
+                                               if (fgcolor != 8) fgcolor -= 8;
+                                               flags |= GUI_PRINT_FLAG_BOLD;
+                                       }
+                               }
+                               ptr++;
+                               if (*ptr != FORMAT_COLOR_NOCHANGE) {
+                                       bgcolor = *ptr-'0';
+                                       if (bgcolor <= 7)
+                                               flags &= ~GUI_PRINT_FLAG_BLINK;
+                                       else {
+                                               /* blink */
+                                                bgcolor -= 8;
+                                                flags |= GUI_PRINT_FLAG_BLINK;
+                                       }
+                               }
+                       }
+                       ptr++;
+                       break;
+               case 6:
+                       /* blink */
+                       if (!hide_text_style)
+                               flags ^= GUI_PRINT_FLAG_BLINK;
+                       break;
+               case 15:
+                       /* remove all styling */
+                       fgcolor = bgcolor = -1;
+                       flags &= GUI_PRINT_FLAG_INDENT;
+                       break;
+               case 22:
+                       /* reverse */
+                       if (!hide_text_style)
+                               flags ^= GUI_PRINT_FLAG_REVERSE;
+                       break;
+               case 31:
+                       /* underline */
+                       if (!hide_text_style)
+                               flags ^= GUI_PRINT_FLAG_UNDERLINE;
+                       break;
+               case 27:
+                       /* ansi color code */
+                       ptr = (char *)
+                               get_ansi_color(dest->window == NULL || dest->window->theme == NULL ?
+                                              current_theme : dest->window->theme,
+                                              ptr,
+                                              hide_text_style ? NULL : &fgcolor,
+                                              hide_text_style ? NULL : &bgcolor,
+                                              hide_text_style ? NULL : &flags);
+                       break;
+               }
+
+               str = ptr;
+       }
+
+       g_free(dup);
+}
+
+static void read_settings(void)
+{
+       timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
+       if (timestamp_level > 0) {
+               timestamp_level =
+                       level2bits(settings_get_str("timestamp_level"));
+       }
+       timestamp_timeout = settings_get_int("timestamp_timeout");
+
+       hide_server_tags = settings_get_bool("hide_server_tags");
+       hide_text_style = settings_get_bool("hide_text_style");
+       hide_mirc_colors = settings_get_bool("hide_mirc_colors");
+}
+
+void formats_init(void)
+{
+       signal_gui_print_text = signal_get_uniq_id("gui print text");
+
+       read_settings();
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void formats_deinit(void)
+{
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/formats.h b/apps/irssi/src/fe-common/core/formats.h
new file mode 100644 (file)
index 0000000..b98d7be
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef __FORMATS_H
+#define __FORMATS_H
+
+#include "themes.h"
+#include "fe-windows.h"
+
+#define GUI_PRINT_FLAG_BOLD          0x0001
+#define GUI_PRINT_FLAG_REVERSE       0x0002
+#define GUI_PRINT_FLAG_UNDERLINE     0x0004
+#define GUI_PRINT_FLAG_BLINK         0x0008
+#define GUI_PRINT_FLAG_MIRC_COLOR    0x0010
+#define GUI_PRINT_FLAG_INDENT        0x0020
+#define GUI_PRINT_FLAG_INDENT_FUNC   0x0040
+#define GUI_PRINT_FLAG_NEWLINE       0x0080
+#define GUI_PRINT_FLAG_CLRTOEOL      0x0100
+
+#define MAX_FORMAT_PARAMS 10
+#define DEFAULT_FORMAT_ARGLIST_SIZE 200
+
+enum {
+       FORMAT_STRING,
+       FORMAT_INT,
+       FORMAT_LONG,
+       FORMAT_FLOAT
+};
+
+struct _FORMAT_REC {
+       char *tag;
+       char *def;
+
+       int params;
+       int paramtypes[MAX_FORMAT_PARAMS];
+};
+
+#define PRINT_FLAG_SET_LINE_START      0x0001
+#define PRINT_FLAG_SET_LINE_START_IRSSI        0x0002
+#define PRINT_FLAG_UNSET_LINE_START    0x0003
+
+#define PRINT_FLAG_SET_TIMESTAMP       0x0004
+#define PRINT_FLAG_UNSET_TIMESTAMP     0x0008
+
+#define PRINT_FLAG_SET_SERVERTAG       0x0010
+#define PRINT_FLAG_UNSET_SERVERTAG     0x0020
+
+typedef struct {
+       WINDOW_REC *window;
+       SERVER_REC *server;
+        const char *server_tag; /* if server is non-NULL, must be server->tag */
+       const char *target;
+       int level;
+
+       int hilight_priority;
+       char *hilight_color;
+        int flags;
+} TEXT_DEST_REC;
+
+#define window_get_theme(window) \
+       (window != NULL && (window)->theme != NULL ? \
+       (window)->theme : current_theme)
+
+int format_find_tag(const char *module, const char *tag);
+
+/* Return length of text part in string (ie. without % codes) */
+int format_get_length(const char *str);
+/* Return how many characters in `str' must be skipped before `len'
+   characters of text is skipped. Like strip_real_length(), except this
+   handles %codes. */
+int format_real_length(const char *str, int len);
+
+char *format_string_expand(const char *text, int *flags);
+
+char *format_get_text(const char *module, WINDOW_REC *window,
+                     void *server, const char *target,
+                     int formatnum, ...);
+
+/* good size for buffer is DEFAULT_FORMAT_ARGLIST_SIZE */
+void format_read_arglist(va_list va, FORMAT_REC *format,
+                        char **arglist, int arglist_size,
+                        char *buffer, int buffer_size);
+char *format_get_text_theme(THEME_REC *theme, const char *module,
+                           TEXT_DEST_REC *dest, int formatnum, ...);
+char *format_get_text_theme_args(THEME_REC *theme, const char *module,
+                                TEXT_DEST_REC *dest, int formatnum,
+                                va_list va);
+char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
+                                    TEXT_DEST_REC *dest, int formatnum,
+                                    char **args);
+
+/* add `linestart' to start of each line in `text'. `text' may contain
+   multiple lines separated with \n. */
+char *format_add_linestart(const char *text, const char *linestart);
+
+/* return the "-!- " text at the start of the line */
+char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest);
+
+/* return timestamp + server tag */
+char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t);
+
+
+/* "private" functions for printtext */
+void format_create_dest(TEXT_DEST_REC *dest,
+                       void *server, const char *target,
+                       int level, WINDOW_REC *window);
+void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
+                           const char *server_tag, const char *target,
+                           int level, WINDOW_REC *window);
+
+void format_newline(WINDOW_REC *window);
+
+/* Return how many characters in `str' must be skipped before `len'
+   characters of text is skipped. */
+int strip_real_length(const char *str, int len,
+                     int *last_color_pos, int *last_color_len);
+
+/* strip all color (etc.) codes from `input'.
+   Returns newly allocated string. */
+char *strip_codes(const char *input);
+
+/* send a fully parsed text string for GUI to print */
+void format_send_to_gui(TEXT_DEST_REC *dest, const char *text);
+
+#define FORMAT_COLOR_NOCHANGE  ('0'-1) /* don't change this, at least hilighting depends this value */
+
+#define FORMAT_STYLE_SPECIAL   0x60
+#define FORMAT_STYLE_BLINK     (0x01 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_UNDERLINE (0x02 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_BOLD      (0x03 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_REVERSE   (0x04 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_INDENT    (0x05 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_INDENT_FUNC (0x06 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_DEFAULTS  (0x07 + FORMAT_STYLE_SPECIAL)
+#define FORMAT_STYLE_CLRTOEOL  (0x08 + FORMAT_STYLE_SPECIAL)
+int format_expand_styles(GString *out, const char **format, int *flags);
+
+void formats_init(void);
+void formats_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/hilight-text.c b/apps/irssi/src/fe-common/core/hilight-text.c
new file mode 100644 (file)
index 0000000..0bdda55
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ hilight-text.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "servers.h"
+#include "channels.h"
+#include "nicklist.h"
+
+#include "hilight-text.h"
+#include "nickmatch-cache.h"
+#include "printtext.h"
+#include "formats.h"
+
+static NICKMATCH_REC *nickmatch;
+static int never_hilight_level, default_hilight_level;
+GSList *hilights;
+
+static void reset_cache(void)
+{
+       GSList *tmp;
+
+        never_hilight_level = MSGLEVEL_ALL & ~default_hilight_level;
+       for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
+               HILIGHT_REC *rec = tmp->data;
+
+               if (never_hilight_level & rec->level)
+                        never_hilight_level &= ~rec->level;
+       }
+
+       nickmatch_rebuild(nickmatch);
+}
+
+static void hilight_add_config(HILIGHT_REC *rec)
+{
+       CONFIG_NODE *node;
+
+       g_return_if_fail(rec != NULL);
+
+       node = iconfig_node_traverse("(hilights", TRUE);
+       node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+        iconfig_node_set_str(node, "text", rec->text);
+        if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
+        if (rec->color) iconfig_node_set_str(node, "color", rec->color);
+        if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
+        if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
+        iconfig_node_set_bool(node, "nick", rec->nick);
+        iconfig_node_set_bool(node, "word", rec->word);
+        if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
+        if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
+        if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
+
+       if (rec->channels != NULL && *rec->channels != NULL) {
+               node = config_node_section(node, "channels", NODE_TYPE_LIST);
+               iconfig_node_add_list(node, rec->channels);
+       }
+}
+
+static void hilight_remove_config(HILIGHT_REC *rec)
+{
+       CONFIG_NODE *node;
+
+       g_return_if_fail(rec != NULL);
+
+       node = iconfig_node_traverse("hilights", FALSE);
+       if (node != NULL) iconfig_node_list_remove(node, g_slist_index(hilights, rec));
+}
+
+static void hilight_destroy(HILIGHT_REC *rec)
+{
+       g_return_if_fail(rec != NULL);
+
+#ifdef HAVE_REGEX_H
+       if (rec->regexp_compiled) regfree(&rec->preg);
+#endif
+       if (rec->channels != NULL) g_strfreev(rec->channels);
+       g_free_not_null(rec->color);
+       g_free_not_null(rec->act_color);
+       g_free(rec->text);
+       g_free(rec);
+}
+
+static void hilights_destroy_all(void)
+{
+       g_slist_foreach(hilights, (GFunc) hilight_destroy, NULL);
+       g_slist_free(hilights);
+       hilights = NULL;
+}
+
+static void hilight_remove(HILIGHT_REC *rec)
+{
+       g_return_if_fail(rec != NULL);
+
+       hilight_remove_config(rec);
+       hilights = g_slist_remove(hilights, rec);
+       hilight_destroy(rec);
+}
+
+static HILIGHT_REC *hilight_find(const char *text, char **channels)
+{
+       GSList *tmp;
+       char **chan;
+
+       g_return_val_if_fail(text != NULL, NULL);
+
+       for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
+               HILIGHT_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->text, text) != 0)
+                       continue;
+
+               if ((channels == NULL && rec->channels == NULL))
+                       return rec; /* no channels - ok */
+
+               if (channels != NULL && strcmp(*channels, "*") == 0)
+                       return rec; /* ignore channels */
+
+               if (channels == NULL || rec->channels == NULL)
+                       continue; /* other doesn't have channels */
+
+               if (strarray_length(channels) != strarray_length(rec->channels))
+                       continue; /* different amount of channels */
+
+               /* check that channels match */
+               for (chan = channels; *chan != NULL; chan++) {
+                       if (strarray_find(rec->channels, *chan) == -1)
+                                break;
+               }
+
+               if (*chan == NULL)
+                       return rec; /* channels ok */
+       }
+
+       return NULL;
+}
+
+static int hilight_match_text(HILIGHT_REC *rec, const char *text,
+                             int *match_beg, int *match_end)
+{
+       char *match;
+
+       if (rec->regexp) {
+#ifdef HAVE_REGEX_H
+               regmatch_t rmatch[1];
+
+               if (rec->regexp_compiled &&
+                   regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
+                       if (rmatch[0].rm_so > 0 &&
+                           match_beg != NULL && match_end != NULL) {
+                               *match_beg = rmatch[0].rm_so;
+                               *match_end = rmatch[0].rm_eo;
+                       }
+                       return TRUE;
+               }
+#endif
+       } else {
+               match = rec->fullword ?
+                       stristr_full(text, rec->text) :
+                       stristr(text, rec->text);
+               if (match != NULL) {
+                       if (match_beg != NULL && match_end != NULL) {
+                               *match_beg = (int) (match-text);
+                               *match_end = *match_beg + strlen(rec->text);
+                       }
+                       return TRUE;
+               }
+       }
+
+        return FALSE;
+}
+
+#define hilight_match_level(rec, level) \
+       (level & (((rec)->level != 0 ? rec->level : default_hilight_level)))
+
+#define hilight_match_channel(rec, channel) \
+       ((rec)->channels == NULL || ((channel) != NULL && \
+               strarray_find((rec)->channels, (channel)) != -1))
+
+HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
+                          const char *nick, const char *address,
+                          int level, const char *str,
+                           int *match_beg, int *match_end)
+{
+       GSList *tmp;
+        CHANNEL_REC *chanrec;
+       NICK_REC *nickrec;
+
+       g_return_val_if_fail(str != NULL, NULL);
+
+       if ((never_hilight_level & level) == level)
+               return NULL;
+
+       if (nick != NULL) {
+                /* check nick mask hilights */
+               chanrec = channel_find(server, channel);
+               nickrec = chanrec == NULL ? NULL :
+                       nicklist_find(chanrec, nick);
+               if (nickrec != NULL) {
+                        HILIGHT_REC *rec;
+
+                       if (nickrec->host == NULL)
+                               nicklist_set_host(chanrec, nickrec, address);
+
+                       rec = nickmatch_find(nickmatch, nickrec);
+                       if (rec != NULL && hilight_match_level(rec, level))
+                               return rec;
+               }
+       }
+
+       for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
+               HILIGHT_REC *rec = tmp->data;
+
+               if (!rec->nickmask && hilight_match_level(rec, level) &&
+                   hilight_match_channel(rec, channel) &&
+                   hilight_match_text(rec, str, match_beg, match_end))
+                       return rec;
+       }
+
+        return NULL;
+}
+
+static char *hilight_get_act_color(HILIGHT_REC *rec)
+{
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       return g_strdup(rec->act_color != NULL ? rec->act_color :
+                       rec->color != NULL ? rec->color :
+                       settings_get_str("hilight_act_color"));
+}
+
+static char *hilight_get_color(HILIGHT_REC *rec)
+{
+       const char *color;
+
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       color = rec->color != NULL ? rec->color :
+               settings_get_str("hilight_color");
+
+       return format_string_expand(color, NULL);
+}
+
+static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
+{
+       dest->level |= MSGLEVEL_HILIGHT;
+
+       if (rec->priority > 0)
+               dest->hilight_priority = rec->priority;
+
+        g_free_not_null(dest->hilight_color);
+        dest->hilight_color = hilight_get_act_color(rec);
+}
+
+static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
+                          const char *stripped)
+{
+       HILIGHT_REC *hilight;
+       char *color, *newstr;
+       int old_level, hilight_start, hilight_end, hilight_len;
+       int nick_match;
+
+       if (dest->level & MSGLEVEL_NOHILIGHT)
+               return;
+
+        hilight_start = hilight_end = 0;
+       hilight = hilight_match(dest->server, dest->target,
+                               NULL, NULL, dest->level, stripped,
+                               &hilight_start,
+                               &hilight_end);
+       if (hilight == NULL)
+               return;
+
+       nick_match = hilight->nick && (dest->level & (MSGLEVEL_PUBLIC|MSGLEVEL_ACTIONS)) == MSGLEVEL_PUBLIC;
+
+       old_level = dest->level;
+       if (!nick_match || (dest->level & MSGLEVEL_HILIGHT)) {
+               /* update the level / hilight info */
+               hilight_update_text_dest(dest, hilight);
+       }
+
+       if (nick_match)
+               return; /* fe-messages.c should have taken care of this */
+
+       if (old_level & MSGLEVEL_HILIGHT) {
+               /* nick is highlighted, just set priority */
+               return;
+       }
+
+       color = hilight_get_color(hilight);
+       hilight_len = hilight_end-hilight_start;
+
+       if (!hilight->word) {
+               /* hilight whole line */
+               char *tmp = strip_codes(text);
+               newstr = g_strconcat(color, tmp, NULL);
+                g_free(tmp);
+       } else {
+               /* hilight part of the line */
+                GString *tmp;
+                char *middle, *lastcolor;
+               int pos, color_pos, color_len;
+
+                tmp = g_string_new(NULL);
+
+                /* start of the line */
+               pos = strip_real_length(text, hilight_start, NULL, NULL);
+               g_string_append(tmp, text);
+                g_string_truncate(tmp, pos);
+
+               /* color */
+                g_string_append(tmp, color);
+
+               /* middle of the line, stripped */
+               middle = strip_codes(text+pos);
+                pos = tmp->len;
+               g_string_append(tmp, middle);
+                g_string_truncate(tmp, pos+hilight_len);
+                g_free(middle);
+
+               /* end of the line */
+               pos = strip_real_length(text, hilight_end,
+                                       &color_pos, &color_len);
+               if (color_pos > 0)
+                       lastcolor = g_strndup(text+color_pos, color_len);
+                else {
+                        /* no colors in line, change back to default */
+                       lastcolor = g_malloc0(3);
+                       lastcolor[0] = 4;
+                        lastcolor[1] = FORMAT_STYLE_DEFAULTS;
+               }
+               g_string_append(tmp, lastcolor);
+               g_string_append(tmp, text+pos);
+               g_free(lastcolor);
+
+                newstr = tmp->str;
+                g_string_free(tmp, FALSE);
+       }
+
+       signal_emit("print text", 3, dest, newstr, stripped);
+
+       g_free(color);
+       g_free(newstr);
+
+       signal_stop();
+}
+
+char *hilight_match_nick(SERVER_REC *server, const char *channel,
+                        const char *nick, const char *address,
+                        int level, const char *msg)
+{
+        HILIGHT_REC *rec;
+       char *color;
+
+       rec = hilight_match(server, channel, nick, address,
+                           level, msg, NULL, NULL);
+       color = rec == NULL || !rec->nick ? NULL :
+               hilight_get_color(rec);
+
+       return color;
+}
+
+static void read_hilight_config(void)
+{
+       CONFIG_NODE *node;
+       HILIGHT_REC *rec;
+       GSList *tmp;
+       char *text, *color;
+
+       hilights_destroy_all();
+
+       node = iconfig_node_traverse("hilights", FALSE);
+       if (node == NULL) {
+                reset_cache();
+               return;
+       }
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               if (node->type != NODE_TYPE_BLOCK)
+                       continue;
+
+               text = config_node_get_str(node, "text", NULL);
+               if (text == NULL || *text == '\0')
+                       continue;
+
+               rec = g_new0(HILIGHT_REC, 1);
+               hilights = g_slist_append(hilights, rec);
+
+               rec->text = g_strdup(text);
+
+               color = config_node_get_str(node, "color", NULL);
+               rec->color = color == NULL || *color == '\0' ? NULL :
+                       g_strdup(color);
+
+               color = config_node_get_str(node, "act_color", NULL);
+               rec->act_color = color == NULL || *color == '\0' ? NULL :
+                       g_strdup(color);
+
+               rec->level = config_node_get_int(node, "level", 0);
+               rec->priority = config_node_get_int(node, "priority", 0);
+               rec->nick = config_node_get_bool(node, "nick", TRUE);
+               rec->word = config_node_get_bool(node, "word", TRUE);
+
+               rec->nickmask = config_node_get_bool(node, "mask", FALSE);
+               rec->fullword = config_node_get_bool(node, "fullword", FALSE);
+               rec->regexp = config_node_get_bool(node, "regexp", FALSE);
+
+#ifdef HAVE_REGEX_H
+               rec->regexp_compiled = !rec->regexp ? FALSE :
+                       regcomp(&rec->preg, rec->text,
+                               REG_EXTENDED|REG_ICASE) == 0;
+#endif
+
+               node = config_node_section(node, "channels", -1);
+               if (node != NULL) rec->channels = config_node_get_list(node);
+       }
+
+        reset_cache();
+}
+
+static void hilight_print(int index, HILIGHT_REC *rec)
+{
+       char *chans, *levelstr;
+
+       chans = rec->channels == NULL ? NULL :
+               g_strjoinv(",", rec->channels);
+       levelstr = rec->level == 0 ? NULL :
+               bits2level(rec->level);
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                   TXT_HILIGHT_LINE, index, rec->text,
+                   chans != NULL ? chans : "",
+                   levelstr != NULL ? levelstr : "",
+                   rec->nickmask ? " -mask" : "",
+                   rec->fullword ? " -full" : "",
+                   rec->regexp ? " -regexp" : "");
+       g_free_not_null(chans);
+       g_free_not_null(levelstr);
+}
+
+static void cmd_hilight_show(void)
+{
+       GSList *tmp;
+       int index;
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_HEADER);
+       index = 1;
+       for (tmp = hilights; tmp != NULL; tmp = tmp->next, index++) {
+               HILIGHT_REC *rec = tmp->data;
+
+               hilight_print(index, rec);
+       }
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER);
+}
+
+/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp]
+                   [-color <color>] [-actcolor <color>] [-level <level>]
+                  [-channels <channels>] <text> */
+static void cmd_hilight(const char *data)
+{
+        GHashTable *optlist;
+       HILIGHT_REC *rec;
+       char *colorarg, *actcolorarg, *levelarg, *priorityarg, *chanarg, *text;
+       char **channels;
+       void *free_arg;
+
+       g_return_if_fail(data != NULL);
+
+       if (*data == '\0') {
+               cmd_hilight_show();
+               return;
+       }
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_GETREST, "hilight", &optlist, &text))
+               return;
+
+       chanarg = g_hash_table_lookup(optlist, "channels");
+       levelarg = g_hash_table_lookup(optlist, "level");
+       priorityarg = g_hash_table_lookup(optlist, "priority");
+       colorarg = g_hash_table_lookup(optlist, "color");
+       actcolorarg = g_hash_table_lookup(optlist, "actcolor");
+
+       if (*text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       channels = (chanarg == NULL || *chanarg == '\0') ? NULL :
+               g_strsplit(replace_chars(chanarg, ',', ' '), " ", -1);
+
+       rec = hilight_find(text, channels);
+       if (rec == NULL) {
+               rec = g_new0(HILIGHT_REC, 1);
+
+               /* default to nick/word hilighting */
+                rec->nick = TRUE;
+               rec->word = TRUE;
+
+               rec->text = g_strdup(text);
+               rec->channels = channels;
+       } else {
+               g_strfreev(channels);
+
+                hilight_remove_config(rec);
+               hilights = g_slist_remove(hilights, rec);
+       }
+
+       rec->level = (levelarg == NULL || *levelarg == '\0') ? 0 :
+               level2bits(replace_chars(levelarg, ',', ' '));
+       rec->priority = priorityarg == NULL ? 0 : atoi(priorityarg);
+
+       if (g_hash_table_lookup(optlist, "line") != NULL) {
+               rec->word = FALSE;
+               rec->nick = FALSE;
+       }
+
+       if (g_hash_table_lookup(optlist, "word") != NULL) {
+               rec->word = TRUE;
+                rec->nick = FALSE;
+       }
+
+       if (g_hash_table_lookup(optlist, "nick") != NULL)
+                rec->nick = TRUE;
+
+       rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL;
+       rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
+       rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
+
+       if (colorarg != NULL) {
+               g_free_and_null(rec->color);
+               if (*colorarg != '\0')
+                       rec->color = g_strdup(colorarg);
+       }
+       if (actcolorarg != NULL) {
+               g_free_and_null(rec->act_color);
+               if (*actcolorarg != '\0')
+                       rec->act_color = g_strdup(actcolorarg);
+       }
+
+#ifdef HAVE_REGEX_H
+       if (rec->regexp_compiled)
+               regfree(&rec->preg);
+       rec->regexp_compiled = !rec->regexp ? FALSE :
+               regcomp(&rec->preg, rec->text, REG_EXTENDED|REG_ICASE) == 0;
+#endif
+
+       hilights = g_slist_append(hilights, rec);
+       hilight_add_config(rec);
+
+       hilight_print(g_slist_index(hilights, rec)+1, rec);
+        cmd_params_free(free_arg);
+
+       reset_cache();
+}
+
+/* SYNTAX: DEHILIGHT <id>|<mask> */
+static void cmd_dehilight(const char *data)
+{
+       HILIGHT_REC *rec;
+       GSList *tmp;
+
+       if (is_numeric(data, ' ')) {
+               /* with index number */
+               tmp = g_slist_nth(hilights, atoi(data)-1);
+               rec = tmp == NULL ? NULL : tmp->data;
+       } else {
+               /* with mask */
+               char *chans[2] = { "*", NULL };
+                rec = hilight_find(data, chans);
+       }
+
+       if (rec == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_NOT_FOUND, data);
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_REMOVED, rec->text);
+               hilight_remove(rec);
+                reset_cache();
+       }
+}
+
+static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
+                              NICK_REC *nick)
+{
+       GSList *tmp;
+       HILIGHT_REC *match;
+        char *nickmask;
+       int len, best_match;
+
+       if (nick->host == NULL)
+                return; /* don't check until host is known */
+
+       nickmask = g_strconcat(nick->nick, "!", nick->host, NULL);
+
+       best_match = 0; match = NULL;
+       for (tmp = hilights; tmp != NULL; tmp = tmp->next) {
+               HILIGHT_REC *rec = tmp->data;
+
+               if (rec->nickmask &&
+                   hilight_match_channel(rec, channel->name) &&
+                   match_wildcards(rec->text, nickmask)) {
+                       len = strlen(rec->text);
+                       if (best_match < len) {
+                               best_match = len;
+                               match = rec;
+                       }
+               }
+       }
+       g_free_not_null(nickmask);
+
+       if (match != NULL)
+                g_hash_table_insert(list, nick, match);
+}
+
+static void read_settings(void)
+{
+       default_hilight_level = level2bits(settings_get_str("hilight_level"));
+}
+
+void hilight_text_init(void)
+{
+       settings_add_str("lookandfeel", "hilight_color", "%Y");
+       settings_add_str("lookandfeel", "hilight_act_color", "%M");
+       settings_add_str("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
+
+        read_settings();
+
+       nickmatch = nickmatch_init(hilight_nick_cache);
+       read_hilight_config();
+
+       signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
+        signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
+        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight);
+       command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight);
+       command_set_options("hilight", "-color -actcolor -level -priority -channels nick word line mask full regexp");
+}
+
+void hilight_text_deinit(void)
+{
+       hilights_destroy_all();
+        nickmatch_deinit(nickmatch);
+
+       signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
+        signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
+        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+
+       command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight);
+       command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight);
+}
diff --git a/apps/irssi/src/fe-common/core/hilight-text.h b/apps/irssi/src/fe-common/core/hilight-text.h
new file mode 100644 (file)
index 0000000..92093bb
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __HILIGHT_TEXT_H
+#define __HILIGHT_TEXT_H
+
+#ifdef HAVE_REGEX_H
+#  include <regex.h>
+#endif
+
+typedef struct {
+       char *text;
+
+       char **channels; /* if non-NULL, check the text only from these channels */
+       int level; /* match only messages with this level, 0=default */
+       char *color; /* if starts with number, \003 is automatically
+                       inserted before it. */
+        char *act_color; /* color for window activity */
+       int priority;
+
+       unsigned int nick:1; /* hilight only nick if possible */
+       unsigned int word:1; /* hilight only word, not full line */
+
+       unsigned int nickmask:1; /* `text' is a nick mask */
+       unsigned int fullword:1; /* match `text' only for full words */
+       unsigned int regexp:1; /* `text' is a regular expression */
+#ifdef HAVE_REGEX_H
+       unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
+       regex_t preg;
+#endif
+} HILIGHT_REC;
+
+extern GSList *hilights;
+
+HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
+                          const char *nick, const char *address,
+                          int level, const char *str,
+                          int *match_beg, int *match_end);
+
+char *hilight_match_nick(SERVER_REC *server, const char *channel,
+                        const char *nick, const char *address,
+                        int level, const char *msg);
+
+void hilight_text_init(void);
+void hilight_text_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/keyboard.c b/apps/irssi/src/fe-common/core/keyboard.c
new file mode 100644 (file)
index 0000000..ac68200
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ keyboard.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "keyboard.h"
+#include "fe-windows.h"
+#include "printtext.h"
+
+GSList *keyinfos;
+static GHashTable *keys, *default_keys;
+
+/* A cache of some sort for key presses that generate a single char only.
+   If the key isn't used, used_keys[key] is zero. */
+static char used_keys[256];
+
+/* Contains a list of all possible executable key bindings (not "key" keys).
+   Format is _always_ in key1-key2-key3 format and fully extracted, like
+   ^[-[-A, not meta-A */
+static GTree *key_states;
+static int key_config_frozen;
+
+struct KEYBOARD_REC {
+       char *key_state; /* the ongoing key combo */
+        void *gui_data; /* GUI specific data sent in "key pressed" signal */
+};
+
+/* Creates a new "keyboard" - this is used only for keeping track of
+   key combo states and sending the gui_data parameter in "key pressed"
+   signal */
+KEYBOARD_REC *keyboard_create(void *data)
+{
+       KEYBOARD_REC *rec;
+
+       rec = g_new0(KEYBOARD_REC, 1);
+       rec->gui_data = data;
+
+       signal_emit("keyboard created", 1, rec);
+        return rec;
+}
+
+/* Destroys a keyboard */
+void keyboard_destroy(KEYBOARD_REC *keyboard)
+{
+       signal_emit("keyboard destroyed", 1, keyboard);
+
+        g_free_not_null(keyboard->key_state);
+        g_free(keyboard);
+}
+
+static void key_destroy(KEY_REC *rec, GHashTable *hash)
+{
+       g_hash_table_remove(hash, rec->key);
+
+       g_free_not_null(rec->data);
+       g_free(rec->key);
+       g_free(rec);
+}
+
+static void key_default_add(const char *id, const char *key, const char *data)
+{
+        KEYINFO_REC *info;
+       KEY_REC *rec;
+
+       info = key_info_find(id);
+       if (info == NULL)
+               return;
+
+       rec = g_hash_table_lookup(default_keys, key);
+       if (rec != NULL) {
+               /* key already exists, replace */
+               rec->info->default_keys =
+                       g_slist_remove(rec->info->default_keys, rec);
+               key_destroy(rec, default_keys);
+       }
+
+       rec = g_new0(KEY_REC, 1);
+       rec->key = g_strdup(key);
+       rec->info = info;
+       rec->data = g_strdup(data);
+        info->default_keys = g_slist_append(info->default_keys, rec);
+       g_hash_table_insert(default_keys, rec->key, rec);
+}
+
+static CONFIG_NODE *key_config_find(const char *key)
+{
+       CONFIG_NODE *node;
+        GSList *tmp;
+
+       /* remove old keyboard settings */
+       node = iconfig_node_traverse("(keyboard", TRUE);
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               if (strcmp(config_node_get_str(node, "key", ""), key) == 0)
+                        return node;
+       }
+
+        return NULL;
+}
+
+static void keyconfig_save(const char *id, const char *key, const char *data)
+{
+       CONFIG_NODE *node;
+
+       g_return_if_fail(id != NULL);
+       g_return_if_fail(key != NULL);
+
+       node = key_config_find(key);
+       if (node == NULL) {
+               node = iconfig_node_traverse("(keyboard", TRUE);
+               node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+       }
+
+       iconfig_node_set_str(node, "key", key);
+       iconfig_node_set_str(node, "id", id);
+       iconfig_node_set_str(node, "data", data);
+}
+
+static void keyconfig_clear(const char *key)
+{
+       CONFIG_NODE *node;
+
+       g_return_if_fail(key != NULL);
+
+       /* remove old keyboard settings */
+       node = key_config_find(key);
+       if (node != NULL) {
+               iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE),
+                                   node);
+       }
+}
+
+KEYINFO_REC *key_info_find(const char *id)
+{
+       GSList *tmp;
+
+       for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) {
+               KEYINFO_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->id, id) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static int expand_key(const char *key, GSList **out);
+
+#define expand_out_char(out, c) \
+       { \
+         GSList *tmp; \
+         for (tmp = out; tmp != NULL; tmp = tmp->next) \
+            g_string_append_c(tmp->data, c); \
+       }
+
+#define expand_out_free(out) \
+       { \
+         GSList *tmp; \
+         for (tmp = out; tmp != NULL; tmp = tmp->next) \
+            g_string_free(tmp->data, TRUE); \
+         g_slist_free(out); out = NULL; \
+       }
+
+static int expand_combo(const char *start, const char *end, GSList **out)
+{
+        KEY_REC *rec;
+       KEYINFO_REC *info;
+        GSList *tmp, *tmp2, *list, *copy, *newout;
+       char *str;
+
+       if (start == end) {
+               /* single key */
+               expand_out_char(*out, *start);
+                return TRUE;
+       }
+
+       info = key_info_find("key");
+       if (info == NULL)
+               return FALSE;
+
+        /* get list of all key combos that generate the named combo.. */
+        list = NULL;
+       str = g_strndup(start, (int) (end-start)+1);
+       for (tmp = info->keys; tmp != NULL; tmp = tmp->next) {
+               KEY_REC *rec = tmp->data;
+
+               if (strcmp(rec->data, str) == 0)
+                        list = g_slist_append(list, rec);
+       }
+       g_free(str);
+
+       if (list == NULL)
+               return FALSE;
+
+       if (list->next == NULL) {
+                /* only one way to generate the combo, good */
+                rec = list->data;
+               return expand_key(rec->key, out);
+       }
+
+       /* multiple ways to generate the combo -
+          we'll need to include all of them in output */
+        newout = NULL;
+       for (tmp = list->next; tmp != NULL; tmp = tmp->next) {
+               KEY_REC *rec = tmp->data;
+
+               copy = NULL;
+               for (tmp2 = *out; tmp2 != NULL; tmp2 = tmp2->next) {
+                       GString *str = tmp2->data;
+                        copy = g_slist_append(copy, g_string_new(str->str));
+               }
+
+               if (!expand_key(rec->key, &copy)) {
+                       /* illegal key combo, remove from list */
+                        expand_out_free(copy);
+               } else {
+                        newout = g_slist_concat(newout, copy);
+               }
+       }
+
+        rec = list->data;
+       if (!expand_key(rec->key, out)) {
+               /* illegal key combo, remove from list */
+               expand_out_free(*out);
+       }
+
+       *out = g_slist_concat(*out, newout);
+        return *out != NULL;
+}
+
+/* Expand key code - returns TRUE if successful. */
+static int expand_key(const char *key, GSList **out)
+{
+       GSList *tmp;
+       const char *start;
+       int last_hyphen;
+
+       /* meta-^W^Gf -> ^[-^W-^G-f */
+        start = NULL; last_hyphen = TRUE;
+       for (; *key != '\0'; key++) {
+               if (start != NULL) {
+                       if (i_isalnum(*key) || *key == '_') {
+                                /* key combo continues */
+                               continue;
+                       }
+
+                       if (!expand_combo(start, key-1, out))
+                                return FALSE;
+                       expand_out_char(*out, '-');
+                        start = NULL;
+               }
+
+               if (*key == '-') {
+                       if (last_hyphen) {
+                                expand_out_char(*out, '-');
+                                expand_out_char(*out, '-');
+                       }
+                       last_hyphen = !last_hyphen;
+               } else if (*key == '^') {
+                        /* ctrl-code */
+                       if (key[1] != '\0')
+                               key++;
+
+                       expand_out_char(*out, '^');
+                       expand_out_char(*out, *key);
+                       expand_out_char(*out, '-');
+                        last_hyphen = FALSE; /* optional */
+               } else if (last_hyphen && i_isalnum(*key) && !i_isdigit(*key)) {
+                        /* possibly beginning of keycombo */
+                       start = key;
+                        last_hyphen = FALSE;
+               } else {
+                       expand_out_char(*out, *key);
+                       expand_out_char(*out, '-');
+                        last_hyphen = FALSE; /* optional */
+               }
+       }
+
+       if (start != NULL)
+               return expand_combo(start, key-1, out);
+
+       for (tmp = *out; tmp != NULL; tmp = tmp->next) {
+               GString *str = tmp->data;
+
+               g_string_truncate(str, str->len-1);
+       }
+
+        return TRUE;
+}
+
+static void key_states_scan_key(const char *key, KEY_REC *rec)
+{
+       GSList *tmp, *out;
+
+       if (strcmp(rec->info->id, "key") == 0)
+               return;
+
+        out = g_slist_append(NULL, g_string_new(NULL));
+       if (expand_key(key, &out)) {
+               for (tmp = out; tmp != NULL; tmp = tmp->next) {
+                       GString *str = tmp->data;
+
+                       if (str->str[1] == '-' || str->str[1] == '\0')
+                               used_keys[(int)(unsigned char)str->str[0]] = 1;
+
+                       g_tree_insert(key_states, g_strdup(str->str), rec);
+               }
+       }
+
+       expand_out_free(out);
+}
+
+static int key_state_destroy(char *key)
+{
+       g_free(key);
+        return FALSE;
+}
+
+/* Rescan all the key combos and figure out which characters are supposed
+   to be treated as characters and which as key combos.
+   Yes, this is pretty slow function... */
+static void key_states_rescan(void)
+{
+       GString *temp;
+
+       memset(used_keys, 0, sizeof(used_keys));
+
+       g_tree_traverse(key_states, (GTraverseFunc) key_state_destroy,
+                       G_IN_ORDER, NULL);
+       g_tree_destroy(key_states);
+       key_states = g_tree_new((GCompareFunc) strcmp);
+
+        temp = g_string_new(NULL);
+       g_hash_table_foreach(keys, (GHFunc) key_states_scan_key, temp);
+        g_string_free(temp, TRUE);
+}
+
+void key_configure_freeze(void)
+{
+        key_config_frozen++;
+}
+
+void key_configure_thaw(void)
+{
+        g_return_if_fail(key_config_frozen > 0);
+
+       if (--key_config_frozen == 0)
+               key_states_rescan();
+}
+
+static void key_configure_destroy(KEY_REC *rec)
+{
+       g_return_if_fail(rec != NULL);
+
+       rec->info->keys = g_slist_remove(rec->info->keys, rec);
+       g_hash_table_remove(keys, rec->key);
+
+       if (!key_config_frozen)
+                key_states_rescan();
+
+       g_free_not_null(rec->data);
+       g_free(rec->key);
+       g_free(rec);
+}
+
+/* Configure new key */
+static void key_configure_create(const char *id, const char *key,
+                                const char *data)
+{
+       KEYINFO_REC *info;
+       KEY_REC *rec;
+
+       g_return_if_fail(id != NULL);
+       g_return_if_fail(key != NULL && *key != '\0');
+
+       info = key_info_find(id);
+       if (info == NULL)
+               return;
+
+       rec = g_hash_table_lookup(keys, key);
+       if (rec != NULL)
+               key_configure_destroy(rec);
+
+       rec = g_new0(KEY_REC, 1);
+       rec->key = g_strdup(key);
+       rec->info = info;
+       rec->data = g_strdup(data);
+       info->keys = g_slist_append(info->keys, rec);
+       g_hash_table_insert(keys, rec->key, rec);
+
+       if (!key_config_frozen)
+                key_states_rescan();
+}
+
+/* Bind a key for function */
+void key_bind(const char *id, const char *description,
+             const char *key_default, const char *data, SIGNAL_FUNC func)
+{
+       KEYINFO_REC *info;
+       char *key;
+
+       g_return_if_fail(id != NULL);
+
+       /* create key info record */
+       info = key_info_find(id);
+       if (info == NULL) {
+               g_return_if_fail(func != NULL);
+
+               if (description == NULL)
+                       g_warning("key_bind(%s) should have description!", id);
+               info = g_new0(KEYINFO_REC, 1);
+               info->id = g_strdup(id);
+               info->description = g_strdup(description);
+               keyinfos = g_slist_append(keyinfos, info);
+
+               /* add the signal */
+               key = g_strconcat("key ", id, NULL);
+               signal_add(key, func);
+               g_free(key);
+
+               signal_emit("keyinfo created", 1, info);
+       }
+
+       if (key_default != NULL && *key_default != '\0') {
+                key_default_add(id, key_default, data);
+               key_configure_create(id, key_default, data);
+       }
+}
+
+static void keyinfo_remove(KEYINFO_REC *info)
+{
+       g_return_if_fail(info != NULL);
+
+       keyinfos = g_slist_remove(keyinfos, info);
+       signal_emit("keyinfo destroyed", 1, info);
+
+       /* destroy all keys */
+        g_slist_foreach(info->keys, (GFunc) key_destroy, keys);
+        g_slist_foreach(info->default_keys, (GFunc) key_destroy, default_keys);
+
+       /* destroy key info */
+       g_slist_free(info->keys);
+       g_slist_free(info->default_keys);
+       g_free_not_null(info->description);
+       g_free(info->id);
+       g_free(info);
+}
+
+/* Unbind key */
+void key_unbind(const char *id, SIGNAL_FUNC func)
+{
+       KEYINFO_REC *info;
+       char *key;
+
+       g_return_if_fail(id != NULL);
+       g_return_if_fail(func != NULL);
+
+       /* remove keys */
+       info = key_info_find(id);
+       if (info != NULL)
+               keyinfo_remove(info);
+
+       /* remove signal */
+       key = g_strconcat("key ", id, NULL);
+       signal_remove(key, func);
+       g_free(key);
+}
+
+/* Configure new key */
+void key_configure_add(const char *id, const char *key, const char *data)
+{
+       g_return_if_fail(id != NULL);
+       g_return_if_fail(key != NULL && *key != '\0');
+
+       key_configure_create(id, key, data);
+       keyconfig_save(id, key, data);
+}
+
+/* Remove key */
+void key_configure_remove(const char *key)
+{
+       KEY_REC *rec;
+
+       g_return_if_fail(key != NULL);
+
+       rec = g_hash_table_lookup(keys, key);
+       if (rec == NULL) return;
+
+        keyconfig_clear(key);
+       key_configure_destroy(rec);
+}
+
+static int key_emit_signal(KEYBOARD_REC *keyboard, KEY_REC *key)
+{
+       int consumed;
+        char *str;
+
+       str = g_strconcat("key ", key->info->id, NULL);
+       consumed = signal_emit(str, 3, key->data, keyboard->gui_data, key->info);
+       g_free(str);
+
+        return consumed;
+}
+
+static int key_states_search(const unsigned char *combo,
+                            const unsigned char *search)
+{
+       while (*search != '\0') {
+               if (*combo != *search)
+                       return *search - *combo;
+                search++; combo++;
+       }
+
+        return 0;
+}
+
+/* Returns TRUE if key press was consumed. Control characters should be sent
+   as "^@" .. "^_" instead of #0..#31 chars, #127 should be sent as ^? */
+int key_pressed(KEYBOARD_REC *keyboard, const char *key)
+{
+       KEY_REC *rec;
+        char *combo;
+        int first_key, consumed;
+
+       g_return_val_if_fail(keyboard != NULL, FALSE);
+       g_return_val_if_fail(key != NULL && *key != '\0', FALSE);
+
+       if (keyboard->key_state == NULL && key[1] == '\0' &&
+           !used_keys[(int) (unsigned char) key[0]]) {
+               /* fast check - key not used */
+               return FALSE;
+       }
+
+        first_key = keyboard->key_state == NULL;
+       combo = keyboard->key_state == NULL ? g_strdup(key) :
+                g_strconcat(keyboard->key_state, "-", key, NULL);
+       g_free_and_null(keyboard->key_state);
+
+       rec = g_tree_search(key_states,
+                           (GSearchFunc) key_states_search,
+                           combo);
+       if (rec == NULL) {
+               /* unknown key combo, eat the invalid key
+                  unless it was the first key pressed */
+                g_free(combo);
+               return !first_key;
+       }
+
+       if (g_tree_lookup(key_states, combo) != rec) {
+               /* key combo continues.. */
+               keyboard->key_state = combo;
+                return TRUE;
+       }
+
+        /* finished key combo, execute */
+        g_free(combo);
+       consumed = key_emit_signal(keyboard, rec);
+
+       /* never consume non-control characters */
+       return consumed;
+}
+
+void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry,
+                            int flags, void *data)
+{
+       signal_emit("gui entry redirect", 4, func, entry,
+                   GINT_TO_POINTER(flags), data);
+}
+
+static void sig_command(const char *data)
+{
+       const char *cmdchars;
+       char *str;
+
+       cmdchars = settings_get_str("cmdchars");
+       str = strchr(cmdchars, *data) != NULL ? g_strdup(data) :
+               g_strdup_printf("%c%s", *cmdchars, data);
+
+       signal_emit("send command", 3, str, active_win->active_server, active_win->active);
+
+       g_free(str);
+}
+
+static void sig_key(const char *data)
+{
+        /* we should never get here */
+}
+
+static void sig_multi(const char *data, void *gui_data)
+{
+        KEYINFO_REC *info;
+       char **list, **tmp, *p, *str;
+
+       list = g_strsplit(data, ";", -1);
+       for (tmp = list; *tmp != NULL; tmp++) {
+               p = strchr(*tmp, ' ');
+               if (p != NULL) *p++ = '\0'; else p = "";
+
+               info = key_info_find(*tmp);
+               if (info != NULL) {
+                       str = g_strconcat("key ", info->id, NULL);
+                       signal_emit(str, 3, p, gui_data, info);
+                       g_free(str);
+               }
+       }
+        g_strfreev(list);
+}
+
+static void cmd_show_keys(const char *searchkey, int full)
+{
+       GSList *info, *key;
+        int len;
+
+       len = searchkey == NULL ? 0 : strlen(searchkey);
+       for (info = keyinfos; info != NULL; info = info->next) {
+               KEYINFO_REC *rec = info->data;
+
+               for (key = rec->keys; key != NULL; key = key->next) {
+                       KEY_REC *rec = key->data;
+
+                       if ((len == 0 || g_strncasecmp(rec->key, searchkey, len) == 0) &&
+                           (!full || rec->key[len] == '\0')) {
+                               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_KEY,
+                                           rec->key, rec->info->id, rec->data == NULL ? "" : rec->data);
+                       }
+               }
+       }
+}
+
+/* SYNTAX: BIND [-delete] [<key> [<command> [<data>]]] */
+static void cmd_bind(const char *data)
+{
+       GHashTable *optlist;
+       char *key, *id, *keydata;
+       void *free_arg;
+       int command_id;
+
+       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
+                           "bind", &optlist, &key, &id, &keydata))
+               return;
+
+       if (*key != '\0' && g_hash_table_lookup(optlist, "delete")) {
+                /* delete key */
+               key_configure_remove(key);
+               cmd_params_free(free_arg);
+               return;
+       }
+
+       if (*id == '\0') {
+               /* show some/all keys */
+               cmd_show_keys(key, FALSE);
+               cmd_params_free(free_arg);
+               return;
+       }
+
+       command_id = strchr(settings_get_str("cmdchars"), *id) != NULL;
+       if (command_id) {
+               /* using shortcut to command id */
+               keydata = g_strconcat(id, " ", keydata, NULL);
+               id = "command";
+       }
+
+       if (key_info_find(id) == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_BIND_UNKNOWN_ID, id);
+       else {
+               key_configure_add(id, key, keydata);
+               cmd_show_keys(key, TRUE);
+       }
+
+       if (command_id) g_free(keydata);
+        cmd_params_free(free_arg);
+}
+
+static GList *completion_get_keyinfos(const char *info)
+{
+       GList *list;
+       GSList *tmp;
+       int len;
+
+       list = NULL; len = strlen(info);
+       for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) {
+               KEYINFO_REC *rec = tmp->data;
+
+               if (g_strncasecmp(rec->id, info, len) == 0)
+                        list = g_list_append(list, g_strdup(rec->id));
+       }
+
+       return list;
+}
+
+static void sig_complete_bind(GList **list, WINDOW_REC *window,
+                             const char *word, const char *line,
+                             int *want_space)
+{
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       if (*line == '\0' || strchr(line, ' ') != NULL)
+               return;
+
+       *list = completion_get_keyinfos(word);
+       if (*list != NULL) signal_stop();
+}
+
+static int key_destroy_hash(const char *key, KEY_REC *rec)
+{
+       rec->info->keys = g_slist_remove(rec->info->keys, rec);
+
+       g_free_not_null(rec->data);
+       g_free(rec->key);
+       g_free(rec);
+        return TRUE;
+}
+
+static void key_copy_default(const char *key, KEY_REC *orig)
+{
+       KEY_REC *rec;
+
+        rec = g_new0(KEY_REC, 1);
+       rec->key = g_strdup(orig->key);
+       rec->info = orig->info;
+       rec->data = g_strdup(orig->data);
+
+       rec->info->keys = g_slist_append(rec->info->keys, rec);
+       g_hash_table_insert(keys, rec->key, rec);
+}
+
+static void keyboard_reset_defaults(void)
+{
+       g_hash_table_foreach_remove(keys, (GHRFunc) key_destroy_hash, NULL);
+        g_hash_table_foreach(default_keys, (GHFunc) key_copy_default, NULL);
+}
+
+static void key_config_read(CONFIG_NODE *node)
+{
+       char *key, *id, *data;
+
+       g_return_if_fail(node != NULL);
+
+       key = config_node_get_str(node, "key", NULL);
+       id = config_node_get_str(node, "id", NULL);
+       data = config_node_get_str(node, "data", NULL);
+
+       if (key != NULL && id != NULL)
+               key_configure_create(id, key, data);
+}
+
+static void read_keyboard_config(void)
+{
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+        key_configure_freeze();
+
+       keyboard_reset_defaults();
+
+       node = iconfig_node_traverse("keyboard", FALSE);
+       if (node == NULL) {
+               key_configure_thaw();
+               return;
+       }
+
+       /* FIXME: backward "compatibility" - remove after irssi .99 */
+       if (node->type != NODE_TYPE_LIST) {
+                iconfig_node_remove(NULL, node);
+               key_configure_thaw();
+               return;
+       }
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp))
+               key_config_read(tmp->data);
+
+        key_configure_thaw();
+}
+
+void keyboard_init(void)
+{
+       keys = g_hash_table_new((GHashFunc) g_str_hash,
+                               (GCompareFunc) g_str_equal);
+       default_keys = g_hash_table_new((GHashFunc) g_str_hash,
+                                       (GCompareFunc) g_str_equal);
+       keyinfos = NULL;
+       key_states = g_tree_new((GCompareFunc) strcmp);
+        key_config_frozen = 0;
+       memset(used_keys, 0, sizeof(used_keys));
+
+       key_bind("command", "Run any IRC command", NULL, NULL, (SIGNAL_FUNC) sig_command);
+       key_bind("key", "Specify name for key binding", NULL, NULL, (SIGNAL_FUNC) sig_key);
+       key_bind("multi", "Run multiple commands", NULL, NULL, (SIGNAL_FUNC) sig_multi);
+
+       /* read the keyboard config when all key binds are known */
+       signal_add("irssi init read settings", (SIGNAL_FUNC) read_keyboard_config);
+       signal_add("setup reread", (SIGNAL_FUNC) read_keyboard_config);
+       signal_add("complete command bind", (SIGNAL_FUNC) sig_complete_bind);
+
+       command_bind("bind", NULL, (SIGNAL_FUNC) cmd_bind);
+       command_set_options("bind", "delete");
+}
+
+void keyboard_deinit(void)
+{
+       while (keyinfos != NULL)
+               keyinfo_remove(keyinfos->data);
+       g_hash_table_destroy(keys);
+       g_hash_table_destroy(default_keys);
+
+       g_tree_traverse(key_states, (GTraverseFunc) key_state_destroy,
+                       G_IN_ORDER, NULL);
+       g_tree_destroy(key_states);
+
+       signal_remove("irssi init read settings", (SIGNAL_FUNC) read_keyboard_config);
+        signal_remove("setup reread", (SIGNAL_FUNC) read_keyboard_config);
+       signal_remove("complete command bind", (SIGNAL_FUNC) sig_complete_bind);
+       command_unbind("bind", (SIGNAL_FUNC) cmd_bind);
+}
diff --git a/apps/irssi/src/fe-common/core/keyboard.h b/apps/irssi/src/fe-common/core/keyboard.h
new file mode 100644 (file)
index 0000000..9f2652e
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __KEYBOARD_H
+#define __KEYBOARD_H
+
+#include "signals.h"
+
+typedef struct KEYBOARD_REC KEYBOARD_REC;
+
+typedef struct {
+       char *id;
+       char *description;
+
+       GSList *keys, *default_keys;
+} KEYINFO_REC;
+
+typedef struct {
+       KEYINFO_REC *info;
+
+       char *key;
+       char *data;
+} KEY_REC;
+
+extern GSList *keyinfos;
+
+/* Creates a new "keyboard" - this is used only for keeping track of
+   key combo states and sending the gui_data parameter in "key pressed"
+   signal */
+KEYBOARD_REC *keyboard_create(void *gui_data);
+/* Destroys a keyboard */
+void keyboard_destroy(KEYBOARD_REC *keyboard);
+/* Returns TRUE if key press was consumed. Control characters should be sent
+   as "^@" .. "^_" instead of #0..#31 chars, #127 should be sent as ^? */
+int key_pressed(KEYBOARD_REC *keyboard, const char *key);
+
+void key_bind(const char *id, const char *description,
+             const char *key_default, const char *data, SIGNAL_FUNC func);
+void key_unbind(const char *id, SIGNAL_FUNC func);
+
+void key_configure_freeze(void);
+void key_configure_thaw(void);
+
+void key_configure_add(const char *id, const char *key, const char *data);
+void key_configure_remove(const char *key);
+
+KEYINFO_REC *key_info_find(const char *id);
+
+#define ENTRY_REDIRECT_FLAG_HOTKEY     0x01
+#define ENTRY_REDIRECT_FLAG_HIDDEN     0x02
+
+void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry,
+                            int flags, void *data);
+
+void keyboard_init(void);
+void keyboard_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/module-formats.c b/apps/irssi/src/fe-common/core/module-formats.c
new file mode 100644 (file)
index 0000000..3972ea8
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ module-formats.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "formats.h"
+
+FORMAT_REC fecommon_core_formats[] = {
+       { MODULE_NAME, "Core", 0 },
+
+       /* ---- */
+       { NULL, "Windows", 0 },
+
+       { "line_start", "{line_start}", 0 },
+       { "line_start_irssi", "{line_start}{hilight Irssi:} ", 0 },
+        { "timestamp", "{timestamp $Z} ", 0 },
+       { "servertag", "[$0] ", 1, { 0 } },
+       { "daychange", "Day changed to %%d %%b %%Y", 0 },
+       { "talking_with", "You are now talking with {nick $0}", 1, { 0 } },
+       { "refnum_too_low", "Window number must be greater than 1", 0 },
+       { "error_server_sticky", "Window's server is sticky and it cannot be changed without -unsticky option", 0 },
+       { "set_server_sticky", "Window's server set sticky", 1, { 0 } },
+       { "unset_server_sticky", "Window's server isn't sticky anymore", 0 },
+       { "window_name_not_unique", "Window names must be unique", 1, { 0 } },
+       { "window_level", "Window level is now $0", 1, { 0 } },
+       { "window_set_immortal", "Window is now immortal", 0 },
+       { "window_unset_immortal", "Window isn't immortal anymore", 0 },
+       { "window_immortal_error", "Window is immortal, if you really want to close it, say /WINDOW IMMORTAL OFF", 0 },
+       { "windowlist_header", "Ref Name                 Active item     Server          Level", 0 },
+       { "windowlist_line", "$[3]0 %|$[20]1 $[15]2 $[15]3 $4", 5, { 1, 0, 0, 0, 0 } },
+       { "windowlist_footer", "", 0 },
+       { "windows_layout_saved", "Layout of windows is now remembered next time you start irssi", 0 },
+       { "windows_layout_reset", "Layout of windows reset to defaults", 0 },
+       { "window_info_header", "", 0 },
+       { "window_info_footer", "", 0 },
+       { "window_info_refnum", "Window  : {hilight #$0}", 1, { 1 } },
+       { "window_info_refnum_sticky", "Window  : {hilight #$0 (sticky)}", 1, { 1 } },
+       { "window_info_name", "Name    : $0", 1, { 0 } },
+       { "window_info_history", "History : $0", 1, { 0 } },
+       { "window_info_immortal", "Immortal: yes", 0 },
+       { "window_info_size", "Size    : $0x$1", 2, { 1, 1 } },
+       { "window_info_level", "Level   : $0", 1, { 0 } },
+       { "window_info_server", "Server  : $0", 1, { 0 } },
+       { "window_info_server_sticky", "Server  : $0 (sticky)", 1, { 0 } },
+       { "window_info_theme", "Theme   : $0$1", 2, { 0, 0 } },
+       { "window_info_bound_items_header", "Bounds  : {hilight Name                           Server tag}", 0 },
+       { "window_info_bound_item", "        : $[!30]0 $[!15]1 $2", 3, { 0, 0, 0 } },
+       { "window_info_bound_items_footer", "", 0 },
+       { "window_info_items_header", "Items   : {hilight Name                           Server tag}", 0 },
+       { "window_info_item", " $[7]0: $[!30]1 $2", 3, { 0, 0, 0 } },
+       { "window_info_items_footer", "", 0 },
+
+       /* ---- */
+       { NULL, "Server", 0 },
+
+       { "looking_up", "Looking up {server $0}", 1, { 0 } },
+       { "connecting", "Connecting to {server $0} [$1] port {hilight $2}", 3, { 0, 0, 1 } },
+       { "connection_established", "Connection to {server $0} established", 1, { 0 } },
+       { "cant_connect", "Unable to connect server {server $0} port {hilight $1} {reason $2}", 3, { 0, 1, 0 } },
+       { "connection_lost", "Connection lost to {server $0}", 1, { 0 } },
+       { "lag_disconnected", "No PONG reply from server {server $0} in $1 seconds, disconnecting", 2, { 0, 1 } },
+       { "disconnected", "Disconnected from {server $0} {reason $1}", 2, { 0, 0 } },
+       { "server_quit", "Disconnecting from server {server $0}: {reason $1}", 2, { 0, 0 } },
+       { "server_changed", "Changed to {hilight $2} server {server $1}", 3, { 0, 0, 0 } },
+       { "unknown_server_tag", "Unknown server tag {server $0}", 1, { 0 } },
+       { "no_connected_servers", "Not connected to any servers", 0 },
+       { "server_list", "{server $0}: $1:$2 ($3)", 5, { 0, 0, 1, 0, 0 } },
+       { "server_lookup_list", "{server $0}: $1:$2 ($3) (connecting...)", 5, { 0, 0, 1, 0, 0 } },
+       { "server_reconnect_list", "{server $0}: $1:$2 ($3) ($5 left before reconnecting)", 6, { 0, 0, 1, 0, 0, 0 } },
+       { "server_reconnect_removed", "Removed reconnection to server {server $0} port {hilight $1}", 3, { 0, 1, 0 } },
+       { "server_reconnect_not_found", "Reconnection tag {server $0} not found", 1, { 0 } },
+       { "setupserver_added", "Server {server $0} saved", 2, { 0, 1 } },
+       { "setupserver_removed", "Server {server $0} removed", 2, { 0, 1 } },
+       { "setupserver_not_found", "Server {server $0} not found", 2, { 0, 1 } },
+       { "your_nick", "Your nickname is {nick $0}", 1, { 0 } },
+
+       /* ---- */
+       { NULL, "Channels", 0 },
+
+       { "join", "{channick_hilight $0} {chanhost_hilight $1} has joined {channel $2}", 3, { 0, 0, 0 } },
+       { "part", "{channick $0} {chanhost $1} has left {channel $2} {reason $3}", 4, { 0, 0, 0, 0 } },
+       { "kick", "{channick $0} was kicked from {channel $1} by {nick $2} {reason $3}", 4, { 0, 0, 0, 0 } },
+       { "quit", "{channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } },
+       { "quit_once", "{channel $3} {channick $0} {chanhost $1} has quit {reason $2}", 4, { 0, 0, 0, 0 } },
+       { "invite", "{nick $0} invites you to {channel $1}", 2, { 0, 0 } },
+       { "not_invited", "You have not been invited to a channel!", 0 },
+       { "new_topic", "{nick $0} changed the topic of {channel $1} to: $2", 3, { 0, 0, 0 } },
+       { "topic_unset", "Topic unset by {nick $0} on {channel $1}", 2, { 0, 0 } },
+       { "your_nick_changed", "You're now known as {nick $1}", 3, { 0, 0, 0 } },
+       { "nick_changed", "{channick $0} is now known as {channick_hilight $1}", 3, { 0, 0, 0 } },
+       { "talking_in", "You are now talking in {channel $0}", 1, { 0 } },
+       { "not_in_channels", "You are not on any channels", 0 },
+       { "current_channel", "Current channel {channel $0}", 1, { 0 } },
+       { "names", "{names_users Users {names_channel $0}} $1", 2, { 0, 0 } },
+       { "names_prefix", "{names_prefix $0}", 1, { 0 } },
+        { "names_nick_op", "{names_nick_op $0 $1}", 2, { 0, 0 } },
+        { "names_nick_halfop", "{names_nick_halfop $0 $1}", 2, { 0, 0 } },
+        { "names_nick_voice", "{names_nick_voice $0 $1}", 2, { 0, 0 } },
+        { "names_nick", "{names_nick $0 $1}", 2, { 0, 0 } },
+        { "endofnames", "{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal}", 6, { 0, 1, 1, 1, 1, 1 } },
+       { "chanlist_header", "You are on the following channels:", 0 },
+       { "chanlist_line", "{channel $[-10]0} %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } },
+       { "chansetup_not_found", "Channel {channel $0} not found", 2, { 0, 0 } },
+       { "chansetup_added", "Channel {channel $0} saved", 2, { 0, 0 } },
+       { "chansetup_removed", "Channel {channel $0} removed", 2, { 0, 0 } },
+       { "chansetup_header", "Channel         Network    Password   Settings", 0 },
+       { "chansetup_line", "{channel $[15]0} %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } },
+       { "chansetup_footer", "", 0 },
+       { "channel_move_notify", "{channel $0} is already joined in window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } },
+
+       /* ---- */
+       { NULL, "Messages", 0 },
+
+       { "own_msg", "{ownmsgnick $2 {ownnick $0}}$1", 3, { 0, 0, 0 } },
+       { "own_msg_channel", "{ownmsgnick $3 {ownnick $0}{msgchannel $1}}$2", 4, { 0, 0, 0, 0 } },
+       { "own_msg_private", "{ownprivmsg msg $0}$1", 2, { 0, 0 } },
+       { "own_msg_private_query", "{ownprivmsgnick {ownprivnick $2}}$1", 3, { 0, 0, 0 } },
+       { "pubmsg_me", "{pubmsgmenick $2 {menick $0}}$1", 3, { 0, 0, 0 } },
+       { "pubmsg_me_channel", "{pubmsgmenick $3 {menick $0}{msgchannel $1}}$2", 4, { 0, 0, 0, 0 } },
+       { "pubmsg_hilight", "{pubmsghinick $0 $3 $1}$2", 4, { 0, 0, 0, 0 } },
+       { "pubmsg_hilight_channel", "{pubmsghinick $0 $4 $1{msgchannel $2}}$3", 5, { 0, 0, 0, 0, 0 } },
+       { "pubmsg", "{pubmsgnick $2 {pubnick $0}}$1", 3, { 0, 0, 0 } },
+       { "pubmsg_channel", "{pubmsgnick $3 {pubnick $0}{msgchannel $1}}$2", 4, { 0, 0, 0, 0 } },
+       { "msg_private", "{privmsg $0 $1}$2", 3, { 0, 0, 0 } },
+       { "msg_private_query", "{privmsgnick $0}$2", 3, { 0, 0, 0 } },
+       { "no_msgs_got", "You have not received a message from anyone yet", 0 },
+       { "no_msgs_sent", "You have not sent a message to anyone yet", 0 },
+
+       /* ---- */
+       { NULL, "Queries", 0 },
+
+       { "query_start", "Starting query in {server $1} with {nick $0}", 2, { 0, 0 } },
+       { "query_stop", "Closing query with {nick $0}", 1, { 0 } },
+       { "no_query", "No query with {nick $0}", 1, { 0 } },
+       { "query_server_changed", "Query with {nick $0} changed to server {server $1}", 2, { 0, 0 } },
+       { "query_move_notify", "Query with {nick $0} is already created to window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window", 2, { 0, 1 } },
+
+       /* ---- */
+       { NULL, "Highlighting", 0 },
+
+       { "hilight_header", "Highlights:", 0 },
+       { "hilight_line", "$[-4]0 $1 $2 $3$4$5", 7, { 1, 0, 0, 0, 0, 0, 0 } },
+       { "hilight_footer", "", 0 },
+       { "hilight_not_found", "Highlight not found: $0", 1, { 0 } },
+       { "hilight_removed", "Highlight removed: $0", 1, { 0 } },
+
+       /* ---- */
+       { NULL, "Aliases", 0 },
+
+       { "alias_added", "Alias $0 added", 1, { 0 } },
+       { "alias_removed", "Alias $0 removed", 1, { 0 } },
+       { "alias_not_found", "No such alias: $0", 1, { 0 } },
+       { "aliaslist_header", "Aliases:", 0 },
+       { "aliaslist_line", "$[10]0 $1", 2, { 0, 0 } },
+       { "aliaslist_footer", "", 0 },
+
+       /* ---- */
+       { NULL, "Logging", 0 },
+
+       { "log_opened", "Log file {hilight $0} opened", 1, { 0 } },
+       { "log_closed", "Log file {hilight $0} closed", 1, { 0 } },
+       { "log_create_failed", "Couldn't create log file {hilight $0}: $1", 2, { 0, 0 } },
+       { "log_locked", "Log file {hilight $0} is locked, probably by another running Irssi", 1, { 0 } },
+       { "log_not_open", "Log file {hilight $0} not open", 1, { 0 } },
+       { "log_started", "Started logging to file {hilight $0}", 1, { 0 } },
+       { "log_stopped", "Stopped logging to file {hilight $0}", 1, { 0 } },
+       { "log_list_header", "Logs:", 0 },
+       { "log_list", "$0 $1: $2 $3$4", 5, { 1, 0, 0, 0, 0, 0 } },
+       { "log_list_footer", "", 0 },
+       { "windowlog_file", "Window LOGFILE set to $0", 1, { 0 } },
+       { "windowlog_file_logging", "Can't change window's logfile while log is on", 0 },
+       { "no_away_msgs", "No new messages in awaylog", 1, { 0 } },
+       { "away_msgs", "{hilight $1} new messages in awaylog:", 2, { 0, 1 } },
+
+       /* ---- */
+       { NULL, "Modules", 0 },
+
+       { "module_header", "Module               Type    Submodules", 0, },
+       { "module_line", "$[!20]0 $[7]1 $2", 3, { 0, 0, 0 } },
+       { "module_footer", "", 0, },
+       { "module_already_loaded", "Module {hilight $0/$1} already loaded", 2, { 0, 0 } },
+       { "module_not_loaded", "Module {hilight $0/$1} is not loaded", 2, { 0, 0 } },
+       { "module_load_error", "Error loading module {hilight $0/$1}: $2", 3, { 0, 0, 0 } },
+       { "module_invalid", "{hilight $0/$1} isn't Irssi module", 2, { 0, 0 } },
+       { "module_loaded", "Loaded module {hilight $0/$1}", 2, { 0, 0 } },
+       { "module_unloaded", "Unloaded module {hilight $0/$1}", 2, { 0, 0 } },
+
+       /* ---- */
+       { NULL, "Commands", 0 },
+
+       { "command_unknown", "Unknown command: $0", 1, { 0 } },
+       { "command_ambiguous", "Ambiguous command: $0", 1, { 0 } },
+       { "option_unknown", "Unknown option: $0", 1, { 0 } },
+       { "option_ambiguous", "Ambiguous option: $0", 1, { 0 } },
+       { "option_missing_arg", "Missing required argument for: $0", 1, { 0 } },
+       { "not_enough_params", "Not enough parameters given", 0 },
+       { "not_connected", "Not connected to server", 0 },
+       { "not_joined", "Not joined to any channel", 0 },
+       { "chan_not_found", "Not joined to such channel", 0 },
+       { "chan_not_synced", "Channel not fully synchronized yet, try again after a while", 0 },
+       { "illegal_proto", "Command isn't designed for the chat protocol of the active server", 0 },
+       { "not_good_idea", "Doing this is not a good idea. Add -YES if you really mean it", 0 },
+
+       /* ---- */
+       { NULL, "Themes", 0 },
+
+       { "theme_saved", "Theme saved to $0", 1, { 0 } },
+       { "theme_save_failed", "Error saving theme to $0: $1", 2, { 0, 0 } },
+       { "theme_not_found", "Theme {hilight $0} not found", 1, { 0 } },
+       { "theme_changed", "Using now theme {hilight $0} ($1)", 2, { 0, 0 } },
+       { "window_theme", "Using theme {hilight $0} in this window", 2, { 0, 0 } },
+       { "window_theme_default", "No theme is set for this window", 0 },
+       { "window_theme_changed", "Using now theme {hilight $0} ($1) in this window", 2, { 0, 0 } },
+       { "window_theme_removed", "Removed theme from this window", 0 },
+       { "format_title", "%:[{hilight $0}] - [{hilight $1}]%:", 2, { 0, 0 } },
+       { "format_subtitle", "[{hilight $0}]", 1, { 0 } },
+       { "format_item", "$0 = $1", 2, { 0, 0 } },
+
+       /* ---- */
+       { NULL, "Ignores", 0 },
+
+       { "ignored", "Ignoring {hilight $1} from {nick $0}", 2, { 0, 0 } },
+       { "unignored", "Unignored {nick $0}", 1, { 0 } },
+       { "ignore_not_found", "{nick $0} is not being ignored", 1, { 0 } },
+       { "ignore_no_ignores", "There are no ignores", 0 },
+       { "ignore_header", "Ignorance List:", 0 },
+       { "ignore_line", "$[-4]0 $1: $2 $3 $4", 4, { 1, 0, 0, 0 } },
+       { "ignore_footer", "", 0 },
+
+       /* ---- */
+       { NULL, "Misc", 0 },
+
+       { "unknown_chat_protocol", "Unknown chat protocol: $0", 1, { 0 } },
+       { "unknown_chatnet", "Unknown chat network: $0 (create it with /IRCNET ADD)", 1, { 0 } },
+       { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 },
+       { "perl_error", "Perl error: $0", 1, { 0 } },
+       { "bind_key", "$[!20]0 $1 $2", 3, { 0, 0, 0 } },
+       { "bind_unknown_id", "Unknown bind action: $0", 1, { 0 } },
+       { "config_saved", "Saved configuration to file $0", 1, { 0 } },
+       { "config_reloaded", "Reloaded configuration", 1, { 0 } },
+       { "config_modified", "Configuration file was modified since irssi was last started - do you want to overwrite the possible changes?", 1, { 0 } },
+       { "glib_error", "{error GLib $0} $1", 2, { 0, 0 } },
+       { "overwrite_config", "Overwrite config (y/N)?", 0 },
+       { "set_title", "[{hilight $0}]", 1, { 0 } },
+       { "set_item", "$0 = $1", 2, { 0, 0 } },
+       { "set_unknown", "Unknown setting $0", 1, { 0 } },
+       { "set_not_boolean", "Setting {hilight $0} isn't boolean, use /SET", 1, { 0 } },
+       { "translation_not_found", "Error opening translation table file $0: $1", 2, { 0, 0 } },
+       { "translation_file_error", "Error parsing translation table file $0", 1, { 0 } },
+
+       { NULL, NULL, 0 }
+};
diff --git a/apps/irssi/src/fe-common/core/module-formats.h b/apps/irssi/src/fe-common/core/module-formats.h
new file mode 100644 (file)
index 0000000..6ef7043
--- /dev/null
@@ -0,0 +1,234 @@
+#include "formats.h"
+
+enum {
+       TXT_MODULE_NAME,
+
+       TXT_FILL_1,
+
+       TXT_LINE_START,
+       TXT_LINE_START_IRSSI,
+       TXT_TIMESTAMP,
+       TXT_SERVERTAG,
+       TXT_DAYCHANGE,
+       TXT_TALKING_WITH,
+       TXT_REFNUM_TOO_LOW,
+        TXT_ERROR_SERVER_STICKY,
+        TXT_SET_SERVER_STICKY,
+       TXT_UNSET_SERVER_STICKY,
+        TXT_WINDOW_NAME_NOT_UNIQUE,
+        TXT_WINDOW_LEVEL,
+        TXT_WINDOW_SET_IMMORTAL,
+        TXT_WINDOW_UNSET_IMMORTAL,
+        TXT_WINDOW_IMMORTAL_ERROR,
+       TXT_WINDOWLIST_HEADER,
+       TXT_WINDOWLIST_LINE,
+       TXT_WINDOWLIST_FOOTER,
+       TXT_WINDOWS_LAYOUT_SAVED,
+       TXT_WINDOWS_LAYOUT_RESET,
+        TXT_WINDOW_INFO_HEADER,
+        TXT_WINDOW_INFO_FOOTER,
+       TXT_WINDOW_INFO_REFNUM,
+       TXT_WINDOW_INFO_REFNUM_STICKY,
+       TXT_WINDOW_INFO_NAME,
+       TXT_WINDOW_INFO_HISTORY,
+       TXT_WINDOW_INFO_IMMORTAL,
+       TXT_WINDOW_INFO_SIZE,
+       TXT_WINDOW_INFO_LEVEL,
+        TXT_WINDOW_INFO_SERVER,
+       TXT_WINDOW_INFO_SERVER_STICKY,
+        TXT_WINDOW_INFO_THEME,
+       TXT_WINDOW_INFO_BOUND_ITEMS_HEADER,
+       TXT_WINDOW_INFO_BOUND_ITEM,
+       TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER,
+       TXT_WINDOW_INFO_ITEMS_HEADER,
+       TXT_WINDOW_INFO_ITEM,
+        TXT_WINDOW_INFO_ITEMS_FOOTER,
+
+       TXT_FILL_2,
+
+       TXT_LOOKING_UP,
+       TXT_CONNECTING,
+       TXT_CONNECTION_ESTABLISHED,
+       TXT_CANT_CONNECT,
+       TXT_CONNECTION_LOST,
+       TXT_LAG_DISCONNECTED,
+       TXT_DISCONNECTED,
+       TXT_SERVER_QUIT,
+       TXT_SERVER_CHANGED,
+       TXT_UNKNOWN_SERVER_TAG,
+       TXT_NO_CONNECTED_SERVERS,
+       TXT_SERVER_LIST,
+       TXT_SERVER_LOOKUP_LIST,
+       TXT_SERVER_RECONNECT_LIST,
+       TXT_RECONNECT_REMOVED,
+       TXT_RECONNECT_NOT_FOUND,
+       TXT_SETUPSERVER_ADDED,
+       TXT_SETUPSERVER_REMOVED,
+       TXT_SETUPSERVER_NOT_FOUND,
+       TXT_YOUR_NICK,
+
+       TXT_FILL_3,
+
+       TXT_JOIN,
+       TXT_PART,
+       TXT_KICK,
+       TXT_QUIT,
+       TXT_QUIT_ONCE,
+       TXT_INVITE,
+       TXT_NOT_INVITED,
+       TXT_NEW_TOPIC,
+       TXT_TOPIC_UNSET,
+       TXT_YOUR_NICK_CHANGED,
+       TXT_NICK_CHANGED,
+       TXT_TALKING_IN,
+       TXT_NOT_IN_CHANNELS,
+       TXT_CURRENT_CHANNEL,
+       TXT_NAMES,
+       TXT_NAMES_PREFIX,
+       TXT_NAMES_NICK_OP,
+       TXT_NAMES_NICK_HALFOP,
+       TXT_NAMES_NICK_VOICE,
+       TXT_NAMES_NICK,
+       TXT_ENDOFNAMES,
+       TXT_CHANLIST_HEADER,
+       TXT_CHANLIST_LINE,
+       TXT_CHANSETUP_NOT_FOUND,
+       TXT_CHANSETUP_ADDED,
+       TXT_CHANSETUP_REMOVED,
+       TXT_CHANSETUP_HEADER,
+       TXT_CHANSETUP_LINE,
+       TXT_CHANSETUP_FOOTER,
+        TXT_CHANNEL_MOVE_NOTIFY,
+
+        TXT_FILL_4,
+
+       TXT_OWN_MSG,
+       TXT_OWN_MSG_CHANNEL,
+       TXT_OWN_MSG_PRIVATE,
+       TXT_OWN_MSG_PRIVATE_QUERY,
+       TXT_PUBMSG_ME,
+       TXT_PUBMSG_ME_CHANNEL,
+       TXT_PUBMSG_HILIGHT,
+       TXT_PUBMSG_HILIGHT_CHANNEL,
+       TXT_PUBMSG,
+       TXT_PUBMSG_CHANNEL,
+       TXT_MSG_PRIVATE,
+       TXT_MSG_PRIVATE_QUERY,
+       TXT_NO_MSGS_GOT,
+       TXT_NO_MSGS_SENT,
+
+       TXT_FILL_5,
+
+       TXT_QUERY_START,
+       TXT_QUERY_STOP,
+       TXT_NO_QUERY,
+       TXT_QUERY_SERVER_CHANGED,
+        TXT_QUERY_MOVE_NOTIFY,
+
+       TXT_FILL_6,
+
+       TXT_HILIGHT_HEADER,
+       TXT_HILIGHT_LINE,
+       TXT_HILIGHT_FOOTER,
+       TXT_HILIGHT_NOT_FOUND,
+       TXT_HILIGHT_REMOVED,
+
+       TXT_FILL_7,
+
+       TXT_ALIAS_ADDED,
+       TXT_ALIAS_REMOVED,
+       TXT_ALIAS_NOT_FOUND,
+       TXT_ALIASLIST_HEADER,
+       TXT_ALIASLIST_LINE,
+       TXT_ALIASLIST_FOOTER,
+
+       TXT_FILL_8,
+
+       TXT_LOG_OPENED,
+       TXT_LOG_CLOSED,
+       TXT_LOG_CREATE_FAILED,
+       TXT_LOG_LOCKED,
+       TXT_LOG_NOT_OPEN,
+       TXT_LOG_STARTED,
+       TXT_LOG_STOPPED,
+       TXT_LOG_LIST_HEADER,
+       TXT_LOG_LIST,
+       TXT_LOG_LIST_FOOTER,
+       TXT_WINDOWLOG_FILE,
+       TXT_WINDOWLOG_FILE_LOGGING,
+       TXT_LOG_NO_AWAY_MSGS,
+       TXT_LOG_AWAY_MSGS,
+
+       TXT_FILL_9,
+
+       TXT_MODULE_HEADER,
+       TXT_MODULE_LINE,
+        TXT_MODULE_FOOTER,
+       TXT_MODULE_ALREADY_LOADED,
+       TXT_MODULE_NOT_LOADED,
+       TXT_MODULE_LOAD_ERROR,
+       TXT_MODULE_INVALID,
+       TXT_MODULE_LOADED,
+       TXT_MODULE_UNLOADED,
+
+       TXT_FILL_10,
+
+       TXT_COMMAND_UNKNOWN,
+       TXT_COMMAND_AMBIGUOUS,
+       TXT_OPTION_UNKNOWN,
+       TXT_OPTION_AMBIGUOUS,
+       TXT_OPTION_MISSING_ARG,
+       TXT_NOT_ENOUGH_PARAMS,
+       TXT_NOT_CONNECTED,
+       TXT_NOT_JOINED,
+       TXT_CHAN_NOT_FOUND,
+       TXT_CHAN_NOT_SYNCED,
+        TXT_ILLEGAL_PROTO,
+       TXT_NOT_GOOD_IDEA,
+
+       TXT_FILL_11,
+
+       TXT_THEME_SAVED,
+       TXT_THEME_SAVE_FAILED,
+       TXT_THEME_NOT_FOUND,
+       TXT_THEME_CHANGED,
+       TXT_WINDOW_THEME,
+       TXT_WINDOW_THEME_DEFAULT,
+       TXT_WINDOW_THEME_CHANGED,
+       TXT_WINDOW_THEME_REMOVED,
+       TXT_FORMAT_TITLE,
+       TXT_FORMAT_SUBTITLE,
+       TXT_FORMAT_ITEM,
+
+       TXT_FILL_12,
+
+       TXT_IGNORED,
+       TXT_UNIGNORED,
+       TXT_IGNORE_NOT_FOUND,
+       TXT_IGNORE_NO_IGNORES,
+       TXT_IGNORE_HEADER,
+       TXT_IGNORE_LINE,
+       TXT_IGNORE_FOOTER,
+
+       TXT_FILL_13,
+
+        TXT_UNKNOWN_CHAT_PROTOCOL,
+        TXT_UNKNOWN_CHATNET,
+       TXT_NOT_TOGGLE,
+       TXT_PERL_ERROR,
+       TXT_BIND_KEY,
+       TXT_BIND_UNKNOWN_ID,
+       TXT_CONFIG_SAVED,
+       TXT_CONFIG_RELOADED,
+       TXT_CONFIG_MODIFIED,
+       TXT_GLIB_ERROR,
+        TXT_OVERWRITE_CONFIG,
+        TXT_SET_TITLE,
+        TXT_SET_ITEM,
+        TXT_SET_UNKNOWN,
+       TXT_SET_NOT_BOOLEAN,
+       TXT_TRANSLATION_NOT_FOUND,
+        TXT_TRANSLATION_FILE_ERROR
+};
+
+extern FORMAT_REC fecommon_core_formats[];
diff --git a/apps/irssi/src/fe-common/core/module.h b/apps/irssi/src/fe-common/core/module.h
new file mode 100644 (file)
index 0000000..203e3a3
--- /dev/null
@@ -0,0 +1,34 @@
+#include "common.h"
+
+#define MODULE_NAME "fe-common/core"
+
+typedef struct {
+       time_t time;
+       char *nick;
+
+       /* channel specific msg to/from me - this is actually a reference
+          count. it begins from `completion_keep_publics' and is decreased
+          every time some nick is added to lastmsgs list.
+
+          this is because of how the nick completion works. the same nick
+          is never in the lastmsgs list twice, but every time it's used
+          it's just moved to the beginning of the list. if this would be
+          just a boolean value the own-status would never be removed
+          from the nick if it didn't keep quiet for long enough.
+
+          so, the own-status is rememberd only for the last
+           `completion_keep_publics' lines */
+       int own;
+} LAST_MSG_REC;
+
+typedef struct {
+       /* /MSG completion: */
+       GSList *lastmsgs; /* list of nicks who sent you msg or
+                            to who you send msg */
+} MODULE_SERVER_REC;
+
+typedef struct {
+       /* nick completion: */
+       GSList *lastmsgs; /* list of nicks who sent latest msgs and
+                            list of nicks who you sent msgs to */
+} MODULE_CHANNEL_REC;
diff --git a/apps/irssi/src/fe-common/core/printtext.c b/apps/irssi/src/fe-common/core/printtext.c
new file mode 100644 (file)
index 0000000..1900ade
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ printtext.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "settings.h"
+
+#include "levels.h"
+#include "servers.h"
+
+#include "themes.h"
+#include "fe-windows.h"
+#include "printtext.h"
+
+static int beep_msg_level, beep_when_away, beep_when_window_active;
+
+static int signal_gui_print_text_finished;
+static int signal_print_starting;
+static int signal_print_text;
+static int signal_print_format;
+
+static int sending_print_starting;
+
+static void print_line(TEXT_DEST_REC *dest, const char *text);
+
+void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest,
+                                 int formatnum, va_list va)
+{
+       char *arglist[MAX_FORMAT_PARAMS];
+       char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
+       FORMAT_REC *formats;
+       THEME_REC *theme;
+       char *str;
+
+       theme = window_get_theme(dest->window);
+
+       formats = g_hash_table_lookup(default_formats, module);
+       format_read_arglist(va, &formats[formatnum],
+                           arglist, sizeof(arglist)/sizeof(char *),
+                           buffer, sizeof(buffer));
+
+       if (!sending_print_starting) {
+               sending_print_starting = TRUE;
+               signal_emit_id(signal_print_starting, 1, dest);
+                sending_print_starting = FALSE;
+       }
+
+       signal_emit_id(signal_print_format, 5, theme, module,
+                      dest, GINT_TO_POINTER(formatnum), arglist);
+
+       str = format_get_text_theme_charargs(theme, module, dest,
+                                            formatnum, arglist);
+       if (*str != '\0') print_line(dest, str);
+       g_free(str);
+}
+
+void printformat_module_dest(const char *module, TEXT_DEST_REC *dest,
+                            int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_dest_args(module, dest, formatnum, va);
+       va_end(va);
+}
+
+void printformat_module_args(const char *module, void *server,
+                            const char *target, int level,
+                            int formatnum, va_list va)
+{
+       TEXT_DEST_REC dest;
+
+       format_create_dest(&dest, server, target, level, NULL);
+       printformat_module_dest_args(module, &dest, formatnum, va);
+}
+
+void printformat_module(const char *module, void *server, const char *target,
+                       int level, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_args(module, server, target, level, formatnum, va);
+       va_end(va);
+}
+
+void printformat_module_window_args(const char *module, WINDOW_REC *window,
+                                   int level, int formatnum, va_list va)
+{
+       TEXT_DEST_REC dest;
+
+       format_create_dest(&dest, NULL, NULL, level, window);
+       printformat_module_dest_args(module, &dest, formatnum, va);
+}
+
+void printformat_module_window(const char *module, WINDOW_REC *window,
+                              int level, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_window_args(module, window, level, formatnum, va);
+       va_end(va);
+}
+
+void printformat_module_gui_args(const char *module, int formatnum, va_list va)
+{
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS];
+       char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
+       FORMAT_REC *formats;
+        char *str;
+
+       g_return_if_fail(module != NULL);
+
+        memset(&dest, 0, sizeof(dest));
+
+       formats = g_hash_table_lookup(default_formats, module);
+       format_read_arglist(va, &formats[formatnum],
+                           arglist, sizeof(arglist)/sizeof(char *),
+                           buffer, sizeof(buffer));
+
+       str = format_get_text_theme_charargs(window_get_theme(dest.window),
+                                            module, &dest,
+                                            formatnum, arglist);
+       if (*str != '\0') format_send_to_gui(&dest, str);
+       g_free(str);
+}
+
+void printformat_module_gui(const char *module, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+        printformat_module_gui_args(module, formatnum, va);
+       va_end(va);
+}
+
+static void print_line(TEXT_DEST_REC *dest, const char *text)
+{
+       char *str, *tmp, *stripped;
+
+       g_return_if_fail(dest != NULL);
+       g_return_if_fail(text != NULL);
+
+       tmp = format_get_level_tag(window_get_theme(dest->window), dest);
+       str = format_add_linestart(text, tmp);
+       g_free_not_null(tmp);
+
+       /* send both the formatted + stripped (for logging etc.) */
+       stripped = strip_codes(str);
+       signal_emit_id(signal_print_text, 3, dest, str, stripped);
+        g_free_and_null(dest->hilight_color);
+
+       g_free(str);
+        g_free(stripped);
+}
+
+/* append string to `out', expand newlines. */
+static void printtext_append_str(TEXT_DEST_REC *dest, GString *out,
+                                const char *str)
+{
+       while (*str != '\0') {
+               if (*str != '\n')
+                       g_string_append_c(out, *str);
+               else {
+                       print_line(dest, out->str);
+                       g_string_truncate(out, 0);
+               }
+               str++;
+       }
+}
+
+static char *printtext_get_args(TEXT_DEST_REC *dest, const char *str,
+                               va_list va)
+{
+       GString *out;
+       char *ret;
+
+       out = g_string_new(NULL);
+       for (; *str != '\0'; str++) {
+               if (*str != '%') {
+                       g_string_append_c(out, *str);
+                       continue;
+               }
+
+               if (*++str == '\0')
+                       break;
+
+               /* standard parameters */
+               switch (*str) {
+               case 's': {
+                       char *s = (char *) va_arg(va, char *);
+                       if (s && *s) printtext_append_str(dest, out, s);
+                       break;
+               }
+               case 'd': {
+                       int d = (int) va_arg(va, int);
+                       g_string_sprintfa(out, "%d", d);
+                       break;
+               }
+               case 'f': {
+                       double f = (double) va_arg(va, double);
+                       g_string_sprintfa(out, "%0.2f", f);
+                       break;
+               }
+               case 'u': {
+                       unsigned int d =
+                               (unsigned int) va_arg(va, unsigned int);
+                       g_string_sprintfa(out, "%u", d);
+                       break;
+                }
+               case 'l': {
+                       long d = (long) va_arg(va, long);
+
+                       if (*++str != 'd' && *str != 'u') {
+                               g_string_sprintfa(out, "%ld", d);
+                               str--;
+                       } else {
+                               if (*str == 'd')
+                                       g_string_sprintfa(out, "%ld", d);
+                               else
+                                       g_string_sprintfa(out, "%lu", d);
+                       }
+                       break;
+               }
+               default:
+                       if (!format_expand_styles(out, &str, &dest->flags)) {
+                               g_string_append_c(out, '%');
+                               g_string_append_c(out, *str);
+                       }
+                       break;
+               }
+       }
+
+       ret = out->str;
+       g_string_free(out, FALSE);
+       return ret;
+}
+
+static char *printtext_expand_formats(const char *str, int *flags)
+{
+       GString *out;
+       char *ret;
+
+       out = g_string_new(NULL);
+       for (; *str != '\0'; str++) {
+               if (*str != '%') {
+                       g_string_append_c(out, *str);
+                       continue;
+               }
+
+               if (*++str == '\0')
+                       break;
+
+               if (!format_expand_styles(out, &str, flags)) {
+                       g_string_append_c(out, '%');
+                       g_string_append_c(out, *str);
+               }
+       }
+
+       ret = out->str;
+       g_string_free(out, FALSE);
+       return ret;
+}
+
+static void printtext_dest_args(TEXT_DEST_REC *dest, const char *text, va_list va)
+{
+       char *str;
+
+       if (!sending_print_starting) {
+               sending_print_starting = TRUE;
+               signal_emit_id(signal_print_starting, 1, dest);
+                sending_print_starting = FALSE;
+       }
+
+       str = printtext_get_args(dest, text, va);
+       print_line(dest, str);
+       g_free(str);
+}
+
+void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...)
+{
+       va_list va;
+
+       va_start(va, text);
+       printtext_dest_args(dest, text, va);
+       va_end(va);
+}
+
+/* Write text to target - convert color codes */
+void printtext(void *server, const char *target, int level, const char *text, ...)
+{
+       TEXT_DEST_REC dest;
+       va_list va;
+
+       g_return_if_fail(text != NULL);
+
+        format_create_dest(&dest, server, target, level, NULL);
+
+       va_start(va, text);
+       printtext_dest_args(&dest, text, va);
+       va_end(va);
+}
+
+/* Like printtext(), but don't handle %s etc. */
+void printtext_string(void *server, const char *target, int level, const char *text)
+{
+       TEXT_DEST_REC dest;
+        char *str;
+
+       g_return_if_fail(text != NULL);
+
+        format_create_dest(&dest, server, target, level, NULL);
+
+       if (!sending_print_starting) {
+               sending_print_starting = TRUE;
+               signal_emit_id(signal_print_starting, 1, dest);
+                sending_print_starting = FALSE;
+       }
+
+        str = printtext_expand_formats(text, &dest.flags);
+       print_line(&dest, str);
+        g_free(str);
+}
+
+/* Like printtext_window(), but don't handle %s etc. */
+void printtext_string_window(WINDOW_REC *window, int level, const char *text)
+{
+       TEXT_DEST_REC dest;
+        char *str;
+
+       g_return_if_fail(text != NULL);
+
+       format_create_dest(&dest, NULL, NULL, level,
+                          window != NULL ? window : active_win);
+
+       if (!sending_print_starting) {
+               sending_print_starting = TRUE;
+               signal_emit_id(signal_print_starting, 1, dest);
+                sending_print_starting = FALSE;
+       }
+
+        str = printtext_expand_formats(text, &dest.flags);
+       print_line(&dest, str);
+        g_free(str);
+}
+
+void printtext_window(WINDOW_REC *window, int level, const char *text, ...)
+{
+       TEXT_DEST_REC dest;
+       va_list va;
+
+       g_return_if_fail(text != NULL);
+
+       format_create_dest(&dest, NULL, NULL, level,
+                          window != NULL ? window : active_win);
+
+       va_start(va, text);
+       printtext_dest_args(&dest, text, va);
+       va_end(va);
+}
+
+void printtext_gui(const char *text)
+{
+       TEXT_DEST_REC dest;
+        char *str;
+
+       g_return_if_fail(text != NULL);
+
+        memset(&dest, 0, sizeof(dest));
+
+       str = printtext_expand_formats(text, &dest.flags);
+       format_send_to_gui(&dest, str);
+       g_free(str);
+}
+
+static void msg_beep_check(TEXT_DEST_REC *dest)
+{
+       if (dest->level != 0 && (dest->level & MSGLEVEL_NO_ACT) == 0 &&
+           (beep_msg_level & dest->level) &&
+           (beep_when_away || (dest->server != NULL &&
+                               !dest->server->usermode_away)) &&
+           (beep_when_window_active || dest->window != active_win)) {
+                signal_emit("beep", 0);
+       }
+}
+
+static void sig_print_text(TEXT_DEST_REC *dest, const char *text)
+{
+       char *str, *tmp;
+
+       g_return_if_fail(dest != NULL);
+       g_return_if_fail(dest->window != NULL);
+       g_return_if_fail(text != NULL);
+
+       msg_beep_check(dest);
+
+       dest->window->last_line = time(NULL);
+
+       /* add timestamp/server tag here - if it's done in print_line()
+          it would be written to log files too */
+       tmp = format_get_line_start(window_get_theme(dest->window),
+                                   dest, time(NULL));
+       str = format_add_linestart(text, tmp);
+       g_free_not_null(tmp);
+
+       format_send_to_gui(dest, str);
+       g_free(str);
+
+       signal_emit_id(signal_gui_print_text_finished, 1, dest->window);
+}
+
+void printtext_multiline(void *server, const char *target, int level,
+                        const char *format, const char *text)
+{
+       char **lines, **tmp;
+
+       g_return_if_fail(format != NULL);
+       g_return_if_fail(text != NULL);
+
+       lines = g_strsplit(text, "\n", -1);
+        for (tmp = lines; *tmp != NULL; tmp++)
+               printtext(NULL, NULL, level, format, *tmp);
+       g_strfreev(lines);
+}
+
+static void sig_gui_dialog(const char *type, const char *text)
+{
+       char *format;
+
+       if (g_strcasecmp(type, "warning") == 0)
+               format = "%_Warning:%_ %s";
+       else if (g_strcasecmp(type, "error") == 0)
+               format = "%_Error:%_ %s";
+       else
+               format = "%s";
+
+        printtext_multiline(NULL, NULL, MSGLEVEL_NEVER, format, text);
+}
+
+static void read_settings(void)
+{
+       beep_msg_level = level2bits(settings_get_str("beep_msg_level"));
+       beep_when_away = settings_get_bool("beep_when_away");
+        beep_when_window_active = settings_get_bool("beep_when_window_active");
+}
+
+void printtext_init(void)
+{
+       sending_print_starting = FALSE;
+       signal_gui_print_text_finished = signal_get_uniq_id("gui print text finished");
+       signal_print_starting = signal_get_uniq_id("print starting");
+       signal_print_text = signal_get_uniq_id("print text");
+       signal_print_format = signal_get_uniq_id("print format");
+
+       read_settings();
+       signal_add("print text", (SIGNAL_FUNC) sig_print_text);
+       signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void printtext_deinit(void)
+{
+       signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
+       signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/printtext.h b/apps/irssi/src/fe-common/core/printtext.h
new file mode 100644 (file)
index 0000000..463f357
--- /dev/null
@@ -0,0 +1,116 @@
+#ifndef __PRINTTEXT_H
+#define __PRINTTEXT_H
+
+#include "fe-windows.h"
+#include "formats.h"
+
+void printformat_module(const char *module, void *server, const char *target, int level, int formatnum, ...);
+void printformat_module_window(const char *module, WINDOW_REC *window, int level, int formatnum, ...);
+void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, int formatnum, ...);
+
+void printformat_module_args(const char *module, void *server, const char *target, int level, int formatnum, va_list va);
+void printformat_module_window_args(const char *module, WINDOW_REC *window, int level, int formatnum, va_list va);
+void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, int formatnum, va_list va);
+
+void printtext(void *server, const char *target, int level, const char *text, ...);
+void printtext_string(void *server, const char *target, int level, const char *text);
+void printtext_string_window(WINDOW_REC *window, int level, const char *text);
+void printtext_window(WINDOW_REC *window, int level, const char *text, ...);
+void printtext_multiline(void *server, const char *target, int level, const char *format, const char *text);
+void printtext_dest(TEXT_DEST_REC *dest, const char *text, ...);
+
+/* only GUI should call these - used for printing text to somewhere else
+   than windows */
+void printtext_gui(const char *text);
+void printformat_module_gui(const char *module, int formatnum, ...);
+void printformat_module_gui_args(const char *module, int formatnum, va_list va);
+
+void printtext_init(void);
+void printtext_deinit(void);
+
+/* printformat(...) = printformat_format(MODULE_NAME, ...)
+
+   Could this be any harder? :) With GNU C compiler and C99 compilers,
+   use #define. With others use either inline functions if they are
+   supported or static functions if they are not..
+ */
+#if defined (__GNUC__) && !defined (__STRICT_ANSI__)
+/* GCC */
+#  define printformat(server, target, level, formatnum...) \
+       printformat_module(MODULE_NAME, server, target, level, ##formatnum)
+#  define printformat_window(window, level, formatnum...) \
+       printformat_module_window(MODULE_NAME, window, level, ##formatnum)
+#  define printformat_dest(dest, formatnum...) \
+       printformat_module_dest(MODULE_NAME, dest, ##formatnum)
+#  define printformat_gui(formatnum...) \
+       printformat_module_gui(MODULE_NAME, ##formatnum)
+#elif defined (_ISOC99_SOURCE)
+/* C99 */
+#  define printformat(server, target, level, formatnum, ...) \
+       printformat_module(MODULE_NAME, server, target, level, formatnum, __VA_ARGS__)
+#  define printformat_window(window, level, formatnum, ...) \
+       printformat_module_window(MODULE_NAME, window, level, formatnum, __VA_ARGS__)
+#  define printformat_dest(dest, formatnum, ...) \
+       printformat_module_dest(MODULE_NAME, dest, formatnum, __VA_ARGS__)
+#  define printformat_gui(formatnum, ...) \
+       printformat_module_gui(MODULE_NAME, formatnum, __VA_ARGS__)
+#else
+/* inline/static */
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void printformat(void *server, const char *target, int level, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_args(MODULE_NAME, server, target, level, formatnum, va);
+       va_end(va);
+}
+
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void printformat_window(WINDOW_REC *window, int level, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_window_args(MODULE_NAME, window, level, formatnum, va);
+       va_end(va);
+}
+
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void printformat_dest(TEXT_DEST_REC *dest, int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_dest_args(MODULE_NAME, dest, formatnum, va);
+       va_end(va);
+}
+
+#ifdef G_CAN_INLINE
+G_INLINE_FUNC
+#else
+static
+#endif
+void printformat_gui(int formatnum, ...)
+{
+       va_list va;
+
+       va_start(va, formatnum);
+       printformat_module_gui_args(MODULE_NAME, formatnum, va);
+       va_end(va);
+}
+#endif
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/themes.c b/apps/irssi/src/fe-common/core/themes.c
new file mode 100644 (file)
index 0000000..c80c9e2
--- /dev/null
@@ -0,0 +1,1259 @@
+/*
+ themes.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+#include "misc.h"
+#include "special-vars.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "themes.h"
+#include "printtext.h"
+
+#include "default-theme.h"
+
+GSList *themes;
+THEME_REC *current_theme;
+GHashTable *default_formats;
+
+static int init_finished;
+static char *init_errors;
+
+static int theme_read(THEME_REC *theme, const char *path, const char *data);
+
+THEME_REC *theme_create(const char *path, const char *name)
+{
+       THEME_REC *rec;
+
+       g_return_val_if_fail(path != NULL, NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+
+       rec = g_new0(THEME_REC, 1);
+       rec->path = g_strdup(path);
+       rec->name = g_strdup(name);
+       rec->abstracts = g_hash_table_new((GHashFunc) g_str_hash,
+                                         (GCompareFunc) g_str_equal);
+       rec->modules = g_hash_table_new((GHashFunc) g_istr_hash,
+                                       (GCompareFunc) g_istr_equal);
+       themes = g_slist_append(themes, rec);
+       signal_emit("theme created", 1, rec);
+
+       return rec;
+}
+
+static void theme_abstract_destroy(char *key, char *value)
+{
+       g_free(key);
+        g_free(value);
+}
+
+static void theme_module_destroy(const char *key, MODULE_THEME_REC *rec)
+{
+       int n;
+
+       for (n = 0; n < rec->count; n++) {
+               g_free_not_null(rec->formats[n]);
+               g_free_not_null(rec->expanded_formats[n]);
+       }
+       g_free(rec->formats);
+       g_free(rec->expanded_formats);
+
+       g_free(rec->name);
+       g_free(rec);
+}
+
+void theme_destroy(THEME_REC *rec)
+{
+       themes = g_slist_remove(themes, rec);
+
+       signal_emit("theme destroyed", 1, rec);
+
+       g_hash_table_foreach(rec->abstracts, (GHFunc) theme_abstract_destroy, NULL);
+       g_hash_table_destroy(rec->abstracts);
+       g_hash_table_foreach(rec->modules, (GHFunc) theme_module_destroy, NULL);
+       g_hash_table_destroy(rec->modules);
+
+       g_slist_foreach(rec->replace_values, (GFunc) g_free, NULL);
+       g_slist_free(rec->replace_values);
+
+       g_free(rec->path);
+       g_free(rec->name);
+       g_free(rec);
+}
+
+static char *theme_replace_expand(THEME_REC *theme, int index,
+                                 char default_fg, char default_bg,
+                                 char *last_fg, char *last_bg,
+                                 char chr, int flags)
+{
+       GSList *rec;
+       char *ret, *abstract, data[2];
+
+       rec = g_slist_nth(theme->replace_values, index);
+       g_return_val_if_fail(rec != NULL, NULL);
+
+       data[0] = chr; data[1] = '\0';
+
+       abstract = rec->data;
+       abstract = theme_format_expand_data(theme, (const char **) &abstract,
+                                           default_fg, default_bg,
+                                           last_fg, last_bg, flags);
+       ret = parse_special_string(abstract, NULL, NULL, data, NULL,
+                                  PARSE_FLAG_ONLY_ARGS);
+       g_free(abstract);
+       return ret;
+}
+
+static const char *fgcolorformats = "nkrgybmpcwKRGYBMPCW";
+static const char *bgcolorformats = "n01234567";
+
+#define IS_FGCOLOR_FORMAT(c) \
+        ((c) != '\0' && strchr(fgcolorformats, c) != NULL)
+#define IS_BGCOLOR_FORMAT(c) \
+        ((c) != '\0' && strchr(bgcolorformats, c) != NULL)
+
+/* append "variable" part in $variable, ie. not the contents of the variable */
+static void theme_format_append_variable(GString *str, const char **format)
+{
+       const char *orig;
+       char *value, *args[1] = { NULL };
+       int free_ret;
+
+       orig = *format;
+       (*format)++;
+
+       value = parse_special((char **) format, NULL, NULL,
+                             args, &free_ret, NULL, PARSE_FLAG_ONLY_ARGS);
+       if (free_ret) g_free(value);
+       (*format)++;
+
+       /* append the variable name */
+       value = g_strndup(orig, (int) (*format-orig));
+       g_string_append(str, value);
+       g_free(value);
+}
+
+/* append next "item", either a character, $variable or %format */
+static void theme_format_append_next(THEME_REC *theme, GString *str,
+                                    const char **format,
+                                    char default_fg, char default_bg,
+                                    char *last_fg, char *last_bg,
+                                    int flags)
+{
+       int index;
+       unsigned char chr;
+
+       chr = **format;
+       if ((chr == '$' || chr == '%') &&
+           (*format)[1] == '\0') {
+               /* last char, always append */
+               g_string_append_c(str, chr);
+               (*format)++;
+                return;
+       }
+
+       if (chr == '$') {
+               /* $variable .. we'll always need to skip this, since it
+                  may contain characters that are in replace chars. */
+               theme_format_append_variable(str, format);
+               return;
+       }
+
+       if (**format == '%') {
+               /* format */
+               (*format)++;
+               if (**format != '{' && **format != '}') {
+                        chr = **format;
+                       if (**format == 'n') {
+                               /* %n = change to default color */
+                               g_string_append(str, "%n");
+
+                               if (default_bg != 'n') {
+                                       g_string_append_c(str, '%');
+                                       g_string_append_c(str, default_bg);
+                               }
+                               if (default_fg != 'n') {
+                                       g_string_append_c(str, '%');
+                                       g_string_append_c(str, default_fg);
+                               }
+
+                               *last_fg = default_fg;
+                               *last_bg = default_bg;
+                       } else {
+                               if (IS_FGCOLOR_FORMAT(chr))
+                                       *last_fg = chr;
+                               if (IS_BGCOLOR_FORMAT(chr))
+                                       *last_bg = chr;
+                               g_string_append_c(str, '%');
+                               g_string_append_c(str, chr);
+                       }
+                       (*format)++;
+                       return;
+               }
+
+               /* %{ or %} gives us { or } char - keep the % char
+                  though to make sure {} isn't treated as abstract */
+               g_string_append_c(str, '%');
+               chr = **format;
+       }
+
+       index = (flags & EXPAND_FLAG_IGNORE_REPLACES) ? -1 :
+               theme->replace_keys[(int) chr];
+       if (index == -1)
+               g_string_append_c(str, chr);
+       else {
+               char *value;
+
+               value = theme_replace_expand(theme, index,
+                                            default_fg, default_bg,
+                                            last_fg, last_bg, chr, flags);
+               g_string_append(str, value);
+               g_free(value);
+       }
+
+        (*format)++;
+}
+
+/* returns TRUE if data is empty, or the data is a $variable which is empty */
+static int data_is_empty(const char **data)
+{
+       /* since we don't know the real argument list, assume there's always
+          an argument in them */
+       char *arglist[] = {
+               "x", "x", "x", "x", "x", "x","x", "x", "x", "x",
+               NULL
+       };
+       const char *p;
+       char *ret;
+        int free_ret, empty;
+
+        p = *data;
+       while (*p == ' ') p++;
+
+       if (*p == '}') {
+                /* empty */
+                *data = p+1;
+                return TRUE;
+       }
+
+       if (*p != '$') {
+                /* not empty */
+               return FALSE;
+       }
+
+       /* variable - check if it's empty */
+        p++;
+       ret = parse_special((char **) &p,
+                           active_win == NULL ? NULL : active_win->active_server,
+                           active_win == NULL ? NULL : active_win->active,
+                           arglist, &free_ret, NULL, 0);
+        p++;
+
+       while (*p == ' ') p++;
+       empty = *p == '}' && (ret == NULL || *ret == '\0');
+        if (free_ret) g_free(ret);
+
+       if (empty) {
+               /* empty */
+               *data = p+1;
+                return TRUE;
+       }
+
+        return FALSE;
+}
+
+/* expand a single {abstract ...data... } */
+static char *theme_format_expand_abstract(THEME_REC *theme,
+                                         const char **formatp,
+                                         char default_fg, char default_bg,
+                                         int flags)
+{
+       const char *p, *format;
+       char *abstract, *data, *ret;
+       int len;
+
+       format = *formatp;
+
+       /* get abstract name first */
+       p = format;
+       while (*p != '\0' && *p != ' ' &&
+              *p != '{' && *p != '}') p++;
+       if (*p == '\0' || p == format)
+               return NULL; /* error */
+
+       len = (int) (p-format);
+       abstract = g_strndup(format, len);
+
+       /* skip the following space, if there's any more spaces they're
+          treated as arguments */
+       if (*p == ' ') {
+               len++;
+               if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) {
+                       *formatp = p;
+                       g_free(abstract);
+                       return NULL;
+               }
+       }
+       *formatp = format+len;
+
+       /* get the abstract data */
+       data = g_hash_table_lookup(theme->abstracts, abstract);
+       g_free(abstract);
+       if (data == NULL) {
+               /* unknown abstract, just display the data */
+               data = "$0-";
+       }
+       abstract = g_strdup(data);
+
+       /* we'll need to get the data part. it may contain
+          more abstracts, they are automatically expanded. */
+       data = theme_format_expand_data(theme, formatp, default_fg, default_bg,
+                                       NULL, NULL, flags);
+       len = strlen(data);
+
+       if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') {
+               /* ends with $<digit> .. this breaks things if next
+                  character is digit or '-' */
+                char digit, *tmp;
+
+               tmp = data;
+               digit = tmp[len-1];
+               tmp[len-1] = '\0';
+
+               data = g_strdup_printf("%s{%c}", tmp, digit);
+               g_free(tmp);
+       }
+
+       ret = parse_special_string(abstract, NULL, NULL, data, NULL,
+                                  PARSE_FLAG_ONLY_ARGS);
+       g_free(abstract);
+        g_free(data);
+       abstract = ret;
+
+       /* abstract may itself contain abstracts or replaces */
+       p = abstract;
+       ret = theme_format_expand_data(theme, &p, default_fg, default_bg,
+                                      &default_fg, &default_bg,
+                                      flags | EXPAND_FLAG_LASTCOLOR_ARG);
+       g_free(abstract);
+       return ret;
+}
+
+/* expand the data part in {abstract data} */
+char *theme_format_expand_data(THEME_REC *theme, const char **format,
+                              char default_fg, char default_bg,
+                              char *save_last_fg, char *save_last_bg,
+                              int flags)
+{
+       GString *str;
+       char *ret, *abstract;
+       char last_fg, last_bg;
+        int recurse_flags;
+
+       last_fg = default_fg;
+       last_bg = default_bg;
+        recurse_flags = flags & EXPAND_FLAG_RECURSIVE_MASK;
+
+       str = g_string_new(NULL);
+       while (**format != '\0') {
+               if ((flags & EXPAND_FLAG_ROOT) == 0 && **format == '}') {
+                       /* ignore } if we're expanding original string */
+                       (*format)++;
+                       break;
+               }
+
+               if (**format != '{') {
+                       if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) &&
+                           **format == '$' && (*format)[1] == '0') {
+                               /* save the color before $0 ..
+                                  this is for the %n replacing */
+                               if (save_last_fg != NULL) {
+                                       *save_last_fg = last_fg;
+                                       save_last_fg = NULL;
+                               }
+                               if (save_last_bg != NULL) {
+                                       *save_last_bg = last_bg;
+                                       save_last_bg = NULL;
+                               }
+                       }
+
+                       theme_format_append_next(theme, str, format,
+                                                default_fg, default_bg,
+                                                &last_fg, &last_bg,
+                                                recurse_flags);
+                       continue;
+               }
+
+               (*format)++;
+               if (**format == '\0' || **format == '}')
+                       break; /* error */
+
+               /* get a single {...} */
+               abstract = theme_format_expand_abstract(theme, format,
+                                                       last_fg, last_bg,
+                                                       recurse_flags);
+               if (abstract != NULL) {
+                       g_string_append(str, abstract);
+                       g_free(abstract);
+               }
+       }
+
+       if ((flags & EXPAND_FLAG_LASTCOLOR_ARG) == 0) {
+               /* save the last color */
+               if (save_last_fg != NULL)
+                       *save_last_fg = last_fg;
+               if (save_last_bg != NULL)
+                       *save_last_bg = last_bg;
+       }
+
+       ret = str->str;
+        g_string_free(str, FALSE);
+        return ret;
+}
+
+#define IS_OLD_FORMAT(code, last_fg, last_bg) \
+       (((code) == 'n' && (last_fg) == 'n' && (last_bg) == 'n') || \
+       ((code) != 'n' && ((code) == (last_fg) || (code) == (last_bg))))
+
+static char *theme_format_compress_colors(THEME_REC *theme, const char *format)
+{
+       GString *str;
+       char *ret, last_fg, last_bg;
+
+       str = g_string_new(NULL);
+
+       last_fg = last_bg = '\0';
+       while (*format != '\0') {
+               if (*format == '$') {
+                        /* $variable, skrip it entirely */
+                       theme_format_append_variable(str, &format);
+                        last_fg = last_bg = '\0';
+               } else if (*format != '%') {
+                       /* a normal character */
+                       g_string_append_c(str, *format);
+                       format++;
+               } else {
+                       /* %format */
+                       format++;
+                       if (IS_OLD_FORMAT(*format, last_fg, last_bg)) {
+                               /* active color set again */
+                       } else if (IS_FGCOLOR_FORMAT(*format) &&
+                                  format[1] == '%' &&
+                                  IS_FGCOLOR_FORMAT(format[2]) &&
+                                  (*format != 'n' || format[2] == 'n')) {
+                               /* two fg colors in a row. bg colors are
+                                  so rare that we don't bother checking
+                                  them */
+                       } else {
+                               /* some format, add it */
+                               g_string_append_c(str, '%');
+                               g_string_append_c(str, *format);
+
+                               if (IS_FGCOLOR_FORMAT(*format))
+                                       last_fg = *format;
+                               if (IS_BGCOLOR_FORMAT(*format))
+                                       last_bg = *format;
+                       }
+                       format++;
+               }
+       }
+
+       ret = str->str;
+        g_string_free(str, FALSE);
+        return ret;
+}
+
+char *theme_format_expand(THEME_REC *theme, const char *format)
+{
+       char *data, *ret;
+
+       g_return_val_if_fail(theme != NULL, NULL);
+       g_return_val_if_fail(format != NULL, NULL);
+
+       data = theme_format_expand_data(theme, &format, 'n', 'n', NULL, NULL,
+                                       EXPAND_FLAG_ROOT);
+       ret = theme_format_compress_colors(theme, data);
+        g_free(data);
+       return ret;
+}
+
+static MODULE_THEME_REC *theme_module_create(THEME_REC *theme, const char *module)
+{
+       MODULE_THEME_REC *rec;
+       FORMAT_REC *formats;
+
+       rec = g_hash_table_lookup(theme->modules, module);
+       if (rec != NULL) return rec;
+
+       formats = g_hash_table_lookup(default_formats, module);
+        g_return_val_if_fail(formats != NULL, NULL);
+
+       rec = g_new0(MODULE_THEME_REC, 1);
+       rec->name = g_strdup(module);
+
+       for (rec->count = 0; formats[rec->count].def != NULL; rec->count++) ;
+       rec->formats = g_new0(char *, rec->count);
+       rec->expanded_formats = g_new0(char *, rec->count);
+
+       g_hash_table_insert(theme->modules, rec->name, rec);
+       return rec;
+}
+
+static void theme_read_replaces(CONFIG_REC *config, THEME_REC *theme)
+{
+       GSList *tmp;
+       CONFIG_NODE *node;
+       const char *p;
+        int index;
+
+        /* reset replace keys */
+       for (index = 0; index < 256; index++)
+                theme->replace_keys[index] = -1;
+       index = 0;
+
+       node = config_node_traverse(config, "replaces", FALSE);
+       if (node == NULL || node->type !=  NODE_TYPE_BLOCK) return;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               node = tmp->data;
+
+               if (node->key != NULL && node->value != NULL) {
+                       for (p = node->key; *p != '\0'; p++)
+                                theme->replace_keys[(int) *p] = index;
+
+                       theme->replace_values =
+                               g_slist_append(theme->replace_values,
+                                              g_strdup(node->value));
+                        index++;
+               }
+       }
+}
+
+static void theme_read_abstracts(CONFIG_REC *config, THEME_REC *theme)
+{
+       GSList *tmp;
+       CONFIG_NODE *node;
+        gpointer oldkey, oldvalue;
+
+       node = config_node_traverse(config, "abstracts", FALSE);
+       if (node == NULL || node->type !=  NODE_TYPE_BLOCK) return;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               node = tmp->data;
+
+               if (node->key == NULL || node->value == NULL)
+                       continue;
+
+               if (g_hash_table_lookup_extended(theme->abstracts, node->key,
+                                                &oldkey, &oldvalue)) {
+                        /* new values override old ones */
+                        g_hash_table_remove(theme->abstracts, oldkey);
+                       g_free(oldkey);
+                       g_free(oldvalue);
+               }
+
+               g_hash_table_insert(theme->abstracts, g_strdup(node->key),
+                                   g_strdup(node->value));
+       }
+}
+
+static void theme_set_format(THEME_REC *theme, MODULE_THEME_REC *rec,
+                            const char *module,
+                            const char *key, const char *value)
+{
+       int num;
+
+        num = format_find_tag(module, key);
+       if (num != -1) {
+               rec->formats[num] = g_strdup(value);
+               rec->expanded_formats[num] = theme_format_expand(theme, value);
+       }
+}
+
+static void theme_read_formats(THEME_REC *theme, const char *module,
+                              CONFIG_REC *config, MODULE_THEME_REC *rec)
+{
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+       node = config_node_traverse(config, "formats", FALSE);
+       if (node == NULL) return;
+       node = config_node_section(node, module, -1);
+       if (node == NULL) return;
+
+       for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
+               node = tmp->data;
+
+               if (node->key != NULL && node->value != NULL) {
+                       theme_set_format(theme, rec, module,
+                                        node->key, node->value);
+               }
+       }
+}
+
+static void theme_init_module(THEME_REC *theme, const char *module,
+                             CONFIG_REC *config)
+{
+       MODULE_THEME_REC *rec;
+       FORMAT_REC *formats;
+       int n;
+
+       formats = g_hash_table_lookup(default_formats, module);
+       g_return_if_fail(formats != NULL);
+
+       rec = theme_module_create(theme, module);
+
+       if (config != NULL)
+               theme_read_formats(theme, module, config, rec);
+
+       /* expand the remaining formats */
+       for (n = 0; n < rec->count; n++) {
+               if (rec->expanded_formats[n] == NULL) {
+                       rec->expanded_formats[n] =
+                               theme_format_expand(theme, formats[n].def);
+               }
+       }
+}
+
+static void sig_print_errors(void)
+{
+       init_finished = TRUE;
+
+       if (init_errors != NULL) {
+               signal_emit("gui dialog", 2, "error", init_errors);
+                g_free(init_errors);
+       }
+}
+
+static void theme_read_module(THEME_REC *theme, const char *module)
+{
+       CONFIG_REC *config;
+
+       config = config_open(theme->path, -1);
+       if (config != NULL)
+               config_parse(config);
+
+       theme_init_module(theme, module, config);
+
+       if (config != NULL) config_close(config);
+}
+
+static void themes_read_module(const char *module)
+{
+        g_slist_foreach(themes, (GFunc) theme_read_module, (void *) module);
+}
+
+static void theme_remove_module(THEME_REC *theme, const char *module)
+{
+       MODULE_THEME_REC *rec;
+
+       rec = g_hash_table_lookup(theme->modules, module);
+       if (rec == NULL) return;
+
+       g_hash_table_remove(theme->modules, module);
+       theme_module_destroy(module, rec);
+}
+
+static void themes_remove_module(const char *module)
+{
+        g_slist_foreach(themes, (GFunc) theme_remove_module, (void *) module);
+}
+
+void theme_register_module(const char *module, FORMAT_REC *formats)
+{
+       if (g_hash_table_lookup(default_formats, module) != NULL)
+               return;
+
+        g_hash_table_insert(default_formats, g_strdup(module), formats);
+       themes_read_module(module);
+}
+
+void theme_unregister_module(const char *module)
+{
+       gpointer key, value;
+
+       if (default_formats == NULL)
+               return; /* already uninitialized */
+
+       if (!g_hash_table_lookup_extended(default_formats, module, &key, &value))
+               return;
+
+       g_hash_table_remove(default_formats, key);
+       g_free(key);
+
+       themes_remove_module(module);
+}
+
+static THEME_REC *theme_find(const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = themes; tmp != NULL; tmp = tmp->next) {
+               THEME_REC *rec = tmp->data;
+
+               if (g_strcasecmp(rec->name, name) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static void window_themes_update(void)
+{
+       GSList *tmp;
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->theme_name != NULL)
+                        rec->theme = theme_load(rec->theme_name);
+       }
+}
+
+THEME_REC *theme_load(const char *setname)
+{
+       THEME_REC *theme, *oldtheme;
+       struct stat statbuf;
+       char *fname, *name, *p;
+
+        name = g_strdup(setname);
+       p = strrchr(name, '.');
+       if (p != NULL && strcmp(p, ".theme") == 0) {
+               /* remove the trailing .theme */
+                *p = '\0';
+       }
+
+       theme = theme_find(name);
+
+       /* check home dir */
+       fname = g_strdup_printf("%s/%s.theme", get_irssi_dir(), name);
+       if (stat(fname, &statbuf) != 0) {
+               /* check global config dir */
+               g_free(fname);
+               fname = g_strdup_printf(THEMESDIR"/%s.theme", name);
+               if (stat(fname, &statbuf) != 0) {
+                       /* theme not found */
+                       g_free(fname);
+                       g_free(name);
+                       return theme; /* use the one in memory if possible */
+               }
+       }
+
+       if (theme != NULL && theme->last_modify == statbuf.st_mtime) {
+               /* theme not modified, use the one already in memory */
+               g_free(fname);
+               g_free(name);
+               return theme;
+       }
+
+        oldtheme = theme;
+       theme = theme_create(fname, name);
+       theme->last_modify = statbuf.st_mtime;
+       if (!theme_read(theme, theme->path, NULL)) {
+                /* error reading .theme file */
+               theme_destroy(theme);
+               theme = NULL;
+       }
+
+       if (oldtheme != NULL && theme != NULL) {
+               theme_destroy(oldtheme);
+               window_themes_update();
+       }
+
+       g_free(fname);
+       g_free(name);
+       return theme;
+}
+
+typedef struct {
+        THEME_REC *theme;
+       CONFIG_REC *config;
+} THEME_READ_REC;
+
+static void theme_read_modules(const char *module, void *value,
+                              THEME_READ_REC *rec)
+{
+       theme_init_module(rec->theme, module, rec->config);
+}
+
+static void read_error(const char *str)
+{
+       char *old;
+
+       if (init_finished)
+                printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
+       else if (init_errors == NULL)
+               init_errors = g_strdup(str);
+       else {
+                old = init_errors;
+               init_errors = g_strconcat(init_errors, "\n", str, NULL);
+                g_free(old);
+       }
+}
+
+static int theme_read(THEME_REC *theme, const char *path, const char *data)
+{
+       CONFIG_REC *config;
+       THEME_READ_REC rec;
+        char *str;
+
+       config = config_open(data == NULL ? path : NULL, -1) ;
+       if (config == NULL) {
+               /* didn't exist or no access? */
+               str = g_strdup_printf("Error reading theme file %s: %s",
+                                     path, g_strerror(errno));
+               read_error(str);
+               g_free(str);
+               return FALSE;
+       }
+
+       if (data != NULL)
+               config_parse_data(config, data, "internal");
+        else
+               config_parse(config);
+
+       if (config_last_error(config) != NULL) {
+               str = g_strdup_printf("Ignored errors in theme %s:\n%s",
+                                     theme->name, config_last_error(config));
+               read_error(str);
+                g_free(str);
+       }
+
+       theme->default_color =
+               config_get_int(config, NULL, "default_color", -1);
+       /* FIXME: remove after 0.7.99 */
+       if (theme->default_color == 0 &&
+           config_get_int(config, NULL, "default_real_color", -1) != -1)
+                theme->default_color = -1;
+       theme_read_replaces(config, theme);
+
+       if (data == NULL) {
+               /* get the default abstracts from default theme. */
+               CONFIG_REC *default_config;
+
+               default_config = config_open(NULL, -1);
+               config_parse_data(default_config, default_theme, "internal");
+               theme_read_abstracts(default_config, theme);
+               config_close(default_config);
+       }
+       theme_read_abstracts(config, theme);
+
+       rec.theme = theme;
+       rec.config = config;
+       g_hash_table_foreach(default_formats,
+                            (GHFunc) theme_read_modules, &rec);
+       config_close(config);
+
+        return TRUE;
+}
+
+typedef struct {
+       char *name;
+       char *short_name;
+} THEME_SEARCH_REC;
+
+static int theme_search_equal(THEME_SEARCH_REC *r1, THEME_SEARCH_REC *r2)
+{
+       return g_strcasecmp(r1->short_name, r2->short_name);
+}
+
+static void theme_get_modules(char *module, FORMAT_REC *formats, GSList **list)
+{
+       THEME_SEARCH_REC *rec;
+
+       rec = g_new(THEME_SEARCH_REC, 1);
+       rec->name = module;
+       rec->short_name = strrchr(module, '/');
+       if (rec->short_name != NULL)
+               rec->short_name++; else rec->short_name = module;
+       *list = g_slist_insert_sorted(*list, rec, (GCompareFunc) theme_search_equal);
+}
+
+static GSList *get_sorted_modules(void)
+{
+       GSList *list;
+
+       list = NULL;
+       g_hash_table_foreach(default_formats, (GHFunc) theme_get_modules, &list);
+       return list;
+}
+
+static THEME_SEARCH_REC *theme_search(GSList *list, const char *module)
+{
+       THEME_SEARCH_REC *rec;
+
+       while (list != NULL) {
+               rec = list->data;
+
+               if (g_strcasecmp(rec->short_name, module) == 0)
+                       return rec;
+               list = list->next;
+       }
+
+       return NULL;
+}
+
+static void theme_show(THEME_SEARCH_REC *rec, const char *key, const char *value, int reset)
+{
+       MODULE_THEME_REC *theme;
+       FORMAT_REC *formats;
+       const char *text, *last_title;
+       int n, first;
+
+       formats = g_hash_table_lookup(default_formats, rec->name);
+       theme = g_hash_table_lookup(current_theme->modules, rec->name);
+
+       last_title = NULL; first = TRUE;
+       for (n = 1; formats[n].def != NULL; n++) {
+               text = theme != NULL && theme->formats[n] != NULL ?
+                       theme->formats[n] : formats[n].def;
+
+               if (formats[n].tag == NULL)
+                       last_title = text;
+               else if ((value != NULL && key != NULL && g_strcasecmp(formats[n].tag, key) == 0) ||
+                        (value == NULL && (key == NULL || stristr(formats[n].tag, key) != NULL))) {
+                       if (first) {
+                               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_TITLE, rec->short_name, formats[0].def);
+                               first = FALSE;
+                       }
+                       if (last_title != NULL)
+                               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_SUBTITLE, last_title);
+                       if (reset || value != NULL) {
+                               theme = theme_module_create(current_theme, rec->name);
+                                g_free_not_null(theme->formats[n]);
+                                g_free_not_null(theme->expanded_formats[n]);
+
+                               text = reset ? formats[n].def : value;
+                               theme->formats[n] = reset ? NULL : g_strdup(value);
+                               theme->expanded_formats[n] = theme_format_expand(current_theme, text);
+                       }
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_FORMAT_ITEM, formats[n].tag, text);
+                       last_title = NULL;
+               }
+       }
+}
+
+/* SYNTAX: FORMAT [-delete | -reset] [<module>] [<key> [<value>]] */
+static void cmd_format(const char *data)
+{
+        GHashTable *optlist;
+       GSList *tmp, *modules;
+       char *module, *key, *value;
+       void *free_arg;
+       int reset;
+
+       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
+                           "format", &optlist, &module, &key, &value))
+               return;
+
+       modules = get_sorted_modules();
+       if (*module == '\0')
+               module = NULL;
+       else if (theme_search(modules, module) == NULL) {
+               /* first argument isn't module.. */
+               cmd_params_free(free_arg);
+               if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST | PARAM_FLAG_OPTIONS,
+                                   "format", &optlist, &key, &value))
+                       return;
+               module = NULL;
+       }
+
+       reset = FALSE;
+       if (*key == '\0') key = NULL;
+       if (g_hash_table_lookup(optlist, "reset"))
+               reset = TRUE;
+       else if (g_hash_table_lookup(optlist, "delete"))
+               value = "";
+       else if (*value == '\0')
+               value = NULL;
+
+       for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+               THEME_SEARCH_REC *rec = tmp->data;
+
+               if (module == NULL || g_strcasecmp(rec->short_name, module) == 0)
+                       theme_show(rec, key, value, reset);
+       }
+       g_slist_foreach(modules, (GFunc) g_free, NULL);
+       g_slist_free(modules);
+
+        cmd_params_free(free_arg);
+}
+
+typedef struct {
+       CONFIG_REC *config;
+        int save_all;
+} THEME_SAVE_REC;
+
+static void module_save(const char *module, MODULE_THEME_REC *rec,
+                        THEME_SAVE_REC *data)
+{
+       CONFIG_NODE *fnode, *node;
+       FORMAT_REC *formats;
+       int n;
+
+        formats = g_hash_table_lookup(default_formats, rec->name);
+       if (formats == NULL) return;
+
+       fnode = config_node_traverse(data->config, "formats", TRUE);
+
+       node = config_node_section(fnode, rec->name, NODE_TYPE_BLOCK);
+       for (n = 1; formats[n].def != NULL; n++) {
+                if (rec->formats[n] != NULL) {
+                        config_node_set_str(data->config, node, formats[n].tag,
+                                            rec->formats[n]);
+               } else if (data->save_all && formats[n].tag != NULL) {
+                        config_node_set_str(data->config, node, formats[n].tag,
+                                            formats[n].def);
+               }
+        }
+
+        if (node->value == NULL) {
+                /* not modified, don't keep the empty section */
+                config_node_remove(data->config, fnode, node);
+               if (fnode->value == NULL) {
+                       config_node_remove(data->config,
+                                          data->config->mainnode, fnode);
+               }
+        }
+}
+
+static void theme_save(THEME_REC *theme, int save_all)
+{
+       CONFIG_REC *config;
+       THEME_SAVE_REC data;
+       char *path;
+       int ok;
+
+       config = config_open(theme->path, -1);
+        if (config != NULL)
+                config_parse(config);
+        else {
+                if (g_strcasecmp(theme->name, "default") == 0) {
+                        config = config_open(NULL, -1);
+                        config_parse_data(config, default_theme, "internal");
+                        config_change_file_name(config, theme->path, 0660);
+                } else {
+                        config = config_open(theme->path, 0660);
+                        if (config == NULL)
+                                return;
+                        config_parse(config);
+                }
+        }
+
+       data.config = config;
+        data.save_all = save_all;
+       g_hash_table_foreach(theme->modules, (GHFunc) module_save, &data);
+
+        /* always save the theme to ~/.irssi/ */
+       path = g_strdup_printf("%s/%s", get_irssi_dir(),
+                              g_basename(theme->path));
+       ok = config_write(config, path, 0660) == 0;
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   ok ? TXT_THEME_SAVED : TXT_THEME_SAVE_FAILED,
+                   path, config_last_error(config));
+
+       g_free(path);
+       config_close(config);
+}
+
+/* save changed formats, -format saves all */
+static void cmd_save(const char *data)
+{
+       GSList *tmp;
+        GHashTable *optlist;
+        void *free_arg;
+       char *fname;
+       int saveall;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "save", &optlist, &fname))
+               return;
+
+        saveall = g_hash_table_lookup(optlist, "formats") != NULL;
+       for (tmp = themes; tmp != NULL; tmp = tmp->next) {
+               THEME_REC *theme = tmp->data;
+
+               theme_save(theme, saveall);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static void complete_format_list(THEME_SEARCH_REC *rec, const char *key, GList **list)
+{
+       FORMAT_REC *formats;
+       int n, len;
+
+       formats = g_hash_table_lookup(default_formats, rec->name);
+
+       len = strlen(key);
+       for (n = 1; formats[n].def != NULL; n++) {
+               const char *item = formats[n].tag;
+
+               if (item != NULL && g_strncasecmp(item, key, len) == 0)
+                        *list = g_list_append(*list, g_strdup(item));
+       }
+}
+
+static GList *completion_get_formats(const char *module, const char *key)
+{
+       GSList *modules, *tmp;
+       GList *list;
+
+       g_return_val_if_fail(key != NULL, NULL);
+
+       list = NULL;
+
+       modules = get_sorted_modules();
+       if (*module == '\0' || theme_search(modules, module) != NULL) {
+               for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+                       THEME_SEARCH_REC *rec = tmp->data;
+
+                       if (*module == '\0' || g_strcasecmp(rec->short_name, module) == 0)
+                               complete_format_list(rec, key, &list);
+               }
+       }
+       g_slist_foreach(modules, (GFunc) g_free, NULL);
+       g_slist_free(modules);
+
+       return list;
+}
+
+static void sig_complete_format(GList **list, WINDOW_REC *window,
+                               const char *word, const char *line, int *want_space)
+{
+       const char *ptr;
+       int words;
+
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+        ptr = line;
+
+       words = 0;
+       do {
+               words++;
+                ptr = strchr(ptr, ' ');
+       } while (ptr != NULL);
+
+       if (words > 2)
+               return;
+
+       *list = completion_get_formats(line, word);
+       if (*list != NULL) signal_stop();
+}
+
+static void change_theme(const char *name, int verbose)
+{
+       THEME_REC *rec;
+
+       rec = theme_load(name);
+       if (rec != NULL) {
+               current_theme = rec;
+               if (verbose) {
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                                   TXT_THEME_CHANGED,
+                                   rec->name, rec->path);
+               }
+       } else if (verbose) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_THEME_NOT_FOUND, name);
+       }
+}
+
+static void read_settings(void)
+{
+       const char *theme;
+        int len;
+
+       theme = settings_get_str("theme");
+       len = strlen(current_theme->name);
+       if (strcmp(current_theme->name, theme) != 0 &&
+           (strncmp(current_theme->name, theme, len) != 0 ||
+            strcmp(theme+len, ".theme") != 0))
+               change_theme(theme, TRUE);
+}
+
+static void themes_read(void)
+{
+       char *fname;
+
+       while (themes != NULL)
+               theme_destroy(themes->data);
+
+       /* first there's default theme.. */
+       current_theme = theme_load("default");
+       if (current_theme == NULL) {
+               fname = g_strdup_printf("%s/default.theme", get_irssi_dir());
+               current_theme = theme_create(fname, "default");
+               current_theme->default_color = -1;
+                theme_read(current_theme, NULL, default_theme);
+               g_free(fname);
+       }
+
+        window_themes_update();
+        change_theme(settings_get_str("theme"), FALSE);
+}
+
+void themes_init(void)
+{
+       settings_add_str("lookandfeel", "theme", "default");
+
+       default_formats = g_hash_table_new((GHashFunc) g_str_hash,
+                                          (GCompareFunc) g_str_equal);
+
+        init_finished = FALSE;
+        init_errors = NULL;
+
+       themes = NULL;
+       themes_read();
+
+       command_bind("format", NULL, (SIGNAL_FUNC) cmd_format);
+       command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
+       signal_add("complete command format", (SIGNAL_FUNC) sig_complete_format);
+       signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
+        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       signal_add("setup reread", (SIGNAL_FUNC) themes_read);
+
+       command_set_options("format", "delete reset");
+       command_set_options("save", "formats");
+}
+
+void themes_deinit(void)
+{
+       while (themes != NULL)
+               theme_destroy(themes->data);
+
+       g_hash_table_destroy(default_formats);
+       default_formats = NULL;
+
+       command_unbind("format", (SIGNAL_FUNC) cmd_format);
+       command_unbind("save", (SIGNAL_FUNC) cmd_save);
+       signal_remove("complete command format", (SIGNAL_FUNC) sig_complete_format);
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
+        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+        signal_remove("setup reread", (SIGNAL_FUNC) themes_read);
+}
diff --git a/apps/irssi/src/fe-common/core/themes.h b/apps/irssi/src/fe-common/core/themes.h
new file mode 100644 (file)
index 0000000..59d2810
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef __THEMES_H
+#define __THEMES_H
+
+typedef struct {
+       char *name;
+
+       int count;
+       char **formats; /* in same order as in module's default formats */
+       char **expanded_formats; /* this contains the formats after
+                                   expanding {templates} */
+} MODULE_THEME_REC;
+
+typedef struct {
+       char *path;
+       char *name;
+        time_t last_modify;
+
+       int default_color; /* default color to use with text with default
+                             background. default is -1 which means the
+                             default color set by terminal */
+       GHashTable *modules;
+
+        int replace_keys[256]; /* index to replace_values for each char */
+       GSList *replace_values;
+       GHashTable *abstracts;
+
+       void *gui_data;
+} THEME_REC;
+
+typedef struct _FORMAT_REC FORMAT_REC;
+
+extern GSList *themes;
+extern THEME_REC *current_theme;
+extern GHashTable *default_formats;
+
+THEME_REC *theme_create(const char *path, const char *name);
+void theme_destroy(THEME_REC *rec);
+
+THEME_REC *theme_load(const char *name);
+
+#define theme_register(formats) theme_register_module(MODULE_NAME, formats)
+#define theme_unregister() theme_unregister_module(MODULE_NAME)
+void theme_register_module(const char *module, FORMAT_REC *formats);
+void theme_unregister_module(const char *module);
+
+#define EXPAND_FLAG_IGNORE_REPLACES     0x01 /* don't use the character replaces when expanding */
+#define EXPAND_FLAG_IGNORE_EMPTY        0x02 /* if abstract's argument is empty, or the argument is a $variable that is empty, don't try to expand it (ie. {xx }, but not {xx}) */
+#define EXPAND_FLAG_RECURSIVE_MASK      0x0f
+/* private */
+#define EXPAND_FLAG_ROOT               0x10
+#define EXPAND_FLAG_LASTCOLOR_ARG      0x20
+
+char *theme_format_expand(THEME_REC *theme, const char *format);
+char *theme_format_expand_data(THEME_REC *theme, const char **format,
+                              char default_fg, char default_bg,
+                              char *save_last_fg, char *save_last_bg,
+                              int flags);
+
+void themes_init(void);
+void themes_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/translation.c b/apps/irssi/src/fe-common/core/translation.c
new file mode 100644 (file)
index 0000000..d573fc1
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ translation.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "line-split.h"
+#include "misc.h"
+#include "levels.h"
+#include "settings.h"
+
+#include "printtext.h"
+
+unsigned char translation_in[256], translation_out[256];
+static char *current_translation;
+
+void translation_reset(void)
+{
+       int n;
+
+       for (n = 0; n < 256; n++)
+               translation_in[n] = (unsigned char) n;
+       for (n = 0; n < 256; n++)
+               translation_out[n] = (unsigned char) n;
+}
+
+void translate_output(char *text)
+{
+       while (*text != '\0') {
+               *text = (char) translation_out[(int) (unsigned char) *text];
+               text++;
+       }
+}
+
+#define gethex(a) \
+       (i_isdigit(a) ? ((a)-'0') : (i_toupper(a)-'A'+10))
+
+void translation_parse_line(const char *str, int *pos)
+{
+       const char *ptr;
+       int value;
+
+       for (ptr = str; *ptr != '\0'; ptr++) {
+               if (ptr[0] != '0' || ptr[1] != 'x')
+                       break;
+               ptr += 2;
+
+               value = (gethex(ptr[0]) << 4) + gethex(ptr[1]);
+               if (*pos < 256)
+                       translation_in[*pos] = (unsigned char) value;
+               else
+                       translation_out[*pos-256] = (unsigned char) value;
+               (*pos)++;
+
+               ptr += 2;
+               if (*ptr != ',') break;
+       }
+}
+
+int translation_read(const char *file)
+{
+       char tmpbuf[1024], *str, *path;
+       LINEBUF_REC *buffer;
+       int f, pos, ret, recvlen;
+
+       g_return_val_if_fail(file != NULL, FALSE);
+
+       path = convert_home(file);
+       f = open(file, O_RDONLY);
+       g_free(path);
+
+       if (f == -1) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_TRANSLATION_NOT_FOUND, file,
+                           g_strerror(errno));
+               return FALSE;
+       }
+
+       pos = 0; buffer = NULL;
+       while (pos < 512) {
+               recvlen = read(f, tmpbuf, sizeof(tmpbuf));
+
+               ret = line_split(tmpbuf, recvlen, &str, &buffer);
+               if (ret <= 0) break;
+
+                translation_parse_line(str, &pos);
+       }
+       line_split_free(buffer);
+
+       close(f);
+       if (pos != 512) {
+               translation_reset();
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_TRANSLATION_FILE_ERROR, file);
+       }
+       return pos == 512;
+}
+
+static void read_settings(void)
+{
+       const char *translation;
+
+       translation = settings_get_str("translation");
+       if (*translation == '\0') {
+               if (current_translation != NULL) {
+                       g_free_and_null(current_translation);
+                        translation_reset();
+               }
+               return;
+       }
+
+       if (current_translation == NULL ||
+           strcmp(translation, current_translation) != 0) {
+                g_free_not_null(current_translation);
+               current_translation = g_strdup(translation);
+               translation_read(translation);
+       }
+}
+
+void translation_init(void)
+{
+       translation_reset();
+
+        current_translation = NULL;
+       settings_add_str("misc", "translation", "");
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+
+       read_settings();
+}
+
+void translation_deinit(void)
+{
+       read_settings();
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/translation.h b/apps/irssi/src/fe-common/core/translation.h
new file mode 100644 (file)
index 0000000..48b2c3d
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __TRANSLATION_H
+#define __TRANSLATION_H
+
+extern unsigned char translation_in[256], translation_out[256];
+
+int translation_read(const char *file);
+void translate_output(char *text);
+
+void translation_init(void);
+void translation_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/window-activity.c b/apps/irssi/src/fe-common/core/window-activity.c
new file mode 100644 (file)
index 0000000..3e4cd80
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ window-activity.c : irssi
+
+    Copyright (C) 1999-2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "levels.h"
+#include "servers.h"
+#include "channels.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "fe-windows.h"
+#include "window-items.h"
+#include "nicklist.h"
+#include "hilight-text.h"
+#include "formats.h"
+
+static char **hide_targets;
+static int hide_level, msg_level, hilight_level;
+
+static void window_activity(WINDOW_REC *window, int data_level,
+                           const char *hilight_color)
+{
+       int old_data_level;
+
+       old_data_level = window->data_level;
+       if (data_level == 0 || window->data_level < data_level) {
+               window->data_level = data_level;
+                g_free_not_null(window->hilight_color);
+               window->hilight_color = g_strdup(hilight_color);
+               signal_emit("window hilight", 1, window);
+       }
+
+       signal_emit("window activity", 2, window,
+                   GINT_TO_POINTER(old_data_level));
+}
+
+static void window_item_activity(WI_ITEM_REC *item, int data_level,
+                                const char *hilight_color)
+{
+       int old_data_level;
+
+       old_data_level = item->data_level;
+       if (data_level == 0 || item->data_level < data_level) {
+               item->data_level = data_level;
+                g_free_not_null(item->hilight_color);
+               item->hilight_color = g_strdup(hilight_color);
+               signal_emit("window item hilight", 1, item);
+       }
+
+       signal_emit("window item activity", 2, item,
+                   GINT_TO_POINTER(old_data_level));
+}
+
+#define hide_target_activity(data_level, target) \
+       ((target) != NULL && hide_targets != NULL && \
+       strarray_find(hide_targets, target) != -1)
+
+static void sig_hilight_text(TEXT_DEST_REC *dest, const char *msg)
+{
+       WI_ITEM_REC *item;
+       int data_level;
+
+       if (dest->window == active_win || (dest->level & hide_level))
+               return;
+
+       if (dest->level & hilight_level) {
+               data_level = DATA_LEVEL_HILIGHT+dest->hilight_priority;
+       } else {
+               data_level = (dest->level & msg_level) ?
+                       DATA_LEVEL_MSG : DATA_LEVEL_TEXT;
+       }
+
+       if ((dest->level & MSGLEVEL_HILIGHT) == 0 &&
+           hide_target_activity(data_level, dest->target))
+               return;
+
+       if (dest->target != NULL) {
+               item = window_item_find(dest->server, dest->target);
+               if (item != NULL) {
+                       window_item_activity(item, data_level,
+                                            dest->hilight_color);
+               }
+       }
+       window_activity(dest->window, data_level, dest->hilight_color);
+}
+
+static void sig_dehilight_window(WINDOW_REC *window)
+{
+        GSList *tmp;
+
+       g_return_if_fail(window != NULL);
+
+       if (window->data_level != 0) {
+               window_activity(window, 0, NULL);
+               for (tmp = window->items; tmp != NULL; tmp = tmp->next)
+                       window_item_activity(tmp->data, 0, NULL);
+       }
+}
+
+static void read_settings(void)
+{
+       const char *targets;
+
+       if (hide_targets != NULL)
+               g_strfreev(hide_targets);
+
+        targets = settings_get_str("activity_hide_targets");
+       hide_targets = *targets == '\0' ? NULL :
+               g_strsplit(targets, " ", -1);
+
+       hide_level = MSGLEVEL_NEVER | MSGLEVEL_NO_ACT |
+               level2bits(settings_get_str("activity_hide_level"));
+       msg_level = level2bits(settings_get_str("activity_msg_level"));
+       hilight_level = MSGLEVEL_HILIGHT |
+               level2bits(settings_get_str("activity_hilight_level"));
+}
+
+void window_activity_init(void)
+{
+       settings_add_str("lookandfeel", "activity_hide_targets", "");
+       settings_add_str("lookandfeel", "activity_hide_level", "");
+       settings_add_str("lookandfeel", "activity_msg_level", "PUBLIC");
+       settings_add_str("lookandfeel", "activity_hilight_level", "MSGS DCCMSGS");
+
+       read_settings();
+       signal_add("print text", (SIGNAL_FUNC) sig_hilight_text);
+       signal_add("window changed", (SIGNAL_FUNC) sig_dehilight_window);
+       signal_add("window dehilight", (SIGNAL_FUNC) sig_dehilight_window);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void window_activity_deinit(void)
+{
+       if (hide_targets != NULL)
+               g_strfreev(hide_targets);
+
+       signal_remove("print text", (SIGNAL_FUNC) sig_hilight_text);
+       signal_remove("window changed", (SIGNAL_FUNC) sig_dehilight_window);
+       signal_remove("window dehilight", (SIGNAL_FUNC) sig_dehilight_window);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-common/core/window-commands.c b/apps/irssi/src/fe-common/core/window-commands.c
new file mode 100644 (file)
index 0000000..e54478d
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ window-commands.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "misc.h"
+#include "servers.h"
+
+#include "levels.h"
+
+#include "themes.h"
+#include "fe-windows.h"
+#include "window-items.h"
+#include "windows-layout.h"
+#include "printtext.h"
+
+static void window_print_binds(WINDOW_REC *win)
+{
+       GSList *tmp;
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_BOUND_ITEMS_HEADER);
+       for (tmp = win->bound_items; tmp != NULL; tmp = tmp->next) {
+               WINDOW_BIND_REC *bind = tmp->data;
+
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_BOUND_ITEM,
+                                  bind->name, bind->servertag,
+                                  bind->sticky ? "sticky" : "");
+       }
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_BOUND_ITEMS_FOOTER);
+}
+
+static void window_print_items(WINDOW_REC *win)
+{
+       GSList *tmp;
+        const char *type;
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_ITEMS_HEADER);
+       for (tmp = win->items; tmp != NULL; tmp = tmp->next) {
+               WI_ITEM_REC *item = tmp->data;
+
+               type = module_find_id_str("WINDOW ITEM TYPE", item->type);
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_ITEM,
+                                  type == NULL ? "??" : type, item->name,
+                                  item->server == NULL ? "" :
+                                  item->server->tag);
+       }
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_ITEMS_FOOTER);
+}
+
+static void cmd_window_info(WINDOW_REC *win)
+{
+        char *levelstr;
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_HEADER);
+
+        /* Window reference number + sticky status */
+       if (!win->sticky_refnum) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_REFNUM, win->refnum);
+       } else {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_REFNUM_STICKY, win->refnum);
+       }
+
+        /* Window name */
+       if (win->name != NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_NAME, win->name);
+       }
+
+        /* Window width / height */
+       printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_SIZE,
+                          win->width, win->height);
+
+       /* Window immortality */
+       if (win->immortal) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_IMMORTAL);
+       }
+
+       /* Window history name */
+       if (win->history_name != NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_HISTORY, win->history_name);
+       }
+
+        /* Window level */
+       levelstr = win->level == 0 ?
+               g_strdup("NONE") : bits2level(win->level);
+       printformat_window(win, MSGLEVEL_CLIENTCRAP, TXT_WINDOW_INFO_LEVEL,
+                          levelstr);
+       g_free(levelstr);
+
+        /* Active window server + sticky status */
+       if (win->servertag == NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_SERVER,
+                                  win->active_server != NULL ?
+                                  win->active_server->tag : "NONE");
+       } else {
+               if (win->active_server != NULL &&
+                   strcmp(win->active_server->tag, win->servertag) != 0)
+                        g_warning("Active server isn't the sticky server!");
+
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_SERVER_STICKY,
+                                  win->servertag);
+       }
+
+        /* Window theme + error status */
+       if (win->theme_name != NULL) {
+               printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                                  TXT_WINDOW_INFO_THEME, win->theme_name,
+                                  win->theme != NULL ? "" : "(not loaded)");
+       }
+
+        /* Bound items in window */
+       if (win->bound_items != NULL)
+                window_print_binds(win);
+
+        /* Item */
+       if (win->items != NULL)
+                window_print_items(win);
+
+        signal_emit("window print info", 1, win);
+
+       printformat_window(win, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_FOOTER);
+}
+
+static void cmd_window(const char *data, void *server, WI_ITEM_REC *item)
+{
+        while (*data == ' ') data++;
+
+       if (*data == '\0')
+                cmd_window_info(active_win);
+       else if (is_numeric(data, 0))
+                signal_emit("command window refnum", 3, data, server, item);
+        else
+               command_runsub("window", data, server, item);
+}
+
+/* SYNTAX: WINDOW NEW [hide] */
+static void cmd_window_new(const char *data, void *server, WI_ITEM_REC *item)
+{
+       WINDOW_REC *window;
+       int type;
+
+       g_return_if_fail(data != NULL);
+
+       type = (g_strncasecmp(data, "hid", 3) == 0 || g_strcasecmp(data, "tab") == 0) ? 1 :
+               (g_strcasecmp(data, "split") == 0 ? 2 : 0);
+       signal_emit("gui window create override", 1, GINT_TO_POINTER(type));
+
+       window = window_create(NULL, FALSE);
+       window_change_server(window, server);
+}
+
+/* SYNTAX: WINDOW CLOSE [<first> [<last>] */
+static void cmd_window_close(const char *data)
+{
+        GSList *tmp, *destroys;
+       char *first, *last;
+        int first_num, last_num;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 2, &first, &last))
+               return;
+
+       if ((*first != '\0' && !is_numeric(first, '\0')) ||
+           ((*last != '\0') && !is_numeric(last, '\0'))) {
+               cmd_params_free(free_arg);
+                return;
+       }
+
+       first_num = *first == '\0' ? active_win->refnum : atoi(first);
+       last_num = *last == '\0' ? first_num : atoi(last);
+
+        /* get list of windows to destroy */
+        destroys = NULL;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->refnum >= first_num && rec->refnum <= last_num)
+                       destroys = g_slist_append(destroys, rec);
+       }
+
+        /* really destroy the windows */
+       while (destroys != NULL) {
+               WINDOW_REC *rec = destroys->data;
+
+               if (windows->next != NULL) {
+                       if (!rec->immortal)
+                               window_destroy(rec);
+                       else {
+                               printformat_window(rec, MSGLEVEL_CLIENTERROR,
+                                                  TXT_WINDOW_IMMORTAL_ERROR);
+                       }
+               }
+
+                destroys = g_slist_remove(destroys, rec);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: WINDOW REFNUM <number> */
+static void cmd_window_refnum(const char *data)
+{
+       WINDOW_REC *window;
+
+       if (!is_numeric(data, 0))
+               return;
+
+       window = window_find_refnum(atoi(data));
+       if (window != NULL)
+               window_set_active(window);
+}
+
+/* return the first window number with the highest activity */
+static WINDOW_REC *window_highest_activity(WINDOW_REC *window)
+{
+       WINDOW_REC *rec, *max_win;
+       GSList *tmp;
+       int max_act, through;
+
+       g_return_val_if_fail(window != NULL, NULL);
+
+       max_win = NULL; max_act = 0; through = FALSE;
+
+       tmp = g_slist_find(windows, window);
+       for (;; tmp = tmp->next) {
+               if (tmp == NULL) {
+                       tmp = windows;
+                       through = TRUE;
+               }
+
+               if (through && tmp->data == window)
+                       break;
+
+               rec = tmp->data;
+
+               if (rec->data_level > 0 && max_act < rec->data_level) {
+                       max_act = rec->data_level;
+                       max_win = rec;
+               }
+       }
+
+       return max_win;
+}
+
+/* SYNTAX: WINDOW GOTO active|<number>|<name> */
+static void cmd_window_goto(const char *data)
+{
+       WINDOW_REC *window;
+
+       g_return_if_fail(data != NULL);
+
+       if (is_numeric(data, 0)) {
+               cmd_window_refnum(data);
+               return;
+       }
+
+       if (g_strcasecmp(data, "active") == 0)
+                window = window_highest_activity(active_win);
+       else
+                window = window_find_item(active_win->active_server, data);
+
+       if (window != NULL)
+               window_set_active(window);
+}
+
+/* SYNTAX: WINDOW NEXT */
+static void cmd_window_next(void)
+{
+       int num;
+
+       num = window_refnum_next(active_win->refnum, TRUE);
+       if (num < 1) num = windows_refnum_last();
+
+       window_set_active(window_find_refnum(num));
+}
+
+/* SYNTAX: WINDOW LAST */
+static void cmd_window_last(void)
+{
+       if (windows->next != NULL)
+               window_set_active(windows->next->data);
+}
+
+/* SYNTAX: WINDOW PREVIOUS */
+static void cmd_window_previous(void)
+{
+       int num;
+
+       num = window_refnum_prev(active_win->refnum, TRUE);
+       if (num < 1) num = window_refnum_next(0, TRUE);
+
+       window_set_active(window_find_refnum(num));
+}
+
+/* SYNTAX: WINDOW LEVEL [<level>] */
+static void cmd_window_level(const char *data)
+{
+       char *level;
+
+       g_return_if_fail(data != NULL);
+
+       window_set_level(active_win, combine_level(active_win->level, data));
+
+       level = active_win->level == 0 ? g_strdup("NONE") :
+               bits2level(active_win->level);
+       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                          TXT_WINDOW_LEVEL, level);
+       g_free(level);
+}
+
+/* SYNTAX: WINDOW IMMORTAL on|off|toggle */
+static void cmd_window_immortal(const char *data)
+{
+       int set;
+
+       if (g_strcasecmp(data, "ON") == 0)
+                set = TRUE;
+       else if (g_strcasecmp(data, "OFF") == 0)
+                set = FALSE;
+       else if (g_strcasecmp(data, "TOGGLE") == 0)
+                set = !active_win->immortal;
+       else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
+               return;
+       }
+
+       if (set) {
+                window_set_immortal(active_win, TRUE);
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_WINDOW_SET_IMMORTAL);
+       } else {
+                window_set_immortal(active_win, FALSE);
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_WINDOW_UNSET_IMMORTAL);
+       }
+}
+
+/* SYNTAX: WINDOW SERVER [-sticky | -unsticky] <tag> */
+static void cmd_window_server(const char *data)
+{
+       GHashTable *optlist;
+       SERVER_REC *server;
+        char *tag;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "window server", &optlist, &tag))
+               return;
+
+       if (*tag == '\0' && active_win->active_server != NULL &&
+           (g_hash_table_lookup(optlist, "sticky") != NULL ||
+            g_hash_table_lookup(optlist, "unsticky") != NULL)) {
+               tag = active_win->active_server->tag;
+       }
+
+       if (*tag == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+       server = server_find_tag(tag);
+
+       if (g_hash_table_lookup(optlist, "unsticky") != NULL &&
+           active_win->servertag != NULL) {
+               g_free_and_null(active_win->servertag);
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_UNSET_SERVER_STICKY);
+       }
+
+       if (active_win->servertag != NULL &&
+           g_hash_table_lookup(optlist, "sticky") == NULL) {
+               printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+                                  TXT_ERROR_SERVER_STICKY);
+       } else if (server == NULL) {
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_UNKNOWN_SERVER_TAG, tag);
+       } else if (active_win->active == NULL) {
+               window_change_server(active_win, server);
+               if (g_hash_table_lookup(optlist, "sticky") != NULL) {
+                        g_free_not_null(active_win->servertag);
+                       active_win->servertag = g_strdup(server->tag);
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_SET_SERVER_STICKY, server->tag);
+               }
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_SERVER_CHANGED,
+                                  server->tag, server->connrec->address,
+                                  server->connrec->chatnet == NULL ? "" :
+                                  server->connrec->chatnet);
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static void cmd_window_item(const char *data, void *server, WI_ITEM_REC *item)
+{
+       command_runsub("window item", data, server, item);
+}
+
+/* SYNTAX: WINDOW ITEM PREV */
+static void cmd_window_item_prev(void)
+{
+       window_item_prev(active_win);
+}
+
+/* SYNTAX: WINDOW ITEM NEXT */
+static void cmd_window_item_next(void)
+{
+       window_item_next(active_win);
+}
+
+/* SYNTAX: WINDOW ITEM GOTO <name> */
+static void cmd_window_item_goto(const char *data, SERVER_REC *server)
+{
+        WI_ITEM_REC *item;
+
+        item = window_item_find_window(active_win, server, data);
+        if (item != NULL)
+                window_item_set_active(active_win, item);
+}
+
+/* SYNTAX: WINDOW ITEM MOVE <number>|<name> */
+static void cmd_window_item_move(const char *data, SERVER_REC *server,
+                                 WI_ITEM_REC *item)
+{
+       WINDOW_REC *window;
+       void *free_arg;
+       char *target;
+
+       if (!cmd_get_params(data, &free_arg, 1, &target))
+               return;
+
+        if (is_numeric(target, '\0')) {
+                /* move current window item to specified window */
+                window = window_find_refnum(atoi(target));
+        } else {
+                /* move specified window item to current window */
+                item = window_item_find(server, target);
+                window = active_win;
+        }
+        if (window != NULL && item != NULL)
+               window_item_set_active(window, item);
+
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: WINDOW NUMBER [-sticky] <number> */
+static void cmd_window_number(const char *data)
+{
+       GHashTable *optlist;
+        char *refnum;
+       void *free_arg;
+        int num;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "window number", &optlist, &refnum))
+               return;
+
+       if (*refnum == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       num = atoi(refnum);
+       if (num < 1) {
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_REFNUM_TOO_LOW);
+       } else {
+               window_set_refnum(active_win, num);
+               active_win->sticky_refnum =
+                       g_hash_table_lookup(optlist, "sticky") != NULL;
+       }
+
+        cmd_params_free(free_arg);
+}
+
+/* SYNTAX: WINDOW NAME <name> */
+static void cmd_window_name(const char *data)
+{
+       if (window_find_name(data) == NULL)
+               window_set_name(active_win, data);
+       else {
+               printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+                                  TXT_WINDOW_NAME_NOT_UNIQUE, data);
+       }
+}
+
+/* SYNTAX: WINDOW HISTORY <name> */
+void cmd_window_history(const char *data)
+{
+       window_set_history(active_win, data);
+}
+
+/* we're moving the first window to last - move the first contiguous block
+   of refnums to left. Like if there's windows 1..5 and 7..10, move 1 to
+   11, 2..5 to 1..4 and leave 7..10 alone */
+static void window_refnums_move_left(WINDOW_REC *move_window)
+{
+       WINDOW_REC *window;
+       int refnum, new_refnum;
+
+        new_refnum = windows_refnum_last();
+       for (refnum = move_window->refnum+1; refnum <= new_refnum; refnum++) {
+               window = window_find_refnum(refnum);
+               if (window == NULL) {
+                        new_refnum++;
+                       break;
+               }
+
+               window_set_refnum(window, refnum-1);
+       }
+
+       window_set_refnum(move_window, new_refnum);
+}
+
+/* we're moving the last window to first - make some space so we can use the
+   refnum 1 */
+static void window_refnums_move_right(WINDOW_REC *move_window)
+{
+       WINDOW_REC *window;
+       int refnum, new_refnum;
+
+        new_refnum = 1;
+       if (window_find_refnum(new_refnum) == NULL) {
+               window_set_refnum(move_window, new_refnum);
+                return;
+       }
+
+       /* find the first unused refnum, like if there's windows
+          1..5 and 7..10, we only need to move 1..5 to 2..6 */
+       refnum = new_refnum;
+       while (move_window->refnum == refnum ||
+              window_find_refnum(refnum) != NULL) refnum++;
+       refnum--;
+
+       while (refnum >= new_refnum) {
+               window = window_find_refnum(refnum);
+               window_set_refnum(window, refnum+1);
+
+               refnum--;
+       }
+
+       window_set_refnum(move_window, new_refnum);
+}
+
+/* SYNTAX: WINDOW MOVE PREV */
+static void cmd_window_move_prev(void)
+{
+       int refnum;
+
+       refnum = window_refnum_prev(active_win->refnum, FALSE);
+       if (refnum != -1) {
+               window_set_refnum(active_win, refnum);
+               return;
+       }
+
+       window_refnums_move_left(active_win);
+}
+
+/* SYNTAX: WINDOW MOVE NEXT */
+static void cmd_window_move_next(void)
+{
+       int refnum;
+
+       refnum = window_refnum_next(active_win->refnum, FALSE);
+       if (refnum != -1) {
+               window_set_refnum(active_win, refnum);
+               return;
+       }
+
+        window_refnums_move_right(active_win);
+}
+
+/* SYNTAX: WINDOW MOVE <number>|<direction> */
+static void cmd_window_move(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       int new_refnum, refnum;
+
+       if (!is_numeric(data, 0)) {
+               command_runsub("window move", data, server, item);
+                return;
+       }
+
+       new_refnum = atoi(data);
+       if (new_refnum > active_win->refnum) {
+               for (;;) {
+                       refnum = window_refnum_next(active_win->refnum, FALSE);
+                       if (refnum == -1 || refnum > new_refnum)
+                               break;
+
+                       window_set_refnum(active_win, refnum);
+               }
+       } else {
+               for (;;) {
+                       refnum = window_refnum_prev(active_win->refnum, FALSE);
+                       if (refnum == -1 || refnum < new_refnum)
+                               break;
+
+                       window_set_refnum(active_win, refnum);
+               }
+       }
+}
+
+/* SYNTAX: WINDOW LIST */
+static void cmd_window_list(void)
+{
+       GSList *tmp, *sorted;
+       char *levelstr;
+
+       sorted = windows_get_sorted();
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_HEADER);
+       for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               levelstr = bits2level(rec->level);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_LINE,
+                           rec->refnum, rec->name == NULL ? "" : rec->name,
+                           rec->active == NULL ? "" : rec->active->name,
+                           rec->active_server == NULL ? "" : ((SERVER_REC *) rec->active_server)->tag,
+                           levelstr);
+               g_free(levelstr);
+       }
+       g_slist_free(sorted);
+        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_WINDOWLIST_FOOTER);
+}
+
+/* SYNTAX: WINDOW THEME [-delete] [<name>] */
+static void cmd_window_theme(const char *data)
+{
+       THEME_REC *theme;
+       GHashTable *optlist;
+        char *name;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "window theme", &optlist, &name))
+               return;
+
+       if (g_hash_table_lookup(optlist, "delete") != NULL) {
+               g_free_and_null(active_win->theme_name);
+
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_WINDOW_THEME_REMOVED);
+       } else if (*name == '\0') {
+               if (active_win->theme == NULL) {
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_WINDOW_THEME_DEFAULT);
+               } else {
+                        theme = active_win->theme;
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_WINDOW_THEME,
+                                          theme->name, theme->path);
+               }
+       } else {
+               g_free_not_null(active_win->theme_name);
+               active_win->theme_name = g_strdup(data);
+
+               active_win->theme = theme = theme_load(data);
+               if (theme != NULL) {
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_WINDOW_THEME_CHANGED,
+                                          theme->name, theme->path);
+               } else {
+                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                          TXT_THEME_NOT_FOUND, data);
+               }
+       }
+
+       cmd_params_free(free_arg);
+}
+
+static void cmd_layout(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
+{
+       command_runsub("layout", data, server, item);
+}
+
+/* SYNTAX: FOREACH WINDOW <command> */
+static void cmd_foreach_window(const char *data)
+{
+        WINDOW_REC *old;
+        GSList *list;
+
+        old = active_win;
+
+       list = g_slist_copy(windows);
+       while (list != NULL) {
+               WINDOW_REC *rec = list->data;
+
+               active_win = rec;
+               signal_emit("send command", 3, data, rec->active_server,
+                           rec->active);
+                list = g_slist_remove(list, list->data);
+       }
+
+        active_win = old;
+}
+
+void window_commands_init(void)
+{
+       command_bind("window", NULL, (SIGNAL_FUNC) cmd_window);
+       command_bind("window new", NULL, (SIGNAL_FUNC) cmd_window_new);
+       command_bind("window close", NULL, (SIGNAL_FUNC) cmd_window_close);
+       command_bind("window kill", NULL, (SIGNAL_FUNC) cmd_window_close);
+       command_bind("window server", NULL, (SIGNAL_FUNC) cmd_window_server);
+       command_bind("window refnum", NULL, (SIGNAL_FUNC) cmd_window_refnum);
+       command_bind("window goto", NULL, (SIGNAL_FUNC) cmd_window_goto);
+       command_bind("window previous", NULL, (SIGNAL_FUNC) cmd_window_previous);
+       command_bind("window next", NULL, (SIGNAL_FUNC) cmd_window_next);
+       command_bind("window last", NULL, (SIGNAL_FUNC) cmd_window_last);
+       command_bind("window level", NULL, (SIGNAL_FUNC) cmd_window_level);
+       command_bind("window immortal", NULL, (SIGNAL_FUNC) cmd_window_immortal);
+       command_bind("window item", NULL, (SIGNAL_FUNC) cmd_window_item);
+       command_bind("window item prev", NULL, (SIGNAL_FUNC) cmd_window_item_prev);
+       command_bind("window item next", NULL, (SIGNAL_FUNC) cmd_window_item_next);
+       command_bind("window item goto", NULL, (SIGNAL_FUNC) cmd_window_item_goto);
+       command_bind("window item move", NULL, (SIGNAL_FUNC) cmd_window_item_move);
+       command_bind("window number", NULL, (SIGNAL_FUNC) cmd_window_number);
+       command_bind("window name", NULL, (SIGNAL_FUNC) cmd_window_name);
+       command_bind("window history", NULL, (SIGNAL_FUNC) cmd_window_history);
+       command_bind("window move", NULL, (SIGNAL_FUNC) cmd_window_move);
+       command_bind("window move prev", NULL, (SIGNAL_FUNC) cmd_window_move_prev);
+       command_bind("window move next", NULL, (SIGNAL_FUNC) cmd_window_move_next);
+       command_bind("window list", NULL, (SIGNAL_FUNC) cmd_window_list);
+       command_bind("window theme", NULL, (SIGNAL_FUNC) cmd_window_theme);
+       command_bind("layout", NULL, (SIGNAL_FUNC) cmd_layout);
+       /* SYNTAX: LAYOUT SAVE */
+       command_bind("layout save", NULL, (SIGNAL_FUNC) windows_layout_save);
+       /* SYNTAX: LAYOUT RESET */
+       command_bind("layout reset", NULL, (SIGNAL_FUNC) windows_layout_reset);
+       command_bind("foreach window", NULL, (SIGNAL_FUNC) cmd_foreach_window);
+
+       command_set_options("window number", "sticky");
+       command_set_options("window server", "sticky unsticky");
+       command_set_options("window theme", "delete");
+}
+
+void window_commands_deinit(void)
+{
+       command_unbind("window", (SIGNAL_FUNC) cmd_window);
+       command_unbind("window new", (SIGNAL_FUNC) cmd_window_new);
+       command_unbind("window close", (SIGNAL_FUNC) cmd_window_close);
+       command_unbind("window kill", (SIGNAL_FUNC) cmd_window_close);
+       command_unbind("window server", (SIGNAL_FUNC) cmd_window_server);
+       command_unbind("window refnum", (SIGNAL_FUNC) cmd_window_refnum);
+       command_unbind("window goto", (SIGNAL_FUNC) cmd_window_goto);
+       command_unbind("window previous", (SIGNAL_FUNC) cmd_window_previous);
+       command_unbind("window next", (SIGNAL_FUNC) cmd_window_next);
+       command_unbind("window last", (SIGNAL_FUNC) cmd_window_last);
+       command_unbind("window level", (SIGNAL_FUNC) cmd_window_level);
+       command_unbind("window immortal", (SIGNAL_FUNC) cmd_window_immortal);
+       command_unbind("window item", (SIGNAL_FUNC) cmd_window_item);
+       command_unbind("window item prev", (SIGNAL_FUNC) cmd_window_item_prev);
+       command_unbind("window item next", (SIGNAL_FUNC) cmd_window_item_next);
+       command_unbind("window item goto", (SIGNAL_FUNC) cmd_window_item_goto);
+       command_unbind("window item move", (SIGNAL_FUNC) cmd_window_item_move);
+       command_unbind("window number", (SIGNAL_FUNC) cmd_window_number);
+       command_unbind("window name", (SIGNAL_FUNC) cmd_window_name);
+       command_unbind("window history", (SIGNAL_FUNC) cmd_window_history);
+       command_unbind("window move", (SIGNAL_FUNC) cmd_window_move);
+       command_unbind("window move prev", (SIGNAL_FUNC) cmd_window_move_prev);
+       command_unbind("window move next", (SIGNAL_FUNC) cmd_window_move_next);
+       command_unbind("window list", (SIGNAL_FUNC) cmd_window_list);
+       command_unbind("window theme", (SIGNAL_FUNC) cmd_window_theme);
+       command_unbind("layout", (SIGNAL_FUNC) cmd_layout);
+       command_unbind("layout save", (SIGNAL_FUNC) windows_layout_save);
+       command_unbind("layout reset", (SIGNAL_FUNC) windows_layout_reset);
+       command_unbind("foreach window", (SIGNAL_FUNC) cmd_foreach_window);
+}
diff --git a/apps/irssi/src/fe-common/core/window-items.c b/apps/irssi/src/fe-common/core/window-items.c
new file mode 100644 (file)
index 0000000..e53e5a1
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ window-items.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "module-formats.h"
+#include "modules.h"
+#include "signals.h"
+#include "servers.h"
+#include "settings.h"
+
+#include "levels.h"
+
+#include "fe-windows.h"
+#include "window-items.h"
+#include "printtext.h"
+
+void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic)
+{
+       g_return_if_fail(window != NULL);
+       g_return_if_fail(item != NULL);
+       g_return_if_fail(item->window == NULL);
+
+        item->window = window;
+
+       if (window->items == NULL) {
+               window->active = item;
+               window->active_server = item->server;
+       }
+
+       if (!automatic || settings_get_bool("window_auto_change")) {
+               if (automatic)
+                       signal_emit("window changed automatic", 1, window);
+               window_set_active(window);
+       }
+
+       window->items = g_slist_append(window->items, item);
+       signal_emit("window item new", 2, window, item);
+
+       if (g_slist_length(window->items) == 1 ||
+           (!automatic && settings_get_bool("autofocus_new_items"))) {
+                window->active = NULL;
+               window_item_set_active(window, item);
+       }
+}
+
+void window_item_remove(WI_ITEM_REC *item)
+{
+       WINDOW_REC *window;
+
+       g_return_if_fail(item != NULL);
+
+       window = window_item_window(item);
+
+       if (window == NULL)
+               return;
+
+        item->window = NULL;
+       window->items = g_slist_remove(window->items, item);
+
+       if (window->active == item) {
+               window_item_set_active(window, window->items == NULL ? NULL :
+                                      window->items->data);
+       }
+
+       signal_emit("window item remove", 2, window, item);
+}
+
+void window_item_destroy(WI_ITEM_REC *item)
+{
+       WINDOW_REC *window;
+
+       window = window_item_window(item);
+        window_item_remove(item);
+        item->destroy(item);
+}
+
+void window_item_change_server(WI_ITEM_REC *item, void *server)
+{
+       WINDOW_REC *window;
+
+       g_return_if_fail(item != NULL);
+
+       window = window_item_window(item);
+       item->server = server;
+
+        signal_emit("window item server changed", 2, window, item);
+       if (window->active == item) window_change_server(window, item->server);
+}
+
+void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item)
+{
+        g_return_if_fail(window != NULL);
+
+        if (item != NULL && window_item_window(item) != window) {
+                /* move item to different window */
+                window_item_remove(item);
+                window_item_add(window, item, FALSE);
+        }
+
+       if (window->active != item) {
+               window->active = item;
+               if (item != NULL && window->active_server != item->server)
+                       window_change_server(window, item->server);
+               signal_emit("window item changed", 2, window, item);
+       }
+}
+
+/* Return TRUE if `item' is the active window item in the window.
+   `item' can be NULL. */
+int window_item_is_active(WI_ITEM_REC *item)
+{
+       WINDOW_REC *window;
+
+       if (item == NULL)
+               return FALSE;
+
+       window = window_item_window(item);
+       if (window == NULL)
+               return FALSE;
+
+       return window->active == item;
+}
+
+void window_item_prev(WINDOW_REC *window)
+{
+       WI_ITEM_REC *last;
+       GSList *tmp;
+
+       g_return_if_fail(window != NULL);
+
+       last = NULL;
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+               WI_ITEM_REC *rec = tmp->data;
+
+               if (rec != window->active)
+                       last = rec;
+               else {
+                       /* current channel. did we find anything?
+                          if not, go to the last channel */
+                       if (last != NULL) break;
+               }
+       }
+
+       if (last != NULL)
+                window_item_set_active(window, last);
+}
+
+void window_item_next(WINDOW_REC *window)
+{
+       WI_ITEM_REC *next;
+       GSList *tmp;
+       int gone;
+
+       g_return_if_fail(window != NULL);
+
+       next = NULL; gone = FALSE;
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+               WI_ITEM_REC *rec = tmp->data;
+
+               if (rec == window->active)
+                       gone = TRUE;
+               else {
+                       if (gone) {
+                               /* found the next channel */
+                               next = rec;
+                               break;
+                       }
+
+                       if (next == NULL)
+                               next = rec; /* fallback to first channel */
+               }
+       }
+
+       if (next != NULL)
+                window_item_set_active(window, next);
+}
+
+WI_ITEM_REC *window_item_find_window(WINDOW_REC *window,
+                                     void *server, const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+               WI_ITEM_REC *rec = tmp->data;
+
+               if ((server == NULL || rec->server == server) &&
+                   g_strcasecmp(name, rec->name) == 0) return rec;
+       }
+
+       return NULL;
+}
+
+/* Find wanted window item by name. `server' can be NULL. */
+WI_ITEM_REC *window_item_find(void *server, const char *name)
+{
+       WI_ITEM_REC *item;
+       GSList *tmp;
+
+       g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               item = window_item_find_window(rec, server, name);
+               if (item != NULL) return item;
+       }
+
+       return NULL;
+}
+
+static int window_bind_has_sticky(WINDOW_REC *window)
+{
+       GSList *tmp;
+
+       for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) {
+               WINDOW_BIND_REC *rec = tmp->data;
+
+               if (rec->sticky)
+                        return TRUE;
+       }
+
+        return FALSE;
+}
+
+void window_item_create(WI_ITEM_REC *item, int automatic)
+{
+       WINDOW_REC *window;
+        WINDOW_BIND_REC *bind;
+       GSList *tmp, *sorted;
+       int clear_waiting, reuse_unused_windows;
+
+       g_return_if_fail(item != NULL);
+
+       reuse_unused_windows = settings_get_bool("reuse_unused_windows");
+
+       clear_waiting = TRUE;
+       window = NULL;
+        sorted = windows_get_sorted();
+       for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+                /* is item bound to this window? */
+               if (item->server != NULL) {
+                       bind = window_bind_find(rec, item->server->tag,
+                                               item->name);
+                       if (bind != NULL) {
+                                if (!bind->sticky)
+                                       window_bind_destroy(rec, bind);
+                               window = rec;
+                               clear_waiting = FALSE;
+                               break;
+                       }
+               }
+
+               /* use this window IF:
+                    - reuse_unused_windows is ON
+                    - window has no existing items
+                    - window has no name
+                    - window has no sticky binds (/LAYOUT SAVEd)
+                    - we already haven't found "good enough" window,
+                      except if
+                         - this is the active window
+                         - old window had some temporary bounds and this
+                          one doesn't
+                    */
+               if (reuse_unused_windows && rec->items == NULL &&
+                   rec->name == NULL && !window_bind_has_sticky(rec) &&
+                   (window == NULL || rec == active_win ||
+                    window->bound_items != NULL))
+                       window = rec;
+       }
+        g_slist_free(sorted);
+
+        if (window == NULL && !settings_get_bool("autocreate_windows")) {
+                /* never create new windows automatically */
+                window = active_win;
+        }
+
+       if (window == NULL) {
+               /* create new window to use */
+               if (settings_get_bool("autocreate_split_windows")) {
+                       signal_emit("gui window create override", 1,
+                                   GINT_TO_POINTER(0));
+               }
+               window = window_create(item, automatic);
+       } else {
+               /* use existing window */
+               window_item_add(window, item, automatic);
+       }
+
+       if (clear_waiting)
+                window_bind_remove_unsticky(window);
+}
+
+static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item)
+{
+       g_return_if_fail(window != NULL);
+
+       if (g_slist_length(window->items) > 1) {
+               /* default to printing "talking with ...",
+                  you can override it it you wish */
+               printformat(item->server, item->name, MSGLEVEL_CLIENTNOTICE,
+                           TXT_TALKING_WITH, item->name);
+       }
+}
+
+void window_items_init(void)
+{
+       settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE);
+       settings_add_bool("lookandfeel", "autocreate_windows", TRUE);
+       settings_add_bool("lookandfeel", "autocreate_split_windows", FALSE);
+       settings_add_bool("lookandfeel", "autofocus_new_items", TRUE);
+
+       signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
+}
+
+void window_items_deinit(void)
+{
+       signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
+}
diff --git a/apps/irssi/src/fe-common/core/window-items.h b/apps/irssi/src/fe-common/core/window-items.h
new file mode 100644 (file)
index 0000000..f8db3c3
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __WINDOW_ITEMS_H
+#define __WINDOW_ITEMS_H
+
+#include "fe-windows.h"
+
+/* Add/remove/destroy window item from `window' */
+void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic);
+void window_item_remove(WI_ITEM_REC *item);
+void window_item_destroy(WI_ITEM_REC *item);
+
+/* Find a window for `item' and call window_item_add(). */
+void window_item_create(WI_ITEM_REC *item, int automatic);
+
+#define window_item_window(item) \
+       ((WINDOW_REC *) ((WI_ITEM_REC *) (item))->window)
+void window_item_change_server(WI_ITEM_REC *item, void *server);
+
+void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item);
+/* Return TRUE if `item' is the active window item in the window.
+   `item' can be NULL. */
+int window_item_is_active(WI_ITEM_REC *item);
+
+void window_item_prev(WINDOW_REC *window);
+void window_item_next(WINDOW_REC *window);
+
+/* Find wanted window item by name. `server' can be NULL. */
+WI_ITEM_REC *window_item_find(void *server, const char *name);
+WI_ITEM_REC *window_item_find_window(WINDOW_REC *window,
+                                     void *server, const char *name);
+
+void window_items_init(void);
+void window_items_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-common/core/windows-layout.c b/apps/irssi/src/fe-common/core/windows-layout.c
new file mode 100644 (file)
index 0000000..5d53f1a
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ windows-layout.c : irssi
+
+    Copyright (C) 2000-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "levels.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "chat-protocols.h"
+#include "servers.h"
+#include "queries.h"
+
+#include "module-formats.h"
+#include "printtext.h"
+#include "themes.h"
+#include "fe-windows.h"
+#include "window-items.h"
+
+static WINDOW_REC *restore_win;
+
+static void signal_query_created_curwin(QUERY_REC *query)
+{
+       g_return_if_fail(IS_QUERY(query));
+
+       window_item_add(restore_win, (WI_ITEM_REC *) query, TRUE);
+}
+
+static void sig_layout_restore_item(WINDOW_REC *window, const char *type,
+                                   CONFIG_NODE *node)
+{
+       char *name, *tag, *chat_type;
+
+       chat_type = config_node_get_str(node, "chat_type", NULL);
+       name = config_node_get_str(node, "name", NULL);
+       tag = config_node_get_str(node, "tag", NULL);
+
+       if (name == NULL || tag == NULL)
+               return;
+
+       if (g_strcasecmp(type, "CHANNEL") == 0) {
+               /* bind channel to window */
+               WINDOW_BIND_REC *rec = window_bind_add(window, tag, name);
+                rec->sticky = TRUE;
+       } else if (g_strcasecmp(type, "QUERY") == 0 && chat_type != NULL) {
+               /* create query immediately */
+               signal_add("query created",
+                          (SIGNAL_FUNC) signal_query_created_curwin);
+
+                restore_win = window;
+               chat_protocol_find(chat_type)->query_create(tag, name, TRUE);
+
+               signal_remove("query created",
+                             (SIGNAL_FUNC) signal_query_created_curwin);
+       }
+}
+
+static void window_add_items(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       GSList *tmp;
+       char *type;
+
+       if (node == NULL)
+               return;
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               CONFIG_NODE *node = tmp->data;
+
+               type = config_node_get_str(node, "type", NULL);
+               if (type != NULL) {
+                       signal_emit("layout restore item", 3,
+                                   window, type, node);
+               }
+       }
+}
+
+void windows_layout_restore(void)
+{
+       signal_emit("layout restore", 0);
+}
+
+static void sig_layout_restore(void)
+{
+       WINDOW_REC *window;
+       CONFIG_NODE *node;
+       GSList *tmp;
+
+       node = iconfig_node_traverse("windows", FALSE);
+       if (node == NULL) return;
+
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               CONFIG_NODE *node = tmp->data;
+
+               window = window_find_refnum(atoi(node->key));
+               if (window == NULL)
+                       window = window_create(NULL, TRUE);
+
+               window_set_refnum(window, atoi(node->key));
+                window->sticky_refnum = config_node_get_bool(node, "sticky_refnum", FALSE);
+                window->immortal = config_node_get_bool(node, "immortal", FALSE);
+               window_set_name(window, config_node_get_str(node, "name", NULL));
+               window_set_history(window, config_node_get_str(node, "history_name", NULL));
+               window_set_level(window, level2bits(config_node_get_str(node, "level", "")));
+
+               window->servertag = g_strdup(config_node_get_str(node, "servertag", NULL));
+               window->theme_name = g_strdup(config_node_get_str(node, "theme", NULL));
+               if (window->theme_name != NULL)
+                       window->theme = theme_load(window->theme_name);
+
+               window_add_items(window, config_node_section(node, "items", -1));
+               signal_emit("layout restore window", 2, window, node);
+       }
+}
+
+static void window_save_items(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       CONFIG_NODE *subnode;
+       GSList *tmp;
+       const char *type;
+
+       node = config_node_section(node, "items", NODE_TYPE_LIST);
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+               WI_ITEM_REC *rec = tmp->data;
+               SERVER_REC *server = rec->server;
+
+               type = module_find_id_str("WINDOW ITEM TYPE", rec->type);
+               if (type == NULL) continue;
+
+               subnode = config_node_section(node, NULL, NODE_TYPE_BLOCK);
+
+               iconfig_node_set_str(subnode, "type", type);
+               type = chat_protocol_find_id(rec->chat_type)->name;
+               iconfig_node_set_str(subnode, "chat_type", type);
+               iconfig_node_set_str(subnode, "name", rec->name);
+
+               if (server != NULL)
+                       iconfig_node_set_str(subnode, "tag", server->tag);
+               else if (IS_QUERY(rec)) {
+                       iconfig_node_set_str(subnode, "tag",
+                                            QUERY(rec)->server_tag);
+               }
+       }
+}
+
+static void window_save(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       char refnum[MAX_INT_STRLEN];
+
+        ltoa(refnum, window->refnum);
+       node = config_node_section(node, refnum, NODE_TYPE_BLOCK);
+
+       if (window->sticky_refnum)
+               iconfig_node_set_bool(node, "sticky_refnum", TRUE);
+
+       if (window->immortal)
+               iconfig_node_set_bool(node, "immortal", TRUE);
+
+       if (window->name != NULL)
+               iconfig_node_set_str(node, "name", window->name);
+
+       if (window->history_name != NULL)
+               iconfig_node_set_str(node, "history_name", window->history_name);
+
+       if (window->servertag != NULL)
+               iconfig_node_set_str(node, "servertag", window->servertag);
+       if (window->level != 0) {
+                char *level = bits2level(window->level);
+               iconfig_node_set_str(node, "level", level);
+               g_free(level);
+       }
+       if (window->theme_name != NULL)
+               iconfig_node_set_str(node, "theme", window->theme_name);
+
+       if (window->items != NULL)
+               window_save_items(window, node);
+
+       signal_emit("layout save window", 2, window, node);
+}
+
+void windows_layout_save(void)
+{
+       CONFIG_NODE *node;
+
+       iconfig_set_str(NULL, "windows", NULL);
+       node = iconfig_node_traverse("windows", TRUE);
+
+       g_slist_foreach(windows, (GFunc) window_save, node);
+       signal_emit("layout save", 0);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_WINDOWS_LAYOUT_SAVED);
+}
+
+void windows_layout_reset(void)
+{
+       iconfig_set_str(NULL, "windows", NULL);
+       signal_emit("layout reset", 0);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                   TXT_WINDOWS_LAYOUT_RESET);
+}
+
+void windows_layout_init(void)
+{
+       signal_add("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item);
+       signal_add("layout restore", (SIGNAL_FUNC) sig_layout_restore);
+}
+
+void windows_layout_deinit(void)
+{
+       signal_remove("layout restore item", (SIGNAL_FUNC) sig_layout_restore_item);
+       signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore);
+}
diff --git a/apps/irssi/src/fe-common/core/windows-layout.h b/apps/irssi/src/fe-common/core/windows-layout.h
new file mode 100644 (file)
index 0000000..d33fda5
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __WINDOWS_LAYOUT_H
+#define __WINDOWS_LAYOUT_H
+
+void windows_layout_restore(void);
+void windows_layout_save(void);
+void windows_layout_reset(void);
+
+void windows_layout_init(void);
+void windows_layout_deinit(void);
+
+#endif
index 74efdc416f593ef86126cd9d5347c24d75816cc4..72abc05ef880c6de8aaf73f42bba61c0d8802e64 100644 (file)
@@ -1,13 +1,13 @@
-INCLUDES = $(GLIB_CFLAGS) -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src
-
-SILC_INCLUDE=../../../..
 IRSSI_INCLUDE=../../..
 
-INCLUDES = \
+include $(top_srcdir)/Makefile.defines.in
+
+ADD_INCLUDES = \
         $(GLIB_CFLAGS) \
-        -DSYSCONFDIR=\""$(sysconfdir)"\" \
+        -DSYSCONFDIR=\""$(silc_etcdir)"\" \
         -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src \
         -I$(IRSSI_INCLUDE)/src/core \
+        -I$(IRSSI_INCLUDE)/src/fe-common/core \
         -I$(IRSSI_INCLUDE)/src/silc \
         -I$(IRSSI_INCLUDE)/src/silc/core \
         -I$(SILC_INCLUDE)/includes \
@@ -26,8 +26,10 @@ noinst_LIBRARIES = libfe_common_silc.a
 libfe_common_silc_a_SOURCES = \
        fe-channels.c \
        fe-common-silc.c \
+       module-formats.c \
        silc-modules.c 
 
 noinst_HEADERS = \
+       module-formats.h \
        fe-common-silc.h \
        module.h
index c779caee2aed33f53b31d44f495eed6bb646a5c4..0d7b8cc7690d04431d44f43c854f68c5c75898ae 100644 (file)
@@ -1,25 +1,27 @@
 /*
- fe-common-silc.c : irssi
 
-    Copyright (C) 2000 Timo Sirainen
+  fe-common-silc.c
 
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include "module.h"
+#include "module-formats.h"
 #include "signals.h"
+#include "themes.h"
 
 void fe_silc_channels_init(void);
 void fe_silc_channels_deinit(void);
@@ -29,15 +31,18 @@ void fe_silc_modules_deinit(void);
 
 void fe_common_silc_init(void)
 {
-       fe_silc_channels_init();
-       fe_silc_modules_init();
+  theme_register(fecommon_silc_formats);
+
+  fe_silc_channels_init();
+  fe_silc_modules_init();
 }
 
 void fe_common_silc_deinit(void)
 {
-       fe_silc_modules_deinit();
+  fe_silc_modules_deinit();
+  fe_silc_channels_deinit();
 
-       fe_silc_channels_deinit();
+  theme_unregister();
 }
 
 void fe_common_silc_finish_init(void)
index 2b30bffe7feb57cad3d7fbf289e0832c6bbb0692..3a259fd0d6edcdee5fd98fbfd435ff10c8ad02aa 100644 (file)
 /*
- module-formats.c : irssi
 
-    Copyright (C) 2000 Timo Sirainen
+  modules-formats.c
 
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include "module.h"
+#include "fe-common/core/formats.h"
 #include "printtext.h"
 
-FORMAT_REC fecommon_irc_formats[] = {
-       { MODULE_NAME, "IRC", 0 },
-
-       /* ---- */
-       { NULL, "Server", 0 },
-
-       { "lag_disconnected", "No PONG reply from server %_$0%_ in $1 seconds, disconnecting", 2, { 0, 1 } },
-       { "disconnected", "Disconnected from %_$0%_ %K[%n$1%K]", 2, { 0, 0 } },
-       { "server_list", "%_$0%_: $1:$2 ($3)", 5, { 0, 0, 1, 0, 0 } },
-       { "server_lookup_list", "%_$0%_: $1:$2 ($3) (connecting...)", 5, { 0, 0, 1, 0, 0 } },
-       { "server_reconnect_list", "%_$0%_: $1:$2 ($3) ($5 left before reconnecting)", 6, { 0, 0, 1, 0, 0, 0 } },
-       { "server_reconnect_removed", "Removed reconnection to server %_$0%_ port %_$1%_", 3, { 0, 1, 0 } },
-       { "server_reconnect_not_found", "Reconnection tag %_$0%_ not found", 1, { 0 } },
-       { "query_server_changed", "Query with %_$2%_ changed to server %_$1%_", 3, { 0, 0, 0 } },
-       { "setupserver_added", "Server $0 saved", 2, { 0, 1 } },
-       { "setupserver_removed", "Server $0 removed", 2, { 0, 1 } },
-       { "setupserver_not_found", "Server $0 not found", 2, { 0, 1 } },
-       { "setupserver_header", "Server               Port  IRC Net    Settings", 0 },
-       { "setupserver_line", "%|$[!20]0 $[5]1 $[10]2 $3", 4, { 0, 1, 0, 0 } },
-       { "setupserver_footer", "", 0 },
-       { "netsplit", "%RNetsplit%n %_$0%_ %_$1%_ quits: $2", 3, { 0, 0, 0 } },
-       { "netsplit_more", "%RNetsplit%n %_$0%_ %_$1%_ quits: $2 (+$3 more, use /NETSPLIT to show all of them)", 4, { 0, 0, 0, 1 } },
-       { "netsplit_join", "%CNetsplit%n over, joins: $0", 1, { 0 } },
-       { "netsplit_join_more", "%CNetsplit%n over, joins: $0 (+$1 more)", 2, { 0, 1 } },
-       { "no_netsplits", "There are no net splits", 0 },
-       { "netsplits_header", "Nick      Channel    Server               Splitted server", 0 },
-       { "netsplits_line", "$[9]0 $[10]1 $[20]2 $3", 4, { 0, 0, 0, 0 } },
-       { "netsplits_footer", "", 0 },
-       { "ircnet_added", "Ircnet $0 saved", 1, { 0 } },
-       { "ircnet_removed", "Ircnet $0 removed", 1, { 0 } },
-       { "ircnet_not_found", "Ircnet $0 not found", 1, { 0 } },
-       { "ircnet_header", "Ircnets:", 0 },
-       { "ircnet_line", "$0: $1", 2, { 0, 0 } },
-       { "ircnet_footer", "", 0 },
-
-       /* ---- */
-       { NULL, "Channels", 0 },
-
-       { "join", "%c%_$0%_ %K[%c$1%K]%n has joined %_$2", 3, { 0, 0, 0 } },
-       { "part", "%c$0 %K[%n$1%K]%n has left %_$2%_ %K[%n$3%K]", 4, { 0, 0, 0, 0 } },
-       { "joinerror_toomany", "Cannot join to channel %_$0%_ %K(%nYou have joined to too many channels%K)", 1, { 0 } },
-       { "joinerror_full", "Cannot join to channel %_$0%_ %K(%nChannel is full%K)", 1, { 0 } },
-       { "joinerror_invite", "Cannot join to channel %_$0%_ %K(%nYou must be invited%K)", 1, { 0 } },
-       { "joinerror_banned", "Cannot join to channel %_$0%_ %K(%nYou are banned%K)", 1, { 0 } },
-       { "joinerror_bad_key", "Cannot join to channel %_$0%_ %K(%nBad channel key%K)", 1, { 0 } },
-       { "joinerror_bad_mask", "Cannot join to channel %_$0%_ %K(%nBad channel mask%K)", 1, { 0 } },
-       { "joinerror_unavail", "Cannot join to channel %_$0%_ %K(%nChannel is temporarily unavailable%K)", 1, { 0 } },
-       { "kick", "%c$0%n was kicked from %_$1%_ by %_$2%_ %K[%n$3%K]", 4, { 0, 0, 0, 0 } },
-       { "quit", "%c$0 %K[%n$1%K]%n has quit IRC %K[%n$2%K]", 3, { 0, 0, 0 } },
-       { "quit_once", "%_$3%_ %c$0 %K[%n$1%K]%n has quit IRC %K[%n$2%K]", 4, { 0, 0, 0, 0 } },
-       { "invite", "%_$0%_ invites you to %_$1", 2, { 0, 0 } },
-       { "inviting", "Inviting $0 to %_$1", 2, { 0, 0 } },
-       { "not_invited", "You have not been invited to a channel!", 0 },
-       { "names", "%K[%g%_Users%_%K(%g$0%K)]%n $1", 2, { 0, 0 } },
-       { "names_nick", "%K[%n%_$0%_$1%K] ", 2, { 0, 0 } },
-       { "endofnames", "%g%_$0%_%K:%n Total of %_$1%_ nicks %K[%n%_$2%_ ops, %_$3%_ voices, %_$4%_ normal%K]", 5, { 0, 1, 1, 1, 1 } },
-       { "channel_created", "Channel %_$0%_ created $1", 2, { 0, 0 } },
-       { "topic", "Topic for %c$0%K:%n $1", 2, { 0, 0 } },
-       { "no_topic", "No topic set for %c$0", 1, { 0 } },
-       { "new_topic", "%_$0%_ changed the topic of %c$1%n to%K:%n $2", 3, { 0, 0, 0 } },
-       { "topic_unset", "Topic unset by %_$0%_ on %c$1", 2, { 0, 0 } },
-       { "topic_info", "Topic set by %_$0%_ %K[%n$1%K]", 2, { 0, 0 } },
-       { "chanmode_change", "mode/%c$0 %K[%n$1%K]%n by %_$2", 3, { 0, 0, 0 } },
-       { "server_chanmode_change", "%RServerMode/%c$0 %K[%n$1%K]%n by %_$2", 3, { 0, 0, 0 } },
-       { "channel_mode", "mode/%c$0 %K[%n$1%K]", 2, { 0, 0 } },
-       { "bantype", "Ban type changed to %_$0", 1, { 0 } },
-       { "no_bans", "No bans in channel %_$0%_", 1, { 0 } },
-       { "banlist", "%_$0%_: ban %c$1", 2, { 0, 0 } },
-       { "banlist_long", "%_$0%_: ban %c$1 %K[%nby %_$2%_, $3 secs ago%K]", 4, { 0, 0, 0, 1 } },
-       { "ebanlist", "%_$0%_: ban exception %c$1", 2, { 0, 0 } },
-       { "ebanlist_long", "%_$0%_: ban exception %c$1 %K[%nby %_$2%_, $3 secs ago%K]", 4, { 0, 0, 0, 1 } },
-       { "invitelist", "%_$0%_: invite %c$1", 2, { 0, 0 } },
-       { "no_such_channel", "$0: No such channel", 1, { 0 } },
-       { "channel_synced", "Join to %_$0%_ was synced in %_$1%_ secs", 2, { 0, 2 } },
-       { "not_in_channels", "You are not on any channels", 0 },
-       { "current_channel", "Current channel $0", 1, { 0 } },
-       { "chanlist_header", "You are on the following channels:", 0 },
-       { "chanlist_line", "$[-10]0 %|+$1 ($2): $3", 4, { 0, 0, 0, 0 } },
-       { "chansetup_not_found", "Channel $0 not found", 2, { 0, 0 } },
-       { "chansetup_added", "Channel $0 saved", 2, { 0, 0 } },
-       { "chansetup_removed", "Channel $0 removed", 2, { 0, 0 } },
-       { "chansetup_header", "Channel         IRC net    Password   Settings", 0 },
-       { "chansetup_line", "$[15]0 %|$[10]1 $[10]2 $3", 4, { 0, 0, 0, 0 } },
-       { "chansetup_footer", "", 0 },
-
-       /* ---- */
-       { NULL, "Nick", 0 },
-
-       { "usermode_change", "Mode change %K[%n%_$0%_%K]%n for user %c$1", 2, { 0, 0 } },
-       { "user_mode", "Your user mode is %K[%n%_$0%_%K]", 1, { 0 } },
-       { "away", "You have been marked as being away", 0 },
-       { "unaway", "You are no longer marked as being away", 0 },
-       { "nick_away", "$0 is away: $1", 2, { 0, 0 } },
-       { "no_such_nick", "$0: No such nick/channel", 1, { 0 } },
-       { "your_nick", "Your nickname is $0", 1, { 0 } },
-       { "your_nick_changed", "You're now known as %c$0", 1, { 0 } },
-       { "nick_changed", "%_$0%_ is now known as %c$1", 2, { 0, 0 } },
-       { "nick_in_use", "Nick %_$0%_ is already in use", 1, { 0 } },
-       { "nick_unavailable", "Nick %_$0%_ is temporarily unavailable", 1, { 0 } },
-       { "your_nick_owned", "Your nick is owned by %_$3%_ %K[%n$1@$2%K]", 4, { 0, 0, 0, 0 } },
-
-       /* ---- */
-       { NULL, "Who queries", 0 },
-
-       { "whois", "%_$0%_ %K[%n$1@$2%K]%n%: ircname  : $3", 4, { 0, 0, 0, 0 } },
-       { "whowas", "%_$0%_ %K[%n$1@$2%K]%n%: ircname  : $3", 4, { 0, 0, 0, 0 } },
-       { "whois_idle", " idle     : $1 days $2 hours $3 mins $4 secs", 5, { 0, 1, 1, 1, 1 } },
-       { "whois_idle_signon", " idle     : $1 days $2 hours $3 mins $4 secs %K[%nsignon: $5%K]", 6, { 0, 1, 1, 1, 1, 0 } },
-       { "whois_server", " server   : $1 %K[%n$2%K]", 3, { 0, 0, 0 } },
-       { "whois_oper", "          : %_IRC operator%_", 1, { 0 } },
-       { "whois_registered", "          : has registered this nick", 1, { 0 } },
-       { "whois_channels", " channels : $1", 2, { 0, 0 } },
-       { "whois_away", " away     : $1", 2, { 0, 0 } },
-       { "end_of_whois", "End of WHOIS", 1, { 0 } },
-       { "end_of_whowas", "End of WHOWAS", 1, { 0 } },
-       { "whois_not_found", "There is no such nick $0", 1, { 0 } },
-       { "who", "$[-10]0 %|%_$[!9]1%_ $[!3]2 $[!2]3 $4@$5 %K(%W$6%K)", 7, { 0, 0, 0, 0, 0, 0, 0 } },
-       { "end_of_who", "End of /WHO list", 1, { 0 } },
-
-       /* ---- */
-       { NULL, "Your messages", 0 },
-
-       { "own_msg", "%K<%n$2%W$0%K>%n %|$1", 3, { 0, 0, 0 } },
-       { "own_msg_channel", "%K<%n$3%W$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
-       { "own_msg_private", "%K[%rmsg%K(%R$0%K)]%n $1", 2, { 0, 0 } },
-       { "own_msg_private_query", "%K<%W$2%K>%n %|$1", 3, { 0, 0, 0 } },
-       { "own_notice", "%K[%rnotice%K(%R$0%K)]%n $1", 2, { 0, 0 } },
-       { "own_me", "%W * $0%n $1", 2, { 0, 0 } },
-       { "own_ctcp", "%K[%rctcp%K(%R$0%K)]%n $1 $2", 3, { 0, 0, 0 } },
-       { "own_wall", "%K[%WWall%K/%c$0%K]%n $1", 2, { 0, 0 } },
-
-       /* ---- */
-       { NULL, "Received messages", 0 },
-
-       { "pubmsg_me", "%K<%n$2%Y$0%K>%n %|$1", 3, { 0, 0, 0 } },
-       { "pubmsg_me_channel", "%K<%n$3%Y$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
-       { "pubmsg_hilight", "%K<%n$3$0$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
-       { "pubmsg_hilight_channel", "%K<%n$4$0$1%K:%c$2%K>%n %|$3", 5, { 0, 0, 0, 0, 0 } },
-       { "pubmsg", "%K<%n$2$0%K>%n %|$1", 3, { 0, 0, 0 } },
-       { "pubmsg_channel", "%K<%n$3$0%K:%c$1%K>%n %|$2", 4, { 0, 0, 0, 0 } },
-       { "msg_private", "%K[%R$0%K(%r$1%K)]%n $2", 3, { 0, 0, 0 } },
-       { "msg_private_query", "%K<%R$0%K>%n %|$2", 3, { 0, 0, 0 } },
-       { "notice_server", "%g!$0%n $1", 2, { 0, 0 } },
-       { "notice_public", "%K-%M$0%K:%m$1%K-%n $2", 3, { 0, 0, 0 } },
-       { "notice_public_ops", "%K-%M$0%K:%m@$1%K-%n $2", 3, { 0, 0, 0 } },
-       { "notice_private", "%K-%M$0%K(%m$1%K)-%n $2", 3, { 0, 0, 0 } },
-       { "action_private", "%W (*) $0%n $2", 3, { 0, 0, 0 } },
-       { "action_private_query", "%W * $0%n $2", 3, { 0, 0, 0 } },
-       { "action_public", "%W * $0%n $1", 2, { 0, 0 } },
-       { "action_public_channel", "%W * $0%K:%c$1%n $2", 3, { 0, 0, 0 } },
-
-       /* ---- */
-       { NULL, "CTCPs", 0 },
-
-       { "ctcp_reply", "CTCP %_$0%_ reply from %_$1%_%K:%n $2", 3, { 0, 0, 0 } },
-       { "ctcp_reply_channel", "CTCP %_$0%_ reply from %_$1%_ in channel %_$3%_%K:%n $2", 4, { 0, 0, 0, 0 } },
-       { "ctcp_ping_reply", "CTCP %_PING%_ reply from %_$0%_: $1.$2 seconds", 3, { 0, 2, 2 } },
-       { "ctcp_requested", "%g>>> %_$0%_ %K[%g$1%K] %grequested %_$2%_ from %_$3", 4, { 0, 0, 0, 0 } },
-
-       /* ---- */
-       { NULL, "Other server events", 0 },
-
-       { "online", "Users online: %_$0", 1, { 0 } },
-       { "pong", "PONG received from $0: $1", 2, { 0, 0 } },
-       { "wallops", "%WWALLOP%n $0: $1", 2, { 0, 0 } },
-       { "action_wallops", "%WWALLOP * $0%n $1", 2, { 0, 0 } },
-       { "error", "%_ERROR%_ $0", 1, { 0 } },
-       { "unknown_mode", "Unknown mode character $0", 1, { 0 } },
-       { "not_chanop", "You're not channel operator in $0", 1, { 0 } },
-
-       /* ---- */
+FORMAT_REC fecommon_silc_formats[] = {
+       { MODULE_NAME, "SILC", 0 },
+
+       /* Channel related messages */
+       { NULL, "Channel", 0 },
+
+       { "channel_founder_you", "You are channel founder on {channel $0}", 1, { 0 } },
+       { "channel_founder", "channel founder on {channel $0} is: {channick_hilight $1}", 2, { 0, 0 } },
+       { "channel_topic", "Topic for {channel $0} is: $1", 2, { 0, 0 } },
+       { "channel_topic_not_set", "Topic for {channel $0} not set", 1, { 0 } },
+       { "cmode", "channel mode/{channel $0} {mode $1} by {nick $2}", 3, { 0, 0, 0 } },
+       { "cumode", "channel user mode/{channel $0}/{nick $1} {mode $2} by {nick $3}", 4, { 0, 0, 0, 0 } },
+       { "action", "{action $0-}", 2, { 0, 0 } },
+       { "notice", "{notice $0-}", 2, { 0, 0 } },
+       { "ownaction", "{ownaction $0-}", 2, { 0, 0 } },
+       { "ownnotice", "{ownnotice $0-}", 2, { 0, 0 } },
+       { "invite_list", "channel {channel $0} invite list: $1", 2, { 0, 0 } },
+       { "no_invite_list", "channel {channel $0} invite list not set", 1, { 0 } },
+       { "ban_list", "channel {channel $0} ban list: $1", 2, { 0, 0 } },
+       { "no_ban_list", "channel {channel $0} ban list not set", 1, { 0 } },
+       { "inviting", "Inviting {nick $0} to channel {channel $1}", 2, { 0, 0 } },
+       { "kicked_you", "You have been kicked off channel {channel $0} by {nick $1} ($2)", 3, { 0, 0 } },
+       { "kicked", "{nick $0} has been kicked off channel {channel $1} by {nick $2} ($3)", 4, { 0, 0, 0 } },
+       { "killed_you", "You have been killed from the SILC Network", 0 },
+       { "killed", "{nick $0} has been killed from the SILC Network ($1)", 2, { 0, 0 } },
+
+       /* WHOIS, WHOWAS and USERS (alias WHO) messages */
+       { NULL, "Who Queries", 0 },
+
+       { "whois", "{nick $0} {nickhost $1@$2}%: nickname    : $3 ($4)", 5, { 0, 0, 0, 0, 0 } },
+       { "whois_realname",    " realname    : $0", 1, { 0 } },
+       { "whois_channels",    " channels    : $0", 1, { 0 } },
+       { "whois_modes",       " modes       : $0", 1, { 0 } },
+       { "whois_idle",        " idle        : $0", 1, { 0 } },
+       { "whois_fingerprint", " fingerprint : $0", 1, { 0 } },
+       { "whowas", "{nick $0} was {nickhost $1} ($2)", 3, { 0, 0, 0 } },
+       { "users_header", "Users on {channelhilight $0}", 1, { 0 } },
+       { "users", " %|{nick $[!20]0} $[!5]1 $2@$3 {comment {hilight $4}}", 5, { 0, 0, 0, 0, 0 } },
+
+       /* Key management and key agreement */
+       { NULL, "Key Management And Key Agreement", 0 },
+
+       { "channel_private_key_add", "Private key set to channel {channel $0}", 1, { 0 } },
+       { "channel_private_key_nomode", "Private key mode is not set on channel {channel $0}", 1, { 0 } },
+       { "channel_private_key_error", "Could not add private key to channel {channel $0}", 1, { 0 } },
+       { "channel_private_key_list", "Channel {channel $0} private keys%:  Cipher           Hmac             Key", 1, { 0 } },
+       { "private_key_list", "Private message keys%:  Client                         Cipher         Key", 0 },
+       { "private_key_list_nick", "Private message keys with {nick $0}%:  Client                         Cipher         Key", 1, { 0 } },
+       { "key_agreement", "Requesting key agreement with {nick $0}", 1, { 0 } },
+       { "key_agreement_request", "{nick $0} wants to perform key agreement", 1, { 0 } },
+       { "key_agreement_request_host", "{nick $0} wants to perform key agreement on {nickhost $1} port {hilight $2}", 3, { 0, 0, 0 } },
+       { "key_agreement_negotiate", "Starting key agreement with {nick $0}", 1, { 0 } },
+       { "key_agreement_privmsg", "The private messages with {nick $0} are now protected with private key", 1, { 0 } },
+       { "key_agreement_ok", "Key agreement completed successfully with {nick $0}", 1, { 0 } },
+       { "key_agreement_error", "Error occurred during key agreement with {nick $0}", 1, { 0 } },
+       { "key_agreement_failure", "Key agreement failed with {nick $0}", 1, { 0 } },
+       { "key_agreement_timeout", "Timeout during key agreement. The key agreement was not performed with {nick $0}", 1, { 0 } },
+       { "key_agreement_aborted", "Key agreement was aborted with {nick $0}", 1, { 0 } },
+       { "pubkey_received", "Received {hilight $0} public key", 1, { 0 } },
+       { "pubkey_fingerprint", "Fingerprint and babbleprint for the {hilight $0} key are %: $1", 2, { 0, 0 } },
+       { "pubkey_babbleprint", " $0", 1, { 0 } },
+       { "pubkey_unsupported", "We don't support {hilight $0} public key type {hilight $1}", 2, { 0, 0 } },
+       { "pubkey_discard", "Will not accept the {hilight $0} key", 1, { 0 } },
+       { "pubkey_accept", "Would you like to accept the key (y/n)? ", 0 },
+       { "pubkey_accept_anyway", "Would you like to accept the key anyway (y/n)? ", 0 },
+       { "pubkey_could_not_load", "Could not load your local copy of the {hilight $0} key", 1, { 0 } },
+       { "pubkey_malformed", "Your local copy of the {hilight $0} key is malformed", 1, { 0 } },
+       { "pubkey_no_match", "{hilight $0} key does not match with your local copy", 1, { 0 } },
+       { "pubkey_maybe_expired", "It is possible that the key has expired or changed", 0 },
+       { "pubkey_mitm_attack", "It is also possible that someone is performing a man-in-the-middle attack", 0 },
+       { "getkey_notkey", "Server did not return any public key", 0 },
+       { "getkey_verified", "Verified successfully $0 {hilight $1}'s cached public key", 2, { 0, 0 } },
+       { "getkey_discard", "Could not verify $0 {hilight $1}'s public key", 2, { 0, 0 } },
+
+       /* Misc messages */
        { NULL, "Misc", 0 },
 
-       { "ignored", "Ignoring %_$1%_ from %_$0%_", 2, { 0, 0 } },
-       { "unignored", "Unignored %_$0%_", 1, { 0 } },
-       { "ignore_not_found", "%_$0%_ is not being ignored", 1, { 0 } },
-       { "ignore_no_ignores", "There are no ignores", 0 },
-       { "ignore_header", "Ignorance List:", 0 },
-       { "ignore_line", "$[-4]0 $1: $2 $3 $4", 4, { 1, 0, 0, 0 } },
-       { "ignore_footer", "", 0 },
-       { "talking_in", "You are now talking in %_$0%_", 1, { 0 } },
-       { "query_start", "Starting query with %_$0%_", 1, { 0 } },
-       { "no_query", "No query with %_$0%_", 1, { 0 } },
-       { "no_msgs_got", "You have not received a message from anyone yet", 0 },
-       { "no_msgs_sent", "You have not sent a message to anyone yet", 0 },
+       { "server_oper", "You are now {hilight server operator}", 0 },
+       { "router_oper", "You are now {hilight SILC operator}", 0 },
+       { "list_header", "  Channel                              Users   Topic", 0 },
+       { "list", "  %|{channelhilight $[36]0} {hilight $[7]1} $2", 3, { 0, 0, 0 } },
+       { "bad_nick", "Bad nickname {hilight $0}", 1, { 0 } },
+       { "unknown_notify", "Unknown notify type {hilight $0}", 1, { 0 } },
+       { "ke_bad_version", "You are running an incompatible client version (it may be too old or too new) ", 0 },
+       { "ke_unsupported_public_key", "Remote does not trust/support your public key", 0 },
+       { "ke_unknown_group", "Remote does not support one of your proposed KE group", 0 },
+       { "ke_unknown_cipher", "Remote does not support one of your proposed cipher", 0 },
+       { "ke_unknown_pkcs", "Remote does not support one of your proposed PKCS", 0 },
+       { "ke_unknown_hash_function", "Remote does not support one of your proposed hash function", 0 },
+       { "ke_unknown_hmac", "Remote does not support one of your proposed HMAC", 0 },
+       { "ke_incorrect_signature", "Incorrect signature", 0 },
+       { "ke_invalid_cookie", "Invalid cookie", 0 },
+       { "auth_failed", "Authentication failed", 0 },
+       { "set_away", "You have been marked as being away (away message: {hilight $0})", 1, { 0 } },
+       { "unset_away", "You are no longer marked as being away", 0 },
+       { "auth_meth_unresolved", "Could not resolve authentication method to use, assume no authentication", 0 },
+
+       /* File transfer messages */
+       { NULL, "FileTransfer", 0 },
+
+       { "file_send", "File transfer request sent to {nick $0} for $1", 2, { 0, 0 } },
+       { "file_transmit", "Transmitting file {hilight $0} [$1kB] to {nick $2}", 3, { 0, 0, 0 } },
+       { "file_transmitted", "Transmitted file {hilight $0} [$1kB] to {nick $2} [{hilight $3kB/s}]", 4, { 0, 0, 0, 3 } },
+       { "file_receive", "Receiving file {hilight $0} [$1kB] from {nick $2}", 3, { 0, 0 } },
+       { "file_received", "Received file {hilight $0} [$1kB] from {nick $2} [{hilight $3kB/s}]", 4, { 0, 0, 0, 3 } },
+       { "file_request", "File transfer request from {nick $0}", 1, { 0 } },
+       { "file_request_host", "File transfer request from {nick $0} [$1 port $2]", 3, { 0, 0, 0 } },
+       { "file_key_exchange", "Negotiating keys for file transfer with {nick $0}", 1, { 0 } },
+       { "file_na", "No file transfers available", 0 },
+       { "file_client_na", "No file transfer offered by {nick $0}", 1, { 0 } },
+       { "file_show_header", "File transfers", 0 },
+       { "file_show_line", "  $0 $1: $2kB of $3kB ($4%%) - $5kB/s - $6", 7, { 0, 0, 1, 1, 1, 3, 0 } },
+       { "file_already_started", "File transfer already started with {nick $0}", 1, { 0  } },
+       { "file_error", "Error during file transfer with {nick $0}", 1, { 0 } },
+       { "file_error_no_such_file", "Error during file transfer with {nick $0}: $1: No such file", 2, { 0, 0 } },
+       { "file_error_permission_denied", "Error during file transfer with {nick $0}: Permission denied", 1, { 0 } },
+       { "file_close", "File transfer closed with {nick $0} - $1", 2, { 0, 0 } },
 
        { NULL, NULL, 0 }
 };
index aa4c87c3e3c916b4d34f9a756a3dd2ed9538cc1e..62b2db5a2c50f4fc5ac4f8f3e3e8a822cd091fd5 100644 (file)
-#include "printtext.h"
+/*
+
+  modules-formats.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "fe-common/core/formats.h"
 
 enum {
-       IRCTXT_MODULE_NAME,
-
-       IRCTXT_FILL_1,
-
-       IRCTXT_LAG_DISCONNECTED,
-       IRCTXT_DISCONNECTED,
-       IRCTXT_SERVER_LIST,
-       IRCTXT_SERVER_LOOKUP_LIST,
-       IRCTXT_SERVER_RECONNECT_LIST,
-       IRCTXT_RECONNECT_REMOVED,
-       IRCTXT_RECONNECT_NOT_FOUND,
-       IRCTXT_QUERY_SERVER_CHANGED,
-       IRCTXT_SETUPSERVER_ADDED,
-       IRCTXT_SETUPSERVER_REMOVED,
-       IRCTXT_SETUPSERVER_NOT_FOUND,
-       IRCTXT_SETUPSERVER_HEADER,
-       IRCTXT_SETUPSERVER_LINE,
-       IRCTXT_SETUPSERVER_FOOTER,
-       IRCTXT_NETSPLIT,
-       IRCTXT_NETSPLIT_MORE,
-       IRCTXT_NETSPLIT_JOIN,
-       IRCTXT_NETSPLIT_JOIN_MORE,
-       IRCTXT_NO_NETSPLITS,
-       IRCTXT_NETSPLITS_HEADER,
-       IRCTXT_NETSPLITS_LINE,
-       IRCTXT_NETSPLITS_FOOTER,
-       IRCTXT_IRCNET_ADDED,
-       IRCTXT_IRCNET_REMOVED,
-       IRCTXT_IRCNET_NOT_FOUND,
-       IRCTXT_IRCNET_HEADER,
-       IRCTXT_IRCNET_LINE,
-       IRCTXT_IRCNET_FOOTER,
-
-       IRCTXT_FILL_2,
-
-       IRCTXT_JOIN,
-       IRCTXT_PART,
-       IRCTXT_JOINERROR_TOOMANY,
-       IRCTXT_JOINERROR_FULL,
-       IRCTXT_JOINERROR_INVITE,
-       IRCTXT_JOINERROR_BANNED,
-       IRCTXT_JOINERROR_BAD_KEY,
-       IRCTXT_JOINERROR_BAD_MASK,
-       IRCTXT_JOINERROR_UNAVAIL,
-       IRCTXT_KICK,
-       IRCTXT_QUIT,
-       IRCTXT_QUIT_ONCE,
-       IRCTXT_INVITE,
-       IRCTXT_INVITING,
-       IRCTXT_NOT_INVITED,
-       IRCTXT_NAMES,
-       IRCTXT_NAMES_NICK,
-       IRCTXT_ENDOFNAMES,
-       IRCTXT_CHANNEL_CREATED,
-       IRCTXT_TOPIC,
-       IRCTXT_NO_TOPIC,
-       IRCTXT_NEW_TOPIC,
-       IRCTXT_TOPIC_UNSET,
-       IRCTXT_TOPIC_INFO,
-       IRCTXT_CHANMODE_CHANGE,
-       IRCTXT_SERVER_CHANMODE_CHANGE,
-       IRCTXT_CHANNEL_MODE,
-       IRCTXT_BANTYPE,
-       IRCTXT_NO_BANS,
-       IRCTXT_BANLIST,
-       IRCTXT_BANLIST_LONG,
-       IRCTXT_EBANLIST,
-       IRCTXT_EBANLIST_LONG,
-       IRCTXT_INVITELIST,
-       IRCTXT_NO_SUCH_CHANNEL,
-       IRCTXT_CHANNEL_SYNCED,
-       IRCTXT_NOT_IN_CHANNELS,
-       IRCTXT_CURRENT_CHANNEL,
-       IRCTXT_CHANLIST_HEADER,
-       IRCTXT_CHANLIST_LINE,
-       IRCTXT_CHANSETUP_NOT_FOUND,
-       IRCTXT_CHANSETUP_ADDED,
-       IRCTXT_CHANSETUP_REMOVED,
-       IRCTXT_CHANSETUP_HEADER,
-       IRCTXT_CHANSETUP_LINE,
-       IRCTXT_CHANSETUP_FOOTER,
-
-       IRCTXT_FILL_4,
-
-       IRCTXT_USERMODE_CHANGE,
-       IRCTXT_USER_MODE,
-       IRCTXT_AWAY,
-       IRCTXT_UNAWAY,
-       IRCTXT_NICK_AWAY,
-       IRCTXT_NO_SUCH_NICK,
-       IRCTXT_YOUR_NICK,
-       IRCTXT_YOUR_NICK_CHANGED,
-       IRCTXT_NICK_CHANGED,
-       IRCTXT_NICK_IN_USE,
-       IRCTXT_NICK_UNAVAILABLE,
-       IRCTXT_YOUR_NICK_OWNED,
-
-       IRCTXT_FILL_5,
-
-       IRCTXT_WHOIS,
-       IRCTXT_WHOWAS,
-       IRCTXT_WHOIS_IDLE,
-       IRCTXT_WHOIS_IDLE_SIGNON,
-       IRCTXT_WHOIS_SERVER,
-       IRCTXT_WHOIS_OPER,
-       IRCTXT_WHOIS_REGISTERED,
-       IRCTXT_WHOIS_CHANNELS,
-       IRCTXT_WHOIS_AWAY,
-       IRCTXT_END_OF_WHOIS,
-       IRCTXT_END_OF_WHOWAS,
-       IRCTXT_WHOIS_NOT_FOUND,
-       IRCTXT_WHO,
-       IRCTXT_END_OF_WHO,
-
-       IRCTXT_FILL_6,
-
-       IRCTXT_OWN_MSG,
-       IRCTXT_OWN_MSG_CHANNEL,
-       IRCTXT_OWN_MSG_PRIVATE,
-       IRCTXT_OWN_MSG_PRIVATE_QUERY,
-       IRCTXT_OWN_NOTICE,
-       IRCTXT_OWN_ME,
-       IRCTXT_OWN_CTCP,
-       IRCTXT_OWN_WALL,
-       
-       IRCTXT_FILL_7,
-
-       IRCTXT_PUBMSG_ME,
-       IRCTXT_PUBMSG_ME_CHANNEL,
-       IRCTXT_PUBMSG_HILIGHT,
-       IRCTXT_PUBMSG_HILIGHT_CHANNEL,
-       IRCTXT_PUBMSG,
-       IRCTXT_PUBMSG_CHANNEL,
-       IRCTXT_MSG_PRIVATE,
-       IRCTXT_MSG_PRIVATE_QUERY,
-       IRCTXT_NOTICE_SERVER,
-       IRCTXT_NOTICE_PUBLIC,
-       IRCTXT_NOTICE_PUBLIC_OPS,
-       IRCTXT_NOTICE_PRIVATE,
-       IRCTXT_ACTION_PRIVATE,
-       IRCTXT_ACTION_PRIVATE_QUERY,
-       IRCTXT_ACTION_PUBLIC,
-       IRCTXT_ACTION_PUBLIC_CHANNEL,
-
-       IRCTXT_FILL_8,
-
-       IRCTXT_CTCP_REPLY,
-       IRCTXT_CTCP_REPLY_CHANNEL,
-       IRCTXT_CTCP_PING_REPLY,
-       IRCTXT_CTCP_REQUESTED,
-
-       IRCTXT_FILL_10,
-
-       IRCTXT_ONLINE,
-       IRCTXT_PONG,
-       IRCTXT_WALLOPS,
-       IRCTXT_ACTION_WALLOPS,
-       IRCTXT_ERROR,
-       IRCTXT_UNKNOWN_MODE,
-       IRCTXT_NOT_CHANOP,
-
-       IRCTXT_FILL_11,
-
-       IRCTXT_IGNORED,
-       IRCTXT_UNIGNORED,
-       IRCTXT_IGNORE_NOT_FOUND,
-       IRCTXT_IGNORE_NO_IGNORES,
-       IRCTXT_IGNORE_HEADER,
-       IRCTXT_IGNORE_LINE,
-       IRCTXT_IGNORE_FOOTER,
-       IRCTXT_TALKING_IN,
-       IRCTXT_QUERY_STARTED,
-       IRCTXT_NO_QUERY,
-       IRCTXT_NO_MSGS_GOT,
-       IRCTXT_NO_MSGS_SENT
+  SILCTXT_MODULE_NAME,
+  
+  SILCTXT_FILL_1,
+  
+  SILCTXT_CHANNEL_FOUNDER_YOU,
+  SILCTXT_CHANNEL_FOUNDER,
+  SILCTXT_CHANNEL_TOPIC,
+  SILCTXT_CHANNEL_TOPIC_NOT_SET,
+  SILCTXT_CHANNEL_CMODE,
+  SILCTXT_CHANNEL_CUMODE,
+  SILCTXT_CHANNEL_ACTION,
+  SILCTXT_CHANNEL_NOTICE,
+  SILCTXT_CHANNEL_OWNACTION,
+  SILCTXT_CHANNEL_OWNNOTICE,
+  SILCTXT_CHANNEL_INVITE_LIST,
+  SILCTXT_CHANNEL_NO_INVITE_LIST,
+  SILCTXT_CHANNEL_BAN_LIST,
+  SILCTXT_CHANNEL_NO_BAN_LIST,
+  SILCTXT_CHANNEL_INVITING,
+  SILCTXT_CHANNEL_KICKED_YOU,
+  SILCTXT_CHANNEL_KICKED,
+  SILCTXT_CHANNEL_KILLED_YOU,
+  SILCTXT_CHANNEL_KILLED,
+
+  SILCTXT_FILL_2,
+
+  SILCTXT_WHOIS_USERINFO,
+  SILCTXT_WHOIS_REALNAME,
+  SILCTXT_WHOIS_CHANNELS,
+  SILCTXT_WHOIS_MODES,
+  SILCTXT_WHOIS_IDLE,
+  SILCTXT_WHOIS_FINGERPRINT,
+  SILCTXT_WHOWAS_USERINFO,
+  SILCTXT_USERS_HEADER,
+  SILCTXT_USERS,
+
+  SILCTXT_FILL_3,
+
+  SILCTXT_CH_PRIVATE_KEY_ADD,
+  SILCTXT_CH_PRIVATE_KEY_NOMODE,
+  SILCTXT_CH_PRIVATE_KEY_ERROR,
+  SILCTXT_CH_PRIVATE_KEY_LIST,
+  SILCTXT_PRIVATE_KEY_LIST,
+  SILCTXT_PRIVATE_KEY_LIST_NICK,
+  SILCTXT_KEY_AGREEMENT,
+  SILCTXT_KEY_AGREEMENT_REQUEST,
+  SILCTXT_KEY_AGREEMENT_REQUEST_HOST,
+  SILCTXT_KEY_AGREEMENT_NEGOTIATE,
+  SILCTXT_KEY_AGREEMENT_PRIVMSG,
+  SILCTXT_KEY_AGREEMENT_OK,
+  SILCTXT_KEY_AGREEMENT_ERROR,
+  SILCTXT_KEY_AGREEMENT_FAILURE,
+  SILCTXT_KEY_AGREEMENT_TIMEOUT,
+  SILCTXT_KEY_AGREEMENT_ABORTED,
+  SILCTXT_PUBKEY_RECEIVED,
+  SILCTXT_PUBKEY_FINGERPRINT,
+  SILCTXT_PUBKEY_BABBLEPRINT,
+  SILCTXT_PUBKEY_UNSUPPORTED,
+  SILCTXT_PUBKEY_DISCARD,
+  SILCTXT_PUBKEY_ACCEPT,
+  SILCTXT_PUBKEY_ACCEPT_ANYWAY,
+  SILCTXT_PUBKEY_COULD_NOT_LOAD,
+  SILCTXT_PUBKEY_MALFORMED,
+  SILCTXT_PUBKEY_NO_MATCH,
+  SILCTXT_PUBKEY_MAYBE_EXPIRED,
+  SILCTXT_PUBKEY_MITM_ATTACK,
+  SILCTXT_GETKEY_NOKEY,
+  SILCTXT_GETKEY_VERIFIED,
+  SILCTXT_GETKEY_DISCARD,
+
+  SILCTXT_FILL_4,
+
+  SILCTXT_SERVER_OPER,
+  SILCTXT_ROUTER_OPER,
+  SILCTXT_LIST_HEADER,
+  SILCTXT_LIST,
+  SILCTXT_BAD_NICK,
+  SILCTXT_UNKNOWN_NOTIFY,
+  SILCTXT_KE_BAD_VERSION,
+  SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY,
+  SILCTXT_KE_UNKNOWN_GROUP,
+  SILCTXT_KE_UNKNOWN_CIPHER,
+  SILCTXT_KE_UNKNOWN_PKCS,
+  SILCTXT_KE_UNKNOWN_HASH_FUNCTION,
+  SILCTXT_KE_UNKNOWN_HMAC,
+  SILCTXT_KE_INCORRECT_SIGNATURE,
+  SILCTXT_KE_INVALID_COOKIE,
+  SILCTXT_AUTH_FAILED,
+  SILCTXT_SET_AWAY,
+  SILCTXT_UNSET_AWAY,
+  SILCTXT_AUTH_METH_UNRESOLVED,
+
+  SILCTXT_FILL_5,
+
+  SILCTXT_FILE_SEND,
+  SILCTXT_FILE_TRANSMIT,
+  SILCTXT_FILE_TRANSMITTED,
+  SILCTXT_FILE_RECEIVE,
+  SILCTXT_FILE_RECEIVED,
+  SILCTXT_FILE_REQUEST,
+  SILCTXT_FILE_REQUEST_HOST,
+  SILCTXT_FILE_KEY_EXCHANGE,
+  SILCTXT_FILE_NA,
+  SILCTXT_FILE_CLIENT_NA,
+  SILCTXT_FILE_SHOW_HEADER,
+  SILCTXT_FILE_SHOW_LINE,
+  SILCTXT_FILE_ALREADY_STARTED,
+  SILCTXT_FILE_ERROR,
+  SILCTXT_FILE_ERROR_NO_SUCH_FILE,
+  SILCTXT_FILE_ERROR_PERMISSION_DENIED,
+  SILCTXT_FILE_CLOSED,
 };
 
-extern FORMAT_REC fecommon_irc_formats[];
+extern FORMAT_REC fecommon_silc_formats[];
diff --git a/apps/irssi/src/fe-common/silc/silc-modules.c b/apps/irssi/src/fe-common/silc/silc-modules.c
deleted file mode 100644 (file)
index 16081e1..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-/* this file is automatically generated by configure - don't change */
-void fe_silc_modules_init(void) {  }
-void fe_silc_modules_deinit(void) {  }
diff --git a/apps/irssi/src/fe-text/.cvsignore b/apps/irssi/src/fe-text/.cvsignore
new file mode 100644 (file)
index 0000000..6974ee8
--- /dev/null
@@ -0,0 +1,9 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+irssi
index ef0f561662ab1904920eed23288a4e12081f56ab..6407d8611aa92094059f4d1cc8fc1a7b3ba60f3e 100644 (file)
@@ -1,24 +1,49 @@
 bin_PROGRAMS = silc
 
+include $(top_srcdir)/Makefile.defines.in
+
 INCLUDES = \
-       $(GLIB_CFLAGS) \
        -I$(top_srcdir)/src \
        -I$(top_srcdir)/src/core/ \
-       -I$(top_srcdir)/src/silc/core/ \
        -I$(top_srcdir)/src/fe-common/core/ \
        -I$(top_srcdir)/src/fe-common/silc/ \
+       $(GLIB_CFLAGS) \
        $(CURSES_INCLUDEDIR) \
        -DLOCALEDIR=\""$(datadir)/locale"\"
 
-silc_DEPENDENCIES = @COMMON_LIBS@
+silc_DEPENDENCIES =            \
+       @COMMON_LIBS@           \
+       @PERL_LINK_LIBS@        \
+       @PERL_FE_LINK_LIBS@
 
+LIBS = $(SILC_COMMON_LIBS)
 silc_LDADD = \
        @COMMON_LIBS@ \
        @PERL_LINK_LIBS@ \
        @PERL_FE_LINK_LIBS@ \
-       $(PROG_LIBS) \
-        $(CURSES_LIBS) \
-       -L../../../lib -lsilcclient -lsilc
+       @PERL_LINK_FLAGS@ \
+       @PROG_LIBS@     \
+       -L../../../lib -lsilcclient
+
+tparm_sources = \
+       tparm.c
+
+terminfo_sources = \
+        term-terminfo.c \
+        terminfo-core.c
+
+curses_sources = \
+       term-curses.c
+
+if NEED_TPARM
+use_tparm_sources = $(tparm_sources)
+endif
+
+if USE_CURSES
+use_term_sources = $(curses_sources)
+else
+use_term_sources = $(terminfo_sources)
+endif
 
 silc_SOURCES = \
         gui-entry.c \
@@ -29,13 +54,19 @@ silc_SOURCES = \
        lastlog.c \
         mainwindows.c \
         mainwindow-activity.c \
-        mainwindows-save.c \
-        screen.c \
+        mainwindows-layout.c \
         statusbar.c \
+        statusbar-config.c \
         statusbar-items.c \
+        term.c \
+       term-dummy.c \
+       $(use_tparm_sources) \
+       $(use_term_sources) \
         textbuffer.c \
         textbuffer-commands.c \
+        textbuffer-reformat.c \
         textbuffer-view.c \
+       utf8.c \
         silc.c \
        module-formats.c
 
@@ -46,8 +77,17 @@ noinst_HEADERS = \
         gui-windows.h \
         mainwindows.h \
         statusbar.h \
-        screen.h \
+        statusbar-config.h \
+       term.h \
+       terminfo-core.h \
         textbuffer.h \
         textbuffer-view.h \
+        textbuffer-reformat.h \
+       utf8.h \
        module.h \
        module-formats.h
+
+EXTRA_DIST = \
+       $(tparm_sources) \
+       $(terminfo_sources) \
+       $(curses_sources)
index 7544539a304c4059ef586f1d263bfe2e7aee4764..f70bf0dfe04e22279198230f460aed0f90edbdc8 100644 (file)
 */
 
 #include "module.h"
+#include "utf8.h"
 #include "formats.h"
 
+#include "gui-entry.h"
 #include "gui-printtext.h"
-#include "screen.h"
+#include "term.h"
 
-static GString *entry;
-static int promptlen, permanent_prompt, pos, scrstart, scrpos;
-static int prompt_hidden;
-static char *prompt;
+GUI_ENTRY_REC *active_entry;
 
-static void entry_screenpos(void)
+GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
 {
-       if (pos-scrstart < COLS-2-promptlen && pos-scrstart > 0) {
-               scrpos = pos-scrstart;
-               return;
-       }
+       GUI_ENTRY_REC *rec;
+
+       rec = g_new0(GUI_ENTRY_REC, 1);
+       rec->xpos = xpos;
+       rec->ypos = ypos;
+        rec->width = width;
+       rec->text = g_string_new(NULL);
+        rec->utf8 = utf8;
+       return rec;
+}
+
+void gui_entry_destroy(GUI_ENTRY_REC *entry)
+{
+        g_return_if_fail(entry != NULL);
 
-       if (pos < COLS-1-promptlen) {
-               scrstart = 0;
-               scrpos = pos;
+       if (active_entry == entry)
+               gui_entry_set_active(NULL);
+
+       g_free_not_null(entry->prompt);
+       g_string_free(entry->text, TRUE);
+        g_free(entry);
+}
+
+/* Fixes the cursor position in screen */
+static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
+{
+       int old_scrstart;
+
+        old_scrstart = entry->scrstart;
+       if (entry->pos - entry->scrstart < entry->width-2 - entry->promptlen &&
+           entry->pos - entry->scrstart > 0) {
+               entry->scrpos = entry->pos - entry->scrstart;
+       } else if (entry->pos < entry->width-1 - entry->promptlen) {
+               entry->scrstart = 0;
+               entry->scrpos = entry->pos;
        } else {
-               scrpos = (COLS-promptlen)*2/3;
-               scrstart = pos-scrpos;
+               entry->scrpos = (entry->width - entry->promptlen)*2/3;
+               entry->scrstart = entry->pos - entry->scrpos;
        }
+
+       if (old_scrstart != entry->scrstart)
+                entry->redraw_needed_from = 0;
 }
 
-static void entry_update(void)
+static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
 {
-       char *p;
-       int n, len;
-
-       len = entry->len-scrstart > COLS-1-promptlen ?
-               COLS-1-promptlen : entry->len-scrstart;
-
-       set_color(stdscr, 0);
-       move(LINES-1, promptlen);
-
-       for (p = entry->str+scrstart, n = 0; n < len; n++, p++) {
-               if (prompt_hidden)
-                        addch(' ');
-               else if ((unsigned char) *p >= 32)
-                       addch((unsigned char) *p);
-               else {
-                       set_color(stdscr, ATTR_REVERSE);
-                       addch(*p+'A'-1);
-                       set_color(stdscr, 0);
+       const unsigned char *p, *end;
+       int xpos, end_xpos;
+
+       if (entry->utf8) {
+               /* FIXME: a stupid kludge to make the chars output correctly */
+               pos = 0;
+       }
+
+        xpos = entry->xpos + entry->promptlen + pos;
+        end_xpos = entry->xpos + entry->width;
+       if (xpos > end_xpos)
+                return;
+
+       term_set_color(root_window, ATTR_RESET);
+       term_move(root_window, xpos, entry->ypos);
+
+       p = (unsigned char *) (entry->scrstart + pos >= entry->text->len ? "" :
+                              entry->text->str + entry->scrstart + pos);
+       for (; *p != '\0' && xpos < end_xpos; p++, xpos++) {
+                end = p;
+               if (entry->utf8)
+                       get_utf8_char(&end);
+
+               if (entry->hidden)
+                        term_addch(root_window, ' ');
+               else if (*p >= 32 && (end != p || (*p & 127) >= 32)) {
+                        for (; p < end; p++)
+                               term_addch(root_window, *p);
+                       term_addch(root_window, *p);
+               } else {
+                       term_set_color(root_window, ATTR_RESET|ATTR_REVERSE);
+                       term_addch(root_window, *p+'A'-1);
+                       term_set_color(root_window, ATTR_RESET);
                }
        }
-       clrtoeol();
 
-       move_cursor(LINES-1, scrpos+promptlen);
-       screen_refresh(NULL);
+        /* clear the rest of the input line */
+        if (end_xpos == term_width)
+               term_clrtoeol(root_window);
+       else {
+               while (xpos < end_xpos) {
+                        term_addch(root_window, ' ');
+                        xpos++;
+               }
+       }
 }
 
-void gui_entry_set_prompt(const char *str)
+static void gui_entry_draw(GUI_ENTRY_REC *entry)
 {
-       if (str != NULL) {
-               if (permanent_prompt) return;
+       if (entry->redraw_needed_from >= 0) {
+               gui_entry_draw_from(entry, entry->redraw_needed_from);
+                entry->redraw_needed_from = -1;
+       }
+
+       term_move_cursor(entry->xpos + entry->scrpos + entry->promptlen,
+                        entry->ypos);
+       term_refresh(NULL);
+}
 
-               g_free_not_null(prompt);
-               prompt = g_strdup(str);
-               promptlen = format_get_length(prompt);
+static void gui_entry_redraw_from(GUI_ENTRY_REC *entry, int pos)
+{
+       pos -= entry->scrstart;
+       if (pos < 0) pos = 0;
+
+       if (entry->redraw_needed_from == -1 ||
+           entry->redraw_needed_from > pos)
+               entry->redraw_needed_from = pos;
+}
+
+void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width)
+{
+       int old_width;
+
+        g_return_if_fail(entry != NULL);
+
+       if (entry->xpos != xpos || entry->ypos != ypos) {
+                /* position in screen changed - needs a full redraw */
+               entry->xpos = xpos;
+               entry->ypos = ypos;
+               entry->width = width;
+               gui_entry_redraw(entry);
+                return;
+       }
+
+       if (entry->width == width)
+                return; /* no changes */
+
+       if (width > entry->width) {
+                /* input line grew - need to draw text at the end */
+                old_width = width;
+               entry->width = width;
+               gui_entry_redraw_from(entry, old_width);
+       } else {
+               /* input line shrinked - make sure the cursor
+                  is inside the input line */
+               entry->width = width;
+               if (entry->pos - entry->scrstart >
+                   entry->width-2 - entry->promptlen) {
+                       gui_entry_fix_cursor(entry);
+               }
        }
 
-        if (prompt != NULL)
-               gui_printtext(0, LINES-1, prompt);
+       gui_entry_draw(entry);
+}
 
-       entry_screenpos();
-       entry_update();
+void gui_entry_set_active(GUI_ENTRY_REC *entry)
+{
+       active_entry = entry;
+
+       if (entry != NULL) {
+               term_move_cursor(entry->xpos + entry->scrpos +
+                                entry->promptlen, entry->ypos);
+               term_refresh(NULL);
+       }
 }
 
-void gui_entry_set_perm_prompt(const char *str)
+void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str)
 {
-       g_return_if_fail(str != NULL);
+       int oldlen;
+
+        g_return_if_fail(entry != NULL);
 
-       g_free_not_null(prompt);
-       prompt = g_strdup(str);
-       promptlen = format_get_length(prompt);
+        oldlen = entry->promptlen;
+       if (str != NULL) {
+               g_free_not_null(entry->prompt);
+               entry->prompt = g_strdup(str);
+               entry->promptlen = format_get_length(str);
+       }
+
+        if (entry->prompt != NULL)
+               gui_printtext(entry->xpos, entry->ypos, entry->prompt);
 
-       permanent_prompt = TRUE;
-       gui_entry_set_prompt(NULL);
+       if (entry->promptlen != oldlen) {
+               gui_entry_fix_cursor(entry);
+               gui_entry_draw(entry);
+       }
 }
 
-void gui_entry_set_hidden(int hidden)
+void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden)
 {
-        prompt_hidden = hidden;
+        g_return_if_fail(entry != NULL);
+
+        entry->hidden = hidden;
 }
 
-void gui_entry_remove_perm_prompt(void)
+void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8)
 {
-        permanent_prompt = FALSE;
+        g_return_if_fail(entry != NULL);
+
+        entry->utf8 = utf8;
 }
 
-void gui_entry_set_text(const char *str)
+void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str)
 {
+        g_return_if_fail(entry != NULL);
        g_return_if_fail(str != NULL);
 
-       g_string_assign(entry, str);
-       pos = entry->len;
+       g_string_assign(entry->text, str);
+       entry->pos = entry->text->len;
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, 0);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-char *gui_entry_get_text(void)
+char *gui_entry_get_text(GUI_ENTRY_REC *entry)
 {
-       return entry->str;
+       g_return_val_if_fail(entry != NULL, NULL);
+
+       return entry->text->str;
 }
 
-void gui_entry_insert_text(const char *str)
+void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
 {
+        g_return_if_fail(entry != NULL);
        g_return_if_fail(str != NULL);
 
-       g_string_insert(entry, pos, str);
-       pos += strlen(str);
+        gui_entry_redraw_from(entry, entry->pos);
+       g_string_insert(entry->text, entry->pos, str);
+       entry->pos += strlen(str);
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-void gui_entry_insert_char(char chr)
+void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr)
 {
-       g_string_insert_c(entry, pos, chr);
-       pos++;
+        g_return_if_fail(entry != NULL);
+
+       if (chr == 0 || chr == 13 || chr == 10)
+               return; /* never insert NUL, CR or LF characters */
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, entry->pos);
+       g_string_insert_c(entry->text, entry->pos, chr);
+       entry->pos++;
+
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-void gui_entry_erase(int size)
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size)
 {
-       if (pos < size) return;
+        g_return_if_fail(entry != NULL);
+
+       if (entry->pos < size)
+               return;
 
 #ifdef WANT_BIG5
-       if (is_big5(entry->str[pos-2], entry->str[pos-1]))
+       if (is_big5(entry->text->str[entry->pos-2],
+                   entry->text->str[entry->pos-1]))
                size++;
-#endif WANT_BIG5
+#endif
 
-       pos -= size;
-       g_string_erase(entrypos, size);
+       entry->pos -= size;
+       g_string_erase(entry->text, entry->pos, size);
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_redraw_from(entry, entry->pos);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-void gui_entry_erase_word(void)
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
 {
        int to;
-       
-       if (pos == 0) return;
-
-       to = pos - 1;
 
-       while (entry->str[to] == ' ' && to > 0)
-               to--;
+        g_return_if_fail(entry != NULL);
+       if (entry->pos == 0)
+               return;
 
-       while (entry->str[to] != ' ' && to > 0)
-               to--;
+       to = entry->pos - 1;
 
-       if (entry->str[to] == ' ' && to > 0) 
-               to++;
+       if (to_space) {
+               while (entry->text->str[to] == ' ' && to > 0)
+                       to--;
+               while (entry->text->str[to] != ' ' && to > 0)
+                       to--;
+       } else {
+               while (!i_isalnum(entry->text->str[to]) && to > 0)
+                       to--;
+               while (i_isalnum(entry->text->str[to]) && to > 0)
+                       to--;
+       }
+       if (to > 0) to++;
 
-       g_string_erase(entry, to, pos - to);
-       pos = to;
+       g_string_erase(entry->text, to, entry->pos - to);
+       entry->pos = to;
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, entry->pos);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-void gui_entry_erase_next_word(void)
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
 {
-       int to = pos;
-       
-       if (pos == entry->len) return;
+       int to;
 
-       while (entry->str[to] == ' ' && to < entry->len)
-               to++;
+        g_return_if_fail(entry != NULL);
+       if (entry->pos == entry->text->len)
+               return;
 
-       while (entry->str[to] != ' ' && to < entry->len)
-               to++;
+        to = entry->pos;
+       if (to_space) {
+               while (entry->text->str[to] == ' ' && to < entry->text->len)
+                       to++;
+               while (entry->text->str[to] != ' ' && to < entry->text->len)
+                       to++;
+       } else {
+               while (!i_isalnum(entry->text->str[to]) && to < entry->text->len)
+                       to++;
+               while (i_isalnum(entry->text->str[to]) && to < entry->text->len)
+                       to++;
+       }
 
-       g_string_erase(entry, pos, to - pos);
+       g_string_erase(entry->text, entry->pos, to - entry->pos);
 
-       entry_screenpos();
-       entry_update();
+        gui_entry_redraw_from(entry, entry->pos);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-int gui_entry_get_pos(void)
+int gui_entry_get_pos(GUI_ENTRY_REC *entry)
 {
-       return pos;
+        g_return_val_if_fail(entry != NULL, 0);
+
+       return entry->pos;
 }
 
-void gui_entry_set_pos(int p)
+void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos)
 {
-       if (p >= 0 && p <= entry->len)
-               pos = p;
+        g_return_if_fail(entry != NULL);
+
+       if (pos >= 0 && pos <= entry->text->len)
+               entry->pos = pos;
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-void gui_entry_move_pos(int p)
+void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
 {
-#ifdef WANT_BIG5
-       if (p > 0 && is_big5 (entry->str[pos], entry->str[pos+1]))
-               p++;
-       else if (p < 0 && is_big5 (entry->str[pos-1], entry->str[pos]))
-               p--;
-#endif WANT_BIG5
+        g_return_if_fail(entry != NULL);
 
-       if (pos+p >= 0 && pos+p <= entry->len)
-               pos += p;
-
-       entry_screenpos();
-       entry_update();
+#ifdef WANT_BIG5
+       if (pos > 0 && is_big5(entry->text->str[entry->pos],
+                              entry->text->str[entry->pos+1]))
+               pos++;
+       else if (pos < 0 && is_big5(entry->text->str[entry->pos-1],
+                                   entry->text->str[entry->pos]))
+               pos--;
+#endif
+
+       if (entry->pos+pos >= 0 && entry->pos+pos <= entry->text->len)
+               entry->pos += pos;
+
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-static void gui_entry_move_words_left(int count)
+static void gui_entry_move_words_left(GUI_ENTRY_REC *entry, int count, int to_space)
 {
-       if (pos == 0) return;
+       int pos;
 
+       pos = entry->pos;
        while (count > 0 && pos > 0) {
-               while (pos > 0 && entry->str[pos-1] == ' ')
-                       pos--;
-               while (pos > 0 && entry->str[pos-1] != ' ')
-                       pos--;
+               if (to_space) {
+                       while (pos > 0 && entry->text->str[pos-1] == ' ')
+                               pos--;
+                       while (pos > 0 && entry->text->str[pos-1] != ' ')
+                               pos--;
+               } else {
+                       while (pos > 0 && !i_isalnum(entry->text->str[pos-1]))
+                               pos--;
+                       while (pos > 0 &&  i_isalnum(entry->text->str[pos-1]))
+                               pos--;
+               }
                count--;
        }
+
+        entry->pos = pos;
 }
 
-static void gui_entry_move_words_right(int count)
+static void gui_entry_move_words_right(GUI_ENTRY_REC *entry, int count, int to_space)
 {
-       if (pos == entry->len) return;
-
-       while (count > 0 && pos < entry->len) {
-               while (pos < entry->len && entry->str[pos] != ' ')
-                       pos++;
-               while (pos < entry->len && entry->str[pos] == ' ')
-                       pos++;
+       int pos;
+
+       pos = entry->pos;
+       while (count > 0 && pos < entry->text->len) {
+               if (to_space) {
+                       while (pos < entry->text->len && entry->text->str[pos] == ' ')
+                               pos++;
+                       while (pos < entry->text->len && entry->text->str[pos] != ' ')
+                               pos++;
+               } else {
+                       while (pos < entry->text->len && !i_isalnum(entry->text->str[pos]))
+                               pos++;
+                       while (pos < entry->text->len &&  i_isalnum(entry->text->str[pos]))
+                               pos++;
+               }
                count--;
        }
+
+        entry->pos = pos;
 }
 
-void gui_entry_move_words(int count)
+void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space)
 {
+        g_return_if_fail(entry != NULL);
+
        if (count < 0)
-               gui_entry_move_words_left(-count);
+               gui_entry_move_words_left(entry, -count, to_space);
        else if (count > 0)
-               gui_entry_move_words_right(count);
+               gui_entry_move_words_right(entry, count, to_space);
 
-       entry_screenpos();
-       entry_update();
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
 
-void gui_entry_redraw(void)
+void gui_entry_redraw(GUI_ENTRY_REC *entry)
 {
-       gui_entry_set_prompt(NULL);
+        g_return_if_fail(entry != NULL);
 
-       entry_screenpos();
-       entry_update();
-}
-
-void gui_entry_init(void)
-{
-       entry = g_string_new(NULL);
-
-       pos = scrpos = 0;
-       prompt = NULL; promptlen = 0;
-       permanent_prompt = FALSE;
-        prompt_hidden = FALSE;
-}
-
-void gui_entry_deinit(void)
-{
-       if (prompt != NULL) g_free(prompt);
-       g_string_free(entry, TRUE);
+       gui_entry_set_prompt(entry, NULL);
+        gui_entry_redraw_from(entry, 0);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
 }
index ff8f3ef5b42d159e6fb2f409eb66976e79879209..364f92da3a6700042fb2abb778410daa529277b1 100644 (file)
@@ -1,31 +1,46 @@
 #ifndef __GUI_ENTRY_H
 #define __GUI_ENTRY_H
 
-void gui_entry_set_prompt(const char *str);
+typedef struct {
+       GString *text;
+       int xpos, ypos, width; /* entry position in screen */
+       int pos, scrstart, scrpos; /* cursor position */
+        int hidden; /* print the chars as spaces in input line (useful for passwords) */
 
-/* permanent prompt can't be overwritten with gui_entry_set_prompt() */
-void gui_entry_set_perm_prompt(const char *str);
-void gui_entry_remove_perm_prompt(void);
-void gui_entry_set_hidden(int hidden);
+       int promptlen;
+       char *prompt;
 
-void gui_entry_set_text(const char *str);
-char *gui_entry_get_text(void);
+       int redraw_needed_from;
+       unsigned int utf8:1;
+} GUI_ENTRY_REC;
 
-void gui_entry_insert_text(const char *str);
-void gui_entry_insert_char(char chr);
+extern GUI_ENTRY_REC *active_entry;
 
-void gui_entry_erase(int size);
-void gui_entry_erase_word(void);
-void gui_entry_erase_next_word(void);
+GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8);
+void gui_entry_destroy(GUI_ENTRY_REC *entry);
 
-int gui_entry_get_pos(void);
-void gui_entry_set_pos(int pos);
-void gui_entry_move_pos(int pos);
-void gui_entry_move_words(int count);
+void gui_entry_move(GUI_ENTRY_REC *entry, int xpos, int ypos, int width);
+void gui_entry_set_active(GUI_ENTRY_REC *entry);
 
-void gui_entry_redraw(void);
+void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str);
+void gui_entry_set_hidden(GUI_ENTRY_REC *entry, int hidden);
+void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8);
 
-void gui_entry_init(void);
-void gui_entry_deinit(void);
+void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str);
+char *gui_entry_get_text(GUI_ENTRY_REC *entry);
+
+void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
+void gui_entry_insert_char(GUI_ENTRY_REC *entry, char chr);
+
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size);
+void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
+void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
+
+int gui_entry_get_pos(GUI_ENTRY_REC *entry);
+void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos);
+void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos);
+void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
+
+void gui_entry_redraw(GUI_ENTRY_REC *entry);
 
 #endif
index c95ac621b40ebfbc0369109b9f12b5ffb4d9e318..b07bf129cacd44bf0d4ba3406ed8312c897db2e5 100644 (file)
@@ -37,7 +37,7 @@ static char *expando_idletime(SERVER_REC *server, void *item, int *free_ret)
 /* current contents of the input line */
 static char *expando_inputline(SERVER_REC *server, void *item, int *free_ret)
 {
-       return gui_entry_get_text();
+       return gui_entry_get_text(active_entry);
 }
 
 /* value of cutbuffer */
index 5faae0fa3b53b2d025a726dae523ff5124081aaa..affd2b9f518aa9601a24d20988f791bb0fbd4d92 100644 (file)
 #include "formats.h"
 #include "printtext.h"
 
-#include "screen.h"
+#include "term.h"
+#include "gui-printtext.h"
 #include "gui-windows.h"
 
-int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 };
+int mirc_colors[] = { 15, 0, 1, 2, 12, 4, 5, 6, 14, 10, 3, 11, 9, 13, 8, 7 };
 static int scrollback_lines, scrollback_hours, scrollback_burst_remove;
 
-static int scrollback_save_formats;
-static GString *format;
-
-static int last_color, last_flags;
+static int last_fg, last_bg, last_flags;
 static int next_xpos, next_ypos;
 
+static GHashTable *indent_functions;
+static INDENT_FUNC default_indent_func;
+
+void gui_register_indent_func(const char *name, INDENT_FUNC func)
+{
+       gpointer key, value;
+        GSList *list;
+
+       if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
+                list = value;
+               g_hash_table_remove(indent_functions, key);
+       } else {
+               key = g_strdup(name);
+                list = NULL;
+       }
+
+       list = g_slist_append(list, (void *) func);
+       g_hash_table_insert(indent_functions, key, list);
+}
+
+void gui_unregister_indent_func(const char *name, INDENT_FUNC func)
+{
+       gpointer key, value;
+        GSList *list;
+
+       if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
+               list = value;
+
+               list = g_slist_remove(list, (void *) func);
+               g_hash_table_remove(indent_functions, key);
+               if (list == NULL)
+                       g_free(key);
+                else
+                       g_hash_table_insert(indent_functions, key, list);
+       }
+
+       if (default_indent_func == func)
+               gui_set_default_indent(NULL);
+
+       textbuffer_views_unregister_indent_func(func);
+}
+
+void gui_set_default_indent(const char *name)
+{
+       GSList *list;
+
+       list = name == NULL ? NULL :
+               g_hash_table_lookup(indent_functions, name);
+       default_indent_func = list == NULL ? NULL : list->data;
+        gui_windows_reset_settings();
+}
+
+INDENT_FUNC get_default_indent_func(void)
+{
+        return default_indent_func;
+}
+
 void gui_printtext(int xpos, int ypos, const char *str)
 {
        next_xpos = xpos;
@@ -47,6 +102,18 @@ void gui_printtext(int xpos, int ypos, const char *str)
        next_xpos = next_ypos = -1;
 }
 
+void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str)
+{
+       GUI_WINDOW_REC *gui;
+
+       gui = WINDOW_GUI(dest->window);
+
+       gui->use_insert_after = TRUE;
+       gui->insert_after = prev;
+       format_send_to_gui(dest, str);
+       gui->use_insert_after = FALSE;
+}
+
 static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
 {
        LINE_REC *line;
@@ -57,9 +124,12 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
            scrollback_lines+scrollback_burst_remove) {
                 /* remove lines by line count */
                while (view->buffer->lines_count > scrollback_lines) {
-                       line = view->buffer->lines->data;
-                       if (line->info.time >= old_time) {
-                               /* too new line, don't remove yet */
+                       line = view->buffer->first_line;
+                       if (line->info.time >= old_time ||
+                           scrollback_lines == 0) {
+                               /* too new line, don't remove yet - also
+                                  if scrollback_lines is 0, we want to check
+                                  only scrollback_hours setting. */
                                break;
                        }
                        textbuffer_view_remove_line(view, line);
@@ -67,77 +137,85 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
        }
 }
 
-static void get_colors(int flags, int *fg, int *bg)
+static void get_colors(int flags, int *fg, int *bg, int *attr)
 {
-       if (flags & PRINTFLAG_MIRC_COLOR) {
+       if (flags & GUI_PRINT_FLAG_MIRC_COLOR) {
                /* mirc colors - real range is 0..15, but after 16
                   colors wrap to 0, 1, ... */
-               *bg = *bg < 0 ? 0 : mirc_colors[*bg % 16];
-               if (*fg > 0) *fg = mirc_colors[*fg % 16];
-       } else {
-               /* default colors */
-               *bg = *bg < 0 || *bg > 15 ? 0 : *bg;
-                if (*fg > 8) *fg &= ~8;
+                if (*bg >= 0) *bg = mirc_colors[*bg % 16];
+               if (*fg >= 0) *fg = mirc_colors[*fg % 16];
        }
 
-       if (*fg < 0 || *fg > 15) {
-               *fg = *bg == 0 ? current_theme->default_color :
-                       current_theme->default_real_color;
-       }
-
-       if (flags & PRINTFLAG_REVERSE) {
-               int tmp;
-
-               tmp = *fg; *fg = *bg; *bg = tmp;
-       }
+       if (*fg < 0 || *fg > 15)
+               *fg = current_theme->default_color;
+       if (*bg < 0 || *bg > 15)
+                *bg = -1;
 
-       if (*fg == 8) *fg |= ATTR_COLOR8;
-       if (flags & PRINTFLAG_BOLD) {
-               if (*fg == 0) *fg = current_theme->default_real_color;
-               *fg |= 8;
-       }
-       if (flags & PRINTFLAG_UNDERLINE) *fg |= ATTR_UNDERLINE;
-       if (flags & PRINTFLAG_BLINK) *bg |= 0x08;
+       *attr = 0;
+       if (flags & GUI_PRINT_FLAG_REVERSE) *attr |= ATTR_REVERSE;
+       if (flags & GUI_PRINT_FLAG_BOLD) *attr |= ATTR_BOLD;
+       if (flags & GUI_PRINT_FLAG_UNDERLINE) *attr |= ATTR_UNDERLINE;
+       if (flags & GUI_PRINT_FLAG_BLINK) *attr |= ATTR_BLINK;
 }
 
 static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
                            int fg, int bg, int flags)
 {
-       unsigned char data[12];
-       int color, pos;
+       unsigned char data[20];
+       int pos;
 
-       /* color should never have last bit on or it would be treated as a
-          command! */
-       color = (fg & 0x0f) | ((bg & 0x07) << 4);
-       pos = 0;
+        /* get the fg & bg command chars */
+       fg = fg < 0 ? LINE_COLOR_DEFAULT : fg & 0x0f;
+       bg = LINE_COLOR_BG | (bg < 0 ? LINE_COLOR_DEFAULT : bg & 0x0f);
+       if (flags & GUI_PRINT_FLAG_BOLD)
+               fg |= LINE_COLOR_BOLD;
+       if (flags & GUI_PRINT_FLAG_BLINK)
+                bg |= LINE_COLOR_BLINK;
 
-       if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != last_color) ||
-           ((fg & ATTR_COLOR8) && (fg & 0xf0) != (last_color & 0xf0))) {
+       pos = 0;
+       if (fg != last_fg) {
+               last_fg = fg;
                data[pos++] = 0;
-               data[pos++] = color == 0 ? LINE_CMD_COLOR0 : color;
+               data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg;
        }
-
-       if ((flags & PRINTFLAG_UNDERLINE) != (last_flags & PRINTFLAG_UNDERLINE)) {
+       if (bg != last_bg) {
+                last_bg = bg;
                data[pos++] = 0;
-               data[pos++] = LINE_CMD_UNDERLINE;
+               data[pos++] = bg;
        }
-       if (fg & ATTR_COLOR8) {
+
+       if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (last_flags & GUI_PRINT_FLAG_UNDERLINE)) {
                data[pos++] = 0;
-               data[pos++] = LINE_CMD_COLOR8;
+               data[pos++] = LINE_CMD_UNDERLINE;
        }
-       if (bg & 0x08) {
+       if ((flags & GUI_PRINT_FLAG_REVERSE) != (last_flags & GUI_PRINT_FLAG_REVERSE)) {
                data[pos++] = 0;
-               data[pos++] = LINE_CMD_BLINK;
+               data[pos++] = LINE_CMD_REVERSE;
        }
-       if (flags & PRINTFLAG_INDENT) {
+       if (flags & GUI_PRINT_FLAG_INDENT) {
                data[pos++] = 0;
                data[pos++] = LINE_CMD_INDENT;
        }
 
-       *line = textbuffer_insert(buffer, *line, data, pos, NULL);
+        if (pos > 0)
+               *line = textbuffer_insert(buffer, *line, data, pos, NULL);
 
        last_flags = flags;
-       last_color = fg | (bg << 4);
+}
+
+static void line_add_indent_func(TEXT_BUFFER_REC *buffer, LINE_REC **line,
+                                const char *function)
+{
+        GSList *list;
+        unsigned char data[1+sizeof(INDENT_FUNC)];
+
+        list = g_hash_table_lookup(indent_functions, function);
+       if (list != NULL) {
+               data[0] = LINE_CMD_INDENT_FUNC;
+               memcpy(data+1, list->data, sizeof(INDENT_FUNC));
+               *line = textbuffer_insert(buffer, *line,
+                                         data, sizeof(data), NULL);
+       }
 }
 
 static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
@@ -152,22 +230,28 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
                               void *bgcolor, void *pflags,
                               char *str, void *level)
 {
+        GUI_WINDOW_REC *gui;
         TEXT_BUFFER_VIEW_REC *view;
        LINE_REC *insert_after;
         LINE_INFO_REC lineinfo;
-       int fg, bg, flags;
+       int fg, bg, flags, attr;
 
        flags = GPOINTER_TO_INT(pflags);
        fg = GPOINTER_TO_INT(fgcolor);
        bg = GPOINTER_TO_INT(bgcolor);
-       get_colors(flags, &fg, &bg);
+       get_colors(flags, &fg, &bg, &attr);
 
        if (window == NULL) {
                 g_return_if_fail(next_xpos != -1);
 
-               wmove(stdscr, next_ypos, next_xpos);
-               set_color(stdscr, fg | (bg << 4));
-                addstr(str);
+               attr |= fg >= 0 ? fg : ATTR_RESETFG;
+               attr |= bg >= 0 ? (bg << 4) : ATTR_RESETBG;
+               term_set_color(root_window, attr);
+
+               term_move(root_window, next_xpos, next_ypos);
+               if (flags & GUI_PRINT_FLAG_CLRTOEOL)
+                       term_clrtoeol(root_window);
+               term_addstr(root_window, str);
                next_xpos += strlen(str);
                 return;
        }
@@ -175,23 +259,33 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
        lineinfo.level = GPOINTER_TO_INT(level);
         lineinfo.time = time(NULL);
 
-       view = WINDOW_GUI(window)->view;
-       insert_after = WINDOW_GUI(window)->use_insert_after ?
-               WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
+        gui = WINDOW_GUI(window);
+       view = gui->view;
+       insert_after = gui->use_insert_after ?
+               gui->insert_after : view->buffer->cur_line;
 
-       if (flags & PRINTFLAG_NEWLINE)
+       if (flags & GUI_PRINT_FLAG_NEWLINE)
                 view_add_eol(view, &insert_after);
        line_add_colors(view->buffer, &insert_after, fg, bg, flags);
-       textbuffer_insert(view->buffer, insert_after,
-                         str, strlen(str), &lineinfo);
+
+       if (flags & GUI_PRINT_FLAG_INDENT_FUNC) {
+               /* specify the indentation function */
+                line_add_indent_func(view->buffer, &insert_after, str);
+       } else {
+               insert_after = textbuffer_insert(view->buffer, insert_after,
+                                                (unsigned char *) str,
+                                                strlen(str), &lineinfo);
+       }
+       if (gui->use_insert_after)
+                gui->insert_after = insert_after;
 }
 
-static void sig_printtext_finished(WINDOW_REC *window)
+static void sig_gui_printtext_finished(WINDOW_REC *window)
 {
        TEXT_BUFFER_VIEW_REC *view;
        LINE_REC *insert_after;
 
-        last_color = 0;
+        last_fg = last_bg = -1;
        last_flags = 0;
 
        view = WINDOW_GUI(window)->view;
@@ -202,74 +296,36 @@ static void sig_printtext_finished(WINDOW_REC *window)
        remove_old_lines(view);
 }
 
-static void sig_print_format(THEME_REC *theme, const char *module,
-                            TEXT_DEST_REC *dest, void *formatnump,
-                            char **args)
-{
-       FORMAT_REC *formats;
-       int formatnum, n;
-
-       if (!scrollback_save_formats)
-               return;
-
-       formatnum = GPOINTER_TO_INT(formatnump);
-       formats = g_hash_table_lookup(default_formats, module);
-
-       /* <module><format_name><arg...> */
-       g_string_truncate(format, 0);
-
-       g_string_append_c(format, '\0');
-       g_string_append_c(format, (char)LINE_CMD_FORMAT);
-
-        g_string_append(format, module);
-
-       g_string_append_c(format, '\0');
-       g_string_append_c(format, (char)LINE_CMD_FORMAT);
-
-       g_string_append(format, formats[formatnum].tag);
-
-       for (n = 0; n < formats[formatnum].params; n++) {
-               g_string_append_c(format, '\0');
-               g_string_append_c(format, (char)LINE_CMD_FORMAT);
-
-               g_string_append(format, args[n]);
-       }
-}
-
 static void read_settings(void)
 {
        scrollback_lines = settings_get_int("scrollback_lines");
        scrollback_hours = settings_get_int("scrollback_hours");
         scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
-        scrollback_save_formats = settings_get_bool("scrollback_save_formats");
 }
 
 void gui_printtext_init(void)
 {
        next_xpos = next_ypos = -1;
-       format = g_string_new(NULL);
+       default_indent_func = NULL;
+       indent_functions = g_hash_table_new((GHashFunc) g_str_hash,
+                                           (GCompareFunc) g_str_equal);
 
        settings_add_int("history", "scrollback_lines", 500);
        settings_add_int("history", "scrollback_hours", 24);
        settings_add_int("history", "scrollback_burst_remove", 10);
-       settings_add_bool("history", "scrollback_save_formats", FALSE);
 
        signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
-       signal_add("print text finished", (SIGNAL_FUNC) sig_printtext_finished);
-       signal_add("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
-       signal_add("beep", (SIGNAL_FUNC) beep);
 
        read_settings();
 }
 
 void gui_printtext_deinit(void)
 {
-       g_string_free(format, TRUE);
+       g_hash_table_destroy(indent_functions);
 
        signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
-       signal_remove("print text finished", (SIGNAL_FUNC) sig_printtext_finished);
-       signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
-       signal_remove("beep", (SIGNAL_FUNC) beep);
 }
index 3b2098b7805a25dcecf315f7d32d0a30a6f479a7..47cd3c2322bfc0248747169d3810d8f831885a25 100644 (file)
@@ -2,12 +2,21 @@
 #define __GUI_PRINTTEXT_H
 
 #include "gui-windows.h"
+#include "textbuffer-view.h"
+#include "formats.h"
 
 extern int mirc_colors[];
 
 void gui_printtext_init(void);
 void gui_printtext_deinit(void);
 
+void gui_register_indent_func(const char *name, INDENT_FUNC func);
+void gui_unregister_indent_func(const char *name, INDENT_FUNC func);
+
+void gui_set_default_indent(const char *name);
+INDENT_FUNC get_default_indent_func(void);
+
 void gui_printtext(int xpos, int ypos, const char *str);
+void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str);
 
 #endif
index bc3429d8b04d8ee0f16338146e86a2f484cdd36c..8fe1741089524393a68f6032d7220fd4f7ad5094 100644 (file)
 #include "misc.h"
 #include "settings.h"
 #include "special-vars.h"
+#include "servers.h"
 
 #include "completion.h"
 #include "command-history.h"
 #include "keyboard.h"
 #include "translation.h"
 
-#include "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "gui-windows.h"
 
@@ -51,6 +52,25 @@ char *cutbuffer;
 static int readtag;
 static time_t idle_time;
 
+static void sig_input(void);
+
+void input_listen_init(int handle)
+{
+        GIOChannel *stdin_channel;
+
+       stdin_channel = g_io_channel_unix_new(handle);
+       readtag = g_input_add_full(stdin_channel,
+                                  G_PRIORITY_HIGH, G_INPUT_READ,
+                                  (GInputFunction) sig_input, NULL);
+        g_io_channel_unref(stdin_channel);
+}
+
+void input_listen_deinit(void)
+{
+       g_source_remove(readtag);
+        readtag = -1;
+}
+
 static void handle_key_redirect(int key)
 {
        ENTRY_REDIRECT_KEY_FUNC func;
@@ -63,8 +83,7 @@ static void handle_key_redirect(int key)
        if (func != NULL)
                func(key, data, active_win->active_server, active_win->active);
 
-       gui_entry_remove_perm_prompt();
-       window_update_prompt();
+       gui_entry_set_prompt(active_entry, "");
 }
 
 static void handle_entry_redirect(const char *line)
@@ -72,7 +91,7 @@ static void handle_entry_redirect(const char *line)
        ENTRY_REDIRECT_ENTRY_FUNC func;
        void *data;
 
-        gui_entry_set_hidden(FALSE);
+        gui_entry_set_hidden(active_entry, FALSE);
 
        func = (ENTRY_REDIRECT_ENTRY_FUNC) redir->func;
        data = redir->data;
@@ -83,8 +102,7 @@ static void handle_entry_redirect(const char *line)
                     active_win->active);
        }
 
-       gui_entry_remove_perm_prompt();
-       window_update_prompt();
+       gui_entry_set_prompt(active_entry, "");
 }
 
 static int get_scroll_count(void)
@@ -99,8 +117,9 @@ static int get_scroll_count(void)
        else if (count < 1)
                 count = 1.0/count;
 
-       if (*str == '/')
-               count = WINDOW_GUI(active_win)->parent->height/count;
+       if (*str == '/') {
+               count = (active_mainwin->height-active_mainwin->statusbar_lines)/count;
+       }
        return (int)count;
 }
 
@@ -141,36 +160,44 @@ void handle_key(int key)
 
        if (!key_pressed(keyboard, str)) {
                 /* key wasn't used for anything, print it */
-               gui_entry_insert_char((char) key);
+               gui_entry_insert_char(active_entry, (char) key);
        }
 }
 
 static void key_send_line(void)
 {
-       int add_history;
-        char *str;
+       HISTORY_REC *history;
+        char *str, *add_history;
 
-       str = gui_entry_get_text();
+       str = gui_entry_get_text(active_entry);
        if (*str == '\0') return;
 
+       /* we can't use gui_entry_get_text() later, since the entry might
+          have been destroyed after we get back */
+       add_history = g_strdup(str);
+       history = command_history_current(active_win);
+
        translate_output(str);
 
-       add_history = TRUE;
        if (redir == NULL) {
                signal_emit("send command", 3, str,
                            active_win->active_server,
                            active_win->active);
        } else {
                if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
-                       add_history = FALSE;
+                        g_free_and_null(add_history);
                handle_entry_redirect(str);
        }
 
-       if (add_history) {
-               command_history_add(active_win, gui_entry_get_text(),
-                                   FALSE);
+       if (add_history != NULL) {
+               history = command_history_find(history);
+               if (history != NULL)
+                       command_history_add(history, add_history);
+                g_free(add_history);
        }
-       gui_entry_set_text("");
+
+       if (active_entry != NULL)
+               gui_entry_set_text(active_entry, "");
        command_history_clear_pos(active_win);
 }
 
@@ -182,83 +209,93 @@ static void key_backward_history(void)
 {
        const char *text;
 
-       text = command_history_prev(active_win, gui_entry_get_text());
-       gui_entry_set_text(text);
+       text = command_history_prev(active_win, gui_entry_get_text(active_entry));
+       gui_entry_set_text(active_entry, text);
 }
 
 static void key_forward_history(void)
 {
        const char *text;
 
-       text = command_history_next(active_win, gui_entry_get_text());
-       gui_entry_set_text(text);
+       text = command_history_next(active_win, gui_entry_get_text(active_entry));
+       gui_entry_set_text(active_entry, text);
 }
 
 static void key_beginning_of_line(void)
 {
-        gui_entry_set_pos(0);
+        gui_entry_set_pos(active_entry, 0);
 }
 
 static void key_end_of_line(void)
 {
-       gui_entry_set_pos(strlen(gui_entry_get_text()));
+       gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry)));
 }
 
 static void key_backward_character(void)
 {
-       gui_entry_move_pos(-1);
+       gui_entry_move_pos(active_entry, -1);
 }
 
 static void key_forward_character(void)
 {
-       gui_entry_move_pos(1);
+       gui_entry_move_pos(active_entry, 1);
 }
 
 static void key_backward_word(void)
 {
-       gui_entry_move_words(-1);
+       gui_entry_move_words(active_entry, -1, FALSE);
 }
 
 static void key_forward_word(void)
 {
-       gui_entry_move_words(1);
+       gui_entry_move_words(active_entry, 1, FALSE);
+}
+
+static void key_backward_to_space(void)
+{
+       gui_entry_move_words(active_entry, -1, TRUE);
+}
+
+static void key_forward_to_space(void)
+{
+       gui_entry_move_words(active_entry, 1, TRUE);
 }
 
 static void key_erase_line(void)
 {
        g_free_not_null(cutbuffer);
-       cutbuffer = g_strdup(gui_entry_get_text());
+       cutbuffer = g_strdup(gui_entry_get_text(active_entry));
 
-       gui_entry_set_text("");
+       gui_entry_set_text(active_entry, "");
 }
 
 static void key_erase_to_beg_of_line(void)
 {
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
        g_free_not_null(cutbuffer);
-       cutbuffer = g_strndup(gui_entry_get_text(), pos);
+       cutbuffer = g_strndup(gui_entry_get_text(active_entry), pos);
 
-       gui_entry_erase(pos);
+       gui_entry_erase(active_entry, pos);
 }
 
 static void key_erase_to_end_of_line(void)
 {
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
        g_free_not_null(cutbuffer);
-       cutbuffer = g_strdup(gui_entry_get_text()+pos);
+       cutbuffer = g_strdup(gui_entry_get_text(active_entry)+pos);
 
-       gui_entry_set_pos(strlen(gui_entry_get_text()));
-       gui_entry_erase(strlen(gui_entry_get_text()) - pos);
+       gui_entry_set_pos(active_entry, strlen(gui_entry_get_text(active_entry)));
+       gui_entry_erase(active_entry, strlen(gui_entry_get_text(active_entry)) - pos);
 }
 
 static void key_yank_from_cutbuffer(void)
 {
        if (cutbuffer != NULL)
-               gui_entry_insert_text(cutbuffer);
+               gui_entry_insert_text(active_entry, cutbuffer);
 }
 
 static void key_transpose_characters(void)
@@ -266,61 +303,72 @@ static void key_transpose_characters(void)
        char *line, c;
        int pos;
 
-       pos = gui_entry_get_pos();
-       line = gui_entry_get_text();
+       pos = gui_entry_get_pos(active_entry);
+       line = gui_entry_get_text(active_entry);
        if (pos == 0 || strlen(line) < 2)
                return;
 
        if (line[pos] != '\0')
-               gui_entry_move_pos(1);
-       c = line[gui_entry_get_pos()-1];
-        gui_entry_erase(1);
-       gui_entry_move_pos(-1);
-       gui_entry_insert_char(c);
-        gui_entry_set_pos(pos);
+               gui_entry_move_pos(active_entry, 1);
+       c = line[gui_entry_get_pos(active_entry)-1];
+        gui_entry_erase(active_entry, 1);
+       gui_entry_move_pos(active_entry, -1);
+       gui_entry_insert_char(active_entry, c);
+        gui_entry_set_pos(active_entry, pos);
+       gui_entry_move_pos(active_entry, 1);
 }
 
 static void key_delete_character(void)
 {
-       if (gui_entry_get_pos() < (int)strlen(gui_entry_get_text())) {
-               gui_entry_move_pos(1);
-               gui_entry_erase(1);
+       if (gui_entry_get_pos(active_entry) < (int)strlen(gui_entry_get_text(active_entry))) {
+               gui_entry_move_pos(active_entry, 1);
+               gui_entry_erase(active_entry, 1);
        }
 }
 
 static void key_backspace(void)
 {
-       gui_entry_erase(1);
+       gui_entry_erase(active_entry, 1);
 }
 
 static void key_delete_previous_word(void)
 {
-  gui_entry_erase_word();
+       gui_entry_erase_word(active_entry, FALSE);
 }
 
 static void key_delete_next_word(void)
 {
-       gui_entry_erase_next_word();
+       gui_entry_erase_next_word(active_entry, FALSE);
 }
 
 static void key_delete_to_previous_space(void)
 {
-       gui_entry_erase_word();
+       gui_entry_erase_word(active_entry, TRUE);
+}
+
+static void key_delete_to_next_space(void)
+{
+       gui_entry_erase_next_word(active_entry, TRUE);
 }
 
-void readline(void)
+static void sig_input(void)
 {
-       int key;
+        unsigned char buffer[128];
+       int ret, i;
 
-       for (;;) {
-               key = getch();
-               if (key == ERR
-#ifdef KEY_RESIZE
-                   || key == KEY_RESIZE
-#endif
-                  ) break;
+       if (!active_entry) {
+                /* no active entry yet - wait until we have it */
+               return;
+       }
 
-               handle_key(key);
+       ret = term_gets(buffer, sizeof(buffer));
+       if (ret == -1) {
+               /* lost terminal */
+               if (!term_detached)
+                       signal_emit("command quit", 1, "Lost terminal");
+       } else {
+               for (i = 0; i < ret; i++)
+                       handle_key(buffer[i]);
        }
 }
 
@@ -354,32 +402,43 @@ static void key_change_window(const char *data)
        signal_emit("command window goto", 3, data, active_win->active_server, active_win->active);
 }
 
-static void key_word_completion(void)
+static void key_completion(int erase)
 {
        char *line;
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
 
-       line = word_complete(active_win, gui_entry_get_text(), &pos);
+       line = word_complete(active_win, gui_entry_get_text(active_entry),
+                            &pos, erase);
        if (line != NULL) {
-               gui_entry_set_text(line);
-               gui_entry_set_pos(pos);
+               gui_entry_set_text(active_entry, line);
+               gui_entry_set_pos(active_entry, pos);
                g_free(line);
        }
 }
 
+static void key_word_completion(void)
+{
+        key_completion(FALSE);
+}
+
+static void key_erase_completion(void)
+{
+        key_completion(TRUE);
+}
+
 static void key_check_replaces(void)
 {
        char *line;
        int pos;
 
-       pos = gui_entry_get_pos();
+       pos = gui_entry_get_pos(active_entry);
 
-       line = auto_word_complete(gui_entry_get_text(), &pos);
+       line = auto_word_complete(gui_entry_get_text(active_entry), &pos);
        if (line != NULL) {
-               gui_entry_set_text(line);
-               gui_entry_set_pos(pos);
+               gui_entry_set_text(active_entry, line);
+               gui_entry_set_pos(active_entry, pos);
                g_free(line);
        }
 }
@@ -467,14 +526,22 @@ static void key_insert_text(const char *data)
 
        str = parse_special_string(data, active_win->active_server,
                                   active_win->active, "", NULL, 0);
-       gui_entry_insert_text(str);
+       gui_entry_insert_text(active_entry, str);
         g_free(str);
 }
 
+static void key_sig_stop(void)
+{
+        term_stop();
+}
+
 static void sig_window_auto_changed(void)
 {
-       command_history_next(active_win, gui_entry_get_text());
-       gui_entry_set_text("");
+       if (active_entry == NULL)
+               return;
+
+       command_history_next(active_win, gui_entry_get_text(active_entry));
+       gui_entry_set_text(active_entry, "");
 }
 
 static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
@@ -486,8 +553,8 @@ static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
        redir->data = data;
 
        if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
-               gui_entry_set_hidden(TRUE);
-       gui_entry_set_perm_prompt(entry);
+               gui_entry_set_hidden(active_entry, TRUE);
+       gui_entry_set_prompt(active_entry, entry);
 }
 
 void gui_readline_init(void)
@@ -499,17 +566,18 @@ void gui_readline_init(void)
        cutbuffer = NULL;
        redir = NULL;
        idle_time = time(NULL);
-       readtag = g_input_add_full(g_io_channel_unix_new(0),
-                                  G_PRIORITY_HIGH, G_INPUT_READ,
-                                  (GInputFunction) readline, NULL);
+        input_listen_init(STDIN_FILENO);
 
        settings_add_str("history", "scroll_page_count", "/2");
 
        keyboard = keyboard_create(NULL);
         key_configure_freeze();
 
+       key_bind("key", NULL, " ", "space", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "^M", "return", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "^J", "return", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "^H", "backspace", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "^?", "backspace", (SIGNAL_FUNC) key_combo);
 
         /* meta */
        key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo);
@@ -539,11 +607,18 @@ void gui_readline_init(void)
        key_bind("key", NULL, "meta2-2~", "insert", (SIGNAL_FUNC) key_combo);
        key_bind("key", NULL, "meta2-3~", "delete", (SIGNAL_FUNC) key_combo);
 
-        /* cursor movement */
+       key_bind("key", NULL, "meta2-d", "cleft", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "meta2-c", "cright", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "meta2-5D", "cleft", (SIGNAL_FUNC) key_combo);
+       key_bind("key", NULL, "meta2-5C", "cright", (SIGNAL_FUNC) key_combo);
+
+       /* cursor movement */
        key_bind("backward_character", "", "left", NULL, (SIGNAL_FUNC) key_backward_character);
        key_bind("forward_character", "", "right", NULL, (SIGNAL_FUNC) key_forward_character);
-       key_bind("backward_word", "", "meta2-d", NULL, (SIGNAL_FUNC) key_backward_word);
-       key_bind("forward_word", "", "meta2-c", NULL, (SIGNAL_FUNC) key_forward_word);
+       key_bind("backward_word", "", "cleft", NULL, (SIGNAL_FUNC) key_backward_word);
+       key_bind("forward_word", "", "cright", NULL, (SIGNAL_FUNC) key_forward_word);
+       key_bind("backward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_backward_to_space);
+       key_bind("forward_to_space", "", NULL, NULL, (SIGNAL_FUNC) key_forward_to_space);
        key_bind("beginning_of_line", "", "home", NULL, (SIGNAL_FUNC) key_beginning_of_line);
        key_bind("beginning_of_line", NULL, "^A", NULL, (SIGNAL_FUNC) key_beginning_of_line);
        key_bind("end_of_line", "", "end", NULL, (SIGNAL_FUNC) key_end_of_line);
@@ -554,13 +629,13 @@ void gui_readline_init(void)
        key_bind("forward_history", "", "down", NULL, (SIGNAL_FUNC) key_forward_history);
 
         /* line editing */
-       key_bind("backspace", "", "^H", NULL, (SIGNAL_FUNC) key_backspace);
-       key_bind("backspace", "", "^?", NULL, (SIGNAL_FUNC) key_backspace);
+       key_bind("backspace", "", "backspace", NULL, (SIGNAL_FUNC) key_backspace);
        key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_next_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_next_word);
-       key_bind("delete_previous_word", "", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word);
+       key_bind("delete_previous_word", "meta-backspace", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word);
        key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space);
+       key_bind("delete_to_next_space", "", "", NULL, (SIGNAL_FUNC) key_delete_to_next_space);
        key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line);
        key_bind("erase_to_beg_of_line", "", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
        key_bind("erase_to_end_of_line", "", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
@@ -570,8 +645,8 @@ void gui_readline_init(void)
         /* line transmitting */
        key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line);
        key_bind("word_completion", "", "^I", NULL, (SIGNAL_FUNC) key_word_completion);
-       key_bind("check_replaces", "Check word replaces", " ", NULL, (SIGNAL_FUNC) key_check_replaces);
-       key_bind("check_replaces", NULL, NULL, NULL, (SIGNAL_FUNC) key_check_replaces);
+       key_bind("erase_completion", "", "meta-k", NULL, (SIGNAL_FUNC) key_erase_completion);
+       key_bind("check_replaces", "Check word replaces", NULL, NULL, (SIGNAL_FUNC) key_check_replaces);
 
         /* window managing */
        key_bind("previous_window", "Previous window", "^P", NULL, (SIGNAL_FUNC) key_previous_window);
@@ -595,8 +670,11 @@ void gui_readline_init(void)
         /* inserting special input characters to line.. */
        key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text);
 
+        /* autoreplaces */
        key_bind("multi", NULL, "return", "check_replaces;send_line", NULL);
+       key_bind("multi", NULL, "space", "check_replaces;insert_text  ", NULL);
 
+        /* moving between windows */
        for (n = 0; changekeys[n] != '\0'; n++) {
                key = g_strdup_printf("meta-%c", changekeys[n]);
                ltoa(data, n+1);
@@ -604,6 +682,9 @@ void gui_readline_init(void)
                g_free(key);
        }
 
+        /* misc */
+       key_bind("stop_irc", "Send SIGSTOP to client", "^Z", NULL, (SIGNAL_FUNC) key_sig_stop);
+
         key_configure_thaw();
 
        signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
@@ -613,7 +694,7 @@ void gui_readline_init(void)
 void gui_readline_deinit(void)
 {
        g_free_not_null(cutbuffer);
-       g_source_remove(readtag);
+        input_listen_deinit();
 
         key_configure_freeze();
 
@@ -621,6 +702,8 @@ void gui_readline_deinit(void)
        key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character);
        key_unbind("backward_word", (SIGNAL_FUNC) key_backward_word);
        key_unbind("forward_word", (SIGNAL_FUNC) key_forward_word);
+       key_unbind("backward_to_space", (SIGNAL_FUNC) key_backward_to_space);
+       key_unbind("forward_to_space", (SIGNAL_FUNC) key_forward_to_space);
        key_unbind("beginning_of_line", (SIGNAL_FUNC) key_beginning_of_line);
        key_unbind("end_of_line", (SIGNAL_FUNC) key_end_of_line);
 
@@ -631,6 +714,7 @@ void gui_readline_deinit(void)
        key_unbind("delete_character", (SIGNAL_FUNC) key_delete_character);
        key_unbind("delete_next_word", (SIGNAL_FUNC) key_delete_next_word);
        key_unbind("delete_previous_word", (SIGNAL_FUNC) key_delete_previous_word);
+       key_unbind("delete_to_next_space", (SIGNAL_FUNC) key_delete_to_next_space);
        key_unbind("delete_to_previous_space", (SIGNAL_FUNC) key_delete_to_previous_space);
        key_unbind("erase_line", (SIGNAL_FUNC) key_erase_line);
        key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
@@ -657,6 +741,7 @@ void gui_readline_deinit(void)
 
        key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
        key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
+       key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
         keyboard_destroy(keyboard);
 
         key_configure_thaw();
index 6464921f59e56b636b3c79e06e030ba9925683de..9ce3a742412b036390ce64ced9c70e5a4762b7d2 100644 (file)
@@ -3,6 +3,9 @@
 
 extern char *cutbuffer;
 
+void input_listen_init(int handle);
+void input_listen_deinit(void);
+
 void readline(void);
 time_t get_idle_time(void);
 
index 9a199cdded559dc24af7bd7c350264dbb378f05b..cd834ee48c791f1c387710e67098d08ee70bb802 100644 (file)
 #include "settings.h"
 #include "special-vars.h"
 
-#include "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "gui-windows.h"
 #include "gui-printtext.h"
 
 static int window_create_override;
 
-static char *prompt, *prompt_window;
-
 static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
                                       MAIN_WINDOW_REC *parent)
 {
        GUI_WINDOW_REC *gui;
 
        window->width = parent->width;
-        window->height = parent->height;
+        window->height = MAIN_WINDOW_TEXT_HEIGHT(parent);
 
        gui = g_new0(GUI_WINDOW_REC, 1);
        gui->parent = parent;
        gui->view = textbuffer_view_create(textbuffer_create(),
                                           window->width, window->height,
+                                          settings_get_bool("scroll"),
+                                          settings_get_bool("term_utf8"));
+       textbuffer_view_set_default_indent(gui->view,
                                           settings_get_int("indent"),
-                                          settings_get_bool("indent_always"));
+                                          !settings_get_bool("indent_always"),
+                                          get_default_indent_func());
+       if (parent->active == window)
+               textbuffer_view_set_window(gui->view, parent->screen_win);
        return gui;
 }
 
@@ -61,33 +65,37 @@ static void sig_window_create_override(gpointer tab)
        window_create_override = GPOINTER_TO_INT(tab);
 }
 
-static void gui_window_created(WINDOW_REC *window)
+static void gui_window_created(WINDOW_REC *window, void *automatic)
 {
        MAIN_WINDOW_REC *parent;
+        int empty_window, new_parent;
 
        g_return_if_fail(window != NULL);
 
-       parent = window_create_override != 0 &&
-               active_win != NULL && WINDOW_GUI(active_win) != NULL ?
-               WINDOW_GUI(active_win)->parent : mainwindow_create();
+       new_parent = window_create_override == 0 ||
+               window_create_override == 2 ||
+               active_win == NULL || WINDOW_GUI(active_win) == NULL;
+       parent = !new_parent ? WINDOW_MAIN(active_win) : mainwindow_create();
        if (parent == NULL) {
                /* not enough space for new window, but we really can't
                   abort creation of the window anymore, so create hidden
                   window instead. */
-               parent = WINDOW_GUI(active_win)->parent;
+               parent = WINDOW_MAIN(active_win);
        }
        window_create_override = -1;
 
-       if (settings_get_bool("autostick_split_windows") &&
-           (parent->sticky_windows != NULL ||
-            (mainwindows->next != NULL && parent->active == NULL))) {
-                /* set the window sticky */
-               parent->sticky_windows =
-                       g_slist_append(parent->sticky_windows, window);
-       }
+        empty_window = parent->active == NULL;
 
        if (parent->active == NULL) parent->active = window;
        window->gui_data = gui_window_init(window, parent);
+
+       /* set only non-automatic windows sticky so that the windows
+          irssi creates at startup wont get sticky. */
+       if (automatic == NULL &&
+           (parent->sticky_windows ||
+            (new_parent && settings_get_bool("autostick_split_windows"))))
+               gui_window_set_sticky(window);
+
        signal_emit("gui window created", 1, window);
 }
 
@@ -101,21 +109,29 @@ static void gui_window_destroyed(WINDOW_REC *window)
        gui = WINDOW_GUI(window);
        parent = gui->parent;
 
+       gui_window_set_unsticky(window);
+
        signal_emit("gui window destroyed", 1, window);
 
        gui_window_deinit(gui);
        window->gui_data = NULL;
 
-       if (parent->active == window && mainwindows->next != NULL)
-               mainwindow_destroy(parent);
+       if (parent->active == window)
+               mainwindow_change_active(parent, window);
 }
 
 void gui_window_resize(WINDOW_REC *window, int width, int height)
 {
        GUI_WINDOW_REC *gui;
 
+       if (window->width == width && window->height == height)
+                return;
+
        gui = WINDOW_GUI(window);
 
+       irssi_set_dirty();
+        WINDOW_MAIN(window)->dirty = TRUE;
+
         window->width = width;
        window->height = height;
         textbuffer_view_resize(gui->view, width, height);
@@ -138,68 +154,66 @@ void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line)
        signal_emit("gui page scrolled", 1, window);
 }
 
-void window_update_prompt(void)
+void gui_window_set_sticky(WINDOW_REC *window)
 {
-        const char *special;
-       char *prompt, *text;
-        int var_used;
-
-       special = settings_get_str(active_win->active != NULL ?
-                                  "prompt" : "prompt_window");
-       if (*special == '\0') {
-               gui_entry_set_prompt("");
-               return;
-       }
+       GUI_WINDOW_REC *gui = WINDOW_GUI(window);
 
-       prompt = parse_special_string(special, active_win->active_server,
-                                     active_win->active, "", &var_used,
-                                     PARSE_FLAG_ISSET_ANY |
-                                     PARSE_FLAG_ESCAPE_VARS);
-       if (!var_used && strchr(special, '$') != NULL) {
-                /* none of the $vars had non-empty values, use empty prompt */
-               *prompt = '\0';
+       if (!gui->sticky) {
+               gui->sticky = TRUE;
+               gui->parent->sticky_windows++;
        }
-
-       /* set prompt */
-       text = show_lowascii(prompt);
-       gui_entry_set_prompt(text);
-       g_free(text);
-
-       g_free(prompt);
 }
 
-static void window_update_prompt_server(SERVER_REC *server)
+void gui_window_set_unsticky(WINDOW_REC *window)
 {
-       if (server == active_win->active_server)
-                window_update_prompt();
-}
+       GUI_WINDOW_REC *gui = WINDOW_GUI(window);
 
-static void window_update_prompt_window(WINDOW_REC *window)
-{
-       if (window == active_win)
-                window_update_prompt();
-}
-
-static void window_update_prompt_window_item(WI_ITEM_REC *item)
-{
-       if (item == active_win->active)
-                window_update_prompt();
+       if (gui->sticky) {
+               gui->sticky = FALSE;
+               gui->parent->sticky_windows--;
+       }
 }
 
 void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent)
 {
        MAIN_WINDOW_REC *oldparent;
 
-       oldparent = WINDOW_GUI(window)->parent;
+       oldparent = WINDOW_MAIN(window);
        if (oldparent == parent)
                return;
 
+        gui_window_set_unsticky(window);
        textbuffer_view_set_window(WINDOW_GUI(window)->view, NULL);
 
-       WINDOW_GUI(window)->parent = parent;
-       if (parent->height != oldparent->height ||
-           parent->width != oldparent->width)
-               gui_window_resize(window, parent->width, parent->height);
+       WINDOW_MAIN(window) = parent;
+        if (parent->sticky_windows)
+               gui_window_set_sticky(window);
+
+       if (MAIN_WINDOW_TEXT_HEIGHT(parent) !=
+           MAIN_WINDOW_TEXT_HEIGHT(oldparent) ||
+           parent->width != oldparent->width) {
+               gui_window_resize(window, parent->width,
+                                 MAIN_WINDOW_TEXT_HEIGHT(parent));
+       }
+}
+
+void gui_windows_reset_settings(void)
+{
+       GSList *tmp;
+
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+                GUI_WINDOW_REC *gui = WINDOW_GUI(rec);
+
+                textbuffer_view_set_default_indent(gui->view,
+                                                  settings_get_int("indent"),
+                                                  !settings_get_bool("indent_always"),
+                                                   get_default_indent_func());
+
+               textbuffer_view_set_scroll(gui->view,
+                                          gui->use_scroll ? gui->scroll :
+                                          settings_get_bool("scroll"));
+       }
 }
 
 static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
@@ -209,7 +223,7 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-               if (rec->sticky_windows == NULL)
+               if (!rec->sticky_windows)
                         return rec;
        }
 
@@ -217,83 +231,51 @@ static MAIN_WINDOW_REC *mainwindow_find_unsticky(void)
         return active_mainwin;
 }
 
-static void signal_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
+static void signal_window_changed(WINDOW_REC *window)
 {
        MAIN_WINDOW_REC *parent;
+        WINDOW_REC *old_window;
 
        g_return_if_fail(window != NULL);
 
         if (quitting) return;
 
-        parent = WINDOW_GUI(window)->parent;
+        parent = WINDOW_MAIN(window);
        if (is_window_visible(window)) {
                /* already visible */
                active_mainwin = parent;
        } else if (active_mainwin == NULL) {
                 /* no main window set yet */
                active_mainwin = parent;
-       } else if (g_slist_find(parent->sticky_windows, window) != NULL) {
+       } else if (WINDOW_GUI(window)->sticky) {
                 /* window is sticky, switch to correct main window */
                if (parent != active_mainwin)
                         active_mainwin = parent;
        } else {
                /* move window to active main window */
-                if (active_mainwin->sticky_windows != NULL) {
+                if (active_mainwin->sticky_windows) {
                        /* active mainwindow is sticky, we'll need to
                           set the window active somewhere else */
                         active_mainwin = mainwindow_find_unsticky();
                }
                gui_window_reparent(window, active_mainwin);
        }
-       active_mainwin->active = window;
 
-       if (old_window != NULL && !is_window_visible(old_window))
-                textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL);
+       old_window = active_mainwin->active;
+       if (old_window != NULL && old_window != window)
+               textbuffer_view_set_window(WINDOW_GUI(old_window)->view, NULL);
 
-       textbuffer_view_set_window(WINDOW_GUI(window)->view,
-                                  parent->curses_win);
-
-       window_update_prompt();
-}
+       active_mainwin->active = window;
 
-static void sig_check_window_update(WINDOW_REC *window)
-{
-       if (window == active_win)
-                window_update_prompt();
+       textbuffer_view_set_window(WINDOW_GUI(window)->view,
+                                  active_mainwin->screen_win);
+       if (WINDOW_GUI(window)->view->dirty)
+               active_mainwin->dirty = TRUE;
 }
 
 static void read_settings(void)
 {
-       GSList *tmp;
-
-       SIGNAL_FUNC funcs[] = {
-                (SIGNAL_FUNC) window_update_prompt,
-                (SIGNAL_FUNC) window_update_prompt_server,
-                (SIGNAL_FUNC) window_update_prompt_window,
-                (SIGNAL_FUNC) window_update_prompt_window_item
-       };
-
-       if (prompt != NULL) {
-               special_vars_remove_signals(prompt, 4, funcs);
-               special_vars_remove_signals(prompt_window, 4, funcs);
-               g_free(prompt);
-                g_free(prompt_window);
-       }
-       prompt = g_strdup(settings_get_str("prompt"));
-       prompt_window = g_strdup(settings_get_str("prompt_window"));
-
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               WINDOW_REC *rec = tmp->data;
-
-                textbuffer_view_set_default_indent(WINDOW_GUI(rec)->view,
-                                                  settings_get_int("indent"),
-                                                  settings_get_bool("indent_always"));
-       }
-
-       special_vars_add_signals(prompt, 4, funcs);
-       special_vars_add_signals(prompt_window, 4, funcs);
-
-       if (active_win != NULL) window_update_prompt();
+        gui_windows_reset_settings();
 }
 
 void gui_windows_init(void)
@@ -301,10 +283,8 @@ void gui_windows_init(void)
         settings_add_bool("lookandfeel", "autostick_split_windows", TRUE);
        settings_add_int("lookandfeel", "indent", 10);
        settings_add_bool("lookandfeel", "indent_always", FALSE);
-       settings_add_str("lookandfeel", "prompt", "[$[.15]T] ");
-       settings_add_str("lookandfeel", "prompt_window", "[$winname] ");
+       settings_add_bool("lookandfeel", "scroll", TRUE);
 
-        prompt = NULL; prompt_window = NULL;
        window_create_override = -1;
 
        read_settings();
@@ -312,15 +292,11 @@ void gui_windows_init(void)
        signal_add("window created", (SIGNAL_FUNC) gui_window_created);
        signal_add("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
        signal_add_first("window changed", (SIGNAL_FUNC) signal_window_changed);
-       signal_add("window item remove", (SIGNAL_FUNC) sig_check_window_update);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void gui_windows_deinit(void)
 {
-        g_free_not_null(prompt);
-        g_free_not_null(prompt_window);
-
        while (windows != NULL)
                window_destroy(windows->data);
 
@@ -328,6 +304,5 @@ void gui_windows_deinit(void)
        signal_remove("window created", (SIGNAL_FUNC) gui_window_created);
        signal_remove("window destroyed", (SIGNAL_FUNC) gui_window_destroyed);
        signal_remove("window changed", (SIGNAL_FUNC) signal_window_changed);
-       signal_remove("window item remove", (SIGNAL_FUNC) sig_check_window_update);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
index fb94b46f589d1a8583bfe40c589d529d9d49c09b..9d4afac7a4df2cff1ffc6f5f3e2b7563c80d227a 100644 (file)
@@ -5,6 +5,7 @@
 #include "textbuffer-view.h"
 
 #define WINDOW_GUI(a) ((GUI_WINDOW_REC *) ((a)->gui_data))
+#define WINDOW_MAIN(a) (WINDOW_GUI(a)->parent)
 
 #define is_window_visible(win) \
     (WINDOW_GUI(win)->parent->active == (win))
@@ -13,6 +14,10 @@ typedef struct {
        MAIN_WINDOW_REC *parent;
        TEXT_BUFFER_VIEW_REC *view;
 
+       unsigned int scroll:1;
+       unsigned int use_scroll:1;
+
+       unsigned int sticky:1;
        unsigned int use_insert_after:1;
         LINE_REC *insert_after;
 } GUI_WINDOW_REC;
@@ -31,6 +36,9 @@ void gui_window_reparent(WINDOW_REC *window, MAIN_WINDOW_REC *parent);
 void gui_window_scroll(WINDOW_REC *window, int lines);
 void gui_window_scroll_line(WINDOW_REC *window, LINE_REC *line);
 
-void window_update_prompt(void);
+void gui_window_set_sticky(WINDOW_REC *window);
+void gui_window_set_unsticky(WINDOW_REC *window);
+
+void gui_windows_reset_settings(void);
 
 #endif
index 75e68da3188fc83aa9a0bec11f7da87c915062d7..61fb5fc1d2a4617af6a30977f1789368c8356599 100644 (file)
 #include "gui-windows.h"
 #include "gui-printtext.h"
 
+#define DEFAULT_LASTLOG_BEFORE 3
+#define DEFAULT_LASTLOG_AFTER 3
 #define MAX_LINES_WITHOUT_FORCE 1000
 
 static void window_lastlog_clear(WINDOW_REC *window)
 {
-        TEXT_BUFFER_VIEW_REC *view;
-       GList *tmp, *next;
+       TEXT_BUFFER_VIEW_REC *view;
+        LINE_REC *line, *next;
 
-        screen_refresh_freeze();
+        term_refresh_freeze();
        view = WINDOW_GUI(window)->view;
-       for (tmp = textbuffer_view_get_lines(view); tmp != NULL; tmp = next) {
-               LINE_REC *line = tmp->data;
+       line = textbuffer_view_get_lines(view);
 
-                next = tmp->next;
-                if (line->info.level & MSGLEVEL_LASTLOG)
+       while (line != NULL) {
+                next = line->next;
+
+               if (line->info.level & MSGLEVEL_LASTLOG)
                        textbuffer_view_remove_line(view, line);
+                line = next;
        }
         textbuffer_view_redraw(view);
-        screen_refresh_thaw();
+        term_refresh_thaw();
 }
 
 /* Only unknown keys in `optlist' should be levels.
@@ -98,7 +102,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        GList *list, *tmp;
        GString *line;
         char *str;
-       int level, len;
+       int level, before, after, len;
 
         level = cmd_options_get_level("lastlog", optlist);
        if (level == -1) return; /* error in options */
@@ -131,14 +135,26 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        else
                startline = NULL;
 
-       if (startline == NULL) {
-                list = textbuffer_view_get_lines(WINDOW_GUI(window)->view);
-               startline = list == NULL ? NULL : list->data;
+       if (startline == NULL)
+                startline = textbuffer_view_get_lines(WINDOW_GUI(window)->view);
+
+       str = g_hash_table_lookup(optlist, "#");
+       if (str != NULL) {
+               before = after = atoi(str);
+       } else {
+               str = g_hash_table_lookup(optlist, "before");
+               before = str == NULL ? 0 : *str != '\0' ?
+                       atoi(str) : DEFAULT_LASTLOG_BEFORE;
+
+               str = g_hash_table_lookup(optlist, "after");
+               if (str == NULL) str = g_hash_table_lookup(optlist, "a");
+               after = str == NULL ? 0 : *str != '\0' ?
+                       atoi(str) : DEFAULT_LASTLOG_AFTER;
        }
 
        list = textbuffer_find_text(WINDOW_GUI(window)->view->buffer, startline,
                                    level, MSGLEVEL_LASTLOG,
-                                   searchtext,
+                                   searchtext, before, after,
                                    g_hash_table_lookup(optlist, "regexp") != NULL,
                                    g_hash_table_lookup(optlist, "word") != NULL,
                                    g_hash_table_lookup(optlist, "case") != NULL);
@@ -147,15 +163,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        if (count <= 0)
                tmp = list;
        else {
-               int pos = len-count;
-
+               int pos = len-count-start;
                if (pos < 0) pos = 0;
-               pos += start;
 
                tmp = pos > len ? NULL : g_list_nth(list, pos);
                len = g_list_length(tmp);
        }
 
+       if (g_hash_table_lookup(optlist, "count") != NULL) {
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_LASTLOG_COUNT, len);
+               g_list_free(list);
+               return;
+       }
+
        if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 &&
            g_hash_table_lookup(optlist, "force") == NULL) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
@@ -171,6 +192,20 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
         while (tmp != NULL && (count < 0 || count > 0)) {
                LINE_REC *rec = tmp->data;
 
+               if (rec == NULL) {
+                       if (tmp->next == NULL)
+                                break;
+                       if (fhandle != -1) {
+                               write(fhandle, "--\n", 3);
+                       } else {
+                               printformat_window(active_win,
+                                                  MSGLEVEL_LASTLOG,
+                                                  TXT_LASTLOG_SEPARATOR);
+                       }
+                        tmp = tmp->next;
+                       continue;
+               }
+
                 /* get the line text */
                textbuffer_line2text(rec, fhandle == -1, line);
                if (!settings_get_bool("timestamps")) {
@@ -207,9 +242,10 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
        g_list_free(list);
 }
 
-/* SYNTAX: LASTLOG [-] [-file <filename>] [-clear] [-<level> -<level...>]
-                  [-new | -away] [-regexp | -word] [-case]
-                  [-window <ref#|name>] [<pattern>] [<count> [<start>]] */
+/* SYNTAX: LASTLOG [-] [-file <filename>] [-window <ref#|name>] [-new | -away]
+                  [-<level> -<level...>] [-clear] [-count] [-case]
+                  [-regexp | -word] [-before [<#>]] [-after [<#>]]
+                  [-<# before+after>] [<pattern>] [<count> [<start>]] */
 static void cmd_lastlog(const char *data)
 {
        GHashTable *optlist;
@@ -219,13 +255,14 @@ static void cmd_lastlog(const char *data)
 
        g_return_if_fail(data != NULL);
 
-       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS,
-                           "lastlog", &optlist, &text, &countstr, &start))
+       if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS, "lastlog", &optlist,
+                           &text, &countstr, &start))
                return;
 
-       if (*start == '\0' && is_numeric(text, 0)) {
-               if (is_numeric(countstr, 0))
-                       start = countstr;
+       if (*start == '\0' && is_numeric(text, 0) && *text != '0' &&
+           (*countstr == '\0' || is_numeric(countstr, 0))) {
+               start = countstr;
                countstr = text;
                text = "";
        }
@@ -258,7 +295,7 @@ void lastlog_init(void)
 {
        command_bind("lastlog", NULL, (SIGNAL_FUNC) cmd_lastlog);
 
-       command_set_options("lastlog", "!- force clear -file -window new away word regexp case");
+       command_set_options("lastlog", "!- # force clear -file -window new away word regexp case count @a @after @before");
 }
 
 void lastlog_deinit(void)
diff --git a/apps/irssi/src/fe-text/mainwindows-layout.c b/apps/irssi/src/fe-text/mainwindows-layout.c
new file mode 100644 (file)
index 0000000..072b9b2
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ mainwindows-layout.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+
+#include "mainwindows.h"
+#include "gui-windows.h"
+#include "textbuffer-view.h"
+
+static void sig_layout_window_save(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       WINDOW_REC *active;
+        GUI_WINDOW_REC *gui;
+
+        gui = WINDOW_GUI(window);
+       if (gui->sticky) {
+               iconfig_node_set_bool(node, "sticky", TRUE);
+               active = gui->parent->active;
+               if (window != active)
+                       iconfig_node_set_int(node, "parent", active->refnum);
+       }
+
+       if (gui->use_scroll)
+                iconfig_node_set_bool(node, "scroll", gui->scroll);
+}
+
+static void sig_layout_window_restore(WINDOW_REC *window, CONFIG_NODE *node)
+{
+       WINDOW_REC *parent;
+        GUI_WINDOW_REC *gui;
+
+        gui = WINDOW_GUI(window);
+
+       parent = window_find_refnum(config_node_get_int(node, "parent", -1));
+       if (parent != NULL)
+               gui_window_reparent(window, WINDOW_MAIN(parent));
+
+       if (config_node_get_bool(node, "sticky", FALSE))
+               gui_window_set_sticky(window);
+       if (config_node_get_str(node, "scroll", NULL) != NULL) {
+               gui->use_scroll = TRUE;
+               gui->scroll = config_node_get_bool(node, "scroll", TRUE);
+                textbuffer_view_set_scroll(gui->view, gui->scroll);
+       }
+}
+
+static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node)
+{
+        char num[MAX_INT_STRLEN];
+
+        ltoa(num, window->active->refnum);
+       node = config_node_section(node, num, NODE_TYPE_BLOCK);
+
+       iconfig_node_set_int(node, "first_line", window->first_line);
+       iconfig_node_set_int(node, "lines", window->height);
+}
+
+static void sig_layout_save(void)
+{
+       CONFIG_NODE *node;
+
+       iconfig_set_str(NULL, "mainwindows", NULL);
+       node = iconfig_node_traverse("mainwindows", TRUE);
+
+       g_slist_foreach(mainwindows, (GFunc) main_window_save, node);
+}
+
+static int window_node_cmp(CONFIG_NODE *n1, CONFIG_NODE *n2)
+{
+       return config_node_get_int(n1, "first_line", 0) >
+               config_node_get_int(n2, "first_line", 0) ? -1 : 1;
+}
+
+/* Returns list of mainwindow nodes sorted by first_line
+   (lowest in screen first) */
+static GSList *get_sorted_windows_config(CONFIG_NODE *node)
+{
+       GSList *tmp, *output;
+
+        output = NULL;
+       tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               output = g_slist_insert_sorted(output, tmp->data,
+                                              (GCompareFunc) window_node_cmp);
+       }
+
+        return output;
+}
+
+static void sig_layout_restore(void)
+{
+        MAIN_WINDOW_REC *lower_window;
+        WINDOW_REC *window;
+       CONFIG_NODE *node;
+       GSList *tmp, *sorted_config;
+        int avail_height, height, *heights;
+       int i, lower_size, windows_count, diff;
+
+       node = iconfig_node_traverse("mainwindows", FALSE);
+       if (node == NULL) return;
+
+       sorted_config = get_sorted_windows_config(node);
+        windows_count = g_slist_length(sorted_config);
+
+        /* calculate the saved terminal height */
+       avail_height = term_height -
+               screen_reserved_top - screen_reserved_bottom;
+       height = 0;
+        heights = g_new0(int, windows_count);
+       for (i = 0, tmp = sorted_config; tmp != NULL; tmp = tmp->next, i++) {
+               CONFIG_NODE *node = tmp->data;
+
+                heights[i] = config_node_get_int(node, "lines", 0);
+               height += heights[i];
+       }
+
+       if (avail_height <= (WINDOW_MIN_SIZE*2)+1) {
+               /* we can fit only one window to screen -
+                  give it all the height we can */
+               windows_count = 1;
+                heights[0] = avail_height;
+       } else if (height != avail_height) {
+               /* Terminal's height is different from the saved one.
+                  Resize the windows so they fit to screen. */
+               while (height > avail_height &&
+                      windows_count*(WINDOW_MIN_SIZE+1) > avail_height) {
+                       /* all windows can't fit into screen,
+                          remove the lowest ones */
+                        windows_count--;
+               }
+
+                /* try to keep the windows' size about the same in percents */
+               for (i = 0; i < windows_count; i++) {
+                       int size = avail_height*heights[i]/height;
+                       if (size < WINDOW_MIN_SIZE+1)
+                                size = WINDOW_MIN_SIZE+1;
+                       heights[i] = size;
+               }
+
+               /* give/remove the last bits */
+                height = 0;
+               for (i = 0; i < windows_count; i++)
+                        height += heights[i];
+
+               diff = height < avail_height ? 1 : -1;
+               for (i = 0; height != avail_height; i++) {
+                       if (i == windows_count)
+                               i = 0;
+
+                       if (heights[i] > WINDOW_MIN_SIZE+1) {
+                               height += diff;
+                               heights[i] += diff;
+                       }
+               }
+       }
+
+       /* create all the visible windows with correct size */
+       lower_window = NULL; lower_size = 0;
+       for (i = 0, tmp = sorted_config; i < windows_count; tmp = tmp->next, i++) {
+               CONFIG_NODE *node = tmp->data;
+
+               /* create a new window + mainwindow */
+               signal_emit("gui window create override", 1,
+                           GINT_TO_POINTER(0));
+
+               window = window_create(NULL, TRUE);
+                window_set_refnum(window, atoi(node->key));
+
+               if (lower_size > 0)
+                       mainwindow_set_size(lower_window, lower_size, FALSE);
+
+               window_set_active(window);
+                active_mainwin = WINDOW_MAIN(window);
+
+                lower_window = WINDOW_MAIN(window);
+               lower_size = heights[i];
+               if (lower_size < WINDOW_MIN_SIZE+1)
+                       lower_size = WINDOW_MIN_SIZE+1;
+       }
+       g_slist_free(sorted_config);
+       g_free(heights);
+
+       if (lower_size > 0)
+               mainwindow_set_size(lower_window, lower_size, FALSE);
+}
+
+static void sig_layout_reset(void)
+{
+       iconfig_set_str(NULL, "mainwindows", NULL);
+}
+
+void mainwindows_layout_init(void)
+{
+       signal_add("layout save window", (SIGNAL_FUNC) sig_layout_window_save);
+       signal_add("layout restore window", (SIGNAL_FUNC) sig_layout_window_restore);
+       signal_add("layout save", (SIGNAL_FUNC) sig_layout_save);
+       signal_add_first("layout restore", (SIGNAL_FUNC) sig_layout_restore);
+       signal_add("layout reset", (SIGNAL_FUNC) sig_layout_reset);
+}
+
+void mainwindows_layout_deinit(void)
+{
+       signal_remove("layout save window", (SIGNAL_FUNC) sig_layout_window_save);
+       signal_remove("layout restore window", (SIGNAL_FUNC) sig_layout_window_restore);
+       signal_remove("layout save", (SIGNAL_FUNC) sig_layout_save);
+       signal_remove("layout restore", (SIGNAL_FUNC) sig_layout_restore);
+       signal_remove("layout reset", (SIGNAL_FUNC) sig_layout_reset);
+}
index d727464ee3efeb9992ce18ad17cf148e3591c631..7fcd910333b3ad0beb6cf69f00e91c63c88a9ab4 100644 (file)
@@ -27,8 +27,7 @@
 #include "settings.h"
 #include "printtext.h"
 
-#include "screen.h"
-#include "statusbar.h"
+#include "term.h"
 #include "gui-windows.h"
 
 #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1)
 GSList *mainwindows;
 MAIN_WINDOW_REC *active_mainwin;
 
-static int reserved_up, reserved_down;
-static int screen_width, screen_height;
+int screen_reserved_top, screen_reserved_bottom;
+static int old_screen_width, old_screen_height;
+
+#define mainwindow_create_screen(window) \
+       term_window_create(0, \
+                          (window)->first_line + (window)->statusbar_lines_top, \
+                          (window)->width, \
+                          (window)->height - (window)->statusbar_lines)
+
+#define mainwindow_set_screen_size(window) \
+       term_window_move((window)->screen_win, 0, \
+                        (window)->first_line + (window)->statusbar_lines_top, \
+                        (window)->width, \
+                        (window)->height - (window)->statusbar_lines);
+
 
 static MAIN_WINDOW_REC *find_window_with_room(void)
 {
@@ -49,7 +61,7 @@ static MAIN_WINDOW_REC *find_window_with_room(void)
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-               space = rec->height;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec);
                if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) {
                        biggest = space;
                        biggest_rec = rec;
@@ -59,45 +71,100 @@ static MAIN_WINDOW_REC *find_window_with_room(void)
        return biggest_rec;
 }
 
-#ifdef USE_CURSES_WINDOWS
-static void create_curses_window(MAIN_WINDOW_REC *window)
+#define window_size_equals(window, mainwin) \
+       ((window)->width == (mainwin)->width && \
+        (window)->height == MAIN_WINDOW_TEXT_HEIGHT(mainwin))
+
+static void mainwindow_resize_windows(MAIN_WINDOW_REC *window)
 {
-       window->curses_win = newwin(window->height, window->width,
-                                   window->first_line, 0);
-        idlok(window->curses_win, 1);
+       GSList *tmp;
+        int resized;
+
+       mainwindow_set_screen_size(window);
+
+       resized = FALSE;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec->gui_data != NULL &&
+                   WINDOW_GUI(rec)->parent == window &&
+                   !window_size_equals(rec, window)) {
+                        resized = TRUE;
+                       gui_window_resize(rec, window->width,
+                                         MAIN_WINDOW_TEXT_HEIGHT(window));
+               }
+       }
+
+        if (resized)
+               signal_emit("mainwindow resized", 1, window);
 }
-#endif
 
 static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff)
 {
-       GSList *tmp;
-
-       if (xdiff == 0 && ydiff == 0)
+       if (quitting || (xdiff == 0 && ydiff == 0))
                 return;
 
         window->width += xdiff;
        window->height = window->last_line-window->first_line+1;
-#ifdef USE_CURSES_WINDOWS
-#ifdef HAVE_CURSES_WRESIZE
-       wresize(window->curses_win, window->height, window->width);
-       mvwin(window->curses_win, window->first_line, 0);
-#else
-       delwin(window->curses_win);
-       create_curses_window(window);
-#endif
-#endif
+        window->size_dirty = TRUE;
+}
+
+static GSList *get_sticky_windows_sorted(MAIN_WINDOW_REC *mainwin)
+{
+       GSList *tmp, *list;
 
+        list = NULL;
        for (tmp = windows; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *rec = tmp->data;
 
-               if (rec->gui_data != NULL &&
-                   WINDOW_GUI(rec)->parent == window)
-                       gui_window_resize(rec, window->width, window->height);
+               if (WINDOW_GUI(rec)->sticky && WINDOW_MAIN(rec) == mainwin) {
+                       list = g_slist_insert_sorted(list, rec, (GCompareFunc)
+                                                    window_refnum_cmp);
+               }
        }
 
-       textbuffer_view_set_window(WINDOW_GUI(window->active)->view,
-                                  window->curses_win);
-       signal_emit("mainwindow resized", 1, window);
+        return list;
+}
+
+void mainwindow_change_active(MAIN_WINDOW_REC *mainwin,
+                             WINDOW_REC *skip_window)
+{
+        WINDOW_REC *window, *other;
+       GSList *tmp;
+
+        mainwin->active = NULL;
+       if (mainwin->sticky_windows) {
+               /* sticky window */
+                tmp = get_sticky_windows_sorted(mainwin);
+                window = tmp->data;
+               if (window == skip_window) {
+                       window = tmp->next == NULL ? NULL :
+                               tmp->next->data;
+               }
+                g_slist_free(tmp);
+
+               if (window != NULL) {
+                       window_set_active(window);
+                       return;
+               }
+       }
+
+        other = NULL;
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               if (rec != skip_window) {
+                       if (WINDOW_MAIN(rec) == mainwin) {
+                               window_set_active(rec);
+                               return;
+                       }
+                        other = rec;
+               }
+       }
+
+       /* no more non-sticky windows, remove main window */
+       window_set_active(other);
+       mainwindow_destroy(mainwin);
 }
 
 void mainwindows_recreate(void)
@@ -107,11 +174,10 @@ void mainwindows_recreate(void)
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-#ifdef USE_CURSES_WINDOWS
-               create_curses_window(rec);
-#endif
+               rec->screen_win = mainwindow_create_screen(rec);
+                rec->dirty = TRUE;
                textbuffer_view_set_window(WINDOW_GUI(rec->active)->view,
-                                          rec->curses_win);
+                                          rec->screen_win);
        }
 }
 
@@ -121,37 +187,36 @@ MAIN_WINDOW_REC *mainwindow_create(void)
        int space;
 
        rec = g_new0(MAIN_WINDOW_REC, 1);
-       rec->width = screen_width;
-       rec->statusbar_lines = 1;
+       rec->dirty = TRUE;
+       rec->width = term_width;
 
        if (mainwindows == NULL) {
                active_mainwin = rec;
 
-               rec->first_line = reserved_up;
-               rec->last_line = screen_height-1 -
-                       reserved_down-rec->statusbar_lines;
+               rec->first_line = screen_reserved_top;
+               rec->last_line = term_height-1 - screen_reserved_bottom;
                rec->height = rec->last_line-rec->first_line+1;
        } else {
-               parent = WINDOW_GUI(active_win)->parent;
-               if (parent->height < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
+               parent = WINDOW_MAIN(active_win);
+               if (MAIN_WINDOW_TEXT_HEIGHT(parent) <
+                   WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
                        parent = find_window_with_room();
                if (parent == NULL)
                        return NULL; /* not enough space */
 
-               space = (parent->height-parent->statusbar_lines)/2;
+               space = parent->height / 2;
                rec->first_line = parent->first_line;
-               rec->last_line = rec->first_line + space-rec->statusbar_lines;
+               rec->last_line = rec->first_line + space;
                rec->height = rec->last_line-rec->first_line+1;
-               parent->first_line = rec->last_line+1+rec->statusbar_lines;
+
+               parent->first_line = rec->last_line+1;
                parent->height = parent->last_line-parent->first_line+1;
 
                mainwindow_resize(parent, 0, -space-1);
        }
 
-#ifdef USE_CURSES_WINDOWS
-       rec->curses_win = newwin(rec->height, rec->width, rec->first_line, 0);
-       refresh();
-#endif
+       rec->screen_win = mainwindow_create_screen(rec);
+       term_refresh(NULL);
 
        mainwindows = g_slist_append(mainwindows, rec);
        signal_emit("mainwindow created", 1, rec);
@@ -211,7 +276,7 @@ static void mainwindows_add_space(int first_line, int last_line)
 
        rec = mainwindows_find_upper(first_line);
        if (rec != NULL) {
-               rec->last_line = last_line-rec->statusbar_lines;
+               rec->last_line = last_line;
                mainwindow_resize(rec, 0, size);
        }
 }
@@ -225,7 +290,7 @@ static void gui_windows_remove_parent(MAIN_WINDOW_REC *window)
        for (tmp = windows; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *rec = tmp->data;
 
-               if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window)
+               if (rec->gui_data != NULL && WINDOW_MAIN(rec) == window)
                         gui_window_reparent(rec, new_parent);
        }
 }
@@ -234,20 +299,18 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window)
 {
        g_return_if_fail(window != NULL);
 
-#ifdef USE_CURSES_WINDOWS
-       delwin(window->curses_win);
-#endif
-
        mainwindows = g_slist_remove(mainwindows, window);
        signal_emit("mainwindow destroyed", 1, window);
 
+        term_window_destroy(window->screen_win);
+
        if (!quitting && mainwindows != NULL) {
                gui_windows_remove_parent(window);
-               mainwindows_add_space(window->first_line, window->last_line+window->statusbar_lines);
+               mainwindows_add_space(window->first_line, window->last_line);
 
                mainwindows_redraw();
-               statusbar_redraw(NULL);
        }
+
        g_free(window);
 
        if (active_mainwin == window) active_mainwin = NULL;
@@ -257,13 +320,12 @@ void mainwindows_redraw(void)
 {
         GSList *tmp;
 
-       screen_refresh_freeze();
+        irssi_set_dirty();
        for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-                gui_window_redraw(rec->active);
+                rec->dirty = TRUE;
        }
-       screen_refresh_thaw();
 }
 
 static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
@@ -289,62 +351,46 @@ GSList *mainwindows_get_sorted(int reverse)
        return list;
 }
 
-static void mainwindows_resize_too_small(int xdiff, int ydiff)
-{
-       GSList *sorted, *tmp;
-        int space, moved;
-
-       /* terminal is too small - just take the space whereever possible */
-       sorted = mainwindows_get_sorted(FALSE);
-       moved = 0;
-       for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
-
-               space = rec->height;
-               if (ydiff == 0 || space <= 0) {
-                       if (moved > 0) {
-                               rec->first_line -= moved;
-                               rec->last_line -= moved;
-                               signal_emit("mainwindow moved", 1, rec);
-                       }
-                       continue;
-               }
-
-               if (space > -ydiff) space = -ydiff;
-               ydiff += space;
-               rec->first_line -= moved;
-               moved += space;
-               rec->last_line -= space;
-               mainwindow_resize(rec, xdiff, -space);
-       }
-       g_slist_free(sorted);
-}
-
 static void mainwindows_resize_smaller(int xdiff, int ydiff)
 {
+        MAIN_WINDOW_REC *rec;
        GSList *sorted, *tmp;
         int space;
 
-        space = 0;
-       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
+       for (;;) {
+               sorted = mainwindows_get_sorted(TRUE);
+               space = 0;
+               for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+                       rec = tmp->data;
+                       space += MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
+               }
 
-               space += rec->height-WINDOW_MIN_SIZE;
-       }
+               if (space >= -ydiff)
+                       break;
 
-       if (space < -ydiff) {
-               /* not enough space, use different algorithm */
-               mainwindows_resize_too_small(xdiff, ydiff);
-               return;
+               rec = sorted->data;
+               sorted = g_slist_remove(sorted, rec);
+
+               if (sorted != NULL) {
+                       /* terminal is too small - destroy the
+                          uppest window and try again */
+                       mainwindow_destroy(rec);
+               } else {
+                       /* only one window in screen.. just force the resize */
+                       rec->last_line += ydiff;
+                       mainwindow_resize(rec, xdiff, ydiff);
+                        return;
+               }
        }
 
        /* resize windows that have space */
-       sorted = mainwindows_get_sorted(TRUE);
        for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
+               rec = tmp->data;
 
-               space = rec->height-WINDOW_MIN_SIZE;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
                if (space <= 0) {
+                       mainwindow_resize(rec, xdiff, 0);
+
                        rec->first_line += ydiff;
                        rec->last_line += ydiff;
                        signal_emit("mainwindow moved", 1, rec);
@@ -359,6 +405,14 @@ static void mainwindows_resize_smaller(int xdiff, int ydiff)
 
                mainwindow_resize(rec, xdiff, -space);
        }
+
+       if (xdiff != 0) {
+               while (tmp != NULL) {
+                       mainwindow_resize(tmp->data, xdiff, 0);
+                       tmp = tmp->next;
+               }
+       }
+
        g_slist_free(sorted);
 }
 
@@ -372,8 +426,9 @@ static void mainwindows_resize_bigger(int xdiff, int ydiff)
        for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
-               space = rec->height-WINDOW_MIN_SIZE;
+               space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE;
                if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
+                       mainwindow_resize(rec, xdiff, 0);
                        if (moved > 0) {
                                rec->first_line += moved;
                                rec->last_line += moved;
@@ -415,12 +470,11 @@ void mainwindows_resize(int width, int height)
 {
        int xdiff, ydiff;
 
-       xdiff = width-screen_width;
-       ydiff = height-screen_height;
-        screen_width = width;
-        screen_height = height;
+       xdiff = width-old_screen_width;
+       ydiff = height-old_screen_height;
+        old_screen_width = width;
+        old_screen_height = height;
 
-       screen_refresh_freeze();
        if (ydiff < 0)
                mainwindows_resize_smaller(xdiff, ydiff);
        else if (ydiff > 0)
@@ -428,104 +482,197 @@ void mainwindows_resize(int width, int height)
         else if (xdiff != 0)
                mainwindows_resize_horiz(xdiff);
 
+        signal_emit("terminal resized", 0);
+
        irssi_redraw();
-       screen_refresh_thaw();
 }
 
-int mainwindows_reserve_lines(int count, int up)
+int mainwindows_reserve_lines(int top, int bottom)
 {
        MAIN_WINDOW_REC *window;
        int ret;
 
-       if (up) {
-               g_return_val_if_fail(count > 0 || reserved_up > count, -1);
+        ret = -1;
+       if (top != 0) {
+               g_return_val_if_fail(top > 0 || screen_reserved_top > top, -1);
 
-               ret = reserved_up;
-               reserved_up += count;
+               ret = screen_reserved_top;
+               screen_reserved_top += top;
 
                window = mainwindows_find_lower(-1);
-               if (window != NULL) window->first_line += count;
-       } else {
-               g_return_val_if_fail(count > 0 || reserved_down > count, -1);
+               if (window != NULL) {
+                       window->first_line += top;
+                       mainwindow_resize(window, 0, -top);
+               }
+       }
 
-               ret = reserved_down;
-               reserved_down += count;
+       if (bottom != 0) {
+               g_return_val_if_fail(bottom > 0 || screen_reserved_bottom > bottom, -1);
 
-               window = mainwindows_find_upper(screen_height);
-               if (window != NULL) window->last_line -= count;
-       }
+               ret = screen_reserved_bottom;
+               screen_reserved_bottom += bottom;
 
-       if (window != NULL)
-               mainwindow_resize(window, 0, -count);
+               window = mainwindows_find_upper(term_height);
+               if (window != NULL) {
+                       window->last_line -= bottom;
+                       mainwindow_resize(window, 0, -bottom);
+               }
+       }
 
        return ret;
 }
 
+int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window,
+                                  int top, int bottom)
+{
+       int ret;
+
+        ret = -1;
+       if (top != 0) {
+                ret = window->statusbar_lines_top;
+               window->statusbar_lines_top += top;
+                window->statusbar_lines += top;
+       }
+
+       if (bottom != 0) {
+                ret = window->statusbar_lines_bottom;
+                window->statusbar_lines_bottom += bottom;
+                window->statusbar_lines += bottom;
+       }
+
+       if (top+bottom != 0)
+                window->size_dirty = TRUE;
+
+        return ret;
+}
+
 static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win,
                                   MAIN_WINDOW_REC *shrink_win, int count)
 {
+        irssi_set_dirty();
+
        mainwindow_resize(grow_win, 0, count);
        mainwindow_resize(shrink_win, 0, -count);
-       gui_window_redraw(grow_win->active);
-       gui_window_redraw(shrink_win->active);
-       statusbar_redraw(grow_win->statusbar);
-       statusbar_redraw(shrink_win->statusbar);
+       grow_win->dirty = TRUE;
+       shrink_win->dirty = TRUE;
 }
 
-static int mainwindow_grow(MAIN_WINDOW_REC *window, int count)
+static int try_shrink_lower(MAIN_WINDOW_REC *window, int count)
 {
        MAIN_WINDOW_REC *shrink_win;
 
-       /* shrink lower window */
        shrink_win = mainwindows_find_lower(window->last_line);
-       if (shrink_win != NULL && shrink_win->height-count >= WINDOW_MIN_SIZE) {
+       if (shrink_win != NULL &&
+           MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) {
                 window->last_line += count;
                shrink_win->first_line += count;
-       } else {
-               /* shrink upper window */
-               shrink_win = mainwindows_find_upper(window->first_line);
-               if (shrink_win == NULL ||
-                   shrink_win->height-count < WINDOW_MIN_SIZE)
-                       return FALSE;
+               mainwindows_resize_two(window, shrink_win, count);
+                return TRUE;
+       }
 
+        return FALSE;
+}
+
+static int try_shrink_upper(MAIN_WINDOW_REC *window, int count)
+{
+       MAIN_WINDOW_REC *shrink_win;
+
+       shrink_win = mainwindows_find_upper(window->first_line);
+       if (shrink_win != NULL &&
+           MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) {
                window->first_line -= count;
                shrink_win->last_line -= count;
+               mainwindows_resize_two(window, shrink_win, count);
+                return TRUE;
+       }
+
+        return FALSE;
+}
+
+static int mainwindow_grow(MAIN_WINDOW_REC *window, int count,
+                          int resize_lower)
+{
+       if (!resize_lower || !try_shrink_lower(window, count)) {
+               if (!try_shrink_upper(window, count)) {
+                        if (resize_lower || !try_shrink_lower(window, count))
+                               return FALSE;
+               }
        }
 
-       mainwindows_resize_two(window, shrink_win, count);
         return TRUE;
 }
 
-static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count)
+static int try_grow_lower(MAIN_WINDOW_REC *window, int count)
 {
        MAIN_WINDOW_REC *grow_win;
 
-       if (window->height-count < WINDOW_MIN_SIZE)
-                return FALSE;
-
        grow_win = mainwindows_find_lower(window->last_line);
        if (grow_win != NULL) {
                 window->last_line -= count;
                grow_win->first_line -= count;
-       } else {
-               grow_win = mainwindows_find_upper(window->first_line);
-               if (grow_win == NULL) return FALSE;
+               mainwindows_resize_two(grow_win, window, count);
+       }
 
+        return grow_win != NULL;
+}
+
+static int try_grow_upper(MAIN_WINDOW_REC *window, int count)
+{
+       MAIN_WINDOW_REC *grow_win;
+
+       grow_win = mainwindows_find_upper(window->first_line);
+       if (grow_win != NULL) {
                window->first_line += count;
                grow_win->last_line += count;
+               mainwindows_resize_two(grow_win, window, count);
+       }
+
+        return grow_win != NULL;
+}
+
+static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lower)
+{
+       if (MAIN_WINDOW_TEXT_HEIGHT(window)-count < WINDOW_MIN_SIZE)
+                return FALSE;
+
+       if (!resize_lower || !try_grow_lower(window, count)) {
+               if (!try_grow_upper(window, count)) {
+                        if (resize_lower || !try_grow_lower(window, count))
+                               return FALSE;
+               }
        }
 
-       mainwindows_resize_two(grow_win, window, count);
         return TRUE;
 }
 
-void mainwindow_set_size(MAIN_WINDOW_REC *window, int size)
+/* Change the window height - the height includes the lines needed for
+   statusbars. If resize_lower is TRUE, the lower window is first tried
+   to be resized instead of upper window. */
+void mainwindow_set_size(MAIN_WINDOW_REC *window, int height, int resize_lower)
 {
-        size -= window->height;
-       if (size < 0)
-               mainwindow_shrink(window, size);
+        height -= window->height;
+       if (height < 0)
+               mainwindow_shrink(window, -height, resize_lower);
        else
-               mainwindow_grow(window, size);
+               mainwindow_grow(window, height, resize_lower);
+}
+
+void mainwindows_redraw_dirty(void)
+{
+       GSList *tmp;
+
+       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+               MAIN_WINDOW_REC *rec = tmp->data;
+
+               if (rec->size_dirty) {
+                        rec->size_dirty = FALSE;
+                       mainwindow_resize_windows(rec);
+               }
+               if (rec->dirty) {
+                        rec->dirty = FALSE;
+                       gui_window_redraw(rec->active);
+               }
+       }
 }
 
 /* SYNTAX: WINDOW GROW [<lines>] */
@@ -535,9 +682,9 @@ static void cmd_window_grow(const char *data)
        int count;
 
        count = *data == '\0' ? 1 : atoi(data);
-       window = WINDOW_GUI(active_win)->parent;
+       window = WINDOW_MAIN(active_win);
 
-       if (!mainwindow_grow(window, count)) {
+       if (!mainwindow_grow(window, count, FALSE)) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_TOO_SMALL);
        }
@@ -546,13 +693,10 @@ static void cmd_window_grow(const char *data)
 /* SYNTAX: WINDOW SHRINK [<lines>] */
 static void cmd_window_shrink(const char *data)
 {
-       MAIN_WINDOW_REC *window;
        int count;
 
        count = *data == '\0' ? 1 : atoi(data);
-       window = WINDOW_GUI(active_win)->parent;
-
-       if (!mainwindow_shrink(window, count)) {
+       if (!mainwindow_shrink(WINDOW_MAIN(active_win), count, FALSE)) {
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_TOO_SMALL);
        }
@@ -567,7 +711,8 @@ static void cmd_window_size(const char *data)
        if (!is_numeric(data, 0)) return;
        size = atoi(data);
 
-       size -= WINDOW_GUI(active_win)->parent->height;
+       size -= WINDOW_MAIN(active_win)->height -
+               WINDOW_MAIN(active_win)->statusbar_lines;
        if (size == 0) return;
 
        ltoa(sizestr, size < 0 ? -size : size);
@@ -587,33 +732,32 @@ static void cmd_window_balance(void)
        windows = g_slist_length(mainwindows);
        if (windows == 1) return;
 
-       avail_size = screen_height - reserved_up-reserved_down;
+       avail_size = term_height - screen_reserved_top-screen_reserved_bottom;
        unit_size = avail_size/windows;
        bigger_units = avail_size%windows;
 
        sorted = mainwindows_get_sorted(FALSE);
-        last_line = 0;
+        last_line = screen_reserved_top;
        for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
                MAIN_WINDOW_REC *rec = tmp->data;
 
                old_size = rec->height;
-               rec->first_line = last_line+1;
-               rec->last_line = rec->first_line-1 + unit_size -
-                       rec->statusbar_lines;
-               rec->height = rec->last_line-rec->first_line+1;
+               rec->first_line = last_line;
+               rec->last_line = rec->first_line + unit_size-1;
 
                if (bigger_units > 0) {
                        rec->last_line++;
                         bigger_units--;
                }
-               last_line = rec->last_line + rec->statusbar_lines;
+
+               rec->height = rec->last_line-rec->first_line+1;
+               last_line = rec->last_line+1;
 
                mainwindow_resize(rec, 0, rec->height-old_size);
        }
        g_slist_free(sorted);
 
        mainwindows_redraw();
-       statusbar_redraw(NULL);
 }
 
 /* SYNTAX: WINDOW HIDE [<number>|<name>] */
@@ -642,16 +786,16 @@ static void cmd_window_hide(const char *data)
        if (window == NULL || !is_window_visible(window))
                return;
 
-       if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
+       if (WINDOW_MAIN(window)->sticky_windows) {
                printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                   TXT_CANT_HIDE_STICKY_WINDOWS);
                 return;
        }
 
-       mainwindow_destroy(WINDOW_GUI(window)->parent);
+       mainwindow_destroy(WINDOW_MAIN(window));
 
        if (active_mainwin == NULL) {
-               active_mainwin = WINDOW_GUI(active_win)->parent;
+               active_mainwin = WINDOW_MAIN(active_win);
                 window_set_active(active_mainwin->active);
        }
 }
@@ -677,20 +821,19 @@ static void cmd_window_show(const char *data)
        if (window == NULL || is_window_visible(window))
                return;
 
-       if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
+       if (WINDOW_MAIN(window)->sticky_windows) {
                printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                   TXT_CANT_SHOW_STICKY_WINDOWS);
                 return;
        }
 
        parent = mainwindow_create();
-       if (settings_get_bool("autostick_split_windows")) {
-               parent->sticky_windows =
-                       g_slist_append(parent->sticky_windows, window);
-       }
-        parent->active = window;
+       parent->active = window;
         gui_window_reparent(window, parent);
 
+       if (settings_get_bool("autostick_split_windows"))
+                gui_window_set_sticky(window);
+
        active_mainwin = NULL;
        window_set_active(window);
 }
@@ -701,6 +844,8 @@ static void cmd_window_up(void)
        MAIN_WINDOW_REC *rec;
 
        rec = mainwindows_find_upper(active_mainwin->first_line);
+       if (rec == NULL)
+               rec = mainwindows_find_upper(term_height);
        if (rec != NULL)
                window_set_active(rec->active);
 }
@@ -711,171 +856,220 @@ static void cmd_window_down(void)
        MAIN_WINDOW_REC *rec;
 
        rec = mainwindows_find_lower(active_mainwin->last_line);
+       if (rec == NULL)
+               rec = mainwindows_find_lower(-1);
        if (rec != NULL)
                window_set_active(rec->active);
 }
 
+#define WINDOW_STICKY_MATCH(window, sticky_parent) \
+       ((!WINDOW_GUI(window)->sticky && (sticky_parent) == NULL) || \
+        (WINDOW_GUI(window)->sticky && \
+         WINDOW_MAIN(window) == (sticky_parent)))
+
+static int window_refnum_left(int refnum, int wrap)
+{
+        MAIN_WINDOW_REC *find_sticky;
+       WINDOW_REC *window;
+
+       window = window_find_refnum(refnum);
+       g_return_val_if_fail(window != NULL, -1);
+
+       find_sticky = WINDOW_MAIN(window)->sticky_windows ?
+               WINDOW_MAIN(window) : NULL;
+
+       do {
+               refnum = window_refnum_prev(refnum, wrap);
+               if (refnum < 0)
+                       break;
+
+               window = window_find_refnum(refnum);
+       } while (!WINDOW_STICKY_MATCH(window, find_sticky));
+
+        return refnum;
+}
+
+static int window_refnum_right(int refnum, int wrap)
+{
+        MAIN_WINDOW_REC *find_sticky;
+       WINDOW_REC *window;
+
+       window = window_find_refnum(refnum);
+       g_return_val_if_fail(window != NULL, -1);
+
+       find_sticky = WINDOW_MAIN(window)->sticky_windows ?
+               WINDOW_MAIN(window) : NULL;
+
+       do {
+               refnum = window_refnum_next(refnum, wrap);
+               if (refnum < 0)
+                       break;
+
+               window = window_find_refnum(refnum);
+       } while (!WINDOW_STICKY_MATCH(window, find_sticky));
+
+        return refnum;
+}
+
 /* SYNTAX: WINDOW LEFT */
 static void cmd_window_left(const char *data, SERVER_REC *server, void *item)
 {
-        MAIN_WINDOW_REC *parent;
-        WINDOW_REC *window;
-       int pos, num;
-
-        window = NULL;
-       if (active_mainwin->sticky_windows == NULL) {
-               /* no sticky windows, go to previous non-sticky window */
-                num = active_win->refnum;
-               do {
-                       num = window_refnum_prev(num, TRUE);
-                       if (num < 0) {
-                                window = NULL;
-                               break;
-                       }
-                        window = window_find_refnum(num);
-                       parent = WINDOW_GUI(window)->parent;
-               } while (g_slist_find(parent->sticky_windows, window) != NULL);
-       } else {
-               pos = g_slist_index(active_mainwin->sticky_windows,
-                                   active_win);
-               if (pos > 0) {
-                       window = g_slist_nth_data(
-                                       active_mainwin->sticky_windows, pos-1);
-               } else {
-                       window = g_slist_last(
-                                       active_mainwin->sticky_windows)->data;
-               }
-       }
+       int refnum;
 
-        if (window != NULL)
-               window_set_active(window);
+       refnum = window_refnum_left(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_active(window_find_refnum(refnum));
 }
 
 /* SYNTAX: WINDOW RIGHT */
 static void cmd_window_right(void)
 {
-        MAIN_WINDOW_REC *parent;
-       WINDOW_REC *window;
-        GSList *tmp;
-       int num;
-
-        window = NULL;
-       if (active_mainwin->sticky_windows == NULL) {
-               /* no sticky windows, go to next non-sticky window */
-                num = active_win->refnum;
-               do {
-                       num = window_refnum_next(num, TRUE);
-                       if (num < 0) {
-                                window = NULL;
-                               break;
-                       }
-                        window = window_find_refnum(num);
-                       parent = WINDOW_GUI(window)->parent;
-               } while (g_slist_find(parent->sticky_windows, window) != NULL);
-       } else {
-               tmp = g_slist_find(active_mainwin->sticky_windows, active_win);
-               if (tmp != NULL) {
-                       window = tmp->next != NULL ? tmp->next->data :
-                               active_mainwin->sticky_windows->data;
-               }
-       }
+       int refnum;
 
-        if (window != NULL)
-               window_set_active(window);
+       refnum = window_refnum_right(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_active(window_find_refnum(refnum));
 }
 
-static void mainwindow_change_window(MAIN_WINDOW_REC *mainwin,
-                                    WINDOW_REC *window)
+static void window_reparent(WINDOW_REC *win, MAIN_WINDOW_REC *mainwin)
 {
-       MAIN_WINDOW_REC *parent;
-       GSList *tmp;
+       MAIN_WINDOW_REC *old_mainwin;
 
-       if (mainwin->sticky_windows != NULL) {
-               /* sticky window */
-               window_set_active(mainwin->sticky_windows->data);
-                return;
-       }
+       old_mainwin = WINDOW_MAIN(win);
 
-       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-               WINDOW_REC *rec = tmp->data;
+       if (old_mainwin != mainwin) {
+               gui_window_set_unsticky(win);
 
-                parent = WINDOW_GUI(rec)->parent;
-               if (rec != window &&
-                   g_slist_find(parent->sticky_windows, rec) == NULL) {
-                        window_set_active(rec);
-                        return;
+               if (old_mainwin->active == win) {
+                       mainwindow_change_active(old_mainwin, win);
+                       if (active_mainwin == NULL) {
+                               active_mainwin = mainwin;
+                               window_set_active(mainwin->active);
+                       }
                }
-       }
 
-        /* no more non-sticky windows, remove main window */
-        mainwindow_destroy(mainwin);
+               gui_window_reparent(win, mainwin);
+               window_set_active(win);
+       }
 }
 
-/* SYNTAX: WINDOW STICK [ON|OFF|<ref#>] */
+/* SYNTAX: WINDOW STICK [<ref#>] [ON|OFF] */
 static void cmd_window_stick(const char *data)
 {
-       MAIN_WINDOW_REC *window = active_mainwin;
+        MAIN_WINDOW_REC *mainwin;
+        WINDOW_REC *win;
 
-       if (is_numeric(data, '\0')) {
-               WINDOW_REC *win = window_find_refnum(atoi(data));
+        mainwin = active_mainwin;
+        win = active_mainwin->active;
+
+       if (is_numeric(data, ' ')) {
+               /* ref# specified */
+               win = window_find_refnum(atoi(data));
                if (win == NULL) {
                        printformat_window(active_win, MSGLEVEL_CLIENTERROR,
                                           TXT_REFNUM_NOT_FOUND, data);
                        return;
                }
-                window = WINDOW_GUI(win)->parent;
+
+               while (*data != ' ' && *data != '\0') data++;
+               while (*data == ' ') data++;
        }
 
-       if (g_strncasecmp(data, "OF", 2) == 0 || toupper(*data) == 'N') {
+       if (g_strncasecmp(data, "OF", 2) == 0 || i_toupper(*data) == 'N') {
                /* unset sticky */
-               if (g_slist_find(window->sticky_windows, active_win) == NULL) {
-                       printformat_window(active_win, MSGLEVEL_CLIENTERROR,
+               if (!WINDOW_GUI(win)->sticky) {
+                       printformat_window(win, MSGLEVEL_CLIENTERROR,
                                           TXT_WINDOW_NOT_STICKY);
                } else {
-                       window->sticky_windows =
-                               g_slist_remove(window->sticky_windows,
-                                              active_win);
-                       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                        gui_window_set_unsticky(win);
+                       printformat_window(win, MSGLEVEL_CLIENTNOTICE,
                                           TXT_WINDOW_UNSET_STICKY);
                }
        } else {
-                /* set sticky */
-               active_mainwin->sticky_windows =
-                       g_slist_remove(active_mainwin->sticky_windows,
-                                      active_win);
-
-               if (g_slist_find(window->sticky_windows, active_win) == NULL) {
-                       window->sticky_windows =
-                               g_slist_append(window->sticky_windows,
-                                              active_win);
-               }
-               if (window != active_mainwin) {
-                        WINDOW_REC *movewin;
-
-                        movewin = active_win;
-                       gui_window_reparent(movewin, window);
-                        mainwindow_change_window(active_mainwin, movewin);
-
-                       active_mainwin = window;
-                        window_set_active(movewin);
-               }
+               /* set sticky */
+               window_reparent(win, mainwin);
+                gui_window_set_sticky(win);
 
                printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                   TXT_WINDOW_SET_STICKY);
        }
 }
 
+/* SYNTAX: WINDOW MOVE LEFT */
+static void cmd_window_move_left(void)
+{
+       int refnum;
+
+       refnum = window_refnum_left(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_refnum(active_win, refnum);
+}
+
+/* SYNTAX: WINDOW MOVE RIGHT */
+static void cmd_window_move_right(void)
+{
+       int refnum;
+
+       refnum = window_refnum_right(active_win->refnum, TRUE);
+       if (refnum != -1)
+               window_set_refnum(active_win, refnum);
+}
+
+/* SYNTAX: WINDOW MOVE UP */
+static void cmd_window_move_up(void)
+{
+       MAIN_WINDOW_REC *rec;
+
+       rec = mainwindows_find_upper(active_mainwin->first_line);
+        if (rec != NULL)
+               window_reparent(active_win, rec);
+}
+
+/* SYNTAX: WINDOW MOVE DOWN */
+static void cmd_window_move_down(void)
+{
+       MAIN_WINDOW_REC *rec;
+
+       rec = mainwindows_find_lower(active_mainwin->last_line);
+       if (rec != NULL)
+               window_reparent(active_win, rec);
+}
+
+static void windows_print_sticky(MAIN_WINDOW_REC *win)
+{
+        GSList *tmp, *list;
+       GString *str;
+
+        /* convert to string */
+       str = g_string_new(NULL);
+       list = get_sticky_windows_sorted(win);
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               WINDOW_REC *rec = tmp->data;
+
+               g_string_sprintfa(str, "#%d, ", rec->refnum);
+       }
+        g_string_truncate(str, str->len-2);
+        g_slist_free(list);
+
+       printformat_window(win->active, MSGLEVEL_CLIENTCRAP,
+                          TXT_WINDOW_INFO_STICKY, str->str);
+        g_string_free(str, TRUE);
+}
+
+static void sig_window_print_info(WINDOW_REC *win)
+{
+       if (WINDOW_MAIN(win)->sticky_windows)
+                windows_print_sticky(WINDOW_MAIN(win));
+}
+
 void mainwindows_init(void)
 {
-       screen_width = COLS;
-       screen_height = LINES;
+       old_screen_width = term_width;
+       old_screen_height = term_height;
 
        mainwindows = NULL;
        active_mainwin = NULL;
-       reserved_up = reserved_down = 0;
-
-       /* for entry line */
-       mainwindows_reserve_lines(1, FALSE);
+       screen_reserved_top = screen_reserved_bottom = 0;
 
        command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow);
        command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink);
@@ -888,6 +1082,11 @@ void mainwindows_init(void)
        command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left);
        command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right);
        command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick);
+       command_bind("window move left", NULL, (SIGNAL_FUNC) cmd_window_move_left);
+       command_bind("window move right", NULL, (SIGNAL_FUNC) cmd_window_move_right);
+       command_bind("window move up", NULL, (SIGNAL_FUNC) cmd_window_move_up);
+       command_bind("window move down", NULL, (SIGNAL_FUNC) cmd_window_move_down);
+        signal_add("window print info", (SIGNAL_FUNC) sig_window_print_info);
 }
 
 void mainwindows_deinit(void)
@@ -906,4 +1105,9 @@ void mainwindows_deinit(void)
        command_unbind("window left", (SIGNAL_FUNC) cmd_window_left);
        command_unbind("window right", (SIGNAL_FUNC) cmd_window_right);
        command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick);
+       command_unbind("window move left", (SIGNAL_FUNC) cmd_window_move_left);
+       command_unbind("window move right", (SIGNAL_FUNC) cmd_window_move_right);
+       command_unbind("window move up", (SIGNAL_FUNC) cmd_window_move_up);
+       command_unbind("window move down", (SIGNAL_FUNC) cmd_window_move_down);
+        signal_remove("window print info", (SIGNAL_FUNC) sig_window_print_info);
 }
index 491449c62c9ad61f12e48abcbca53820114080de..1bca333d128d7d9781716e4372482d581a01ef18 100644 (file)
@@ -2,27 +2,34 @@
 #define __MAINWINDOWS_H
 
 #include "fe-windows.h"
-#include "screen.h"
+#include "term.h"
 
 #define WINDOW_MIN_SIZE 2
 
+#define MAIN_WINDOW_TEXT_HEIGHT(window) \
+        ((window)->height-(window)->statusbar_lines)
+
 typedef struct {
        WINDOW_REC *active;
-        GSList *sticky_windows; /* list of windows allowed to show only in this mainwindow */
 
-#ifdef USE_CURSES_WINDOWS
-       WINDOW *curses_win;
-#else
-#error disable-curses-windows is currently broken /* FIXME */
-#endif
-       int first_line, last_line, width, height;
-       int statusbar_lines;
-       void *statusbar;
-       void *statusbar_window_item;
+       TERM_WINDOW *screen_win;
+        int sticky_windows; /* number of sticky windows */
+
+       int first_line, last_line; /* first/last line used by this window (0..x) (includes statusbars) */
+       int width, height; /* width/height of the window (includes statusbars) */
+
+       GSList *statusbars;
+       int statusbar_lines_top;
+       int statusbar_lines_bottom;
+       int statusbar_lines; /* top+bottom */
+
+       unsigned int dirty:1; /* This window needs a redraw */
+       unsigned int size_dirty:1; /* We'll need to resize the window, but haven't got around doing it just yet. */
 } MAIN_WINDOW_REC;
 
 extern GSList *mainwindows;
 extern MAIN_WINDOW_REC *active_mainwin;
+extern int screen_reserved_top, screen_reserved_bottom;
 
 void mainwindows_init(void);
 void mainwindows_deinit(void);
@@ -33,10 +40,21 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window);
 void mainwindows_redraw(void);
 void mainwindows_recreate(void);
 
-void mainwindow_set_size(MAIN_WINDOW_REC *window, int size);
+/* Change the window height - the height includes the lines needed for
+   statusbars. If resize_lower is TRUE, the lower window is first tried
+   to be resized instead of upper window. */
+void mainwindow_set_size(MAIN_WINDOW_REC *window, int height,
+                        int resize_lower);
 void mainwindows_resize(int width, int height);
 
-int mainwindows_reserve_lines(int count, int up);
+void mainwindow_change_active(MAIN_WINDOW_REC *mainwin,
+                             WINDOW_REC *skip_window);
+
+int mainwindows_reserve_lines(int top, int bottom);
+int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window,
+                                  int top, int bottom);
+void mainwindows_redraw_dirty(void);
+
 GSList *mainwindows_get_sorted(int reverse);
 
 #endif
index be3c0eab62d198b2cf44806c08bcc24334379c5a..4a37db25e643f32a9665c878768d75e00ee2db37 100644 (file)
@@ -26,8 +26,10 @@ FORMAT_REC gui_text_formats[] =
        { MODULE_NAME, "Text user interface", 0 },
 
        { "lastlog_too_long", "/LASTLOG would print $0 lines. If you really want to print all these lines use -force option.", 1, { 1 } },
+       { "lastlog_count", "{hilight Lastlog}: $0 lines", 1, { 1 } },
        { "lastlog_start", "{hilight Lastlog}:", 0 },
        { "lastlog_end", "{hilight End of Lastlog}", 0 },
+       { "lastlog_separator", "--", 0 },
 
        { "refnum_not_found", "Window number $0 not found", 1, { 0 } },
        { "window_too_small", "Not enough room to resize this window", 0 },
@@ -37,6 +39,9 @@ FORMAT_REC gui_text_formats[] =
        { "window_not_sticky", "Window is not sticky", 0 },
        { "window_set_sticky", "Window set sticky", 0 },
        { "window_unset_sticky", "Window is not sticky anymore", 0 },
+       { "window_info_sticky", "Sticky  : $0", 1, { 0 } },
+       { "window_scroll", "Window scroll mode is now $0", 1, { 0 } },
+       { "window_scroll_unknown", "Unknown scroll mode $0, must be ON, OFF or DEFAULT", 1, { 0 } },
 
        { NULL, NULL, 0 }
 };
index fe33c8e269e34b6021ab8f7088a49a1048f90cd5..2bbf4007260498402e565e5960ed2fa20ae7d133 100644 (file)
@@ -4,8 +4,10 @@ enum {
        TXT_MODULE_NAME,
 
         TXT_LASTLOG_TOO_LONG,
+        TXT_LASTLOG_COUNT,
        TXT_LASTLOG_START,
        TXT_LASTLOG_END,
+       TXT_LASTLOG_SEPARATOR,
 
         TXT_REFNUM_NOT_FOUND,
         TXT_WINDOW_TOO_SMALL,
@@ -14,7 +16,10 @@ enum {
         TXT_CANT_SHOW_STICKY_WINDOWS,
         TXT_WINDOW_NOT_STICKY,
         TXT_WINDOW_SET_STICKY,
-        TXT_WINDOW_UNSET_STICKY
+       TXT_WINDOW_UNSET_STICKY,
+        TXT_WINDOW_INFO_STICKY,
+        TXT_WINDOW_SCROLL,
+        TXT_WINDOW_SCROLL_UNKNOWN
 };
 
 extern FORMAT_REC gui_text_formats[];
index ba5888adebc69132cd4f8d06d8842dbbf24f9bad..c66435453654b6ca3de44506af85712a6ea1a68e 100644 (file)
@@ -4,3 +4,4 @@
 
 extern int quitting;
 void irssi_redraw(void);
+void irssi_set_dirty(void);
index 5378b5c9e48a7aa5004ea4408da5a7206ccda9cf..3657a23d0fa0a92e04114e6a76d9d7019726b869 100644 (file)
 static int scrx, scry;
 static int use_colors;
 static int freeze_refresh;
+static int resized;
 
 static int init_screen_int(void);
 static void deinit_screen_int(void);
 
 #ifdef SIGWINCH
-
 static void sig_winch(int p)
+{
+       resized = TRUE;
+}
+#endif
+
+static void screen_resize(void)
 {
 #if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
        struct winsize ws;
@@ -74,7 +80,14 @@ static void sig_winch(int p)
 
        mainwindows_resize(COLS, LINES);
 }
-#endif
+
+void screen_check_resizes(void)
+{
+       if (!resized)
+               return;
+       screen_resize();
+       resized = FALSE;
+}
 
 static void read_signals(void)
 {
index c1449bfb1916502300e5e232492cdfa83d0383f5..9e6143fb1804102553c5214ccc7b9471249359d2 100644 (file)
@@ -29,7 +29,9 @@
 #define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE))   /* extended */
 #define is_big5_hi(hi)  (((char)0x81<=hi)&&(hi<=(char)0xFE))
 #define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo))
-#endif WANT_BIG5
+#endif
+
+void screen_check_resizes(void);
 
 int init_screen(void); /* Initialize screen, detect screen length */
 void deinit_screen(void); /* Deinitialize screen */
index 9112b568506c17e3eb93b4e53051975b3c30561a..7e296dccac4edc07a8a6372963a250331611beb4 100644 (file)
 
 #include "module.h"
 #include "module-formats.h"
+#include "modules-load.h"
 #include "args.h"
 #include "signals.h"
 #include "levels.h"
 #include "core.h"
 #include "settings.h"
+#include "session.h"
 
 #include "printtext.h"
 #include "fe-common-core.h"
 #include "fe-common-silc.h"
 #include "themes.h"
 
-#include "screen.h"
+#include "term.h"
 #include "gui-entry.h"
 #include "mainwindows.h"
 #include "gui-printtext.h"
 #include "gui-readline.h"
 #include "statusbar.h"
 #include "gui-windows.h"
+#include "textbuffer-reformat.h"
 
 #include <signal.h>
 
@@ -64,8 +67,13 @@ void lastlog_deinit(void);
 void mainwindow_activity_init(void);
 void mainwindow_activity_deinit(void);
 
-void mainwindows_save_init(void);
-void mainwindows_save_deinit(void);
+void mainwindows_layout_init(void);
+void mainwindows_layout_deinit(void);
+
+void term_dummy_init(void);
+void term_dummy_deinit(void);
+
+static int dirty, full_redraw, dummy;
 
 static GMainLoop *main_loop;
 int quitting;
@@ -83,31 +91,57 @@ static int display_firsttimer = FALSE;
 
 static void sig_exit(void)
 {
-       g_main_quit(main_loop);
+        quitting = TRUE;
 }
 
 /* redraw irssi's screen.. */
 void irssi_redraw(void)
 {
-       clear();
-       refresh();
-
-       /* windows */
-        mainwindows_redraw();
-       /* statusbar */
-       statusbar_redraw(NULL);
-       /* entry line */
-       gui_entry_redraw();
+       dirty = TRUE;
+        full_redraw = TRUE;
+}
+
+void irssi_set_dirty(void)
+{
+        dirty = TRUE;
+}
+
+static void dirty_check(void)
+{
+       if (!dirty || dummy)
+               return;
+
+        term_resize_dirty();
+
+       if (full_redraw) {
+                full_redraw = FALSE;
+
+               /* first clear the screen so curses will be
+                  forced to redraw the screen */
+               term_clear();
+               term_refresh(NULL);
+
+               mainwindows_redraw();
+               statusbar_redraw(NULL, TRUE);
+       }
+
+       mainwindows_redraw_dirty();
+        statusbar_redraw_dirty();
+       term_refresh(NULL);
+
+        dirty = FALSE;
 }
 
 static void textui_init(void)
 {
-       static struct poptOption options[] = {
-               POPT_AUTOHELP
-               { NULL, '\0', 0, NULL }
-       };
+#ifdef SIGTRAP
+       struct sigaction act;
 
-       args_register(options);
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = SIG_IGN;
+       sigaction(SIGTRAP, &act, NULL);
+#endif
 
        irssi_gui = IRSSI_GUI_TEXT;
        core_init();
@@ -116,79 +150,91 @@ static void textui_init(void)
        fe_common_silc_init();
 
        theme_register(gui_text_formats);
-       signal_add("gui exit", (SIGNAL_FUNC) sig_exit);
+       signal_add_last("gui exit", (SIGNAL_FUNC) sig_exit);
 }
 
 static void textui_finish_init(void)
 {
        quitting = FALSE;
 
-       screen_refresh_freeze();
-        textbuffer_init();
-        textbuffer_view_init();
-       textbuffer_commands_init();
-       gui_entry_init();
-       gui_expandos_init();
-       gui_printtext_init();
-       gui_readline_init();
-        lastlog_init();
-       mainwindows_init();
-       mainwindow_activity_init();
-       mainwindows_save_init();
-       gui_windows_init();
-       statusbar_init();
+       if (dummy)
+               term_dummy_init();
+       else {
+               term_refresh_freeze();
+               textbuffer_init();
+               textbuffer_view_init();
+               textbuffer_commands_init();
+               textbuffer_reformat_init();
+               gui_expandos_init();
+               gui_printtext_init();
+               gui_readline_init();
+               lastlog_init();
+               mainwindows_init();
+               mainwindow_activity_init();
+               mainwindows_layout_init();
+               gui_windows_init();
+               statusbar_init();
+               term_refresh_thaw();
+       }
 
        settings_check();
-
-       fe_common_core_finish_init();
+       module_register("core", "fe-text");
 
 #ifdef HAVE_STATIC_PERL
        perl_core_init();
-        fe_perl_init();
+       fe_perl_init();
 #endif
+
+       dirty_check();
+
+       fe_common_core_finish_init();
        signal_emit("irssi init finished", 0);
 
+#if 0
        if (display_firsttimer) {
                printtext_window(active_win, MSGLEVEL_CLIENTNOTICE,
                                 "%s", firsttimer_text);
        }
-
-       screen_refresh_thaw();
+#endif
 }
 
 static void textui_deinit(void)
 {
-       quitting = TRUE;
        signal(SIGINT, SIG_DFL);
 
-        screen_refresh_freeze();
+        term_refresh_freeze();
        while (modules != NULL)
                module_unload(modules->data);
 
-       signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
-
-        lastlog_deinit();
-       statusbar_deinit();
-       gui_printtext_deinit();
-       gui_readline_deinit();
-       gui_windows_deinit();
-       mainwindows_save_deinit();
-       mainwindow_activity_deinit();
-       mainwindows_deinit();
-       gui_expandos_deinit();
-       gui_entry_deinit();
-       textbuffer_commands_deinit();
-        textbuffer_view_deinit();
-        textbuffer_deinit();
-
-        screen_refresh_thaw();
-       deinit_screen();
-
 #ifdef HAVE_STATIC_PERL
-        fe_perl_deinit();
         perl_core_deinit();
+        fe_perl_deinit();
 #endif
 
+        dirty_check(); /* one last time to print any quit messages */
+       signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
+
+       if (dummy)
+               term_dummy_deinit();
+       else {
+               lastlog_deinit();
+               statusbar_deinit();
+               gui_printtext_deinit();
+               gui_readline_deinit();
+               gui_windows_deinit();
+               mainwindows_layout_deinit();
+               mainwindow_activity_deinit();
+               mainwindows_deinit();
+               gui_expandos_deinit();
+               textbuffer_reformat_deinit();
+               textbuffer_commands_deinit();
+               textbuffer_view_deinit();
+               textbuffer_deinit();
+
+               term_refresh_thaw();
+               term_deinit();
+       }
+
        theme_unregister();
 
        fe_common_silc_deinit();
@@ -204,7 +250,7 @@ static void check_oldcrap(void)
         int found;
 
         /* check that default.theme is up-to-date */
-       path = g_strdup_printf("%s/.irssi/default.theme", g_get_home_dir());
+       path = g_strdup_printf("%s/default.theme", get_irssi_dir());
        f = fopen(path, "r+");
        if (f == NULL) {
                g_free(path);
@@ -220,7 +266,7 @@ static void check_oldcrap(void)
                return;
        }
 
-       printf("\nYou seem to have old default.theme in ~/.irssi/ directory.\n");
+       printf("\nYou seem to have old default.theme in "IRSSI_DIR_SHORT"/ directory.\n");
         printf("Themeing system has changed a bit since last irssi release,\n");
         printf("you should either delete your old default.theme or manually\n");
         printf("merge it with the new default.theme.\n\n");
@@ -228,7 +274,7 @@ static void check_oldcrap(void)
 
        str[0] = '\0';
        fgets(str, sizeof(str), stdin);
-       if (toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0')
+       if (i_toupper(str[0]) == 'Y' || str[0] == '\n' || str[0] == '\0')
                 remove(path);
        g_free(path);
 }
@@ -236,16 +282,13 @@ static void check_oldcrap(void)
 static void check_files(void)
 {
        struct stat statbuf;
-        char *path;
 
-        path = g_strdup_printf("%s/.irssi", g_get_home_dir());
-       if (stat(path, &statbuf) != 0) {
+       if (stat(get_irssi_dir(), &statbuf) != 0) {
                /* ~/.irssi doesn't exist, first time running irssi */
                display_firsttimer = TRUE;
        } else {
                 check_oldcrap();
        }
-        g_free(path);
 }
 
 #ifdef WIN32
@@ -265,7 +308,17 @@ static void winsock_init(void)
 
 int main(int argc, char **argv)
 {
+       static struct poptOption options[] = {
+               { "dummy", 'd', POPT_ARG_NONE, &dummy, 0, "Use the dummy terminal mode", NULL },
+               { NULL, '\0', 0, NULL }
+       };
+
+       dummy = FALSE;
+       quitting = FALSE;
+       core_init_paths(argc, argv);
+
        check_files();
+
 #ifdef WIN32
         winsock_init();
 #endif
@@ -279,20 +332,32 @@ int main(int argc, char **argv)
 #endif
 
        textui_init();
+       args_register(options);
        args_execute(argc, argv);
 
-       if (!init_screen())
-               g_error("Can't initialize screen handling, quitting.\n");
+#if 0
+       silc_init_finish();
+#endif
+
+       if (!dummy && !term_init()) {
+               fprintf(stderr, "Can't initialize screen handling, quitting.\n");
+               fprintf(stderr, "You can still use the dummy mode with -d parameter\n");
+               return 1;
+       }
 
        textui_finish_init();
        main_loop = g_main_new(TRUE);
-       g_main_run(main_loop);
+
+       /* Does the same as g_main_run(main_loop), except we
+          can call our dirty-checker after each iteration */
+       while (!quitting) {
+               g_main_iteration(TRUE);
+                dirty_check();
+       }
+
        g_main_destroy(main_loop);
        textui_deinit();
 
-#ifdef MEM_DEBUG
-       ig_mem_profile();
-#endif
-
+        session_upgrade(); /* if we /UPGRADEd, start the new process */
        return 0;
 }
diff --git a/apps/irssi/src/fe-text/statusbar-config.c b/apps/irssi/src/fe-text/statusbar-config.c
new file mode 100644 (file)
index 0000000..f9ea05f
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ statusbar-config.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "settings.h"
+#include "lib-config/iconfig.h"
+
+#include "statusbar.h"
+
+static void read_statusbar_config_from_node(CONFIG_NODE *node);
+
+static STATUSBAR_CONFIG_REC *
+statusbar_config_create(STATUSBAR_GROUP_REC *group, const char *name)
+{
+       STATUSBAR_CONFIG_REC *bar;
+
+        g_return_val_if_fail(group != NULL, NULL);
+        g_return_val_if_fail(name != NULL, NULL);
+
+       bar = g_new0(STATUSBAR_CONFIG_REC, 1);
+       group->config_bars = g_slist_append(group->config_bars, bar);
+
+       bar->name = g_strdup(name);
+       return bar;
+}
+
+static SBAR_ITEM_CONFIG_REC *
+statusbar_item_config_create(STATUSBAR_CONFIG_REC *bar, const char *name,
+                            int priority, int right_alignment)
+{
+       SBAR_ITEM_CONFIG_REC *rec;
+
+       g_return_val_if_fail(bar != NULL, NULL);
+       g_return_val_if_fail(name != NULL, NULL);
+
+       rec = g_new0(SBAR_ITEM_CONFIG_REC, 1);
+       bar->items = g_slist_append(bar->items, rec);
+
+        rec->name = g_strdup(name);
+       rec->priority = priority;
+        rec->right_alignment = right_alignment;
+
+       return rec;
+}
+
+static void statusbar_config_item_destroy(STATUSBAR_CONFIG_REC *barconfig,
+                                         SBAR_ITEM_CONFIG_REC *itemconfig)
+{
+       barconfig->items = g_slist_remove(barconfig->items, itemconfig);
+
+       g_free(itemconfig->name);
+        g_free(itemconfig);
+}
+
+void statusbar_config_destroy(STATUSBAR_GROUP_REC *group,
+                             STATUSBAR_CONFIG_REC *config)
+{
+       group->config_bars = g_slist_remove(group->config_bars, config);
+
+       while (config->items != NULL)
+               statusbar_config_item_destroy(config, config->items->data);
+
+       g_free(config->name);
+        g_free(config);
+}
+
+static STATUSBAR_CONFIG_REC *
+statusbar_config_find(STATUSBAR_GROUP_REC *group, const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_CONFIG_REC *config = tmp->data;
+
+               if (strcmp(config->name, name) == 0)
+                        return config;
+       }
+
+        return NULL;
+}
+
+static void statusbar_reset_defaults(void)
+{
+       CONFIG_REC *config;
+        CONFIG_NODE *node;
+
+       while (statusbar_groups != NULL)
+               statusbar_group_destroy(statusbar_groups->data);
+       active_statusbar_group = NULL;
+
+        /* read the default statusbar settings from internal config */
+       config = config_open(NULL, -1);
+       config_parse_data(config, default_config, "internal");
+       node = config_node_traverse(config, "statusbar", FALSE);
+        if (node != NULL)
+               read_statusbar_config_from_node(node);
+        config_close(config);
+}
+
+static void statusbar_read_items(CONFIG_NODE *items)
+{
+       GSList *tmp;
+
+       tmp = config_node_first(items->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               CONFIG_NODE *node = tmp->data;
+
+               statusbar_item_register(node->key, node->value, NULL);
+       }
+}
+
+static void statusbar_read_item(STATUSBAR_CONFIG_REC *bar, CONFIG_NODE *node)
+{
+       int priority, right_alignment;
+
+       priority = config_node_get_int(node, "priority", 0);
+       right_alignment = strcmp(config_node_get_str(node, "alignment", ""), "right") == 0;
+       statusbar_item_config_create(bar, node->key,
+                                    priority, right_alignment);
+}
+
+static void statusbar_read(STATUSBAR_GROUP_REC *group, CONFIG_NODE *node)
+{
+       STATUSBAR_CONFIG_REC *bar;
+        GSList *tmp;
+       int visible;
+        const char *visible_str;
+
+       bar = statusbar_config_find(group, node->key);
+       visible = bar == NULL ? STATUSBAR_VISIBLE_ALWAYS : bar->visible;
+
+        /* first make sure we want to see this statusbar */
+        visible_str = config_node_get_str(node, "visible", "");
+       if (strcmp(visible_str, "active") == 0)
+                visible = STATUSBAR_VISIBLE_ACTIVE;
+       else if (strcmp(visible_str, "inactive") == 0)
+               visible = STATUSBAR_VISIBLE_INACTIVE;
+       else if (strcmp(visible_str, "never") == 0) {
+               /* we don't want this statusbar -
+                  destroy it if it already exists */
+               if (bar != NULL)
+                       statusbar_config_destroy(group, bar);
+                return;
+       }
+
+       if (bar == NULL) {
+               bar = statusbar_config_create(group, node->key);
+               bar->type = STATUSBAR_TYPE_ROOT;
+               bar->placement = STATUSBAR_BOTTOM;
+               bar->position = 0;
+       }
+       bar->visible = visible;
+
+       if (strcmp(config_node_get_str(node, "type", ""), "window") == 0)
+                bar->type = STATUSBAR_TYPE_WINDOW;
+       if (strcmp(config_node_get_str(node, "placement", ""), "top") == 0)
+                bar->placement = STATUSBAR_TOP;
+       bar->position = config_node_get_int(node, "position", 0);
+
+       node = config_node_section(node, "items", -1);
+       if (node != NULL) {
+                /* we're overriding the items - destroy the old */
+                while (bar->items != NULL)
+                       statusbar_config_item_destroy(bar, bar->items->data);
+
+               tmp = config_node_first(node->value);
+               for (; tmp != NULL; tmp = config_node_next(tmp))
+                       statusbar_read_item(bar, tmp->data);
+       }
+}
+
+static void statusbar_read_group(CONFIG_NODE *node)
+{
+       STATUSBAR_GROUP_REC *group;
+       GSList *tmp;
+
+       group = statusbar_group_find(node->key);
+       if (group == NULL) {
+               group = statusbar_group_create(node->key);
+               if (active_statusbar_group == NULL)
+                       active_statusbar_group = group;
+       }
+
+        tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp))
+               statusbar_read(group, tmp->data);
+}
+
+static void create_root_statusbars(void)
+{
+        STATUSBAR_REC *bar;
+       GSList *tmp;
+
+        tmp = active_statusbar_group->config_bars;
+       for (; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_CONFIG_REC *rec = tmp->data;
+
+               if (rec->type == STATUSBAR_TYPE_ROOT) {
+                       bar = statusbar_create(active_statusbar_group, rec, NULL);
+                        statusbar_redraw(bar, TRUE);
+               }
+       }
+}
+
+static void read_statusbar_config_from_node(CONFIG_NODE *node)
+{
+       CONFIG_NODE *items;
+       GSList *tmp;
+
+       items = config_node_section(node, "items", -1);
+       if (items != NULL)
+               statusbar_read_items(items);
+
+        tmp = config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               if (tmp->data != items)
+                       statusbar_read_group(tmp->data);
+       }
+}
+
+static void read_statusbar_config(void)
+{
+       CONFIG_NODE *node;
+
+        statusbar_reset_defaults();
+
+       node = iconfig_node_traverse("statusbar", FALSE);
+       if (node != NULL)
+               read_statusbar_config_from_node(node);
+
+        create_root_statusbars();
+}
+
+void statusbar_config_init(void)
+{
+        read_statusbar_config();
+       signal_add("setup reread", (SIGNAL_FUNC) read_statusbar_config);
+}
+
+void statusbar_config_deinit(void)
+{
+       signal_remove("setup reread", (SIGNAL_FUNC) read_statusbar_config);
+}
diff --git a/apps/irssi/src/fe-text/statusbar-config.h b/apps/irssi/src/fe-text/statusbar-config.h
new file mode 100644 (file)
index 0000000..a2a0b04
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __STATUSBAR_CONFIG_H
+#define __STATUSBAR_CONFIG_H
+
+#include "statusbar.h"
+
+void statusbar_config_destroy(STATUSBAR_GROUP_REC *group,
+                             STATUSBAR_CONFIG_REC *config);
+
+void statusbar_config_init(void);
+void statusbar_config_deinit(void);
+
+#endif
index 137e7417069ad739ff33c529b97a8b29d1645326..ac98c77a8bc46ce29d10ab47d70f342218a8e99f 100644 (file)
 
 #include "module.h"
 #include "signals.h"
-#include "misc.h"
 #include "settings.h"
-#include "special-vars.h"
-
-#include "window-items.h"
-#include "formats.h"
+#include "servers.h"
 
+#include "themes.h"
 #include "statusbar.h"
-#include "gui-printtext.h"
+#include "gui-entry.h"
+#include "gui-windows.h"
 
 /* how often to redraw lagging time (seconds) */
 #define LAG_REFRESH_TIME 10
 
-/* how often to check for new mail (seconds) */
-#define MAIL_REFRESH_TIME 60
-
-/* If we haven't been able to check lag for this long, "(??)" is added after
-   the lag */
-#define MAX_LAG_UNKNOWN_TIME 30
-
-static STATUSBAR_REC *mainbar;
-static MAIN_WINDOW_REC *mainbar_window;
-static int use_colors;
-
-/* clock */
-static SBAR_ITEM_REC *clock_item;
-static int clock_timetag;
-static time_t clock_last;
-
-/* nick */
-static SBAR_ITEM_REC *nick_item;
-
-/* channel */
-static SBAR_ITEM_REC *window_item;
-
-/* activity */
-static SBAR_ITEM_REC *activity_item;
 static GList *activity_list;
+static GSList *more_visible; /* list of MAIN_WINDOW_RECs which have --more-- */
+static GHashTable *input_entries;
+static int last_lag, last_lag_unknown, lag_timeout_tag;
 
-/* more */
-static SBAR_ITEM_REC *more_item;
-
-/* lag */
-static SBAR_ITEM_REC *lag_item;
-static int lag_timetag, lag_min_show;
-static time_t lag_last_draw;
-
-/* mbox counter */
-static SBAR_ITEM_REC *mail_item;
-static int mail_timetag, mail_last_count;
-static time_t mail_last_mtime = -1;
-static off_t mail_last_size = -1;
-
-/* topic */
-static SBAR_ITEM_REC *topic_item;
-static STATUSBAR_REC *topic_bar;
-
-static void item_default(SBAR_ITEM_REC *item, int get_size_only,
-                        const char *str, const char *data)
-{
-       SERVER_REC *server;
-       WI_ITEM_REC *wiitem;
-        char *tmpstr, *tmpstr2;
-       int len;
-
-       if (active_win == NULL) {
-               server = NULL;
-                wiitem = NULL;
-       } else {
-               server = active_win->active_server;
-                wiitem = active_win->active;
-       }
-
-       /* expand $variables */
-       tmpstr = parse_special_string(str, server, wiitem, data, NULL,
-                                     PARSE_FLAG_ESCAPE_VARS);
-
-       /* expand templates */
-        str = tmpstr;
-       tmpstr2 = theme_format_expand_data(current_theme, &str,
-                                          'n', '0' + item->bar->color,
-                                          NULL, NULL,
-                                          EXPAND_FLAG_ROOT |
-                                          EXPAND_FLAG_IGNORE_REPLACES |
-                                          EXPAND_FLAG_IGNORE_EMPTY);
-       g_free(tmpstr);
-
-       /* remove color codes */
-       tmpstr = strip_codes(tmpstr2);
-        g_free(tmpstr2);
-
-       if (get_size_only) {
-               item->min_size = item->max_size = format_get_length(tmpstr);
-       } else {
-               if (item->size < item->min_size) {
-                        /* they're forcing us smaller than minimum size.. */
-                       len = format_real_length(tmpstr, item->size);
-                        tmpstr[len] = '\0';
-               }
-
-               tmpstr2 = g_strconcat(item->bar->color_string, tmpstr, NULL);
-               gui_printtext(item->xpos, item->bar->ypos, tmpstr2);
-                g_free(tmpstr2);
-       }
-       g_free(tmpstr);
-}
-
-/* redraw clock */
-static void statusbar_clock(SBAR_ITEM_REC *item, int get_size_only)
+static void item_window_active(SBAR_ITEM_REC *item, int get_size_only)
 {
-       item_default(item, get_size_only, "{sb $Z}", "");
-}
-
-/* check if we need to redraw clock.. */
-static int statusbar_clock_timeout(void)
-{
-       struct tm *tm;
-       time_t t;
-       int min;
-
-       tm = localtime(&clock_last);
-       min = tm->tm_min;
+       WINDOW_REC *window;
 
-       t = time(NULL);
-       tm = localtime(&t);
+        window = active_win;
+       if (item->bar->parent_window != NULL)
+               window = item->bar->parent_window->active;
 
-       if (tm->tm_min != min) {
-               /* minute changed, redraw! */
-                clock_last = t;
-               statusbar_item_redraw(clock_item);
+       if (window != NULL && window->active != NULL) {
+               statusbar_item_default_handler(item, get_size_only,
+                                              NULL, "", TRUE);
+       } else if (get_size_only) {
+                item->min_size = item->max_size = 0;
        }
-       return 1;
 }
 
-/* redraw nick */
-static void statusbar_nick(SBAR_ITEM_REC *item, int get_size_only)
+static void item_window_empty(SBAR_ITEM_REC *item, int get_size_only)
 {
-       item_default(item, get_size_only,
-                    "{sb $P$N{sbmode $usermode}{sbaway $A}}", "");
-}
+       WINDOW_REC *window;
 
-static void sig_statusbar_nick_redraw(void)
-{
-       statusbar_item_redraw(nick_item);
-}
+        window = active_win;
+       if (item->bar->parent_window != NULL)
+               window = item->bar->parent_window->active;
 
-/* redraw window */
-static void statusbar_window(SBAR_ITEM_REC *item, int get_size_only)
-{
-       if (active_win->active != NULL) {
-               item_default(item, get_size_only,
-                            "{sb $winref:$T{sbmode $M}}", "");
-       } else {
-               item_default(item, get_size_only,
-                            "{sb $winref{sbservertag $tag}}", "");
+       if (window != NULL && window->active == NULL) {
+               statusbar_item_default_handler(item, get_size_only,
+                                              NULL, "", TRUE);
+       } else if (get_size_only) {
+                item->min_size = item->max_size = 0;
        }
 }
 
-static void sig_statusbar_window_redraw(void)
-{
-       statusbar_item_redraw(window_item);
-}
-
-static void sig_statusbar_window_redraw_window(WINDOW_REC *window)
-{
-       if (is_window_visible(window))
-               statusbar_item_redraw(window_item);
-}
-
-static void sig_statusbar_window_redraw_window_item(WI_ITEM_REC *item)
-{
-       WINDOW_REC *window;
-
-        window = window_item_window(item);
-       if (window->active == item && is_window_visible(window))
-               statusbar_item_redraw(window_item);
-}
-
-static char *get_activity_list(int normal, int hilight)
+static char *get_activity_list(MAIN_WINDOW_REC *window, int normal, int hilight)
 {
+        THEME_REC *theme;
        GString *str;
        GList *tmp;
-        char *ret;
+        char *ret, *name, *format, *value;
         int is_det;
 
        str = g_string_new(NULL);
 
+       theme = window != NULL && window->active != NULL &&
+               window->active->theme != NULL ?
+               window->active->theme : current_theme;
+
        for (tmp = activity_list; tmp != NULL; tmp = tmp->next) {
                WINDOW_REC *window = tmp->data;
 
@@ -214,26 +89,41 @@ static char *get_activity_list(int normal, int hilight)
                if ((!is_det && !normal) || (is_det && !hilight))
                         continue;
 
-               g_string_append(str, "%c");
-                if (str->len > 2)
-                       g_string_append_c(str, ',');
+                /* comma separator */
+               if (str->len > 0) {
+                       value = theme_format_expand(theme, "{sb_act_sep ,}");
+                       g_string_append(str, value);
+                       g_free(value);
+               }
 
                switch (window->data_level) {
                case DATA_LEVEL_NONE:
                case DATA_LEVEL_TEXT:
+                       name = "{sb_act_text %d}";
                        break;
                case DATA_LEVEL_MSG:
-                        g_string_append(str, "%W");
+                       name = "{sb_act_msg %d}";
                        break;
                default:
-                       g_string_append(str, window->hilight_color == NULL ?
-                                       "%M" : window->hilight_color);
+                        if (window->hilight_color == NULL)
+                               name = "{sb_act_hilight %d}";
+                       else
+                                name = NULL;
                        break;
                }
-               g_string_sprintfa(str, "%d", window->refnum);
 
-                /* make sure the background is returned to default */
-               g_string_append(str, "%n");
+               if (name != NULL)
+                       format = g_strdup_printf(name, window->refnum);
+               else
+                       format = g_strdup_printf("{sb_act_hilight_color %s %d}",
+                                                window->hilight_color,
+                                                window->refnum);
+
+               value = theme_format_expand(theme, format);
+               g_string_append(str, value);
+                g_free(value);
+
+               g_free(format);
        }
 
        ret = str->len == 0 ? NULL : str->str;
@@ -244,31 +134,21 @@ static char *get_activity_list(int normal, int hilight)
 /* redraw activity, FIXME: if we didn't get enough size, this gets buggy.
    At least "Det:" isn't printed properly. also we should rearrange the
    act list so that the highest priority items comes first. */
-static void statusbar_activity(SBAR_ITEM_REC *item, int get_size_only)
+static void item_act(SBAR_ITEM_REC *item, int get_size_only)
 {
-       char *actlist, *detlist, *data;
+       char *actlist;
 
-       if (use_colors) {
-               actlist = get_activity_list(TRUE, TRUE);
-                detlist = NULL;
-       } else {
-                actlist = get_activity_list(TRUE, FALSE);
-                detlist = get_activity_list(FALSE, TRUE);
-       }
-
-       if (actlist == NULL && detlist == NULL) {
+       actlist = get_activity_list(item->bar->parent_window, TRUE, TRUE);
+       if (actlist == NULL) {
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
-       data = g_strconcat("{sbact ", actlist != NULL ? actlist : "",
-                          " ", detlist != NULL ? detlist : "", "}", NULL);
-       item_default(item, get_size_only, data, "");
-        g_free(data);
+       statusbar_item_default_handler(item, get_size_only,
+                                      NULL, actlist, FALSE);
 
        g_free_not_null(actlist);
-        g_free_not_null(detlist);
 }
 
 static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel)
@@ -284,7 +164,7 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel
                        activity_list = g_list_remove(activity_list, window);
                if (window->data_level != 0)
                        activity_list = g_list_prepend(activity_list, window);
-               statusbar_item_redraw(activity_item);
+               statusbar_items_redraw("act");
                return;
        }
 
@@ -293,12 +173,12 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel
                if (window->data_level == 0) {
                        /* remove from activity list */
                        activity_list = g_list_remove(activity_list, window);
-                       statusbar_item_redraw(activity_item);
+                       statusbar_items_redraw("act");
                } else if (window->data_level != GPOINTER_TO_INT(oldlevel) ||
                         window->hilight_color != 0) {
                        /* different level as last time (or maybe different
                           hilight color?), just redraw it. */
-                       statusbar_item_redraw(activity_item);
+                       statusbar_items_redraw("act");
                }
                return;
        }
@@ -320,7 +200,7 @@ static void sig_statusbar_activity_hilight(WINDOW_REC *window, gpointer oldlevel
        if (tmp == NULL)
                activity_list = g_list_append(activity_list, window);
 
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window)
@@ -329,437 +209,250 @@ static void sig_statusbar_activity_window_destroyed(WINDOW_REC *window)
 
        if (g_list_find(activity_list, window) != NULL)
                activity_list = g_list_remove(activity_list, window);
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
 static void sig_statusbar_activity_updated(void)
 {
-       statusbar_item_redraw(activity_item);
+       statusbar_items_redraw("act");
 }
 
-/* redraw -- more -- */
-static void statusbar_more(SBAR_ITEM_REC *item, int get_size_only)
+static void item_more(SBAR_ITEM_REC *item, int get_size_only)
 {
-       item_default(item, get_size_only, "{sbmore}", "");
-}
-
-static void sig_statusbar_more_check_remove(WINDOW_REC *window)
-{
-       g_return_if_fail(window != NULL);
-
-       if (!is_window_visible(window))
-               return;
+        MAIN_WINDOW_REC *mainwin;
+       int visible;
 
-       if (more_item != NULL && WINDOW_GUI(window)->view->bottom) {
-               statusbar_item_remove(more_item);
-               more_item = NULL;
+       if (active_win == NULL) {
+                mainwin = NULL;
+               visible = FALSE;
+       } else {
+               mainwin = WINDOW_MAIN(active_win);
+               visible = WINDOW_GUI(active_win)->view->more_text;
        }
-}
 
-static void sig_statusbar_more_check(WINDOW_REC *window)
-{
-       if (window == NULL || !is_window_visible(window))
-               return;
-
-       if (!WINDOW_GUI(window)->view->bottom) {
-               if (more_item == NULL) {
-                       more_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_more);
-                       statusbar_redraw(mainbar);
-               }
-       } else if (more_item != NULL) {
-               statusbar_item_remove(more_item);
-               more_item = NULL;
-       }
-}
-
-static void statusbar_lag(SBAR_ITEM_REC *item, int get_size_only)
-{
-       SERVER_REC *server;
-       GString *str;
-       int lag_unknown;
-       time_t now;
-
-       server = active_win == NULL ? NULL : active_win->active_server;
-       if (server == NULL || server->lag_last_check == 0) {
-                /* No lag information */
+       if (!visible) {
+               if (mainwin != NULL)
+                       more_visible = g_slist_remove(more_visible, mainwin);
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
-       now = time(NULL);
-       str = g_string_new(NULL);
-
-       /* FIXME: ugly ugly.. */
-       if (server->lag_sent == 0 || now-server->lag_sent < 5) {
-               lag_unknown = now-server->lag_last_check >
-                       MAX_LAG_UNKNOWN_TIME+settings_get_int("lag_check_time");
-
-               if (lag_min_show < 0 || (server->lag < lag_min_show && !lag_unknown)) {
-                        /* small lag, don't display */
-               } else {
-                       g_string_sprintfa(str, "%d.%02d", server->lag/1000,
-                                         (server->lag % 1000)/10);
-                       if (lag_unknown)
-                               g_string_append(str, " (??)");
-               }
-       } else {
-               /* big lag, still waiting .. */
-               g_string_sprintfa(str, "%ld (??)",
-                                 (long) (now-server->lag_sent));
-       }
-
-       item_default(item, get_size_only, "{sblag $0-}", str->str);
-
-       g_string_free(str, TRUE);
+       more_visible = g_slist_prepend(more_visible, mainwin);
+       statusbar_item_default_handler(item, get_size_only, NULL, "", FALSE);
 }
 
-static void sig_statusbar_lag_redraw(void)
+static void sig_statusbar_more_updated(void)
 {
-       statusbar_item_redraw(lag_item);
+       int visible;
+
+        visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL;
+       if (WINDOW_GUI(active_win)->view->more_text != visible)
+                statusbar_items_redraw("more");
 }
 
-static int statusbar_lag_timeout(void)
+/* Returns the lag in milliseconds. If we haven't been able to ask the lag
+   for a while, unknown is set to TRUE. */
+static int get_lag(SERVER_REC *server, int *unknown)
 {
-       /* refresh statusbar every 10 seconds */
-       if (time(NULL)-lag_last_draw < LAG_REFRESH_TIME)
-               return 1;
+       long lag;
 
-       statusbar_item_redraw(lag_item);
-       return 1;
-}
+        *unknown = FALSE;
 
-/* FIXME: this isn't very good.. it handles only mbox mailboxes.
-   this whole mail feature should really be in it's own module with lots
-   of other mail formats supported and people who don't want to use it
-   wouldn't need to.. */
-static int get_mail_count(void)
-{
-       struct stat statbuf;
-       FILE *f;
-       char str[512], *fname;
-       int count;
-
-       fname = g_getenv("MAIL");
-       if (fname == NULL) return 0;
-
-       if (stat(fname, &statbuf) != 0) {
-               mail_last_mtime = -1;
-               mail_last_size = -1;
-                mail_last_count = 0;
+       if (server == NULL || server->lag_last_check == 0) {
+                /* lag has not been asked even once yet */
                return 0;
        }
 
-       if (statbuf.st_mtime == mail_last_mtime &&
-           statbuf.st_size == mail_last_size)
-               return mail_last_count;
-       mail_last_mtime = statbuf.st_mtime;
-       mail_last_size = statbuf.st_size;
-
-       f = fopen(fname, "r");
-       if (f == NULL) {
-                mail_last_count = 0;
-               return 0;
+       if (server->lag_sent.tv_sec == 0) {
+               /* no lag queries going on currently */
+                return server->lag;
        }
 
-       count = 0;
-       while (fgets(str, sizeof(str), f) != NULL) {
-               if (strncmp(str, "From ", 5) == 0)
-                       count++;
-               if (strncmp(str, "Subject: ", 9) == 0 &&
-                   strstr(str, "FOLDER INTERNAL DATA")) {
-                       /* don't count these. */
-                       count--;
-               }
+        /* we're not sure about our current lag.. */
+       *unknown = TRUE;
+
+        lag = (long) (time(NULL)-server->lag_sent.tv_sec);
+       if (server->lag/1000 > lag) {
+               /* we've been waiting the lag reply less time than
+                  what last known lag was -> use the last known lag */
+               return server->lag;
        }
 
-       fclose(f);
-       mail_last_count = count;
-       return count;
+        /* return how long we have been waiting for lag reply */
+        return lag*1000;
 }
 
-static void statusbar_mail(SBAR_ITEM_REC *item, int get_size_only)
+static void item_lag(SBAR_ITEM_REC *item, int get_size_only)
 {
-       char countstr[MAX_INT_STRLEN];
-       int mail_count;
+       SERVER_REC *server;
+        char str[MAX_INT_STRLEN+10];
+       int lag, lag_unknown;
 
-       mail_count = settings_get_bool("mail_counter") ? get_mail_count() : 0;
+       server = active_win == NULL ? NULL : active_win->active_server;
+       lag = get_lag(server, &lag_unknown)/10;
 
-       if (mail_count <= 0) {
+       if (lag <= 0 || lag < settings_get_int("lag_min_show")) {
+               /* don't print the lag item */
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
-       ltoa(countstr, mail_count);
-       item_default(item, get_size_only, "{sbmail $0-}", countstr);
-}
-
-static int statusbar_mail_timeout(void)
-{
-       statusbar_item_redraw(mail_item);
-       return 1;
-}
-
-static void statusbar_topic(SBAR_ITEM_REC *item, int get_size_only)
-{
-        item_default(item, get_size_only, "$topic", "");
-}
-
-static void sig_statusbar_topic_redraw(void)
-{
-       if (topic_item != NULL) statusbar_item_redraw(topic_item);
-}
-
-static void sig_sidebars_redraw(void)
-{
-       GSList *tmp;
+       last_lag = lag;
+       last_lag_unknown = lag_unknown;
 
-       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
-               MAIN_WINDOW_REC *rec = tmp->data;
-
-               if (rec->statusbar_window_item != NULL)
-                        statusbar_item_redraw(rec->statusbar_window_item);
+       if (lag_unknown) {
+               g_snprintf(str, sizeof(str), "%d (?""?)", lag/100);
+       } else {
+               g_snprintf(str, sizeof(str),
+                          lag%100 == 0 ? "%d" : "%d.%02d", lag/100, lag%100);
        }
-}
 
-static void topicbar_create(void)
-{
-       if (topic_bar != NULL)
-               return;
-
-       topic_bar = statusbar_create(STATUSBAR_POS_UP, 0);
-       topic_item = statusbar_item_create(topic_bar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_topic);
-       topic_item->max_size = TRUE;
-       statusbar_redraw(topic_bar);
-
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_add("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_add("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
+       statusbar_item_default_handler(item, get_size_only,
+                                      NULL, str, TRUE);
 }
 
-static void topicbar_destroy(void)
+static void lag_check_update(void)
 {
-       if (topic_bar == NULL)
-               return;
+       SERVER_REC *server;
+       int lag, lag_unknown;
 
-       statusbar_destroy(topic_bar);
-       topic_item = NULL;
-       topic_bar = NULL;
+       server = active_win == NULL ? NULL : active_win->active_server;
+       lag = get_lag(server, &lag_unknown)/10;
 
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_remove("channel topic changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-       signal_remove("query address changed", (SIGNAL_FUNC) sig_statusbar_topic_redraw);
-}
+       if (lag < settings_get_int("lag_min_show"))
+                lag = 0;
 
-static void mainbar_remove_items(void)
-{
-        statusbar_item_remove(clock_item);
-        statusbar_item_remove(nick_item);
-        statusbar_item_remove(window_item);
-       statusbar_item_remove(mail_item);
-       statusbar_item_remove(lag_item);
-        statusbar_item_remove(activity_item);
+       if (lag != last_lag || (lag > 0 && lag_unknown != last_lag_unknown))
+                statusbar_items_redraw("lag");
 }
 
-static void mainbar_add_items(MAIN_WINDOW_REC *window)
+static void sig_server_lag_updated(SERVER_REC *server)
 {
-       mainbar = window->statusbar;
-       mainbar_window = window;
-
-       clock_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_clock);
-       nick_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_nick);
-       window_item = statusbar_item_create(mainbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window);
-       mail_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_mail);
-       lag_item = statusbar_item_create(mainbar, SBAR_PRIORITY_LOW, FALSE, statusbar_lag);
-       activity_item = statusbar_item_create(mainbar, SBAR_PRIORITY_HIGH, FALSE, statusbar_activity);
+       if (active_win != NULL && active_win->active_server == server)
+                lag_check_update();
 }
 
-static void sidebar_add_items(MAIN_WINDOW_REC *window)
+static int sig_lag_timeout(void)
 {
-       window->statusbar_window_item =
-               statusbar_item_create(window->statusbar, SBAR_PRIORITY_NORMAL, FALSE, statusbar_window);
+        lag_check_update();
+        return 1;
 }
 
-static void sidebar_remove_items(MAIN_WINDOW_REC *window)
+static void item_input(SBAR_ITEM_REC *item, int get_size_only)
 {
-       if (window->statusbar_window_item != NULL) {
-               statusbar_item_remove(window->statusbar_window_item);
-               window->statusbar_window_item = NULL;
-       }
-}
+       GUI_ENTRY_REC *rec;
 
-static void sig_mainwindow_created(MAIN_WINDOW_REC *window)
-{
-       window->statusbar =
-               statusbar_create(STATUSBAR_POS_MIDDLE,
-                                window->first_line+window->height);
-        ((STATUSBAR_REC *) window->statusbar)->window = window;
-       sidebar_add_items(window);
-}
+       rec = g_hash_table_lookup(input_entries, item);
+       if (rec == NULL) {
+               rec = gui_entry_create(item->xpos, item->bar->real_ypos,
+                                      item->size,
+                                      settings_get_bool("term_utf8"));
+               gui_entry_set_active(rec);
+               g_hash_table_insert(input_entries, item, rec);
+       }
 
-static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
-{
-       if (window == mainbar_window) {
-               mainbar = NULL;
-               mainbar_window = NULL;
+       if (get_size_only) {
+               item->min_size = 2+term_width/10;
+                item->max_size = term_width;
+                return;
        }
 
-       if (window->statusbar != NULL)
-               statusbar_destroy(window->statusbar);
+       gui_entry_move(rec, item->xpos, item->bar->real_ypos,
+                      item->size);
+       gui_entry_redraw(rec); /* FIXME: this is only necessary with ^L.. */
 }
 
-static void sig_main_statusbar_changed(WINDOW_REC *window)
+static void sig_statusbar_item_destroyed(SBAR_ITEM_REC *item)
 {
-       MAIN_WINDOW_REC *parent;
-
-       if (window == NULL)
-               return;
-
-       parent = WINDOW_GUI(window)->parent;
-       if (mainbar == parent->statusbar)
-               return;
+       GUI_ENTRY_REC *rec;
 
-       if (mainbar != NULL) {
-               mainbar_remove_items();
-               sidebar_add_items(mainbar_window);
+       rec = g_hash_table_lookup(input_entries, item);
+       if (rec != NULL) {
+               gui_entry_destroy(rec);
+               g_hash_table_remove(input_entries, item);
        }
-       sidebar_remove_items(parent);
-        mainbar_add_items(parent);
 }
 
 static void read_settings(void)
 {
-       use_colors = settings_get_bool("colors") && has_colors();
-       if (settings_get_bool("topicbar"))
-               topicbar_create();
-       else
-               topicbar_destroy();
-
-       lag_min_show = settings_get_int("lag_min_show")*10;
-       statusbar_redraw(NULL);
+       if (active_entry != NULL) {
+               gui_entry_set_utf8(active_entry,
+                                  settings_get_bool("term_utf8"));
+       }
 }
 
 void statusbar_items_init(void)
 {
-       GSList *tmp;
-
        settings_add_int("misc", "lag_min_show", 100);
-       settings_add_bool("lookandfeel", "topicbar", TRUE);
        settings_add_bool("lookandfeel", "actlist_moves", FALSE);
-       settings_add_bool("misc", "mail_counter", TRUE);
-
-       /* clock */
-       clock_timetag = g_timeout_add(1000, (GSourceFunc) statusbar_clock_timeout, NULL);
-
-       /* nick */
-       signal_add("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_add("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-
-       /* channel */
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw);
-       signal_add("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_add("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item);
-       signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-
-       /* activity */
+
+       statusbar_item_register("window", NULL, item_window_active);
+       statusbar_item_register("window_empty", NULL, item_window_empty);
+       statusbar_item_register("prompt", NULL, item_window_active);
+       statusbar_item_register("prompt_empty", NULL, item_window_empty);
+       statusbar_item_register("lag", NULL, item_lag);
+       statusbar_item_register("act", NULL, item_act);
+       statusbar_item_register("more", NULL, item_more);
+       statusbar_item_register("input", NULL, item_input);
+
+        /* activity */
        activity_list = NULL;
        signal_add("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight);
        signal_add("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed);
        signal_add("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated);
 
-       /* more */
-       more_item = NULL;
-       signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove);
-       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_check);
-       signal_add("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check);
-
-       /* lag */
-       lag_timetag = g_timeout_add(1000*LAG_REFRESH_TIME, (GSourceFunc) statusbar_lag_timeout, NULL);
-       signal_add("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-       signal_add("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-
-       /* mail */
-       mail_timetag = g_timeout_add(1000*MAIL_REFRESH_TIME, (GSourceFunc) statusbar_mail_timeout, NULL);
-
-       /* topic */
-       topic_item = NULL; topic_bar = NULL;
-       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+        /* more */
+        more_visible = NULL;
+       signal_add("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add("window changed", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add_last("gui print text finished", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add_last("command clear", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_add_last("command scrollback", (SIGNAL_FUNC) sig_statusbar_more_updated);
+
+        /* lag */
+       last_lag = 0; last_lag_unknown = FALSE;
+       signal_add("server lag", (SIGNAL_FUNC) sig_server_lag_updated);
+       signal_add("window changed", (SIGNAL_FUNC) lag_check_update);
+       signal_add("window server changed", (SIGNAL_FUNC) lag_check_update);
+        lag_timeout_tag = g_timeout_add(5000, (GSourceFunc) sig_lag_timeout, NULL);
+
+        /* input */
+       input_entries = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+       signal_add("statusbar item destroyed", (SIGNAL_FUNC) sig_statusbar_item_destroyed);
 
        read_settings();
-       statusbar_redraw(NULL);
-
-       /* middle bars */
-        signal_add("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created);
-        signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
-       signal_add("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed);
-       signal_add("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw);
-
-       /* add statusbars to existing windows */
-       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next)
-               sig_mainwindow_created(tmp->data);
-       sig_main_statusbar_changed(active_win);
+        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 }
 
 void statusbar_items_deinit(void)
 {
-       /* clock */
-       g_source_remove(clock_timetag);
-
-       /* nick */
-       signal_remove("server connected", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("channel wholist", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("nick mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("user mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("server nick changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-       signal_remove("away mode changed", (SIGNAL_FUNC) sig_statusbar_nick_redraw);
-
-       /* channel */
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_window_redraw);
-       signal_remove("window item changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_remove("channel mode changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window_item);
-       signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-       signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_window_redraw_window);
-
-       /* activity */
+        /* activity */
        signal_remove("window activity", (SIGNAL_FUNC) sig_statusbar_activity_hilight);
        signal_remove("window destroyed", (SIGNAL_FUNC) sig_statusbar_activity_window_destroyed);
        signal_remove("window refnum changed", (SIGNAL_FUNC) sig_statusbar_activity_updated);
        g_list_free(activity_list);
-
-       /* more */
-       signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_check_remove);
-       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_check);
-       signal_remove("gui print text", (SIGNAL_FUNC) sig_statusbar_more_check);
-
-       /* lag */
-       g_source_remove(lag_timetag);
-       signal_remove("server lag", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-       signal_remove("window server changed", (SIGNAL_FUNC) sig_statusbar_lag_redraw);
-
-       /* mail */
-       g_source_remove(mail_timetag);
-
-       /* topic */
-       topicbar_destroy();
-       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
-
-       /* middle bars */
-        signal_remove("mainwindow created", (SIGNAL_FUNC) sig_mainwindow_created);
-        signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
-       signal_remove("window changed", (SIGNAL_FUNC) sig_main_statusbar_changed);
-       signal_remove("window refnum changed", (SIGNAL_FUNC) sig_sidebars_redraw);
+        activity_list = NULL;
+
+        /* more */
+        g_slist_free(more_visible);
+       signal_remove("gui page scrolled", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("window changed", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("gui print text finished", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("command clear", (SIGNAL_FUNC) sig_statusbar_more_updated);
+       signal_remove("command scrollback", (SIGNAL_FUNC) sig_statusbar_more_updated);
+
+        /* lag */
+       signal_remove("server lag", (SIGNAL_FUNC) sig_server_lag_updated);
+       signal_remove("window changed", (SIGNAL_FUNC) lag_check_update);
+       signal_remove("window server changed", (SIGNAL_FUNC) lag_check_update);
+        g_source_remove(lag_timeout_tag);
+
+        /* input */
+       signal_remove("statusbar item destroyed", (SIGNAL_FUNC) sig_statusbar_item_destroyed);
+        g_hash_table_destroy(input_entries);
+
+        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
index 8e99c4fe5634a1e80515c911aea820e317e2a481..a9271b3cf7808a6fb42c47f65c2da3698d66e5ed 100644 (file)
 
 #include "module.h"
 #include "signals.h"
+#include "expandos.h"
+#include "special-vars.h"
 
 #include "themes.h"
 
 #include "statusbar.h"
+#include "statusbar-config.h"
 #include "gui-windows.h"
-
-static int backs[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* FIXME: should be in some more generic place.. */
+#include "gui-printtext.h"
 
 void statusbar_items_init(void);
 void statusbar_items_deinit(void);
 
-static GSList *statusbars;
-static int sbar_uppest, sbar_lowest, sbars_up, sbars_down;
+GSList *statusbar_groups;
+STATUSBAR_GROUP_REC *active_statusbar_group;
+
+/*
+   sbar_item_defs: char *name => char *value
+   sbar_item_funcs: char *name => STATUSBAR_FUNC func
+   sbar_signal_items: int signal_id => GSList *(SBAR_ITEM_REC *items)
+   sbar_item_signals: SBAR_ITEM_REC *item => GSList *(int *signal_ids)
+   named_sbar_items: const char *name => GSList *(SBAR_ITEM_REC *items)
+*/
+static GHashTable *sbar_item_defs, *sbar_item_funcs;
+static GHashTable *sbar_signal_items, *sbar_item_signals;
+static GHashTable *named_sbar_items;
+static int statusbar_need_recreate_items;
+
+void statusbar_item_register(const char *name, const char *value,
+                            STATUSBAR_FUNC func)
+{
+       gpointer hkey, hvalue;
+
+       statusbar_need_recreate_items = TRUE;
+       if (value != NULL) {
+               if (g_hash_table_lookup_extended(sbar_item_defs,
+                                                name, &hkey, &hvalue)) {
+                       g_hash_table_remove(sbar_item_defs, name);
+                       g_free(hkey);
+                        g_free(hvalue);
+               }
+               g_hash_table_insert(sbar_item_defs,
+                                   g_strdup(name), g_strdup(value));
+       }
+
+       if (func != NULL) {
+               if (g_hash_table_lookup(sbar_item_funcs, name) == NULL) {
+                       g_hash_table_insert(sbar_item_funcs,
+                                           g_strdup(name), (void *) func);
+               }
+       }
+}
+
+void statusbar_item_unregister(const char *name)
+{
+       gpointer key, value;
+
+       statusbar_need_recreate_items = TRUE;
+       if (g_hash_table_lookup_extended(sbar_item_defs,
+                                        name, &key, &value)) {
+               g_hash_table_remove(sbar_item_defs, key);
+               g_free(key);
+                g_free(value);
+       }
+
+       if (g_hash_table_lookup_extended(sbar_item_funcs,
+                                        name, &key, &value)) {
+               g_hash_table_remove(sbar_item_funcs, key);
+               g_free(key);
+       }
+}
+
+STATUSBAR_GROUP_REC *statusbar_group_create(const char *name)
+{
+       STATUSBAR_GROUP_REC *rec;
+
+       rec = g_new0(STATUSBAR_GROUP_REC, 1);
+       rec->name = g_strdup(name);
+
+        statusbar_groups = g_slist_append(statusbar_groups, rec);
+       return rec;
+}
 
-static void statusbar_item_destroy(SBAR_ITEM_REC *rec)
+void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec)
 {
-       rec->bar->items = g_slist_remove(rec->bar->items, rec);
-       g_free(rec);
+       statusbar_groups = g_slist_remove(statusbar_groups, rec);
+
+       while (rec->bars != NULL)
+               statusbar_destroy(rec->bars->data);
+       while (rec->config_bars != NULL)
+                statusbar_config_destroy(rec, rec->config_bars->data);
+
+        g_free(rec->name);
+        g_free(rec);
+}
+
+STATUSBAR_GROUP_REC *statusbar_group_find(const char *name)
+{
+       GSList *tmp;
+
+       for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_GROUP_REC *rec = tmp->data;
+
+               if (strcmp(rec->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
 }
 
 static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2)
 {
-       return item1->priority == item2->priority ? 0 :
-               item1->priority < item2->priority ? -1 : 1;
+       return item1->config->priority == item2->config->priority ? 0 :
+               item1->config->priority < item2->config->priority ? -1 : 1;
 }
 
+static int sbar_cmp_position(STATUSBAR_REC *bar1, STATUSBAR_REC *bar2)
+{
+       return bar1->config->position < bar2->config->position ? -1 : 1;
+}
+
+/* Shink all items in statusbar to their minimum requested size.
+   The items list should be sorted by priority, highest first. */
 static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
 {
        GSList *tmp;
@@ -71,6 +168,9 @@ static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
         return size;
 }
 
+/* shink the items in statusbar, even if their size gets smaller than
+   their minimum requested size. The items list should be sorted by
+   priority, highest first. */
 static void statusbar_shrink_forced(GSList *items, int size, int max_width)
 {
        GSList *tmp;
@@ -80,7 +180,7 @@ static void statusbar_shrink_forced(GSList *items, int size, int max_width)
 
                if (size-rec->size > max_width) {
                        /* remove the whole item */
-                        size -= rec->size+1; /* +1 == the marginal */
+                        size -= rec->size;
                        rec->size = 0;
                } else {
                        /* shrink the item */
@@ -90,14 +190,14 @@ static void statusbar_shrink_forced(GSList *items, int size, int max_width)
        }
 }
 
-static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width)
+static void statusbar_resize_items(STATUSBAR_REC *bar, int max_width)
 {
        GSList *tmp, *prior_sorted;
         int width;
 
         /* first give items their max. size */
        prior_sorted = NULL;
-       width = -1; /* -1 because of the marginals */
+       width = 0;
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
@@ -105,8 +205,7 @@ static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width)
                rec->size = rec->max_size;
 
                if (rec->size > 0) {
-                        /* +1 == marginal between items */
-                       width += rec->max_size+1;
+                       width += rec->max_size;
 
                        prior_sorted = g_slist_insert_sorted(prior_sorted, rec,
                                                             (GCompareFunc)
@@ -131,239 +230,908 @@ static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width)
        g_slist_free(prior_sorted);
 }
 
-static void statusbar_redraw_line(STATUSBAR_REC *bar)
+#define SBAR_ITEM_REDRAW_NEEDED(_bar, _item, _xpos) \
+       (((_bar)->dirty_xpos != -1 && \
+         (_xpos) >= (_bar)->dirty_xpos) || \
+        (_item)->xpos != (_xpos) || (_item)->current_size != (_item)->size)
+
+static void statusbar_calc_item_positions(STATUSBAR_REC *bar)
 {
         WINDOW_REC *old_active_win;
-       GSList *tmp;
+       GSList *tmp, *right_items;
        int xpos, rxpos;
 
        old_active_win = active_win;
-        if (bar->window != NULL)
-               active_win = bar->window->active;
+        if (bar->parent_window != NULL)
+               active_win = bar->parent_window->active;
 
-       statusbar_get_sizes(bar, COLS-2);
+       statusbar_resize_items(bar, term_width);
 
-       xpos = 1;
+        /* left-aligned items */
+       xpos = 0;
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
-               if (!rec->right_justify && rec->size > 0) {
-                       rec->xpos = xpos;
-                        xpos += rec->size+1;
-                        rec->func(rec, FALSE);
+               if (!rec->config->right_alignment &&
+                   (rec->size > 0 || rec->current_size > 0)) {
+                       if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, xpos)) {
+                                /* redraw the item */
+                               rec->dirty = TRUE;
+                               if (bar->dirty_xpos == -1 ||
+                                   xpos < bar->dirty_xpos) {
+                                        irssi_set_dirty();
+                                       bar->dirty = TRUE;
+                                       bar->dirty_xpos = xpos;
+                               }
+
+                               rec->xpos = xpos;
+                       }
+                       xpos += rec->size;
                }
        }
 
-       rxpos = COLS-1;
+       /* right-aligned items - first copy them to a new list backwards,
+          easier to draw them in right order */
+        right_items = NULL;
        for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
                SBAR_ITEM_REC *rec = tmp->data;
 
-               if (rec->right_justify && rec->size > 0) {
-                        rxpos -= rec->size+1;
-                       rec->xpos = rxpos+1;
-                       rec->func(rec, FALSE);
+               if (rec->config->right_alignment) {
+                        if (rec->size > 0)
+                               right_items = g_slist_prepend(right_items, rec);
+                       else if (rec->current_size > 0) {
+                               /* item was hidden - set the dirty position
+                                  to begin from the item's old xpos */
+                               irssi_set_dirty();
+                               bar->dirty = TRUE;
+                                bar->dirty_xpos = rec->xpos;
+                       }
+               }
+       }
+
+       rxpos = term_width;
+       for (tmp = right_items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_REC *rec = tmp->data;
+
+               rxpos -= rec->size;
+               if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, rxpos)) {
+                       rec->dirty = TRUE;
+                       if (bar->dirty_xpos == -1 ||
+                           rxpos < bar->dirty_xpos) {
+                               irssi_set_dirty();
+                               bar->dirty = TRUE;
+                               bar->dirty_xpos = rxpos;
+                       }
+                       rec->xpos = rxpos;
                }
        }
+        g_slist_free(right_items);
 
        active_win = old_active_win;
 }
 
-static void statusbar_redraw_all(void)
+void statusbar_redraw(STATUSBAR_REC *bar, int force)
 {
-       screen_refresh_freeze();
-       g_slist_foreach(statusbars, (GFunc) statusbar_redraw, NULL);
-       screen_refresh_thaw();
+       if (statusbar_need_recreate_items)
+               return; /* don't bother yet */
+
+       if (bar != NULL) {
+               if (force) {
+                       irssi_set_dirty();
+                       bar->dirty = TRUE;
+                        bar->dirty_xpos = 0;
+               }
+               statusbar_calc_item_positions(bar);
+       } else if (active_statusbar_group != NULL) {
+               g_slist_foreach(active_statusbar_group->bars,
+                               (GFunc) statusbar_redraw,
+                               GINT_TO_POINTER(force));
+       }
 }
 
-STATUSBAR_REC *statusbar_find(int pos, int line)
+void statusbar_item_redraw(SBAR_ITEM_REC *item)
 {
-       GSList *tmp;
+        WINDOW_REC *old_active_win;
 
-       for (tmp = statusbars; tmp != NULL; tmp = tmp->next) {
-               STATUSBAR_REC *rec = tmp->data;
+       g_return_if_fail(item != NULL);
+
+       old_active_win = active_win;
+        if (item->bar->parent_window != NULL)
+               active_win = item->bar->parent_window->active;
+
+       item->func(item, TRUE);
+
+       item->dirty = TRUE;
+       item->bar->dirty = TRUE;
+       irssi_set_dirty();
 
-               if (rec->pos == pos && rec->line == line)
-                       return rec;
+       if (item->max_size != item->size) {
+               /* item wants a new size - we'll need to redraw
+                  the statusbar to see if this is allowed */
+               statusbar_redraw(item->bar, FALSE);
        }
 
-       return NULL;
+       active_win = old_active_win;
 }
 
-void statusbar_redraw(STATUSBAR_REC *bar)
+void statusbar_items_redraw(const char *name)
 {
-       if (bar == NULL) {
-               statusbar_redraw_all();
+       g_slist_foreach(g_hash_table_lookup(named_sbar_items, name),
+                       (GFunc) statusbar_item_redraw, NULL);
+}
+
+static void statusbars_recalc_ypos(STATUSBAR_REC *bar)
+{
+       GSList *tmp, *bar_group;
+        int ypos;
+
+       /* get list of statusbars with same type and placement,
+          sorted by position */
+        bar_group = NULL;
+       tmp = bar->config->type == STATUSBAR_TYPE_ROOT ? bar->group->bars :
+                bar->parent_window->statusbars;
+
+        for (; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *rec = tmp->data;
+
+               if (rec->config->type == bar->config->type &&
+                   rec->config->placement == bar->config->placement) {
+                       bar_group = g_slist_insert_sorted(bar_group, rec,
+                                                         (GCompareFunc)
+                                                         sbar_cmp_position);
+               }
+       }
+
+       if (bar_group == NULL) {
+               /* we just destroyed the last statusbar in this
+                  type/placement group */
                return;
        }
 
-       set_bg(stdscr, backs[bar->color] << 4);
-       move(bar->ypos, 0); clrtoeol();
-       set_bg(stdscr, 0);
+        /* get the Y-position for the first statusbar */
+       if (bar->config->type == STATUSBAR_TYPE_ROOT) {
+               ypos = bar->config->placement == STATUSBAR_TOP ? 0 :
+                       term_height - g_slist_length(bar_group);
+       } else {
+               ypos = bar->config->placement == STATUSBAR_TOP ?
+                       bar->parent_window->first_line :
+                       bar->parent_window->last_line -
+                       (g_slist_length(bar_group)-1);
+       }
 
-       statusbar_redraw_line(bar);
+        /* set the Y-positions */
+       while (bar_group != NULL) {
+               bar = bar_group->data;
+
+               if (bar->real_ypos != ypos) {
+                       bar->real_ypos = ypos;
+                        statusbar_redraw(bar, TRUE);
+               }
 
-        screen_refresh(NULL);
+                ypos++;
+                bar_group = g_slist_remove(bar_group, bar_group->data);
+       }
 }
 
-void statusbar_item_redraw(SBAR_ITEM_REC *item)
+static void sig_terminal_resized(void)
 {
-       g_return_if_fail(item != NULL);
+       GSList *tmp;
 
-       item->func(item, TRUE);
-       if (item->max_size != item->size)
-               statusbar_redraw(item->bar);
-       else {
-               item->func(item, FALSE);
-                screen_refresh(NULL);
+       for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *bar = tmp->data;
+
+               if (bar->config->type == STATUSBAR_TYPE_ROOT &&
+                   bar->config->placement == STATUSBAR_BOTTOM) {
+                       statusbars_recalc_ypos(bar);
+                        break;
+               }
        }
 }
 
-static int get_last_bg(const char *str)
+static void mainwindow_recalc_ypos(MAIN_WINDOW_REC *window, int placement)
 {
-       int last = -1;
+       GSList *tmp;
 
-       while (*str != '\0') {
-               if (*str == '%' && str[1] != '\0') {
-                        str++;
-                       if (*str >= '0' && *str <= '7')
-                               last = *str-'0';
+       for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *bar = tmp->data;
+
+               if (bar->config->placement == placement) {
+                       statusbars_recalc_ypos(bar);
+                        break;
                }
-                str++;
        }
+}
 
-        return last;
+static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
+{
+        mainwindow_recalc_ypos(window, STATUSBAR_TOP);
+        mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM);
 }
 
-/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */
-STATUSBAR_REC *statusbar_create(int pos, int ypos)
+STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group,
+                                STATUSBAR_CONFIG_REC *config,
+                                MAIN_WINDOW_REC *parent_window)
 {
-       STATUSBAR_REC *rec;
-        char *str;
+       STATUSBAR_REC *bar;
+       THEME_REC *theme;
+        GSList *tmp;
+       char *name, *value;
+
+        g_return_val_if_fail(group != NULL, NULL);
+        g_return_val_if_fail(config != NULL, NULL);
+       g_return_val_if_fail(config->type != STATUSBAR_TYPE_WINDOW ||
+                            parent_window != NULL, NULL);
+
+       bar = g_new0(STATUSBAR_REC, 1);
+       group->bars = g_slist_append(group->bars, bar);
+
+       bar->group = group;
+
+        bar->config = config;
+        bar->parent_window = parent_window;
+
+       irssi_set_dirty();
+       bar->dirty = TRUE;
+        bar->dirty_xpos = 0;
+
+        signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+       signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
 
-       rec = g_new0(STATUSBAR_REC, 1);
-       statusbars = g_slist_append(statusbars, rec);
+       if (config->type == STATUSBAR_TYPE_ROOT) {
+               /* top/bottom of the screen */
+               mainwindows_reserve_lines(config->placement == STATUSBAR_TOP,
+                                         config->placement == STATUSBAR_BOTTOM);
+                theme = current_theme;
+       } else {
+               /* top/bottom of the window */
+               parent_window->statusbars =
+                       g_slist_append(parent_window->statusbars, bar);
+               mainwindow_set_statusbar_lines(parent_window,
+                                              config->placement == STATUSBAR_TOP,
+                                              config->placement == STATUSBAR_BOTTOM);
+               theme = parent_window != NULL && parent_window->active != NULL &&
+                       parent_window->active->theme != NULL ?
+                       parent_window->active->theme : current_theme;
+       }
 
-       rec->pos = pos;
-       rec->line = pos == STATUSBAR_POS_MIDDLE ? ypos :
-               mainwindows_reserve_lines(1, pos == STATUSBAR_POS_UP);
-       rec->ypos = pos == STATUSBAR_POS_MIDDLE ? ypos :
-               pos == STATUSBAR_POS_UP ? rec->line : LINES-1-rec->line;
+        signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+       signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
 
         /* get background color from sb_background abstract */
-       str = theme_format_expand(current_theme, "{sb_background}");
-       if (str == NULL) str = g_strdup("%n%8");
-       rec->color_string = g_strconcat("%n", str, NULL);
-        g_free(str);
+        name = g_strdup_printf("{sb_%s_bg}", config->name);
+       value = theme_format_expand(theme, name);
+       g_free(name);
+
+       if (*value == '\0') {
+                /* try with the statusbar group name */
+               g_free(value);
+
+               name = g_strdup_printf("{sb_%s_bg}", group->name);
+               value = theme_format_expand(theme, name);
+               g_free(name);
+
+               if (*value == '\0') {
+                       /* fallback to default statusbar background
+                          (also provides backwards compatibility..) */
+                        g_free(value);
+                       value = theme_format_expand(theme, "{sb_background}");
+               }
+       }
+
+       if (*value == '\0') {
+                g_free(value);
+               value = g_strdup("%8");
+       }
+       bar->color = g_strconcat("%n", value, NULL);
+       g_free(value);
 
-       rec->color = get_last_bg(rec->color_string);
-        if (rec->color < 0) rec->color = current_theme->default_real_color;
+        statusbars_recalc_ypos(bar);
+        signal_emit("statusbar created", 1, bar);
 
-       if (pos == STATUSBAR_POS_UP) {
-                if (sbars_up == 0) sbar_uppest = rec->line;
-                sbars_up++;
-               rec->line -= sbar_uppest;
-       } else if (pos == STATUSBAR_POS_DOWN) {
-               if (sbars_down == 0) sbar_lowest = rec->line;
-               sbars_down++;
-               rec->line -= sbar_lowest;
+        /* create the items to statusbar */
+       for (tmp = config->items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_CONFIG_REC *rec = tmp->data;
+
+                statusbar_item_create(bar, rec);
        }
+       return bar;
+}
 
-       set_bg(stdscr, backs[rec->color] << 4);
-       move(rec->ypos, 0); clrtoeol();
-       set_bg(stdscr, 0);
+void statusbar_destroy(STATUSBAR_REC *bar)
+{
+       int top;
 
-       return rec;
+       g_return_if_fail(bar != NULL);
+
+       bar->group->bars = g_slist_remove(bar->group->bars, bar);
+       if (bar->parent_window != NULL) {
+               bar->parent_window->statusbars =
+                       g_slist_remove(bar->parent_window->statusbars, bar);
+       }
+
+        signal_emit("statusbar destroyed", 1, bar);
+
+       while (bar->items != NULL)
+               statusbar_item_destroy(bar->items->data);
+
+        g_free(bar->color);
+
+       if (bar->config->type != STATUSBAR_TYPE_WINDOW ||
+           bar->parent_window != NULL)
+               statusbars_recalc_ypos(bar);
+
+       top = bar->config->placement == STATUSBAR_TOP;
+       if (bar->config->type == STATUSBAR_TYPE_ROOT) {
+               /* top/bottom of the screen */
+               mainwindows_reserve_lines(top ? -1 : 0, !top ? -1 : 0);
+       } else if (bar->parent_window != NULL) {
+               /* top/bottom of the window */
+               mainwindow_set_statusbar_lines(bar->parent_window,
+                                              top ? -1 : 0, !top ? -1 : 0);
+       }
+
+       g_free(bar);
 }
 
-static void statusbars_pack(int pos, int line)
+void statusbar_recreate_items(STATUSBAR_REC *bar)
 {
        GSList *tmp;
 
-       for (tmp = statusbars; tmp != NULL; tmp = tmp->next) {
+       /* destroy */
+       while (bar->items != NULL)
+               statusbar_item_destroy(bar->items->data);
+
+        /* create */
+       for (tmp = bar->config->items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_CONFIG_REC *rec = tmp->data;
+
+                statusbar_item_create(bar, rec);
+       }
+
+        statusbar_redraw(bar, TRUE);
+}
+
+void statusbars_recreate_items(void)
+{
+       if (active_statusbar_group != NULL) {
+               g_slist_foreach(active_statusbar_group->bars,
+                               (GFunc) statusbar_recreate_items, NULL);
+       }
+}
+
+STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
+                             MAIN_WINDOW_REC *window)
+{
+       GSList *tmp;
+
+       for (tmp = group->bars; tmp != NULL; tmp = tmp->next) {
                STATUSBAR_REC *rec = tmp->data;
 
-               if (rec->pos == pos && rec->line > line) {
-                       rec->line--;
-                       rec->ypos += (pos == STATUSBAR_POS_UP ? -1 : 1);
+               if (rec->parent_window == window &&
+                   strcmp(rec->config->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+static char *update_statusbar_bg(const char *str, const char *color)
+{
+       GString *out;
+        char *ret;
+
+       out = g_string_new(color);
+       while (*str != '\0') {
+               if (*str == '%' && str[1] == 'n') {
+                        g_string_append(out, color);
+                       str += 2;
+                        continue;
                }
+
+               g_string_append_c(out, *str);
+                str++;
        }
+
+        ret = out->str;
+        g_string_free(out, FALSE);
+        return ret;
 }
 
-void statusbar_destroy(STATUSBAR_REC *bar)
+const char *statusbar_item_get_value(SBAR_ITEM_REC *item)
 {
-       g_return_if_fail(bar != NULL);
+       const char *value;
 
-       if (bar->pos != STATUSBAR_POS_MIDDLE)
-               mainwindows_reserve_lines(-1, bar->pos == STATUSBAR_POS_UP);
+       value = item->config->value;
+       if (value == NULL) {
+               value = g_hash_table_lookup(sbar_item_defs,
+                                           item->config->name);
+       }
 
-       if (bar->pos == STATUSBAR_POS_UP) sbars_up--;
-       if (bar->pos == STATUSBAR_POS_DOWN) sbars_down--;
-        statusbars = g_slist_remove(statusbars, bar);
+        return value;
+}
 
-       while (bar->items != NULL)
-               statusbar_item_destroy(bar->items->data);
+void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
+                                   const char *str, const char *data,
+                                   int escape_vars)
+{
+       SERVER_REC *server;
+       WI_ITEM_REC *wiitem;
+        char *tmpstr, *tmpstr2;
+       int len;
+
+       if (str == NULL)
+               str = statusbar_item_get_value(item);
+       if (str == NULL || *str == '\0') {
+               item->min_size = item->max_size = 0;
+               return;
+       }
 
-       if (bar->pos != STATUSBAR_POS_MIDDLE)
-               statusbars_pack(bar->pos, bar->pos);
-        g_free(bar->color_string);
-       g_free(bar);
+       if (active_win == NULL) {
+               server = NULL;
+                wiitem = NULL;
+       } else {
+               server = active_win->active_server;
+                wiitem = active_win->active;
+       }
+
+       /* expand templates */
+       tmpstr = theme_format_expand_data(current_theme, &str,
+                                         'n', 'n',
+                                         NULL, NULL,
+                                         EXPAND_FLAG_ROOT |
+                                         EXPAND_FLAG_IGNORE_REPLACES |
+                                         EXPAND_FLAG_IGNORE_EMPTY);
+       /* expand $variables */
+       tmpstr2 = parse_special_string(tmpstr, server, wiitem, data, NULL,
+                                      (escape_vars ? PARSE_FLAG_ESCAPE_VARS : 0 ));
+        g_free(tmpstr);
+
+       /* remove color codes (not %formats) */
+       tmpstr = strip_codes(tmpstr2);
+        g_free(tmpstr2);
+
+       if (get_size_only) {
+               item->min_size = item->max_size = format_get_length(tmpstr);
+       } else {
+               if (item->size < item->min_size) {
+                        /* they're forcing us smaller than minimum size.. */
+                       len = format_real_length(tmpstr, item->size);
+                        tmpstr[len] = '\0';
+               }
+
+               tmpstr2 = update_statusbar_bg(tmpstr, item->bar->color);
+               gui_printtext(item->xpos, item->bar->real_ypos, tmpstr2);
+                g_free(tmpstr2);
+       }
+       g_free(tmpstr);
+}
+
+static void statusbar_item_default_func(SBAR_ITEM_REC *item, int get_size_only)
+{
+       statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE);
+}
+
+static void statusbar_update_item(void)
+{
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               statusbar_item_redraw(item);
+               items = items->next;
+       }
+}
+
+static void statusbar_update_server(SERVER_REC *server)
+{
+        SERVER_REC *item_server;
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               item_server = item->bar->parent_window != NULL ?
+                       item->bar->parent_window->active->active_server :
+                       active_win->active_server;
 
-       if (!quitting) statusbar_redraw_all();
+               if (item_server == server)
+                       statusbar_item_redraw(item);
+
+               items = items->next;
+       }
+}
+
+static void statusbar_update_window(WINDOW_REC *window)
+{
+        WINDOW_REC *item_window;
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               item_window = item->bar->parent_window != NULL ?
+                       item->bar->parent_window->active : active_win;
+
+               if (item_window == window)
+                       statusbar_item_redraw(item);
+
+               items = items->next;
+       }
+}
+
+static void statusbar_update_window_item(WI_ITEM_REC *wiitem)
+{
+        WI_ITEM_REC *item_wi;
+       GSList *items;
+
+       items = g_hash_table_lookup(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_get_emitted_id()));
+       while (items != NULL) {
+               SBAR_ITEM_REC *item = items->data;
+
+               item_wi = item->bar->parent_window != NULL ?
+                       item->bar->parent_window->active->active :
+                       active_win->active;
+
+               if (item_wi == wiitem)
+                       statusbar_item_redraw(item);
+
+               items = items->next;
+       }
+}
+
+static void statusbar_item_default_signals(SBAR_ITEM_REC *item)
+{
+       SIGNAL_FUNC func;
+        GSList *list;
+       const char *value;
+        void *signal_id;
+        int *signals, *pos;
+
+       value = statusbar_item_get_value(item);
+       if (value == NULL)
+               return;
+
+       signals = special_vars_get_signals(value);
+       if (signals == NULL)
+               return;
+
+       for (pos = signals; *pos != -1; pos += 2) {
+               /* update signal -> item mappings */
+                signal_id = GINT_TO_POINTER(*pos);
+               list = g_hash_table_lookup(sbar_signal_items, signal_id);
+               if (list == NULL) {
+                       switch (pos[1]) {
+                       case EXPANDO_ARG_NONE:
+                               func = (SIGNAL_FUNC) statusbar_update_item;
+                               break;
+                       case EXPANDO_ARG_SERVER:
+                               func = (SIGNAL_FUNC) statusbar_update_server;
+                               break;
+                       case EXPANDO_ARG_WINDOW:
+                               func = (SIGNAL_FUNC) statusbar_update_window;
+                               break;
+                       case EXPANDO_ARG_WINDOW_ITEM:
+                               func = (SIGNAL_FUNC) statusbar_update_window_item;
+                               break;
+                       default:
+                                func = NULL;
+                                break;
+                       }
+                        if (func != NULL)
+                               signal_add_to_id(MODULE_NAME, 1, *pos, func);
+               }
+
+               if (g_slist_find(list, item) == NULL)
+                       list = g_slist_append(list, item);
+               g_hash_table_insert(sbar_signal_items, signal_id, list);
+
+                /* update item -> signal mappings */
+               list = g_hash_table_lookup(sbar_item_signals, item);
+                if (g_slist_find(list, signal_id) == NULL)
+                       list = g_slist_append(list, signal_id);
+               g_hash_table_insert(sbar_item_signals, item, list);
+       }
+        g_free(signals);
 }
 
 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
-                                    int priority, int right_justify,
-                                    STATUSBAR_FUNC func)
+                                    SBAR_ITEM_CONFIG_REC *config)
 {
        SBAR_ITEM_REC *rec;
+        GSList *items;
 
        g_return_val_if_fail(bar != NULL, NULL);
-       g_return_val_if_fail(func != NULL, NULL);
+       g_return_val_if_fail(config != NULL, NULL);
 
        rec = g_new0(SBAR_ITEM_REC, 1);
-       rec->bar = bar;
        bar->items = g_slist_append(bar->items, rec);
 
-        rec->priority = priority;
-       rec->right_justify = right_justify;
-       rec->func = func;
+       rec->bar = bar;
+       rec->config = config;
+
+       rec->func = (STATUSBAR_FUNC) g_hash_table_lookup(sbar_item_funcs,
+                                                        config->name);
+       if (rec->func == NULL)
+               rec->func = statusbar_item_default_func;
+       statusbar_item_default_signals(rec);
+
+       items = g_hash_table_lookup(named_sbar_items, config->name);
+       items = g_slist_append(items, rec);
+        g_hash_table_insert(named_sbar_items, config->name, items);
 
+       irssi_set_dirty();
+       rec->dirty = TRUE;
+       bar->dirty = TRUE;
+
+        signal_emit("statusbar item created", 1, rec);
        return rec;
 }
 
-void statusbar_item_remove(SBAR_ITEM_REC *item)
+static void statusbar_signal_remove(int signal_id)
 {
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_item);
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_server);
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window);
+       signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window_item);
+}
+
+static void statusbar_item_remove_signal(SBAR_ITEM_REC *item, int signal_id)
+{
+       GSList *list;
+
+        /* update signal -> item hash */
+       list = g_hash_table_lookup(sbar_signal_items,
+                                  GINT_TO_POINTER(signal_id));
+       list = g_slist_remove(list, item);
+       if (list != NULL) {
+               g_hash_table_insert(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_id), list);
+       } else {
+               g_hash_table_remove(sbar_signal_items,
+                                   GINT_TO_POINTER(signal_id));
+                statusbar_signal_remove(signal_id);
+       }
+}
+
+void statusbar_item_destroy(SBAR_ITEM_REC *item)
+{
+       GSList *list;
+
        g_return_if_fail(item != NULL);
 
-       statusbar_item_destroy(item);
-       if (!quitting) statusbar_redraw_all();
+       item->bar->items = g_slist_remove(item->bar->items, item);
+
+       list = g_hash_table_lookup(named_sbar_items, item->config->name);
+       list = g_slist_remove(list, item);
+       if (list == NULL)
+               g_hash_table_remove(named_sbar_items, item->config->name);
+        else
+               g_hash_table_insert(named_sbar_items, item->config->name, list);
+
+        signal_emit("statusbar item destroyed", 1, item);
+
+       list = g_hash_table_lookup(sbar_item_signals, item);
+        g_hash_table_remove(sbar_item_signals, item);
+
+       while (list != NULL) {
+                statusbar_item_remove_signal(item, GPOINTER_TO_INT(list->data));
+               list = g_slist_remove(list, list->data);
+       }
+
+       g_free(item);
 }
 
-static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
+static void statusbar_redraw_needed_items(STATUSBAR_REC *bar)
 {
-       STATUSBAR_REC *rec;
+        WINDOW_REC *old_active_win;
+       GSList *tmp;
+       char *str;
 
-       rec = window->statusbar;
-        rec->ypos = window->first_line+window->height;
+       old_active_win = active_win;
+        if (bar->parent_window != NULL)
+               active_win = bar->parent_window->active;
+
+       if (bar->dirty_xpos >= 0) {
+               str = g_strconcat(bar->color, "%>", NULL);
+               gui_printtext(bar->dirty_xpos, bar->real_ypos, str);
+               g_free(str);
+       }
+
+       for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+               SBAR_ITEM_REC *rec = tmp->data;
+
+               if (rec->dirty ||
+                   (bar->dirty_xpos != -1 &&
+                    rec->xpos >= bar->dirty_xpos)) {
+                        rec->current_size = rec->size;
+                       rec->func(rec, FALSE);
+                       rec->dirty = FALSE;
+               }
+       }
+
+        active_win = old_active_win;
 }
 
-void statusbar_init(void)
+void statusbar_redraw_dirty(void)
 {
-       statusbars = NULL;
-       sbars_up = sbars_down = 0;
+       GSList *tmp;
 
-       statusbar_items_init();
+       if (statusbar_need_recreate_items) {
+               statusbar_need_recreate_items = FALSE;
+               statusbars_recreate_items();
+       }
+
+       for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_REC *rec = tmp->data;
+
+               if (rec->dirty) {
+                        statusbar_redraw_needed_items(rec);
+                       rec->dirty = FALSE;
+                       rec->dirty_xpos = -1;
+               }
+       }
+}
+
+#define STATUSBAR_IS_VISIBLE(bar, window) \
+       ((bar)->visible == STATUSBAR_VISIBLE_ALWAYS || \
+       (active_mainwin == (window) && \
+        (bar)->visible == STATUSBAR_VISIBLE_ACTIVE) || \
+       (active_mainwin != (window) && \
+        (bar)->visible == STATUSBAR_VISIBLE_INACTIVE))
+
+static void statusbars_remove_unvisible(MAIN_WINDOW_REC *window)
+{
+       GSList *tmp, *next;
+
+       for (tmp = window->statusbars; tmp != NULL; tmp = next) {
+               STATUSBAR_REC *bar = tmp->data;
+
+               next = tmp->next;
+                if (!STATUSBAR_IS_VISIBLE(bar->config, window))
+                        statusbar_destroy(bar);
+       }
+}
+
+static void statusbars_add_visible(MAIN_WINDOW_REC *window)
+{
+       STATUSBAR_GROUP_REC *group;
+        STATUSBAR_REC *bar;
+       GSList *tmp;
+
+        group = active_statusbar_group;
+       for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
+               STATUSBAR_CONFIG_REC *config = tmp->data;
+
+               if (config->type == STATUSBAR_TYPE_WINDOW &&
+                   STATUSBAR_IS_VISIBLE(config, window) &&
+                   statusbar_find(group, config->name, window) == NULL) {
+                       bar = statusbar_create(group, config, window);
+                       statusbar_redraw(bar, TRUE);
+               }
+       }
+}
+
+static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
+{
+       while (window->statusbars != NULL) {
+               STATUSBAR_REC *bar = window->statusbars->data;
+
+               bar->parent_window->statusbars =
+                       g_slist_remove(bar->parent_window->statusbars, bar);
+               bar->parent_window = NULL;
+               statusbar_destroy(bar);
+       }
+}
+
+static void sig_window_changed(void)
+{
+       GSList *tmp;
+
+       for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+               MAIN_WINDOW_REC *rec = tmp->data;
+
+               statusbars_remove_unvisible(rec);
+                statusbars_add_visible(rec);
+       }
+}
+
+static void sig_gui_window_created(WINDOW_REC *window)
+{
+        statusbars_add_visible(WINDOW_MAIN(window));
+}
+
+static void statusbar_item_def_destroy(void *key, void *value)
+{
+       g_free(key);
+        g_free(value);
+}
+
+static void statusbar_signal_item_destroy(void *key, GSList *value)
+{
+       while (value != NULL) {
+               statusbar_signal_remove(GPOINTER_TO_INT(value->data));
+                value->data = g_slist_remove(value, value->data);
+       }
+}
+
+static void statusbar_item_signal_destroy(void *key, GSList *value)
+{
+        g_slist_free(value);
+}
+
+static void sig_setup_reload(void)
+{
+       /* statusbar-config.c recreates root statusbars,
+          we need to create window-statusbars */
+        g_slist_foreach(mainwindows, (GFunc) statusbars_add_visible, NULL);
+}
+
+void statusbar_init(void)
+{
+        statusbar_need_recreate_items = FALSE;
+       statusbar_groups = NULL;
+       active_statusbar_group = NULL;
+       sbar_item_defs = g_hash_table_new((GHashFunc) g_str_hash,
+                                         (GCompareFunc) g_str_equal);
+       sbar_item_funcs = g_hash_table_new((GHashFunc) g_str_hash,
+                                          (GCompareFunc) g_str_equal);
+       sbar_signal_items = g_hash_table_new((GHashFunc) g_direct_hash,
+                                            (GCompareFunc) g_direct_equal);
+       sbar_item_signals = g_hash_table_new((GHashFunc) g_direct_hash,
+                                            (GCompareFunc) g_direct_equal);
+       named_sbar_items = g_hash_table_new((GHashFunc) g_str_hash,
+                                           (GCompareFunc) g_str_equal);
+
+        signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
        signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_add("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
+       signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
+       signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
+       signal_add_last("setup reread", (SIGNAL_FUNC) sig_setup_reload);
+
+       statusbar_items_init();
+       statusbar_config_init(); /* signals need to be before this call */
 }
 
 void statusbar_deinit(void)
 {
-       statusbar_items_deinit();
+       while (statusbar_groups != NULL)
+               statusbar_group_destroy(statusbar_groups->data);
 
-       while (statusbars != NULL)
-               statusbar_destroy(statusbars->data);
+       g_hash_table_foreach(sbar_item_defs,
+                            (GHFunc) statusbar_item_def_destroy, NULL);
+       g_hash_table_destroy(sbar_item_defs);
 
+       g_hash_table_foreach(sbar_item_funcs, (GHFunc) g_free, NULL);
+       g_hash_table_destroy(sbar_item_funcs);
+
+       g_hash_table_foreach(sbar_signal_items,
+                            (GHFunc) statusbar_signal_item_destroy, NULL);
+       g_hash_table_destroy(sbar_signal_items);
+       g_hash_table_foreach(sbar_item_signals,
+                            (GHFunc) statusbar_item_signal_destroy, NULL);
+       g_hash_table_destroy(sbar_item_signals);
+       g_hash_table_destroy(named_sbar_items);
+
+        signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
        signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
        signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+       signal_remove("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
+       signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
+       signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
+       signal_remove("setup reread", (SIGNAL_FUNC) sig_setup_reload);
+
+       statusbar_items_deinit();
+       statusbar_config_deinit();
 }
index 32d35b8238687a9f658349d55eb6b63ebf3491b6..0d61c059bad6ade7b67cf22415a53869dc8817c6 100644 (file)
 
 #include "mainwindows.h"
 
-#define SBAR_PRIORITY_HIGH     100
-#define SBAR_PRIORITY_NORMAL   0
-#define SBAR_PRIORITY_LOW      -100
-
-enum {
-       STATUSBAR_POS_UP,
-       STATUSBAR_POS_MIDDLE,
-       STATUSBAR_POS_DOWN
-};
+#define STATUSBAR_PRIORITY_HIGH                100
+#define STATUSBAR_PRIORITY_NORMAL      0
+#define STATUSBAR_PRIORITY_LOW         -100
 
 typedef struct SBAR_ITEM_REC SBAR_ITEM_REC;
 typedef void (*STATUSBAR_FUNC) (SBAR_ITEM_REC *item, int get_size_only);
 
+/* type */
+#define STATUSBAR_TYPE_ROOT    1
+#define STATUSBAR_TYPE_WINDOW  2
+
+/* placement */
+#define STATUSBAR_TOP          1
+#define STATUSBAR_BOTTOM       2
+
+/* visible */
+#define STATUSBAR_VISIBLE_ALWAYS        1
+#define STATUSBAR_VISIBLE_ACTIVE        2
+#define STATUSBAR_VISIBLE_INACTIVE      3
+
 typedef struct {
-       MAIN_WINDOW_REC *window;
+       char *name;
+        GSList *config_bars;
+       GSList *bars;
+} STATUSBAR_GROUP_REC;
 
-       int pos;
-       int line;
+typedef struct {
+       char *name;
 
-        char *color_string;
-        int color;
+       int type; /* root/window */
+       int placement; /* top/bottom */
+       int position; /* the higher the number, the lower it is in screen */
+       int visible; /* active/inactive/always */
 
-       int ypos; /* real position in screen at the moment */
        GSList *items;
+} STATUSBAR_CONFIG_REC;
+
+typedef struct {
+       STATUSBAR_GROUP_REC *group;
+       STATUSBAR_CONFIG_REC *config;
+
+       MAIN_WINDOW_REC *parent_window; /* if config->type == STATUSBAR_TYPE_WINDOW */
+        GSList *items;
+
+       char *color; /* background color */
+       int real_ypos; /* real Y-position in screen at the moment */
+
+       int dirty:1;
+        int dirty_xpos; /* -1 = only redraw some items, >= 0 = redraw all items after from xpos */
 } STATUSBAR_REC;
 
+typedef struct {
+       char *name;
+       char *value; /* if non-NULL, overrides the default */
+
+       int priority;
+       unsigned int right_alignment:1;
+} SBAR_ITEM_CONFIG_REC;
+
 struct SBAR_ITEM_REC {
        STATUSBAR_REC *bar;
-       STATUSBAR_FUNC func;
+       SBAR_ITEM_CONFIG_REC *config;
+        STATUSBAR_FUNC func;
 
        /* what item wants */
-        int priority;
        int min_size, max_size;
-       unsigned int right_justify:1;
 
        /* what item gets */
-        int xpos, size;
+       int xpos, size;
+
+        int current_size; /* item size currently in screen */
+       int dirty:1;
 };
 
-/* ypos is used only when pos == STATUSBAR_POS_MIDDLE */
-STATUSBAR_REC *statusbar_create(int pos, int ypos);
-void statusbar_destroy(STATUSBAR_REC *bar);
+extern GSList *statusbar_groups;
+extern STATUSBAR_GROUP_REC *active_statusbar_group;
+
+void statusbar_item_register(const char *name, const char *value,
+                            STATUSBAR_FUNC func);
+void statusbar_item_unregister(const char *name);
 
-STATUSBAR_REC *statusbar_find(int pos, int line);
+STATUSBAR_GROUP_REC *statusbar_group_create(const char *name);
+void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec);
+STATUSBAR_GROUP_REC *statusbar_group_find(const char *name);
+
+STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group,
+                                STATUSBAR_CONFIG_REC *config,
+                                MAIN_WINDOW_REC *parent_window);
+void statusbar_destroy(STATUSBAR_REC *bar);
+STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
+                             MAIN_WINDOW_REC *window);
 
 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
-                                    int priority, int right_justify,
-                                    STATUSBAR_FUNC func);
-void statusbar_item_remove(SBAR_ITEM_REC *item);
+                                    SBAR_ITEM_CONFIG_REC *config);
+void statusbar_item_destroy(SBAR_ITEM_REC *item);
+
+void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
+                                   const char *str, const char *data,
+                                   int escape_vars);
 
 /* redraw statusbar, NULL = all */
-void statusbar_redraw(STATUSBAR_REC *bar);
+void statusbar_redraw(STATUSBAR_REC *bar, int force);
 void statusbar_item_redraw(SBAR_ITEM_REC *item);
+void statusbar_items_redraw(const char *name);
+
+void statusbar_recreate_items(STATUSBAR_REC *bar);
+void statusbars_recreate_items(void);
+
+void statusbar_redraw_dirty(void);
 
 void statusbar_init(void);
 void statusbar_deinit(void);
diff --git a/apps/irssi/src/fe-text/term-curses.c b/apps/irssi/src/fe-text/term-curses.c
new file mode 100644 (file)
index 0000000..69e2b92
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ term-curses.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "settings.h"
+
+#include "term.h"
+#include "mainwindows.h"
+
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+#  include <ncurses.h>
+#else
+#  include <curses.h>
+#endif
+#include <termios.h>
+#include <signal.h>
+
+#ifndef COLOR_PAIRS
+#  define COLOR_PAIRS 64
+#endif
+
+#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
+#  define USE_RESIZE_TERM
+#endif
+
+#ifndef _POSIX_VDISABLE
+#  define _POSIX_VDISABLE 0
+#endif
+
+struct _TERM_WINDOW {
+       int x, y;
+        int width, height;
+       WINDOW *win;
+};
+
+TERM_WINDOW *root_window;
+int term_width, term_height;
+
+static int curs_x, curs_y;
+static int freeze_refresh;
+static struct termios old_tio;
+
+static int init_curses(void)
+{
+       char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+       int num;
+        struct termios tio;
+
+       if (!initscr())
+               return FALSE;
+
+       cbreak(); noecho(); idlok(stdscr, 1);
+#ifdef HAVE_CURSES_IDCOK
+       /*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */
+#endif
+       intrflush(stdscr, FALSE); nodelay(stdscr, TRUE);
+
+        /* Disable INTR, QUIT, VDSUSP and SUSP keys */
+       if (tcgetattr(0, &old_tio) == 0) {
+                memcpy(&tio, &old_tio, sizeof(tio));
+               tio.c_cc[VINTR] = _POSIX_VDISABLE;
+                tio.c_cc[VQUIT] = _POSIX_VDISABLE;
+#ifdef VDSUSP
+               tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+#ifdef VSUSP
+               tio.c_cc[VSUSP] = _POSIX_VDISABLE;
+#endif
+               tcsetattr(0, TCSADRAIN, &tio);
+       }
+
+       if (has_colors())
+               start_color();
+       else if (term_use_colors)
+                term_use_colors = FALSE;
+
+#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS
+       /* this lets us to use the "default" background color for colors <= 7 so
+          background pixmaps etc. show up right */
+       use_default_colors();
+
+       for (num = 1; num < COLOR_PAIRS; num++)
+               init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]);
+
+       init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more
+                                people want dark grey than white on white.. */
+#else
+       for (num = 1; num < COLOR_PAIRS; num++)
+               init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]);
+       init_pair(63, 0, 0);
+#endif
+
+       clear();
+       return TRUE;
+}
+
+static int term_init_int(void)
+{
+       int ret;
+
+       ret = init_curses();
+       if (!ret) return 0;
+
+        curs_x = curs_y = 0;
+       freeze_refresh = 0;
+
+       root_window = g_new0(TERM_WINDOW, 1);
+        root_window->win = stdscr;
+
+       term_width = COLS;
+       term_height = LINES;
+        return ret;
+}
+
+static void term_deinit_int(void)
+{
+        tcsetattr(0, TCSADRAIN, &old_tio);
+
+       endwin();
+       g_free_and_null(root_window);
+}
+
+int term_init(void)
+{
+       if (!term_init_int())
+                return FALSE;
+
+        settings_add_int("lookandfeel", "default_color", 7);
+       term_common_init();
+        return TRUE;
+}
+
+void term_deinit(void)
+{
+        term_common_deinit();
+       term_deinit_int();
+}
+
+/* Resize terminal - if width or height is negative,
+   the new size is unknown and should be figured out somehow */
+void term_resize(int width, int height)
+{
+#ifdef HAVE_CURSES_RESIZETERM
+       if (width < 0 || height < 0) {
+#endif
+               term_deinit_int();
+               term_init_int();
+#ifdef HAVE_CURSES_RESIZETERM
+       } else if (term_width != width || term_height != height) {
+               term_width = width;
+               term_height = height;
+                resizeterm(term_height, term_width);
+       }
+#endif
+}
+
+void term_resize_final(int width, int height)
+{
+#ifdef HAVE_CURSES_RESIZETERM
+        if (width < 0 || height < 0)
+               mainwindows_recreate();
+#else
+       mainwindows_recreate();
+#endif
+}
+
+/* Returns TRUE if terminal has colors */
+int term_has_colors(void)
+{
+        return has_colors();
+}
+
+/* Force the colors on any way you can */
+void term_force_colors(int set)
+{
+        /* don't do anything with curses */
+}
+
+/* Clear screen */
+void term_clear(void)
+{
+        term_set_color(root_window, 0);
+        clear();
+}
+
+/* Beep */
+void term_beep(void)
+{
+        beep();
+}
+
+/* Create a new window in terminal */
+TERM_WINDOW *term_window_create(int x, int y, int width, int height)
+{
+        TERM_WINDOW *window;
+
+       window = g_new0(TERM_WINDOW, 1);
+       window->x = x; window->y = y;
+        window->width = width; window->height = height;
+       window->win = newwin(height, width, y, x);
+       if (window->win == NULL)
+               g_error("newwin() failed: %d,%d %d,%d", x, y, width, height);
+       idlok(window->win, 1);
+
+        return window;
+}
+
+/* Destroy a terminal window */
+void term_window_destroy(TERM_WINDOW *window)
+{
+       delwin(window->win);
+        g_free(window);
+}
+
+/* Move/resize a window */
+void term_window_move(TERM_WINDOW *window, int x, int y,
+                     int width, int height)
+{
+       /* some checks to make sure the window is visible in screen,
+          otherwise curses could get nasty and not show our window anymore. */
+        if (width < 1) width = 1;
+       if (height < 1) height = 1;
+       if (x+width > term_width) x = term_width-width;
+       if (y+height > term_height) y = term_height-height;
+
+#ifdef HAVE_CURSES_WRESIZE
+       if (window->width != width || window->height != height)
+               wresize(window->win, height, width);
+        if (window->x != x || window->y != y)
+               mvwin(window->win, y, x);
+#else
+       if (window->width != width || window->height != height ||
+           window->x != x || window->y != y) {
+               delwin(window->win);
+               window->win = newwin(height, width, y, x);
+               idlok(window->win, 1);
+       }
+#endif
+        window->x = x; window->y = y;
+        window->width = width; window->height = height;
+}
+
+/* Clear window */
+void term_window_clear(TERM_WINDOW *window)
+{
+        werase(window->win);
+}
+
+/* Scroll window up/down */
+void term_window_scroll(TERM_WINDOW *window, int count)
+{
+       scrollok(window->win, TRUE);
+       wscrl(window->win, count);
+       scrollok(window->win, FALSE);
+}
+
+static int get_attr(int color)
+{
+       int attr;
+
+       if (!term_use_colors)
+               attr = (color & 0x70) ? A_REVERSE : 0;
+       else if (((color & 0x0f) == 8) && (color & ATTR_BOLD) == 0)
+                attr = (A_DIM | COLOR_PAIR(63));
+       else if ((color & 0x77) == 0)
+               attr = A_NORMAL;
+       else {
+               if (color & ATTR_RESETFG) {
+                       color &= ~0x0f;
+                       color |= settings_get_int("default_color");
+               }
+               attr = (COLOR_PAIR((color&7) + (color&0x70)/2));
+       }
+
+       if ((color & 0x08) || (color & ATTR_BOLD)) attr |= A_BOLD;
+       if (color & ATTR_BLINK) attr |= A_BLINK;
+
+       if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE;
+       if (color & ATTR_REVERSE) attr |= A_REVERSE;
+        return attr;
+}
+
+/* Change active color */
+void term_set_color(TERM_WINDOW *window, int col)
+{
+       wattrset(window->win, get_attr(col));
+       wbkgdset(window->win, ' ' | get_attr(col));
+}
+
+void term_move(TERM_WINDOW *window, int x, int y)
+{
+        wmove(window->win, y, x);
+}
+
+void term_addch(TERM_WINDOW *window, int chr)
+{
+        waddch(window->win, chr);
+}
+
+void term_addstr(TERM_WINDOW *window, const char *str)
+{
+        waddstr(window->win, (const char *) str);
+}
+
+void term_clrtoeol(TERM_WINDOW *window)
+{
+        wclrtoeol(window->win);
+}
+
+void term_move_cursor(int x, int y)
+{
+       curs_x = x;
+        curs_y = y;
+}
+
+void term_refresh_freeze(void)
+{
+       freeze_refresh++;
+}
+
+void term_refresh_thaw(void)
+{
+       if (freeze_refresh > 0) {
+               freeze_refresh--;
+               if (freeze_refresh == 0) term_refresh(NULL);
+       }
+}
+
+void term_refresh(TERM_WINDOW *window)
+{
+       if (window != NULL)
+               wnoutrefresh(window->win);
+
+       if (freeze_refresh == 0) {
+               move(curs_y, curs_x);
+               wnoutrefresh(stdscr);
+               doupdate();
+       }
+}
+
+void term_stop(void)
+{
+       term_deinit_int();
+       kill(getpid(), SIGSTOP);
+        term_init_int();
+       irssi_redraw();
+}
+
+int term_gets(unsigned char *buffer, int size)
+{
+       int key, count;
+
+       for (count = 0; count < size; ) {
+               key = getch();
+#ifdef KEY_RESIZE
+               if (key == KEY_RESIZE)
+                       continue;
+#endif
+
+               if (key == ERR)
+                       break;
+
+                buffer[count] = key;
+                count++;
+       }
+
+       return count;
+}
diff --git a/apps/irssi/src/fe-text/term-dummy.c b/apps/irssi/src/fe-text/term-dummy.c
new file mode 100644 (file)
index 0000000..a4f5c09
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ term-dummy.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+
+#include "fe-windows.h"
+
+static int newline;
+
+static GIOChannel *stdin_channel;
+static int readtag;
+static GString *input;
+
+static void sig_gui_printtext(WINDOW_REC *window, void *fgcolor,
+                              void *bgcolor, void *pflags,
+                              char *str, void *level)
+{
+       if (newline) {
+               newline = FALSE;
+               printf("\r");
+       }
+
+       printf("%s", str);
+}
+
+static void sig_gui_printtext_finished(WINDOW_REC *window)
+{
+       printf("\n");
+       newline = TRUE;
+}
+
+static void sig_window_created(WINDOW_REC *window)
+{
+       window->width = 80;
+       window->height = 25;
+}
+
+static void readline(void)
+{
+        unsigned char buffer[128];
+       char *p;
+       int ret, i;
+
+       ret = read(0, buffer, sizeof(buffer));
+       if (ret == 0 || (ret == -1 && errno != EINTR)) {
+               /* lost terminal */
+               signal_emit("command quit", 1, "Lost terminal");
+                return;
+       }
+
+       for (i = 0; i < ret; i++)
+               g_string_append_c(input, buffer[i]);
+
+       p = strchr(input->str, '\n');
+       if (p != NULL) {
+               *p = '\0';
+               signal_emit("send command", 3, input->str,
+                           active_win->active_server, active_win->active);
+               *p = '\n';
+               g_string_erase(input, 0, (int) (p-input->str)+1);
+       }
+}
+
+void term_dummy_init(void)
+{
+       newline = TRUE;
+       input = g_string_new(NULL);
+
+       signal_add("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
+       signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
+       signal_add("window created", (SIGNAL_FUNC) sig_window_created);
+
+        stdin_channel = g_io_channel_unix_new(0);
+       readtag = g_input_add_full(stdin_channel,
+                                  G_PRIORITY_HIGH, G_INPUT_READ,
+                                  (GInputFunction) readline, NULL);
+        g_io_channel_unref(stdin_channel);
+}
+
+void term_dummy_deinit(void)
+{
+       signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
+       signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
+       signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
+
+       g_source_remove(readtag);
+       g_string_free(input, TRUE);
+}
diff --git a/apps/irssi/src/fe-text/term-terminfo.c b/apps/irssi/src/fe-text/term-terminfo.c
new file mode 100644 (file)
index 0000000..a1b106a
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ term-terminfo.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "term.h"
+#include "terminfo-core.h"
+
+#include <signal.h>
+
+struct _TERM_WINDOW {
+        /* Terminal to use for window */
+       TERM_REC *term;
+
+        /* Area for window in terminal */
+       int x, y;
+       int width, height;
+};
+
+TERM_WINDOW *root_window;
+int term_width, term_height, term_detached;
+
+static char *term_lines_empty; /* 1 if line is entirely empty */
+static int vcmove, vcx, vcy, curs_visible;
+static int crealx, crealy, cforcemove;
+static int curs_x, curs_y;
+static int auto_detach;
+
+static int last_fg, last_bg, last_attrs;
+
+static int redraw_needed, redraw_tag;
+static int freeze_counter;
+
+/* SIGCONT handler */
+static void sig_cont(int p)
+{
+        redraw_needed = TRUE;
+       terminfo_cont(current_term);
+}
+
+static int redraw_timeout(void)
+{
+       if (redraw_needed) {
+               irssi_redraw();
+                redraw_needed = FALSE;
+       }
+
+        return 1;
+}
+
+int term_init(void)
+{
+        struct sigaction act;
+
+       last_fg = last_bg = -1;
+       last_attrs = 0;
+       vcx = vcy = 0; crealx = crealy = -1;
+       vcmove = FALSE; cforcemove = TRUE;
+        curs_visible = TRUE;
+
+       current_term = terminfo_core_init(stdin, stdout);
+       if (current_term == NULL)
+               return FALSE;
+
+        /* grab CONT signal */
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = sig_cont;
+       sigaction(SIGCONT, &act, NULL);
+        redraw_tag = g_timeout_add(500, (GSourceFunc) redraw_timeout, NULL);
+
+       curs_x = curs_y = 0;
+       term_width = current_term->width;
+       term_height = current_term->height;
+       root_window = term_window_create(0, 0, term_width, term_height);
+        term_detached = FALSE;
+
+        term_lines_empty = g_new0(char, term_height);
+
+       term_common_init();
+        g_atexit(term_deinit);
+        return TRUE;
+}
+
+void term_deinit(void)
+{
+       if (current_term != NULL) {
+               g_source_remove(redraw_tag);
+
+               term_common_deinit();
+               terminfo_core_deinit(current_term);
+               current_term = NULL;
+       }
+}
+
+static void term_move_real(void)
+{
+       if (term_detached) return;
+
+       if (vcx != crealx || vcy != crealy || cforcemove) {
+               if (curs_visible) {
+                       terminfo_set_cursor_visible(FALSE);
+                       curs_visible = FALSE;
+               }
+
+               if (cforcemove) {
+                       crealx = crealy = -1;
+                       cforcemove = FALSE;
+               }
+               terminfo_move_relative(crealx, crealy, vcx, vcy);
+                crealx = vcx; crealy = vcy;
+       }
+
+        vcmove = FALSE;
+}
+
+/* Cursor position is unknown - move it immediately to known position */
+static void term_move_reset(int x, int y)
+{
+       if (x >= term_width) x = term_width-1;
+       if (y >= term_height) y = term_height-1;
+
+       vcx = x; vcy = y;
+        cforcemove = TRUE;
+        term_move_real();
+}
+
+/* Resize terminal - if width or height is negative,
+   the new size is unknown and should be figured out somehow */
+void term_resize(int width, int height)
+{
+       if (width < 0 || height < 0) {
+               terminfo_resize(current_term);
+               width = current_term->width;
+                height = current_term->height;
+       }
+
+       if (term_width != width || term_height != height) {
+               term_width = current_term->width = width;
+               term_height = current_term->height = height;
+               term_window_move(root_window, 0, 0, term_width, term_height);
+
+                g_free(term_lines_empty);
+               term_lines_empty = g_new0(char, term_height);
+       }
+
+        term_move_reset(0, 0);
+}
+
+void term_resize_final(int width, int height)
+{
+}
+
+/* Returns TRUE if terminal has colors */
+int term_has_colors(void)
+{
+        return current_term->has_colors;
+}
+
+/* Force the colors on any way you can */
+void term_force_colors(int set)
+{
+       if (term_detached) return;
+
+       terminfo_setup_colors(current_term, set);
+}
+
+/* Clear screen */
+void term_clear(void)
+{
+       if (term_detached) return;
+
+        term_set_color(root_window, ATTR_RESET);
+       terminfo_clear();
+        term_move_reset(0, 0);
+
+       memset(term_lines_empty, 1, term_height);
+}
+
+/* Beep */
+void term_beep(void)
+{
+       if (term_detached) return;
+
+        terminfo_beep(current_term);
+}
+
+/* Create a new window in terminal */
+TERM_WINDOW *term_window_create(int x, int y, int width, int height)
+{
+       TERM_WINDOW *window;
+
+       window = g_new0(TERM_WINDOW, 1);
+        window->term = current_term;
+       window->x = x; window->y = y;
+       window->width = width; window->height = height;
+        return window;
+}
+
+/* Destroy a terminal window */
+void term_window_destroy(TERM_WINDOW *window)
+{
+        g_free(window);
+}
+
+/* Move/resize a window */
+void term_window_move(TERM_WINDOW *window, int x, int y,
+                     int width, int height)
+{
+       window->x = x;
+       window->y = y;
+       window->width = width;
+        window->height = height;
+}
+
+/* Clear window */
+void term_window_clear(TERM_WINDOW *window)
+{
+       int y;
+
+       if (term_detached) return;
+
+        terminfo_set_normal();
+        if (window->y == 0 && window->height == term_height) {
+               term_clear();
+        } else {
+               for (y = 0; y < window->height; y++) {
+                       term_move(window, 0, y);
+                       term_clrtoeol(window);
+               }
+       }
+}
+
+/* Scroll window up/down */
+void term_window_scroll(TERM_WINDOW *window, int count)
+{
+       int y;
+
+       if (term_detached) return;
+
+       terminfo_scroll(window->y, window->y+window->height-1, count);
+        term_move_reset(vcx, vcy);
+
+        /* set the newly scrolled area dirty */
+       for (y = 0; y < window->height; y++)
+               term_lines_empty[window->y+y] = FALSE;
+}
+
+/* Change active color */
+void term_set_color(TERM_WINDOW *window, int col)
+{
+       int set_normal;
+
+       if (term_detached) return;
+
+        set_normal = ((col & ATTR_RESETFG) && last_fg != -1) ||
+               ((col & ATTR_RESETBG) && last_bg != -1);
+       if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
+           ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
+               /* we'll need to get rid of bold/blink - this can only be
+                  done with setting the default color */
+               set_normal = TRUE;
+       }
+
+       if (set_normal) {
+               last_fg = last_bg = -1;
+                last_attrs = 0;
+               terminfo_set_normal();
+       }
+
+       if (!term_use_colors && (col & 0xf0) != 0)
+               col |= ATTR_REVERSE;
+
+       /* reversed text (use standout) */
+       if (col & ATTR_REVERSE) {
+               if ((last_attrs & ATTR_REVERSE) == 0)
+                       terminfo_set_standout(TRUE);
+       } else if (last_attrs & ATTR_REVERSE)
+               terminfo_set_standout(FALSE);
+
+       /* set foreground color */
+       if ((col & 0x0f) != last_fg &&
+           ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) {
+                if (term_use_colors) {
+                       last_fg = col & 0x0f;
+                       terminfo_set_fg(last_fg);
+               }
+       }
+
+       /* set background color */
+       if (col & ATTR_BLINK)
+               col |= 0x80;
+       else if (col & 0x80)
+               col |= ATTR_BLINK;
+
+       if ((col & 0xf0) >> 4 != last_bg &&
+           ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) {
+                if (term_use_colors) {
+                       last_bg = (col & 0xf0) >> 4;
+                       terminfo_set_bg(last_bg);
+               }
+       }
+
+       /* bold */
+       if (col & 0x08)
+               col |= ATTR_BOLD;
+       else if (col & ATTR_BOLD)
+               terminfo_set_bold();
+
+       /* underline */
+       if (col & ATTR_UNDERLINE) {
+               if ((last_attrs & ATTR_UNDERLINE) == 0)
+                       terminfo_set_uline(TRUE);
+       } else if (last_attrs & ATTR_UNDERLINE)
+               terminfo_set_uline(FALSE);
+
+        last_attrs = col & ~0xff;
+}
+
+void term_move(TERM_WINDOW *window, int x, int y)
+{
+       vcmove = TRUE;
+       vcx = x+window->x;
+        vcy = y+window->y;
+
+       if (vcx >= term_width)
+               vcx = term_width-1;
+       if (vcy >= term_height)
+                vcy = term_height-1;
+}
+
+static void term_printed_text(int count)
+{
+       term_lines_empty[vcy] = FALSE;
+
+       /* if we continued writing past the line, wrap to next line.
+          However, next term_move() really shouldn't try to cache
+          the move, otherwise terminals would try to combine the
+          last word in upper line with first word in lower line. */
+        cforcemove = TRUE;
+       vcx += count;
+       while (vcx >= term_width) {
+               vcx -= term_width;
+               if (vcy < term_height) vcy++;
+               if (vcx > 0) term_lines_empty[vcy] = FALSE;
+       }
+}
+
+void term_addch(TERM_WINDOW *window, int chr)
+{
+       if (term_detached) return;
+
+       if (vcmove) term_move_real();
+        term_printed_text(1);
+       if (vcy != term_height || vcx != 0)
+               putc(chr, window->term->out);
+}
+
+void term_addstr(TERM_WINDOW *window, const char *str)
+{
+       int len;
+
+       if (term_detached) return;
+
+       if (vcmove) term_move_real();
+       len = strlen(str);
+        term_printed_text(len);
+
+       if (vcy != term_height || vcx != 0)
+               fputs(str, window->term->out);
+       else
+               fwrite(str, 1, len-1, window->term->out);
+}
+
+void term_clrtoeol(TERM_WINDOW *window)
+{
+       if (term_detached) return;
+
+       /* clrtoeol() doesn't necessarily understand colors */
+       if (last_fg == -1 && last_bg == -1 &&
+           (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) {
+               if (!term_lines_empty[vcy]) {
+                       if (vcmove) term_move_real();
+                       terminfo_clrtoeol();
+                       if (vcx == 0) term_lines_empty[vcy] = TRUE;
+               }
+       } else if (vcx < term_width) {
+               /* we'll need to fill the line ourself. */
+               if (vcmove) term_move_real();
+               terminfo_repeat(' ', term_width-vcx);
+               terminfo_move(vcx, vcy);
+                term_lines_empty[vcy] = FALSE;
+       }
+}
+
+void term_move_cursor(int x, int y)
+{
+       curs_x = x;
+        curs_y = y;
+}
+
+void term_refresh(TERM_WINDOW *window)
+{
+       if (term_detached || freeze_counter > 0)
+               return;
+
+       term_move(root_window, curs_x, curs_y);
+       term_move_real();
+
+       if (!curs_visible) {
+               terminfo_set_cursor_visible(TRUE);
+                curs_visible = TRUE;
+       }
+       term_set_color(window, ATTR_RESET);
+       fflush(window != NULL ? window->term->out : current_term->out);
+}
+
+void term_refresh_freeze(void)
+{
+        freeze_counter++;
+
+       if (!term_detached && curs_visible) {
+               terminfo_set_cursor_visible(FALSE);
+                curs_visible = FALSE;
+       }
+}
+
+void term_refresh_thaw(void)
+{
+       if (--freeze_counter == 0)
+                term_refresh(NULL);
+}
+
+void term_auto_detach(int set)
+{
+        auto_detach = set;
+}
+
+void term_detach(void)
+{
+       terminfo_stop(current_term);
+
+        fclose(current_term->in);
+        fclose(current_term->out);
+
+       current_term->in = NULL;
+       current_term->out = NULL;
+        term_detached = TRUE;
+}
+
+void term_attach(FILE *in, FILE *out)
+{
+       current_term->in = in;
+       current_term->out = out;
+        term_detached = FALSE;
+
+       terminfo_cont(current_term);
+       irssi_redraw();
+}
+
+void term_stop(void)
+{
+       if (term_detached) {
+               kill(getpid(), SIGSTOP);
+       } else {
+               terminfo_stop(current_term);
+               kill(getpid(), SIGSTOP);
+               terminfo_cont(current_term);
+               irssi_redraw();
+       }
+}
+
+int term_gets(unsigned char *buffer, int size)
+{
+       int ret;
+
+       if (term_detached)
+               return 0;
+
+        /* fread() doesn't work */
+        ret = read(fileno(current_term->in), buffer, size);
+       if (ret == 0) {
+               /* EOF - terminal got lost */
+               if (auto_detach)
+                        term_detach();
+               ret = -1;
+       } else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
+               ret = 0;
+
+       return ret;
+}
diff --git a/apps/irssi/src/fe-text/term.c b/apps/irssi/src/fe-text/term.c
new file mode 100644 (file)
index 0000000..c32b249
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ term.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "commands.h"
+#include "settings.h"
+
+#include "term.h"
+#include "mainwindows.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#  include <sys/ioctl.h>
+#endif
+#include <signal.h>
+#include <termios.h>
+
+#define MIN_SCREEN_WIDTH 20
+
+int term_use_colors;
+
+static int force_colors;
+static int resize_dirty;
+
+/* Resize the terminal if needed */
+void term_resize_dirty(void)
+{
+#ifdef TIOCGWINSZ
+       struct winsize ws;
+#endif
+        int width, height;
+
+       if (!resize_dirty)
+               return;
+
+        resize_dirty = FALSE;
+
+#ifdef TIOCGWINSZ
+       /* Get new window size */
+       if (ioctl(0, TIOCGWINSZ, &ws) < 0)
+               return;
+
+       if (ws.ws_row == term_height && ws.ws_col == term_width) {
+               /* Same size, abort. */
+               return;
+       }
+
+       if (ws.ws_col < MIN_SCREEN_WIDTH)
+               ws.ws_col = MIN_SCREEN_WIDTH;
+
+       width = ws.ws_col;
+        height = ws.ws_row;
+#else
+        width = height = -1;
+#endif
+        term_resize(width, height);
+       mainwindows_resize(term_width, term_height);
+        term_resize_final(width, height);
+}
+
+#ifdef SIGWINCH
+static void sig_winch(int p)
+{
+        irssi_set_dirty();
+        resize_dirty = TRUE;
+}
+#endif
+
+static void cmd_resize(void)
+{
+       resize_dirty = TRUE;
+        term_resize_dirty();
+}
+
+static void read_settings(void)
+{
+       int old_colors = term_use_colors;
+
+        term_auto_detach(settings_get_bool("term_auto_detach"));
+
+       if (force_colors != settings_get_bool("term_force_colors")) {
+               force_colors = settings_get_bool("term_force_colors");
+               term_force_colors(force_colors);
+       }
+
+       term_use_colors = settings_get_bool("colors") &&
+               (force_colors || term_has_colors());
+
+       if (term_use_colors != old_colors)
+               irssi_redraw();
+}
+
+void term_common_init(void)
+{
+#ifdef SIGWINCH
+       struct sigaction act;
+#endif
+       settings_add_bool("lookandfeel", "colors", TRUE);
+       settings_add_bool("lookandfeel", "term_force_colors", FALSE);
+        settings_add_bool("lookandfeel", "term_auto_detach", FALSE);
+        settings_add_bool("lookandfeel", "term_utf8", FALSE);
+
+       force_colors = FALSE;
+       term_use_colors = term_has_colors() && settings_get_bool("colors");
+        read_settings();
+
+       signal_add("beep", (SIGNAL_FUNC) term_beep);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       command_bind("resize", NULL, (SIGNAL_FUNC) cmd_resize);
+
+#ifdef SIGWINCH
+       sigemptyset (&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = sig_winch;
+       sigaction(SIGWINCH, &act, NULL);
+#endif
+}
+
+void term_common_deinit(void)
+{
+       command_unbind("resize", (SIGNAL_FUNC) cmd_resize);
+       signal_remove("beep", (SIGNAL_FUNC) term_beep);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-text/term.h b/apps/irssi/src/fe-text/term.h
new file mode 100644 (file)
index 0000000..9cd1b15
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __TERM_H
+#define __TERM_H
+
+typedef struct _TERM_WINDOW TERM_WINDOW;
+
+#define ATTR_RESETFG   0x0100
+#define ATTR_RESETBG   0x0200
+#define ATTR_BOLD      0x0400
+#define ATTR_BLINK      0x0800
+#define ATTR_UNDERLINE 0x1000
+#define ATTR_REVERSE   0x2000
+
+#define ATTR_RESET     (ATTR_RESETFG|ATTR_RESETBG)
+
+#define ATTR_NOCOLORS (ATTR_UNDERLINE|ATTR_REVERSE)
+
+#ifdef WANT_BIG5
+/* XXX I didn't check the encoding range of big5+. This is standard big5. */
+#  define is_big5_los(lo) (((char)0x40<=lo)&&(lo<=(char)0x7E)) /* standard */
+#  define is_big5_lox(lo) (((char)0x80<=lo)&&(lo<=(char)0xFE)) /* extended */
+#  define is_big5_hi(hi)  (((char)0x81<=hi)&&(hi<=(char)0xFE))
+#  define is_big5(hi,lo) is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo))
+#endif
+
+extern TERM_WINDOW *root_window;
+extern int term_width, term_height, term_use_colors, term_detached;
+
+/* Initialize / deinitialize terminal */
+int term_init(void);
+void term_deinit(void);
+
+/* Resize terminal - if width or height is negative,
+   the new size is unknown and should be figured out somehow */
+void term_resize(int width, int height);
+void term_resize_final(int width, int height);
+/* Resize the terminal if needed */
+void term_resize_dirty(void);
+
+/* Returns TRUE if terminal has colors */
+int term_has_colors(void);
+/* Force the colors on any way you can */
+void term_force_colors(int set);
+
+/* Clear screen */
+void term_clear(void);
+/* Beep */
+void term_beep(void);
+
+/* Create a new window in terminal */
+TERM_WINDOW *term_window_create(int x, int y, int width, int height);
+/* Destroy a terminal window */
+void term_window_destroy(TERM_WINDOW *window);
+
+/* Move/resize window */
+void term_window_move(TERM_WINDOW *window, int x, int y,
+                     int width, int height);
+/* Clear window */
+void term_window_clear(TERM_WINDOW *window);
+/* Scroll window up/down */
+void term_window_scroll(TERM_WINDOW *window, int count);
+
+void term_set_color(TERM_WINDOW *window, int col);
+
+void term_move(TERM_WINDOW *window, int x, int y);
+void term_addch(TERM_WINDOW *window, int chr);
+void term_addstr(TERM_WINDOW *window, const char *str);
+void term_clrtoeol(TERM_WINDOW *window);
+
+void term_move_cursor(int x, int y);
+
+void term_refresh_freeze(void);
+void term_refresh_thaw(void);
+void term_refresh(TERM_WINDOW *window);
+
+/* Automatically detach irssi when terminal is lost */
+void term_auto_detach(int set);
+void term_detach(void);
+void term_attach(FILE *in, FILE *out);
+
+void term_stop(void);
+int term_gets(unsigned char *buffer, int size);
+
+/* internal */
+void term_common_init(void);
+void term_common_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/fe-text/terminfo-core.c b/apps/irssi/src/fe-text/terminfo-core.c
new file mode 100644 (file)
index 0000000..21a7b0a
--- /dev/null
@@ -0,0 +1,658 @@
+#include "module.h"
+#include "signals.h"
+#include "terminfo-core.h"
+
+#ifndef _POSIX_VDISABLE
+#  define _POSIX_VDISABLE 0
+#endif
+
+#define tput(s) tputs(s, 0, term_putchar)
+inline static int term_putchar(int c)
+{
+        return fputc(c, current_term->out);
+}
+
+/* Don't bother including curses.h because of these -
+   they might not even be defined there */
+char *tparm();
+int tputs();
+
+#ifdef HAVE_TERMINFO
+int setupterm();
+char *tigetstr();
+int tigetnum();
+int tigetflag();
+#define term_getstr(x, buffer) tigetstr(x.ti_name)
+#define term_getnum(x) tigetnum(x.ti_name);
+#define term_getflag(x) tigetflag(x.ti_name);
+#else
+int tgetent();
+char *tgetstr();
+int tgetnum();
+int tgetflag();
+#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer)
+#define term_getnum(x) tgetnum(x.tc_name)
+#define term_getflag(x) tgetflag(x.tc_name)
+#endif
+
+#define CAP_TYPE_FLAG  0
+#define CAP_TYPE_INT   1
+#define CAP_TYPE_STR   2
+
+typedef struct {
+        const char *ti_name; /* terminfo name */
+       const char *tc_name; /* termcap name */
+       int type;
+        void *ptr;
+} TERMINFO_REC;
+
+TERM_REC *current_term;
+static TERM_REC temp_term; /* not really used for anything */
+
+/* Define only what we might need */
+static TERMINFO_REC tcaps[] = {
+        /* Terminal size */
+       { "cols",       "co",   CAP_TYPE_INT,   &temp_term.width },
+       { "lines",      "li",   CAP_TYPE_INT,   &temp_term.height },
+
+        /* Cursor movement */
+       { "smcup",      "ti",   CAP_TYPE_STR,   &temp_term.TI_smcup },
+       { "rmcup",      "te",   CAP_TYPE_STR,   &temp_term.TI_rmcup },
+       { "cup",        "cm",   CAP_TYPE_STR,   &temp_term.TI_cup },
+       { "hpa",        "ch",   CAP_TYPE_STR,   &temp_term.TI_hpa },
+       { "vpa",        "vh",   CAP_TYPE_STR,   &temp_term.TI_vpa },
+       { "cub1",       "le",   CAP_TYPE_STR,   &temp_term.TI_cub1 },
+       { "cuf1",       "nd",   CAP_TYPE_STR,   &temp_term.TI_cuf1 },
+       { "civis",      "vi",   CAP_TYPE_STR,   &temp_term.TI_civis },
+       { "cnorm",      "ve",   CAP_TYPE_STR,   &temp_term.TI_cnorm },
+
+        /* Scrolling */
+       { "csr",        "cs",   CAP_TYPE_STR,   &temp_term.TI_csr },
+       { "wind",       "wi",   CAP_TYPE_STR,   &temp_term.TI_wind },
+       { "ri",         "sr",   CAP_TYPE_STR,   &temp_term.TI_ri },
+       { "rin",        "SR",   CAP_TYPE_STR,   &temp_term.TI_rin },
+       { "ind",        "sf",   CAP_TYPE_STR,   &temp_term.TI_ind },
+       { "indn",       "SF",   CAP_TYPE_STR,   &temp_term.TI_indn },
+       { "il",         "AL",   CAP_TYPE_STR,   &temp_term.TI_il },
+       { "il1",        "al",   CAP_TYPE_STR,   &temp_term.TI_il1 },
+       { "dl",         "DL",   CAP_TYPE_STR,   &temp_term.TI_dl },
+       { "dl1",        "dl",   CAP_TYPE_STR,   &temp_term.TI_dl1 },
+
+       /* Clearing screen */
+       { "clear",      "cl",   CAP_TYPE_STR,   &temp_term.TI_clear },
+       { "ed",         "cd",   CAP_TYPE_STR,   &temp_term.TI_ed },
+
+        /* Clearing to end of line */
+       { "el",         "ce",   CAP_TYPE_STR,   &temp_term.TI_el },
+
+        /* Repeating character */
+       { "rep",        "rp",   CAP_TYPE_STR,   &temp_term.TI_rep },
+
+       /* Colors */
+       { "sgr0",       "me",   CAP_TYPE_STR,   &temp_term.TI_sgr0 },
+       { "smul",       "us",   CAP_TYPE_STR,   &temp_term.TI_smul },
+       { "rmul",       "ue",   CAP_TYPE_STR,   &temp_term.TI_rmul },
+       { "smso",       "so",   CAP_TYPE_STR,   &temp_term.TI_smso },
+       { "rmso",       "se",   CAP_TYPE_STR,   &temp_term.TI_rmso },
+       { "bold",       "md",   CAP_TYPE_STR,   &temp_term.TI_bold },
+       { "blink",      "mb",   CAP_TYPE_STR,   &temp_term.TI_blink },
+       { "setaf",      "AF",   CAP_TYPE_STR,   &temp_term.TI_setaf },
+       { "setab",      "AB",   CAP_TYPE_STR,   &temp_term.TI_setab },
+       { "setf",       "Sf",   CAP_TYPE_STR,   &temp_term.TI_setf },
+       { "setb",       "Sb",   CAP_TYPE_STR,   &temp_term.TI_setb },
+
+        /* Beep */
+       { "bel",        "bl",   CAP_TYPE_STR,   &temp_term.TI_bel },
+};
+
+/* Move cursor (cursor_address / cup) */
+static void _move_cup(TERM_REC *term, int x, int y)
+{
+       tput(tparm(term->TI_cup, y, x));
+}
+
+/* Move cursor (column_address+row_address / hpa+vpa) */
+static void _move_pa(TERM_REC *term, int x, int y)
+{
+       tput(tparm(term->TI_hpa, x));
+       tput(tparm(term->TI_vpa, y));
+}
+
+/* Move cursor from a known position */
+static void _move_relative(TERM_REC *term, int oldx, int oldy, int x, int y)
+{
+       if (oldx == 0 && x == 0 && y == oldy+1) {
+               /* move to beginning of next line -
+                  hope this works everywhere */
+               tput("\r\n");
+                return;
+       }
+
+       if (oldx > 0 && y == oldy) {
+                /* move cursor left/right */
+               if (x == oldx-1 && term->TI_cub1) {
+                       tput(tparm(term->TI_cub1));
+                        return;
+               }
+               if (x == oldx+1 && y == oldy && term->TI_cuf1) {
+                       tput(tparm(term->TI_cuf1));
+                        return;
+               }
+       }
+
+        /* fallback to absolute positioning */
+       if (term->TI_cup) {
+               tput(tparm(term->TI_cup, y, x));
+                return;
+       }
+
+       if (oldy != y)
+               tput(tparm(term->TI_vpa, y));
+        if (oldx != x)
+               tput(tparm(term->TI_hpa, x));
+}
+
+/* Set cursor visible/invisible */
+static void _set_cursor_visible(TERM_REC *term, int set)
+{
+       tput(tparm(set ? term->TI_cnorm : term->TI_civis));
+}
+
+#define scroll_region_setup(term, y1, y2) \
+       if ((term)->TI_csr != NULL) \
+               tput(tparm((term)->TI_csr, y1, y2)); \
+       else if ((term)->TI_wind != NULL) \
+               tput(tparm((term)->TI_wind, y1, y2, 0, (term)->width-1));
+
+/* Scroll (change_scroll_region+parm_rindex+parm_index / csr+rin+indn) */
+static void _scroll_region(TERM_REC *term, int y1, int y2, int count)
+{
+        /* setup the scrolling region to wanted area */
+        scroll_region_setup(term, y1, y2);
+
+       term->move(term, 0, y1);
+       if (count > 0) {
+               term->move(term, 0, y2);
+               tput(tparm(term->TI_indn, count, count));
+       } else if (count < 0) {
+               term->move(term, 0, y1);
+               tput(tparm(term->TI_rin, -count, -count));
+       }
+
+        /* reset the scrolling region to full screen */
+        scroll_region_setup(term, 0, term->height-1);
+}
+
+/* Scroll (change_scroll_region+scroll_reverse+scroll_forward / csr+ri+ind) */
+static void _scroll_region_1(TERM_REC *term, int y1, int y2, int count)
+{
+       int i;
+
+        /* setup the scrolling region to wanted area */
+        scroll_region_setup(term, y1, y2);
+
+       if (count > 0) {
+               term->move(term, 0, y2);
+               for (i = 0; i < count; i++)
+                       tput(tparm(term->TI_ind));
+       } else if (count < 0) {
+               term->move(term, 0, y1);
+               for (i = count; i < 0; i++)
+                       tput(tparm(term->TI_ri));
+               tput(tparm(term->TI_rin, -count, -count));
+       }
+
+        /* reset the scrolling region to full screen */
+        scroll_region_setup(term, 0, term->height-1);
+}
+
+/* Scroll (parm_insert_line+parm_delete_line / il+dl) */
+static void _scroll_line(TERM_REC *term, int y1, int y2, int count)
+{
+       /* setup the scrolling region to wanted area -
+          this might not necessarily work with il/dl, but at least it
+          looks better if it does */
+        scroll_region_setup(term, y1, y2);
+
+       if (count > 0) {
+               term->move(term, 0, y1);
+               tput(tparm(term->TI_dl, count, count));
+               term->move(term, 0, y2-count+1);
+               tput(tparm(term->TI_il, count, count));
+       } else if (count < 0) {
+               term->move(term, 0, y2+count+1);
+               tput(tparm(term->TI_dl, -count, -count));
+               term->move(term, 0, y1);
+               tput(tparm(term->TI_il, -count, -count));
+       }
+
+        /* reset the scrolling region to full screen */
+        scroll_region_setup(term, 0, term->height-1);
+}
+
+/* Scroll (insert_line+delete_line / il1+dl1) */
+static void _scroll_line_1(TERM_REC *term, int y1, int y2, int count)
+{
+       int i;
+
+       if (count > 0) {
+               term->move(term, 0, y1);
+                for (i = 0; i < count; i++)
+                       tput(tparm(term->TI_dl1));
+               term->move(term, 0, y2-count+1);
+                for (i = 0; i < count; i++)
+                       tput(tparm(term->TI_il1));
+       } else if (count < 0) {
+               term->move(term, 0, y2+count+1);
+               for (i = count; i < 0; i++)
+                       tput(tparm(term->TI_dl1));
+               term->move(term, 0, y1);
+               for (i = count; i < 0; i++)
+                       tput(tparm(term->TI_il1));
+       }
+}
+
+/* Clear screen (clear_screen / clear) */
+static void _clear_screen(TERM_REC *term)
+{
+       tput(tparm(term->TI_clear));
+}
+
+/* Clear screen (clr_eos / ed) */
+static void _clear_eos(TERM_REC *term)
+{
+        term->move(term, 0, 0);
+       tput(tparm(term->TI_ed));
+}
+
+/* Clear screen (parm_delete_line / dl) */
+static void _clear_del(TERM_REC *term)
+{
+        term->move(term, 0, 0);
+       tput(tparm(term->TI_dl, term->height, term->height));
+}
+
+/* Clear screen (delete_line / dl1) */
+static void _clear_del_1(TERM_REC *term)
+{
+       int i;
+
+       term->move(term, 0, 0);
+        for (i = 0; i < term->height; i++)
+               tput(tparm(term->TI_dl1));
+}
+
+/* Clear to end of line (clr_eol / el) */
+static void _clrtoeol(TERM_REC *term)
+{
+       tput(tparm(term->TI_el));
+}
+
+/* Repeat character (rep / rp) */
+static void _repeat(TERM_REC *term, int chr, int count)
+{
+       tput(tparm(term->TI_rep, chr, count));
+}
+
+/* Repeat character (manual) */
+static void _repeat_manual(TERM_REC *term, int chr, int count)
+{
+       while (count > 0) {
+               putc(chr, term->out);
+               count--;
+       }
+}
+
+/* Reset all terminal attributes */
+static void _set_normal(TERM_REC *term)
+{
+       tput(tparm(term->TI_normal));
+}
+
+/* Bold on */
+static void _set_bold(TERM_REC *term)
+{
+       tput(tparm(term->TI_bold));
+}
+
+/* Underline on/off */
+static void _set_uline(TERM_REC *term, int set)
+{
+       tput(tparm(set ? term->TI_smul : term->TI_rmul));
+}
+
+/* Standout on/off */
+static void _set_standout(TERM_REC *term, int set)
+{
+       tput(tparm(set ? term->TI_smso : term->TI_rmso));
+}
+
+/* Change foreground color */
+static void _set_fg(TERM_REC *term, int color)
+{
+       tput(tparm(term->TI_fg[color & 0x0f]));
+}
+
+/* Change background color */
+static void _set_bg(TERM_REC *term, int color)
+{
+       tput(tparm(term->TI_bg[color & 0x0f]));
+}
+
+/* Beep */
+static void _beep(TERM_REC *term)
+{
+       tput(tparm(term->TI_bel));
+}
+
+static void _ignore(TERM_REC *term)
+{
+}
+
+static void _ignore_parm(TERM_REC *term, int param)
+{
+}
+
+static void term_fill_capabilities(TERM_REC *term)
+{
+       int i, ival;
+       char *sval;
+        void *ptr;
+
+#ifndef HAVE_TERMINFO
+       char *tptr = term->buffer2;
+#endif
+       for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) {
+               ptr = (char *) term + (int) ((char *) tcaps[i].ptr - (char *) &temp_term);
+
+               switch (tcaps[i].type) {
+               case CAP_TYPE_FLAG:
+                       ival = term_getflag(tcaps[i]);
+                        *(int *)ptr = ival;
+                        break;
+               case CAP_TYPE_INT:
+                       ival = term_getnum(tcaps[i]);
+                        *(int *)ptr = ival;
+                        break;
+               case CAP_TYPE_STR:
+                       sval = term_getstr(tcaps[i], tptr);
+                       if (sval == (char *) -1)
+                               *(char **)ptr = NULL;
+                       else
+                               *(char **)ptr = sval;
+                        break;
+               }
+       }
+}
+
+/* Terminal was resized - ask the width/height from terminfo again */
+void terminfo_resize(TERM_REC *term)
+{
+       /* FIXME: is this possible? */
+}
+
+static void terminfo_colors_deinit(TERM_REC *term)
+{
+       int i;
+
+       if (terminfo_is_colors_set(term)) {
+               for (i = 0; i < 16; i++) {
+                       g_free(term->TI_fg[i]);
+                       g_free(term->TI_bg[i]);
+               }
+
+                memset(term->TI_fg, 0, sizeof(term->TI_fg));
+                memset(term->TI_bg, 0, sizeof(term->TI_fg));
+       }
+}
+
+/* Setup colors - if force is set, use ANSI-style colors if
+   terminal capabilities don't contain color codes */
+void terminfo_setup_colors(TERM_REC *term, int force)
+{
+       static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+       const char *bold, *blink;
+       int i;
+
+       terminfo_colors_deinit(term);
+        term->has_colors = term->TI_setf || term->TI_setaf;
+
+       if (term->TI_setf) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0));
+       } else if (term->TI_setaf) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup(tparm(term->TI_setaf, ansitab[i], 0));
+       } else if (force) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup_printf("\033[%dm", 30+ansitab[i]);
+       }
+
+       if (term->TI_setb) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0));
+       } else if (term->TI_setab) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup(tparm(term->TI_setab, ansitab[i], 0));
+       } else if (force) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup_printf("\033[%dm", 40+ansitab[i]);
+       }
+
+       if (term->TI_setf || term->TI_setaf || force) {
+                term->set_fg = _set_fg;
+                term->set_bg = _set_bg;
+
+               /* bold fg, blink bg */
+               bold = term->TI_bold ? term->TI_bold : "";
+               for (i = 0; i < 8; i++)
+                       term->TI_fg[i+8] = g_strconcat(bold, term->TI_fg[i], NULL);
+
+               blink = term->TI_blink ? term->TI_blink : "";
+               for (i = 0; i < 8; i++)
+                       term->TI_bg[i+8] = g_strconcat(blink, term->TI_bg[i], NULL);
+       } else {
+               /* no colors */
+                term->set_fg = term->set_bg = _ignore_parm;
+       }
+}
+
+static void terminfo_input_init(TERM_REC *term)
+{
+       tcgetattr(fileno(term->in), &term->old_tio);
+       memcpy(&term->tio, &term->old_tio, sizeof(term->tio));
+
+       term->tio.c_lflag &= ~(ICANON | ECHO); /* CBREAK, no ECHO */
+       term->tio.c_cc[VMIN] = 1; /* read() is satisfied after 1 char */
+       term->tio.c_cc[VTIME] = 0; /* No timer */
+
+        /* Disable INTR, QUIT, VDSUSP and SUSP keys */
+       term->tio.c_cc[VINTR] = _POSIX_VDISABLE;
+       term->tio.c_cc[VQUIT] = _POSIX_VDISABLE;
+#ifdef VDSUSP
+       term->tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+#ifdef VSUSP
+       term->tio.c_cc[VSUSP] = _POSIX_VDISABLE;
+#endif
+
+        tcsetattr(fileno(term->in), TCSADRAIN, &term->tio);
+
+}
+
+static void terminfo_input_deinit(TERM_REC *term)
+{
+        tcsetattr(fileno(term->in), TCSADRAIN, &term->old_tio);
+}
+
+void terminfo_cont(TERM_REC *term)
+{
+       if (term->TI_smcup)
+                tput(tparm(term->TI_smcup));
+        terminfo_input_init(term);
+}
+
+void terminfo_stop(TERM_REC *term)
+{
+        /* reset colors */
+       terminfo_set_normal();
+        /* move cursor to bottom of the screen */
+       terminfo_move(0, term->height-1);
+
+       /* stop cup-mode */
+       if (term->TI_rmcup)
+               tput(tparm(term->TI_rmcup));
+
+        /* reset input settings */
+       terminfo_input_deinit(term);
+        fflush(term->out);
+}
+
+static int term_setup(TERM_REC *term)
+{
+       GString *str;
+#ifdef HAVE_TERMINFO
+       int err;
+#endif
+        char *term_env;
+
+       term_env = getenv("TERM");
+       if (term_env == NULL) {
+               fprintf(term->out, "TERM environment not set\n");
+                return 0;
+       }
+
+#ifdef HAVE_TERMINFO
+       if (setupterm(term_env, 1, &err) != 0) {
+               fprintf(term->out, "setupterm() failed for TERM=%s: %d\n", term_env, err);
+               return 0;
+       }
+#else
+       if (tgetent(term->buffer1, term_env) < 1)
+       {
+               fprintf(term->out, "Termcap not found for TERM=%s\n", term_env);
+               return -1;
+       }
+#endif
+
+        term_fill_capabilities(term);
+
+       /* Cursor movement */
+       if (term->TI_cup)
+               term->move = _move_cup;
+       else if (term->TI_hpa && term->TI_vpa)
+               term->move = _move_pa;
+       else {
+                fprintf(term->out, "Terminal doesn't support cursor movement\n");
+               return 0;
+       }
+       term->move_relative = _move_relative;
+       term->set_cursor_visible = term->TI_civis && term->TI_cnorm ?
+               _set_cursor_visible : _ignore_parm;
+
+        /* Scrolling */
+       if ((term->TI_csr || term->TI_wind) && term->TI_rin && term->TI_indn)
+               term->scroll = _scroll_region;
+       else if (term->TI_il && term->TI_dl)
+               term->scroll = _scroll_line;
+       else if ((term->TI_csr || term->TI_wind) && term->TI_ri && term->TI_ind)
+               term->scroll = _scroll_region_1;
+       else if (term->scroll == NULL && (term->TI_il1 && term->TI_dl1))
+               term->scroll = _scroll_line_1;
+       else if (term->scroll == NULL) {
+                fprintf(term->out, "Terminal doesn't support scrolling\n");
+               return 0;
+       }
+
+       /* Clearing screen */
+       if (term->TI_clear)
+               term->clear = _clear_screen;
+       else if (term->TI_ed)
+               term->clear = _clear_eos;
+       else if (term->TI_dl)
+               term->clear = _clear_del;
+       else if (term->TI_dl1)
+               term->clear = _clear_del_1;
+       else {
+               /* we could do this by line inserts as well, but don't
+                  bother - if some terminal has insert line it most probably
+                  has delete line as well, if not a regular clear screen */
+                fprintf(term->out, "Terminal doesn't support clearing screen\n");
+               return 0;
+       }
+
+       /* Clearing to end of line */
+       if (term->TI_el)
+               term->clrtoeol = _clrtoeol;
+       else {
+                fprintf(term->out, "Terminal doesn't support clearing to end of line\n");
+               return 0;
+       }
+
+       /* Repeating character */
+       if (term->TI_rep)
+               term->repeat = _repeat;
+       else
+               term->repeat = _repeat_manual;
+
+       /* Bold, underline, standout */
+       term->set_bold = term->TI_bold ? _set_bold : _ignore;
+       term->set_uline = term->TI_smul && term->TI_rmul ?
+               _set_uline : _ignore_parm;
+       term->set_standout = term->TI_smso && term->TI_rmso ?
+               _set_standout : _ignore_parm;
+
+        /* Create a string to set all attributes off */
+        str = g_string_new(NULL);
+       if (term->TI_sgr0)
+               g_string_append(str, term->TI_sgr0);
+       if (term->TI_rmul && (term->TI_sgr0 == NULL || strcmp(term->TI_rmul, term->TI_sgr0) != 0))
+               g_string_append(str, term->TI_rmul);
+       if (term->TI_rmso && (term->TI_sgr0 == NULL || strcmp(term->TI_rmso, term->TI_sgr0) != 0))
+               g_string_append(str, term->TI_rmso);
+        term->TI_normal = str->str;
+       g_string_free(str, FALSE);
+        term->set_normal = _set_normal;
+
+       term->beep = term->TI_bel ? _beep : _ignore;
+
+       terminfo_setup_colors(term, FALSE);
+        terminfo_cont(term);
+        return 1;
+}
+
+TERM_REC *terminfo_core_init(FILE *in, FILE *out)
+{
+       TERM_REC *old_term, *term;
+
+        old_term = current_term;
+       current_term = term = g_new0(TERM_REC, 1);
+
+       term->in = in;
+       term->out = out;
+
+       if (!term_setup(term)) {
+               g_free(term);
+                term = NULL;
+       }
+
+       current_term = old_term;
+        return term;
+}
+
+void terminfo_core_deinit(TERM_REC *term)
+{
+       TERM_REC *old_term;
+
+       old_term = current_term;
+        current_term = term;
+       term->set_normal(term);
+        current_term = old_term;
+
+        terminfo_stop(term);
+
+       g_free(term->TI_normal);
+       terminfo_colors_deinit(term);
+
+        g_free(term);
+}
diff --git a/apps/irssi/src/fe-text/terminfo-core.h b/apps/irssi/src/fe-text/terminfo-core.h
new file mode 100644 (file)
index 0000000..93afa78
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef __TERMINFO_CORE_H
+#define __TERMINFO_CORE_H
+
+#include <termios.h>
+
+#define terminfo_move(x, y) current_term->move(current_term, x, y)
+#define terminfo_move_relative(oldx, oldy, x, y) current_term->move_relative(current_term, oldx, oldy, x, y)
+#define terminfo_set_cursor_visible(set) current_term->set_cursor_visible(current_term, set)
+#define terminfo_scroll(y1, y2, count) current_term->scroll(current_term, y1, y2, count)
+#define terminfo_clear() current_term->clear(current_term)
+#define terminfo_clrtoeol() current_term->clrtoeol(current_term)
+#define terminfo_repeat(chr, count) current_term->repeat(current_term, chr, count)
+#define terminfo_set_fg(color) current_term->set_fg(current_term, color)
+#define terminfo_set_bg(color) current_term->set_bg(current_term, color)
+#define terminfo_set_normal() current_term->set_normal(current_term)
+#define terminfo_set_bold() current_term->set_bold(current_term)
+#define terminfo_set_uline(set) current_term->set_uline(current_term, set)
+#define terminfo_set_standout(set) current_term->set_standout(current_term, set)
+#define terminfo_is_colors_set(term) (term->TI_fg[0] != NULL)
+#define terminfo_beep(term) current_term->beep(current_term)
+
+typedef struct _TERM_REC TERM_REC;
+
+struct _TERM_REC {
+        /* Functions */
+       void (*move)(TERM_REC *term, int x, int y);
+       void (*move_relative)(TERM_REC *term, int oldx, int oldy, int x, int y);
+       void (*set_cursor_visible)(TERM_REC *term, int set);
+       void (*scroll)(TERM_REC *term, int y1, int y2, int count);
+
+        void (*clear)(TERM_REC *term);
+       void (*clrtoeol)(TERM_REC *term);
+       void (*repeat)(TERM_REC *term, int chr, int count);
+
+       void (*set_fg)(TERM_REC *term, int color);
+       void (*set_bg)(TERM_REC *term, int color);
+       void (*set_normal)(TERM_REC *term);
+       void (*set_bold)(TERM_REC *term);
+       void (*set_uline)(TERM_REC *term, int set);
+       void (*set_standout)(TERM_REC *term, int set);
+
+        void (*beep)(TERM_REC *term);
+
+#ifndef HAVE_TERMINFO
+       char buffer1[1024], buffer2[1024];
+#endif
+       FILE *in, *out;
+       struct termios tio, old_tio;
+
+        /* Terminal size */
+        int width, height;
+
+        /* Cursor movement */
+       const char *TI_smcup, *TI_rmcup, *TI_cup;
+       const char *TI_hpa, *TI_vpa, *TI_cub1, *TI_cuf1;
+        const char *TI_civis, *TI_cnorm;
+
+       /* Scrolling */
+       const char *TI_csr, *TI_wind;
+       const char *TI_ri, *TI_rin, *TI_ind, *TI_indn;
+       const char *TI_il, *TI_il1, *TI_dl, *TI_dl1;
+
+       /* Clearing screen */
+       const char *TI_clear, *TI_ed; /* + *TI_dl, *TI_dl1; */
+
+        /* Clearing to end of line */
+       const char *TI_el;
+
+       /* Repeating character */
+       const char *TI_rep;
+
+       /* Colors */
+        int has_colors;
+       const char *TI_sgr0; /* turn off all attributes */
+       const char *TI_smul, *TI_rmul; /* underline on/off */
+        const char *TI_smso, *TI_rmso; /* standout on/off */
+        const char *TI_bold, *TI_blink;
+       const char *TI_setaf, *TI_setab, *TI_setf, *TI_setb;
+
+        /* Colors - generated and dynamically allocated */
+       char *TI_fg[16], *TI_bg[16], *TI_normal;
+
+       /* Beep */
+        char *TI_bel;
+};
+
+extern TERM_REC *current_term;
+
+TERM_REC *terminfo_core_init(FILE *in, FILE *out);
+void terminfo_core_deinit(TERM_REC *term);
+
+/* Setup colors - if force is set, use ANSI-style colors if
+   terminal capabilities don't contain color codes */
+void terminfo_setup_colors(TERM_REC *term, int force);
+
+/* Terminal was resized - ask the width/height from terminfo again */
+void terminfo_resize(TERM_REC *term);
+
+void terminfo_cont(TERM_REC *term);
+void terminfo_stop(TERM_REC *term);
+
+#endif
index 92ec70bdddfe80aeb2455d3a90d2df69426de1c7..db5bc782f93aef69e9db49cf742ad40fd60a52d9 100644 (file)
 */
 
 #include "module.h"
+#include "module-formats.h"
 #include "signals.h"
 #include "commands.h"
 #include "misc.h"
 #include "levels.h"
+#include "settings.h"
+#include "servers.h"
 
 #include "printtext.h"
 #include "gui-windows.h"
+#include "textbuffer-reformat.h"
 
 /* SYNTAX: CLEAR */
 static void cmd_clear(const char *data)
@@ -54,6 +58,32 @@ static void cmd_clear(const char *data)
        cmd_params_free(free_arg);
 }
 
+static void cmd_window_scroll(const char *data)
+{
+       GUI_WINDOW_REC *gui;
+
+       gui = WINDOW_GUI(active_win);
+       if (g_strcasecmp(data, "default") == 0) {
+                gui->use_scroll = FALSE;
+       } else if (g_strcasecmp(data, "on") == 0) {
+               gui->use_scroll = TRUE;
+               gui->scroll = TRUE;
+       } else if (g_strcasecmp(data, "off") == 0) {
+               gui->use_scroll = TRUE;
+               gui->scroll = FALSE;
+       } else if (*data != '\0') {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_WINDOW_SCROLL_UNKNOWN, data);
+                return;
+       }
+
+       printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                          TXT_WINDOW_SCROLL, !gui->use_scroll ? "DEFAULT" :
+                          gui->scroll ? "ON" : "OFF");
+       textbuffer_view_set_scroll(gui->view, gui->use_scroll ?
+                                  gui->scroll : settings_get_bool("scroll"));
+}
+
 static void cmd_scrollback(const char *data, SERVER_REC *server,
                           WI_ITEM_REC *item)
 {
@@ -74,13 +104,13 @@ static void scrollback_goto_line(int linenum)
        if (view->buffer->lines_count == 0)
                return;
 
-       textbuffer_view_scroll_line(view, view->buffer->lines->data);
+       textbuffer_view_scroll_line(view, view->buffer->first_line);
        gui_window_scroll(active_win, linenum);
 }
 
 static void scrollback_goto_time(const char *datearg, const char *timearg)
 {
-        GList *tmp;
+        LINE_REC *line;
        struct tm tm;
        time_t now, stamp;
        int day, month;
@@ -145,12 +175,10 @@ static void scrollback_goto_time(const char *datearg, const char *timearg)
        }
 
        /* scroll to first line after timestamp */
-        tmp = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view);
-       for (; tmp != NULL; tmp = tmp->next) {
-               LINE_REC *rec = tmp->data;
-
-               if (rec->info.time >= stamp) {
-                       gui_window_scroll_line(active_win, rec);
+       line = textbuffer_view_get_lines(WINDOW_GUI(active_win)->view);
+       for (; line != NULL; line = line->next) {
+               if (line->info.time >= stamp) {
+                       gui_window_scroll_line(active_win, line);
                        break;
                }
        }
@@ -188,7 +216,7 @@ static void cmd_scrollback_home(const char *data)
 
        buffer = WINDOW_GUI(active_win)->view->buffer;
        if (buffer->lines_count > 0)
-               gui_window_scroll_line(active_win, buffer->lines->data);
+               gui_window_scroll_line(active_win, buffer->first_line);
 }
 
 /* SYNTAX: SCROLLBACK END */
@@ -200,28 +228,28 @@ static void cmd_scrollback_end(const char *data)
        if (view->bottom_startline == NULL)
                return;
 
-       textbuffer_view_scroll_line(view, view->bottom_startline->data);
+       textbuffer_view_scroll_line(view, view->bottom_startline);
        gui_window_scroll(active_win, view->bottom_subline);
 }
 
 /* SYNTAX: SCROLLBACK REDRAW */
 static void cmd_scrollback_redraw(void)
 {
-#if 0
        GUI_WINDOW_REC *gui;
-       GList *tmp, *next;
+       LINE_REC *line, *next;
 
        gui = WINDOW_GUI(active_win);
 
-       screen_refresh_freeze();
-       for (tmp = gui->lines; tmp != NULL; tmp = next) {
-               next = tmp->next;
-               gui_window_reformat_line(active_win, tmp->data);
+       term_refresh_freeze();
+       line = textbuffer_view_get_lines(gui->view);
+       while (line != NULL) {
+               next = line->next;
+               textbuffer_reformat_line(active_win, line);
+                line = next;
        }
 
        gui_window_redraw(active_win);
-       screen_refresh_thaw();
-#endif
+       term_refresh_thaw();
 }
 
 static void cmd_scrollback_status(void)
@@ -269,6 +297,7 @@ static void sig_away_changed(SERVER_REC *server)
 void textbuffer_commands_init(void)
 {
        command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
+       command_bind("window scroll", NULL, (SIGNAL_FUNC) cmd_window_scroll);
        command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
        command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
        command_bind("scrollback goto", NULL, (SIGNAL_FUNC) cmd_scrollback_goto);
@@ -285,6 +314,7 @@ void textbuffer_commands_init(void)
 void textbuffer_commands_deinit(void)
 {
        command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
+       command_unbind("window scroll", (SIGNAL_FUNC) cmd_window_scroll);
        command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
        command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
        command_unbind("scrollback goto", (SIGNAL_FUNC) cmd_scrollback_goto);
diff --git a/apps/irssi/src/fe-text/textbuffer-reformat.c b/apps/irssi/src/fe-text/textbuffer-reformat.c
new file mode 100644 (file)
index 0000000..da75a5a
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ textbuffer-reformat.c : Reformatting lines in text buffer
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "settings.h"
+
+#include "formats.h"
+
+#include "gui-windows.h"
+#include "gui-printtext.h"
+#include "textbuffer.h"
+
+static GString *format;
+static int scrollback_save_formats;
+
+/* Read one block between \0<format>s */
+static char *line_read_format(unsigned const char **text)
+{
+       GString *str;
+       char *ret;
+
+       str = g_string_new(NULL);
+       for (;;) {
+               if (**text == '\0') {
+                       if ((*text)[1] == LINE_CMD_EOL) {
+                               /* leave text at \0<eof> */
+                               break;
+                       }
+                       if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
+                               /* leave text at \0<format_cont> */
+                               break;
+                       }
+                       (*text)++;
+
+                        if (**text == LINE_CMD_FORMAT) {
+                               /* move text to start after \0<format> */
+                               (*text)++;
+                               break;
+                       }
+
+                       if (**text == LINE_CMD_CONTINUE) {
+                               unsigned char *tmp;
+
+                               memcpy(&tmp, (*text)+1, sizeof(char *));
+                               *text = tmp;
+                               continue;
+                       } else if (**text & 0x80)
+                               (*text)++;
+                       continue;
+               }
+
+               g_string_append_c(str, (char) **text);
+               (*text)++;
+       }
+
+       ret = str->str;
+       g_string_free(str, FALSE);
+       return ret;
+}
+
+static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
+                                       GString *raw)
+{
+       const unsigned char *text;
+       char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
+       TEXT_DEST_REC dest;
+       int formatnum, argcount;
+
+       text = (const unsigned char *) line->text;
+
+       /* skip the beginning of the line until we find the format */
+       g_free(line_read_format(&text));
+       if (text[1] == LINE_CMD_FORMAT_CONT) {
+               if (raw != NULL) {
+                       g_string_append_c(raw, '\0');
+                       g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
+               }
+               return NULL;
+       }
+
+       /* read format information */
+        module = line_read_format(&text);
+       format_name = line_read_format(&text);
+
+       if (raw != NULL) {
+               g_string_append_c(raw, '\0');
+               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+
+               g_string_append(raw, module);
+
+               g_string_append_c(raw, '\0');
+               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
+
+               g_string_append(raw, format_name);
+       }
+
+       formatnum = format_find_tag(module, format_name);
+       if (formatnum == -1)
+               ret = NULL;
+       else {
+                argcount = 0;
+                memset(args, 0, sizeof(args));
+               while (*text != '\0' || text[1] != LINE_CMD_EOL) {
+                       args[argcount] = line_read_format(&text);
+                       if (raw != NULL) {
+                               g_string_append_c(raw, '\0');
+                               g_string_append_c(raw,
+                                                 (char)LINE_CMD_FORMAT);
+
+                               g_string_append(raw, args[argcount]);
+                       }
+                       argcount++;
+               }
+
+               /* get the format text */
+               format_create_dest(&dest, NULL, NULL, line->info.level, window);
+               ret = format_get_text_theme_charargs(current_theme,
+                                                    module, &dest,
+                                                    formatnum, args);
+               while (argcount > 0)
+                       g_free(args[--argcount]);
+       }
+
+       g_free(module);
+       g_free(format_name);
+
+       return ret;
+}
+
+void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line)
+{
+        GUI_WINDOW_REC *gui;
+       TEXT_DEST_REC dest;
+       LINE_REC *line_prev;
+        LINE_INFO_REC line_info;
+       GString *raw;
+       char *str, *tmp, *prestr, *linestart, *leveltag;
+
+        gui = WINDOW_GUI(window);
+
+       raw = g_string_new(NULL);
+       str = textbuffer_line_get_format(window, line, raw);
+
+        if (str == NULL && raw->len == 2 &&
+            raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
+                /* multiline format, format explained in one the
+                   following lines. remove this line. */
+                textbuffer_view_remove_line(gui->view, line);
+       } else if (str != NULL) {
+                /* FIXME: ugly ugly .. and this can't handle
+                   unformatted lines.. */
+               g_string_append_c(raw, '\0');
+               g_string_append_c(raw, (char)LINE_CMD_EOL);
+
+               line_prev = line->prev;
+                memcpy(&line_info, &line->info, sizeof(line_info));
+                textbuffer_view_remove_line(gui->view, line); line = NULL;
+
+               format_create_dest(&dest, NULL, NULL, line_info.level, window);
+
+               linestart = format_get_line_start(current_theme, &dest, line_info.time);
+               leveltag = format_get_level_tag(current_theme, &dest);
+
+               prestr = g_strconcat(linestart == NULL ? "" : linestart,
+                                    leveltag, NULL);
+               g_free_not_null(linestart);
+               g_free_not_null(leveltag);
+
+               tmp = format_add_linestart(str, prestr);
+               g_free(str);
+               g_free(prestr);
+
+                gui_printtext_after(&dest, line_prev, tmp);
+               g_free(tmp);
+
+                line = textbuffer_insert(gui->view->buffer, gui->insert_after,
+                                        (unsigned char *) raw->str,
+                                        raw->len, &line_info);
+               textbuffer_view_insert_line(gui->view, line);
+       }
+       g_string_free(raw, TRUE);
+}
+
+static void sig_print_format(THEME_REC *theme, const char *module,
+                            TEXT_DEST_REC *dest, void *formatnump,
+                            char **args)
+{
+       FORMAT_REC *formats;
+       int formatnum, n;
+
+       if (!scrollback_save_formats)
+               return;
+
+       formatnum = GPOINTER_TO_INT(formatnump);
+       formats = g_hash_table_lookup(default_formats, module);
+
+       /* <module><format_name><arg...> */
+       g_string_truncate(format, 0);
+
+       g_string_append_c(format, '\0');
+       g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+        g_string_append(format, module);
+
+       g_string_append_c(format, '\0');
+       g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+       g_string_append(format, formats[formatnum].tag);
+
+       for (n = 0; n < formats[formatnum].params; n++) {
+               g_string_append_c(format, '\0');
+               g_string_append_c(format, (char)LINE_CMD_FORMAT);
+
+               if (args[n] != NULL)
+                       g_string_append(format, args[n]);
+       }
+}
+
+static void sig_gui_printtext_finished(WINDOW_REC *window)
+{
+       GUI_WINDOW_REC *gui;
+        LINE_REC *insert_after;
+
+       if (format->len == 0)
+                return;
+
+       /* save format of the line */
+        gui = WINDOW_GUI(window);
+       insert_after = gui->use_insert_after ?
+               gui->insert_after : gui->view->buffer->cur_line;
+
+       textbuffer_insert(gui->view->buffer, insert_after,
+                         (unsigned char *) format->str,
+                         format->len, NULL);
+
+       g_string_truncate(format, 0);
+}
+
+static void read_settings(void)
+{
+        scrollback_save_formats = settings_get_bool("scrollback_save_formats");
+}
+
+void textbuffer_reformat_init(void)
+{
+       format = g_string_new(NULL);
+       settings_add_bool("history", "scrollback_save_formats", FALSE);
+
+        read_settings();
+       signal_add("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_add_first("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void textbuffer_reformat_deinit(void)
+{
+       g_string_free(format, TRUE);
+
+       signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
+       signal_remove("print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+}
diff --git a/apps/irssi/src/fe-text/textbuffer-reformat.h b/apps/irssi/src/fe-text/textbuffer-reformat.h
new file mode 100644 (file)
index 0000000..2818ec6
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __TEXTBUFFER_REFORMAT_H
+#define __TEXTBUFFER_REFORMAT_H
+
+void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line);
+
+void textbuffer_reformat_init(void);
+void textbuffer_reformat_deinit(void);
+
+#endif
index f3a74080f4d0d5bb12ebaa931bfcfd203f299acf..d98fc85c759e38a234dd7e0d5b4bedd754aa3fa2 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "module.h"
 #include "textbuffer-view.h"
-#include "screen.h"
+#include "utf8.h"
 
 typedef struct {
        char *name;
@@ -102,21 +102,64 @@ static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache)
                 textbuffer_cache_destroy(cache);
 }
 
+#define FGATTR (ATTR_NOCOLORS | ATTR_RESETFG | ATTR_BOLD | 0x0f)
+#define BGATTR (ATTR_NOCOLORS | ATTR_RESETBG | ATTR_BLINK | 0xf0)
+
+static void update_cmd_color(unsigned char cmd, int *color)
+{
+       if ((cmd & 0x80) == 0) {
+               if (cmd & LINE_COLOR_BG) {
+                       /* set background color */
+                       *color &= FGATTR;
+                       if ((cmd & LINE_COLOR_DEFAULT) == 0)
+                               *color |= (cmd & 0x0f) << 4;
+                       else {
+                               *color = (*color & FGATTR) | ATTR_RESETBG;
+                                if (cmd & LINE_COLOR_BLINK)
+                                       *color |= ATTR_BLINK;
+                       }
+               } else {
+                       /* set foreground color */
+                       *color &= BGATTR;
+                       if ((cmd & LINE_COLOR_DEFAULT) == 0)
+                               *color |= cmd & 0x0f;
+                       else {
+                               *color = (*color & BGATTR) | ATTR_RESETFG;
+                                if (cmd & LINE_COLOR_BOLD)
+                                       *color |= ATTR_BOLD;
+                       }
+               }
+       } else switch (cmd) {
+       case LINE_CMD_UNDERLINE:
+               *color ^= ATTR_UNDERLINE;
+               break;
+       case LINE_CMD_REVERSE:
+               *color ^= ATTR_REVERSE;
+               break;
+       case LINE_CMD_COLOR0:
+               *color &= BGATTR;
+               break;
+       }
+}
+
 static LINE_CACHE_REC *
 view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
+        INDENT_FUNC indent_func;
        LINE_CACHE_REC *rec;
        LINE_CACHE_SUB_REC *sub;
        GSList *lines;
         unsigned char cmd;
-       char *ptr, *last_space_ptr;
+       const unsigned char *ptr, *last_space_ptr;
        int xpos, pos, indent_pos, last_space, last_color, color, linecount;
 
        g_return_val_if_fail(line->text != NULL, NULL);
 
-       xpos = 0; color = 0; indent_pos = view->default_indent;
+       color = ATTR_RESETFG | ATTR_RESETBG;
+       xpos = 0; indent_pos = view->default_indent;
        last_space = last_color = 0; last_space_ptr = NULL; sub = NULL;
 
+        indent_func = view->default_indent_func;
         linecount = 1;
        lines = NULL;
        for (ptr = line->text;;) {
@@ -130,49 +173,38 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                                break;
 
                        if (cmd == LINE_CMD_CONTINUE) {
-                               char *tmp;
+                               unsigned char *tmp;
 
                                memcpy(&tmp, ptr, sizeof(char *));
                                ptr = tmp;
                                continue;
                        }
 
-                       if ((cmd & 0x80) == 0) {
-                               /* set color */
-                               color = (color & ATTR_UNDERLINE) | cmd;
-                       } else switch (cmd) {
-                       case LINE_CMD_UNDERLINE:
-                               color ^= ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR0:
-                               color = color & ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR8:
-                               color &= 0xfff0;
-                               color |= 8|ATTR_COLOR8;
-                               break;
-                       case LINE_CMD_BLINK:
-                               color |= 0x80;
-                               break;
-                       case LINE_CMD_INDENT:
+                       if (cmd == LINE_CMD_INDENT) {
                                /* set indentation position here - don't do
                                   it if we're too close to right border */
                                if (xpos < view->width-5) indent_pos = xpos;
-                               break;
-                       }
+                       } else if (cmd == LINE_CMD_INDENT_FUNC) {
+                               memcpy(&indent_func, ptr, sizeof(INDENT_FUNC));
+                               ptr += sizeof(INDENT_FUNC);
+                               if (indent_func == NULL)
+                                        indent_func = view->default_indent_func;
+                       } else
+                               update_cmd_color(cmd, &color);
                        continue;
                }
 
                if (xpos == view->width && sub != NULL &&
                    (last_space <= indent_pos || last_space <= 10) &&
-                   !view->longword_noindent) {
+                   view->longword_noindent) {
                         /* long word, remove the indentation from this line */
                        xpos -= sub->indent;
                         sub->indent = 0;
                }
 
                if (xpos == view->width) {
-                       xpos = indent_pos;
+                       xpos = indent_func == NULL ? indent_pos :
+                               indent_func(view, line, -1);
 
                        sub = g_new0(LINE_CACHE_SUB_REC, 1);
                        if (last_space > indent_pos && last_space > 10) {
@@ -180,7 +212,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                                 color = last_color;
                                ptr = last_space_ptr;
                                while (*ptr == ' ') ptr++;
-                       } else if (!view->longword_noindent) {
+                       } else if (view->longword_noindent) {
                                /* long word, no indentation in next line */
                                xpos = 0;
                                sub->continues = TRUE;
@@ -188,6 +220,7 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 
                        sub->start = ptr;
                        sub->indent = xpos;
+                        sub->indent_func = indent_func;
                        sub->color = color;
 
                        lines = g_slist_append(lines, sub);
@@ -197,6 +230,9 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                        continue;
                }
 
+               if (view->utf8)
+                       get_utf8_char(&ptr);
+
                xpos++;
                if (*ptr++ == ' ') {
                        last_space = xpos-1;
@@ -224,23 +260,76 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        return rec;
 }
 
+static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+                             unsigned char update_counter)
+{
+       LINE_CACHE_REC *cache;
+
+       if (view->cache->update_counter == update_counter)
+               return;
+       view->cache->update_counter = update_counter;
+
+       cache = g_hash_table_lookup(view->cache->line_cache, line);
+       if (cache != NULL) {
+                g_free(cache);
+               g_hash_table_remove(view->cache->line_cache, line);
+       }
+}
+
+static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+                             unsigned char update_counter)
+{
+       view_remove_cache(view, line, update_counter);
+
+       if (view->buffer->cur_line == line)
+               view->cache->last_linecount = view_get_linecount(view, line);
+}
+
+static void view_reset_cache(TEXT_BUFFER_VIEW_REC *view)
+{
+       GSList *tmp;
+
+       /* destroy line caches - note that you can't do simultaneously
+          unrefs + cache_get()s or it will keep using the old caches */
+       textbuffer_cache_unref(view->cache);
+        g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL);
+
+       view->cache = textbuffer_cache_get(view->siblings, view->width);
+       for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
+               TEXT_BUFFER_VIEW_REC *rec = tmp->data;
+
+               rec->cache = textbuffer_cache_get(rec->siblings, rec->width);
+       }
+}
+
 static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                          int subline, int ypos, int max)
 {
+        INDENT_FUNC indent_func;
        LINE_CACHE_REC *cache;
         const unsigned char *text, *text_newline;
-       char *tmp;
-       int xpos, color, drawcount, first;
+       unsigned char *tmp;
+       int xpos, color, drawcount, first, need_move, need_clrtoeol;
+
+       if (view->dirty) /* don't bother drawing anything - redraw is coming */
+                return 0;
 
        cache = textbuffer_view_get_line_cache(view, line);
        if (subline >= cache->count)
                 return 0;
 
-       xpos = color = drawcount = 0; first = TRUE;
+        color = ATTR_RESET;
+        need_move = TRUE; need_clrtoeol = FALSE;
+       xpos = drawcount = 0; first = TRUE;
        text_newline = text =
                subline == 0 ? line->text : cache->lines[subline-1].start;
        for (;;) {
                if (text == text_newline) {
+                       if (need_clrtoeol && xpos < term_width) {
+                               term_set_color(view->window, ATTR_RESET);
+                               term_clrtoeol(view->window);
+                       }
+
                        if (first)
                                first = FALSE;
                        else {
@@ -250,21 +339,37 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                        }
 
                        if (subline > 0) {
-                               xpos = cache->lines[subline-1].indent;
+                                /* continuing previous line - indent it */
+                               indent_func = cache->lines[subline-1].indent_func;
+                               xpos = indent_func != NULL ?
+                                       indent_func(view, line, ypos) :
+                                       cache->lines[subline-1].indent;
                                 color = cache->lines[subline-1].color;
                        }
 
-                       set_color(view->window, 0);
-                       wmove(view->window, ypos, 0);
-                       wclrtoeol(view->window);
+                       if (xpos == 0)
+                                need_clrtoeol = TRUE;
+                       else {
+                               /* line was indented - need to clear the
+                                   indented area first */
+                               term_set_color(view->window, ATTR_RESET);
+                               term_move(view->window, 0, ypos);
+                               term_clrtoeol(view->window);
+                       }
 
-                       wmove(view->window, ypos, xpos);
-                       set_color(view->window, color);
+                       if (need_move || xpos > 0)
+                               term_move(view->window, xpos, ypos);
 
-                       /* get the beginning of the next subline */
-                       text_newline = subline == cache->count-1 ? NULL :
-                                cache->lines[subline].start;
+                       term_set_color(view->window, color);
 
+                       if (subline == cache->count-1) {
+                               text_newline = NULL;
+                               need_move = FALSE;
+                       } else {
+                               /* get the beginning of the next subline */
+                               text_newline = cache->lines[subline].start;
+                               need_move = !cache->lines[subline].continues;
+                       }
                         drawcount++;
                        subline++;
                }
@@ -275,43 +380,45 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                        if (*text == LINE_CMD_EOL || *text == LINE_CMD_FORMAT)
                                 break;
 
-                       if ((*text & 0x80) == 0) {
-                               /* set color */
-                               color = (color & ATTR_UNDERLINE) | *text;
-                       } else if (*text == LINE_CMD_CONTINUE) {
+                       if (*text == LINE_CMD_CONTINUE) {
                                 /* jump to next block */
                                memcpy(&tmp, text+1, sizeof(unsigned char *));
                                text = tmp;
                                continue;
-                       } else switch (*text) {
-                       case LINE_CMD_UNDERLINE:
-                               color ^= ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR0:
-                               color = color & ATTR_UNDERLINE;
-                               break;
-                       case LINE_CMD_COLOR8:
-                               color &= 0xfff0;
-                               color |= 8|ATTR_COLOR8;
-                               break;
-                       case LINE_CMD_BLINK:
-                               color |= 0x80;
-                                break;
+                       } else if (*text == LINE_CMD_INDENT_FUNC) {
+                               text += sizeof(INDENT_FUNC);
+                       } else {
+                               update_cmd_color(*text, &color);
+                               term_set_color(view->window, color);
                        }
-                       set_color(view->window, color);
                        text++;
                        continue;
                }
 
-               if ((*text & 127) >= 32)
-                       waddch(view->window, *text);
-               else {
-                       /* low-ascii */
-                       set_color(view->window, ATTR_REVERSE);
-                       waddch(view->window, (*text & 127)+'A'-1);
-                       set_color(view->window, color);
+               if (xpos < term_width) {
+                       const unsigned char *end = text;
+                       if (view->utf8)
+                               get_utf8_char(&end);
+
+                       if (*text >= 32 &&
+                           (end != text || (*text & 127) >= 32)) {
+                               for (; text < end; text++)
+                                       term_addch(view->window, *text);
+                               term_addch(view->window, *text);
+                       } else {
+                               /* low-ascii */
+                               term_set_color(view->window, ATTR_RESET|ATTR_REVERSE);
+                               term_addch(view->window, (*text & 127)+'A'-1);
+                               term_set_color(view->window, color);
+                       }
                }
                text++;
+               xpos++;
+       }
+
+       if (need_clrtoeol && xpos < term_width) {
+               term_set_color(view->window, ATTR_RESET);
+               term_clrtoeol(view->window);
        }
 
         return drawcount;
@@ -321,7 +428,7 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
    original if possible */
 static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
 {
-       GList *tmp;
+        LINE_REC *line;
         int linecount, total;
 
        if (view->empty_linecount == 0) {
@@ -331,12 +438,10 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
        }
 
        total = 0;
-       tmp = g_list_last(view->buffer->lines);
-       for (; tmp != NULL; tmp = tmp->prev) {
-               LINE_REC *line = tmp->data;
-
+        line = textbuffer_line_last(view->buffer);
+       for (; line != NULL; line = line->prev) {
                linecount = view_get_linecount(view, line);
-               if (tmp == view->bottom_startline) {
+               if (line == view->bottom_startline) {
                        /* keep the old one, make sure that subline is ok */
                        if (view->bottom_subline > linecount)
                                view->bottom_subline = linecount;
@@ -347,7 +452,7 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
 
                 total += linecount;
                if (total >= view->height) {
-                       view->bottom_startline = tmp;
+                       view->bottom_startline = line;
                        view->bottom_subline = total - view->height;
                         view->empty_linecount = 0;
                         return;
@@ -355,27 +460,26 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
        }
 
         /* not enough lines so we must be at the beginning of the buffer */
-       view->bottom_startline = view->buffer->lines;
+       view->bottom_startline = view->buffer->first_line;
        view->bottom_subline = 0;
        view->empty_linecount = view->height - total;
 }
 
 static void textbuffer_view_init_ypos(TEXT_BUFFER_VIEW_REC *view)
 {
-       GList *tmp;
+        LINE_REC *line;
 
        g_return_if_fail(view != NULL);
 
        view->ypos = -view->subline-1;
-       for (tmp = view->startline; tmp != NULL; tmp = tmp->next)
-               view->ypos += view_get_linecount(view, tmp->data);
+       for (line = view->startline; line != NULL; line = line->next)
+               view->ypos += view_get_linecount(view, line);
 }
 
 /* Create new view. */
 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
                                             int width, int height,
-                                            int default_indent,
-                                            int longword_noindent)
+                                            int scroll, int utf8)
 {
        TEXT_BUFFER_VIEW_REC *view;
 
@@ -388,8 +492,8 @@ TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
 
        view->width = width;
         view->height = height;
-       view->default_indent = default_indent;
-        view->longword_noindent = longword_noindent;
+       view->scroll = scroll;
+        view->utf8 = utf8;
 
        view->cache = textbuffer_cache_get(view->siblings, width);
        textbuffer_view_init_bottom(view);
@@ -439,60 +543,130 @@ void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view)
 /* Change the default indent position */
 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
                                        int default_indent,
-                                       int longword_noindent)
+                                       int longword_noindent,
+                                       INDENT_FUNC indent_func)
 {
-       view->default_indent = default_indent;
-        view->longword_noindent = longword_noindent;
+        if (default_indent != -1)
+               view->default_indent = default_indent;
+        if (longword_noindent != -1)
+               view->longword_noindent = longword_noindent;
+
+       view->default_indent_func = indent_func;
 }
 
-static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, GList *lines)
+static void view_unregister_indent_func(TEXT_BUFFER_VIEW_REC *view,
+                                       INDENT_FUNC indent_func)
+{
+        INDENT_FUNC func;
+       LINE_REC *line;
+        const unsigned char *text, *tmp;
+
+       if (view->default_indent_func == indent_func)
+               view->default_indent_func = NULL;
+
+       /* recreate cache so it won't contain references
+          to the indent function */
+       view_reset_cache(view);
+       view->cache = textbuffer_cache_get(view->siblings, view->width);
+
+        /* remove all references to the indent function from buffer */
+       line = view->buffer->first_line;
+       while (line != NULL) {
+               text = line->text;
+
+               for (text = line->text;; text++) {
+                       if (*text != '\0')
+                               continue;
+
+                        text++;
+                       if (*text == LINE_CMD_EOL)
+                               break;
+
+                       if (*text == LINE_CMD_INDENT_FUNC) {
+                               text++;
+                               memcpy(&func, text, sizeof(INDENT_FUNC));
+                               if (func == indent_func)
+                                        memset(&func, 0, sizeof(INDENT_FUNC));
+                               text += sizeof(INDENT_FUNC);
+                       } else if (*text == LINE_CMD_CONTINUE) {
+                               memcpy(&tmp, text+1, sizeof(char *));
+                               text = tmp-1;
+                       }
+               }
+
+               line = line->next;
+       }
+}
+
+void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func)
+{
+       g_slist_foreach(views, (GFunc) view_unregister_indent_func,
+                       (void *) indent_func);
+}
+
+void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll)
+{
+        view->scroll = scroll;
+}
+
+void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8)
+{
+        view->utf8 = utf8;
+}
+
+static int view_get_linecount_all(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
        int linecount;
 
         linecount = 0;
-       while (lines != NULL) {
-               linecount += view_get_linecount(view, lines->data);
-                lines = lines->next;
+       while (line != NULL) {
+               linecount += view_get_linecount(view, line);
+                line = line->next;
        }
 
         return linecount;
 }
 
-static void view_draw(TEXT_BUFFER_VIEW_REC *view, GList *line,
-                     int subline, int ypos, int lines)
+static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
+                     int subline, int ypos, int lines, int fill_bottom)
 {
        int linecount;
 
-       while (line != NULL && lines > 0) {
-               LINE_REC *rec = line->data;
+       if (view->dirty) /* don't bother drawing anything - redraw is coming */
+                return;
 
-                linecount = view_line_draw(view, rec, subline, ypos, lines);
+       while (line != NULL && lines > 0) {
+                linecount = view_line_draw(view, line, subline, ypos, lines);
                ypos += linecount; lines -= linecount;
 
                subline = 0;
                 line = line->next;
        }
 
-        /* clear the rest of the view */
-       while (lines > 0) {
-               wmove(view->window, ypos, 0);
-               wclrtoeol(view->window);
-               ypos++; lines--;
+       if (fill_bottom) {
+               /* clear the rest of the view */
+               term_set_color(view->window, ATTR_RESET);
+               while (lines > 0) {
+                       term_move(view->window, 0, ypos);
+                       term_clrtoeol(view->window);
+                       ypos++; lines--;
+               }
        }
 }
 
-#define view_draw_top(view, lines) \
-       view_draw(view, (view)->startline, (view)->subline, 0, lines)
+#define view_draw_top(view, lines, fill_bottom) \
+       view_draw(view, (view)->startline, (view)->subline, \
+                 0, lines, fill_bottom)
 
 static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
 {
-       GList *line;
+       LINE_REC *line;
        int ypos, maxline, subline, linecount;
 
        maxline = view->height-lines;
        line = view->startline; ypos = -view->subline; subline = 0;
        while (line != NULL && ypos < maxline) {
-                linecount = view_get_linecount(view, line->data);
+                linecount = view_get_linecount(view, line);
                ypos += linecount;
                if (ypos > maxline) {
                        subline = maxline-(ypos-linecount);
@@ -501,12 +675,12 @@ static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
                 line = line->next;
        }
 
-        view_draw(view, line, subline, maxline, lines);
+        view_draw(view, line, subline, maxline, lines, TRUE);
 }
 
 /* Returns number of lines actually scrolled */
-static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
-                      int scrollcount, int draw_nonclean)
+static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines,
+                      int *subline, int scrollcount, int draw_nonclean)
 {
        int linecount, realcount, scroll_visible;
 
@@ -520,7 +694,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
        scrollcount += *subline;
         *subline = 0;
        while (scrollcount > 0) {
-               linecount = view_get_linecount(view, (*lines)->data);
+               linecount = view_get_linecount(view, *lines);
 
                if ((scroll_visible && *lines == view->bottom_startline) &&
                    (scrollcount >= view->bottom_subline)) {
@@ -545,7 +719,7 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
         /* scroll up */
        while (scrollcount < 0 && (*lines)->prev != NULL) {
                *lines = (*lines)->prev;
-               linecount = view_get_linecount(view, (*lines)->data);
+               linecount = view_get_linecount(view, *lines);
 
                 realcount -= linecount;
                scrollcount += linecount;
@@ -562,18 +736,17 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, GList **lines, int *subline,
                           whole view */
                         textbuffer_view_redraw(view);
                } else {
-                       scrollok(view->window, TRUE);
-                       wscrl(view->window, realcount);
-                       scrollok(view->window, FALSE);
+                       term_set_color(view->window, ATTR_RESET);
+                       term_window_scroll(view->window, realcount);
 
                        if (draw_nonclean) {
                                if (realcount < 0)
-                                        view_draw_top(view, -realcount);
+                                        view_draw_top(view, -realcount, TRUE);
                                else
                                        view_draw_bottom(view, realcount);
                        }
 
-                       screen_refresh(view->window);
+                       term_refresh(view->window);
                }
        }
 
@@ -588,23 +761,25 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
         g_return_if_fail(view != NULL);
         g_return_if_fail(width > 0);
 
-       if (view->buffer->lines == NULL)
-                return;
-
        if (view->width != width) {
                 /* line cache needs to be recreated */
                textbuffer_cache_unref(view->cache);
                view->cache = textbuffer_cache_get(view->siblings, width);
        }
 
-       view->width = width;
-       view->height = height;
+       view->width = width > 10 ? width : 10;
+       view->height = height > 1 ? height : 1;
+
+       if (view->buffer->first_line == NULL) {
+                view->empty_linecount = height;
+               return;
+       }
 
        textbuffer_view_init_bottom(view);
 
        /* check that we didn't scroll lower than bottom startline.. */
-       if (g_list_find(view->bottom_startline->next,
-                       view->startline->data) != NULL) {
+       if (textbuffer_line_exists_after(view->bottom_startline->next,
+                                        view->startline)) {
                view->startline = view->bottom_startline;
                 view->subline = view->bottom_subline;
        } else if (view->startline == view->bottom_startline &&
@@ -612,7 +787,7 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
                 view->subline = view->bottom_subline;
        } else {
                /* make sure the subline is still in allowed range */
-               linecount = view_get_linecount(view, view->startline->data);
+               linecount = view_get_linecount(view, view->startline);
                if (view->subline > linecount)
                         view->subline = linecount;
        }
@@ -637,9 +812,10 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height)
                        view->subline;
                 if (view->empty_linecount < view->height-linecount)
                        view->empty_linecount = view->height-linecount;
+                view->more_text = FALSE;
        }
 
-        textbuffer_view_redraw(view);
+       view->dirty = TRUE;
 }
 
 /* Clear the view, don't actually remove any lines from buffer. */
@@ -649,12 +825,13 @@ void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view)
 
        view->ypos = -1;
        view->bottom_startline = view->startline =
-               g_list_last(view->buffer->lines);
+               textbuffer_line_last(view->buffer);
        view->bottom_subline = view->subline =
                view->buffer->cur_line == NULL ? 0 :
                view_get_linecount(view, view->buffer->cur_line);
        view->empty_linecount = view->height;
        view->bottom = TRUE;
+       view->more_text = FALSE;
 
         textbuffer_view_redraw(view);
 }
@@ -670,35 +847,28 @@ void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines)
                            lines, TRUE);
        view->ypos += lines < 0 ? count : -count;
        view->bottom = view_is_bottom(view);
+        if (view->bottom) view->more_text = FALSE;
 
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* Scroll to specified line */
 void textbuffer_view_scroll_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
-       GList *tmp;
-
         g_return_if_fail(view != NULL);
 
-       if (g_list_find(view->bottom_startline->next, line) != NULL) {
+       if (textbuffer_line_exists_after(view->bottom_startline->next, line)) {
                view->startline = view->bottom_startline;
                view->subline = view->bottom_subline;
        } else {
-               for (tmp = view->buffer->lines; tmp != NULL; tmp = tmp->next) {
-                       LINE_REC *rec = tmp->data;
-
-                       if (rec == line) {
-                               view->startline = tmp;
-                               view->subline = 0;
-                               break;
-                       }
-               }
+               view->startline = line;
+                view->subline = 0;
        }
 
        textbuffer_view_init_ypos(view);
        view->bottom = view_is_bottom(view);
+        if (view->bottom) view->more_text = FALSE;
 
        textbuffer_view_redraw(view);
 }
@@ -721,42 +891,20 @@ LINE_CACHE_REC *textbuffer_view_get_line_cache(TEXT_BUFFER_VIEW_REC *view,
         return cache;
 }
 
-static void view_remove_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
-                             unsigned char update_counter)
-{
-       LINE_CACHE_REC *cache;
-
-       if (view->cache->update_counter == update_counter)
-               return;
-       view->cache->update_counter = update_counter;
-
-       cache = g_hash_table_lookup(view->cache->line_cache, line);
-       if (cache != NULL) {
-                g_free(cache);
-               g_hash_table_remove(view->cache->line_cache, line);
-       }
-}
-
-static void view_update_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
-                             unsigned char update_counter)
-{
-       view_remove_cache(view, line, update_counter);
-
-       if (view->buffer->cur_line == line)
-               view->cache->last_linecount = view_get_linecount(view, line);
-}
-
 static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
        int linecount, ypos, subline;
 
+        if (!view->bottom)
+               view->more_text = TRUE;
+
        if (view->bottom_startline == NULL) {
                view->startline = view->bottom_startline =
-                       view->buffer->lines;
+                       view->buffer->first_line;
        }
 
        if (view->buffer->cur_line != line &&
-           g_list_find(view->bottom_startline, line) == NULL)
+           !textbuffer_line_exists_after(view->bottom_startline, line))
                return;
 
        linecount = view->cache->last_linecount;
@@ -777,11 +925,13 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        }
 
        if (view->bottom) {
-               if (view->ypos >= view->height) {
+               if (view->scroll && view->ypos >= view->height) {
                        linecount = view->ypos-view->height+1;
                        view_scroll(view, &view->startline,
                                    &view->subline, linecount, FALSE);
                        view->ypos -= linecount;
+               } else {
+                       view->bottom = view_is_bottom(view);
                }
 
                if (view->window != NULL) {
@@ -792,13 +942,15 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                                subline = -ypos;
                                ypos = 0;
                        }
-                       view_line_draw(view, line, subline, ypos,
-                                      view->height - ypos);
+                       if (ypos < view->height) {
+                               view_line_draw(view, line, subline, ypos,
+                                              view->height - ypos);
+                       }
                }
        }
 
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* Update some line in the buffer which has been modified using
@@ -841,7 +993,7 @@ static void bookmark_check_remove(char *key, LINE_REC *line,
 static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 {
         BOOKMARK_FIND_REC rec;
-        LINE_REC *newline;
+        LINE_REC *new_line;
        GSList *tmp;
 
         rec.remove_line = line;
@@ -850,16 +1002,13 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                             (GHFunc) bookmark_check_remove, &rec);
 
        if (rec.remove_list != NULL) {
-               GList *pos = g_list_find(view->buffer->lines, line);
-
-               newline = pos == NULL || pos->prev == NULL ? NULL :
-                       (pos->next == NULL ? pos->prev->data :
-                        pos->next->data);
+               new_line = line->prev == NULL ? NULL :
+                       (line->next == NULL ? line->prev : line->next);
                for (tmp = rec.remove_list; tmp != NULL; tmp = tmp->next) {
                        g_hash_table_remove(view->bookmarks, tmp->data);
-                       if (newline != NULL) {
+                       if (new_line != NULL) {
                                g_hash_table_insert(view->bookmarks,
-                                                   tmp->data, newline);
+                                                   tmp->data, new_line);
                        }
                }
                g_slist_free(rec.remove_list);
@@ -869,29 +1018,53 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 /* Return number of real lines `lines' list takes -
    stops counting when the height reaches the view height */
 static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view,
-                                GList *lines, int subline,
+                                LINE_REC *line, int subline,
                                 LINE_REC *skip_line)
 {
        int height, linecount;
 
         height = -subline;
-       while (lines != NULL && height < view->height) {
-               LINE_REC *line = lines->data;
-
+       while (line != NULL && height < view->height) {
                if (line != skip_line) {
                         linecount = view_get_linecount(view, line);
                        height += linecount;
                }
-                lines = lines->next;
+                line = line->next;
        }
 
        return height < view->height ? height : view->height;
 }
 
+static void view_remove_line_update_startline(TEXT_BUFFER_VIEW_REC *view,
+                                             LINE_REC *line, int linecount)
+{
+       int scroll;
+
+       if (view->startline == line) {
+               view->startline = view->startline->prev != NULL ?
+                       view->startline->prev : view->startline->next;
+               view->subline = 0;
+       } else {
+               scroll = view->height -
+                       view_get_lines_height(view, view->startline,
+                                             view->subline, line);
+               if (scroll > 0) {
+                       view_scroll(view, &view->startline,
+                                   &view->subline, -scroll, FALSE);
+               }
+       }
+
+       /* FIXME: this is slow and unnecessary, but it's easy and
+          really works :) */
+       textbuffer_view_init_ypos(view);
+       if (textbuffer_line_exists_after(view->startline, line))
+               view->ypos -= linecount;
+}
+
 static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                             int linecount)
 {
-       int realcount, scroll;
+       int realcount;
 
        view_bookmarks_check(view, line);
 
@@ -899,62 +1072,49 @@ static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
                 /* the last line is being removed */
                LINE_REC *prevline;
 
-               prevline = view->buffer->lines->data == line ? NULL :
-                       g_list_last(view->bottom_startline)->data;
+               prevline = view->buffer->first_line == line ? NULL :
+                       textbuffer_line_last(view->buffer);
                view->cache->last_linecount = prevline == NULL ? 0 :
                        view_get_linecount(view, prevline);
        }
 
-       if (line == view->buffer->lines->data) {
+       if (view->buffer->first_line == line) {
                /* first line in the buffer - this is the most commonly
                   removed line.. */
-               if (view->bottom_startline->data == line) {
+               if (view->bottom_startline == line) {
                        /* very small scrollback.. */
                         view->bottom_startline = view->bottom_startline->next;
                        view->bottom_subline = 0;
                }
 
-               if (view->startline->data == line) {
+               if (view->startline == line) {
                         /* removing the first line in screen */
                        realcount = view_scroll(view, &view->startline,
                                                &view->subline,
-                                               linecount, TRUE);
+                                               linecount, FALSE);
                        view->ypos -= realcount;
                        view->empty_linecount += linecount-realcount;
                }
-       } else if (g_list_find(view->bottom_startline, line) != NULL) {
-               realcount = view_scroll(view, &view->bottom_startline,
-                                       &view->bottom_subline,
-                                       -linecount, FALSE);
-               if (view->bottom) {
-                       /* we're at the bottom, remove the same amount as
-                          from bottom_startline */
-                       view_scroll(view, &view->startline,
-                                   &view->subline, -linecount, TRUE);
-                       view->ypos -= linecount-realcount;
-               } else {
-                       if (view->startline->data == line) {
-                               view->startline =
-                                       view->startline->next != NULL ?
-                                       view->startline->next :
-                                       view->startline->prev;
-                                view->subline = 0;
-                       }
-                       scroll = view->height -
-                               view_get_lines_height(view, view->startline,
-                                                      view->subline, line);
-                       if (scroll > 0) {
-                               view_scroll(view, &view->startline,
-                                           &view->subline, -scroll, TRUE);
-                                view->ypos -= scroll;
-                       }
+       } else {
+               if (textbuffer_line_exists_after(view->bottom_startline,
+                                                line)) {
+                       realcount = view_scroll(view, &view->bottom_startline,
+                                               &view->bottom_subline,
+                                               -linecount, FALSE);
+                       view->empty_linecount += linecount-realcount;
+               }
+
+               if (textbuffer_line_exists_after(view->startline,
+                                                line)) {
+                       view_remove_line_update_startline(view, line,
+                                                         linecount);
                }
-               view->empty_linecount += linecount-realcount;
        }
 
        view->bottom = view_is_bottom(view);
+        if (view->bottom) view->more_text = FALSE;
         if (view->window != NULL)
-               screen_refresh(view->window);
+               term_refresh(view->window);
 }
 
 /* Remove one line from buffer. */
@@ -983,30 +1143,25 @@ void textbuffer_view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
        textbuffer_remove(view->buffer, line);
 }
 
+static int g_free_true(void *data)
+{
+       g_free(data);
+        return TRUE;
+}
+
 /* Remove all lines from buffer. */
 void textbuffer_view_remove_all_lines(TEXT_BUFFER_VIEW_REC *view)
 {
-       GSList *tmp;
-
        g_return_if_fail(view != NULL);
 
        textbuffer_remove_all_lines(view->buffer);
 
-       /* destroy line caches - note that you can't do simultaneously
-          unrefs + cache_get()s or it will keep using the old caches */
-       textbuffer_cache_unref(view->cache);
-        g_slist_foreach(view->siblings, (GFunc) textbuffer_cache_unref, NULL);
+       g_hash_table_foreach_remove(view->bookmarks,
+                                   (GHRFunc) g_free_true, NULL);
 
-        /* recreate caches, clear screens */
-       view->cache = textbuffer_cache_get(view->siblings, view->width);
+       view_reset_cache(view);
        textbuffer_view_clear(view);
-
-       for (tmp = view->siblings; tmp != NULL; tmp = tmp->next) {
-               TEXT_BUFFER_VIEW_REC *rec = tmp->data;
-
-               rec->cache = textbuffer_cache_get(rec->siblings, rec->width);
-               textbuffer_view_clear(rec);
-       }
+       g_slist_foreach(view->siblings, (GFunc) textbuffer_view_clear, NULL);
 }
 
 /* Set a bookmark in view */
@@ -1037,7 +1192,7 @@ void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
        g_return_if_fail(name != NULL);
 
        if (view->bottom_startline != NULL) {
-                line = g_list_last(view->bottom_startline)->data;
+                line = textbuffer_line_last(view->buffer);
                textbuffer_view_set_bookmark(view, name, line);
        }
 }
@@ -1054,14 +1209,15 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
 
 /* Specify window where the changes in view should be drawn,
    NULL disables it. */
-void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window)
+void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view,
+                               TERM_WINDOW *window)
 {
        g_return_if_fail(view != NULL);
 
        if (view->window != window) {
                view->window = window;
-               if (window != NULL)
-                       textbuffer_view_redraw(view);
+                if (window != NULL)
+                       view->dirty = TRUE;
        }
 }
 
@@ -1071,9 +1227,9 @@ void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view)
        g_return_if_fail(view != NULL);
 
        if (view->window != NULL) {
-                werase(view->window);
-               view_draw_top(view, view->height);
-               screen_refresh(view->window);
+               view->dirty = FALSE;
+               view_draw_top(view, view->height, TRUE);
+               term_refresh(view->window);
        }
 }
 
@@ -1104,6 +1260,8 @@ static int sig_check_linecache(void)
                                            (GHRFunc) line_cache_check_remove,
                                            &now);
        }
+
+        g_slist_free(caches);
        return 1;
 }
 
index 21ed28cfb4b80c39f460b442f1f18d1554565ead..b529ebedd099436225c64e8c10b768b1da5c5872 100644 (file)
@@ -2,11 +2,18 @@
 #define __TEXTBUFFER_VIEW_H
 
 #include "textbuffer.h"
-#include "screen.h"
+#include "term.h"
+
+typedef struct _TEXT_BUFFER_VIEW_REC TEXT_BUFFER_VIEW_REC;
+
+/* if ypos == -1, don't print anything, just return the indent size */
+typedef int (*INDENT_FUNC) (TEXT_BUFFER_VIEW_REC *view,
+                           LINE_REC *line, int ypos);
 
 typedef struct {
-       unsigned char *start;
+       const unsigned char *start;
        int indent;
+        INDENT_FUNC indent_func;
        int color;
 
        /* first word in line belong to the end of the last word in
@@ -37,24 +44,27 @@ typedef struct {
        int last_linecount;
 } TEXT_BUFFER_CACHE_REC;
 
-typedef struct {
+struct _TEXT_BUFFER_VIEW_REC {
        TEXT_BUFFER_REC *buffer;
        GSList *siblings; /* other views that use the same buffer */
 
-        WINDOW *window;
+        TERM_WINDOW *window;
        int width, height;
 
        int default_indent;
-       int longword_noindent:1;
+        INDENT_FUNC default_indent_func;
+       unsigned int longword_noindent:1;
+       unsigned int scroll:1; /* scroll down automatically when at bottom */
+       unsigned int utf8:1; /* use UTF8 in this view */
 
        TEXT_BUFFER_CACHE_REC *cache;
        int ypos; /* cursor position - visible area is 0..height-1 */
 
-       GList *startline; /* line at the top of the screen */
+       LINE_REC *startline; /* line at the top of the screen */
        int subline; /* number of "real lines" to skip from `startline' */
 
         /* marks the bottom of the text buffer */
-       GList *bottom_startline;
+       LINE_REC *bottom_startline;
        int bottom_subline;
 
        /* how many empty lines are in screen. a screenful when started
@@ -62,23 +72,31 @@ typedef struct {
        int empty_linecount; 
         /* window is at the bottom of the text buffer */
        unsigned int bottom:1;
+        /* if !bottom - new text has been printed since we were at bottom */
+       unsigned int more_text:1;
+        /* Window needs a redraw */
+       unsigned int dirty:1;
 
        /* Bookmarks to the lines in the buffer - removed automatically
           when the line gets removed from buffer */
         GHashTable *bookmarks;
-} TEXT_BUFFER_VIEW_REC;
+};
 
 /* Create new view. */
 TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
                                             int width, int height,
-                                            int default_indent,
-                                            int longword_noindent);
+                                            int scroll, int utf8);
 /* Destroy the view. */
 void textbuffer_view_destroy(TEXT_BUFFER_VIEW_REC *view);
 /* Change the default indent position */
 void textbuffer_view_set_default_indent(TEXT_BUFFER_VIEW_REC *view,
                                        int default_indent,
-                                       int longword_noindent);
+                                       int longword_noindent,
+                                       INDENT_FUNC indent_func);
+void textbuffer_views_unregister_indent_func(INDENT_FUNC indent_func);
+
+void textbuffer_view_set_scroll(TEXT_BUFFER_VIEW_REC *view, int scroll);
+void textbuffer_view_set_utf8(TEXT_BUFFER_VIEW_REC *view, int utf8);
 
 /* Resize the view. */
 void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
@@ -86,7 +104,7 @@ void textbuffer_view_resize(TEXT_BUFFER_VIEW_REC *view, int width, int height);
 void textbuffer_view_clear(TEXT_BUFFER_VIEW_REC *view);
 
 #define textbuffer_view_get_lines(view) \
-        ((view)->buffer->lines)
+        ((view)->buffer->first_line)
 
 /* Scroll the view up/down */
 void textbuffer_view_scroll(TEXT_BUFFER_VIEW_REC *view, int lines);
@@ -121,7 +139,8 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
 
 /* Specify window where the changes in view should be drawn,
    NULL disables it. */
-void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view, WINDOW *window);
+void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view,
+                               TERM_WINDOW *window);
 /* Redraw the view */
 void textbuffer_view_redraw(TEXT_BUFFER_VIEW_REC *view);
 
index f97f46c74144336cad34ae769f9e4d3c75b1c9fe..8ac8bf0043efdb0fa7d3c8f40d2c5886efe9a213 100644 (file)
@@ -73,7 +73,7 @@ static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer,
 static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
 {
        TEXT_CHUNK_REC *rec;
-       char *buf, *ptr, **pptr;
+       unsigned char *buf, *ptr, **pptr;
 
        rec = g_mem_chunk_alloc(text_chunk);
        rec->pos = 0;
@@ -90,7 +90,7 @@ static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
                   breaks at least NetBSD/Alpha, so don't go "optimize"
                   it :) */
                ptr = rec->buffer; pptr = &ptr;
-               memcpy(buf, pptr, sizeof(char *));
+               memcpy(buf, pptr, sizeof(unsigned char *));
        } else {
                /* just to be safe */
                mark_temp_eol(rec);
@@ -140,7 +140,7 @@ static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
 }
 
 static void text_chunk_append(TEXT_BUFFER_REC *buffer,
-                             const char *data, int len)
+                             const unsigned char *data, int len)
 {
         TEXT_CHUNK_REC *chunk;
        int left;
@@ -151,7 +151,8 @@ static void text_chunk_append(TEXT_BUFFER_REC *buffer,
         chunk = buffer->cur_text;
        while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
                left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
-               if (data[left-1] == 0) left--; /* don't split the commands */
+               if (left > 0 && data[left-1] == 0)
+                       left--; /* don't split the commands */
 
                memcpy(chunk->buffer + chunk->pos, data, left);
                chunk->pos += left;
@@ -187,14 +188,22 @@ static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
 {
        LINE_REC *line;
 
-        line = textbuffer_line_create(buffer);
-       if (prev == buffer->cur_line) {
-               buffer->cur_line = line;
-               buffer->lines = g_list_append(buffer->lines, buffer->cur_line);
+       line = textbuffer_line_create(buffer);
+       line->prev = prev;
+       if (prev == NULL) {
+               line->next = buffer->first_line;
+                if (buffer->first_line != NULL)
+                       buffer->first_line->prev = line;
+               buffer->first_line = line;
        } else {
-               buffer->lines = g_list_insert(buffer->lines, line,
-                                             g_list_index(buffer->lines, prev)+1);
+               line->next = prev->next;
+                if (line->next != NULL)
+                       line->next->prev = line;
+               prev->next = line;
        }
+
+       if (prev == buffer->cur_line)
+               buffer->cur_line = line;
         buffer->lines_count++;
 
         return line;
@@ -224,11 +233,34 @@ void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list)
        g_return_if_fail(buffer != NULL);
 
        while (list != NULL) {
-                textbuffer_line_unref(buffer, list->data);
+                if (list->data != NULL)
+                       textbuffer_line_unref(buffer, list->data);
                 list = list->next;
        }
 }
 
+LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer)
+{
+       LINE_REC *line;
+
+       line = buffer->cur_line;
+       if (line != NULL) {
+               while (line->next != NULL)
+                       line = line->next;
+       }
+        return line;
+}
+
+int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
+{
+       while (line != NULL) {
+               if (line == search)
+                       return TRUE;
+                line = line->next;
+       }
+        return FALSE;
+}
+
 LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
                            const unsigned char *data, int len,
                            LINE_INFO_REC *info)
@@ -245,6 +277,9 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(data != NULL, NULL);
 
+       if (len == 0)
+                return insert_after;
+
        line = !buffer->last_eol ? insert_after :
                textbuffer_line_insert(buffer, insert_after);
 
@@ -264,13 +299,20 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
        g_return_if_fail(buffer != NULL);
        g_return_if_fail(line != NULL);
 
-       buffer->lines = g_list_remove(buffer->lines, line);
+       if (buffer->first_line == line)
+               buffer->first_line = line->next;
+       if (line->prev != NULL)
+               line->prev->next = line->next;
+       if (line->next != NULL)
+               line->next->prev = line->prev;
 
        if (buffer->cur_line == line) {
-               buffer->cur_line = buffer->lines == NULL ? NULL :
-                       g_list_last(buffer->lines)->data;
+               buffer->cur_line = line->next != NULL ?
+                       line->next : line->prev;
        }
 
+        line->prev = line->next = NULL;
+
        buffer->lines_count--;
         textbuffer_line_unref(buffer, line);
 }
@@ -279,40 +321,85 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
 void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
 {
        GSList *tmp;
+        LINE_REC *line;
 
        g_return_if_fail(buffer != NULL);
 
        for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
                 g_mem_chunk_free(text_chunk, tmp->data);
        g_slist_free(buffer->text_chunks);
-        buffer->text_chunks = NULL;
+       buffer->text_chunks = NULL;
 
-       g_list_free(buffer->lines);
-        buffer->lines = NULL;
+       while (buffer->first_line != NULL) {
+               line = buffer->first_line->next;
+               g_mem_chunk_free(line_chunk, buffer->first_line);
+                buffer->first_line = line;
+       }
+       buffer->lines_count = 0;
 
         buffer->cur_line = NULL;
-       buffer->lines_count = 0;
+        buffer->cur_text = NULL;
+
+       buffer->last_eol = TRUE;
+}
+
+static void set_color(GString *str, int cmd, int *last_fg, int *last_bg)
+{
+       if (cmd & LINE_COLOR_DEFAULT) {
+               g_string_sprintfa(str, "\004%c", FORMAT_STYLE_DEFAULTS);
+
+               /* need to reset the fg/bg color */
+               if (cmd & LINE_COLOR_BG) {
+                        *last_bg = -1;
+                       if (*last_fg != -1) {
+                               g_string_sprintfa(str, "\004%c%c",
+                                                 *last_fg,
+                                                 FORMAT_COLOR_NOCHANGE);
+                       }
+               } else {
+                        *last_fg = -1;
+                       if (*last_bg != -1) {
+                               g_string_sprintfa(str, "\004%c%c",
+                                                 FORMAT_COLOR_NOCHANGE,
+                                                 *last_bg);
+                       }
+               }
+                return;
+       }
+
+       if ((cmd & LINE_COLOR_BG) == 0) {
+                /* change foreground color */
+                *last_fg = (cmd & 0x0f)+'0';
+               g_string_sprintfa(str, "\004%c%c", *last_fg,
+                                 FORMAT_COLOR_NOCHANGE);
+       } else {
+               /* change background color */
+                *last_bg = (cmd & 0x0f)+'0';
+               g_string_sprintfa(str, "\004%c%c",
+                                 FORMAT_COLOR_NOCHANGE, *last_bg);
+       }
 }
 
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 {
-        unsigned char cmd;
-       char *ptr, *tmp;
+        unsigned char cmd, *ptr, *tmp;
+        int last_fg, last_bg;
 
        g_return_if_fail(line != NULL);
        g_return_if_fail(str != NULL);
 
         g_string_truncate(str, 0);
 
+        last_fg = last_bg = -1;
        for (ptr = line->text;;) {
                if (*ptr != 0) {
-                       g_string_append_c(str, *ptr);
+                       g_string_append_c(str, (char) *ptr);
                         ptr++;
                        continue;
                }
 
                ptr++;
-                cmd = (unsigned char) *ptr;
+                cmd = *ptr;
                ptr++;
 
                if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
@@ -322,7 +409,7 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                if (cmd == LINE_CMD_CONTINUE) {
                         /* line continues in another address.. */
-                       memcpy(&tmp, ptr, sizeof(char *));
+                       memcpy(&tmp, ptr, sizeof(unsigned char *));
                        ptr = tmp;
                         continue;
                }
@@ -334,40 +421,39 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                if ((cmd & 0x80) == 0) {
                        /* set color */
-                       g_string_sprintfa(str, "\004%c%c",
-                                         (cmd & 0x0f)+'0',
-                                         ((cmd & 0xf0) >> 4)+'0');
+                        set_color(str, cmd, &last_fg, &last_bg);
                } else switch (cmd) {
                case LINE_CMD_UNDERLINE:
                        g_string_append_c(str, 31);
                        break;
+               case LINE_CMD_REVERSE:
+                       g_string_append_c(str, 22);
+                       break;
                case LINE_CMD_COLOR0:
                        g_string_sprintfa(str, "\004%c%c",
                                          '0', FORMAT_COLOR_NOCHANGE);
                        break;
-               case LINE_CMD_COLOR8:
-                       g_string_sprintfa(str, "\004%c%c",
-                                         '8', FORMAT_COLOR_NOCHANGE);
-                       break;
-               case LINE_CMD_BLINK:
-                       g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK);
-                       break;
                case LINE_CMD_INDENT:
                        break;
+               case LINE_CMD_INDENT_FUNC:
+                        ptr += sizeof(void *);
+                       break;
                }
        }
 }
 
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
+                           int before, int after,
                            int regexp, int fullword, int case_sensitive)
 {
 #ifdef HAVE_REGEX_H
        regex_t preg;
 #endif
-       GList *line, *tmp;
+        LINE_REC *line, *pre_line;
        GList *matches;
-        GString *str;
+       GString *str;
+        int i, match_after, line_matched;
 
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(text != NULL, NULL);
@@ -383,216 +469,65 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
 #endif
        }
 
-       matches = NULL;
+       matches = NULL; match_after = 0;
         str = g_string_new(NULL);
 
-        line = g_list_find(buffer->lines, startline);
-       if (line == NULL)
-               line = buffer->lines;
-
-       for (tmp = line; tmp != NULL; tmp = tmp->next) {
-               LINE_REC *rec = tmp->data;
+       line = startline != NULL ? startline : buffer->first_line;
 
-               if ((rec->info.level & level) == 0 ||
-                   (rec->info.level & nolevel) != 0)
+       for (; line != NULL; line = line->next) {
+               if ((line->info.level & level) == 0 ||
+                   (line->info.level & nolevel) != 0)
                         continue;
 
                if (*text == '\0') {
                         /* no search word, everything matches */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
                        continue;
                }
 
-                textbuffer_line2text(rec, FALSE, str);
+                textbuffer_line2text(line, FALSE, str);
 
-                if (
+               line_matched =
 #ifdef HAVE_REGEX_H
-                   regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+                       regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
 #endif
-                   fullword ? strstr_full_case(str->str, text,
-                                               !case_sensitive) != NULL :
-                   case_sensitive ? strstr(str->str, text) != NULL :
-                                    stristr(str->str, text) != NULL) {
-                       /* matched */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
-               }
-       }
-#ifdef HAVE_REGEX_H
-       if (regexp) regfree(&preg);
-#endif
-        g_string_free(str, TRUE);
-       return matches;
-}
-
-#if 0 /* FIXME: saving formats is broken */
-static char *line_read_format(unsigned const char **text)
-{
-       GString *str;
-       char *ret;
-
-       str = g_string_new(NULL);
-       for (;;) {
-               if (**text == '\0') {
-                       if ((*text)[1] == LINE_CMD_EOL) {
-                               /* leave text at \0<eof> */
-                               break;
+                       fullword ? strstr_full_case(str->str, text, !case_sensitive) != NULL :
+                       case_sensitive ? strstr(str->str, text) != NULL :
+                       stristr(str->str, text) != NULL;
+               if (line_matched) {
+                        /* add the -before lines */
+                       pre_line = line;
+                       for (i = 0; i < before; i++) {
+                               if (pre_line->prev == NULL ||
+                                   g_list_find(matches, pre_line->prev) != NULL)
+                                       break;
+                                pre_line = pre_line->prev;
                        }
-                       if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
-                               /* leave text at \0<format_cont> */
-                               break;
-                       }
-                       (*text)++;
 
-                       if (**text == LINE_CMD_FORMAT) {
-                               /* move text to start after \0<format> */
-                               (*text)++;
-                               break;
+                       for (; pre_line != line; pre_line = pre_line->next) {
+                               textbuffer_line_ref(pre_line);
+                               matches = g_list_append(matches, pre_line);
                        }
 
-                       if (**text == LINE_CMD_CONTINUE) {
-                               unsigned char *tmp;
-
-                               memcpy(&tmp, (*text)+1, sizeof(char *));
-                               *text = tmp;
-                               continue;
-                       } else if (**text & 0x80)
-                               (*text)++;
-                       continue;
+                       match_after = after;
                }
 
-               g_string_append_c(str, (char) **text);
-               (*text)++;
-       }
-
-       ret = str->str;
-       g_string_free(str, FALSE);
-       return ret;
-}
-
-static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
-                                       GString *raw)
-{
-       const unsigned char *text;
-       char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
-       TEXT_DEST_REC dest;
-       int formatnum, argcount;
-
-       text = (const unsigned char *) line->text;
-
-       /* skip the beginning of the line until we find the format */
-       g_free(line_read_format(&text));
-       if (text[1] == LINE_CMD_FORMAT_CONT) {
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
-               return NULL;
-       }
-
-       /* read format information */
-        module = line_read_format(&text);
-       format_name = line_read_format(&text);
-
-       if (raw != NULL) {
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
-               g_string_append(raw, module);
-
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
-               g_string_append(raw, format_name);
-       }
+               if (line_matched || match_after > 0) {
+                       /* matched */
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
 
-       formatnum = format_find_tag(module, format_name);
-       if (formatnum == -1)
-               ret = NULL;
-       else {
-                argcount = 0;
-                memset(args, 0, sizeof(args));
-               while (*text != '\0' || text[1] != LINE_CMD_EOL) {
-                       args[argcount] = line_read_format(&text);
-                       if (raw != NULL) {
-                               g_string_append_c(raw, '\0');
-                               g_string_append_c(raw,
-                                                 (char)LINE_CMD_FORMAT);
-
-                               g_string_append(raw, args[argcount]);
-                       }
-                       argcount++;
+                       if (!line_matched && --match_after == 0)
+                               matches = g_list_append(matches, NULL);
                }
-
-               /* get the format text */
-               format_create_dest(&dest, NULL, NULL, line->level, window);
-               ret = format_get_text_theme_charargs(current_theme,
-                                                    module, &dest,
-                                                    formatnum, args);
-               while (argcount > 0)
-                       g_free(args[--argcount]);
        }
-
-       g_free(module);
-       g_free(format_name);
-
-       return ret;
-}
-
-void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line)
-{
-       GUI_WINDOW_REC *gui;
-       TEXT_DEST_REC dest;
-       GString *raw;
-       char *str, *tmp, *prestr, *linestart, *leveltag;
-
-       gui = WINDOW_GUI(window);
-
-       raw = g_string_new(NULL);
-       str = textbuffer_line_get_format(window, line, raw);
-
-        if (str == NULL && raw->len == 2 &&
-            raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
-                /* multiline format, format explained in one the
-                   following lines. remove this line. */
-                textbuffer_line_remove(window, line, FALSE);
-       } else if (str != NULL) {
-                /* FIXME: ugly ugly .. and this can't handle
-                   non-formatted lines.. */
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_EOL);
-
-                textbuffer_line_text_free(gui, line);
-
-                gui->temp_line = line;
-               gui->temp_line->text = gui->cur_text->buffer+gui->cur_text->pos;
-                gui->cur_text->lines++;
-               gui->eol_marked = FALSE;
-
-               format_create_dest(&dest, NULL, NULL, line->level, window);
-
-               linestart = format_get_line_start(current_theme, &dest, line->time);
-               leveltag = format_get_level_tag(current_theme, &dest);
-
-               prestr = g_strconcat(linestart == NULL ? "" : linestart,
-                                    leveltag, NULL);
-               g_free_not_null(linestart);
-               g_free_not_null(leveltag);
-
-               tmp = format_add_linestart(str, prestr);
-               g_free(str);
-               g_free(prestr);
-
-               format_send_to_gui(&dest, tmp);
-               g_free(tmp);
-
-               textbuffer_line_append(gui, raw->str, raw->len);
-
-               gui->eol_marked = TRUE;
-               gui->temp_line = NULL;
-       }
-       g_string_free(raw, TRUE);
-}
+#ifdef HAVE_REGEX_H
+       if (regexp) regfree(&preg);
 #endif
+        g_string_free(str, TRUE);
+       return matches;
+}
 
 void textbuffer_init(void)
 {
index 21f70e260de022c4ad49631e6b921ee0035a2598..adea3604c3a84613c71181235758e6fe1c40c9b2 100644 (file)
@@ -1,20 +1,21 @@
 #ifndef __TEXTBUFFER_H
 #define __TEXTBUFFER_H
 
-/* FIXME: Textbuffer code gets a lot faster in some points when I get rid of
-   GList and make prev/next pointers directly in LINE_REC. However, this
-   can still wait for a while until I get rid of GList entirely everywhere. */
-
 #define LINE_TEXT_CHUNK_SIZE 16384
 
+#define LINE_COLOR_BG          0x20
+#define LINE_COLOR_DEFAULT     0x10
+#define LINE_COLOR_BOLD                0x08
+#define LINE_COLOR_BLINK               0x08
+
 enum {
        LINE_CMD_EOL=0x80,      /* line ends here */
        LINE_CMD_CONTINUE,      /* line continues in next block */
        LINE_CMD_COLOR0,        /* change to black, would be same as \0\0 but it breaks things.. */
-       LINE_CMD_COLOR8,        /* change to dark grey, normally 8 = bold black */
        LINE_CMD_UNDERLINE,     /* enable/disable underlining */
+       LINE_CMD_REVERSE,       /* enable/disable reversed text */
        LINE_CMD_INDENT,        /* if line is split, indent it at this position */
-       LINE_CMD_BLINK,         /* blinking background */
+       LINE_CMD_INDENT_FUNC,   /* if line is split, use the specified indentation function */
        LINE_CMD_FORMAT,        /* end of line, but next will come the format that was used to create the
                                   text in format <module><format_name><arg><arg2...> - fields are separated
                                   with \0<format> and last argument ends with \0<eol>. \0<continue> is allowed
@@ -27,13 +28,23 @@ typedef struct {
        time_t time;
 } LINE_INFO_REC;
 
-typedef struct {
-       /* text in the line. \0 means that the next char will be a
-          color or command. <= 127 = color or if 8. bit is set, the
-          first 7 bits are the command. See LINE_CMD_xxxx.
+typedef struct _LINE_REC {
+       /* Text in the line. \0 means that the next char will be a
+          color or command.
+
+          If the 7th bit is set, the lowest 7 bits are the command
+          (see LINE_CMD_xxxx). Otherwise they specify a color change:
+
+          Bit:
+            5 - Setting a background color
+            4 - Use "default terminal color"
+            3 - Bold (fg) / blink (bg) - can be used with 4th bit
+            0-2 - Color
 
           DO NOT ADD BLACK WITH \0\0 - this will break things. Use
           LINE_CMD_COLOR0 instead. */
+       struct _LINE_REC *prev, *next;
+
        unsigned char *text;
         unsigned char refcount;
         LINE_INFO_REC info;
@@ -47,7 +58,7 @@ typedef struct {
 
 typedef struct {
        GSList *text_chunks;
-       GList *lines;
+        LINE_REC *first_line;
         int lines_count;
 
        LINE_REC *cur_line;
@@ -65,6 +76,9 @@ void textbuffer_line_ref(LINE_REC *line);
 void textbuffer_line_unref(TEXT_BUFFER_REC *buffer, LINE_REC *line);
 void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list);
 
+LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer);
+int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search);
+
 /* Append text to buffer. When \0<EOL> is found at the END OF DATA, a new
    line is created. You must send the EOL command before you can do anything
    else with the buffer. */
@@ -82,6 +96,7 @@ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer);
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str);
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
+                           int before, int after,
                            int regexp, int fullword, int case_sensitive);
 
 void textbuffer_init(void);
diff --git a/apps/irssi/src/fe-text/tparm.c b/apps/irssi/src/fe-text/tparm.c
new file mode 100644 (file)
index 0000000..3f58e6f
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * tparm.c
+ *
+ * By Ross Ridge
+ * Public Domain
+ * 92/02/01 07:30:36
+ *
+ */
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef MAX_PUSHED
+#define MAX_PUSHED     32
+#endif
+
+#define ARG    1
+#define NUM    2
+
+#define INTEGER        1
+#define STRING 2
+
+#define MAX_LINE 640
+
+typedef void* anyptr;
+
+typedef struct stack_str {
+       int     type;
+       int     argnum;
+       int     value;
+} stack;
+
+static stack S[MAX_PUSHED];
+static stack vars['z'-'a'+1];
+static int pos = 0;
+
+static struct arg_str {
+       int type;
+       int     integer;
+       char    *string;
+} arg_list[10];
+
+static int argcnt;
+
+static va_list tparm_args;
+
+static int pusharg(int arg)
+{
+       if (pos == MAX_PUSHED)
+               return 1;
+       S[pos].type = ARG;
+       S[pos++].argnum = arg;
+       return 0;
+}
+
+static int pushnum(int num)
+{
+       if (pos == MAX_PUSHED)
+               return 1;
+       S[pos].type = NUM;
+       S[pos++].value = num;
+       return 0;
+}
+
+/* VARARGS2 */
+static int getarg(int argnum, int type, anyptr p)
+{
+       while (argcnt < argnum) {
+               arg_list[argcnt].type = INTEGER;
+               arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
+       }
+       if (argcnt > argnum) {
+               if (arg_list[argnum].type != type)
+                       return 1;
+               else if (type == STRING)
+                       *(char **)p = arg_list[argnum].string;
+               else
+                       *(int *)p = arg_list[argnum].integer;
+       } else {
+               arg_list[argcnt].type = type;
+               if (type == STRING)
+                       *(char **)p = arg_list[argcnt++].string
+                               = (char *) va_arg(tparm_args, char *);
+               else
+                       *(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
+       }
+       return 0;
+}
+
+
+static int popstring(char **str)
+{
+       if (pos-- == 0)
+               return 1;
+       if (S[pos].type != ARG)
+               return 1;
+       return(getarg(S[pos].argnum, STRING, (anyptr) str));
+}
+
+static int popnum(int *num)
+{
+       if (pos-- == 0)
+               return 1;
+       switch (S[pos].type) {
+       case ARG:
+               return (getarg(S[pos].argnum, INTEGER, (anyptr) num));
+       case NUM:
+               *num = S[pos].value;
+               return 0;
+       }
+       return 1;
+}
+
+static int cvtchar(const char *sp, char *c)
+{
+       switch(*sp) {
+       case '\\':
+               switch(*++sp) {
+               case '\'':
+               case '$':
+               case '\\':
+               case '%':
+                       *c = *sp;
+                       return 2;
+               case '\0':
+                       *c = '\\';
+                       return 1;
+               case '0':
+                       if (sp[1] == '0' && sp[2] == '0') {
+                               *c = '\0';
+                               return 4;
+                       }
+                       *c = '\200'; /* '\0' ???? */
+                       return 2;
+               default:
+                       *c = *sp;
+                       return 2;
+               }
+       default:
+               *c = *sp;
+               return 1;
+       }
+}
+
+static int termcap;
+
+/* sigh... this has got to be the ugliest code I've ever written.
+   Trying to handle everything has its cost, I guess.
+
+   It actually isn't to hard to figure out if a given % code is supposed
+   to be interpeted with its termcap or terminfo meaning since almost
+   all terminfo codes are invalid unless something has been pushed on
+   the stack and termcap strings will never push things on the stack
+   (%p isn't used by termcap). So where we have a choice we make the
+   decision by wether or not somthing has been pushed on the stack.
+   The static variable termcap keeps track of this; it starts out set
+   to 1 and is incremented as each argument processed by a termcap % code,
+   however if something is pushed on the stack it's set to 0 and the
+   rest of the % codes are interpeted as terminfo % codes. Another way
+   of putting it is that if termcap equals one we haven't decided either
+   way yet, if it equals zero we're looking for terminfo codes, and if
+   its greater than 1 we're looking for termcap codes.
+
+   Terminfo % codes:
+
+       %%      output a '%'
+       %[[:][-+# ][width][.precision]][doxXs]
+               output pop according to the printf format
+       %c      output pop as a char
+       %'c'    push character constant c.
+       %{n}    push decimal constant n.
+       %p[1-9] push paramter [1-9]
+       %g[a-z] push variable [a-z]
+       %P[a-z] put pop in variable [a-z]
+       %l      push the length of pop (a string)
+       %+      add pop to pop and push the result
+       %-      subtract pop from pop and push the result
+       %*      multiply pop and pop and push the result
+       %&      bitwise and pop and pop and push the result
+       %|      bitwise or pop and pop and push the result
+       %^      bitwise xor pop and pop and push the result
+       %~      push the bitwise not of pop
+       %=      compare if pop and pop are equal and push the result
+       %>      compare if pop is less than pop and push the result
+       %<      compare if pop is greater than pop and push the result
+       %A      logical and pop and pop and push the result
+       %O      logical or pop and pop and push the result
+       %!      push the logical not of pop
+       %? condition %t if_true [%e if_false] %;
+               if condtion evaulates as true then evaluate if_true,
+               else evaluate if_false. elseif's can be done:
+%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %;
+       %i      add one to parameters 1 and 2. (ANSI)
+
+  Termcap Codes:
+
+       %%      output a %
+       %.      output parameter as a character
+       %d      output parameter as a decimal number
+       %2      output parameter in printf format %02d
+       %3      output parameter in printf format %03d
+       %+x     add the character x to parameter and output it as a character
+(UW)   %-x     subtract parameter FROM the character x and output it as a char
+(UW)   %ax     add the character x to parameter
+(GNU)  %a[+*-/=][cp]x
+               GNU arithmetic.
+(UW)   %sx     subtract parameter FROM the character x
+       %>xy    if parameter > character x then add character y to parameter
+       %B      convert to BCD (parameter = (parameter/10)*16 + parameter%16)
+       %D      Delta Data encode (parameter = parameter - 2*(paramter%16))
+       %i      increment the first two parameters by one
+       %n      xor the first two parameters by 0140
+(GNU)  %m      xor the first two parameters by 0177
+       %r      swap the first two parameters
+(GNU)  %b      backup to previous parameter
+(GNU)  %f      skip this parameter
+
+  Note the two definitions of %a, the GNU defintion is used if the characters
+  after the 'a' are valid, otherwise the UW definition is used.
+
+  (GNU) used by GNU Emacs termcap libraries
+  (UW) used by the University of Waterloo (MFCF) termcap libraries
+
+*/
+
+char *tparm(const char *str, ...) {
+       static char OOPS[] = "OOPS";
+       static char buf[MAX_LINE];
+       register const char *sp;
+       register char *dp;
+       register char *fmt;
+       char conv_char;
+       char scan_for;
+       int scan_depth = 0, if_depth;
+       static int i, j;
+       static char *s, c;
+       char fmt_buf[MAX_LINE];
+       char sbuf[MAX_LINE];
+
+       va_start(tparm_args, str);
+
+       sp = str;
+       dp = buf;
+       scan_for = 0;
+       if_depth = 0;
+       argcnt = 0;
+       pos = 0;
+       termcap = 1;
+       while (*sp != '\0') {
+               switch(*sp) {
+               case '\\':
+                       if (scan_for) {
+                               if (*++sp != '\0')
+                                       sp++;
+                               break;
+                       }
+                       *dp++ = *sp++;
+                       if (*sp != '\0')
+                               *dp++ = *sp++;
+                       break;
+               case '%':
+                       sp++;
+                       if (scan_for) {
+                               if (*sp == scan_for && if_depth == scan_depth) {
+                                       if (scan_for == ';')
+                                               if_depth--;
+                                       scan_for = 0;
+                               } else if (*sp == '?')
+                                       if_depth++;
+                               else if (*sp == ';') {
+                                       if (if_depth == 0)
+                                               return OOPS;
+                                       else
+                                               if_depth--;
+                               }
+                               sp++;
+                               break;
+                       }
+                       fmt = NULL;
+                       switch(*sp) {
+                       case '%':
+                               *dp++ = *sp++;
+                               break;
+                       case '+':
+                               if (!termcap) {
+                                       if (popnum(&j) || popnum(&i))
+                                               return OOPS;
+                                       i += j;
+                                       if (pushnum(i))
+                                               return OOPS;
+                                       sp++;
+                                       break;
+                               }
+                               ;/* FALLTHROUGH */
+                       case 'C':
+                               if (*sp == 'C') {
+                                       if (getarg(termcap - 1, INTEGER, &i))
+                                               return OOPS;
+                                       if (i >= 96) {
+                                               i /= 96;
+                                               if (i == '$')
+                                                       *dp++ = '\\';
+                                               *dp++ = i;
+                                       }
+                               }
+                               fmt = "%c";
+                               /* FALLTHROUGH */
+                       case 'a':
+                               if (!termcap)
+                                       return OOPS;
+                               if (getarg(termcap - 1, INTEGER, (anyptr) &i))
+                                       return OOPS;
+                               if (*++sp == '\0')
+                                       return OOPS;
+                               if ((sp[1] == 'p' || sp[1] == 'c')
+                                   && sp[2] != '\0' && fmt == NULL) {
+                                       /* GNU aritmitic parameter, what they
+                                          realy need is terminfo.            */
+                                       int val, lc;
+                                       if (sp[1] == 'p'
+                                           && getarg(termcap - 1 + sp[2] - '@',
+                                                     INTEGER, (anyptr) &val))
+                                               return OOPS;
+                                       if (sp[1] == 'c') {
+                                               lc = cvtchar(sp + 2, &c) + 2;
+                                       /* Mask out 8th bit so \200 can be
+                                          used for \0 as per GNU doc's    */
+                                               val = c & 0177;
+                                       } else
+                                               lc = 2;
+                                       switch(sp[0]) {
+                                       case '=':
+                                               break;
+                                       case '+':
+                                               val = i + val;
+                                               break;
+                                       case '-':
+                                               val = i - val;
+                                               break;
+                                       case '*':
+                                               val = i * val;
+                                               break;
+                                       case '/':
+                                               val = i / val;
+                                               break;
+                                       default:
+                                       /* Not really GNU's %a after all... */
+                                               lc = cvtchar(sp, &c);
+                                               val = c + i;
+                                               break;
+                                       }
+                                       arg_list[termcap - 1].integer = val;
+                                       sp += lc;
+                                       break;
+                               }
+                               sp += cvtchar(sp, &c);
+                               arg_list[termcap - 1].integer = c + i;
+                               if (fmt == NULL)
+                                       break;
+                               sp--;
+                               /* FALLTHROUGH */
+                       case '-':
+                               if (!termcap) {
+                                       if (popnum(&j) || popnum(&i))
+                                               return OOPS;
+                                       i -= j;
+                                       if (pushnum(i))
+                                               return OOPS;
+                                       sp++;
+                                       break;
+                               }
+                               fmt = "%c";
+                               /* FALLTHROUGH */
+                       case 's':
+                               if (termcap && (fmt == NULL || *sp == '-')) {
+                                       if (getarg(termcap - 1, INTEGER, &i))
+                                               return OOPS;
+                                       if (*++sp == '\0')
+                                               return OOPS;
+                                       sp += cvtchar(sp, &c);
+                                       arg_list[termcap - 1].integer = c - i;
+                                       if (fmt == NULL)
+                                               break;
+                                       sp--;
+                               }
+                               if (!termcap)
+                                       return OOPS;
+                               ;/* FALLTHROUGH */
+                       case '.':
+                               if (termcap && fmt == NULL)
+                                       fmt = "%c";
+                               ;/* FALLTHROUGH */
+                       case 'd':
+                               if (termcap && fmt == NULL)
+                                       fmt = "%d";
+                               ;/* FALLTHROUGH */
+                       case '2':
+                               if (termcap && fmt == NULL)
+                                       fmt = "%02d";
+                               ;/* FALLTHROUGH */
+                       case '3':
+                               if (termcap && fmt == NULL)
+                                       fmt = "%03d";
+                               ;/* FALLTHROUGH */
+                       case ':': case ' ': case '#': case 'u':
+                       case 'x': case 'X': case 'o': case 'c':
+                       case '0': case '1': case '4': case '5':
+                       case '6': case '7': case '8': case '9':
+                               if (fmt == NULL) {
+                                       if (termcap)
+                                               return OOPS;
+                                       if (*sp == ':')
+                                               sp++;
+                                       fmt = fmt_buf;
+                                       *fmt++ = '%';
+                                       while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') {
+                                               if (*sp == '\0')
+                                                       return OOPS;
+                                               *fmt++ = *sp++;
+                                       }
+                                       *fmt++ = *sp;
+                                       *fmt = '\0';
+                                       fmt = fmt_buf;
+                               }
+                               conv_char = fmt[strlen(fmt) - 1];
+                               if (conv_char == 's') {
+                                       if (popstring(&s))
+                                               return OOPS;
+                                       sprintf(sbuf, fmt, s);
+                               } else {
+                                       if (termcap) {
+                                               if (getarg(termcap++ - 1,
+                                                          INTEGER, &i))
+                                                       return OOPS;
+                                       } else
+                                               if (popnum(&i))
+                                                       return OOPS;
+                                       if (i == 0 && conv_char == 'c')
+                                               *sbuf = 0;
+                                       else
+                                               sprintf(sbuf, fmt, i);
+                               }
+                               sp++;
+                               fmt = sbuf;
+                               while(*fmt != '\0') {
+                                       if (*fmt == '$')
+                                               *dp++ = '\\';
+                                       *dp++ = *fmt++;
+                               }
+                               break;
+                       case 'r':
+                               if (!termcap || getarg(1, INTEGER, &i))
+                                       return OOPS;
+                               arg_list[1].integer = arg_list[0].integer;
+                               arg_list[0].integer = i;
+                               sp++;
+                               break;
+                       case 'i':
+                               if (getarg(1, INTEGER, &i)
+                                   || arg_list[0].type != INTEGER)
+                                       return OOPS;
+                               arg_list[1].integer++;
+                               arg_list[0].integer++;
+                               sp++;
+                               break;
+                       case 'n':
+                               if (!termcap || getarg(1, INTEGER, &i))
+                                       return OOPS;
+                               arg_list[0].integer ^= 0140;
+                               arg_list[1].integer ^= 0140;
+                               sp++;
+                               break;
+                       case '>':
+                               if (!termcap) {
+                                       if (popnum(&j) || popnum(&i))
+                                               return OOPS;
+                                       i = (i > j);
+                                       if (pushnum(i))
+                                               return OOPS;
+                                       sp++;
+                                       break;
+                               }
+                               if (getarg(termcap-1, INTEGER, &i))
+                                       return OOPS;
+                               sp += cvtchar(sp, &c);
+                               if (i > c) {
+                                       sp += cvtchar(sp, &c);
+                                       arg_list[termcap-1].integer += c;
+                               } else
+                                       sp += cvtchar(sp, &c);
+                               sp++;
+                               break;
+                       case 'B':
+                               if (!termcap || getarg(termcap-1, INTEGER, &i))
+                                       return OOPS;
+                               arg_list[termcap-1].integer = 16*(i/10)+i%10;
+                               sp++;
+                               break;
+                       case 'D':
+                               if (!termcap || getarg(termcap-1, INTEGER, &i))
+                                       return OOPS;
+                               arg_list[termcap-1].integer = i - 2 * (i % 16);
+                               sp++;
+                               break;
+                       case 'p':
+                               if (termcap > 1)
+                                       return OOPS;
+                               if (*++sp == '\0')
+                                       return OOPS;
+                               if (*sp == '0')
+                                       i = 9;
+                               else
+                                       i = *sp - '1';
+                               if (i < 0 || i > 9)
+                                       return OOPS;
+                               if (pusharg(i))
+                                       return OOPS;
+                               termcap = 0;
+                               sp++;
+                               break;
+                       case 'P':
+                               if (termcap || *++sp == '\0')
+                                       return OOPS;
+                               i = *sp++ - 'a';
+                               if (i < 0 || i > 25)
+                                       return OOPS;
+                               if (pos-- == 0)
+                                       return OOPS;
+                               switch(vars[i].type = S[pos].type) {
+                               case ARG:
+                                       vars[i].argnum = S[pos].argnum;
+                                       break;
+                               case NUM:
+                                       vars[i].value = S[pos].value;
+                                       break;
+                               }
+                               break;
+                       case 'g':
+                               if (termcap || *++sp == '\0')
+                                       return OOPS;
+                               i = *sp++ - 'a';
+                               if (i < 0 || i > 25)
+                                       return OOPS;
+                               switch(vars[i].type) {
+                               case ARG:
+                                       if (pusharg(vars[i].argnum))
+                                               return OOPS;
+                                       break;
+                               case NUM:
+                                       if (pushnum(vars[i].value))
+                                               return OOPS;
+                                       break;
+                               }
+                               break;
+                       case '\'':
+                               if (termcap > 1)
+                                       return OOPS;
+                               if (*++sp == '\0')
+                                       return OOPS;
+                               sp += cvtchar(sp, &c);
+                               if (pushnum(c) || *sp++ != '\'')
+                                       return OOPS;
+                               termcap = 0;
+                               break;
+                       case '{':
+                               if (termcap > 1)
+                                       return OOPS;
+                               i = 0;
+                               sp++;
+                               while(isdigit((int) (unsigned char) *sp))
+                                       i = 10 * i + *sp++ - '0';
+                               if (*sp++ != '}' || pushnum(i))
+                                       return OOPS;
+                               termcap = 0;
+                               break;
+                       case 'l':
+                               if (termcap || popstring(&s))
+                                       return OOPS;
+                               i = strlen(s);
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '*':
+                               if (termcap || popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i *= j;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '/':
+                               if (termcap || popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i /= j;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case 'm':
+                               if (termcap) {
+                                       if (getarg(1, INTEGER, &i))
+                                               return OOPS;
+                                       arg_list[0].integer ^= 0177;
+                                       arg_list[1].integer ^= 0177;
+                                       sp++;
+                                       break;
+                               }
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i %= j;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '&':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i &= j;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '|':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i |= j;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '^':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i ^= j;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '=':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i = (i == j);
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '<':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i = (i < j);
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case 'A':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i = (i && j);
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case 'O':
+                               if (popnum(&j) || popnum(&i))
+                                       return OOPS;
+                               i = (i || j);
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '!':
+                               if (popnum(&i))
+                                       return OOPS;
+                               i = !i;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '~':
+                               if (popnum(&i))
+                                       return OOPS;
+                               i = ~i;
+                               if (pushnum(i))
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case '?':
+                               if (termcap > 1)
+                                       return OOPS;
+                               termcap = 0;
+                               if_depth++;
+                               sp++;
+                               break;
+                       case 't':
+                               if (popnum(&i) || if_depth == 0)
+                                       return OOPS;
+                               if (!i) {
+                                       scan_for = 'e';
+                                       scan_depth = if_depth;
+                               }
+                               sp++;
+                               break;
+                       case 'e':
+                               if (if_depth == 0)
+                                       return OOPS;
+                               scan_for = ';';
+                               scan_depth = if_depth;
+                               sp++;
+                               break;
+                       case ';':
+                               if (if_depth-- == 0)
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case 'b':
+                               if (--termcap < 1)
+                                       return OOPS;
+                               sp++;
+                               break;
+                       case 'f':
+                               if (!termcap++)
+                                       return OOPS;
+                               sp++;
+                               break;
+                       }
+                       break;
+               default:
+                       if (scan_for)
+                               sp++;
+                       else
+                               *dp++ = *sp++;
+                       break;
+               }
+       }
+       va_end(tparm_args);
+       *dp = '\0';
+       return buf;
+}
diff --git a/apps/irssi/src/fe-text/utf8.c b/apps/irssi/src/fe-text/utf8.c
new file mode 100644 (file)
index 0000000..63a834c
--- /dev/null
@@ -0,0 +1,64 @@
+
+#define UTF8_COMPUTE(Char, Mask, Len)                                        \
+  if (Char < 128)                                                            \
+    {                                                                        \
+      Len = 1;                                                               \
+      Mask = 0x7f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xe0) == 0xc0)                                            \
+    {                                                                        \
+      Len = 2;                                                               \
+      Mask = 0x1f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf0) == 0xe0)                                            \
+    {                                                                        \
+      Len = 3;                                                               \
+      Mask = 0x0f;                                                           \
+    }                                                                        \
+  else if ((Char & 0xf8) == 0xf0)                                            \
+    {                                                                        \
+      Len = 4;                                                               \
+      Mask = 0x07;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfc) == 0xf8)                                            \
+    {                                                                        \
+      Len = 5;                                                               \
+      Mask = 0x03;                                                           \
+    }                                                                        \
+  else if ((Char & 0xfe) == 0xfc)                                            \
+    {                                                                        \
+      Len = 6;                                                               \
+      Mask = 0x01;                                                           \
+    }                                                                        \
+  else                                                                       \
+    Len = -1;
+
+#define UTF8_GET(Result, Chars, Count, Mask, Len)                            \
+  (Result) = (Chars)[0] & (Mask);                                            \
+  for ((Count) = 1; (Count) < (Len); ++(Count))                                      \
+    {                                                                        \
+      if (((Chars)[(Count)] & 0xc0) != 0x80)                                 \
+       {                                                                     \
+         (Result) = -1;                                                      \
+         break;                                                              \
+       }                                                                     \
+      (Result) <<= 6;                                                        \
+      (Result) |= ((Chars)[(Count)] & 0x3f);                                 \
+    }
+
+void get_utf8_char(const unsigned char **ptr)
+{
+       int i, mask = 0, len;
+       unsigned int result;
+       unsigned char c = (unsigned char) **ptr;
+
+       UTF8_COMPUTE(c, mask, len);
+       if (len == -1)
+               return;
+
+       UTF8_GET(result, *ptr, i, mask, len);
+       if (result == -1)
+                return;
+
+        *ptr += len-1;
+}
diff --git a/apps/irssi/src/fe-text/utf8.h b/apps/irssi/src/fe-text/utf8.h
new file mode 100644 (file)
index 0000000..3d8f378
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UTF8_H
+#define __UTF8_H
+
+void get_utf8_char(const unsigned char **ptr);
+
+#endif
diff --git a/apps/irssi/src/lib-config/.cvsignore b/apps/irssi/src/lib-config/.cvsignore
new file mode 100644 (file)
index 0000000..577aa17
--- /dev/null
@@ -0,0 +1,7 @@
+*.la
+*.lo
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
index 98e2a30b328dfa7cd09175f27bf9277e2203d2fc..124d710129c98d4bb601097a01a4aa0d25c241e2 100644 (file)
@@ -153,7 +153,7 @@ int config_get_bool(CONFIG_REC *rec, const char *section, const char *key, int d
        str = config_get_str(rec, section, key, NULL);
        if (str == NULL) return def;
 
-        return toupper(*str) == 'T' || toupper(*str) == 'Y';
+        return i_toupper(*str) == 'T' || i_toupper(*str) == 'Y';
 }
 
 /* Return value of key `value_key' from list item where `key' is `value' */
@@ -224,8 +224,8 @@ int config_node_get_bool(CONFIG_NODE *parent, const char *key, int def)
        str = config_node_get_str(parent, key, NULL);
        if (str == NULL) return def;
 
-       return toupper(*str) == 'T' || toupper(*str) == 'Y' ||
-               (toupper(*str) == 'O' && toupper(str[1]) == 'N');
+       return i_toupper(*str) == 'T' || i_toupper(*str) == 'Y' ||
+               (i_toupper(*str) == 'O' && i_toupper(str[1]) == 'N');
 }
 
 /* Get the value of keys `key' and `key_value' and put them to
@@ -308,3 +308,23 @@ CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index)
 
        return NULL;
 }
+
+/* Returns the first non-comment node in list */
+GSList *config_node_first(GSList *list)
+{
+       while (list != NULL) {
+               CONFIG_NODE *node = list->data;
+
+               if (node->type != NODE_TYPE_COMMENT)
+                        break;
+               list = list->next;
+       }
+       return list;
+}
+
+/* Returns the next non-comment node in list */
+GSList *config_node_next(GSList *list)
+{
+       list = list->next;
+        return config_node_first(list);
+}
index ea2e6c37739eafb77df42862d41883c8d6b15952..fc475f4ec3498c2a328a2a0df7dc0c950e3824b4 100644 (file)
@@ -100,6 +100,11 @@ CONFIG_NODE *config_list_find_node(CONFIG_REC *rec, const char *section, const c
 /* Returns n'th node from list. */
 CONFIG_NODE *config_node_index(CONFIG_NODE *node, int index);
 
+/* Returns the first non-comment node in list */
+GSList *config_node_first(GSList *list);
+/* Returns the next non-comment node in list */
+GSList *config_node_next(GSList *list);
+
 /* Setting values */
 int config_set_str(CONFIG_REC *rec, const char *section, const char *key, const char *value);
 int config_set_int(CONFIG_REC *rec, const char *section, const char *key, int value);
index 883a920b68443760472583c1574403ad32ac86e1..02eb4255f5ba4c5d6b9a5ef752589c166c1ff36c 100644 (file)
@@ -32,7 +32,7 @@ static unsigned int g_istr_hash(gconstpointer v)
        unsigned int h = 0, g;
 
        while (*s != '\0') {
-               h = (h << 4) + toupper(*s);
+               h = (h << 4) + i_toupper(*s);
                if ((g = h & 0xf0000000UL)) {
                        h = h ^ (g >> 24);
                        h = h ^ g;
@@ -82,11 +82,7 @@ static void config_parse_get_token(GScanner *scanner, CONFIG_NODE *node)
                } else {
                        if (scanner->token == G_TOKEN_INT) {
                                scanner->token = G_TOKEN_STRING;
-#undef g_strdup_printf /* This is free'd by GLib itself */
                                scanner->value.v_string = g_strdup_printf("%lu", scanner->value.v_int);
-#ifdef MEM_DEBUG
-#define g_strdup_printf(a, b...) ig_strdup_printf(__FILE__, __LINE__, a, ##b)
-#endif
                        }
                        break;
                }
index 446735cdcf8b3d052829de167f14e4b772229e92..194478279c0da77e3d4d8f5b5a86f645c3657d23 100644 (file)
@@ -77,7 +77,7 @@ static int config_has_specials(const char *text)
        g_return_val_if_fail(text != NULL, FALSE);
 
        while (*text != '\0') {
-               if (!isalnum((int) *text) && *text != '_')
+               if (!i_isalnum(*text) && *text != '_')
                        return TRUE;
                text++;
        }
index c09a0b301caaadc1b33ce72fcedeeb2df43e7664..a23ae8aa78b917f45b37921fff98f26e8da550b7 100644 (file)
@@ -452,7 +452,8 @@ int poptGetNextOpt(poptContext con) {
 
        if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE
                     && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) 
-           con->finalArgv[con->finalArgvCount++] = g_strdup(con->os->nextArg);
+           con->finalArgv[con->finalArgvCount++] =
+               strcpy(malloc(strlen(con->os->nextArg)+1), con->os->nextArg);
     }
 
     if (dup) g_free(dup);
index d064297c8b284d83c468d3c04627513662053d78..835798a5803529e42121b51659db3744fcbe87ca 100644 (file)
@@ -17,19 +17,19 @@ static void configLine(poptContext con, char * line) {
     
     if (strncmp(line, con->appName, nameLength)) return;
     line += nameLength;
-    if (!*line || !isspace(*line)) return;
-    while (*line && isspace(*line)) line++;
+    if (!*line || !i_isspace(*line)) return;
+    while (*line && i_isspace(*line)) line++;
     entryType = line;
 
-    while (!*line || !isspace(*line)) line++;
+    while (!*line || !i_isspace(*line)) line++;
     *line++ = '\0';
-    while (*line && isspace(*line)) line++;
+    while (*line && i_isspace(*line)) line++;
     if (!*line) return;
     opt = line;
 
-    while (!*line || !isspace(*line)) line++;
+    while (!*line || !i_isspace(*line)) line++;
     *line++ = '\0';
-    while (*line && isspace(*line)) line++;
+    while (*line && i_isspace(*line)) line++;
     if (!*line) return;
 
     if (opt[0] == '-' && opt[1] == '-')
@@ -92,7 +92,7 @@ int poptReadConfigFile(poptContext con, char * fn) {
          case '\n':
            *dst = '\0';
            dst = buf;
-           while (*dst && isspace(*dst)) dst++;
+           while (*dst && i_isspace(*dst)) dst++;
            if (*dst && *dst != '#') {
                configLine(con, dst);
            }
index c1876d74ed18369db56b91e33aea6a9bc5a45c7d..243f868e4bafe7024def119f383f86da6fc5d96c 100644 (file)
@@ -94,15 +94,15 @@ static void singleOptionHelp(FILE * f, int maxLeftCol,
     helpLength = strlen(help);
     while (helpLength > lineLength) {
        ch = help + lineLength - 1;
-       while (ch > help && !isspace(*ch)) ch--;
+       while (ch > help && !i_isspace(*ch)) ch--;
        if (ch == help) break;          /* give up */
-       while (ch > (help + 1) && isspace(*ch)) ch--;
+       while (ch > (help + 1) && i_isspace(*ch)) ch--;
        ch++;
 
        sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
        fprintf(f, format, help, " ");
        help = ch;
-       while (isspace(*help) && *help) help++;
+       while (i_isspace(*help) && *help) help++;
        helpLength = strlen(help);
     }
 
index eb2a6721633f9d931722fa22239e1e7aeca9736e..135ead56e2ac9d2623b846fed0e8d99875b8b741 100644 (file)
@@ -45,7 +45,7 @@ int poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr) {
                if (*src != quote) *buf++ = '\\';
            }
            *buf++ = *src;
-       } else if (isspace(*src)) {
+       } else if (isspace((int) (unsigned char) *src)) {
            if (*argv[argc]) {
                buf++, argc++;
                if (argc == argvAlloced) {
diff --git a/apps/irssi/src/perl/.cvsignore b/apps/irssi/src/perl/.cvsignore
new file mode 100644 (file)
index 0000000..189d2fd
--- /dev/null
@@ -0,0 +1,10 @@
+*.la
+*.lo
+*.o
+.deps
+.libs
+Makefile
+Makefile.in
+so_locations
+perl-signals-list.h
+irssi-core.pl.h
diff --git a/apps/irssi/src/perl/Makefile.am b/apps/irssi/src/perl/Makefile.am
new file mode 100644 (file)
index 0000000..81be9c6
--- /dev/null
@@ -0,0 +1,149 @@
+LIBTOOL = $(PERL_LIBTOOL)
+
+include $(top_srcdir)/Makefile.defines.in
+
+moduledir = $(silc_modulesdir)
+
+perl_dirs = common ui textui
+
+module_LTLIBRARIES = $(perl_module_lib) $(perl_module_fe_lib)
+noinst_LTLIBRARIES = $(perl_static_lib) $(perl_static_fe_lib)
+EXTRA_LTLIBRARIES = \
+       libperl_core.la libfe_perl.la \
+       libperl_core_static.la libfe_perl_static.la
+
+libperl_core_la_LDFLAGS = -avoid-version -rpath $(moduledir)
+libfe_perl_la_LDFLAGS = -avoid-version -rpath $(moduledir)
+
+perl-core.c: perl-signals-list.h irssi-core.pl.h
+
+INCLUDES = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/core \
+       -I$(top_srcdir)/src/fe-common/core \
+       -I$(top_srcdir)/src/fe-common/silc \
+       $(GLIB_CFLAGS) \
+       -DSCRIPTDIR=\""$(libdir)/irssi/scripts"\" \
+       -DPERL_USE_LIB=\""$(PERL_USE_LIB)"\" \
+       -DPERL_STATIC_LIBS=$(PERL_STATIC_LIBS) \
+       $(PERL_CFLAGS)
+
+perl_sources = \
+       perl-core.c \
+       perl-common.c \
+       perl-signals.c \
+       perl-sources.c
+
+perl_fe_sources = \
+       module-formats.c \
+       perl-fe.c
+
+noinst_HEADERS = \
+       module.h \
+       module-fe.h \
+       module-formats.h \
+       perl-core.h \
+       perl-common.h \
+       perl-signals.h \
+       perl-sources.h
+
+libperl_core_la_DEPENDENCIES = .libs/libperl_orig.a .libs/DynaLoader.a
+
+.libs/libperl_orig.a:
+       if [ ! -d .libs ]; then mkdir .libs; fi
+       rm -f .libs/libperl_orig.a
+       if [ x$(LIBPERL_A) = x ]; then touch .libs/libperl_orig.a; else $(LN_S) $(LIBPERL_A) .libs/libperl_orig.a; fi
+.libs/DynaLoader.a:
+       if [ ! -d .libs ]; then mkdir .libs; fi
+       rm -f .libs/DynaLoader.a
+       $(LN_S) $(DYNALOADER_A) .libs/DynaLoader.a
+
+libperl_core_la_SOURCES = \
+       $(perl_sources)
+
+libperl_core_static_la_SOURCES = \
+       $(perl_sources)
+
+libfe_perl_la_SOURCES = \
+       $(perl_fe_sources)
+
+libfe_perl_static_la_SOURCES = \
+       $(perl_fe_sources)
+
+perl-signals-list.h: $(top_srcdir)/docs/signals.txt $(srcdir)/get-signals.pl
+       cat $(top_srcdir)/docs/signals.txt | $(perlpath) $(srcdir)/get-signals.pl > perl-signals-list.h
+
+irssi-core.pl.h: irssi-core.pl
+       $(top_srcdir)/file2header.sh $(srcdir)/irssi-core.pl irssi_core_code > irssi-core.pl.h
+
+common_sources = \
+       common/Irssi.xs \
+       common/Irssi.pm \
+       common/Channel.xs \
+       common/Core.xs \
+       common/Ignore.xs \
+       common/Log.xs \
+       common/Masks.xs \
+       common/Query.xs \
+       common/Rawlog.xs \
+       common/Server.xs \
+       common/Settings.xs \
+       common/Makefile.PL.in \
+       common/typemap \
+       common/module.h
+
+ui_sources = \
+       ui/UI.xs \
+       ui/UI.pm \
+       ui/Formats.xs \
+       ui/Themes.xs \
+       ui/Window.xs \
+       ui/Makefile.PL.in \
+       ui/typemap \
+       ui/module.h
+
+textui_sources = \
+       textui/TextUI.xs \
+       textui/TextUI.pm \
+       textui/TextBuffer.xs \
+       textui/TextBufferView.xs \
+       textui/Statusbar.xs \
+       textui/Makefile.PL.in \
+       textui/typemap \
+       textui/module.h
+
+EXTRA_DIST = \
+       libperl_dynaloader.la \
+       libperl_orig.la \
+       get-signals.pl \
+       irssi-core.pl \
+       $(common_sources) \
+       $(ui_sources) \
+       $(textui_sources)
+
+all-local:
+       for dir in $(perl_dirs); do \
+         cd $$dir && \
+         if [ ! -f Makefile ]; then \
+            $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
+         fi && \
+         ($(MAKE) || $(MAKE)) && \
+         cd ..; \
+       done
+
+install-exec-local:
+       for dir in $(perl_dirs); do \
+         cd $$dir && $(MAKE) install && cd ..; \
+       done
+
+clean-generic:
+       for dir in $(perl_dirs); do rm -f $$dir/Makefile; done
+
+distclean-generic:
+       for dir in $(perl_dirs); do \
+         cd $$dir; \
+         $(MAKE) realclean; rm -f Makefile.PL; \
+         cd ..; \
+       done
+
+libperl_core_la_LIBADD = $(PERL_LDFLAGS)
diff --git a/apps/irssi/src/perl/common/.cvsignore b/apps/irssi/src/perl/common/.cvsignore
new file mode 100644 (file)
index 0000000..e965849
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.PL
+Irssi.bs
+*.c
+*.o
+pm_to_blib
+blib
diff --git a/apps/irssi/src/perl/common/Channel.xs b/apps/irssi/src/perl/common/Channel.xs
new file mode 100644 (file)
index 0000000..f425dc2
--- /dev/null
@@ -0,0 +1,124 @@
+#include "module.h"
+
+MODULE = Irssi::Channel  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+channels()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = channels; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data)));
+       }
+
+Irssi::Channel
+channel_find(channel)
+       char *channel
+CODE:
+       RETVAL = channel_find(NULL, channel);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Channel  PACKAGE = Irssi::Server
+#*******************************
+
+void
+channels(server)
+       Irssi::Server server
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data)));
+       }
+
+void
+channels_join(server, channels, automatic)
+       Irssi::Server server
+       char *channels
+       int automatic
+CODE:
+       server->channels_join(server, channels, automatic);
+
+Irssi::Channel
+channel_create(server, name, automatic)
+       Irssi::Server server
+       char *name
+       int automatic
+CODE:
+       CHAT_PROTOCOL(server)->channel_create(server, name, automatic);
+
+Irssi::Channel
+channel_find(server, name)
+       Irssi::Server server
+       char *name
+
+void
+nicks_get_same(server, nick)
+       Irssi::Server server
+        char *nick
+PREINIT:
+       GSList *list, *tmp;
+PPCODE:
+       list = nicklist_get_same(server, nick);
+
+       for (tmp = list; tmp != NULL; tmp = tmp->next->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHANNEL_REC *) tmp->data)));
+               XPUSHs(sv_2mortal(iobject_bless((NICK_REC *) tmp->next->data)));
+       }
+       g_slist_free(list);
+
+#*******************************
+MODULE = Irssi::Channel  PACKAGE = Irssi::Channel  PREFIX = channel_
+#*******************************
+
+void
+channel_destroy(channel)
+       Irssi::Channel channel
+
+void
+nick_insert(channel, nick)
+       Irssi::Channel channel
+       Irssi::Nick nick
+CODE:
+       nicklist_insert(channel, nick);
+
+void
+nick_remove(channel, nick)
+       Irssi::Channel channel
+       Irssi::Nick nick
+CODE:
+       nicklist_remove(channel, nick);
+
+Irssi::Nick
+nick_find(channel, nick)
+       Irssi::Channel channel
+       char *nick
+CODE:
+       RETVAL = nicklist_find(channel, nick);
+OUTPUT:
+       RETVAL
+
+Irssi::Nick
+nick_find_mask(channel, mask)
+       Irssi::Channel channel
+       char *mask
+CODE:
+       RETVAL = nicklist_find_mask(channel, mask);
+OUTPUT:
+       RETVAL
+
+void
+nicks(channel)
+       Irssi::Channel channel
+PREINIT:
+       GSList *list, *tmp;
+PPCODE:
+       list = nicklist_getnicks(channel);
+
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((NICK_REC *) tmp->data)));
+       }
+       g_slist_free(list);
diff --git a/apps/irssi/src/perl/common/Core.xs b/apps/irssi/src/perl/common/Core.xs
new file mode 100644 (file)
index 0000000..4db742c
--- /dev/null
@@ -0,0 +1,528 @@
+#include "module.h"
+#include "irssi-version.h"
+
+#define DEFAULT_COMMAND_CATEGORY "Perl scripts' commands"
+
+void perl_signal_add_hash(int priority, SV *sv)
+{
+       HV *hv;
+        HE *he;
+       I32 len;
+
+       if (!is_hvref(sv))
+               croak("Usage: Irssi::signal_add(hash)");
+
+        hv = hvref(sv);
+       hv_iterinit(hv);
+       while ((he = hv_iternext(hv)) != NULL)
+                perl_signal_add_to(hv_iterkey(he, &len), HeVAL(he), priority);
+}
+
+static void perl_command_bind_add_hash(int priority, SV *sv, char *category)
+{
+       HV *hv;
+        HE *he;
+       I32 len;
+
+        hv = hvref(sv);
+       hv_iterinit(hv);
+       while ((he = hv_iternext(hv)) != NULL)
+               perl_command_bind_to(hv_iterkey(he, &len), category, HeVAL(he), priority);
+}
+
+static void handle_command_bind(int priority, int items, SV *p0, SV *p1, SV *p2)
+{
+       char *category;
+       int hash;
+
+       hash = items > 0 && is_hvref(p0);
+       if (!hash) {
+               if (items < 2 || items > 3)
+                       croak("Usage: Irssi::command_bind(signal, func, category)");
+       } else if (items > 2)
+               croak("Usage: Irssi::command_bind(signals_hash, category)");
+
+       if (!hash) {
+               category = items < 3 ? DEFAULT_COMMAND_CATEGORY :
+                       (char *)SvPV(p2, PL_na);
+               perl_command_bind_to((char *)SvPV(p0, PL_na), category, p1, priority);
+       } else {
+               category = items < 2 ? DEFAULT_COMMAND_CATEGORY :
+                       (char *)SvPV(p1, PL_na);
+               perl_command_bind_add_hash(priority, p0, category);
+       }
+}
+
+MODULE = Irssi::Core  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+signal_emit(signal, ...)
+       char *signal
+CODE:
+       void *p[SIGNAL_MAX_ARGUMENTS];
+       int n;
+
+       memset(p, 0, sizeof(p));
+       for (n = 1; n < items && n < SIGNAL_MAX_ARGUMENTS+1; n++) {
+               if (SvPOKp(ST(n)))
+                       p[n-1] = SvPV(ST(n), PL_na);
+               else if (irssi_is_ref_object(ST(n)))
+                       p[n-1] = irssi_ref_object(ST(n));
+               else if (SvROK(ST(n)))
+                       p[n-1] = (void *) SvIV((SV*)SvRV(ST(n)));
+               else
+                       p[n-1] = NULL;
+       }
+       signal_emit(signal, items-1, p[0], p[1], p[2], p[3], p[4], p[5]);
+
+void
+signal_add(...)
+CODE:
+       if (items != 1 && items != 2)
+               croak("Usage: Irssi::signal_add(signal, func)");
+       if (items == 2)
+               perl_signal_add((char *)SvPV(ST(0),PL_na), ST(1));
+       else
+               perl_signal_add_hash(1, ST(0));
+
+void
+signal_add_first(...)
+CODE:
+       if (items != 1 && items != 2)
+               croak("Usage: Irssi::signal_add_first(signal, func)");
+       if (items == 2)
+               perl_signal_add_first((char *)SvPV(ST(0),PL_na), ST(1));
+       else
+               perl_signal_add_hash(0, ST(0));
+
+void
+signal_add_last(...)
+CODE:
+       if (items != 1 && items != 2)
+               croak("Usage: Irssi::signal_add_last(signal, func)");
+       if (items == 2)
+               perl_signal_add_last((char *)SvPV(ST(0),PL_na), ST(1));
+       else
+               perl_signal_add_hash(2, ST(0));
+
+void
+signal_remove(signal, func)
+       char *signal
+       SV *func
+CODE:
+       perl_signal_remove(signal, func);
+
+void
+signal_stop()
+
+void
+signal_stop_by_name(signal)
+       char *signal
+
+char *
+signal_get_emitted()
+CODE:
+       RETVAL = (char *) signal_get_emitted();
+OUTPUT:
+       RETVAL
+
+int
+signal_get_emitted_id()
+
+int
+timeout_add(msecs, func, data)
+       int msecs
+       SV *func
+       SV *data
+CODE:
+       RETVAL = perl_timeout_add(msecs, func, data);
+OUTPUT:
+       RETVAL
+
+void
+timeout_remove(tag)
+       int tag
+CODE:
+       perl_source_remove(tag);
+
+
+int
+INPUT_READ()
+CODE:
+       RETVAL = G_INPUT_READ;
+OUTPUT:
+       RETVAL
+
+int
+INPUT_WRITE()
+CODE:
+       RETVAL = G_INPUT_WRITE;
+OUTPUT:
+       RETVAL
+
+int
+input_add(source, condition, func, data)
+       int source
+       int condition
+       SV *func
+       SV *data
+CODE:
+       RETVAL = perl_input_add(source, condition, func, data);
+OUTPUT:
+       RETVAL
+
+void
+input_remove(tag)
+       int tag
+CODE:
+       perl_source_remove(tag);
+
+# maybe there's some easier way than this..? :)
+int
+MSGLEVEL_CRAP()
+CODE:
+       RETVAL = MSGLEVEL_CRAP;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_MSGS()
+CODE:
+       RETVAL = MSGLEVEL_MSGS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_PUBLIC()
+CODE:
+       RETVAL = MSGLEVEL_PUBLIC;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NOTICES()
+CODE:
+       RETVAL = MSGLEVEL_NOTICES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_SNOTES()
+CODE:
+       RETVAL = MSGLEVEL_SNOTES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CTCPS()
+CODE:
+       RETVAL = MSGLEVEL_CTCPS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_ACTIONS()
+CODE:
+       RETVAL = MSGLEVEL_ACTIONS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_JOINS()
+CODE:
+       RETVAL = MSGLEVEL_JOINS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_PARTS()
+CODE:
+       RETVAL = MSGLEVEL_PARTS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_QUITS()
+CODE:
+       RETVAL = MSGLEVEL_QUITS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_KICKS()
+CODE:
+       RETVAL = MSGLEVEL_KICKS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_MODES()
+CODE:
+       RETVAL = MSGLEVEL_MODES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_TOPICS()
+CODE:
+       RETVAL = MSGLEVEL_TOPICS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_WALLOPS()
+CODE:
+       RETVAL = MSGLEVEL_WALLOPS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_INVITES()
+CODE:
+       RETVAL = MSGLEVEL_INVITES;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NICKS()
+CODE:
+       RETVAL = MSGLEVEL_NICKS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_DCC()
+CODE:
+       RETVAL = MSGLEVEL_DCC;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_DCCMSGS()
+CODE:
+       RETVAL = MSGLEVEL_DCCMSGS;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CLIENTNOTICE()
+CODE:
+       RETVAL = MSGLEVEL_CLIENTNOTICE;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CLIENTCRAP()
+CODE:
+       RETVAL = MSGLEVEL_CLIENTCRAP;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_CLIENTERROR()
+CODE:
+       RETVAL = MSGLEVEL_CLIENTERROR;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_HILIGHT()
+CODE:
+       RETVAL = MSGLEVEL_HILIGHT;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_ALL()
+CODE:
+       RETVAL = MSGLEVEL_ALL;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NOHILIGHT()
+CODE:
+       RETVAL = MSGLEVEL_NOHILIGHT;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NO_ACT()
+CODE:
+       RETVAL = MSGLEVEL_NO_ACT;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_NEVER()
+CODE:
+       RETVAL = MSGLEVEL_NEVER;
+OUTPUT:
+       RETVAL
+
+int
+MSGLEVEL_LASTLOG()
+CODE:
+       RETVAL = MSGLEVEL_LASTLOG;
+OUTPUT:
+       RETVAL
+
+int
+level2bits(str)
+       char *str
+
+char *
+bits2level(bits)
+       int bits
+
+int
+combine_level(level, str)
+       int level
+       char *str
+
+void
+command(cmd)
+       char *cmd
+CODE:
+       perl_command(cmd, NULL, NULL);
+
+void
+commands()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = commands; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Command")));
+       }
+
+void
+command_bind_first(...)
+CODE:
+       handle_command_bind(0, items, ST(0), ST(1), ST(2));
+
+void
+command_bind(...)
+CODE:
+       handle_command_bind(1, items, ST(0), ST(1), ST(2));
+
+void
+command_bind_last(...)
+CODE:
+       handle_command_bind(2, items, ST(0), ST(1), ST(2));
+
+void
+command_runsub(cmd, data, server, item)
+       char *cmd
+       char *data
+       Irssi::Server server
+       Irssi::Windowitem item
+CODE:
+       perl_command_runsub(cmd, data, server, item);
+
+void
+command_unbind(cmd, func)
+       char *cmd
+       SV *func
+CODE:
+       perl_command_unbind(cmd, func);
+
+void
+command_set_options(cmd, options)
+       char *cmd
+       char *options
+
+void
+pidwait_add(pid)
+       int pid
+
+void 
+pidwait_remove(pid)
+       int pid
+
+char *
+parse_special(cmd, data="", flags=0)
+       char *cmd
+       char *data
+       int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       ret = parse_special_string(cmd, NULL, NULL, data, NULL, flags);
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
+
+char *
+get_irssi_dir()
+CODE:
+       RETVAL = (char *) get_irssi_dir();
+OUTPUT:
+       RETVAL
+
+char *
+get_irssi_config()
+CODE:
+       RETVAL = (char *) get_irssi_config();
+OUTPUT:
+       RETVAL
+
+char *
+version()
+PREINIT:
+       char version[100];
+CODE:
+       g_snprintf(version, sizeof(version), "%d.%04d",
+                  IRSSI_VERSION_DATE, IRSSI_VERSION_TIME);
+       RETVAL = version;
+OUTPUT:
+        RETVAL
+
+#*******************************
+MODULE = Irssi::Core   PACKAGE = Irssi::Server
+#*******************************
+
+void
+parse_special(server, cmd, data="", flags=0)
+       Irssi::Server server
+       char *cmd
+       char *data
+       int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       ret = parse_special_string(cmd, server, NULL, data, NULL, flags);
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
+
+void
+command(server, cmd)
+       Irssi::Server server
+       char *cmd
+CODE:
+       perl_command(cmd, server, NULL);
+
+
+#*******************************
+MODULE = Irssi::Core   PACKAGE = Irssi::Windowitem
+#*******************************
+
+void
+parse_special(item, cmd, data="", flags=0)
+       Irssi::Windowitem item
+       char *cmd
+       char *data
+       int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       ret = parse_special_string(cmd, item->server, item, data, NULL, flags);
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
+
+void
+command(item, cmd)
+       Irssi::Windowitem item
+       char *cmd
+CODE:
+       perl_command(cmd, item->server, item);
+
diff --git a/apps/irssi/src/perl/common/Ignore.xs b/apps/irssi/src/perl/common/Ignore.xs
new file mode 100644 (file)
index 0000000..e4452c7
--- /dev/null
@@ -0,0 +1,50 @@
+#include "module.h"
+
+MODULE = Irssi::Ignore  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+ignores()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Ignore")));
+       }
+
+int
+ignore_check(nick, host, channel, text, level)
+       char *nick
+       char *host
+       char *channel
+       char *text
+       int level
+CODE:
+       RETVAL = ignore_check(NULL, nick, host, channel, text, level);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Ignore  PACKAGE = Irssi::Server
+#*******************************
+
+int
+ignore_check(server, nick, host, channel, text, level)
+       Irssi::Server server
+       char *nick
+       char *host
+       char *channel
+       char *text
+       int level
+
+#*******************************
+MODULE = Irssi::Ignore  PACKAGE = Irssi::Ignore  PREFIX = ignore_
+#*******************************
+
+void
+ignore_add_rec(rec)
+       Irssi::Ignore rec
+
+void
+ignore_update_rec(rec)
+       Irssi::Ignore rec
diff --git a/apps/irssi/src/perl/common/Irssi.bs b/apps/irssi/src/perl/common/Irssi.bs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/irssi/src/perl/common/Irssi.pm b/apps/irssi/src/perl/common/Irssi.pm
new file mode 100644 (file)
index 0000000..ce35c52
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+sub VERSION {
+  my $version = $_[1];
+  die "This script requires irssi version $version or later"
+    if ($version > version());
+}
+
+sub EXPORT_ALL () {
+  no strict 'refs';
+  @EXPORT_OK = grep { /[a-z]/ && defined *{$_}{CODE} } keys %Irssi::;
+}
+
+$VERSION = "0.9";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw(INPUT_READ INPUT_WRITE
+       MSGLEVEL_CRAP MSGLEVEL_MSGS MSGLEVEL_PUBLIC MSGLEVEL_NOTICES
+       MSGLEVEL_SNOTES MSGLEVEL_CTCPS MSGLEVEL_ACTIONS MSGLEVEL_JOINS
+       MSGLEVEL_PARTS MSGLEVEL_QUITS MSGLEVEL_KICKS MSGLEVEL_MODES
+       MSGLEVEL_TOPICS MSGLEVEL_WALLOPS MSGLEVEL_INVITES MSGLEVEL_NICKS
+       MSGLEVEL_DCC MSGLEVEL_DCCMSGS MSGLEVEL_CLIENTNOTICE MSGLEVEL_CLIENTCRAP
+       MSGLEVEL_CLIENTERROR MSGLEVEL_HILIGHT MSGLEVEL_ALL MSGLEVEL_NOHILIGHT
+       MSGLEVEL_NO_ACT MSGLEVEL_NEVER MSGLEVEL_LASTLOG
+);
+@EXPORT_OK = qw();
+
+bootstrap Irssi $VERSION if (!Irssi::Core::is_static());
+
+@Irssi::Channel::ISA = qw(Irssi::Windowitem);
+@Irssi::Query::ISA = qw(Irssi::Windowitem);
+
+Irssi::init();
+
+Irssi::EXPORT_ALL();
+
+1;
+
diff --git a/apps/irssi/src/perl/common/Irssi.xs b/apps/irssi/src/perl/common/Irssi.xs
new file mode 100644 (file)
index 0000000..67d5e96
--- /dev/null
@@ -0,0 +1,33 @@
+#include "module.h"
+
+static int initialized = FALSE;
+
+MODULE = Irssi  PACKAGE = Irssi
+
+PROTOTYPES: ENABLE
+
+void
+init()
+CODE:
+       if (initialized) return;
+       perl_api_version_check("Irssi");
+       initialized = TRUE;
+
+        perl_settings_init();
+
+void
+deinit()
+CODE:
+       if (!initialized) return;
+        perl_settings_deinit();
+
+BOOT:
+        irssi_boot(Channel);
+       irssi_boot(Core);
+       irssi_boot(Ignore);
+       irssi_boot(Log);
+       irssi_boot(Masks);
+       irssi_boot(Query);
+       irssi_boot(Rawlog);
+       irssi_boot(Server);
+       irssi_boot(Settings);
diff --git a/apps/irssi/src/perl/common/Log.xs b/apps/irssi/src/perl/common/Log.xs
new file mode 100644 (file)
index 0000000..c87ee45
--- /dev/null
@@ -0,0 +1,67 @@
+#include "module.h"
+
+MODULE = Irssi::Log  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+logs()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = logs; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Log")));
+       }
+
+Irssi::Log
+log_create_rec(fname, level)
+       char *fname
+       int level
+
+Irssi::Log
+log_find(fname)
+       char *fname
+
+#*******************************
+MODULE = Irssi::Log  PACKAGE = Irssi::Log  PREFIX = log_
+#*******************************
+
+void
+log_item_add(log, type, name, servertag)
+       Irssi::Log log
+       int type
+       char *name
+       char *servertag
+
+void
+log_item_destroy(log, item)
+       Irssi::Log log
+       Irssi::Logitem item
+
+Irssi::Logitem
+log_item_find(log, type, item, servertag)
+       Irssi::Log log
+       int type
+       char *item
+       char *servertag
+
+void
+log_update(log)
+       Irssi::Log log
+
+void
+log_close(log)
+       Irssi::Log log
+
+void
+log_write_rec(log, str, level)
+       Irssi::Log log
+       char *str
+       int level
+
+void
+log_start_logging(log)
+       Irssi::Log log
+
+void
+log_stop_logging(log)
+       Irssi::Log log
diff --git a/apps/irssi/src/perl/common/Makefile.PL.in b/apps/irssi/src/perl/common/Makefile.PL.in
new file mode 100644 (file)
index 0000000..4e545e7
--- /dev/null
@@ -0,0 +1,7 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi',
+              'LIBS' => '',
+             'OBJECT' => '$(O_FILES)',
+              'INC' => '-I../../.. -I@top_srcdir@ -I@top_srcdir@/src -I@top_srcdir@/src/core @GLIB_CFLAGS@',
+             'VERSION_FROM' => '@srcdir@/Irssi.pm');
diff --git a/apps/irssi/src/perl/common/Masks.xs b/apps/irssi/src/perl/common/Masks.xs
new file mode 100644 (file)
index 0000000..1ea1396
--- /dev/null
@@ -0,0 +1,61 @@
+#include "module.h"
+
+MODULE = Irssi::Masks  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+int
+mask_match(mask, nick, user, host)
+       char *mask
+       char *nick
+       char *user
+       char *host
+CODE:
+       RETVAL = mask_match(NULL, mask, nick, user, host);
+OUTPUT:
+       RETVAL
+
+int
+mask_match_address(mask, nick, address)
+       char *mask
+       char *nick
+       char *address
+CODE:
+       RETVAL = mask_match_address(NULL, mask, nick, address);
+OUTPUT:
+       RETVAL
+
+int
+masks_match(masks, nick, address)
+       char *masks
+       char *nick
+       char *address
+CODE:
+       RETVAL = masks_match(NULL, masks, nick, address);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Masks  PACKAGE = Irssi::Server
+#*******************************
+
+int
+mask_match(server, mask, nick, user, host)
+       Irssi::Server server
+       char *mask
+       char *nick
+       char *user
+       char *host
+
+int
+mask_match_address(server, mask, nick, address)
+       Irssi::Server server
+       char *mask
+       char *nick
+       char *address
+
+int
+masks_match(server, masks, nick, address)
+       Irssi::Server server
+       char *masks
+       char *nick
+       char *address
diff --git a/apps/irssi/src/perl/common/Query.xs b/apps/irssi/src/perl/common/Query.xs
new file mode 100644 (file)
index 0000000..54a0582
--- /dev/null
@@ -0,0 +1,57 @@
+#include "module.h"
+
+MODULE = Irssi::Query  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+queries()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               XPUSHs(sv_2mortal(iobject_bless(rec)));
+       }
+
+Irssi::Query
+query_find(nick)
+       char *nick
+CODE:
+       RETVAL = query_find(NULL, nick);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::Query  PACKAGE = Irssi::Server
+#*******************************
+
+void
+queries(server)
+       Irssi::Server server
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
+               QUERY_REC *rec = tmp->data;
+
+               XPUSHs(sv_2mortal(iobject_bless(rec)));
+       }
+
+Irssi::Query
+query_find(server, nick)
+       Irssi::Server server
+       char *nick
+
+#*******************************
+MODULE = Irssi::Query  PACKAGE = Irssi::Query  PREFIX = query_
+#*******************************
+
+void
+query_destroy(query)
+       Irssi::Query query
+
+void
+query_change_server(query, server)
+       Irssi::Query query
+       Irssi::Server server
diff --git a/apps/irssi/src/perl/common/Rawlog.xs b/apps/irssi/src/perl/common/Rawlog.xs
new file mode 100644 (file)
index 0000000..dd95ce5
--- /dev/null
@@ -0,0 +1,58 @@
+#include "module.h"
+
+MODULE = Irssi::Rawlog  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+rawlog_set_size(lines)
+       int lines
+
+Irssi::Rawlog
+rawlog_create()
+
+#*******************************
+MODULE = Irssi::Rawlog  PACKAGE = Irssi::Rawlog  PREFIX = rawlog_
+#*******************************
+
+void
+rawlog_get_lines(rawlog)
+       Irssi::Rawlog rawlog
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = rawlog->lines; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(new_pv(tmp->data)));
+       }
+
+void
+rawlog_destroy(rawlog)
+       Irssi::Rawlog rawlog
+
+void
+rawlog_input(rawlog, str)
+       Irssi::Rawlog rawlog
+       char *str
+
+void
+rawlog_output(rawlog, str)
+       Irssi::Rawlog rawlog
+       char *str
+
+void
+rawlog_redirect(rawlog, str)
+       Irssi::Rawlog rawlog
+       char *str
+
+void
+rawlog_open(rawlog, fname)
+       Irssi::Rawlog rawlog
+       char *fname
+
+void
+rawlog_close(rawlog)
+       Irssi::Rawlog rawlog
+
+void
+rawlog_save(rawlog, fname)
+       Irssi::Rawlog rawlog
+       char *fname
diff --git a/apps/irssi/src/perl/common/Server.xs b/apps/irssi/src/perl/common/Server.xs
new file mode 100644 (file)
index 0000000..adcabb1
--- /dev/null
@@ -0,0 +1,104 @@
+#include "module.h"
+
+MODULE = Irssi::Server  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+servers()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = servers; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((SERVER_REC *) tmp->data)));
+       }
+
+void
+reconnects()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = reconnects; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::Reconnect")));
+       }
+
+void
+chatnets()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(iobject_bless((CHATNET_REC *) tmp->data)));
+       }
+
+Irssi::Connect
+server_create_conn(chat_type, dest, port=6667, chatnet=NULL, password=NULL, nick=NULL)
+       int chat_type
+       char *dest
+       int port
+       char *chatnet
+       char *password
+       char *nick
+
+Irssi::Server
+server_find_tag(tag)
+       char *tag
+
+Irssi::Server
+server_find_chatnet(chatnet)
+       char *chatnet
+
+Irssi::Chatnet
+chatnet_find(name)
+       char *name
+
+#*******************************
+MODULE = Irssi::Server PACKAGE = Irssi::Server  PREFIX = server_
+#*******************************
+
+void
+server_disconnect(server)
+       Irssi::Server server
+
+void
+server_ref(server)
+       Irssi::Server server
+
+void
+server_unref(server)
+       Irssi::Server server
+
+int
+isnickflag(server, flag)
+       Irssi::Server server
+       char flag
+CODE:
+       RETVAL = server->isnickflag(flag);
+OUTPUT:
+       RETVAL
+
+int
+ischannel(server, data)
+       Irssi::Server server
+       char *data
+CODE:
+       RETVAL = server->ischannel(server, data);
+OUTPUT:
+       RETVAL
+
+char *
+get_nick_flags(server)
+       Irssi::Server server
+CODE:
+       RETVAL = (char *) server->get_nick_flags();
+OUTPUT:
+       RETVAL
+
+void
+send_message(server, target, msg, target_type)
+       Irssi::Server server
+       char *target
+       char *msg
+       int target_type
+CODE:
+       server->send_message(server, target, msg, target_type);
+
diff --git a/apps/irssi/src/perl/common/Settings.xs b/apps/irssi/src/perl/common/Settings.xs
new file mode 100644 (file)
index 0000000..a7c5e6f
--- /dev/null
@@ -0,0 +1,134 @@
+#include "module.h"
+#include "misc.h"
+
+static GHashTable *perl_settings;
+
+static void perl_settings_add(const char *key)
+{
+       PERL_SCRIPT_REC *script;
+        GSList *list;
+
+       script = perl_script_find_package(perl_get_package());
+       g_return_if_fail(script != NULL);
+
+       list = g_hash_table_lookup(perl_settings, script);
+        list = g_slist_append(list, g_strdup(key));
+       g_hash_table_insert(perl_settings, script, list);
+}
+
+static void perl_settings_remove(const char *key)
+{
+       PERL_SCRIPT_REC *script;
+        GSList *list, *pos;
+
+       script = perl_script_find_package(perl_get_package());
+       g_return_if_fail(script != NULL);
+
+       list = g_hash_table_lookup(perl_settings, script);
+       pos = gslist_find_icase_string(list, key);
+       if (pos != NULL) {
+               list = g_slist_remove(list, pos->data);
+               g_hash_table_insert(perl_settings, script, list);
+       }
+}
+
+static void perl_settings_free(PERL_SCRIPT_REC *script, GSList *list)
+{
+       g_slist_foreach(list, (GFunc) g_free, NULL);
+        g_slist_free(list);
+}
+
+static void sig_script_destroyed(PERL_SCRIPT_REC *script)
+{
+       GSList *list;
+
+       list = g_hash_table_lookup(perl_settings, script);
+       if (list != NULL) {
+                g_slist_foreach(list, (GFunc) settings_remove, NULL);
+               perl_settings_free(script, list);
+               g_hash_table_remove(perl_settings, script);
+       }
+}
+
+void perl_settings_init(void)
+{
+       perl_settings = g_hash_table_new((GHashFunc) g_direct_hash,
+                                        (GCompareFunc) g_direct_equal);
+        signal_add("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+}
+
+void perl_settings_deinit(void)
+{
+        signal_remove("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+
+       g_hash_table_foreach(perl_settings, (GHFunc) perl_settings_free, NULL);
+       g_hash_table_destroy(perl_settings);
+}
+
+MODULE = Irssi::Settings  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+char *
+settings_get_str(key)
+       char *key
+CODE:
+       RETVAL = (char *) settings_get_str(key);
+OUTPUT:
+       RETVAL
+
+int
+settings_get_int(key)
+       char *key
+
+int
+settings_get_bool(key)
+       char *key
+
+void
+settings_set_str(key, value)
+       char *key
+       char *value
+
+void
+settings_set_int(key, value)
+       char *key
+       int value
+
+void
+settings_set_bool(key, value)
+       char *key
+       int value
+
+void
+settings_add_str(section, key, def)
+       char *section
+       char *key
+       char *def
+CODE:
+        perl_settings_add(key);
+       settings_add_str_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_add_int(section, key, def)
+       char *section
+       char *key
+       int def
+CODE:
+        perl_settings_add(key);
+       settings_add_int_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_add_bool(section, key, def)
+       char *section
+       char *key
+       int def
+CODE:
+        perl_settings_add(key);
+       settings_add_bool_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_remove(key)
+       char *key
+CODE:
+        perl_settings_remove(key);
+       settings_remove(key);
diff --git a/apps/irssi/src/perl/common/module.h b/apps/irssi/src/perl/common/module.h
new file mode 100644 (file)
index 0000000..5456f63
--- /dev/null
@@ -0,0 +1,44 @@
+#define NEED_PERL_H
+#define HAVE_CONFIG_H
+#include "../module.h"
+#include <XSUB.h>
+
+#include "network.h"
+#include "levels.h"
+#include "commands.h"
+#include "log.h"
+#include "rawlog.h"
+#include "ignore.h"
+#include "settings.h"
+#include "masks.h"
+#include "special-vars.h"
+#include "window-item-def.h"
+
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "servers-reconnect.h"
+#include "servers-setup.h"
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+
+#include "perl/perl-core.h"
+#include "perl/perl-common.h"
+#include "perl/perl-signals.h"
+
+typedef COMMAND_REC *Irssi__Command;
+typedef LOG_REC *Irssi__Log;
+typedef LOG_ITEM_REC *Irssi__Logitem;
+typedef RAWLOG_REC *Irssi__Rawlog;
+typedef IGNORE_REC *Irssi__Ignore;
+typedef MODULE_REC *Irssi__Module;
+typedef WI_ITEM_REC *Irssi__Windowitem;
+
+typedef CHATNET_REC *Irssi__Chatnet;
+typedef SERVER_REC *Irssi__Server;
+typedef SERVER_CONNECT_REC *Irssi__Connect;
+typedef RECONNECT_REC *Irssi__Reconnect;
+typedef CHANNEL_REC *Irssi__Channel;
+typedef QUERY_REC *Irssi__Query;
+typedef NICK_REC *Irssi__Nick;
diff --git a/apps/irssi/src/perl/common/typemap b/apps/irssi/src/perl/common/typemap
new file mode 100644 (file)
index 0000000..5b7e0c3
--- /dev/null
@@ -0,0 +1,32 @@
+TYPEMAP
+Irssi::Chatnet         T_IrssiObj
+Irssi::Server          T_IrssiObj
+Irssi::Connect         T_IrssiObj
+Irssi::Reconnect       T_PlainObj
+Irssi::Channel         T_IrssiObj
+Irssi::Query           T_IrssiObj
+Irssi::Command         T_PlainObj
+Irssi::Nick            T_IrssiObj
+Irssi::Ignore          T_PlainObj
+Irssi::Log             T_PlainObj
+Irssi::Logitem         T_PlainObj
+Irssi::Rawlog          T_PlainObj
+Irssi::Module          T_PlainObj
+Irssi::Windowitem      T_IrssiObj
+
+INPUT
+
+T_IrssiObj
+       $var = irssi_ref_object($arg)
+
+T_PlainObj
+       $var = irssi_ref_object($arg)
+
+OUTPUT
+
+T_IrssiObj
+       $arg = iobject_bless((SERVER_REC *)$var);
+
+T_PlainObj
+       $arg = plain_bless($var, \"$type\");
+
diff --git a/apps/irssi/src/perl/get-signals.pl b/apps/irssi/src/perl/get-signals.pl
new file mode 100755 (executable)
index 0000000..df4667c
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+print "static PERL_SIGNAL_ARGS_REC perl_signal_args[] =\n{\n";
+
+while (<STDIN>) {
+       chomp;
+
+       next if (!/^ "([^"]*)"(<.*>)?,\s*(.*)/);
+       next if (/\.\.\./);
+       next if (/\(/);
+
+       $signal = $1;
+       $_ = $3;
+
+       s/GList \* of ([^,]*)/glistptr_\1/g;
+       s/GSList of (\w+)s/gslist_\1/g;
+
+       s/char \*[^,]*/string/g;
+       s/ulong \*[^,]*/ulongptr/g;
+       s/int \*[^,]*/intptr/g;
+       s/int [^,]*/int/g;
+
+       # core
+        s/CHATNET_REC[^,]*/iobject/g;
+        s/SERVER_REC[^,]*/iobject/g;
+        s/RECONNECT_REC[^,]*/iobject/g;
+       s/CHANNEL_REC[^,]*/iobject/g;
+       s/QUERY_REC[^,]*/iobject/g;
+       s/COMMAND_REC[^,]*/Irssi::Command/g;
+       s/NICK_REC[^,]*/iobject/g;
+       s/LOG_REC[^,]*/Irssi::Log/g;
+       s/RAWLOG_REC[^,]*/Irssi::Rawlog/g;
+       s/IGNORE_REC[^,]*/Irssi::Ignore/g;
+       s/MODULE_REC[^,]*/Irssi::Module/g;
+
+       # irc
+       s/BAN_REC[^,]*/Irssi::Irc::Ban/g;
+       s/NETSPLIT_REC[^,]*/Irssi::Irc::Netsplit/g;
+       s/NETSPLIT_SERVER_REC[^,]*/Irssi::Irc::Netsplitserver/g;
+
+       # irc modules
+       s/DCC_REC[^,]*/siobject/g;
+       s/AUTOIGNORE_REC[^,]*/Irssi::Irc::Autoignore/g;
+       s/NOTIFYLIST_REC[^,]*/Irssi::Irc::Notifylist/g;
+
+       # fe-common
+       s/THEME_REC[^,]*/Irssi::UI::Theme/g;
+       s/KEYINFO_REC[^,]*/Irssi::UI::Keyinfo/g;
+       s/PROCESS_REC[^,]*/Irssi::UI::Process/g;
+       s/TEXT_DEST_REC[^,]*/Irssi::UI::TextDest/g;
+       s/WINDOW_REC[^,]*/Irssi::UI::Window/g;
+       s/WI_ITEM_REC[^,]*/iobject/g;
+
+       s/([\w\*:]+)(,|$)/"\1"\2/g;
+       print "    { \"$signal\", { $_, NULL } },\n";
+}
+
+print "\n    { NULL }\n};\n";
diff --git a/apps/irssi/src/perl/irssi-core.pl b/apps/irssi/src/perl/irssi-core.pl
new file mode 100644 (file)
index 0000000..31fbe48
--- /dev/null
@@ -0,0 +1,47 @@
+# NOTE: this is printed through printf()-like function,
+# so no extra percent characters.
+
+# %%d : must be first - 1 if perl libraries are to be linked 
+#       statically with irssi binary, 0 if not
+# %%s : must be second - use Irssi; use Irssi::Irc; etc..
+package Irssi::Core;
+
+use Symbol qw(delete_package);
+
+sub is_static {
+  return %d;
+}
+
+sub destroy {
+  delete_package($_[0]);
+}
+
+sub eval_data {
+  my ($data, $id) = @_;
+  destroy("Irssi::Script::$id");
+
+  my $package = "Irssi::Script::$id";
+  my $eval = qq{package $package; %s sub handler { $data; }};
+  {
+      # hide our variables within this block
+      my ($filename, $package, $data);
+      eval $eval;
+  }
+  die $@ if $@;
+
+  eval {$package->handler;};
+  die $@ if $@;
+}
+
+sub eval_file {
+  my ($filename, $id) = @_;
+
+  local *FH;
+  open FH, $filename or die "File not found: $filename";
+  local($/) = undef;
+  my $data = <FH>;
+  close FH;
+  local($/) = "\n";
+
+  eval_data($data, $id);
+}
diff --git a/apps/irssi/src/perl/irssi-core.pl.h b/apps/irssi/src/perl/irssi-core.pl.h
new file mode 100644 (file)
index 0000000..3e4ec9a
--- /dev/null
@@ -0,0 +1,49 @@
+const char *irssi_core_code =
+"# NOTE: this is printed through printf()-like function,\n"
+"# so no extra percent characters.\n"
+"\n"
+"# %%d : must be first - 1 if perl libraries are to be linked \n"
+"#       statically with irssi binary, 0 if not\n"
+"# %%s : must be second - use Irssi; use Irssi::Irc; etc..\n"
+"package Irssi::Core;\n"
+"\n"
+"use Symbol qw(delete_package);\n"
+"\n"
+"sub is_static {\n"
+"  return %d;\n"
+"}\n"
+"\n"
+"sub destroy {\n"
+"  delete_package($_[0]);\n"
+"}\n"
+"\n"
+"sub eval_data {\n"
+"  my ($data, $id) = @_;\n"
+"  destroy(\"Irssi::Script::$id\");\n"
+"\n"
+"  my $package = \"Irssi::Script::$id\";\n"
+"  my $eval = qq{package $package; %s sub handler { $data; }};\n"
+"  {\n"
+"      # hide our variables within this block\n"
+"      my ($filename, $package, $data);\n"
+"      eval $eval;\n"
+"  }\n"
+"  die $@ if $@;\n"
+"\n"
+"  eval {$package->handler;};\n"
+"  die $@ if $@;\n"
+"}\n"
+"\n"
+"sub eval_file {\n"
+"  my ($filename, $id) = @_;\n"
+"\n"
+"  local *FH;\n"
+"  open FH, $filename or die \"File not found: $filename\";\n"
+"  local($/) = undef;\n"
+"  my $data = <FH>;\n"
+"  close FH;\n"
+"  local($/) = \"\\n\";\n"
+"\n"
+"  eval_data($data, $id);\n"
+"}\n"
+;
diff --git a/apps/irssi/src/perl/libperl_dynaloader.la b/apps/irssi/src/perl/libperl_dynaloader.la
new file mode 100644 (file)
index 0000000..9117cdb
--- /dev/null
@@ -0,0 +1,25 @@
+# libsilc.la - a libtool library file
+# Generated by ltmain.sh - GNU libtool 1.3.5 (1.385.2.206 2000/05/27 11:12:27)
+
+# The name that we can dlopen(3).
+dlname=''
+
+# Names of this library.
+library_names=''
+
+# The name of the static archive.
+old_library='DynaLoader.a'
+
+# Libraries that this one depends upon.
+dependency_libs=''
+
+# Version information for libsilc.
+current=0
+age=0
+revision=0
+
+# Is this an already installed library?
+installed=no
+
+# Directory that this library needs to be installed in:
+libdir=''
diff --git a/apps/irssi/src/perl/libperl_orig.la b/apps/irssi/src/perl/libperl_orig.la
new file mode 100644 (file)
index 0000000..c83ffc4
--- /dev/null
@@ -0,0 +1,25 @@
+# libsilc.la - a libtool library file
+# Generated by ltmain.sh - GNU libtool 1.3.5 (1.385.2.206 2000/05/27 11:12:27)
+
+# The name that we can dlopen(3).
+dlname=''
+
+# Names of this library.
+library_names=''
+
+# The name of the static archive.
+old_library='libperl_orig.a'
+
+# Libraries that this one depends upon.
+dependency_libs=''
+
+# Version information for libsilc.
+current=0
+age=0
+revision=0
+
+# Is this an already installed library?
+installed=no
+
+# Directory that this library needs to be installed in:
+libdir=''
diff --git a/apps/irssi/src/perl/module-fe.h b/apps/irssi/src/perl/module-fe.h
new file mode 100644 (file)
index 0000000..2dc8c28
--- /dev/null
@@ -0,0 +1,4 @@
+#include "module.h"
+
+#undef MODULE_NAME
+#define MODULE_NAME "fe-common/perl"
diff --git a/apps/irssi/src/perl/module-formats.c b/apps/irssi/src/perl/module-formats.c
new file mode 100644 (file)
index 0000000..5c2e1c6
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ module-formats.c : irssi
+
+    Copyright (C) 2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "formats.h"
+
+FORMAT_REC feperl_formats[] = {
+       { MODULE_NAME, "Core", 0 },
+
+       /* ---- */
+       { NULL, "Perl", 0 },
+
+       { "script_not_found", "Script {hilight $0} not found", 1, { 0 } },
+       { "script_not_loaded", "Script {hilight $0} is not loaded", 1, { 0 } },
+       { "script_loaded", "Loaded script {hilight $0}", 2, { 0, 0 } },
+       { "script_unloaded", "Unloaded script {hilight $0}", 1, { 0 } },
+       { "no_scripts_loaded", "No scripts are loaded", 0 },
+       { "script_list_header", "Loaded scripts:", 0 },
+       { "script_list_line", "$[!15]0 $1", 2, { 0, 0 } },
+       { "script_list_footer", "", 0 },
+       { "script_error", "{error Error in script {hilight $0}:}", 1, { 0 } },
+
+       { NULL, NULL, 0 }
+};
diff --git a/apps/irssi/src/perl/module-formats.h b/apps/irssi/src/perl/module-formats.h
new file mode 100644 (file)
index 0000000..74d2e13
--- /dev/null
@@ -0,0 +1,19 @@
+#include "formats.h"
+
+enum {
+       IRCTXT_MODULE_NAME,
+
+       IRCTXT_FILL_1,
+
+        TXT_SCRIPT_NOT_FOUND,
+        TXT_SCRIPT_NOT_LOADED,
+        TXT_SCRIPT_LOADED,
+        TXT_SCRIPT_UNLOADED,
+        TXT_NO_SCRIPTS_LOADED,
+        TXT_SCRIPT_LIST_HEADER,
+        TXT_SCRIPT_LIST_LINE,
+        TXT_SCRIPT_LIST_FOOTER,
+        TXT_SCRIPT_ERROR
+};
+
+extern FORMAT_REC feperl_formats[];
diff --git a/apps/irssi/src/perl/module.h b/apps/irssi/src/perl/module.h
new file mode 100644 (file)
index 0000000..e12482a
--- /dev/null
@@ -0,0 +1,25 @@
+#ifdef NEED_PERL_H
+#  include <EXTERN.h>
+#  ifndef _SEM_SEMUN_UNDEFINED
+#    define HAS_UNION_SEMUN
+#  endif
+#  include <perl.h>
+
+#  undef _
+#  undef PACKAGE
+
+/* For compatibility with perl 5.004 and older */
+#  ifndef ERRSV
+#    define ERRSV GvSV(errgv)
+#  endif
+
+extern PerlInterpreter *my_perl; /* must be called my_perl or some perl implementations won't work */
+#endif
+
+#include "common.h"
+
+#define MODULE_NAME "perl/core"
+
+/* Change this every time when some API changes between irssi's perl module
+   (or irssi itself) and irssi's perl libraries. */
+#define IRSSI_PERL_API_VERSION 20011214
diff --git a/apps/irssi/src/perl/perl-common.c b/apps/irssi/src/perl/perl-common.c
new file mode 100644 (file)
index 0000000..76fa3f3
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ perl-common.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "core.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "commands.h"
+#include "ignore.h"
+#include "log.h"
+#include "rawlog.h"
+#include "servers-reconnect.h"
+
+#include "window-item-def.h"
+#include "chat-protocols.h"
+#include "chatnets.h"
+#include "servers.h"
+#include "channels.h"
+#include "queries.h"
+#include "nicklist.h"
+
+#include "perl-common.h"
+
+typedef struct {
+       char *stash;
+        PERL_OBJECT_FUNC fill_func;
+} PERL_OBJECT_REC;
+
+#ifndef HAVE_PL_PERL
+STRLEN PL_na;
+#endif
+
+static GHashTable *iobject_stashes, *plain_stashes;
+static GSList *use_protocols;
+
+/* returns the package who called us */
+const char *perl_get_package(void)
+{
+       return SvPV(perl_eval_pv("caller", TRUE), PL_na);
+}
+
+/* Parses the package part from function name */
+char *perl_function_get_package(const char *function)
+{
+       const char *p;
+        int pos;
+
+        pos = 0;
+       for (p = function; *p != '\0'; p++) {
+               if (*p == ':' && p[1] == ':') {
+                       if (++pos == 3)
+                                return g_strndup(function, (int) (p-function));
+               }
+       }
+
+        return NULL;
+}
+
+SV *perl_func_sv_inc(SV *func, const char *package)
+{
+       char *name;
+
+       if (SvPOK(func)) {
+               /* prefix with package name */
+               name = g_strdup_printf("%s::%s", package,
+                                      (char *) SvPV(func, PL_na));
+               func = new_pv(name);
+                g_free(name);
+       } else {
+               SvREFCNT_inc(func);
+       }
+
+        return func;
+}
+
+SV *irssi_bless_iobject(int type, int chat_type, void *object)
+{
+        PERL_OBJECT_REC *rec;
+       HV *stash, *hv;
+
+       g_return_val_if_fail((type & ~0xffff) == 0, NULL);
+       g_return_val_if_fail((chat_type & ~0xffff) == 0, NULL);
+
+       rec = g_hash_table_lookup(iobject_stashes,
+                                 GINT_TO_POINTER(type | (chat_type << 16)));
+       if (rec == NULL) {
+                /* unknown iobject */
+               return newSViv(GPOINTER_TO_INT(object));
+       }
+
+       stash = gv_stashpv(rec->stash, 1);
+
+       hv = newHV();
+       hv_store(hv, "_irssi", 6, newSViv(GPOINTER_TO_INT(object)), 0);
+        rec->fill_func(hv, object);
+       return sv_bless(newRV_noinc((SV*)hv), stash);
+}
+
+SV *irssi_bless_plain(const char *stash, void *object)
+{
+        PERL_OBJECT_FUNC fill_func;
+       HV *hv;
+
+       fill_func = g_hash_table_lookup(plain_stashes, stash);
+
+       hv = newHV();
+       hv_store(hv, "_irssi", 6, newSViv(GPOINTER_TO_INT(object)), 0);
+       if (fill_func != NULL)
+               fill_func(hv, object);
+       return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1));
+}
+
+int irssi_is_ref_object(SV *o)
+{
+        SV **sv;
+       HV *hv;
+
+        hv = hvref(o);
+       if (hv != NULL) {
+               sv = hv_fetch(hv, "_irssi", 6, 0);
+               if (sv != NULL)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+void *irssi_ref_object(SV *o)
+{
+        SV **sv;
+       HV *hv;
+
+        hv = hvref(o);
+       if (hv == NULL)
+               return NULL;
+
+       sv = hv_fetch(hv, "_irssi", 6, 0);
+       if (sv == NULL)
+                croak("variable is damaged");
+       return GINT_TO_POINTER(SvIV(*sv));
+}
+
+void irssi_add_object(int type, int chat_type, const char *stash,
+                     PERL_OBJECT_FUNC func)
+{
+       PERL_OBJECT_REC *rec;
+        void *hash;
+
+       g_return_if_fail((type & ~0xffff) == 0);
+       g_return_if_fail((chat_type & ~0xffff) == 0);
+
+        hash = GINT_TO_POINTER(type | (chat_type << 16));
+       rec = g_hash_table_lookup(iobject_stashes, hash);
+       if (rec == NULL) {
+               rec = g_new(PERL_OBJECT_REC, 1);
+               rec->stash = g_strdup(stash);
+               g_hash_table_insert(iobject_stashes, hash, rec);
+       }
+       rec->fill_func = func;
+}
+
+void irssi_add_plain(const char *stash, PERL_OBJECT_FUNC func)
+{
+        if (g_hash_table_lookup(plain_stashes, stash) == NULL)
+               g_hash_table_insert(plain_stashes, g_strdup(stash), func);
+}
+
+void irssi_add_plains(PLAIN_OBJECT_INIT_REC *objects)
+{
+       while (objects->name != NULL) {
+                irssi_add_plain(objects->name, objects->fill_func);
+                objects++;
+       }
+}
+
+char *perl_get_use_list(void)
+{
+       GString *str;
+       GSList *tmp;
+        char *ret;
+        const char *use_lib;
+
+       str = g_string_new(NULL);
+
+       use_lib = settings_get_str("perl_use_lib");
+       g_string_sprintf(str, "use lib qw(%s/scripts "SCRIPTDIR" %s);",
+                        get_irssi_dir(), use_lib);
+
+        g_string_append(str, "use Irssi;");
+       if (irssi_gui != IRSSI_GUI_NONE)
+               g_string_append(str, "use Irssi::UI;");
+
+       for (tmp = use_protocols; tmp != NULL; tmp = tmp->next)
+               g_string_sprintfa(str, "use Irssi::%s;", (char *) tmp->data);
+
+       ret = str->str;
+        g_string_free(str, FALSE);
+        return ret;
+}
+
+void irssi_callXS(void (*subaddr)(CV* cv), CV *cv, SV **mark)
+{
+       dSP;
+
+       PUSHMARK(mark);
+       (*subaddr)(cv);
+
+       PUTBACK;
+}
+
+void perl_chatnet_fill_hash(HV *hv, CHATNET_REC *chatnet)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(chatnet != NULL);
+
+       type = "CHATNET";
+       chat_type = (char *) chat_protocol_find_id(chatnet->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       hv_store(hv, "name", 4, new_pv(chatnet->name), 0);
+
+       hv_store(hv, "nick", 4, new_pv(chatnet->nick), 0);
+       hv_store(hv, "username", 8, new_pv(chatnet->username), 0);
+       hv_store(hv, "realname", 8, new_pv(chatnet->realname), 0);
+
+       hv_store(hv, "own_host", 8, new_pv(chatnet->own_host), 0);
+       hv_store(hv, "autosendcmd", 11, new_pv(chatnet->autosendcmd), 0);
+}
+
+void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(conn != NULL);
+
+       type = "SERVER CONNECT";
+       chat_type = (char *) chat_protocol_find_id(conn->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       hv_store(hv, "address", 7, new_pv(conn->address), 0);
+       hv_store(hv, "port", 4, newSViv(conn->port), 0);
+       hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
+
+       hv_store(hv, "password", 8, new_pv(conn->password), 0);
+       hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
+       hv_store(hv, "username", 8, new_pv(conn->username), 0);
+       hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
+}
+
+void perl_server_fill_hash(HV *hv, SERVER_REC *server)
+{
+       char *type;
+       HV *stash;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(server != NULL);
+
+       perl_connect_fill_hash(hv, server->connrec);
+
+       type = "SERVER";
+       hv_store(hv, "type", 4, new_pv(type), 0);
+
+       hv_store(hv, "connect_time", 12, newSViv(server->connect_time), 0);
+       hv_store(hv, "real_connect_time", 17, newSViv(server->real_connect_time), 0);
+
+       hv_store(hv, "tag", 3, new_pv(server->tag), 0);
+       hv_store(hv, "nick", 4, new_pv(server->nick), 0);
+
+       hv_store(hv, "connected", 9, newSViv(server->connected), 0);
+       hv_store(hv, "connection_lost", 15, newSViv(server->connection_lost), 0);
+
+       stash = gv_stashpv("Irssi::Rawlog", 0);
+       hv_store(hv, "rawlog", 6, sv_bless(newRV_noinc(newSViv(GPOINTER_TO_INT(server->rawlog))), stash), 0);
+
+       hv_store(hv, "version", 7, new_pv(server->version), 0);
+       hv_store(hv, "away_reason", 11, new_pv(server->away_reason), 0);
+       hv_store(hv, "last_invite", 11, new_pv(server->last_invite), 0);
+       hv_store(hv, "server_operator", 15, newSViv(server->server_operator), 0);
+       hv_store(hv, "usermode_away", 13, newSViv(server->usermode_away), 0);
+       hv_store(hv, "banned", 6, newSViv(server->banned), 0);
+
+       hv_store(hv, "lag", 3, newSViv(server->lag), 0);
+}
+
+void perl_window_item_fill_hash(HV *hv, WI_ITEM_REC *item)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(item != NULL);
+
+       type = (char *) module_find_id_str("WINDOW ITEM TYPE", item->type);
+       chat_type = (char *) chat_protocol_find_id(item->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       if (item->server != NULL) {
+               hv_store(hv, "server", 6, iobject_bless(item->server), 0);
+       }
+       hv_store(hv, "name", 4, new_pv(item->name), 0);
+
+       hv_store(hv, "createtime", 10, newSViv(item->createtime), 0);
+       hv_store(hv, "data_level", 8, newSViv(item->data_level), 0);
+       hv_store(hv, "hilight_color", 10, new_pv(item->hilight_color), 0);
+}
+
+void perl_channel_fill_hash(HV *hv, CHANNEL_REC *channel)
+{
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(channel != NULL);
+
+       perl_window_item_fill_hash(hv, (WI_ITEM_REC *) channel);
+
+       hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
+       hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
+       hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
+
+       hv_store(hv, "no_modes", 8, newSViv(channel->no_modes), 0);
+       hv_store(hv, "mode", 4, new_pv(channel->mode), 0);
+       hv_store(hv, "limit", 5, newSViv(channel->limit), 0);
+       hv_store(hv, "key", 3, new_pv(channel->key), 0);
+
+       hv_store(hv, "chanop", 6, newSViv(channel->chanop), 0);
+       hv_store(hv, "names_got", 9, newSViv(channel->names_got), 0);
+       hv_store(hv, "wholist", 7, newSViv(channel->wholist), 0);
+       hv_store(hv, "synced", 6, newSViv(channel->synced), 0);
+
+       hv_store(hv, "joined", 6, newSViv(channel->joined), 0);
+       hv_store(hv, "left", 4, newSViv(channel->left), 0);
+       hv_store(hv, "kicked", 6, newSViv(channel->kicked), 0);
+}
+
+void perl_query_fill_hash(HV *hv, QUERY_REC *query)
+{
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(query != NULL);
+
+       perl_window_item_fill_hash(hv, (WI_ITEM_REC *) query);
+
+       hv_store(hv, "address", 7, new_pv(query->address), 0);
+       hv_store(hv, "server_tag", 10, new_pv(query->server_tag), 0);
+       hv_store(hv, "unwanted", 8, newSViv(query->unwanted), 0);
+}
+
+void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
+{
+       char *type, *chat_type;
+
+        g_return_if_fail(hv != NULL);
+        g_return_if_fail(nick != NULL);
+
+       type = "NICK";
+       chat_type = (char *) chat_protocol_find_id(nick->chat_type)->name;
+
+       hv_store(hv, "type", 4, new_pv(type), 0);
+       hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
+
+       hv_store(hv, "nick", 4, new_pv(nick->nick), 0);
+       hv_store(hv, "host", 4, new_pv(nick->host), 0);
+       hv_store(hv, "realname", 8, new_pv(nick->realname), 0);
+       hv_store(hv, "hops", 4, newSViv(nick->hops), 0);
+
+       hv_store(hv, "gone", 4, newSViv(nick->gone), 0);
+       hv_store(hv, "serverop", 8, newSViv(nick->serverop), 0);
+
+       hv_store(hv, "op", 2, newSViv(nick->op), 0);
+       hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
+       hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
+
+       hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
+       hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
+}
+
+static void perl_command_fill_hash(HV *hv, COMMAND_REC *cmd)
+{
+       hv_store(hv, "category", 8, new_pv(cmd->category), 0);
+       hv_store(hv, "cmd", 3, new_pv(cmd->cmd), 0);
+}
+
+static void perl_ignore_fill_hash(HV *hv, IGNORE_REC *ignore)
+{
+       AV *av;
+       char **tmp;
+
+       hv_store(hv, "mask", 4, new_pv(ignore->mask), 0);
+       hv_store(hv, "servertag", 9, new_pv(ignore->servertag), 0);
+       av = newAV();
+       for (tmp = ignore->channels; *tmp != NULL; tmp++) {
+               av_push(av, new_pv(*tmp));
+       }
+       hv_store(hv, "channels", 8, newRV_noinc((SV*)av), 0);
+       hv_store(hv, "pattern", 7, new_pv(ignore->pattern), 0);
+
+       hv_store(hv, "level", 5, newSViv(ignore->level), 0);
+
+       hv_store(hv, "exception", 6, newSViv(ignore->exception), 0);
+       hv_store(hv, "regexp", 6, newSViv(ignore->regexp), 0);
+       hv_store(hv, "fullword", 8, newSViv(ignore->fullword), 0);
+}
+
+static void perl_log_fill_hash(HV *hv, LOG_REC *log)
+{
+       AV *av;
+       GSList *tmp;
+
+       hv_store(hv, "fname", 5, new_pv(log->fname), 0);
+       hv_store(hv, "real_fname", 10, new_pv(log->real_fname), 0);
+       hv_store(hv, "opened", 6, newSViv(log->opened), 0);
+       hv_store(hv, "level", 5, newSViv(log->level), 0);
+       hv_store(hv, "last", 4, newSViv(log->last), 0);
+       hv_store(hv, "autoopen", 8, newSViv(log->autoopen), 0);
+       hv_store(hv, "failed", 6, newSViv(log->failed), 0);
+       hv_store(hv, "temp", 4, newSViv(log->temp), 0);
+
+       av = newAV();
+       for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
+               av_push(av, plain_bless(tmp->data, "Irssi::Logitem"));
+       }
+       hv_store(hv, "items", 5, newRV_noinc((SV*)av), 0);
+}
+
+static void perl_log_item_fill_hash(HV *hv, LOG_ITEM_REC *item)
+{
+       hv_store(hv, "type", 4, newSViv(item->type), 0);
+       hv_store(hv, "name", 4, new_pv(item->name), 0);
+       hv_store(hv, "servertag", 9, new_pv(item->servertag), 0);
+}
+
+static void perl_rawlog_fill_hash(HV *hv, RAWLOG_REC *rawlog)
+{
+       hv_store(hv, "logging", 7, newSViv(rawlog->logging), 0);
+       hv_store(hv, "nlines", 6, newSViv(rawlog->nlines), 0);
+}
+
+static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
+{
+       char *type;
+
+       perl_connect_fill_hash(hv, reconnect->conn);
+
+       type = "RECONNECT";
+       hv_store(hv, "type", 4, new_pv(type), 0);
+
+       hv_store(hv, "tag", 3, newSViv(reconnect->tag), 0);
+       hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
+}
+
+void perl_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item)
+{
+        const char *cmdchars;
+       char *sendcmd = (char *) cmd;
+
+       if (*cmd == '\0')
+                return;
+
+        cmdchars = settings_get_str("cmdchars");
+       if (strchr(cmdchars, *cmd) == NULL) {
+               /* no command char - let's put it there.. */
+               sendcmd = g_strdup_printf("%c%s", *cmdchars, cmd);
+       }
+
+       signal_emit("send command", 3, sendcmd, server, item);
+       if (sendcmd != cmd) g_free(sendcmd);
+}
+
+static void perl_register_protocol(CHAT_PROTOCOL_REC *rec)
+{
+       static char *items[] = {
+               "Chatnet",
+               "Server", "ServerConnect", "ServerSetup",
+               "Channel", "Query",
+               "Nick"
+       };
+       static char *find_use_code =
+               "my $pkg = Irssi::%s; $pkg =~ s/::/\\//;\n"
+               "foreach my $i (@INC) {\n"
+               "  return 1 if (-f \"$i/$pkg.pm\");\n"
+               "}\n"
+               "return 0;\n";
+
+       char *name, stash[100], code[100], *pcode;
+       int type, chat_type, n;
+        SV *sv;
+
+       chat_type = chat_protocol_lookup(rec->name);
+       g_return_if_fail(chat_type >= 0);
+
+       name = g_strdup(rec->name);
+       g_strdown(name+1);
+
+       /* window items: channel, query */
+       type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Channel", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_channel_fill_hash);
+
+       type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Query", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_query_fill_hash);
+
+        /* channel nicks */
+       type = module_get_uniq_id("NICK", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Nick", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_nick_fill_hash);
+
+        /* chatnets */
+       type = module_get_uniq_id("CHATNET", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Chatnet", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_chatnet_fill_hash);
+
+       /* server specific */
+       type = module_get_uniq_id("SERVER", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Server", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_server_fill_hash);
+
+       type = module_get_uniq_id("SERVER CONNECT", 0);
+       g_snprintf(stash, sizeof(stash), "Irssi::%s::Connect", name);
+       irssi_add_object(type, chat_type, stash,
+                        (PERL_OBJECT_FUNC) perl_connect_fill_hash);
+
+       /* register ISAs */
+       for (n = 0; n < sizeof(items)/sizeof(items[0]); n++) {
+               g_snprintf(code, sizeof(code),
+                          "@Irssi::%s::%s::ISA = qw(Irssi::%s);",
+                          name, items[n], items[n]);
+               perl_eval_pv(code, TRUE);
+       }
+
+       pcode = g_strdup_printf(find_use_code, name);
+       sv = perl_eval_pv(pcode, TRUE);
+       g_free(pcode);
+
+       if (SvIV(sv)) {
+               use_protocols =
+                       g_slist_append(use_protocols, g_strdup(name));
+       }
+
+       g_free(name);
+}
+
+static void free_iobject_hash(void *key, PERL_OBJECT_REC *rec)
+{
+        g_free(rec->stash);
+       g_free(rec);
+}
+
+static int free_iobject_proto(void *key, void *value, void *chat_type)
+{
+       if ((GPOINTER_TO_INT(key) >> 16) == GPOINTER_TO_INT(chat_type)) {
+                free_iobject_hash(key, value);
+                return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void perl_unregister_protocol(CHAT_PROTOCOL_REC *rec)
+{
+        GSList *item;
+
+       item = gslist_find_icase_string(use_protocols, rec->name);
+       if (item != NULL) {
+               g_free(item->data);
+               use_protocols =
+                       g_slist_remove(use_protocols, item->data);
+       }
+       g_hash_table_foreach_remove(iobject_stashes,
+                                   (GHRFunc) free_iobject_proto,
+                                   GINT_TO_POINTER(rec->id));
+}
+
+void perl_common_start(void)
+{
+       static PLAIN_OBJECT_INIT_REC core_plains[] = {
+               { "Irssi::Command", (PERL_OBJECT_FUNC) perl_command_fill_hash },
+               { "Irssi::Ignore", (PERL_OBJECT_FUNC) perl_ignore_fill_hash },
+               { "Irssi::Log", (PERL_OBJECT_FUNC) perl_log_fill_hash },
+               { "Irssi::Logitem", (PERL_OBJECT_FUNC) perl_log_item_fill_hash },
+               { "Irssi::Rawlog", (PERL_OBJECT_FUNC) perl_rawlog_fill_hash },
+               { "Irssi::Reconnect", (PERL_OBJECT_FUNC) perl_reconnect_fill_hash },
+
+               { NULL, NULL }
+       };
+
+       iobject_stashes = g_hash_table_new((GHashFunc) g_direct_hash,
+                                       (GCompareFunc) g_direct_equal);
+       plain_stashes = g_hash_table_new((GHashFunc) g_str_hash,
+                                        (GCompareFunc) g_str_equal);
+        irssi_add_plains(core_plains);
+
+        use_protocols = NULL;
+       g_slist_foreach(chat_protocols, (GFunc) perl_register_protocol, NULL);
+
+       signal_add("chat protocol created", (SIGNAL_FUNC) perl_register_protocol);
+       signal_add("chat protocol destroyed", (SIGNAL_FUNC) perl_unregister_protocol);
+}
+
+void perl_common_stop(void)
+{
+        g_hash_table_foreach(iobject_stashes, (GHFunc) free_iobject_hash, NULL);
+       g_hash_table_destroy(iobject_stashes);
+        iobject_stashes = NULL;
+
+       g_hash_table_foreach(plain_stashes, (GHFunc) g_free, NULL);
+       g_hash_table_destroy(plain_stashes);
+        plain_stashes = NULL;
+
+       g_slist_foreach(use_protocols, (GFunc) g_free, NULL);
+       g_slist_free(use_protocols);
+        use_protocols = NULL;
+
+       signal_remove("chat protocol created", (SIGNAL_FUNC) perl_register_protocol);
+       signal_remove("chat protocol destroyed", (SIGNAL_FUNC) perl_unregister_protocol);
+}
diff --git a/apps/irssi/src/perl/perl-common.h b/apps/irssi/src/perl/perl-common.h
new file mode 100644 (file)
index 0000000..9fa85f0
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef __PERL_COMMON_H
+#define __PERL_COMMON_H
+
+/* helper defines */
+#define new_pv(a) \
+       (newSVpv((a) == NULL ? "" : (a), (a) == NULL ? 0 : strlen(a)))
+
+#define is_hvref(o) \
+       ((o) && SvROK(o) && SvRV(o) && (SvTYPE(SvRV(o)) == SVt_PVHV))
+
+#define hvref(o) \
+       (is_hvref(o) ? (HV *)SvRV(o) : NULL)
+
+typedef void (*PERL_OBJECT_FUNC) (HV *hv, void *object);
+
+typedef struct {
+       char *name;
+        PERL_OBJECT_FUNC fill_func;
+} PLAIN_OBJECT_INIT_REC;
+
+/* Returns the package who called us */
+const char *perl_get_package(void);
+/* Parses the package part from function name */
+char *perl_function_get_package(const char *function);
+/* If SV is a string, prefix it with given package.
+   Increases the reference counter for the return value. */
+SV *perl_func_sv_inc(SV *func, const char *package);
+
+/* For compatibility with perl 5.004 and older */
+#ifndef HAVE_PL_PERL
+#  define PL_sv_undef sv_undef
+extern STRLEN PL_na;
+#endif
+
+#define iobject_bless(object) \
+       ((object) == NULL ? &PL_sv_undef : \
+       irssi_bless_iobject((object)->type, (object)->chat_type, object))
+
+#define simple_iobject_bless(object) \
+       ((object) == NULL ? &PL_sv_undef : \
+       irssi_bless_iobject((object)->type, 0, object))
+
+#define plain_bless(object, stash) \
+       ((object) == NULL ? &PL_sv_undef : \
+       irssi_bless_plain(stash, object))
+
+SV *irssi_bless_iobject(int type, int chat_type, void *object);
+SV *irssi_bless_plain(const char *stash, void *object);
+int irssi_is_ref_object(SV *o);
+void *irssi_ref_object(SV *o);
+
+void irssi_add_object(int type, int chat_type, const char *stash,
+                     PERL_OBJECT_FUNC func);
+void irssi_add_plain(const char *stash, PERL_OBJECT_FUNC func);
+void irssi_add_plains(PLAIN_OBJECT_INIT_REC *objects);
+
+char *perl_get_use_list(void);
+
+#define irssi_boot(x) { \
+       extern void boot_Irssi__##x(CV *cv); \
+       irssi_callXS(boot_Irssi__##x, cv, mark); \
+       }
+void irssi_callXS(void (*subaddr)(CV* cv), CV *cv, SV **mark);
+
+void perl_common_start(void);
+void perl_common_stop(void);
+
+#endif
diff --git a/apps/irssi/src/perl/perl-core.c b/apps/irssi/src/perl/perl-core.c
new file mode 100644 (file)
index 0000000..1ed95c3
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ perl-core.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "modules.h"
+#include "core.h"
+#include "signals.h"
+#include "misc.h"
+#include "settings.h"
+#include "lib-config/iconfig.h" /* FIXME: remove before .99 */
+
+#include "perl-core.h"
+#include "perl-common.h"
+#include "perl-signals.h"
+#include "perl-sources.h"
+
+#include "XSUB.h"
+#include "irssi-core.pl.h"
+
+/* For compatibility with perl 5.004 and older */
+#ifndef HAVE_PL_PERL
+#  define PL_perl_destruct_level perl_destruct_level
+#endif
+
+GSList *perl_scripts;
+PerlInterpreter *my_perl;
+
+static int print_script_errors;
+
+#define IS_PERL_SCRIPT(file) \
+       (strlen(file) > 3 && strcmp(file+strlen(file)-3, ".pl") == 0)
+
+static void perl_script_destroy_package(PERL_SCRIPT_REC *script)
+{
+       dSP;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(new_pv(script->package)));
+       PUTBACK;
+
+       perl_call_pv("Irssi::Core::destroy", G_VOID|G_EVAL|G_DISCARD);
+
+       SPAGAIN;
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+static void perl_script_destroy(PERL_SCRIPT_REC *script)
+{
+       perl_scripts = g_slist_remove(perl_scripts, script);
+
+       signal_emit("script destroyed", 1, script);
+
+       perl_signal_remove_script(script);
+       perl_source_remove_script(script);
+
+       g_free(script->name);
+       g_free(script->package);
+        g_free_not_null(script->path);
+        g_free_not_null(script->data);
+        g_free(script);
+}
+
+extern void boot_DynaLoader(CV* cv);
+
+#if PERL_STATIC_LIBS == 1
+extern void boot_Irssi(CV *cv);
+
+XS(boot_Irssi_Core)
+{
+       dXSARGS;
+
+       irssi_callXS(boot_Irssi, cv, mark);
+        irssi_boot(Irc);
+        irssi_boot(UI);
+        irssi_boot(TextUI);
+       XSRETURN_YES;
+}
+#endif
+
+static void xs_init(void)
+{
+       dXSUB_SYS;
+
+#if PERL_STATIC_LIBS == 1
+       newXS("Irssi::Core::boot_Irssi_Core", boot_Irssi_Core, __FILE__);
+#endif
+
+       /* boot the dynaloader too, if we want to use some
+          other dynamic modules.. */
+       newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
+}
+
+/* Initialize perl interpreter */
+void perl_scripts_init(void)
+{
+       char *args[] = {"", "-e", "0"};
+       char *code, *use_code;
+
+       perl_scripts = NULL;
+        perl_sources_start();
+       perl_signals_start();
+
+       my_perl = perl_alloc();
+       perl_construct(my_perl);
+
+       perl_parse(my_perl, xs_init, 3, args, NULL);
+#if PERL_STATIC_LIBS == 1
+       perl_eval_pv("Irssi::Core::boot_Irssi_Core();", TRUE);
+#endif
+
+        perl_common_start();
+
+       use_code = perl_get_use_list();
+        code = g_strdup_printf(irssi_core_code, PERL_STATIC_LIBS, use_code);
+       perl_eval_pv(code, TRUE);
+
+       g_free(code);
+        g_free(use_code);
+}
+
+/* Destroy all perl scripts and deinitialize perl interpreter */
+void perl_scripts_deinit(void)
+{
+       if (my_perl == NULL)
+               return;
+
+       /* unload all scripts */
+        while (perl_scripts != NULL)
+               perl_script_unload(perl_scripts->data);
+
+        signal_emit("perl scripts deinit", 0);
+
+        perl_signals_stop();
+       perl_sources_stop();
+       perl_common_stop();
+
+       /* Unload all perl libraries loaded with dynaloader */
+       perl_eval_pv("foreach my $lib (@DynaLoader::dl_modules) { if ($lib =~ /^Irssi\\b/) { $lib .= '::deinit();'; eval $lib; } }", TRUE);
+       perl_eval_pv("eval { foreach my $lib (@DynaLoader::dl_librefs) { DynaLoader::dl_unload_file($lib); } }", TRUE);
+
+       /* perl interpreter */
+       perl_destruct(my_perl);
+       perl_free(my_perl);
+       my_perl = NULL;
+}
+
+/* Modify the script name so that all non-alphanumeric characters are
+   translated to '_' */
+void script_fix_name(char *name)
+{
+       char *p;
+
+       p = strrchr(name, '.');
+       if (p != NULL) *p = '\0';
+
+       while (*name != '\0') {
+               if (*name != '_' && !i_isalnum(*name))
+                       *name = '_';
+               name++;
+       }
+}
+
+static char *script_file_get_name(const char *path)
+{
+       char *name;
+
+        name = g_strdup(g_basename(path));
+       script_fix_name(name);
+        return name;
+}
+
+static char *script_data_get_name(void)
+{
+       GString *name;
+        char *ret;
+       int n;
+
+       name = g_string_new(NULL);
+        n = 1;
+       do {
+               g_string_sprintf(name, "data%d", n);
+                n++;
+       } while (perl_script_find(name->str) != NULL);
+
+       ret = name->str;
+        g_string_free(name, FALSE);
+        return ret;
+}
+
+static int perl_script_eval(PERL_SCRIPT_REC *script)
+{
+       dSP;
+       char *error;
+       int retcount;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+       XPUSHs(sv_2mortal(new_pv(script->path != NULL ? script->path :
+                                script->data)));
+       XPUSHs(sv_2mortal(new_pv(script->name)));
+       PUTBACK;
+
+       retcount = perl_call_pv(script->path != NULL ?
+                               "Irssi::Core::eval_file" :
+                               "Irssi::Core::eval_data",
+                               G_EVAL|G_SCALAR);
+       SPAGAIN;
+
+        error = NULL;
+       if (SvTRUE(ERRSV)) {
+                error = SvPV(ERRSV, PL_na);
+       } else if (retcount > 0) {
+               error = POPp;
+       }
+
+       if (error != NULL) {
+               if (*error == '\0')
+                       error = NULL;
+               else {
+                        error = g_strdup(error);
+                       signal_emit("script error", 2, script, error);
+                        g_free(error);
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+
+        return error == NULL;
+}
+
+/* NOTE: name must not be free'd */
+static PERL_SCRIPT_REC *script_load(char *name, const char *path,
+                                   const char *data)
+{
+        PERL_SCRIPT_REC *script;
+
+       /* if there's a script with a same name, destroy it */
+       script = perl_script_find(name);
+       if (script != NULL)
+               perl_script_destroy(script);
+
+       script = g_new0(PERL_SCRIPT_REC, 1);
+       script->name = name;
+       script->package = g_strdup_printf("Irssi::Script::%s", name);
+       script->path = g_strdup(path);
+        script->data = g_strdup(data);
+
+       perl_scripts = g_slist_append(perl_scripts, script);
+       signal_emit("script created", 1, script);
+
+       if (!perl_script_eval(script))
+                script = NULL; /* the script is destroyed in "script error" signal */
+        return script;
+}
+
+/* Load a perl script, path must be a full path. */
+PERL_SCRIPT_REC *perl_script_load_file(const char *path)
+{
+       char *name;
+
+        g_return_val_if_fail(path != NULL, NULL);
+
+        name = script_file_get_name(path);
+        return script_load(name, path, NULL);
+}
+
+/* Load a perl script from given data */
+PERL_SCRIPT_REC *perl_script_load_data(const char *data)
+{
+       char *name;
+
+        g_return_val_if_fail(data != NULL, NULL);
+
+       name = script_data_get_name();
+       return script_load(name, NULL, data);
+}
+
+/* Unload perl script */
+void perl_script_unload(PERL_SCRIPT_REC *script)
+{
+        g_return_if_fail(script != NULL);
+
+       perl_script_destroy_package(script);
+        perl_script_destroy(script);
+}
+
+/* Find loaded script by name */
+PERL_SCRIPT_REC *perl_script_find(const char *name)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(name != NULL, NULL);
+
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+               if (strcmp(rec->name, name) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+/* Find loaded script by package */
+PERL_SCRIPT_REC *perl_script_find_package(const char *package)
+{
+       GSList *tmp;
+
+        g_return_val_if_fail(package != NULL, NULL);
+
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+               if (strcmp(rec->package, package) == 0)
+                        return rec;
+       }
+
+        return NULL;
+}
+
+/* Returns full path for the script */
+char *perl_script_get_path(const char *name)
+{
+       struct stat statbuf;
+       char *file, *path;
+
+       if (g_path_is_absolute(name) || (name[0] == '~' && name[1] == '/')) {
+               /* full path specified */
+                return convert_home(name);
+       }
+
+       /* add .pl suffix if it's missing */
+       file = IS_PERL_SCRIPT(name) ? g_strdup(name) :
+               g_strdup_printf("%s.pl", name);
+
+       /* check from ~/.irssi/scripts/ */
+       path = g_strdup_printf("%s/scripts/%s", get_irssi_dir(), file);
+       if (stat(path, &statbuf) != 0) {
+               /* check from SCRIPTDIR */
+               g_free(path);
+               path = g_strdup_printf(SCRIPTDIR"/%s", file);
+               if (stat(path, &statbuf) != 0)
+                        path = NULL;
+       }
+       g_free(file);
+        return path;
+}
+
+/* If core should handle printing script errors */
+void perl_core_print_script_error(int print)
+{
+        print_script_errors = print;
+}
+
+/* Returns the perl module's API version. */
+int perl_get_api_version(void)
+{
+        return IRSSI_PERL_API_VERSION;
+}
+
+static void perl_scripts_autorun(void)
+{
+       DIR *dirp;
+       struct dirent *dp;
+       struct stat statbuf;
+       char *path, *fname;
+
+        /* run *.pl scripts from ~/.irssi/scripts/autorun/ */
+       path = g_strdup_printf("%s/scripts/autorun", get_irssi_dir());
+       dirp = opendir(path);
+       if (dirp == NULL) {
+               g_free(path);
+               return;
+       }
+
+       while ((dp = readdir(dirp)) != NULL) {
+               if (!IS_PERL_SCRIPT(dp->d_name))
+                       continue;
+
+               fname = g_strdup_printf("%s/%s", path, dp->d_name);
+               if (stat(fname, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode))
+                       perl_script_load_file(fname);
+               g_free(fname);
+       }
+       closedir(dirp);
+       g_free(path);
+}
+
+static void sig_script_error(PERL_SCRIPT_REC *script, const char *error)
+{
+       char *str;
+
+       if (print_script_errors) {
+               str = g_strdup_printf("Script '%s' error:",
+                                     script == NULL ? "??" : script->name);
+               signal_emit("gui dialog", 2, "error", str);
+               signal_emit("gui dialog", 2, "error", error);
+                g_free(str);
+       }
+
+       if (script != NULL) {
+               perl_script_unload(script);
+                signal_stop();
+       }
+}
+
+static void sig_autorun(void)
+{
+       signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun);
+
+        perl_scripts_autorun();
+}
+
+void perl_core_init(void)
+{
+        print_script_errors = 1;
+       settings_add_str("perl", "perl_use_lib", PERL_USE_LIB);
+
+       PL_perl_destruct_level = 1;
+       perl_signals_init();
+        signal_add_last("script error", (SIGNAL_FUNC) sig_script_error);
+
+       perl_scripts_init();
+
+       if (irssi_init_finished)
+               perl_scripts_autorun();
+       else {
+               signal_add("irssi init finished", (SIGNAL_FUNC) sig_autorun);
+               settings_check();
+       }
+
+       module_register("perl", "core");
+}
+
+void perl_core_deinit(void)
+{
+       perl_signals_deinit();
+        perl_scripts_deinit();
+
+       signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
+}
diff --git a/apps/irssi/src/perl/perl-core.h b/apps/irssi/src/perl/perl-core.h
new file mode 100644 (file)
index 0000000..b451cc5
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __PERL_CORE_H
+#define __PERL_CORE_H
+
+typedef struct {
+       char *name; /* unique name */
+        char *package; /* package name */
+
+        /* Script can be loaded from a file, or from some data in memory */
+       char *path; /* FILE: full path for file */
+       char *data; /* DATA: data used for the script */
+} PERL_SCRIPT_REC;
+
+extern GSList *perl_scripts;
+
+/* Initialize perl interpreter */
+void perl_scripts_init(void);
+/* Destroy all perl scripts and deinitialize perl interpreter */
+void perl_scripts_deinit(void);
+
+/* Load a perl script, path must be a full path. */
+PERL_SCRIPT_REC *perl_script_load_file(const char *path);
+/* Load a perl script from given data */
+PERL_SCRIPT_REC *perl_script_load_data(const char *data);
+/* Unload perl script */
+void perl_script_unload(PERL_SCRIPT_REC *script);
+
+/* Find loaded script by name */
+PERL_SCRIPT_REC *perl_script_find(const char *name);
+/* Find loaded script by package */
+PERL_SCRIPT_REC *perl_script_find_package(const char *package);
+
+/* Returns full path for the script */
+char *perl_script_get_path(const char *name);
+/* Modify the script name so that all non-alphanumeric characters are
+   translated to '_' */
+void script_fix_name(char *name);
+
+/* If core should handle printing script errors */
+void perl_core_print_script_error(int print);
+
+/* Returns the perl module's API version. */
+int perl_get_api_version(void);
+
+/* Checks that the API version is correct. */
+#define perl_api_version_check(library) \
+       if (perl_get_api_version() != IRSSI_PERL_API_VERSION) { \
+               die("Version of perl module (%d) doesn't match the " \
+                   "version of "library" library (%d)", \
+                   perl_get_api_version(), IRSSI_PERL_API_VERSION); \
+               return; \
+        }
+
+void perl_core_init(void);
+void perl_core_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/perl/perl-fe.c b/apps/irssi/src/perl/perl-fe.c
new file mode 100644 (file)
index 0000000..3c47a6d
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ perl-core.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module-fe.h"
+#include "modules.h"
+#include "module-formats.h"
+#include "signals.h"
+#include "commands.h"
+#include "levels.h"
+
+#include "printtext.h"
+#include "completion.h"
+
+#include "perl-core.h"
+
+static void cmd_script(const char *data, SERVER_REC *server, void *item)
+{
+       if (*data == '\0')
+                data = "list";
+
+       command_runsub("script", data, server, item);
+}
+
+static void cmd_script_exec(const char *data)
+{
+        PERL_SCRIPT_REC *script;
+       GHashTable *optlist;
+       char *code;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_GETREST,
+                           "script exec", &optlist, &code))
+               return;
+
+        if (*code == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+        script = perl_script_load_data(code);
+       if (script != NULL &&
+           g_hash_table_lookup(optlist, "permanent") == NULL) {
+               /* not a permanent script, unload immediately */
+                perl_script_unload(script);
+       }
+
+
+       cmd_params_free(free_arg);
+}
+
+static void cmd_script_load(const char *data)
+{
+        PERL_SCRIPT_REC *script;
+       char *fname, *path;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1, &path))
+               return;
+
+        if (*path == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       fname = perl_script_get_path(path);
+       if (fname == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                            TXT_SCRIPT_NOT_FOUND, data);
+       } else {
+               script = perl_script_load_file(fname);
+               if (script != NULL) {
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                   TXT_SCRIPT_LOADED,
+                                   script->name, script->path);
+               }
+               g_free(fname);
+       }
+       cmd_params_free(free_arg);
+}
+
+static void cmd_script_unload(const char *data)
+{
+       PERL_SCRIPT_REC *script;
+        char *name;
+       void *free_arg;
+
+       if (!cmd_get_params(data, &free_arg, 1, &name))
+               return;
+
+        if (*name == '\0')
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+        script_fix_name(name);
+       script = perl_script_find(name);
+       if (script == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                            TXT_SCRIPT_NOT_LOADED, name);
+       } else {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                           TXT_SCRIPT_UNLOADED, script->name);
+               perl_script_unload(script);
+       }
+       cmd_params_free(free_arg);
+}
+
+static void cmd_script_reset(const char *data)
+{
+       perl_scripts_deinit();
+       perl_scripts_init();
+}
+
+static void cmd_script_list(void)
+{
+       GSList *tmp;
+        GString *data;
+
+       if (perl_scripts == NULL) {
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                            TXT_NO_SCRIPTS_LOADED);
+                return;
+       }
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                   TXT_SCRIPT_LIST_HEADER);
+
+       data = g_string_new(NULL);
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+                if (rec->path != NULL)
+                       g_string_assign(data, rec->path);
+               else {
+                       g_string_assign(data, rec->data);
+                       if (data->len > 50) {
+                               g_string_truncate(data, 50);
+                                g_string_append(data, " ...");
+                       }
+               }
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                           TXT_SCRIPT_LIST_LINE, rec->name, data->str);
+       }
+        g_string_free(data, TRUE);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
+                   TXT_SCRIPT_LIST_FOOTER);
+}
+
+static void sig_script_error(PERL_SCRIPT_REC *script, const char *error)
+{
+       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                   TXT_SCRIPT_ERROR, script == NULL ? "??" : script->name);
+
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%[-s]%s", error);
+}
+
+static void sig_complete_load(GList **list, WINDOW_REC *window,
+                             const char *word, const char *line,
+                             int *want_space)
+{
+        char *user_dir;
+
+       if (*line != '\0')
+               return;
+
+       /* completing filename parameter for /SCRIPT LOAD */
+       user_dir = g_strdup_printf("%s/scripts", get_irssi_dir());
+       *list = filename_complete(word, user_dir);
+       *list = g_list_concat(*list, filename_complete(word, SCRIPTDIR));
+        g_free(user_dir);
+
+       if (*list != NULL) {
+               *want_space = FALSE;
+               signal_stop();
+       }
+}
+
+static GList *script_complete(const char *name)
+{
+       GSList *tmp;
+        GList *list;
+        int len;
+
+        list = NULL;
+        len = strlen(name);
+       for (tmp = perl_scripts; tmp != NULL; tmp = tmp->next) {
+               PERL_SCRIPT_REC *rec = tmp->data;
+
+               if (strncmp(rec->name, name, len) == 0)
+                        list = g_list_append(list, g_strdup(rec->name));
+       }
+
+        return list;
+}
+
+static void sig_complete_unload(GList **list, WINDOW_REC *window,
+                               const char *word, const char *line,
+                               int *want_space)
+{
+       if (*line != '\0')
+               return;
+
+       /* completing script parameter for /SCRIPT UNLOAD */
+       *list = script_complete(word);
+       if (*list != NULL)
+               signal_stop();
+}
+
+void fe_perl_init(void)
+{
+       theme_register(feperl_formats);
+
+       command_bind("script", NULL, (SIGNAL_FUNC) cmd_script);
+       command_bind("script exec", NULL, (SIGNAL_FUNC) cmd_script_exec);
+       command_bind("script load", NULL, (SIGNAL_FUNC) cmd_script_load);
+       command_bind("script unload", NULL, (SIGNAL_FUNC) cmd_script_unload);
+       command_bind("script reset", NULL, (SIGNAL_FUNC) cmd_script_reset);
+       command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list);
+       command_set_options("script exec", "permanent");
+
+        signal_add("script error", (SIGNAL_FUNC) sig_script_error);
+       signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load);
+       signal_add("complete command script unload", (SIGNAL_FUNC) sig_complete_unload);
+
+        perl_core_print_script_error(FALSE);
+       module_register("perl", "fe");
+}
+
+void fe_perl_deinit(void)
+{
+       command_unbind("script", (SIGNAL_FUNC) cmd_script);
+       command_unbind("script exec", (SIGNAL_FUNC) cmd_script_exec);
+       command_unbind("script load", (SIGNAL_FUNC) cmd_script_load);
+       command_unbind("script unload", (SIGNAL_FUNC) cmd_script_unload);
+       command_unbind("script reset", (SIGNAL_FUNC) cmd_script_reset);
+       command_unbind("script list", (SIGNAL_FUNC) cmd_script_list);
+
+        signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
+       signal_remove("complete command script load", (SIGNAL_FUNC) sig_complete_load);
+       signal_remove("complete command script unload", (SIGNAL_FUNC) sig_complete_unload);
+
+        perl_core_print_script_error(TRUE);
+}
diff --git a/apps/irssi/src/perl/perl-signals-list.h b/apps/irssi/src/perl/perl-signals-list.h
new file mode 100644 (file)
index 0000000..4c1c9ad
--- /dev/null
@@ -0,0 +1,178 @@
+static PERL_SIGNAL_ARGS_REC perl_signal_args[] =
+{
+    { "gui dialog", { "string", "string", NULL } },
+    { "send command", { "string", "iobject", "iobject", NULL } },
+    { "chat protocol created", { "CHAT_PROTOCOL_REC", NULL } },
+    { "chat protocol updated", { "CHAT_PROTOCOL_REC", NULL } },
+    { "chat protocol destroyed", { "CHAT_PROTOCOL_REC", NULL } },
+    { "channel created", { "iobject", "int", NULL } },
+    { "channel destroyed", { "iobject", NULL } },
+    { "chatnet created", { "iobject", NULL } },
+    { "chatnet destroyed", { "iobject", NULL } },
+    { "commandlist new", { "Irssi::Command", NULL } },
+    { "commandlist remove", { "Irssi::Command", NULL } },
+    { "error command", { "int", "string", NULL } },
+    { "send command", { "string", "iobject", "iobject", NULL } },
+    { "send text", { "string", "iobject", "iobject", NULL } },
+    { "command ", { "string", "iobject", "iobject", NULL } },
+    { "default command", { "string", "iobject", "iobject", NULL } },
+    { "ignore created", { "Irssi::Ignore", NULL } },
+    { "ignore destroyed", { "Irssi::Ignore", NULL } },
+    { "ignore changed", { "Irssi::Ignore", NULL } },
+    { "log new", { "Irssi::Log", NULL } },
+    { "log remove", { "Irssi::Log", NULL } },
+    { "log create failed", { "Irssi::Log", NULL } },
+    { "log locked", { "Irssi::Log", NULL } },
+    { "log started", { "Irssi::Log", NULL } },
+    { "log stopped", { "Irssi::Log", NULL } },
+    { "log rotated", { "Irssi::Log", NULL } },
+    { "log written", { "Irssi::Log", "string", NULL } },
+    { "module loaded", { "Irssi::Module", "MODULE_FILE_REC", NULL } },
+    { "module unloaded", { "Irssi::Module", "MODULE_FILE_REC", NULL } },
+    { "module error", { "int", "string", "string", "string", NULL } },
+    { "nicklist new", { "iobject", "iobject", NULL } },
+    { "nicklist remove", { "iobject", "iobject", NULL } },
+    { "nicklist changed", { "iobject", "iobject", "string", NULL } },
+    { "nicklist host changed", { "iobject", "iobject", NULL } },
+    { "nicklist gone changed", { "iobject", "iobject", NULL } },
+    { "nicklist serverop changed", { "iobject", "iobject", NULL } },
+    { "pidwait", { "int", "int", NULL } },
+    { "query created", { "iobject", "int", NULL } },
+    { "query destroyed", { "iobject", NULL } },
+    { "query nick changed", { "iobject", "string", NULL } },
+    { "query address changed", { "iobject", NULL } },
+    { "query server changed", { "iobject", "iobject", NULL } },
+    { "rawlog", { "RAWIrssi::Log", "string", NULL } },
+    { "server looking", { "iobject", NULL } },
+    { "server connected", { "iobject", NULL } },
+    { "server connecting", { "iobject", "ulongptr", NULL } },
+    { "server connect failed", { "iobject", NULL } },
+    { "server disconnected", { "iobject", NULL } },
+    { "server quit", { "iobject", "string", NULL } },
+    { "setup reread", { "string", NULL } },
+    { "setup saved", { "string", "int", NULL } },
+    { "ban type changed", { "string", NULL } },
+    { "channel joined", { "iobject", NULL } },
+    { "channel wholist", { "iobject", NULL } },
+    { "channel sync", { "iobject", NULL } },
+    { "channel topic changed", { "iobject", NULL } },
+    { "ctcp msg", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp msg ", { "iobject", "string", "string", "string", "string", NULL } },
+    { "default ctcp msg", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp reply", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp reply ", { "iobject", "string", "string", "string", "string", NULL } },
+    { "default ctcp reply", { "iobject", "string", "string", "string", "string", NULL } },
+    { "ctcp action", { "iobject", "string", "string", "string", "string", NULL } },
+    { "awaylog show", { "Irssi::Log", "int", "int", NULL } },
+    { "server nick changed", { "iobject", NULL } },
+    { "event connected", { "iobject", NULL } },
+    { "server event", { "iobject", "string", "string", "string", NULL } },
+    { "event ", { "iobject", "string", "string", "string", NULL } },
+    { "default event", { "iobject", "string", "string", "string", NULL } },
+    { "server incoming", { "iobject", "string", NULL } },
+    { "redir ", { "iobject", "string", "string", "string", NULL } },
+    { "server lag", { "iobject", NULL } },
+    { "server lag disconnect", { "iobject", NULL } },
+    { "massjoin", { "iobject", "gslist_iobject", NULL } },
+    { "ban new", { "iobject", "Irssi::Irc::Ban", NULL } },
+    { "ban remove", { "iobject", "Irssi::Irc::Ban", NULL } },
+    { "channel mode changed", { "iobject", NULL } },
+    { "nick mode changed", { "iobject", "iobject", NULL } },
+    { "user mode changed", { "iobject", "string", NULL } },
+    { "away mode changed", { "iobject", NULL } },
+    { "netsplit server new", { "iobject", "NETSPLIT_iobject", NULL } },
+    { "netsplit server remove", { "iobject", "NETSPLIT_iobject", NULL } },
+    { "netsplit new", { "Irssi::Irc::Netsplit", NULL } },
+    { "netsplit remove", { "Irssi::Irc::Netsplit", NULL } },
+    { "dcc ctcp ", { "string", "siobject", NULL } },
+    { "default dcc ctcp", { "string", "siobject", NULL } },
+    { "dcc unknown ctcp", { "string", "string", "string", NULL } },
+    { "dcc reply ", { "string", "siobject", NULL } },
+    { "default dcc reply", { "string", "siobject", NULL } },
+    { "dcc unknown reply", { "string", "string", "string", NULL } },
+    { "dcc chat message", { "siobject", "string", NULL } },
+    { "dcc created", { "siobject", NULL } },
+    { "dcc destroyed", { "siobject", NULL } },
+    { "dcc connected", { "siobject", NULL } },
+    { "dcc rejecting", { "siobject", NULL } },
+    { "dcc closed", { "siobject", NULL } },
+    { "dcc request", { "siobject", "string", NULL } },
+    { "dcc request send", { "siobject", NULL } },
+    { "dcc chat message", { "siobject", "string", NULL } },
+    { "dcc transfer update", { "siobject", NULL } },
+    { "dcc get receive", { "siobject", NULL } },
+    { "dcc error connect", { "siobject", NULL } },
+    { "dcc error file create", { "siobject", "string", NULL } },
+    { "dcc error file open", { "string", "string", "int", NULL } },
+    { "dcc error get not found", { "string", NULL } },
+    { "dcc error send exists", { "string", "string", NULL } },
+    { "dcc error unknown type", { "string", NULL } },
+    { "dcc error close not found", { "string", "string", "string", NULL } },
+    { "autoignore new", { "iobject", "AUTOIrssi::Ignore", NULL } },
+    { "autoignore remove", { "iobject", "AUTOIrssi::Ignore", NULL } },
+    { "flood", { "iobject", "string", "string", "int", "string", NULL } },
+    { "notifylist new", { "Irssi::Irc::Notifylist", NULL } },
+    { "notifylist remove", { "Irssi::Irc::Notifylist", NULL } },
+    { "notifylist joined", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "notifylist away changed", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "notifylist unidle", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "notifylist left", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "proxy client connected", { "CLIENT_REC", NULL } },
+    { "proxy client disconnected", { "CLIENT_REC", NULL } },
+    { "gui print text", { "Irssi::UI::Window", "int", "int", "int", "string", "int", NULL } },
+    { "gui print text finished", { "Irssi::UI::Window", NULL } },
+    { "complete word", { "glistptr_char*", "Irssi::UI::Window", "string", "string", "intptr", NULL } },
+    { "exec new", { "Irssi::UI::Process", NULL } },
+    { "exec remove", { "Irssi::UI::Process", "int", NULL } },
+    { "exec input", { "Irssi::UI::Process", "string", NULL } },
+    { "message public", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message private", { "iobject", "string", "string", "string", NULL } },
+    { "message own_public", { "iobject", "string", "string", NULL } },
+    { "message own_private", { "iobject", "string", "string", "string", NULL } },
+    { "message join", { "iobject", "string", "string", "string", NULL } },
+    { "message part", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message quit", { "iobject", "string", "string", "string", NULL } },
+    { "message kick", { "iobject", "string", "string", "string", "string", "string", NULL } },
+    { "message nick", { "iobject", "string", "string", "string", NULL } },
+    { "message own_nick", { "iobject", "string", "string", "string", NULL } },
+    { "message invite", { "iobject", "string", "string", "string", NULL } },
+    { "message topic", { "iobject", "string", "string", "string", "string", NULL } },
+    { "keyinfo created", { "Irssi::UI::Keyinfo", NULL } },
+    { "keyinfo destroyed", { "Irssi::UI::Keyinfo", NULL } },
+    { "print text", { "Irssi::UI::TextDest", "string", "string", NULL } },
+    { "theme created", { "Irssi::UI::Theme", NULL } },
+    { "theme destroyed", { "Irssi::UI::Theme", NULL } },
+    { "window hilight", { "Irssi::UI::Window", NULL } },
+    { "window activity", { "Irssi::UI::Window", "int", NULL } },
+    { "window item hilight", { "iobject", NULL } },
+    { "window item activity", { "iobject", "int", NULL } },
+    { "window item new", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window item remove", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window item changed", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window item server changed", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window created", { "Irssi::UI::Window", NULL } },
+    { "window destroyed", { "Irssi::UI::Window", NULL } },
+    { "window changed", { "Irssi::UI::Window", "Irssi::UI::Window", NULL } },
+    { "window changed automatic", { "Irssi::UI::Window", NULL } },
+    { "window server changed", { "Irssi::UI::Window", "iobject", NULL } },
+    { "window refnum changed", { "Irssi::UI::Window", "int", NULL } },
+    { "window name changed", { "Irssi::UI::Window", NULL } },
+    { "window history changed", { "Irssi::UI::Window", "string", NULL } },
+    { "window level changed", { "Irssi::UI::Window", NULL } },
+    { "message irc op_public", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message irc own_wall", { "iobject", "string", "string", NULL } },
+    { "message irc own_action", { "iobject", "string", "string", NULL } },
+    { "message irc action", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message irc own_notice", { "iobject", "string", "string", NULL } },
+    { "message irc notice", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message irc own_ctcp", { "iobject", "string", "string", "string", NULL } },
+    { "message irc ctcp", { "iobject", "string", "string", "string", "string", NULL } },
+    { "message dcc own", { "siobject", "string", NULL } },
+    { "message dcc own_action", { "siobject", "string", NULL } },
+    { "message dcc own_ctcp", { "siobject", "string", "string", NULL } },
+    { "message dcc", { "siobject", "string", NULL } },
+    { "message dcc action", { "siobject", "string", NULL } },
+    { "message dcc ctcp", { "siobject", "string", "string", NULL } },
+
+    { NULL }
+};
diff --git a/apps/irssi/src/perl/perl-signals.c b/apps/irssi/src/perl/perl-signals.c
new file mode 100644 (file)
index 0000000..eb3a8b4
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ perl-signals.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "modules.h"
+#include "signals.h"
+#include "commands.h"
+#include "servers.h"
+
+#include "perl-core.h"
+#include "perl-common.h"
+#include "perl-signals.h"
+
+typedef struct {
+        PERL_SCRIPT_REC *script;
+       int signal_id;
+       char *signal;
+
+       SV *func;
+       int priority;
+} PERL_SIGNAL_REC;
+
+typedef struct {
+       char *signal;
+       char *args[7];
+} PERL_SIGNAL_ARGS_REC;
+
+#include "perl-signals-list.h"
+
+static GHashTable *signals[3];
+static GHashTable *perl_signal_args_hash;
+static GSList *perl_signal_args_partial;
+
+static PERL_SIGNAL_ARGS_REC *perl_signal_args_find(int signal_id)
+{
+       PERL_SIGNAL_ARGS_REC *rec;
+        GSList *tmp;
+       const char *signame;
+
+       rec = g_hash_table_lookup(perl_signal_args_hash,
+                                 GINT_TO_POINTER(signal_id));
+        if (rec != NULL) return rec;
+
+       /* try to find by name */
+       signame = signal_get_id_str(signal_id);
+       for (tmp = perl_signal_args_partial; tmp != NULL; tmp = tmp->next) {
+               rec = tmp->data;
+
+               if (strncmp(rec->signal, signame, strlen(rec->signal)) == 0)
+                       return rec;
+       }
+
+       return NULL;
+}
+
+static void perl_call_signal(PERL_SCRIPT_REC *script, SV *func,
+                            int signal_id, gconstpointer *args)
+{
+       dSP;
+
+       PERL_SIGNAL_ARGS_REC *rec;
+       SV *sv, *perlarg, *saved_args[SIGNAL_MAX_ARGUMENTS];
+       AV *av;
+        void *arg;
+       int n;
+
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(sp);
+
+       /* push signal argument to perl stack */
+       rec = perl_signal_args_find(signal_id);
+
+        memset(saved_args, 0, sizeof(saved_args));
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS &&
+                   rec != NULL && rec->args[n] != NULL; n++) {
+               arg = (void *) args[n];
+
+               if (strcmp(rec->args[n], "string") == 0)
+                       perlarg = new_pv(arg);
+               else if (strcmp(rec->args[n], "int") == 0)
+                       perlarg = newSViv(GPOINTER_TO_INT(arg));
+               else if (strcmp(rec->args[n], "ulongptr") == 0)
+                       perlarg = newSViv(*(unsigned long *) arg);
+               else if (strcmp(rec->args[n], "intptr") == 0)
+                       saved_args[n] = perlarg = newRV_noinc(newSViv(*(int *) arg));
+               else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
+                       /* pointer to linked list - push as AV */
+                       GList *tmp, **ptr;
+                        int is_iobject, is_str;
+
+                        is_iobject = strcmp(rec->args[n]+9, "iobject") == 0;
+                        is_str = strcmp(rec->args[n]+9, "char*") == 0;
+                       av = newAV();
+
+                       ptr = arg;
+                       for (tmp = *ptr; tmp != NULL; tmp = tmp->next) {
+                               sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
+                                       is_str ? new_pv(tmp->data) :
+                                       irssi_bless_plain(rec->args[n]+9, tmp->data);
+                               av_push(av, sv);
+                       }
+
+                       saved_args[n] = perlarg = newRV_noinc((SV *) av);
+               } else if (strncmp(rec->args[n], "gslist_", 7) == 0) {
+                       /* linked list - push as AV */
+                       GSList *tmp;
+                       int is_iobject;
+
+                        is_iobject = strcmp(rec->args[n]+7, "iobject") == 0;
+                       av = newAV();
+                       for (tmp = arg; tmp != NULL; tmp = tmp->next) {
+                               sv = is_iobject ? iobject_bless((SERVER_REC *) tmp->data) :
+                                       irssi_bless_plain(rec->args[n]+7, tmp->data);
+                               av_push(av, sv);
+                       }
+
+                       perlarg = newRV_noinc((SV *) av);
+               } else if (arg == NULL) {
+                       /* don't bless NULL arguments */
+                       perlarg = newSViv(0);
+               } else if (strcmp(rec->args[n], "iobject") == 0) {
+                       /* "irssi object" - any struct that has
+                          "int type; int chat_type" as it's first
+                          variables (server, channel, ..) */
+                       perlarg = iobject_bless((SERVER_REC *) arg);
+               } else if (strcmp(rec->args[n], "siobject") == 0) {
+                       /* "simple irssi object" - any struct that has
+                          int type; as it's first variable (dcc) */
+                       perlarg = simple_iobject_bless((SERVER_REC *) arg);
+               } else {
+                       /* blessed object */
+                       perlarg = plain_bless(arg, rec->args[n]);
+               }
+               XPUSHs(sv_2mortal(perlarg));
+       }
+
+       PUTBACK;
+       perl_call_sv(func, G_EVAL|G_DISCARD);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+               char *error = g_strdup(SvPV(ERRSV, PL_na));
+               signal_emit("script error", 2, script, error);
+                g_free(error);
+                rec = NULL;
+       }
+
+        /* restore arguments the perl script modified */
+       for (n = 0; n < SIGNAL_MAX_ARGUMENTS &&
+                   rec != NULL && rec->args[n] != NULL; n++) {
+               arg = (void *) args[n];
+
+               if (saved_args[n] == NULL)
+                        continue;
+
+               if (strcmp(rec->args[n], "intptr") == 0) {
+                       int *val = arg;
+                       *val = SvIV(SvRV(saved_args[n]));
+               } else if (strncmp(rec->args[n], "glistptr_", 9) == 0) {
+                        GList **ret = arg;
+                       GList *out = NULL;
+                        void *val;
+                       STRLEN len;
+                        int count;
+
+                       av = (AV *) SvRV(saved_args[n]);
+                        count = av_len(av);
+                       while (count-- >= 0) {
+                               sv = av_shift(av);
+                               if (SvPOKp(sv))
+                                       val = g_strdup(SvPV(sv, len));
+                               else
+                                        val = GINT_TO_POINTER(SvIV(sv));
+
+                               out = g_list_append(out, val);
+                       }
+
+                       if (strcmp(rec->args[n]+9, "char*") == 0)
+                                g_list_foreach(*ret, (GFunc) g_free, NULL);
+                       g_list_free(*ret);
+                        *ret = out;
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+static void sig_func(int priority, gconstpointer *args)
+{
+       GSList **list, *tmp, *next;
+        int signal_id;
+
+        signal_id = signal_get_emitted_id();
+       list = g_hash_table_lookup(signals[priority],
+                                  GINT_TO_POINTER(signal_id));
+       for (tmp = list == NULL ? NULL : *list; tmp != NULL; tmp = next) {
+               PERL_SIGNAL_REC *rec = tmp->data;
+
+                next = tmp->next;
+               perl_call_signal(rec->script, rec->func, signal_id, args);
+               if (signal_is_stopped(signal_id))
+                        break;
+       }
+}
+
+#define SIG_FUNC_DECL(priority, priority_name) \
+static void sig_func_##priority_name(gconstpointer p1, gconstpointer p2, \
+                                    gconstpointer p3, gconstpointer p4, \
+                                    gconstpointer p5, gconstpointer p6) \
+{ \
+       gconstpointer args[6]; \
+        args[0] = p1; args[1] = p2; args[2] = p3; \
+        args[3] = p4; args[4] = p5; args[5] = p6; \
+        sig_func(priority, args); \
+}
+
+SIG_FUNC_DECL(0, first);
+SIG_FUNC_DECL(1, default);
+SIG_FUNC_DECL(2, last);
+
+#define priority_get_func(priority) \
+       (priority == 0 ? sig_func_first : \
+       priority == 1 ? sig_func_default : sig_func_last)
+
+#define perl_signal_get_func(rec) \
+       (priority_get_func((rec)->priority))
+
+static void perl_signal_add_to_int(const char *signal, SV *func,
+                                  int priority, int command)
+{
+        PERL_SCRIPT_REC *script;
+       PERL_SIGNAL_REC *rec;
+       GHashTable *table;
+       GSList **siglist;
+       void *signal_idp;
+
+        g_return_if_fail(signal != NULL);
+        g_return_if_fail(func != NULL);
+        g_return_if_fail(priority >= 0 && priority <= 2);
+
+        script = perl_script_find_package(perl_get_package());
+        g_return_if_fail(script != NULL);
+
+       if (!command && strncmp(signal, "command ", 8) == 0) {
+               /* we used Irssi::signal_add() instead of
+                  Irssi::command_bind() - oh well, allow this.. */
+               command_bind_to(MODULE_NAME, priority, signal+8, -1,
+                               NULL, priority_get_func(priority));
+                command = TRUE;
+       }
+
+       rec = g_new(PERL_SIGNAL_REC, 1);
+        rec->script = script;
+       rec->signal_id = signal_get_uniq_id(signal);
+       rec->signal = g_strdup(signal);
+       rec->func = perl_func_sv_inc(func, perl_get_package());
+       rec->priority = priority;
+
+       table = signals[priority];
+       signal_idp = GINT_TO_POINTER(rec->signal_id);
+
+       siglist = g_hash_table_lookup(table, signal_idp);
+       if (siglist == NULL) {
+               siglist = g_new0(GSList *, 1);
+               g_hash_table_insert(table, signal_idp, siglist);
+
+               if (!command) {
+                       signal_add_to_id(MODULE_NAME, priority, rec->signal_id,
+                                        perl_signal_get_func(rec));
+               }
+       }
+
+       *siglist = g_slist_append(*siglist, rec);
+}
+
+void perl_signal_add_to(const char *signal, SV *func, int priority)
+{
+        perl_signal_add_to_int(signal, func, priority, FALSE);
+}
+
+static void perl_signal_destroy(PERL_SIGNAL_REC *rec)
+{
+       if (strncmp(rec->signal, "command ", 8) == 0)
+               command_unbind(rec->signal+8, perl_signal_get_func(rec));
+
+        SvREFCNT_dec(rec->func);
+       g_free(rec->signal);
+       g_free(rec);
+}
+
+static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec)
+{
+       void *signal_idp;
+
+       g_return_if_fail(rec != NULL);
+
+       signal_idp = GINT_TO_POINTER(rec->signal_id);
+
+       *siglist = g_slist_remove(*siglist, rec);
+       if (*siglist == NULL) {
+               signal_remove_id(rec->signal_id, perl_signal_get_func(rec));
+               g_free(siglist);
+               g_hash_table_remove(signals[rec->priority], signal_idp);
+       }
+
+        perl_signal_destroy(rec);
+}
+
+#define sv_func_cmp(f1, f2, len) \
+       (f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \
+               strcmp((char *) SvPV(f1, len), (char *) SvPV(f2, len)) == 0))
+
+static void perl_signal_remove_list(GSList **list, SV *func)
+{
+       GSList *tmp;
+
+       g_return_if_fail(list != NULL);
+
+       for (tmp = *list; tmp != NULL; tmp = tmp->next) {
+               PERL_SIGNAL_REC *rec = tmp->data;
+
+               if (sv_func_cmp(rec->func, func, PL_na)) {
+                       perl_signal_remove_list_one(list, rec);
+                       break;
+               }
+       }
+}
+
+void perl_signal_remove(const char *signal, SV *func)
+{
+       GSList **list;
+        void *signal_idp;
+       int n;
+
+       signal_idp = GINT_TO_POINTER(signal_get_uniq_id(signal));
+
+        func = perl_func_sv_inc(func, perl_get_package());
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               list = g_hash_table_lookup(signals[n], signal_idp);
+               if (list != NULL)
+                       perl_signal_remove_list(list, func);
+       }
+        SvREFCNT_dec(func);
+}
+
+void perl_command_bind_to(const char *cmd, const char *category,
+                         SV *func, int priority)
+{
+       char *signal;
+
+       command_bind_to(MODULE_NAME, priority, cmd, -1,
+                       category, priority_get_func(priority));
+
+       signal = g_strconcat("command ", cmd, NULL);
+       perl_signal_add_to_int(signal, func, priority, TRUE);
+       g_free(signal);
+}
+
+void perl_command_runsub(const char *cmd, const char *data, 
+                        SERVER_REC *server, WI_ITEM_REC *item)
+{
+       command_runsub(cmd, data, server, item);
+}
+
+void perl_command_unbind(const char *cmd, SV *func)
+{
+       char *signal;
+
+        /* perl_signal_remove() calls command_unbind() */
+       signal = g_strconcat("command ", cmd, NULL);
+       perl_signal_remove(signal, func);
+       g_free(signal);
+}
+
+static int signal_destroy_hash(void *key, GSList **list, PERL_SCRIPT_REC *script)
+{
+       GSList *tmp, *next;
+
+       for (tmp = *list; tmp != NULL; tmp = next) {
+               PERL_SIGNAL_REC *rec = tmp->data;
+
+               next = tmp->next;
+               if (script == NULL || rec->script == script) {
+                       *list = g_slist_remove(*list, rec);
+                       if (*list == NULL) {
+                               signal_remove_id(rec->signal_id,
+                                                perl_signal_get_func(rec));
+                       }
+                       perl_signal_destroy(rec);
+               }
+       }
+
+       if (*list != NULL)
+               return FALSE;
+
+       g_free(list);
+       return TRUE;
+}
+
+/* destroy all signals used by script */
+void perl_signal_remove_script(PERL_SCRIPT_REC *script)
+{
+       int n;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               g_hash_table_foreach_remove(signals[n],
+                                           (GHRFunc) signal_destroy_hash,
+                                           script);
+       }
+}
+
+void perl_signals_start(void)
+{
+       int n;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               signals[n] = g_hash_table_new((GHashFunc) g_direct_hash,
+                                             (GCompareFunc) g_direct_equal);
+       }
+}
+
+void perl_signals_stop(void)
+{
+       int n;
+
+       for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
+               g_hash_table_foreach(signals[n],
+                                    (GHFunc) signal_destroy_hash, NULL);
+               g_hash_table_destroy(signals[n]);
+                signals[n] = NULL;
+       }
+}
+
+void perl_signals_init(void)
+{
+       int n;
+
+       perl_signal_args_hash = g_hash_table_new((GHashFunc) g_direct_hash,
+                                                (GCompareFunc) g_direct_equal);
+        perl_signal_args_partial = NULL;
+
+       for (n = 0; perl_signal_args[n].signal != NULL; n++) {
+               PERL_SIGNAL_ARGS_REC *rec = &perl_signal_args[n];
+
+               if (rec->signal[strlen(rec->signal)-1] == ' ') {
+                       perl_signal_args_partial =
+                               g_slist_append(perl_signal_args_partial, rec);
+               } else {
+                        int signal_id = signal_get_uniq_id(rec->signal);
+                       g_hash_table_insert(perl_signal_args_hash,
+                                           GINT_TO_POINTER(signal_id),
+                                           rec);
+               }
+       }
+}
+
+void perl_signals_deinit(void)
+{
+        g_slist_free(perl_signal_args_partial);
+        g_hash_table_destroy(perl_signal_args_hash);
+}
diff --git a/apps/irssi/src/perl/perl-signals.h b/apps/irssi/src/perl/perl-signals.h
new file mode 100644 (file)
index 0000000..33e0c1b
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __PERL_SIGNALS_H
+#define __PERL_SIGNALS_H
+
+void perl_signal_add_to(const char *signal, SV *func, int priority);
+#define perl_signal_add_first(signal, func) \
+        perl_signal_add_to(signal, func, 0)
+#define perl_signal_add(signal, func) \
+        perl_signal_add_to(signal, func, 1)
+#define perl_signal_add_last(signal, func) \
+        perl_signal_add_to(signal, func, 2)
+
+void perl_signal_remove(const char *signal, SV *func);
+/* remove all signals used by script */
+void perl_signal_remove_script(PERL_SCRIPT_REC *script);
+
+void perl_command_bind_to(const char *cmd, const char *category,
+                         SV *func, int priority);
+#define perl_command_bind_first(cmd, category, func) \
+        perl_command_bind_to(cmd, category, func, 0)
+#define perl_command_bind(cmd, category, func) \
+        perl_command_bind_to(cmd, category, func, 1)
+#define perl_command_bind_last(cmd, category, func) \
+        perl_command_bind_to(cmd, category, func, 2)
+
+void perl_command_unbind(const char *cmd, SV *func);
+
+void perl_signals_start(void);
+void perl_signals_stop(void);
+
+void perl_signals_init(void);
+void perl_signals_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/perl/perl-sources.c b/apps/irssi/src/perl/perl-sources.c
new file mode 100644 (file)
index 0000000..be1a418
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ perl-sources.c : irssi
+
+    Copyright (C) 1999-2001 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define NEED_PERL_H
+#include "module.h"
+#include "signals.h"
+
+#include "perl-core.h"
+#include "perl-common.h"
+
+typedef struct {
+        PERL_SCRIPT_REC *script;
+       int tag;
+        int refcount;
+
+       SV *func;
+       SV *data;
+} PERL_SOURCE_REC;
+
+static GSList *perl_sources;
+
+static void perl_source_ref(PERL_SOURCE_REC *rec)
+{
+        rec->refcount++;
+}
+
+static void perl_source_unref(PERL_SOURCE_REC *rec)
+{
+       if (--rec->refcount != 0)
+               return;
+
+        SvREFCNT_dec(rec->data);
+        SvREFCNT_dec(rec->func);
+       g_free(rec);
+}
+
+static void perl_source_destroy(PERL_SOURCE_REC *rec)
+{
+       perl_sources = g_slist_remove(perl_sources, rec);
+
+       g_source_remove(rec->tag);
+       rec->tag = -1;
+
+       perl_source_unref(rec);
+}
+
+static int perl_source_event(PERL_SOURCE_REC *rec)
+{
+       dSP;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+       XPUSHs(sv_mortalcopy(rec->data));
+       PUTBACK;
+
+        perl_source_ref(rec);
+       perl_call_sv(rec->func, G_EVAL|G_DISCARD);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+                char *error = g_strdup(SvPV(ERRSV, PL_na));
+               signal_emit("script error", 2, rec->script, error);
+                g_free(error);
+       }
+        perl_source_unref(rec);
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+
+       return 1;
+}
+
+int perl_timeout_add(int msecs, SV *func, SV *data)
+{
+        PERL_SCRIPT_REC *script;
+       PERL_SOURCE_REC *rec;
+       const char *pkg;
+
+        pkg = perl_get_package();
+       script = perl_script_find_package(pkg);
+        g_return_val_if_fail(script != NULL, -1);
+
+       rec = g_new0(PERL_SOURCE_REC, 1);
+       perl_source_ref(rec);
+
+        rec->script = script;
+       rec->func = perl_func_sv_inc(func, pkg);
+       rec->data = SvREFCNT_inc(data);
+       rec->tag = g_timeout_add(msecs, (GSourceFunc) perl_source_event, rec);
+
+       perl_sources = g_slist_append(perl_sources, rec);
+       return rec->tag;
+}
+
+int perl_input_add(int source, int condition, SV *func, SV *data)
+{
+        PERL_SCRIPT_REC *script;
+       PERL_SOURCE_REC *rec;
+       GIOChannel *channel;
+        const char *pkg;
+
+        pkg = perl_get_package();
+       script = perl_script_find_package(pkg);
+        g_return_val_if_fail(script != NULL, -1);
+
+       rec = g_new0(PERL_SOURCE_REC, 1);
+       perl_source_ref(rec);
+
+        rec->script =script;
+       rec->func = perl_func_sv_inc(func, pkg);
+       rec->data = SvREFCNT_inc(data);
+
+       channel = g_io_channel_unix_new(source);
+       rec->tag = g_input_add(channel, condition,
+                              (GInputFunction) perl_source_event, rec);
+       g_io_channel_unref(channel);
+
+       perl_sources = g_slist_append(perl_sources, rec);
+       return rec->tag;
+}
+
+void perl_source_remove(int tag)
+{
+       GSList *tmp;
+
+       for (tmp = perl_sources; tmp != NULL; tmp = tmp->next) {
+               PERL_SOURCE_REC *rec = tmp->data;
+
+               if (rec->tag == tag) {
+                       perl_source_destroy(rec);
+                       break;
+               }
+       }
+}
+
+void perl_source_remove_script(PERL_SCRIPT_REC *script)
+{
+       GSList *tmp, *next;
+
+       for (tmp = perl_sources; tmp != NULL; tmp = next) {
+               PERL_SOURCE_REC *rec = tmp->data;
+
+               next = tmp->next;
+                if (rec->script == script)
+                       perl_source_destroy(rec);
+       }
+}
+
+void perl_sources_start(void)
+{
+       perl_sources = NULL;
+}
+
+void perl_sources_stop(void)
+{
+       /* timeouts and input waits */
+       while (perl_sources != NULL)
+               perl_source_destroy(perl_sources->data);
+}
diff --git a/apps/irssi/src/perl/perl-sources.h b/apps/irssi/src/perl/perl-sources.h
new file mode 100644 (file)
index 0000000..db16568
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __PERL_SOURCES_H
+#define __PERL_SOURCES_H
+
+int perl_timeout_add(int msecs, SV *func, SV *data);
+int perl_input_add(int source, int condition, SV *func, SV *data);
+
+void perl_source_remove(int tag);
+/* remove all sources used by script */
+void perl_source_remove_script(PERL_SCRIPT_REC *script);
+
+void perl_sources_start(void);
+void perl_sources_stop(void);
+
+#endif
diff --git a/apps/irssi/src/perl/textui/.cvsignore b/apps/irssi/src/perl/textui/.cvsignore
new file mode 100644 (file)
index 0000000..517db94
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.PL
+TextUI.bs
+*.c
+*.o
+pm_to_blib
+blib
diff --git a/apps/irssi/src/perl/textui/Makefile.PL.in b/apps/irssi/src/perl/textui/Makefile.PL.in
new file mode 100644 (file)
index 0000000..9e80274
--- /dev/null
@@ -0,0 +1,8 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi::TextUI',
+              'LIBS' => '',
+             'OBJECT' => '$(O_FILES)',
+              'TYPEMAPS' => ['../common/typemap', '../ui/typemap'],
+              'INC' => '-I../../.. -I@top_srcdir@/src -I@top_srcdir@/src/core -I@top_srcdir@/src/fe-common/core -I@top_srcdir@/src/fe-text @GLIB_CFLAGS@',
+             'VERSION_FROM' => '@srcdir@/TextUI.pm');
diff --git a/apps/irssi/src/perl/textui/Statusbar.xs b/apps/irssi/src/perl/textui/Statusbar.xs
new file mode 100644 (file)
index 0000000..67b88e4
--- /dev/null
@@ -0,0 +1,164 @@
+#include "module.h"
+
+static GHashTable *perl_sbar_defs;
+
+static int check_sbar_destroy(char *key, char *value, char *script)
+{
+       if (strncmp(value, script, strlen(script)) == 0 &&
+           value[strlen(script)] == ':') {
+                statusbar_item_unregister(key);
+               g_free(key);
+                g_free(value);
+               return TRUE;
+       }
+
+        return FALSE;
+}
+
+static void script_unregister_statusbars(PERL_SCRIPT_REC *script)
+{
+       g_hash_table_foreach_remove(perl_sbar_defs,
+                                   (GHRFunc) check_sbar_destroy,
+                                   script->package);
+}
+
+void perl_statusbar_init(void)
+{
+       perl_sbar_defs = g_hash_table_new((GHashFunc) g_str_hash,
+                                         (GCompareFunc) g_str_equal);
+       signal_add("script destroyed", (SIGNAL_FUNC) script_unregister_statusbars);
+}
+
+static void statusbar_item_def_destroy(void *key, void *value)
+{
+       g_free(key);
+        g_free(value);
+}
+
+void perl_statusbar_deinit(void)
+{
+       signal_remove("script destroyed", (SIGNAL_FUNC) script_unregister_statusbars);
+
+       g_hash_table_foreach(perl_sbar_defs,
+                            (GHFunc) statusbar_item_def_destroy, NULL);
+       g_hash_table_destroy(perl_sbar_defs);
+}
+
+static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item,
+                                int get_size_only)
+{
+       dSP;
+       int retcount;
+       SV *item_sv, **sv;
+        HV *hv;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK(SP);
+        item_sv = plain_bless(item, "Irssi::TextUI::StatusbarItem");
+       XPUSHs(sv_2mortal(item_sv));
+       XPUSHs(sv_2mortal(newSViv(get_size_only)));
+       PUTBACK;
+
+       retcount = perl_call_pv(function, G_EVAL|G_DISCARD);
+       SPAGAIN;
+
+       if (SvTRUE(ERRSV)) {
+                PERL_SCRIPT_REC *script;
+                char *package;
+
+                package = perl_function_get_package(function);
+                script = perl_script_find_package(package);
+                g_free(package);
+
+               if (script != NULL) {
+                        /* make sure we don't get back here */
+                       script_unregister_statusbars(script);
+               }
+               signal_emit("script error", 2, script, SvPV(ERRSV, PL_na));
+       } else {
+               /* min_size and max_size can be changed, move them to SBAR_ITEM_REC */
+               hv = hvref(item_sv);
+               if (hv != NULL) {
+                       sv = hv_fetch(hv, "min_size", 8, 0);
+                       if (sv != NULL) item->min_size = SvIV(*sv);
+                       sv = hv_fetch(hv, "max_size", 8, 0);
+                       if (sv != NULL) item->max_size = SvIV(*sv);
+               }
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+}
+
+
+static void sig_perl_statusbar(SBAR_ITEM_REC *item, int get_size_only)
+{
+       char *function;
+
+       function = g_hash_table_lookup(perl_sbar_defs, item->config->name);
+       if (function != NULL)
+               perl_statusbar_event(function, item, get_size_only);
+       else {
+               /* use default function - this shouldn't actually happen.. */
+               statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE);
+       }
+}
+
+MODULE = Irssi::TextUI::Statusbar  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+statusbar_item_register(name, value, func = NULL)
+       char *name
+       char *value
+       char *func
+CODE:
+       statusbar_item_register(name, value, func == NULL || *func == '\0' ? NULL : sig_perl_statusbar);
+       if (func != NULL) {
+               g_hash_table_insert(perl_sbar_defs, g_strdup(name),
+                                   g_strdup_printf("%s::%s", perl_get_package(), func));
+       }
+
+void
+statusbar_item_unregister(name)
+       char *name
+PREINIT:
+        gpointer key, value;
+CODE:
+       if (g_hash_table_lookup_extended(perl_sbar_defs, name, &key, &value)) {
+                g_hash_table_remove(perl_sbar_defs, name);
+               g_free(key);
+                g_free(value);
+       }
+       statusbar_item_unregister(name);
+
+void
+statusbar_items_redraw(name)
+       char *name
+
+void
+statusbars_recreate_items()
+
+#*******************************
+MODULE = Irssi::TextUI::Statusbar  PACKAGE = Irssi::TextUI::StatusbarItem  PREFIX = statusbar_item_
+#*******************************
+
+void
+statusbar_item_default_handler(item, get_size_only, str, data, escape_vars = TRUE)
+       Irssi::TextUI::StatusbarItem item
+       int get_size_only
+       char *str
+       char *data
+       int escape_vars
+PREINIT:
+       HV *hv;
+CODE:
+       statusbar_item_default_handler(item, get_size_only,
+                                      *str == '\0' ? NULL : str,
+                                      data, escape_vars);
+       hv = hvref(ST(0));
+       hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+       hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
diff --git a/apps/irssi/src/perl/textui/TextBuffer.xs b/apps/irssi/src/perl/textui/TextBuffer.xs
new file mode 100644 (file)
index 0000000..4fb92f1
--- /dev/null
@@ -0,0 +1,83 @@
+#include "module.h"
+
+MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+Irssi::TextUI::TextBuffer
+textbuffer_create()
+
+#*******************************
+MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi::TextUI::TextBuffer  PREFIX = textbuffer_
+#*******************************
+
+void
+textbuffer_destroy(buffer)
+       Irssi::TextUI::TextBuffer buffer
+
+Irssi::TextUI::Line
+textbuffer_append(buffer, data, len, info)
+       Irssi::TextUI::TextBuffer buffer
+       char *data
+       int len
+       Irssi::TextUI::LineInfo info
+
+Irssi::TextUI::Line
+textbuffer_insert(buffer, insert_after, data, len, info)
+       Irssi::TextUI::TextBuffer buffer
+       Irssi::TextUI::Line insert_after
+       char *data
+       int len
+       Irssi::TextUI::LineInfo info
+
+void
+textbuffer_remove(buffer, line)
+       Irssi::TextUI::TextBuffer buffer
+       Irssi::TextUI::Line line
+
+void
+textbuffer_remove_all_lines(buffer)
+       Irssi::TextUI::TextBuffer buffer
+
+#*******************************
+MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi::TextUI::Line  PREFIX = textbuffer_line_
+#*******************************
+
+Irssi::TextUI::Line
+textbuffer_line_prev(line)
+       Irssi::TextUI::Line line
+CODE:
+       RETVAL = line->prev;
+OUTPUT:
+       RETVAL
+
+Irssi::TextUI::Line
+textbuffer_line_next(line)
+       Irssi::TextUI::Line line
+CODE:
+       RETVAL = line->next;
+OUTPUT:
+       RETVAL
+
+void
+textbuffer_line_ref(line)
+       Irssi::TextUI::Line line
+
+void
+textbuffer_line_unref(line, buffer)
+       Irssi::TextUI::Line line
+       Irssi::TextUI::TextBuffer buffer
+CODE:
+       textbuffer_line_unref(buffer, line);
+
+void
+textbuffer_line_get_text(line, coloring)
+       Irssi::TextUI::Line line
+       int coloring
+PREINIT:
+       GString *str;
+PPCODE:
+       str = g_string_new(NULL);
+       textbuffer_line2text(line, coloring, str);
+       XPUSHs(sv_2mortal(new_pv(str->str)));
+       g_string_free(str, TRUE);
+
diff --git a/apps/irssi/src/perl/textui/TextBufferView.xs b/apps/irssi/src/perl/textui/TextBufferView.xs
new file mode 100644 (file)
index 0000000..5b9b1cc
--- /dev/null
@@ -0,0 +1,108 @@
+#include "module.h"
+
+MODULE = Irssi::TextUI::TextBufferView  PACKAGE = Irssi::TextUI::TextBuffer  PREFIX = textbuffer_
+PROTOTYPES: ENABLE
+
+Irssi::TextUI::TextBufferView
+textbuffer_view_create(buffer, width, height, scroll, utf8)
+       Irssi::TextUI::TextBuffer buffer
+       int width
+       int height
+       int scroll
+       int utf8
+
+#*******************************
+MODULE = Irssi::TextUI::TextBufferView  PACKAGE = Irssi::TextUI::TextBufferView  PREFIX = textbuffer_view_
+#*******************************
+
+void
+textbuffer_view_destroy(view)
+       Irssi::TextUI::TextBufferView view
+
+void
+textbuffer_view_set_default_indent(view, default_indent, longword_noindent)
+       Irssi::TextUI::TextBufferView view
+       int default_indent
+       int longword_noindent
+CODE:
+       textbuffer_view_set_default_indent(view, default_indent, longword_noindent, NULL);
+
+void
+textbuffer_view_set_scroll(view, scroll)
+       Irssi::TextUI::TextBufferView view
+       int scroll
+
+void
+textbuffer_view_resize(view, width, height)
+       Irssi::TextUI::TextBufferView view
+       int width
+       int height
+
+void
+textbuffer_view_clear(view)
+       Irssi::TextUI::TextBufferView view
+
+Irssi::TextUI::Line
+textbuffer_view_get_lines(view)
+       Irssi::TextUI::TextBufferView view
+
+void
+textbuffer_view_scroll(view, lines)
+       Irssi::TextUI::TextBufferView view
+       int lines
+
+void
+textbuffer_view_scroll_line(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+Irssi::TextUI::LineCache
+textbuffer_view_get_line_cache(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_insert_line(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_remove_line(view, line)
+       Irssi::TextUI::TextBufferView view
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_remove_all_lines(view)
+       Irssi::TextUI::TextBufferView view
+
+void
+textbuffer_view_set_bookmark(view, name, line)
+       Irssi::TextUI::TextBufferView view
+       char *name
+       Irssi::TextUI::Line line
+
+void
+textbuffer_view_set_bookmark_bottom(view, name)
+       Irssi::TextUI::TextBufferView view
+       char *name
+
+Irssi::TextUI::Line
+textbuffer_view_get_bookmark(view, name)
+       Irssi::TextUI::TextBufferView view
+       char *name
+
+void
+textbuffer_view_redraw(view)
+       Irssi::TextUI::TextBufferView view
+
+#*******************************
+MODULE = Irssi::TextUI::TextBufferView  PACKAGE = Irssi::UI::Window
+#*******************************
+
+Irssi::TextUI::TextBufferView
+view(window)
+       Irssi::UI::Window window
+CODE:
+       RETVAL = WINDOW_GUI(window)->view;
+OUTPUT:
+       RETVAL
diff --git a/apps/irssi/src/perl/textui/TextUI.pm b/apps/irssi/src/perl/textui/TextUI.pm
new file mode 100644 (file)
index 0000000..50f247c
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi::TextUI;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+$VERSION = "0.9";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw();
+@EXPORT_OK = qw();
+
+bootstrap Irssi::TextUI $VERSION if (!Irssi::Core::is_static());
+
+Irssi::TextUI::init();
+
+Irssi::EXPORT_ALL();
+
+1;
+
diff --git a/apps/irssi/src/perl/textui/TextUI.xs b/apps/irssi/src/perl/textui/TextUI.xs
new file mode 100644 (file)
index 0000000..8d49ac4
--- /dev/null
@@ -0,0 +1,160 @@
+#include "module.h"
+
+static int initialized = FALSE;
+
+static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window)
+{
+       hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0);
+
+       hv_store(hv, "first_line", 10, newSViv(window->first_line), 0);
+       hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+       hv_store(hv, "width", 5, newSViv(window->width), 0);
+       hv_store(hv, "height", 6, newSViv(window->height), 0);
+
+       hv_store(hv, "statusbar_lines", 15, newSViv(window->statusbar_lines), 0);
+}
+
+static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer)
+{
+       hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0);
+       hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0);
+}
+
+static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view)
+{
+       hv_store(hv, "buffer", 6, plain_bless(view->buffer, "Irssi::TextUI::TextBuffer"), 0);
+       hv_store(hv, "width", 5, newSViv(view->width), 0);
+       hv_store(hv, "height", 6, newSViv(view->height), 0);
+
+       hv_store(hv, "default_indent", 14, newSViv(view->default_indent), 0);
+       hv_store(hv, "longword_noindent", 17, newSViv(view->longword_noindent), 0);
+       hv_store(hv, "scroll", 6, newSViv(view->scroll), 0);
+
+       hv_store(hv, "ypos", 4, newSViv(view->ypos), 0);
+
+       hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "subline", 7, newSViv(view->subline), 0);
+
+       hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0);
+       hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0);
+
+       hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0);
+       hv_store(hv, "bottom", 6, newSViv(view->bottom), 0);
+}
+
+static void perl_line_fill_hash(HV *hv, LINE_REC *line)
+{
+       hv_store(hv, "refcount", 8, newSViv(line->refcount), 0);
+       hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0);
+}
+
+static void perl_line_cache_fill_hash(HV *hv, LINE_CACHE_REC *cache)
+{
+       hv_store(hv, "last_access", 11, newSViv(cache->last_access), 0);
+       hv_store(hv, "count", 5, newSViv(cache->count), 0);
+       /*LINE_CACHE_SUB_REC lines[1];*/
+}
+
+static void perl_line_info_fill_hash(HV *hv, LINE_INFO_REC *info)
+{
+       hv_store(hv, "level", 5, newSViv(info->level), 0);
+       hv_store(hv, "time", 4, newSViv(info->time), 0);
+}
+
+static void perl_statusbar_item_fill_hash(HV *hv, SBAR_ITEM_REC *item)
+{
+       hv_store(hv, "min_size", 8, newSViv(item->min_size), 0);
+       hv_store(hv, "max_size", 8, newSViv(item->max_size), 0);
+       hv_store(hv, "xpos", 4, newSViv(item->xpos), 0);
+       hv_store(hv, "size", 4, newSViv(item->size), 0);
+}
+
+static PLAIN_OBJECT_INIT_REC textui_plains[] = {
+       { "Irssi::TextUI::MainWindow", (PERL_OBJECT_FUNC) perl_main_window_fill_hash },
+       { "Irssi::TextUI::TextBuffer", (PERL_OBJECT_FUNC) perl_text_buffer_fill_hash },
+       { "Irssi::TextUI::TextBufferView", (PERL_OBJECT_FUNC) perl_text_buffer_view_fill_hash },
+       { "Irssi::TextUI::Line", (PERL_OBJECT_FUNC) perl_line_fill_hash },
+       { "Irssi::TextUI::LineCache", (PERL_OBJECT_FUNC) perl_line_cache_fill_hash },
+       { "Irssi::TextUI::LineInfo", (PERL_OBJECT_FUNC) perl_line_info_fill_hash },
+       { "Irssi::TextUI::StatusbarItem", (PERL_OBJECT_FUNC) perl_statusbar_item_fill_hash },
+
+       { NULL, NULL }
+};
+
+MODULE = Irssi::TextUI  PACKAGE = Irssi::TextUI
+
+PROTOTYPES: ENABLE
+
+void
+init()
+CODE:
+       if (initialized) return;
+       perl_api_version_check("Irssi::TextUI");
+       initialized = TRUE;
+
+        irssi_add_plains(textui_plains);
+        perl_statusbar_init();
+
+void
+deinit()
+CODE:
+       if (!initialized) return;
+        perl_statusbar_deinit();
+
+MODULE = Irssi::TextUI PACKAGE = Irssi
+
+void
+gui_printtext(xpos, ypos, str)
+       int xpos
+       int ypos
+       char *str
+
+MODULE = Irssi::TextUI PACKAGE = Irssi::UI::Window
+
+void
+gui_printtext_after(window, prev, level, str)
+       Irssi::UI::Window window
+       Irssi::TextUI::Line prev
+       int level
+       char *str
+PREINIT:
+       TEXT_DEST_REC dest;
+CODE:
+       format_create_dest(&dest, NULL, NULL, level, window);
+       gui_printtext_after(&dest, prev, str);
+
+Irssi::TextUI::Line
+last_line_insert(window)
+       Irssi::UI::Window window
+CODE:
+       RETVAL = WINDOW_GUI(window)->insert_after;
+OUTPUT:
+       RETVAL
+
+MODULE = Irssi::TextUI PACKAGE = Irssi::UI::Server
+
+void
+gui_printtext_after(server, target, prev, level, str)
+       Irssi::Server server
+       char *target
+       Irssi::TextUI::Line prev
+       int level
+       char *str
+PREINIT:
+       TEXT_DEST_REC dest;
+CODE:
+       format_create_dest(&dest, server, target, level, NULL);
+       gui_printtext_after(&dest, prev, str);
+
+BOOT:
+       irssi_boot(TextUI__Statusbar);
+       irssi_boot(TextUI__TextBuffer);
+       irssi_boot(TextUI__TextBufferView);
+
+void
+term_refresh_freeze()
+
+void
+term_refresh_thaw()
diff --git a/apps/irssi/src/perl/textui/module.h b/apps/irssi/src/perl/textui/module.h
new file mode 100644 (file)
index 0000000..9123afb
--- /dev/null
@@ -0,0 +1,16 @@
+#include "../ui/module.h"
+
+#include "mainwindows.h"
+#include "gui-windows.h"
+#include "gui-printtext.h"
+#include "statusbar.h"
+#include "textbuffer.h"
+#include "textbuffer-view.h"
+
+typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow;
+typedef TEXT_BUFFER_REC *Irssi__TextUI__TextBuffer;
+typedef TEXT_BUFFER_VIEW_REC *Irssi__TextUI__TextBufferView;
+typedef LINE_REC *Irssi__TextUI__Line;
+typedef LINE_CACHE_REC *Irssi__TextUI__LineCache;
+typedef LINE_INFO_REC *Irssi__TextUI__LineInfo;
+typedef SBAR_ITEM_REC *Irssi__TextUI__StatusbarItem;
diff --git a/apps/irssi/src/perl/textui/typemap b/apps/irssi/src/perl/textui/typemap
new file mode 100644 (file)
index 0000000..364cdf3
--- /dev/null
@@ -0,0 +1,19 @@
+TYPEMAP
+Irssi::TextUI::MainWindow      T_PlainObj
+Irssi::TextUI::TextBuffer      T_PlainObj
+Irssi::TextUI::TextBufferView  T_PlainObj
+Irssi::TextUI::Line            T_PlainObj
+Irssi::TextUI::LineCache       T_PlainObj
+Irssi::TextUI::LineInfo                T_PlainObj
+Irssi::TextUI::StatusbarItem           T_PlainObj
+
+INPUT
+
+T_PlainObj
+       $var = irssi_ref_object($arg)
+
+OUTPUT
+
+T_PlainObj
+       $arg = plain_bless($var, \"$type\");
+
diff --git a/apps/irssi/src/perl/ui/.cvsignore b/apps/irssi/src/perl/ui/.cvsignore
new file mode 100644 (file)
index 0000000..335ef88
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.PL
+UI.bs
+*.c
+*.o
+pm_to_blib
+blib
diff --git a/apps/irssi/src/perl/ui/Formats.xs b/apps/irssi/src/perl/ui/Formats.xs
new file mode 100644 (file)
index 0000000..0ad8c86
--- /dev/null
@@ -0,0 +1,27 @@
+#include "module.h"
+
+MODULE = Irssi::UI::Formats  PACKAGE = Irssi::UI::Window
+PROTOTYPES: ENABLE
+
+void
+format_get_text(window, module, server, target, formatnum, ...)
+       Irssi::UI::Window window
+       char *module
+       Irssi::Server server
+       char *target
+       int formatnum
+PREINIT:
+       char **charargs;
+       char *ret;
+       int n;
+PPCODE:
+       charargs = g_new0(char *, items-5+1);
+       charargs[items-5] = NULL;
+        for (n = 5; n < items; n++) {
+               charargs[n-5] = (char *)SvPV(ST(n), PL_na);
+       }
+       ret = format_get_text(module, window, server, target, formatnum, charargs);
+       g_free(charargs);
+
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
diff --git a/apps/irssi/src/perl/ui/Makefile.PL.in b/apps/irssi/src/perl/ui/Makefile.PL.in
new file mode 100644 (file)
index 0000000..a349918
--- /dev/null
@@ -0,0 +1,8 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile('NAME' => 'Irssi::UI',
+              'LIBS' => '',
+             'OBJECT' => '$(O_FILES)',
+              'TYPEMAPS' => ['../common/typemap'],
+              'INC' => '-I../../.. -I@top_srcdir@/src -I@top_srcdir@/src/core -I@top_srcdir@/src/fe-common/core @GLIB_CFLAGS@',
+             'VERSION_FROM' => '@srcdir@/UI.pm');
diff --git a/apps/irssi/src/perl/ui/Themes.xs b/apps/irssi/src/perl/ui/Themes.xs
new file mode 100644 (file)
index 0000000..fc3165e
--- /dev/null
@@ -0,0 +1,225 @@
+#include "module.h"
+
+void printformat_perl(TEXT_DEST_REC *dest, char *format, char **arglist)
+{
+       THEME_REC *theme;
+       char *module, *str;
+       int formatnum;
+
+       module = g_strdup(perl_get_package());
+       formatnum = format_find_tag(module, format);
+       if (formatnum < 0) {
+               die("printformat(): unregistered format '%s'", format);
+                g_free(module);
+               return;
+       }
+
+       theme = dest->window->theme == NULL ? current_theme :
+               dest->window->theme;
+       signal_emit("print format", 5, theme, module,
+                   dest, GINT_TO_POINTER(formatnum), arglist);
+
+        str = format_get_text_theme_charargs(theme, module, dest, formatnum, arglist);
+       if (*str != '\0') printtext_dest(dest, "%s", str);
+       g_free(str);
+       g_free(module);
+}
+
+static void perl_unregister_theme(const char *package)
+{
+       FORMAT_REC *formats;
+       int n;
+
+       formats = g_hash_table_lookup(default_formats, package);
+       if (formats == NULL) return;
+
+       for (n = 0; formats[n].def != NULL; n++) {
+               g_free(formats[n].tag);
+               g_free(formats[n].def);
+       }
+       g_free(formats);
+       theme_unregister_module(package);
+}
+
+static void sig_script_destroyed(PERL_SCRIPT_REC *script)
+{
+       perl_unregister_theme(script->package);
+}
+
+void perl_themes_init(void)
+{
+       signal_add("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+}
+
+void perl_themes_deinit(void)
+{
+       signal_remove("script destroyed", (SIGNAL_FUNC) sig_script_destroyed);
+}
+
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+Irssi::UI::Theme
+current_theme()
+CODE:
+       RETVAL = current_theme;
+OUTPUT:
+       RETVAL
+
+int
+EXPAND_FLAG_IGNORE_REPLACES()
+CODE:
+       RETVAL = EXPAND_FLAG_IGNORE_REPLACES;
+OUTPUT:
+       RETVAL
+
+int
+EXPAND_FLAG_IGNORE_EMPTY()
+CODE:
+       RETVAL = EXPAND_FLAG_IGNORE_EMPTY;
+OUTPUT:
+       RETVAL
+
+int
+EXPAND_FLAG_RECURSIVE_MASK()
+CODE:
+       RETVAL = EXPAND_FLAG_RECURSIVE_MASK;
+OUTPUT:
+       RETVAL
+
+void
+theme_register(formats)
+       SV *formats
+PREINIT:
+       AV *av;
+       FORMAT_REC *formatrecs;
+       char *key, *value;
+       int len, n, fpos;
+CODE:
+
+        if (!SvROK(formats))
+               croak("formats is not a reference to list");
+       av = (AV *) SvRV(formats);
+       len = av_len(av)+1;
+       if (len == 0 || (len & 1) != 0)
+               croak("formats list is invalid - not divisible by 2 (%d)", len);
+
+       formatrecs = g_new0(FORMAT_REC, len/2+2);
+       formatrecs[0].tag = g_strdup(perl_get_package());
+       formatrecs[0].def = g_strdup("Perl script");
+
+        for (fpos = 1, n = 0; n < len; n++, fpos++) {
+               key = SvPV(*av_fetch(av, n, 0), PL_na); n++;
+               value = SvPV(*av_fetch(av, n, 0), PL_na);
+
+               formatrecs[fpos].tag = g_strdup(key);
+               formatrecs[fpos].def = g_strdup(value);
+               formatrecs[fpos].params = MAX_FORMAT_PARAMS;
+       }
+
+       theme_register_module(perl_get_package(), formatrecs);
+
+void
+printformat(level, format, ...)
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, NULL, NULL, level, NULL);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 2; n < items && n < MAX_FORMAT_PARAMS+2; n++) {
+               arglist[n-2] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::Server
+#*******************************
+
+void
+printformat(server, target, level, format, ...)
+       Irssi::Server server
+       char *target
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, server, target, level, NULL);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 4; n < items && n < MAX_FORMAT_PARAMS+4; n++) {
+               arglist[n-4] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::UI::Window
+#*******************************
+
+void
+printformat(window, level, format, ...)
+       Irssi::UI::Window window
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, NULL, NULL, level, window);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 3; n < items && n < MAX_FORMAT_PARAMS+3; n++) {
+               arglist[n-3] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::Windowitem
+#*******************************
+
+void
+printformat(item, level, format, ...)
+       Irssi::Windowitem item
+       int level
+       char *format
+PREINIT:
+       TEXT_DEST_REC dest;
+       char *arglist[MAX_FORMAT_PARAMS+1];
+       int n;
+CODE:
+       format_create_dest(&dest, item->server, item->name, level, NULL);
+       memset(arglist, 0, sizeof(arglist));
+       for (n = 3; n < items && n < MAX_FORMAT_PARAMS+3; n++) {
+               arglist[n-3] = SvPV(ST(n), PL_na);
+       }
+
+        printformat_perl(&dest, format, arglist);
+
+#*******************************
+MODULE = Irssi::UI::Themes  PACKAGE = Irssi::UI::Theme  PREFIX = theme_
+#*******************************
+
+void
+theme_format_expand(theme, format, flags=0)
+       Irssi::UI::Theme theme
+       char *format
+        int flags
+PREINIT:
+       char *ret;
+PPCODE:
+       if (flags == 0) {
+               ret = theme_format_expand(theme, format);
+       } else {
+               ret = theme_format_expand_data(theme, (const char **) &format, 'n', 'n',
+                                              NULL, NULL, EXPAND_FLAG_ROOT | flags);
+       }
+       XPUSHs(sv_2mortal(new_pv(ret)));
+       g_free_not_null(ret);
diff --git a/apps/irssi/src/perl/ui/UI.pm b/apps/irssi/src/perl/ui/UI.pm
new file mode 100644 (file)
index 0000000..5bab7b6
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Perl interface to irssi functions.
+#
+
+package Irssi::UI;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+
+$VERSION = "0.9";
+
+require Exporter;
+require DynaLoader;
+
+@ISA = qw(Exporter DynaLoader);
+@EXPORT = qw();
+@EXPORT_OK = qw();
+
+bootstrap Irssi::UI $VERSION if (!Irssi::Core::is_static());
+
+Irssi::UI::init();
+
+Irssi::EXPORT_ALL();
+
+1;
diff --git a/apps/irssi/src/perl/ui/UI.xs b/apps/irssi/src/perl/ui/UI.xs
new file mode 100644 (file)
index 0000000..c2ee46b
--- /dev/null
@@ -0,0 +1,104 @@
+#include "module.h"
+
+static int initialized = FALSE;
+
+static void perl_process_fill_hash(HV *hv, PROCESS_REC *process)
+{
+       hv_store(hv, "id", 2, newSViv(process->id), 0);
+       hv_store(hv, "name", 4, new_pv(process->name), 0);
+       hv_store(hv, "args", 4, new_pv(process->args), 0);
+
+       hv_store(hv, "pid", 3, newSViv(process->pid), 0);
+       hv_store(hv, "target", 6, new_pv(process->target), 0);
+       if (process->target_win != NULL) {
+               hv_store(hv, "target_win", 10,
+                        plain_bless(process->target_win, "Irssi::UI::Window"), 0);
+       }
+       hv_store(hv, "shell", 5, newSViv(process->shell), 0);
+       hv_store(hv, "notice", 6, newSViv(process->notice), 0);
+       hv_store(hv, "silent", 6, newSViv(process->silent), 0);
+}
+
+static void perl_window_fill_hash(HV *hv, WINDOW_REC *window)
+{
+       hv_store(hv, "refnum", 6, newSViv(window->refnum), 0);
+       hv_store(hv, "name", 4, new_pv(window->name), 0);
+       hv_store(hv, "history_name", 12, new_pv(window->history_name), 0);
+
+       hv_store(hv, "width", 5, newSViv(window->width), 0);
+       hv_store(hv, "height", 6, newSViv(window->height), 0);
+
+       if (window->active)
+               hv_store(hv, "active", 6, iobject_bless(window->active), 0);
+       if (window->active_server)
+               hv_store(hv, "active_server", 13, iobject_bless(window->active_server), 0);
+
+       hv_store(hv, "servertag", 9, new_pv(window->servertag), 0);
+       hv_store(hv, "level", 5, newSViv(window->level), 0);
+
+       hv_store(hv, "immortal", 8, newSViv(window->immortal), 0);
+       hv_store(hv, "sticky_refnum", 13, newSViv(window->sticky_refnum), 0);
+
+       hv_store(hv, "data_level", 10, newSViv(window->data_level), 0);
+       hv_store(hv, "hilight_color", 13, new_pv(window->hilight_color), 0);
+
+       hv_store(hv, "last_timestamp", 14, newSViv(window->last_timestamp), 0);
+       hv_store(hv, "last_line", 9, newSViv(window->last_line), 0);
+
+       hv_store(hv, "theme", 5, plain_bless(window->theme, "Irssi::UI::Theme"), 0);
+       hv_store(hv, "theme_name", 10, new_pv(window->theme_name), 0);
+}
+
+static void perl_text_dest_fill_hash(HV *hv, TEXT_DEST_REC *dest)
+{
+       hv_store(hv, "window", 6, plain_bless(dest->window, "Irssi::UI::Window"), 0);
+       hv_store(hv, "server", 6, iobject_bless(dest->server), 0);
+       hv_store(hv, "target", 6, new_pv(dest->target), 0);
+       hv_store(hv, "level", 5, newSViv(dest->level), 0);
+
+       hv_store(hv, "hilight_priority", 16, newSViv(dest->hilight_priority), 0);
+       hv_store(hv, "hilight_color", 13, new_pv(dest->hilight_color), 0);
+}
+
+static PLAIN_OBJECT_INIT_REC fe_plains[] = {
+       { "Irssi::UI::Process", (PERL_OBJECT_FUNC) perl_process_fill_hash },
+       { "Irssi::UI::Window", (PERL_OBJECT_FUNC) perl_window_fill_hash },
+       { "Irssi::UI::TextDest", (PERL_OBJECT_FUNC) perl_text_dest_fill_hash },
+
+       { NULL, NULL }
+};
+
+MODULE = Irssi::UI  PACKAGE = Irssi::UI
+
+PROTOTYPES: ENABLE
+
+void
+processes()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = processes; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::UI::Process")));
+       }
+
+
+void
+init()
+CODE:
+       if (initialized) return;
+       perl_api_version_check("Irssi::UI");
+       initialized = TRUE;
+
+        irssi_add_plains(fe_plains);
+        perl_themes_init();
+
+void
+deinit()
+CODE:
+       if (!initialized) return;
+        perl_themes_deinit();
+
+BOOT:
+       irssi_boot(UI__Formats);
+       irssi_boot(UI__Themes);
+       irssi_boot(UI__Window);
diff --git a/apps/irssi/src/perl/ui/Window.xs b/apps/irssi/src/perl/ui/Window.xs
new file mode 100644 (file)
index 0000000..d618378
--- /dev/null
@@ -0,0 +1,270 @@
+#include "module.h"
+
+MODULE = Irssi::UI::Window  PACKAGE = Irssi
+PROTOTYPES: ENABLE
+
+void
+windows()
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+               XPUSHs(sv_2mortal(plain_bless(tmp->data, "Irssi::UI::Window")));
+       }
+
+
+Irssi::UI::Window
+active_win()
+CODE:
+       RETVAL = active_win;
+OUTPUT:
+       RETVAL
+
+Irssi::Server
+active_server()
+CODE:
+       RETVAL = active_win->active_server;
+OUTPUT:
+       RETVAL
+
+void
+print(str, level=MSGLEVEL_CLIENTNOTICE)
+       char *str
+        int level;
+CODE:
+       printtext_string(NULL, NULL, level, str);
+
+Irssi::UI::Window
+window_find_name(name)
+       char *name
+
+Irssi::UI::Window
+window_find_refnum(refnum)
+       int refnum
+
+int
+window_refnum_prev(refnum, wrap)
+       int refnum
+       int wrap
+
+int
+window_refnum_next(refnum, wrap)
+       int refnum
+       int wrap
+
+int
+windows_refnum_last()
+
+Irssi::UI::Window
+window_find_level(level)
+       int level
+CODE:
+       RETVAL = window_find_level(NULL, level);
+OUTPUT:
+       RETVAL
+
+Irssi::UI::Window
+window_find_item(name)
+       char *name
+CODE:
+       RETVAL = window_find_item(NULL, name);
+OUTPUT:
+       RETVAL
+
+Irssi::UI::Window
+window_find_closest(name, level)
+       char *name
+       int level
+CODE:
+       RETVAL = window_find_closest(NULL, name, level);
+OUTPUT:
+       RETVAL
+
+Irssi::Windowitem
+window_item_find(name)
+       char *name
+CODE:
+       RETVAL = window_item_find(NULL, name);
+OUTPUT:
+       RETVAL
+
+
+#*******************************
+MODULE = Irssi::UI::Window  PACKAGE = Irssi::Server
+#*******************************
+
+void
+print(server, channel, str, level=MSGLEVEL_CLIENTNOTICE)
+       Irssi::Server server
+       char *channel
+       char *str
+       int level
+CODE:
+       printtext_string(server, channel, level, str);
+
+Irssi::Windowitem
+window_item_find(server, name)
+       Irssi::Server server
+       char *name
+
+Irssi::UI::Window
+window_find_item(server, name)
+       Irssi::Server server
+       char *name
+
+Irssi::UI::Window
+window_find_level(server, level)
+       Irssi::Server server
+       int level
+
+Irssi::UI::Window
+window_find_closest(server, name, level)
+       Irssi::Server server
+       char *name
+       int level
+
+
+#*******************************
+MODULE = Irssi::UI::Window  PACKAGE = Irssi::UI::Window  PREFIX=window_
+#*******************************
+
+void
+items(window)
+       Irssi::UI::Window window
+PREINIT:
+       GSList *tmp;
+PPCODE:
+       for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
+                CHANNEL_REC *rec = tmp->data;
+
+               XPUSHs(sv_2mortal(iobject_bless(rec)));
+       }
+
+void
+print(window, str, level=MSGLEVEL_CLIENTNOTICE)
+       Irssi::UI::Window window
+       char *str
+        int level;
+CODE:
+       printtext_string_window(window, level, str);
+
+void
+command(window, cmd)
+       Irssi::UI::Window window
+       char *cmd
+PREINIT:
+       WINDOW_REC *old;
+CODE:
+       old = active_win;
+       active_win = window;
+       perl_command(cmd, window->active_server, window->active);
+        active_win = old;
+
+void
+window_item_add(window, item, automatic)
+       Irssi::UI::Window window
+       Irssi::Windowitem item
+       int automatic
+
+void
+window_item_remove(item)
+       Irssi::Windowitem item
+
+void
+window_item_destroy(item)
+       Irssi::Windowitem item
+
+void
+window_item_prev(window)
+       Irssi::UI::Window window
+
+void
+window_item_next(window)
+       Irssi::UI::Window window
+
+void
+window_destroy(window)
+       Irssi::UI::Window window
+
+void
+window_set_active(window)
+       Irssi::UI::Window window
+
+void
+window_change_server(window, server)
+       Irssi::UI::Window window
+       Irssi::Server server
+
+void
+window_set_refnum(window, refnum)
+       Irssi::UI::Window window
+       int refnum
+
+void
+window_set_name(window, name)
+       Irssi::UI::Window window
+       char *name
+
+void
+window_set_history(window, name)
+       Irssi::UI::Window window
+       char *name
+
+void
+window_set_level(window, level)
+       Irssi::UI::Window window
+       int level
+
+char *
+window_get_active_name(window)
+       Irssi::UI::Window window
+
+Irssi::Windowitem
+window_item_find(window, server, name)
+       Irssi::UI::Window window
+       Irssi::Server server
+       char *name
+CODE:
+       RETVAL = window_item_find_window(window, server, name);
+OUTPUT:
+       RETVAL
+
+#*******************************
+MODULE = Irssi::UI::Window  PACKAGE = Irssi::Windowitem  PREFIX = window_item_
+#*******************************
+
+void
+print(item, str, level=MSGLEVEL_CLIENTNOTICE)
+       Irssi::Windowitem item
+       int level
+       char *str
+CODE:
+       printtext_string(item->server, item->name, level, str);
+
+Irssi::UI::Window
+window_create(item, automatic)
+       Irssi::Windowitem item
+       int automatic
+
+Irssi::UI::Window
+window(item)
+       Irssi::Windowitem item
+CODE:
+       RETVAL = window_item_window(item);
+OUTPUT:
+       RETVAL
+
+void
+window_item_change_server(item, server)
+       Irssi::Windowitem item
+       Irssi::Server server
+
+int
+window_item_is_active(item)
+       Irssi::Windowitem item
+
+void
+window_item_set_active(item)
+       Irssi::Windowitem item
+CODE:
+       window_item_set_active(window_item_window(item), item);
diff --git a/apps/irssi/src/perl/ui/module.h b/apps/irssi/src/perl/ui/module.h
new file mode 100644 (file)
index 0000000..3177503
--- /dev/null
@@ -0,0 +1,14 @@
+#include "../common/module.h"
+
+#include "fe-windows.h"
+#include "fe-exec.h"
+#include "formats.h"
+#include "printtext.h"
+#include "window-items.h"
+#include "themes.h"
+#include "keyboard.h"
+
+typedef WINDOW_REC *Irssi__UI__Window;
+typedef TEXT_DEST_REC *Irssi__UI__TextDest;
+typedef THEME_REC *Irssi__UI__Theme;
+typedef KEYINFO_REC *Irssi__UI__Keyinfo;
diff --git a/apps/irssi/src/perl/ui/typemap b/apps/irssi/src/perl/ui/typemap
new file mode 100644 (file)
index 0000000..2a16fe4
--- /dev/null
@@ -0,0 +1,16 @@
+TYPEMAP
+Irssi::UI::Theme               T_PlainObj
+Irssi::UI::Window              T_PlainObj
+Irssi::UI::Keyinfo             T_PlainObj
+Irssi::UI::TextDest            T_PlainObj
+
+INPUT
+
+T_PlainObj
+       $var = irssi_ref_object($arg)
+
+OUTPUT
+
+T_PlainObj
+       $arg = plain_bless($var, \"$type\");
+
diff --git a/apps/irssi/src/silc/core/Makefile.am b/apps/irssi/src/silc/core/Makefile.am
new file mode 100644 (file)
index 0000000..c33f948
--- /dev/null
@@ -0,0 +1,35 @@
+include $(top_srcdir)/Makefile.defines.in
+
+IRSSI_INCLUDE=../../..
+
+ADD_INCLUDES = \
+       $(GLIB_CFLAGS) -I$(IRSSI_INCLUDE) -I$(IRSSI_INCLUDE)/src \
+        -DSYSCONFDIR=\""$(silc_etcdir)"\" \
+        -I$(IRSSI_INCLUDE) \
+       -I$(IRSSI_INCLUDE)/src \
+        -I$(IRSSI_INCLUDE)/src/core \
+        -I$(IRSSI_INCLUDE)/src/fe-common/core \
+        -I$(IRSSI_INCLUDE)/src/fe-common/silc
+
+noinst_LIBRARIES=libsilc_core.a
+
+libsilc_core_a_SOURCES = \
+       client_ops.c \
+       clientutil.c \
+       silc-channels.c \
+       silc-core.c \
+       silc-nicklist.c \
+       silc-queries.c \
+       silc-servers.c \
+       silc-servers-reconnect.c
+
+noinst_HEADERS = \
+       module.h \
+       client_ops.h \
+       clientutil.h \
+       silc-channels.h \
+       silc-core.h \
+       silc-nicklist.h \
+       silc-queries.h \
+       silc-servers.h
+
diff --git a/apps/irssi/src/silc/core/client_ops.c b/apps/irssi/src/silc/core/client_ops.c
new file mode 100644 (file)
index 0000000..6d45145
--- /dev/null
@@ -0,0 +1,1414 @@
+/*
+
+  client_ops.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "module.h"
+#include "chat-protocols.h"
+#include "args.h"
+
+#include "chatnets.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "silc-nicklist.h"
+
+#include "signals.h"
+#include "levels.h"
+#include "settings.h"
+#include "fe-common/core/printtext.h"
+#include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
+#include "fe-common/silc/module-formats.h"
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               const char *name, SilcSocketType conn_type, 
+                               unsigned char *pk, uint32 pk_len, 
+                               SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context);
+
+void silc_say(SilcClient client, SilcClientConnection conn,
+             SilcClientMessageType type, char *msg, ...)
+{
+  SILC_SERVER_REC *server;
+  va_list va;
+  char *str;
+
+  server = conn == NULL ? NULL : conn->context;
+  
+  va_start(va, msg);
+  str = g_strdup_vprintf(msg, va);
+  printtext(server, NULL, MSGLEVEL_CRAP, "%s", str);
+  g_free(str);
+  va_end(va);
+}
+
+void silc_say_error(char *msg, ...)
+{
+  va_list va;
+  char *str;
+
+  va_start(va, msg);
+  str = g_strdup_vprintf(msg, va);
+  printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", str);
+
+  g_free(str);
+  va_end(va);
+}
+
+/* Message for a channel. The `sender' is the nickname of the sender 
+   received in the packet. The `channel_name' is the name of the channel. */
+
+void silc_channel_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcChannelEntry channel,
+                         SilcMessageFlags flags, char *msg)
+{
+  SILC_SERVER_REC *server;
+  SILC_NICK_REC *nick;
+  SILC_CHANNEL_REC *chanrec;
+  
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!msg)
+    return;
+
+  server = conn == NULL ? NULL : conn->context;
+  chanrec = silc_channel_find_entry(server, channel);
+  if (!chanrec)
+    return;
+  
+  nick = silc_nicklist_find(chanrec, sender);
+  if (!nick) {
+    /* We didn't find client but it clearly exists, add it. */
+    SilcChannelUser chu = silc_client_on_channel(channel, sender);
+    if (chu)
+      nick = silc_nicklist_insert(chanrec, chu, FALSE);
+  }
+
+  if (flags & SILC_MESSAGE_FLAG_ACTION)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_ACTION, 
+                       nick == NULL ? "[<unknown>]" : nick->nick, msg);
+  else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_NOTICES, SILCTXT_CHANNEL_NOTICE, 
+                       nick == NULL ? "[<unknown>]" : nick->nick, msg);
+  else
+    signal_emit("message public", 6, server, msg,
+               nick == NULL ? "[<unknown>]" : nick->nick,
+               nick == NULL ? "" : nick->host == NULL ? "" : nick->host,
+               chanrec->name, nick);
+}
+
+/* Private message to the client. The `sender' is the nickname of the
+   sender received in the packet. */
+
+void silc_private_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcMessageFlags flags,
+                         char *msg)
+{
+  SILC_SERVER_REC *server;
+  char userhost[256];
+  
+  SILC_LOG_DEBUG(("Start"));
+
+  server = conn == NULL ? NULL : conn->context;
+  memset(userhost, 0, sizeof(userhost));
+  if (sender->username)
+    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+            sender->username, sender->hostname);
+  signal_emit("message private", 4, server, msg,
+             sender->nickname ? sender->nickname : "[<unknown>]",
+             sender->username ? userhost : NULL);
+}
+
+/* Notify message to the client. The notify arguments are sent in the
+   same order as servers sends them. The arguments are same as received
+   from the server except for ID's.  If ID is received application receives
+   the corresponding entry to the ID. For example, if Client ID is received
+   application receives SilcClientEntry.  Also, if the notify type is
+   for channel the channel entry is sent to application (even if server
+   does not send it). */
+
+typedef struct {
+  int type;
+  const char *name;
+} NOTIFY_REC;
+
+#define MAX_NOTIFY (sizeof(notifies)/sizeof(notifies[0]))
+static NOTIFY_REC notifies[] = {
+  { SILC_NOTIFY_TYPE_NONE,             NULL },
+  { SILC_NOTIFY_TYPE_INVITE,           "invite" },
+  { SILC_NOTIFY_TYPE_JOIN,             "join" },
+  { SILC_NOTIFY_TYPE_LEAVE,            "leave" },
+  { SILC_NOTIFY_TYPE_SIGNOFF,          "signoff" },
+  { SILC_NOTIFY_TYPE_TOPIC_SET,                "topic" },
+  { SILC_NOTIFY_TYPE_NICK_CHANGE,      "nick" },
+  { SILC_NOTIFY_TYPE_CMODE_CHANGE,     "cmode" },
+  { SILC_NOTIFY_TYPE_CUMODE_CHANGE,    "cumode" },
+  { SILC_NOTIFY_TYPE_MOTD,             "motd" },
+  { SILC_NOTIFY_TYPE_CHANNEL_CHANGE,   "channel_change" },
+  { SILC_NOTIFY_TYPE_SERVER_SIGNOFF,   "server_signoff" },
+  { SILC_NOTIFY_TYPE_KICKED,           "kick" },
+  { SILC_NOTIFY_TYPE_KILLED,           "kill" },
+  { SILC_NOTIFY_TYPE_UMODE_CHANGE,      "umode" },
+  { SILC_NOTIFY_TYPE_BAN,               "ban" },
+};
+
+void silc_notify(SilcClient client, SilcClientConnection conn,
+                SilcNotifyType type, ...)
+{
+  SILC_SERVER_REC *server;
+  va_list va;
+  
+  SILC_LOG_DEBUG(("Start"));
+
+  server = conn == NULL ? NULL : conn->context;
+  va_start(va, type);
+  
+  if (type == SILC_NOTIFY_TYPE_NONE) {
+    /* Some generic notice from server */
+    printtext(server, NULL, MSGLEVEL_CRAP, "%s", (char *)va_arg(va, char *));
+  } else if (type < MAX_NOTIFY) {
+    /* Send signal about the notify event */
+    char signal[50];
+    g_snprintf(signal, sizeof(signal), "silc event %s", notifies[type].name);
+    signal_emit(signal, 2, server, va);
+  } else {
+    /* Unknown notify */
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_UNKNOWN_NOTIFY, type);
+  }
+
+  va_end(va);
+}
+
+/* Called to indicate that connection was either successfully established
+   or connecting failed.  This is also the first time application receives
+   the SilcClientConnection object which it should save somewhere. */
+
+void silc_connect(SilcClient client, SilcClientConnection conn, int success)
+{
+  SILC_SERVER_REC *server = conn->context;
+
+  if (!server && !success) {
+    silc_client_close_connection(client, NULL, conn);
+    return;
+  }
+
+  if (success) {
+    server->connected = TRUE;
+    signal_emit("event connected", 1, server);
+  } else {
+    server->connection_lost = TRUE;
+    if (server->conn)
+      server->conn->context = NULL;
+    server_disconnect(SERVER(server));
+  }
+}
+
+/* Called to indicate that connection was disconnected to the server. */
+
+void silc_disconnect(SilcClient client, SilcClientConnection conn)
+{
+  SILC_SERVER_REC *server = conn->context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (server->conn) {
+    nicklist_rename_unique(SERVER(server),
+                          server->conn->local_entry, server->nick,
+                          server->conn->local_entry, 
+                          silc_client->username);
+    silc_change_nick(server, silc_client->username);
+  }
+
+  server->conn->context = NULL;
+  server->conn = NULL;
+  server->connection_lost = TRUE;
+  server_disconnect(SERVER(server));
+}
+
+/* Command handler. This function is called always in the command function.
+   If error occurs it will be called as well. `conn' is the associated
+   client connection. `cmd_context' is the command context that was
+   originally sent to the command. `success' is FALSE if error occured
+   during command. `command' is the command being processed. It must be
+   noted that this is not reply from server. This is merely called just
+   after application has called the command. Just to tell application
+   that the command really was processed. */
+
+void silc_command(SilcClient client, SilcClientConnection conn, 
+                 SilcClientCommandContext cmd_context, int success,
+                 SilcCommand command)
+{
+  SILC_SERVER_REC *server = conn->context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!success)
+    return;
+
+  switch(command) {
+  case SILC_COMMAND_INVITE:
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_INVITING,
+                      cmd_context->argv[2], 
+                      (cmd_context->argv[1][0] == '*' ?
+                       (char *)conn->current_channel->channel_name :
+                       (char *)cmd_context->argv[1]));
+    break;
+  default:
+    break;
+  }
+}
+
+/* Client info resolving callback when JOIN command reply is received.
+   This will cache all users on the channel. */
+
+static void silc_client_join_get_users(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry *clients,
+                                      uint32 clients_count,
+                                      void *context)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+  SilcHashTableList htl;
+  SilcChannelUser chu;
+  SILC_SERVER_REC *server = conn->context;
+  SILC_CHANNEL_REC *chanrec;
+  SilcClientEntry founder = NULL;
+  NICK_REC *ownnick;
+
+  if (!clients)
+    return;
+
+  chanrec = silc_channel_find(server, channel->channel_name);
+  if (chanrec == NULL)
+    return;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+    if (!chu->client->nickname)
+      continue;
+    if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
+      founder = chu->client;
+    silc_nicklist_insert(chanrec, chu, FALSE);
+  }
+  silc_hash_table_list_reset(&htl);
+
+  ownnick = NICK(silc_nicklist_find(chanrec, conn->local_entry));
+  nicklist_set_own(CHANNEL(chanrec), ownnick);
+  signal_emit("channel joined", 1, chanrec);
+
+  if (chanrec->topic)
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
+                      channel->channel_name, chanrec->topic);
+
+  fe_channels_nicklist(CHANNEL(chanrec), CHANNEL_NICKLIST_FLAG_ALL);
+
+  if (founder) {
+    if (founder == conn->local_entry)
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_FOUNDER_YOU,
+                        channel->channel_name);
+    else
+      printformat_module("fe-common/silc", 
+                        server, channel->channel_name, MSGLEVEL_CRAP,
+                        SILCTXT_CHANNEL_FOUNDER,
+                        channel->channel_name, founder->nickname);
+  }
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  void *entry;
+  SilcIdType id_type;
+  char *fingerprint;
+} *GetkeyContext;
+
+void silc_getkey_cb(bool success, void *context)
+{
+  GetkeyContext getkey = (GetkeyContext)context;
+  char *entity = (getkey->id_type == SILC_ID_CLIENT ? "user" : "server");
+  char *name = (getkey->id_type == SILC_ID_CLIENT ? 
+               ((SilcClientEntry)getkey->entry)->nickname :
+               ((SilcServerEntry)getkey->entry)->server_name);
+
+  if (success) {
+    printformat_module("fe-common/silc", NULL, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_GETKEY_VERIFIED, entity, name);
+  } else {
+    printformat_module("fe-common/silc", NULL, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_GETKEY_DISCARD, entity, name);
+  }
+
+  silc_free(getkey->fingerprint);
+  silc_free(getkey);
+}
+
+/* Command reply handler. This function is called always in the command reply
+   function. If error occurs it will be called as well. Normal scenario
+   is that it will be called after the received command data has been parsed
+   and processed. The function is used to pass the received command data to
+   the application. 
+
+   `conn' is the associated client connection. `cmd_payload' is the command
+   payload data received from server and it can be ignored. It is provided
+   if the application would like to re-parse the received command data,
+   however, it must be noted that the data is parsed already by the library
+   thus the payload can be ignored. `success' is FALSE if error occured.
+   In this case arguments are not sent to the application. `command' is the
+   command reply being processed. The function has variable argument list
+   and each command defines the number and type of arguments it passes to the
+   application (on error they are not sent). */
+
+void 
+silc_command_reply(SilcClient client, SilcClientConnection conn,
+                  SilcCommandPayload cmd_payload, int success,
+                  SilcCommand command, SilcCommandStatus status, ...)
+
+{
+  SILC_SERVER_REC *server = conn->context;
+  SILC_CHANNEL_REC *chanrec;
+  va_list vp;
+
+  va_start(vp, status);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  switch(command) {
+  case SILC_COMMAND_WHOIS:
+    {
+      char buf[1024], *nickname, *username, *realname, *nick;
+      unsigned char *fingerprint;
+      uint32 idle, mode;
+      SilcBuffer channels;
+      SilcClientEntry client_entry;
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
+       /* Print the unknown nick for user */
+       unsigned char *tmp =
+         silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                    3, NULL);
+       if (tmp)
+         silc_say_error("%s: %s", tmp, 
+                        silc_client_command_status_message(status));
+       break;
+      } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       /* Try to find the entry for the unknown client ID, since we
+          might have, and print the nickname of it for user. */
+       uint32 tmp_len;
+       unsigned char *tmp =
+         silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                    2, &tmp_len);
+       if (tmp) {
+         SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+         if (client_id) {
+           client_entry = silc_client_get_client_by_id(client, conn,
+                                                       client_id);
+           if (client_entry && client_entry->nickname)
+             silc_say_error("%s: %s", client_entry->nickname,
+                            silc_client_command_status_message(status));
+           silc_free(client_id);
+         }
+       }
+       break;
+      }
+      
+      if (!success)
+       return;
+      
+      client_entry = va_arg(vp, SilcClientEntry);
+      nickname = va_arg(vp, char *);
+      username = va_arg(vp, char *);
+      realname = va_arg(vp, char *);
+      channels = va_arg(vp, SilcBuffer);
+      mode = va_arg(vp, uint32);
+      idle = va_arg(vp, uint32);
+      fingerprint = va_arg(vp, unsigned char *);
+      
+      silc_parse_userfqdn(nickname, &nick, NULL);
+      printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_WHOIS_USERINFO, nickname, 
+                        client_entry->username, client_entry->hostname,
+                        nick, client_entry->nickname);
+      printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_WHOIS_REALNAME, realname);
+      silc_free(nick);
+
+      if (channels) {
+       SilcDList list = silc_channel_payload_parse_list(channels->data,
+                                                        channels->len);
+       if (list) {
+         SilcChannelPayload entry;
+         memset(buf, 0, sizeof(buf));
+         silc_dlist_start(list);
+         while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+           char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
+           uint32 name_len;
+           char *name = silc_channel_get_name(entry, &name_len);
+           
+           if (m)
+             strncat(buf, m, strlen(m));
+           strncat(buf, name, name_len);
+           strncat(buf, " ", 1);
+           silc_free(m);
+         }
+
+         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                            SILCTXT_WHOIS_CHANNELS, buf);
+         silc_channel_payload_list_free(list);
+       }
+      }
+      
+      if (mode) {
+       memset(buf, 0, sizeof(buf));
+
+       if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
+           (mode & SILC_UMODE_ROUTER_OPERATOR)) {
+         strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
+                "Server Operator " :
+                (mode & SILC_UMODE_ROUTER_OPERATOR) ?
+                "SILC Operator " : "[Unknown mode] ");
+       }
+       if (mode & SILC_UMODE_GONE)
+         strcat(buf, "away");
+
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_WHOIS_MODES, buf);
+      }
+      
+      if (idle && nickname) {
+       memset(buf, 0, sizeof(buf));
+       snprintf(buf, sizeof(buf) - 1, "%lu %s",
+                idle > 60 ? (idle / 60) : idle,
+                idle > 60 ? "minutes" : "seconds");
+
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_WHOIS_IDLE, buf);
+      }
+
+      if (fingerprint) {
+       fingerprint = silc_fingerprint(fingerprint, 20);
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_WHOIS_FINGERPRINT, fingerprint);
+       silc_free(fingerprint);
+      }
+    }
+    break;
+    
+  case SILC_COMMAND_IDENTIFY:
+    {
+      SilcClientEntry client_entry;
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
+       /* Print the unknown nick for user */
+       unsigned char *tmp =
+         silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                    3, NULL);
+       if (tmp)
+         silc_say_error("%s: %s", tmp, 
+                        silc_client_command_status_message(status));
+       break;
+      } else if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       /* Try to find the entry for the unknown client ID, since we
+          might have, and print the nickname of it for user. */
+       uint32 tmp_len;
+       unsigned char *tmp =
+         silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                    2, &tmp_len);
+       if (tmp) {
+         SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+         if (client_id) {
+           client_entry = silc_client_get_client_by_id(client, conn,
+                                                       client_id);
+           if (client_entry && client_entry->nickname)
+             silc_say_error("%s: %s", client_entry->nickname,
+                            silc_client_command_status_message(status));
+           silc_free(client_id);
+         }
+       }
+       break;
+      }
+
+      break;
+    }
+
+  case SILC_COMMAND_WHOWAS:
+    {
+      char *nickname, *username, *realname;
+      
+      if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+         status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+       char *tmp;
+       tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                        3, NULL);
+       if (tmp)
+         silc_say_error("%s: %s", tmp, 
+                        silc_client_command_status_message(status));
+       break;
+      }
+      
+      if (!success)
+       return;
+      
+      (void)va_arg(vp, SilcClientEntry);
+      nickname = va_arg(vp, char *);
+      username = va_arg(vp, char *);
+      realname = va_arg(vp, char *);
+      
+      printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_WHOWAS_USERINFO, nickname, username, 
+                        realname ? realname : "");
+    }
+    break;
+    
+  case SILC_COMMAND_INVITE:
+    {
+      SilcChannelEntry channel;
+      char *invite_list;
+      SilcArgumentPayload args;
+      int argc = 0;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      invite_list = va_arg(vp, char *);
+
+      args = silc_command_get_args(cmd_payload);
+      if (args)
+       argc = silc_argument_get_arg_num(args);
+
+      if (invite_list)
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_CHANNEL_INVITE_LIST, channel->channel_name,
+                          invite_list);
+      else if (argc == 3)
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_CHANNEL_NO_INVITE_LIST, 
+                          channel->channel_name);
+    }
+    break;
+
+  case SILC_COMMAND_JOIN: 
+    {
+      char *channel, *mode, *topic;
+      uint32 modei;
+      SilcChannelEntry channel_entry;
+      SilcBuffer client_id_list;
+      uint32 list_count;
+
+      if (!success)
+       return;
+
+      channel = va_arg(vp, char *);
+      channel_entry = va_arg(vp, SilcChannelEntry);
+      modei = va_arg(vp, uint32);
+      (void)va_arg(vp, uint32);
+      (void)va_arg(vp, unsigned char *);
+      (void)va_arg(vp, unsigned char *);
+      (void)va_arg(vp, unsigned char *);
+      topic = va_arg(vp, char *);
+      (void)va_arg(vp, unsigned char *);
+      list_count = va_arg(vp, uint32);
+      client_id_list = va_arg(vp, SilcBuffer);
+
+      chanrec = silc_channel_find(server, channel);
+      if (!chanrec)
+       chanrec = silc_channel_create(server, channel, TRUE);
+
+      if (topic) {
+       g_free_not_null(chanrec->topic);
+       chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
+       signal_emit("channel topic changed", 1, chanrec);
+      }
+
+      mode = silc_client_chmode(modei, 
+                               channel_entry->channel_key ? 
+                               channel_entry->channel_key->cipher->name : "",
+                               channel_entry->hmac ? 
+                               silc_hmac_get_name(channel_entry->hmac) : "");
+      g_free_not_null(chanrec->mode);
+      chanrec->mode = g_strdup(mode == NULL ? "" : mode);
+      signal_emit("channel mode changed", 1, chanrec);
+
+      /* Resolve the client information */
+      silc_client_get_clients_by_list(client, conn, list_count, client_id_list,
+                                     silc_client_join_get_users, 
+                                     channel_entry);
+
+      break;
+    }
+
+  case SILC_COMMAND_NICK: 
+    {
+      SilcClientEntry client = va_arg(vp, SilcClientEntry);
+      char *old;
+      
+      if (!success)
+       return;
+
+      old = g_strdup(server->nick);
+      server_change_nick(SERVER(server), client->nickname);
+      nicklist_rename_unique(SERVER(server),
+                            server->conn->local_entry, server->nick,
+                            client, client->nickname);
+      
+      signal_emit("message own_nick", 4, server, server->nick, old, "");
+      g_free(old);
+      break;
+    }
+    
+  case SILC_COMMAND_LIST:
+    {
+      char *topic, *name;
+      int usercount;
+      char users[20];
+      
+      if (!success)
+       return;
+      
+      (void)va_arg(vp, SilcChannelEntry);
+      name = va_arg(vp, char *);
+      topic = va_arg(vp, char *);
+      usercount = va_arg(vp, int);
+      
+      if (status == SILC_STATUS_LIST_START ||
+         status == SILC_STATUS_OK)
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_LIST_HEADER);
+
+      if (!usercount)
+       snprintf(users, sizeof(users) - 1, "N/A");
+      else
+       snprintf(users, sizeof(users) - 1, "%d", usercount);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_LIST,
+                        name, users, topic ? topic : "");
+    }
+    break;
+    
+  case SILC_COMMAND_UMODE:
+    {
+      uint32 mode;
+      
+      if (!success)
+       return;
+      
+      mode = va_arg(vp, uint32);
+      
+      if (mode & SILC_UMODE_SERVER_OPERATOR &&
+         !(server->umode & SILC_UMODE_SERVER_OPERATOR))
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
+
+      if (mode & SILC_UMODE_ROUTER_OPERATOR &&
+         !(server->umode & SILC_UMODE_ROUTER_OPERATOR))
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
+
+      server->umode = mode;
+    }
+    break;
+    
+  case SILC_COMMAND_OPER:
+    if (!success)
+      return;
+
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_SERVER_OPER);
+    break;
+    
+  case SILC_COMMAND_SILCOPER:
+    if (!success)
+      return;
+
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_ROUTER_OPER);
+    break;
+    
+  case SILC_COMMAND_USERS: 
+    {
+      SilcHashTableList htl;
+      SilcChannelEntry channel;
+      SilcChannelUser chu;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      
+      printformat_module("fe-common/silc", server, channel->channel_name,
+                        MSGLEVEL_CRAP, SILCTXT_USERS_HEADER,
+                        channel->channel_name);
+
+      silc_hash_table_list(channel->user_list, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+       SilcClientEntry e = chu->client;
+       char stat[5], *mode;
+
+       if (!e->nickname)
+         continue;
+       
+       memset(stat, 0, sizeof(stat));
+       mode = silc_client_chumode_char(chu->mode);
+       if (e->mode & SILC_UMODE_GONE)
+         strcat(stat, "G");
+       else
+         strcat(stat, "H");
+       if (mode)
+         strcat(stat, mode);
+
+       printformat_module("fe-common/silc", server, channel->channel_name,
+                          MSGLEVEL_CRAP, SILCTXT_USERS,
+                          e->nickname, stat, 
+                          e->username ? e->username : "",
+                          e->hostname ? e->hostname : "",
+                          e->realname ? e->realname : "");
+       if (mode)
+         silc_free(mode);
+      }
+      silc_hash_table_list_reset(&htl);
+    }
+    break;
+
+  case SILC_COMMAND_BAN:
+    {
+      SilcChannelEntry channel;
+      char *ban_list;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      ban_list = va_arg(vp, char *);
+      
+      if (ban_list)
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_CHANNEL_BAN_LIST, channel->channel_name,
+                          ban_list);
+      else
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_CHANNEL_NO_BAN_LIST, 
+                          channel->channel_name);
+    }
+    break;
+    
+  case SILC_COMMAND_GETKEY:
+    {
+      SilcIdType id_type;
+      void *entry;
+      SilcPublicKey public_key;
+      unsigned char *pk;
+      uint32 pk_len;
+      GetkeyContext getkey;
+      char *name;
+      
+      if (!success)
+       return;
+      
+      id_type = va_arg(vp, uint32);
+      entry = va_arg(vp, void *);
+      public_key = va_arg(vp, SilcPublicKey);
+
+      if (public_key) {
+       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+
+       getkey = silc_calloc(1, sizeof(*getkey));
+       getkey->entry = entry;
+       getkey->id_type = id_type;
+       getkey->client = client;
+       getkey->conn = conn;
+       getkey->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+       name = (id_type == SILC_ID_CLIENT ? 
+               ((SilcClientEntry)entry)->nickname :
+               ((SilcServerEntry)entry)->server_name);
+
+       silc_verify_public_key_internal(client, conn, name,
+                                       (id_type == SILC_ID_CLIENT ?
+                                        SILC_SOCKET_TYPE_CLIENT :
+                                        SILC_SOCKET_TYPE_SERVER),
+                                       pk, pk_len, SILC_SKE_PK_TYPE_SILC,
+                                       silc_getkey_cb, getkey);
+       silc_free(pk);
+      } else {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_GETKEY_NOKEY);
+      }
+    }
+    break;
+
+  case SILC_COMMAND_INFO:
+    {
+      SilcServerEntry server_entry;
+      char *server_name;
+      char *server_info;
+
+      if (!success)
+       return;
+      
+      server_entry = va_arg(vp, SilcServerEntry);
+      server_name = va_arg(vp, char *);
+      server_info = va_arg(vp, char *);
+
+      if (server_name && server_info )
+       {
+         printtext(server, NULL, MSGLEVEL_CRAP, "Server: %s", server_name);
+         printtext(server, NULL, MSGLEVEL_CRAP, "%s", server_info);
+       }
+    }
+    break;
+    
+  case SILC_COMMAND_TOPIC:
+    {
+      SilcChannelEntry channel;
+      char *topic;
+      
+      if (!success)
+       return;
+      
+      channel = va_arg(vp, SilcChannelEntry);
+      topic = va_arg(vp, char *);
+      
+      if (topic) {
+       chanrec = silc_channel_find_entry(server, channel);
+       if (chanrec) {
+         g_free_not_null(chanrec->topic);
+         chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
+         signal_emit("channel topic changed", 1, chanrec);
+       }
+       printformat_module("fe-common/silc", server, channel->channel_name,
+                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC,
+                          channel->channel_name, topic);
+      } else {
+       printformat_module("fe-common/silc", server, channel->channel_name,
+                          MSGLEVEL_CRAP, SILCTXT_CHANNEL_TOPIC_NOT_SET,
+                          channel->channel_name);
+      }
+    }
+    break;
+
+  }
+
+  va_end(vp);
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  char *filename;
+  char *entity;
+  char *entity_name;
+  unsigned char *pk;
+  uint32 pk_len;
+  SilcSKEPKType pk_type;
+  SilcVerifyPublicKey completion;
+  void *context;
+} *PublicKeyVerify;
+
+static void verify_public_key_completion(const char *line, void *context)
+{
+  PublicKeyVerify verify = (PublicKeyVerify)context;
+
+  if (line[0] == 'Y' || line[0] == 'y') {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(TRUE, verify->context);
+
+    /* Save the key for future checking */
+    silc_pkcs_save_public_key_data(verify->filename, verify->pk, 
+                                  verify->pk_len, SILC_PKCS_FILE_PEM);
+  } else {
+    /* Call the completion */
+    if (verify->completion)
+      verify->completion(FALSE, verify->context);
+
+    printformat_module("fe-common/silc", NULL, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_PUBKEY_DISCARD, 
+                      verify->entity_name ? verify->entity_name :
+                      verify->entity);
+  }
+
+  silc_free(verify->filename);
+  silc_free(verify->entity);
+  silc_free(verify->entity_name);
+  silc_free(verify->pk);
+  silc_free(verify);
+}
+
+/* Internal routine to verify public key. If the `completion' is provided
+   it will be called to indicate whether public was verified or not. For
+   server/router public key this will check for filename that includes the
+   remote host's IP address and remote host's hostname. */
+
+static void 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               const char *name, SilcSocketType conn_type, 
+                               unsigned char *pk, uint32 pk_len, 
+                               SilcSKEPKType pk_type,
+                               SilcVerifyPublicKey completion, void *context)
+{
+  int i;
+  char file[256], filename[256], filename2[256], *ipf, *hostf = NULL;
+  char *fingerprint, *babbleprint, *format;
+  struct passwd *pw;
+  struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
+  PublicKeyVerify verify;
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    printformat_module("fe-common/silc", NULL, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_PUBKEY_UNSUPPORTED, 
+                      entity, pk_type);
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw) {
+    if (completion)
+      completion(FALSE, context);
+    return;
+  }
+
+  memset(filename, 0, sizeof(filename));
+  memset(filename2, 0, sizeof(filename2));
+  memset(file, 0, sizeof(file));
+
+  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+      conn_type == SILC_SOCKET_TYPE_ROUTER) {
+    if (!name) {
+      snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+              conn->sock->ip, conn->sock->port);
+      snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+              pw->pw_dir, entity, file);
+      
+      snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+              conn->sock->hostname, conn->sock->port);
+      snprintf(filename2, sizeof(filename2) - 1, "%s/.silc/%skeys/%s", 
+              pw->pw_dir, entity, file);
+      
+      ipf = filename;
+      hostf = filename2;
+    } else {
+      snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+              name, conn->sock->port);
+      snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+              pw->pw_dir, entity, file);
+      
+      ipf = filename;
+    }
+  } else {
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+    
+    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+    silc_free(fingerprint);
+
+    ipf = filename;
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+  babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->client = client;
+  verify->conn = conn;
+  verify->filename = strdup(ipf);
+  verify->entity = strdup(entity);
+  verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ?
+                        (name ? strdup(name) : strdup(conn->sock->hostname))
+                        : NULL);
+  verify->pk = silc_calloc(pk_len, sizeof(*verify->pk));
+  memcpy(verify->pk, pk, pk_len);
+  verify->pk_len = pk_len;
+  verify->pk_type = pk_type;
+  verify->completion = completion;
+  verify->context = context;
+
+  /* Check whether this key already exists */
+  if (stat(ipf, &st) < 0 && (!hostf || stat(hostf, &st) < 0)) {
+    /* Key does not exist, ask user to verify the key and save it */
+
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                      SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
+                      verify->entity_name : entity);
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                      SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                      SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
+    format = format_get_text("fe-common/silc", NULL, NULL, NULL,
+                            SILCTXT_PUBKEY_ACCEPT);
+    keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                           format, 0, verify);
+    g_free(format);
+    silc_free(fingerprint);
+    return;
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    /* Load the key file, try for both IP filename and hostname filename */
+    if (!silc_pkcs_load_public_key(ipf, &public_key, 
+                                  SILC_PKCS_FILE_PEM) &&
+       !silc_pkcs_load_public_key(ipf, &public_key, 
+                                  SILC_PKCS_FILE_BIN) &&
+       (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, 
+                                              SILC_PKCS_FILE_PEM) &&
+                   !silc_pkcs_load_public_key(hostf, &public_key, 
+                                              SILC_PKCS_FILE_BIN)))) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
+                        verify->entity_name : entity);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_COULD_NOT_LOAD, entity);
+      format = format_get_text("fe-common/silc", NULL, NULL, NULL,
+                              SILCTXT_PUBKEY_ACCEPT_ANYWAY);
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             format, 0, verify);
+      g_free(format);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
+                        verify->entity_name : entity);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_MALFORMED, entity);
+      format = format_get_text("fe-common/silc", NULL, NULL, NULL,
+                              SILCTXT_PUBKEY_ACCEPT_ANYWAY);
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             format, 0, verify);
+      g_free(format);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Compare the keys */
+    if (memcmp(encpk, pk, encpk_len)) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_RECEIVED,verify->entity_name ? 
+                        verify->entity_name : entity);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_FINGERPRINT, entity, fingerprint);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_BABBLEPRINT, babbleprint);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_NO_MATCH, entity);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_MAYBE_EXPIRED, entity);
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_PUBKEY_MITM_ATTACK, entity);
+
+      /* Ask user to verify the key and save it */
+      format = format_get_text("fe-common/silc", NULL, NULL, NULL,
+                              SILCTXT_PUBKEY_ACCEPT_ANYWAY);
+      keyboard_entry_redirect((SIGNAL_FUNC)verify_public_key_completion,
+                             format, 0, verify);
+      g_free(format);
+      silc_free(fingerprint);
+      return;
+    }
+
+    /* Local copy matched */
+    if (completion)
+      completion(TRUE, context);
+    silc_free(fingerprint);
+  }
+}
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the key may be saved as trusted public key for later use. The 
+   `completion' must be called after the public key has been verified. */
+
+void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context)
+{
+  silc_verify_public_key_internal(client, conn, NULL, conn_type, pk,
+                                 pk_len, pk_type,
+                                 completion, context);
+}
+
+/* Asks passphrase from user on the input line. */
+
+typedef struct {
+  SilcAskPassphrase completion;
+  void *context;
+} *AskPassphrase;
+
+void ask_passphrase_completion(const char *passphrase, void *context)
+{
+  AskPassphrase p = (AskPassphrase)context;
+  p->completion((unsigned char *)passphrase, 
+               passphrase ? strlen(passphrase) : 0, p->context);
+  silc_free(p);
+}
+
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context)
+{
+  AskPassphrase p = silc_calloc(1, sizeof(*p));
+  p->completion = completion;
+  p->context = context;
+
+  keyboard_entry_redirect((SIGNAL_FUNC)ask_passphrase_completion,
+                         "Passphrase: ", ENTRY_REDIRECT_FLAG_HIDDEN, p);
+}
+
+typedef struct {
+  SilcGetAuthMeth completion;
+  void *context;
+} *InternalGetAuthMethod;
+
+/* Callback called when we've received the authentication method information
+   from the server after we've requested it. This will get the authentication
+   data from the user if needed. */
+
+static void silc_get_auth_method_callback(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcAuthMethod auth_meth,
+                                         void *context)
+{
+  InternalGetAuthMethod internal = (InternalGetAuthMethod)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  switch (auth_meth) {
+  case SILC_AUTH_NONE:
+    /* No authentication required. */
+    (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+    break;
+  case SILC_AUTH_PASSWORD:
+    /* Do not ask the passphrase from user, the library will ask it if
+       we do not provide it here. */
+    (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+    break;
+  case SILC_AUTH_PUBLIC_KEY:
+    /* Do not get the authentication data now, the library will generate
+       it using our default key, if we do not provide it here. */
+    /* XXX In the future when we support multiple local keys and multiple
+       local certificates we will need to ask from user which one to use. */
+    (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
+    break;
+  }
+
+  silc_free(internal);
+}
+
+/* Find authentication method and authentication data by hostname and
+   port. The hostname may be IP address as well. The found authentication
+   method and authentication data is returned to `auth_meth', `auth_data'
+   and `auth_data_len'. The function returns TRUE if authentication method
+   is found and FALSE if not. `conn' may be NULL. */
+
+void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                         char *hostname, uint16 port,
+                         SilcGetAuthMeth completion, void *context)
+{
+  InternalGetAuthMethod internal;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* XXX must resolve from configuration whether this connection has
+     any specific authentication data */
+
+  /* If we do not have this connection configured by the user in a
+     configuration file then resolve the authentication method from the
+     server for this session. */
+  internal = silc_calloc(1, sizeof(*internal));
+  internal->completion = completion;
+  internal->context = context;
+
+  silc_client_request_authentication_method(client, conn, 
+                                           silc_get_auth_method_callback,
+                                           internal);
+}
+
+/* Notifies application that failure packet was received.  This is called
+   if there is some protocol active in the client.  The `protocol' is the
+   protocol context.  The `failure' is opaque pointer to the failure
+   indication.  Note, that the `failure' is protocol dependant and application
+   must explicitly cast it to correct type.  Usually `failure' is 32 bit
+   failure type (see protocol specs for all protocol failure types). */
+
+void silc_failure(SilcClient client, SilcClientConnection conn, 
+                 SilcProtocol protocol, void *failure)
+{
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+    SilcSKEStatus status = (SilcSKEStatus)failure;
+    
+    if (status == SILC_SKE_STATUS_BAD_VERSION)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_BAD_VERSION);
+    if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_UNSUPPORTED_PUBLIC_KEY);
+    if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_UNKNOWN_GROUP);
+    if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_UNKNOWN_CIPHER);
+    if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_UNKNOWN_PKCS);
+    if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_UNKNOWN_HASH_FUNCTION);
+    if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_UNKNOWN_HMAC);
+    if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_INCORRECT_SIGNATURE);
+    if (status == SILC_SKE_STATUS_INVALID_COOKIE)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_KE_INVALID_COOKIE);
+  }
+
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+    uint32 err = (uint32)failure;
+
+    if (err == SILC_AUTH_FAILED)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP, 
+                        SILCTXT_AUTH_FAILED);
+  }
+}
+
+/* Asks whether the user would like to perform the key agreement protocol.
+   This is called after we have received an key agreement packet or an
+   reply to our key agreement packet. This returns TRUE if the user wants
+   the library to perform the key agreement protocol and FALSE if it is not
+   desired (application may start it later by calling the function
+   silc_client_perform_key_agreement). */
+
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                      SilcClientEntry client_entry, const char *hostname,
+                      uint16 port, SilcKeyAgreementCallback *completion,
+                      void **context)
+{
+  char portstr[12];
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* We will just display the info on the screen and return FALSE and user
+     will have to start the key agreement with a command. */
+
+  if (hostname) 
+    snprintf(portstr, sizeof(portstr) - 1, "%d", port);
+
+  if (!hostname)
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_REQUEST, client_entry->nickname);
+  else
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_REQUEST_HOST, 
+                      client_entry->nickname, hostname, portstr);
+
+  *completion = NULL;
+  *context = NULL;
+
+  return FALSE;
+}
+
+void silc_ftp(SilcClient client, SilcClientConnection conn,
+             SilcClientEntry client_entry, uint32 session_id,
+             const char *hostname, uint16 port)
+{
+  SILC_SERVER_REC *server;
+  char portstr[12];
+  FtpSession ftp = silc_calloc(1, sizeof(*ftp));
+
+  SILC_LOG_DEBUG(("Start"));
+
+  server = conn->context;
+
+  ftp->client_entry = client_entry;
+  ftp->session_id = session_id;
+  ftp->send = FALSE;
+  ftp->conn = conn;
+  silc_dlist_add(server->ftp_sessions, ftp);
+  server->current_session = ftp;
+
+  if (hostname) 
+    snprintf(portstr, sizeof(portstr) - 1, "%d", port);
+
+  if (!hostname)
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_FILE_REQUEST, client_entry->nickname);
+  else
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_FILE_REQUEST_HOST, 
+                      client_entry->nickname, hostname, portstr);
+}
+
+/* SILC client operations */
+SilcClientOperations ops = {
+  silc_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connect,
+  silc_disconnect,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_failure,
+  silc_key_agreement,
+  silc_ftp,
+};
diff --git a/apps/irssi/src/silc/core/client_ops.h b/apps/irssi/src/silc/core/client_ops.h
new file mode 100644 (file)
index 0000000..b7788ed
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+
+  client_ops.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_OPS_H
+#define CLIENT_OPS_H
+
+void silc_say(SilcClient client, SilcClientConnection conn, 
+             SilcClientMessageType type, char *msg, ...);
+void silc_say_error(char *msg, ...);
+void silc_channel_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, 
+                         SilcChannelEntry channel, 
+                         SilcMessageFlags flags, char *msg);
+void silc_private_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, 
+                         SilcMessageFlags flags, char *msg);
+void silc_notify(SilcClient client, SilcClientConnection conn, 
+                SilcNotifyType type, ...);
+void silc_command(SilcClient client, SilcClientConnection conn, 
+                 SilcClientCommandContext cmd_context, int success,
+                 SilcCommand command);
+void silc_command_reply(SilcClient client, SilcClientConnection conn,
+                       SilcCommandPayload cmd_payload, int success,
+                       SilcCommand command, SilcCommandStatus status, ...);
+void silc_connect(SilcClient client, SilcClientConnection conn, int success);
+void silc_disconnect(SilcClient client, SilcClientConnection conn);
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context);
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context);
+void silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                         char *hostname, uint16 port,
+                         SilcGetAuthMeth completion, void *context);
+void silc_failure(SilcClient client, SilcClientConnection conn, 
+                 SilcProtocol protocol, void *failure);
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                      SilcClientEntry client_entry, const char *hostname,
+                      uint16 port, SilcKeyAgreementCallback *completion,
+                      void **context);
+void silc_ftp(SilcClient client, SilcClientConnection conn,
+             SilcClientEntry client_entry, uint32 session_id,
+             const char *hostname, uint16 port);
+
+#endif
diff --git a/apps/irssi/src/silc/core/clientutil.c b/apps/irssi/src/silc/core/clientutil.c
new file mode 100644 (file)
index 0000000..b6480e6
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+
+  client.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "module.h"
+
+#include "net-nonblock.h"
+#include "net-sendbuffer.h"
+#include "signals.h"
+#include "servers.h"
+#include "commands.h"
+#include "levels.h"
+#include "modules.h"
+#include "rawlog.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "channels-setup.h"
+
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "silc-nicklist.h"
+#include "window-item-def.h"
+
+#include "fe-common/core/printtext.h"
+#include "fe-common/core/keyboard.h"
+
+/* Lists supported ciphers */
+
+void silc_client_list_ciphers()
+{
+  char *ciphers = silc_cipher_get_supported();
+  fprintf(stdout, "%s\n", ciphers);
+  silc_free(ciphers);
+}
+
+/* Lists supported hash functions */
+
+void silc_client_list_hash_funcs()
+{
+  char *hash = silc_hash_get_supported();
+  fprintf(stdout, "%s\n", hash);
+  silc_free(hash);
+}
+
+/* Lists supported hash functions */
+
+void silc_client_list_hmacs()
+{
+  char *hash = silc_hmac_get_supported();
+  fprintf(stdout, "%s\n", hash);
+  silc_free(hash);
+}
+
+/* Lists supported PKCS algorithms */
+
+void silc_client_list_pkcs()
+{
+  char *pkcs = silc_pkcs_get_supported();
+  fprintf(stdout, "%s\n", pkcs);
+  silc_free(pkcs);
+}
+
+/* Displays input prompt on command line and takes input data from user */
+
+char *silc_client_get_input(const char *prompt)
+{
+  char input[2048];
+  int fd;
+
+  fd = open("/dev/tty", O_RDONLY);
+  if (fd < 0) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    return NULL;
+  }
+
+  memset(input, 0, sizeof(input));
+
+  printf("%s", prompt);
+  fflush(stdout);
+
+  if ((read(fd, input, sizeof(input))) < 0) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    return NULL;
+  }
+
+  if (strlen(input) <= 1)
+    return NULL;
+
+  if (strchr(input, '\n'))
+    *strchr(input, '\n') = '\0';
+
+  return strdup(input);
+}
+
+/* Returns identifier string for public key generation. */
+
+char *silc_client_create_identifier()
+{
+  char *username = NULL, *realname = NULL;
+  char *hostname, email[256];
+  char *ident;
+  
+  /* Get realname */
+  realname = silc_get_real_name();
+
+  /* Get hostname */
+  hostname = silc_net_localhost();
+  if (!hostname)
+    return NULL;
+
+  /* Get username (mandatory) */
+  username = silc_get_username();
+  if (!username)
+    return NULL;
+
+  /* Create default email address, whether it is right or not */
+  snprintf(email, sizeof(email), "%s@%s", username, hostname);
+
+  ident = silc_pkcs_encode_identifier(username, hostname, realname, email,
+                                     NULL, NULL);
+  if (realname)
+    silc_free(realname);
+  silc_free(hostname);
+  silc_free(username);
+
+  return ident;
+}
+
+/* Creates new public key and private key pair. This is used only
+   when user wants to create new key pair from command line. */
+
+int silc_client_create_key_pair(char *pkcs_name, int bits,
+                               char *public_key, char *private_key,
+                               char *identifier, 
+                               SilcPublicKey *ret_pub_key,
+                               SilcPrivateKey *ret_prv_key)
+{
+  SilcPKCS pkcs;
+  SilcPublicKey pub_key;
+  SilcPrivateKey prv_key;
+  SilcRng rng;
+  unsigned char *key;
+  uint32 key_len;
+  char line[256];
+  char *pkfile = NULL, *prvfile = NULL;
+
+  if (!pkcs_name || !public_key || !private_key)
+    printf("\
+New pair of keys will be created.  Please, answer to following questions.\n\
+");
+
+  if (!pkcs_name) {
+  again_name:
+    pkcs_name = 
+      silc_client_get_input("PKCS name (l to list names) [rsa]: ");
+    if (!pkcs_name)
+      pkcs_name = strdup("rsa");
+
+    if (*pkcs_name == 'l' || *pkcs_name == 'L') {
+      silc_client_list_pkcs();
+      silc_free(pkcs_name);
+      goto again_name;
+    }
+  }
+
+  if (!silc_pkcs_is_supported(pkcs_name)) {
+    fprintf(stderr, "Unknown PKCS `%s'", pkcs_name);
+    return FALSE;
+  }
+
+  if (!bits) {
+    char *length = NULL;
+    length = 
+      silc_client_get_input("Key length in bits [1024]: ");
+    if (!length)
+      bits = 1024;
+    else
+      bits = atoi(length);
+  }
+
+  if (!identifier) {
+    char *def = silc_client_create_identifier();
+
+    memset(line, 0, sizeof(line));
+    if (def)
+      snprintf(line, sizeof(line), "Identifier [%s]: ", def);
+    else
+      snprintf(line, sizeof(line),
+              "Identifier (eg. UN=jon, HN=jon.dummy.com, "
+              "RN=Jon Johnson, E=jon@dummy.com): ");
+
+    while (!identifier) {
+      identifier = silc_client_get_input(line);
+      if (!identifier && def)
+       identifier = strdup(def);
+    }
+
+    if (def)
+      silc_free(def);
+  }
+
+  rng = silc_rng_alloc();
+  silc_rng_init(rng);
+  silc_rng_global_init(rng);
+
+  if (!public_key) {
+    memset(line, 0, sizeof(line));
+    snprintf(line, sizeof(line), "Public key filename [%s] ", 
+            SILC_CLIENT_PUBLIC_KEY_NAME);
+    pkfile = silc_client_get_input(line);
+    if (!pkfile)
+      pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
+  } else {
+    pkfile = public_key;
+  }
+
+  if (!private_key) {
+    memset(line, 0, sizeof(line));
+    snprintf(line, sizeof(line), "Public key filename [%s] ", 
+            SILC_CLIENT_PRIVATE_KEY_NAME);
+    prvfile = silc_client_get_input(line);
+    if (!prvfile)
+      prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
+  } else {
+    prvfile = private_key;
+  }
+
+  /* Generate keys */
+  silc_pkcs_alloc(pkcs_name, &pkcs);
+  pkcs->pkcs->init(pkcs->context, bits, rng);
+
+  /* Save public key into file */
+  key = silc_pkcs_get_public_key(pkcs, &key_len);
+  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
+                                      key, key_len);
+  silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
+  if (ret_pub_key)
+    *ret_pub_key = pub_key;
+
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  /* Save private key into file */
+  key = silc_pkcs_get_private_key(pkcs, &key_len);
+  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
+
+  silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
+  if (ret_prv_key)
+    *ret_prv_key = prv_key;
+
+  printf("Public key has been saved into `%s'.\n", pkfile);
+  printf("Private key has been saved into `%s'.\n", prvfile);
+  printf("Press <Enter> to continue...\n");
+  getchar();
+
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  silc_rng_free(rng);
+  silc_pkcs_free(pkcs);
+
+  return TRUE;
+}
+
+/* This checks stats for various SILC files and directories. First it 
+   checks if ~/.silc directory exist and is owned by the correct user. If 
+   it doesn't exist, it will create the directory. After that it checks if
+   user's Public and Private key files exists and that they aren't expired.
+   If they doesn't exist or they are expired, they will be (re)created
+   after return. */
+
+int silc_client_check_silc_dir()
+{
+  char filename[256], file_public_key[256], file_private_key[256];
+  char servfilename[256], clientfilename[256];
+  char *identifier;
+  struct stat st;
+  struct passwd *pw;
+  int firstime = FALSE;
+  time_t curtime, modtime;
+
+  SILC_LOG_DEBUG(("Checking ~./silc directory"));
+
+  memset(filename, 0, sizeof(filename));
+  memset(file_public_key, 0, sizeof(file_public_key));
+  memset(file_private_key, 0, sizeof(file_private_key));
+
+  identifier = silc_client_create_identifier();
+
+  pw = getpwuid(getuid());
+  if (!pw) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    if (identifier)
+      silc_free(identifier);
+    return FALSE;
+  }
+
+  /* We'll take home path from /etc/passwd file to be sure. */
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
+  snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
+          pw->pw_dir);
+  snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/.silc/clientkeys", 
+          pw->pw_dir);
+
+  /*
+   * Check ~/.silc directory
+   */
+  if ((stat(filename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(filename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", filename);
+         return FALSE;
+       }
+
+       /* Directory was created. First time running SILC */
+       firstime = TRUE;
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               filename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  } else {
+    
+    /* Check the owner of the dir */
+    if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
+      fprintf(stderr, "You don't seem to own `%s' directory\n",
+             filename);
+      return FALSE;
+    }
+    
+#if 0
+    /* Check the permissions of the dir */
+    if ((st.st_mode & 0777) != 0755) {
+      if ((chmod(filename, 0755)) == -1) {
+       fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
+               filename);
+       return FALSE;
+      }
+    }
+#endif
+  }
+
+  /*
+   * Check ~./silc/serverkeys directory
+   */
+  if ((stat(servfilename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(servfilename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
+         return FALSE;
+       }
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               servfilename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+  
+  /*
+   * Check ~./silc/clientkeys directory
+   */
+  if ((stat(clientfilename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(clientfilename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
+         return FALSE;
+       }
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               clientfilename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+  
+  /*
+   * Check Public and Private keys
+   */
+  snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
+          filename, SILC_CLIENT_PUBLIC_KEY_NAME);
+  snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
+          filename, SILC_CLIENT_PRIVATE_KEY_NAME);
+  
+  /* If running SILC first time */
+  if (firstime) {
+    fprintf(stdout, "Running SILC for the first time\n");
+    silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                               SILC_CLIENT_DEF_PKCS_LEN,
+                               file_public_key, file_private_key, 
+                               identifier, NULL, NULL);
+    return TRUE;
+  }
+  
+  if ((stat(file_public_key, &st)) == -1) {
+    /* If file doesn't exist */
+    if (errno == ENOENT) {
+      fprintf(stdout, "Your public key doesn't exist\n");
+      silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                                 SILC_CLIENT_DEF_PKCS_LEN,
+                                 file_public_key, 
+                                 file_private_key, identifier, NULL, NULL);
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+
+  if ((stat(file_private_key, &st)) == -1) {
+    /* If file doesn't exist */
+    if (errno == ENOENT) {
+      fprintf(stdout, "Your private key doesn't exist\n");
+      silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                                 SILC_CLIENT_DEF_PKCS_LEN,
+                                 file_public_key, 
+                                 file_private_key, identifier, NULL, NULL);
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+    
+  /* Check the owner of the public key */
+  if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
+    fprintf(stderr, "You don't seem to own your public key!?\n");
+    return FALSE;
+  }
+  
+  /* Check the owner of the private key */
+  if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
+    fprintf(stderr, "You don't seem to own your private key!?\n");
+    return FALSE;
+  }
+    
+  /* Check the permissions for the private key */
+  if ((st.st_mode & 0777) != 0600) {
+    fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
+           "Trying to change them ... ", file_private_key);
+    if ((chmod(file_private_key, 0600)) == -1) {
+      fprintf(stderr,
+             "Failed to change permissions for private key file!\n" 
+             "Permissions for your private key file must be 0600.\n");
+      return FALSE;
+    }
+    fprintf(stderr, "Done.\n\n");
+  }
+
+  /* See if the key has expired. */
+  modtime = st.st_mtime;       /* last modified */
+  curtime = time(0) - modtime;
+    
+  /* 86400 is seconds in a day. */
+  if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
+    fprintf(stdout, 
+           "--------------------------------------------------\n"
+           "Your private key has expired and needs to be\n" 
+           "recreated.  This will be done automatically now.\n"
+           "Your new key will expire in %d days from today.\n"
+           "--------------------------------------------------\n",
+           SILC_CLIENT_KEY_EXPIRES);
+
+    silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                               SILC_CLIENT_DEF_PKCS_LEN,
+                               file_public_key, 
+                               file_private_key, identifier, NULL, NULL);
+  }
+  
+  if (identifier)
+    silc_free(identifier);
+
+  return TRUE;
+}
+
+/* Loads public and private key from files. */
+
+int silc_client_load_keys(SilcClient client)
+{
+  char filename[256];
+  struct passwd *pw;
+
+  SILC_LOG_DEBUG(("Loading public and private keys"));
+
+  pw = getpwuid(getuid());
+  if (!pw)
+    return FALSE;
+
+  memset(filename, 0, sizeof(filename));
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
+          pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
+
+  if (silc_pkcs_load_private_key(filename, &client->private_key,
+                                SILC_PKCS_FILE_BIN) == FALSE)
+    if (silc_pkcs_load_private_key(filename, &client->private_key,
+                                  SILC_PKCS_FILE_PEM) == FALSE)
+      return FALSE;
+
+  memset(filename, 0, sizeof(filename));
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
+          pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
+
+  if (silc_pkcs_load_public_key(filename, &client->public_key,
+                               SILC_PKCS_FILE_PEM) == FALSE)
+    if (silc_pkcs_load_public_key(filename, &client->public_key,
+                                 SILC_PKCS_FILE_BIN) == FALSE)
+      return FALSE;
+
+  silc_pkcs_alloc(client->public_key->name, &client->pkcs);
+  silc_pkcs_public_key_set(client->pkcs, client->public_key);
+  silc_pkcs_private_key_set(client->pkcs, client->private_key);
+
+  return TRUE;
+}
+
+/* Dumps the public key on screen. Used from the command line option. */
+
+int silc_client_show_key(char *keyfile)
+{
+  SilcPublicKey public_key;
+  SilcPublicKeyIdentifier ident;
+  char *fingerprint, *babbleprint;
+  unsigned char *pk;
+  uint32 pk_len;
+  SilcPKCS pkcs;
+  int key_len = 0;
+
+  if (silc_pkcs_load_public_key(keyfile, &public_key,
+                               SILC_PKCS_FILE_PEM) == FALSE)
+    if (silc_pkcs_load_public_key(keyfile, &public_key,
+                                 SILC_PKCS_FILE_BIN) == FALSE) {
+      fprintf(stderr, "Could not load public key file `%s'\n", keyfile);
+      return FALSE;
+    }
+
+  ident = silc_pkcs_decode_identifier(public_key->identifier);
+
+  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+  babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+  if (silc_pkcs_alloc(public_key->name, &pkcs)) {
+    key_len = silc_pkcs_public_key_set(pkcs, public_key);
+    silc_pkcs_free(pkcs);
+  }
+
+  printf("Public key file    : %s\n", keyfile);
+  printf("Algorithm          : %s\n", public_key->name);
+  if (key_len)
+    printf("Key length (bits)  : %d\n", key_len);
+  if (ident->realname)
+    printf("Real name          : %s\n", ident->realname);
+  if (ident->username)
+    printf("Username           : %s\n", ident->username);
+  if (ident->host)
+    printf("Hostname           : %s\n", ident->host);
+  if (ident->email)
+    printf("Email              : %s\n", ident->email);
+  if (ident->org)
+    printf("Organization       : %s\n", ident->org);
+  if (ident->country)
+    printf("Country            : %s\n", ident->country);
+  printf("Fingerprint (SHA1) : %s\n", fingerprint); 
+  printf("Babbleprint (SHA1) : %s\n", babbleprint); 
+
+  fflush(stdout);
+
+  silc_free(fingerprint);
+  silc_free(babbleprint);
+  silc_free(pk);
+  silc_pkcs_public_key_free(public_key);
+  silc_pkcs_free_identifier(ident);
+
+  return TRUE;
+}
similarity index 51%
rename from lib/silccrypt/loki_internal.h
rename to apps/irssi/src/silc/core/clientutil.h
index 650f6b44e33bd35d6ff08413da3afcf3ac3802f7..efd69da009aeb114480966b3c8bb7450cb6cd2bd 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  loki_internal.h
+  client.h
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
 
 */
 
-#ifndef LOKI_INTERNAL_H
-#define LOKI_INTERNAL_H
+#ifndef CLIENTUTIL_H
+#define CLIENTUTIL_H
 
-/* Cipher's context */
-typedef struct {
-  u4byte l_key[96];
-} LokiContext;
+#include "signals.h"
 
 /* Prototypes */
-u4byte *loki_set_key(LokiContext *ctx,
-                    const u4byte in_key[], const u4byte key_len);
-void loki_encrypt(LokiContext *ctx,
-                 const u4byte in_blk[4], u4byte out_blk[4]);
-void loki_decrypt(LokiContext *ctx,
-                 const u4byte in_blk[4], u4byte out_blk[4]);
+char *silc_client_get_input(const char *prompt);
+void silc_client_list_ciphers();
+void silc_client_list_hash_funcs();
+void silc_client_list_hmacs();
+void silc_client_list_pkcs();
+char *silc_client_create_identifier();
+int silc_client_create_key_pair(char *pkcs_name, int bits,
+                               char *public_key, char *private_key,
+                               char *identifier, 
+                               SilcPublicKey *ret_pub_key,
+                               SilcPrivateKey *ret_prv_key);
+int silc_client_check_silc_dir();
+int silc_client_load_keys(SilcClient client);
+int silc_client_show_key(char *keyfile);
 
 #endif
diff --git a/apps/irssi/src/silc/core/module.h b/apps/irssi/src/silc/core/module.h
new file mode 100644 (file)
index 0000000..b3c19a4
--- /dev/null
@@ -0,0 +1,12 @@
+#include "common.h"
+
+#define MODULE_NAME "silc"
+
+#undef PACKAGE
+#undef VERSION
+#include "silcincludes.h"
+#include "clientlibincludes.h"
+#include "client_ops.h"
+#include "silc-core.h"
+
+#define SILC_PROTOCOL (chat_protocol_lookup("SILC"))
diff --git a/apps/irssi/src/silc/core/silc-channels.c b/apps/irssi/src/silc/core/silc-channels.c
new file mode 100644 (file)
index 0000000..e718e61
--- /dev/null
@@ -0,0 +1,1345 @@
+/*
+  silc-channels.c : irssi
+
+  Copyright (C) 2000 - 2001 Timo Sirainen
+                            Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+  
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+
+#include "net-nonblock.h"
+#include "net-sendbuffer.h"
+#include "signals.h"
+#include "servers.h"
+#include "commands.h"
+#include "levels.h"
+#include "modules.h"
+#include "rawlog.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "channels-setup.h"
+
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "silc-nicklist.h"
+#include "window-item-def.h"
+
+#include "fe-common/core/printtext.h"
+#include "fe-common/silc/module-formats.h"
+
+SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
+                                     const char *name, int automatic)
+{
+  SILC_CHANNEL_REC *rec;
+
+  g_return_val_if_fail(server == NULL || IS_SILC_SERVER(server), NULL);
+  g_return_val_if_fail(name != NULL, NULL);
+
+  rec = g_new0(SILC_CHANNEL_REC, 1);
+  rec->chat_type = SILC_PROTOCOL;
+  rec->name = g_strdup(name);
+  rec->server = server;
+
+  channel_init((CHANNEL_REC *) rec, automatic);
+  return rec;
+}
+
+static void sig_channel_destroyed(SILC_CHANNEL_REC *channel)
+{
+  if (!IS_SILC_CHANNEL(channel))
+    return;
+
+  if (channel->server != NULL && !channel->left && !channel->kicked) {
+    /* destroying channel record without actually
+       having left the channel yet */
+    silc_command_exec(channel->server, "PART", channel->name);
+  }
+}
+
+static void silc_channels_join(SILC_SERVER_REC *server,
+                              const char *channels, int automatic)
+{
+  char **list, **tmp;
+  SILC_CHANNEL_REC *chanrec;
+
+  list = g_strsplit(channels, ",", -1);
+  for (tmp = list; *tmp != NULL; tmp++) {
+    chanrec = silc_channel_find(server, *tmp);
+    if (chanrec)
+      continue;
+
+    silc_command_exec(server, "JOIN", *tmp);
+  }
+
+  g_strfreev(list);
+}
+
+static void sig_connected(SILC_SERVER_REC *server)
+{
+  if (IS_SILC_SERVER(server))
+    server->channels_join = (void *) silc_channels_join;
+}
+
+/* "server quit" signal from the core to indicate that QUIT command
+   was called. */
+
+static void sig_server_quit(SILC_SERVER_REC *server, const char *msg)
+{
+  if (IS_SILC_SERVER(server) && server->conn && server->conn->sock)
+    silc_command_exec(server, "QUIT", msg);
+}
+
+/*
+ * "event join". Joined to a channel.
+ */
+
+SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
+                                         SilcChannelEntry entry)
+{
+  GSList *tmp;
+
+  g_return_val_if_fail(IS_SILC_SERVER(server), NULL);
+
+  for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+    SILC_CHANNEL_REC *rec = tmp->data;
+
+    if (rec->entry == entry)
+      return rec;
+  }
+
+  return NULL;
+}
+
+static void event_join(SILC_SERVER_REC *server, va_list va)
+{
+  SILC_CHANNEL_REC *chanrec;
+  SILC_NICK_REC *nickrec;
+  SilcClientEntry client;
+  SilcChannelEntry channel;
+  char userhost[256];
+
+  client = va_arg(va, SilcClientEntry);
+  channel = va_arg(va, SilcChannelEntry);
+
+  if (client == server->conn->local_entry) {
+    /* You joined to channel */
+    chanrec = silc_channel_find(server, channel->channel_name);
+    if (chanrec != NULL && !chanrec->joined)
+      chanrec->entry = channel;
+  } else {
+    chanrec = silc_channel_find_entry(server, channel);
+    if (chanrec != NULL) {
+      SilcChannelUser chu = silc_client_on_channel(channel, client);
+      if (chu)
+       nickrec = silc_nicklist_insert(chanrec, chu, TRUE);
+    }
+  }
+
+  memset(userhost, 0, sizeof(userhost));
+  if (client->username)
+    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+            client->username, client->hostname);
+  signal_emit("message join", 4, server, channel->channel_name,
+             client->nickname,
+             client->username == NULL ? "" : userhost);
+}
+
+/*
+ * "event leave". Left a channel.
+ */
+
+static void event_leave(SILC_SERVER_REC *server, va_list va)
+{
+  SILC_CHANNEL_REC *chanrec;
+  SILC_NICK_REC *nickrec;
+  SilcClientEntry client;
+  SilcChannelEntry channel;
+  char userhost[256];
+
+  client = va_arg(va, SilcClientEntry);
+  channel = va_arg(va, SilcChannelEntry);
+
+  memset(userhost, 0, sizeof(userhost));
+  if (client->username)
+    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+            client->username, client->hostname);
+  signal_emit("message part", 5, server, channel->channel_name,
+             client->nickname,  client->username ?  userhost : "", 
+             client->nickname);
+
+  chanrec = silc_channel_find_entry(server, channel);
+  if (chanrec != NULL) {
+    nickrec = silc_nicklist_find(chanrec, client);
+    if (nickrec != NULL)
+      nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
+  }
+}
+
+/*
+ * "event signoff". Left the network.
+ */
+
+static void event_signoff(SILC_SERVER_REC *server, va_list va)
+{
+  SilcClientEntry client;
+  GSList *nicks, *tmp;
+  char *message;
+  char userhost[256];
+
+  client = va_arg(va, SilcClientEntry);
+  message = va_arg(va, char *);
+
+  silc_server_free_ftp(server, client);
+
+  memset(userhost, 0, sizeof(userhost));
+  if (client->username)
+    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+            client->username, client->hostname);
+  signal_emit("message quit", 4, server, client->nickname,
+             client->username ? userhost : "", 
+             message ? message : "");
+
+  nicks = nicklist_get_same_unique(SERVER(server), client);
+  for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
+    CHANNEL_REC *channel = tmp->data;
+    NICK_REC *nickrec = tmp->next->data;
+    
+    nicklist_remove(channel, nickrec);
+  }
+}
+
+/*
+ * "event topic". Changed topic.
+ */
+
+static void event_topic(SILC_SERVER_REC *server, va_list va)
+{
+  SILC_CHANNEL_REC *chanrec;
+  void *entry;
+  SilcClientEntry client;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel;
+  char *topic;
+  char userhost[256];
+  SilcIdType idtype;
+
+  idtype = va_arg(va, int);
+  entry = va_arg(va, void *);
+  topic = va_arg(va, char *);
+  channel = va_arg(va, SilcChannelEntry);
+
+  chanrec = silc_channel_find_entry(server, channel);
+  if (chanrec != NULL) {
+    g_free_not_null(chanrec->topic);
+    chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
+    signal_emit("channel topic changed", 1, chanrec);
+  }
+
+  if (idtype == SILC_ID_CLIENT) {
+    client = (SilcClientEntry)entry;
+    memset(userhost, 0, sizeof(userhost));
+    snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+            client->username, client->hostname);
+    signal_emit("message topic", 5, server, channel->channel_name,
+               topic, client->nickname, userhost);
+  } else if (idtype == SILC_ID_SERVER) {
+    server_entry = (SilcServerEntry)entry;
+    signal_emit("message topic", 5, server, channel->channel_name,
+               topic, server_entry->server_name, 
+               server_entry->server_name);
+  } else {
+    channel = (SilcChannelEntry)entry;
+    signal_emit("message topic", 5, server, channel->channel_name,
+               topic, channel->channel_name, channel->channel_name);
+  }
+}
+
+/*
+ * "event invite". Invited or modified invite list.
+ */
+
+static void event_invite(SILC_SERVER_REC *server, va_list va)
+{
+  SilcClientEntry client;
+  SilcChannelEntry channel;
+  char *channel_name;
+  char userhost[256];
+  
+  channel = va_arg(va, SilcChannelEntry);
+  channel_name = va_arg(va, char *);
+  client = va_arg(va, SilcClientEntry);
+
+  memset(userhost, 0, sizeof(userhost));
+  snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+          client->username, client->hostname);
+  signal_emit("message invite", 4, server, channel ? channel->channel_name :
+             channel_name, client->nickname, userhost);
+}
+
+/*
+ * "event nick". Changed nickname.
+ */
+
+static void event_nick(SILC_SERVER_REC *server, va_list va)
+{
+  SilcClientEntry oldclient, newclient;
+  char userhost[256];
+
+  oldclient = va_arg(va, SilcClientEntry);
+  newclient = va_arg(va, SilcClientEntry);
+
+  nicklist_rename_unique(SERVER(server),
+                        oldclient, oldclient->nickname,
+                        newclient, newclient->nickname);
+
+  memset(userhost, 0, sizeof(userhost));
+  snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+          newclient->username, newclient->hostname);
+  signal_emit("message nick", 4, server, newclient->nickname, 
+             oldclient->nickname, userhost);
+}
+
+/*
+ * "event cmode". Changed channel mode.
+ */
+
+static void event_cmode(SILC_SERVER_REC *server, va_list va)
+{
+  SILC_CHANNEL_REC *chanrec;
+  void *entry;
+  SilcClientEntry client;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel;
+  char *mode;
+  uint32 modei;
+  SilcIdType idtype;
+
+  idtype = va_arg(va, int);
+  entry = va_arg(va, void *);
+  modei = va_arg(va, uint32);
+  (void)va_arg(va, char *);
+  (void)va_arg(va, char *);
+  channel = va_arg(va, SilcChannelEntry);
+
+  mode = silc_client_chmode(modei, 
+                           channel->channel_key ? 
+                           channel->channel_key->cipher->name : "",
+                           channel->hmac ? 
+                           silc_hmac_get_name(channel->hmac) : "");
+
+  chanrec = silc_channel_find_entry(server, channel);
+  if (chanrec != NULL) {
+    g_free_not_null(chanrec->mode);
+    chanrec->mode = g_strdup(mode == NULL ? "" : mode);
+    signal_emit("channel mode changed", 1, chanrec);
+  }
+  
+  if (idtype == SILC_ID_CLIENT) {
+    client = (SilcClientEntry)entry;
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
+                      channel->channel_name, mode ? mode : "removed all",
+                      client->nickname);
+  } else {
+    server_entry = (SilcServerEntry)entry;
+    printformat_module("fe-common/silc", server, channel->channel_name,
+                      MSGLEVEL_MODES, SILCTXT_CHANNEL_CMODE,
+                      channel->channel_name, mode ? mode : "removed all",
+                      server_entry->server_name);
+  }
+  
+  g_free(mode);
+}
+
+/*
+ * "event cumode". Changed user's mode on channel.
+ */
+
+static void event_cumode(SILC_SERVER_REC *server, va_list va)
+{
+  SILC_CHANNEL_REC *chanrec;
+  SilcClientEntry client, destclient;
+  SilcChannelEntry channel;
+  int mode;
+  char *modestr;
+  
+  client = va_arg(va, SilcClientEntry);
+  mode = va_arg(va, uint32);
+  destclient = va_arg(va, SilcClientEntry);
+  channel = va_arg(va, SilcChannelEntry);
+  
+  modestr = silc_client_chumode(mode);
+  chanrec = silc_channel_find_entry(server, channel);
+  if (chanrec != NULL) {
+    SILC_NICK_REC *nick;
+    
+    if (destclient == server->conn->local_entry) {
+      chanrec->chanop =
+       (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
+    }
+
+    nick = silc_nicklist_find(chanrec, destclient);
+    if (nick != NULL) {
+      nick->op = (mode & SILC_CHANNEL_UMODE_CHANOP) != 0;
+      nick->founder = (mode & SILC_CHANNEL_UMODE_CHANFO) != 0;
+      signal_emit("nick mode changed", 2, chanrec, nick);
+    }
+  }
+  
+  printformat_module("fe-common/silc", server, channel->channel_name,
+                    MSGLEVEL_MODES, SILCTXT_CHANNEL_CUMODE,
+                    channel->channel_name, destclient->nickname, 
+                    modestr ? modestr : "removed all",
+                    client->nickname);
+
+  if (mode & SILC_CHANNEL_UMODE_CHANFO)
+    printformat_module("fe-common/silc", 
+                      server, channel->channel_name, MSGLEVEL_CRAP,
+                      SILCTXT_CHANNEL_FOUNDER,
+                      channel->channel_name, destclient->nickname);
+
+  g_free(modestr);
+}
+
+/*
+ * "event motd". Received MOTD.
+ */
+
+static void event_motd(SILC_SERVER_REC *server, va_list va)
+{
+  char *text = va_arg(va, char *);
+
+  if (!settings_get_bool("skip_motd"))
+    printtext_multiline(server, NULL, MSGLEVEL_CRAP, "%s", text);
+}
+
+/*
+ * "event channel_change". Channel ID has changed.
+ */
+
+static void event_channel_change(SILC_SERVER_REC *server, va_list va)
+{
+  /* Nothing interesting to do */
+}
+
+/*
+ * "event server_signoff". Server has quit the network.
+ */
+
+static void event_server_signoff(SILC_SERVER_REC *server, va_list va)
+{
+  SilcClientEntry *clients;
+  uint32 clients_count;
+  int i;
+  char userhost[256];
+  
+  (void)va_arg(va, void *);
+  clients = va_arg(va, SilcClientEntry *);
+  clients_count = va_arg(va, uint32);
+  
+  for (i = 0; i < clients_count; i++) {
+    GSList *nicks, *tmp;
+
+    memset(userhost, 0, sizeof(userhost));
+    if (clients[i]->username)
+      snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+              clients[i]->username, clients[i]->hostname);
+    signal_emit("message quit", 4, server, clients[i]->nickname,
+               clients[i]->username ? userhost : "", 
+               "server signoff");
+
+    silc_server_free_ftp(server, clients[i]);
+
+    nicks = nicklist_get_same_unique(SERVER(server), clients[i]);
+    for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
+      CHANNEL_REC *channel = tmp->data;
+      NICK_REC *nickrec = tmp->next->data;
+      nicklist_remove(channel, nickrec);
+    }
+  }
+}
+
+/*
+ * "event kick". Someone was kicked from channel.
+ */
+
+static void event_kick(SILC_SERVER_REC *server, va_list va)
+{
+  SilcClientConnection conn = server->conn;
+  SilcClientEntry client_entry, kicker;
+  SilcChannelEntry channel_entry;
+  char *tmp;
+  SILC_CHANNEL_REC *chanrec;
+
+  client_entry = va_arg(va, SilcClientEntry);
+  tmp = va_arg(va, char *);
+  kicker = va_arg(va, SilcClientEntry);
+  channel_entry = va_arg(va, SilcChannelEntry);
+
+  chanrec = silc_channel_find_entry(server, channel_entry);
+  
+  if (client_entry == conn->local_entry) {
+    printformat_module("fe-common/silc", server, channel_entry->channel_name,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED_YOU, 
+                      kicker->nickname,
+                      channel_entry->channel_name, tmp ? tmp : "");
+    if (chanrec) {
+      chanrec->kicked = TRUE;
+      channel_destroy((CHANNEL_REC *)chanrec);
+    }
+  } else {
+    printformat_module("fe-common/silc", server, channel_entry->channel_name,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_KICKED, 
+                      client_entry->nickname,
+                      kicker->nickname,
+                      channel_entry->channel_name, tmp ? tmp : "");
+
+    if (chanrec) {
+      SILC_NICK_REC *nickrec = silc_nicklist_find(chanrec, client_entry);
+      if (nickrec != NULL)
+       nicklist_remove(CHANNEL(chanrec), NICK(nickrec));
+    }
+  }
+}
+
+/*
+ * "event kill". Someone was killed from the network.
+ */
+
+static void event_kill(SILC_SERVER_REC *server, va_list va)
+{
+  SilcClientConnection conn = server->conn;
+  SilcClientEntry client_entry;
+  char *tmp;
+
+  client_entry = va_arg(va, SilcClientEntry);
+  tmp = va_arg(va, char *);
+  
+  if (client_entry == conn->local_entry) {
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED_YOU, 
+                      tmp ? tmp : "");
+  } else {
+    GSList *nicks, *tmpn;
+    nicks = nicklist_get_same_unique(SERVER(server), client_entry);
+    for (tmpn = nicks; tmpn != NULL; tmpn = tmpn->next->next) {
+      CHANNEL_REC *channel = tmpn->data;
+      NICK_REC *nickrec = tmpn->next->data;
+      nicklist_remove(channel, nickrec);
+    }
+
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_CHANNEL_KILLED, 
+                      client_entry->nickname,
+                      tmp ? tmp : "");
+  }
+}
+
+/* PART (LEAVE) command. */
+
+static void command_part(const char *data, SILC_SERVER_REC *server,
+                        WI_ITEM_REC *item)
+{
+  SILC_CHANNEL_REC *chanrec;
+  char userhost[256];
+  
+  if (!IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  if (!strcmp(data, "*") || *data == '\0') {
+    if (!IS_SILC_CHANNEL(item))
+      cmd_return_error(CMDERR_NOT_JOINED);
+    data = item->name;
+  }
+
+  chanrec = silc_channel_find(server, data);
+  if (chanrec == NULL) 
+    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
+
+  memset(userhost, 0, sizeof(userhost));
+  snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+          server->conn->local_entry->username, 
+          server->conn->local_entry->hostname);
+  signal_emit("message part", 5, server, chanrec->name,
+             server->nick, userhost, "");
+  
+  silc_command_exec(server, "LEAVE", chanrec->name);
+  signal_stop();
+  
+  channel_destroy(CHANNEL(chanrec));
+}
+
+/* ME local command. */
+
+static void command_me(const char *data, SILC_SERVER_REC *server,
+                      WI_ITEM_REC *item)
+{
+  SILC_CHANNEL_REC *chanrec;
+  char *tmpcmd = "ME", *tmp;
+  uint32 argc = 0;
+  unsigned char **argv;
+  uint32 *argv_lens, *argv_types;
+  int i;
+  if (!IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  if (!IS_SILC_CHANNEL(item))
+    cmd_return_error(CMDERR_NOT_JOINED);
+
+  /* Now parse all arguments */
+  tmp = g_strconcat(tmpcmd, " ", data, NULL);
+  silc_parse_command_line(tmp, &argv, &argv_lens,
+                         &argv_types, &argc, 2);
+  g_free(tmp);
+
+  if (argc < 2)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  chanrec = silc_channel_find(server, item->name);
+  if (chanrec == NULL) 
+    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
+
+  /* Send the action message */
+  silc_client_send_channel_message(silc_client, server->conn, 
+                                  chanrec->entry, NULL,
+                                  SILC_MESSAGE_FLAG_ACTION, 
+                                  argv[1], argv_lens[1], TRUE);
+
+  printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
+                    MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
+                     server->conn->local_entry->nickname, argv[1]);
+
+  for (i = 0; i < argc; i++)
+    silc_free(argv[i]);
+  silc_free(argv_lens);
+  silc_free(argv_types);
+}
+
+/* ACTION local command. Same as ME but takes the channel as mandatory
+   argument. */
+
+static void command_action(const char *data, SILC_SERVER_REC *server,
+                          WI_ITEM_REC *item)
+{
+  SILC_CHANNEL_REC *chanrec;
+  char *tmpcmd = "ME", *tmp;
+  uint32 argc = 0;
+  unsigned char **argv;
+  uint32 *argv_lens, *argv_types;
+  int i;
+  if (!IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  if (!IS_SILC_CHANNEL(item))
+    cmd_return_error(CMDERR_NOT_JOINED);
+
+  /* Now parse all arguments */
+  tmp = g_strconcat(tmpcmd, " ", data, NULL);
+  silc_parse_command_line(tmp, &argv, &argv_lens,
+                         &argv_types, &argc, 3);
+  g_free(tmp);
+
+  if (argc < 3)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  chanrec = silc_channel_find(server, argv[1]);
+  if (chanrec == NULL) 
+    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
+
+  /* Send the action message */
+  silc_client_send_channel_message(silc_client, server->conn, 
+                                  chanrec->entry, NULL,
+                                  SILC_MESSAGE_FLAG_ACTION, 
+                                  argv[2], argv_lens[2], TRUE);
+
+  printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
+                    MSGLEVEL_ACTIONS, SILCTXT_CHANNEL_OWNACTION, 
+                     server->conn->local_entry->nickname, argv[2]);
+
+  for (i = 0; i < argc; i++)
+    silc_free(argv[i]);
+  silc_free(argv_lens);
+  silc_free(argv_types);
+}
+
+/* NOTICE local command. */
+
+static void command_notice(const char *data, SILC_SERVER_REC *server,
+                          WI_ITEM_REC *item)
+{
+  SILC_CHANNEL_REC *chanrec;
+  char *tmpcmd = "ME", *tmp;
+  uint32 argc = 0;
+  unsigned char **argv;
+  uint32 *argv_lens, *argv_types;
+  int i;
+  if (!IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  if (!IS_SILC_CHANNEL(item))
+    cmd_return_error(CMDERR_NOT_JOINED);
+
+  /* Now parse all arguments */
+  tmp = g_strconcat(tmpcmd, " ", data, NULL);
+  silc_parse_command_line(tmp, &argv, &argv_lens,
+                         &argv_types, &argc, 2);
+  g_free(tmp);
+
+  if (argc < 2)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  chanrec = silc_channel_find(server, item->name);
+  if (chanrec == NULL) 
+    cmd_return_error(CMDERR_CHAN_NOT_FOUND);
+
+  /* Send the action message */
+  silc_client_send_channel_message(silc_client, server->conn, 
+                                  chanrec->entry, NULL,
+                                  SILC_MESSAGE_FLAG_NOTICE, 
+                                  argv[1], argv_lens[1], TRUE);
+
+  printformat_module("fe-common/silc", server, chanrec->entry->channel_name,
+                    MSGLEVEL_NOTICES, SILCTXT_CHANNEL_OWNNOTICE, 
+                     server->conn->local_entry->nickname, argv[1]);
+
+  for (i = 0; i < argc; i++)
+    silc_free(argv[i]);
+  silc_free(argv_lens);
+  silc_free(argv_types);
+}
+
+/* AWAY local command.  Sends UMODE command that sets the SILC_UMODE_GONE
+   flag. */
+
+static void command_away(const char *data, SILC_SERVER_REC *server,
+                        WI_ITEM_REC *item)
+{
+  bool set;
+
+  if (!IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  if (*data == '\0') {
+    /* Remove any possible away message */
+    silc_client_set_away_message(silc_client, server->conn, NULL);
+    set = FALSE;
+
+    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
+                      SILCTXT_UNSET_AWAY);
+  } else {
+    /* Set the away message */
+    silc_client_set_away_message(silc_client, server->conn, (char *)data);
+    set = TRUE;
+
+    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP, 
+                      SILCTXT_SET_AWAY, data);
+  }
+
+  signal_emit("away mode changed", 1, server);
+
+  silc_command_exec(server, "UMODE", set ? "+g" : "-g");
+}
+
+typedef struct {
+  int type;                    /* 1 = msg, 2 = channel */
+  bool responder;
+  SILC_SERVER_REC *server;
+} *KeyInternal;
+
+/* Key agreement callback that is called after the key agreement protocol
+   has been performed. This is called also if error occured during the
+   key agreement protocol. The `key' is the allocated key material and
+   the caller is responsible of freeing it. The `key' is NULL if error
+   has occured. The application can freely use the `key' to whatever
+   purpose it needs. See lib/silcske/silcske.h for the definition of
+   the SilcSKEKeyMaterial structure. */
+
+static void keyagr_completion(SilcClient client,
+                             SilcClientConnection conn,
+                             SilcClientEntry client_entry,
+                             SilcKeyAgreementStatus status,
+                             SilcSKEKeyMaterial *key,
+                             void *context)
+{
+  KeyInternal i = (KeyInternal)context;
+
+  switch(status) {
+  case SILC_KEY_AGREEMENT_OK:
+    printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_OK, client_entry->nickname);
+
+    if (i->type == 1) {
+      /* Set the private key for this client */
+      silc_client_del_private_message_key(client, conn, client_entry);
+      silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                             NULL, key, i->responder);
+      printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_KEY_AGREEMENT_PRIVMSG, 
+                        client_entry->nickname);
+      silc_ske_free_key_material(key);
+    }
+    
+    break;
+    
+  case SILC_KEY_AGREEMENT_ERROR:
+    printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_ERROR, client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_FAILURE:
+    printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_FAILURE, client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_TIMEOUT:
+    printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_TIMEOUT, client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_ABORTED:
+    printformat_module("fe-common/silc", i->server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_ABORTED, client_entry->nickname);
+    break;
+    
+  default:
+    break;
+  } 
+
+  if (i)
+    silc_free(i);
+}
+
+/* Local command KEY. This command is used to set and unset private
+   keys for channels, set and unset private keys for private messages
+   with remote clients and to send key agreement requests and
+   negotiate the key agreement protocol with remote client.  The
+   key agreement is supported only to negotiate private message keys,
+   it currently cannot be used to negotiate private keys for channels,
+   as it is not convenient for that purpose. */
+
+typedef struct {
+  SILC_SERVER_REC *server;
+  char *data;
+  char *nick;
+  WI_ITEM_REC *item;
+} *KeyGetClients;
+
+/* Callback to be called after client information is resolved from the
+   server. */
+
+static void silc_client_command_key_get_clients(SilcClient client,
+                                               SilcClientConnection conn,
+                                               SilcClientEntry *clients,
+                                               uint32 clients_count,
+                                               void *context)
+{
+  KeyGetClients internal = (KeyGetClients)context;
+
+  if (!clients) {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
+             internal->nick);
+    silc_free(internal->data);
+    silc_free(internal->nick);
+    silc_free(internal);
+    return;
+  }
+
+  signal_emit("command key", 3, internal->data, internal->server,
+             internal->item);
+
+  silc_free(internal->data);
+  silc_free(internal->nick);
+  silc_free(internal);
+}
+
+static void command_key(const char *data, SILC_SERVER_REC *server,
+                       WI_ITEM_REC *item)
+{
+  SilcClientConnection conn;
+  SilcClientEntry *entrys, client_entry = NULL;
+  uint32 entry_count;
+  SilcChannelEntry channel_entry = NULL;
+  char *nickname = NULL, *tmp;
+  int command = 0, port = 0, type = 0;
+  char *hostname = NULL;
+  KeyInternal internal = NULL;
+  uint32 argc = 0;
+  unsigned char **argv;
+  uint32 *argv_lens, *argv_types;
+  char *bindhost = NULL;
+  if (!server || !IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  conn = server->conn;
+
+  /* Now parse all arguments */
+  tmp = g_strconcat("KEY", " ", data, NULL);
+  silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 7);
+  g_free(tmp);
+
+  if (argc < 4)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  /* Get type */
+  if (!strcasecmp(argv[1], "msg"))
+    type = 1;
+  if (!strcasecmp(argv[1], "channel"))
+    type = 2;
+
+  if (type == 0)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  if (type == 1) {
+    if (argv[2][0] == '*') {
+      nickname = strdup("*");
+    } else {
+      /* Parse the typed nickname. */
+      if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
+       return;
+      }
+      
+      /* Find client entry */
+      entrys = silc_client_get_clients_local(silc_client, conn, nickname,
+                                            argv[2], &entry_count);
+      if (!entrys) {
+       KeyGetClients inter = silc_calloc(1, sizeof(*inter));
+       inter->server = server;
+       inter->data = strdup(data);
+       inter->nick = strdup(nickname);
+       inter->item = item;
+       silc_client_get_clients(silc_client, conn, nickname, argv[2],
+                               silc_client_command_key_get_clients, inter);
+       goto out;
+      }
+      client_entry = entrys[0];
+      silc_free(entrys);
+    }
+  }
+
+  if (type == 2) {
+    /* Get channel entry */
+    char *name;
+
+    if (argv[2][0] == '*') {
+      if (!conn->current_channel) {
+       silc_free(nickname);
+       cmd_return_error(CMDERR_NOT_JOINED);
+      }
+      name = conn->current_channel->channel_name;
+    } else {
+      name = argv[2];
+    }
+
+    channel_entry = silc_client_get_channel(silc_client, conn, name);
+    if (!channel_entry) {
+      silc_free(nickname);
+      cmd_return_error(CMDERR_NOT_JOINED);
+    }
+  }
+
+  /* Set command */
+  if (!strcasecmp(argv[3], "set")) {
+    command = 1;
+
+    if (argc >= 5) {
+      if (type == 1 && client_entry) {
+       /* Set private message key */
+       
+       silc_client_del_private_message_key(silc_client, conn, client_entry);
+
+       if (argc >= 6)
+         silc_client_add_private_message_key(silc_client, conn, client_entry,
+                                             argv[5], argv[4],
+                                             argv_lens[4],
+                                             (argv[4][0] == '*' ?
+                                              TRUE : FALSE), FALSE);
+       else
+         silc_client_add_private_message_key(silc_client, conn, client_entry,
+                                             NULL, argv[4],
+                                             argv_lens[4],
+                                             (argv[4][0] == '*' ?
+                                              TRUE : FALSE), FALSE);
+
+       /* Send the key to the remote client so that it starts using it
+          too. */
+       silc_client_send_private_message_key(silc_client, conn, 
+                                            client_entry, TRUE);
+      } else if (type == 2) {
+       /* Set private channel key */
+       char *cipher = NULL, *hmac = NULL;
+
+       if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                            SILCTXT_CH_PRIVATE_KEY_NOMODE, 
+                            channel_entry->channel_name);
+         goto out;
+       }
+
+       if (argc >= 6)
+         cipher = argv[5];
+       if (argc >= 7)
+         hmac = argv[6];
+
+       if (!silc_client_add_channel_private_key(silc_client, conn, 
+                                                channel_entry,
+                                                cipher, hmac,
+                                                argv[4],
+                                                argv_lens[4])) {
+         printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                            SILCTXT_CH_PRIVATE_KEY_ERROR, 
+                            channel_entry->channel_name);
+         goto out;
+       }
+
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_CH_PRIVATE_KEY_ADD, 
+                          channel_entry->channel_name);
+      }
+    }
+
+    goto out;
+  }
+  
+  /* Unset command */
+  if (!strcasecmp(argv[3], "unset")) {
+    command = 2;
+
+    if (type == 1 && client_entry) {
+      /* Unset private message key */
+      silc_client_del_private_message_key(silc_client, conn, client_entry);
+    } else if (type == 2) {
+      /* Unset channel key(s) */
+      SilcChannelPrivateKey *keys;
+      uint32 keys_count;
+      int number;
+
+      if (argc == 4)
+       silc_client_del_channel_private_keys(silc_client, conn, 
+                                            channel_entry);
+
+      if (argc > 4) {
+       number = atoi(argv[4]);
+       keys = silc_client_list_channel_private_keys(silc_client, conn, 
+                                                    channel_entry,
+                                                    &keys_count);
+       if (!keys)
+         goto out;
+
+       if (!number || number > keys_count) {
+         silc_client_free_channel_private_keys(keys, keys_count);
+         goto out;
+       }
+
+       silc_client_del_channel_private_key(silc_client, conn, channel_entry,
+                                           keys[number - 1]);
+       silc_client_free_channel_private_keys(keys, keys_count);
+      }
+
+      goto out;
+    }
+  }
+
+  /* List command */
+  if (!strcasecmp(argv[3], "list")) {
+    command = 3;
+
+    if (type == 1) {
+      SilcPrivateMessageKeys keys;
+      uint32 keys_count;
+      int k, i, len;
+      char buf[1024];
+
+      keys = silc_client_list_private_message_keys(silc_client, conn, 
+                                                  &keys_count);
+      if (!keys)
+       goto out;
+
+      /* list the private message key(s) */
+      if (nickname[0] == '*') {
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_PRIVATE_KEY_LIST);
+       for (k = 0; k < keys_count; k++) {
+         memset(buf, 0, sizeof(buf));
+         strncat(buf, "  ", 2);
+         len = strlen(keys[k].client_entry->nickname);
+         strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+         if (len < 30)
+           for (i = 0; i < 30 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+         
+         len = strlen(keys[k].cipher);
+         strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+         if (len < 14)
+           for (i = 0; i < 14 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+
+         if (keys[k].key)
+           strcat(buf, "<hidden>");
+         else
+           strcat(buf, "*generated*");
+
+         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
+       }
+      } else {
+       printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_PRIVATE_KEY_LIST_NICK,
+                          client_entry->nickname);
+       for (k = 0; k < keys_count; k++) {
+         if (keys[k].client_entry != client_entry)
+           continue;
+
+         memset(buf, 0, sizeof(buf));
+         strncat(buf, "  ", 2);
+         len = strlen(keys[k].client_entry->nickname);
+         strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+         if (len < 30)
+           for (i = 0; i < 30 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+         
+         len = strlen(keys[k].cipher);
+         strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+         if (len < 14)
+           for (i = 0; i < 14 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+
+         if (keys[k].key)
+           strcat(buf, "<hidden>");
+         else
+           strcat(buf, "*generated*");
+
+         silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
+       }
+      }
+
+      silc_client_free_private_message_keys(keys, keys_count);
+
+    } else if (type == 2) {
+      SilcChannelPrivateKey *keys;
+      uint32 keys_count;
+      int k, i, len;
+      char buf[1024];
+
+      keys = silc_client_list_channel_private_keys(silc_client, conn, 
+                                                  channel_entry,
+                                                  &keys_count);
+
+      printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_CH_PRIVATE_KEY_LIST,
+                        channel_entry->channel_name);
+
+      if (!keys)
+       goto out;
+      
+      for (k = 0; k < keys_count; k++) {
+       memset(buf, 0, sizeof(buf));
+       strncat(buf, "  ", 2);
+
+       len = strlen(keys[k]->cipher->cipher->name);
+       strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
+       if (len < 16)
+         for (i = 0; i < 16 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+       
+       len = strlen(silc_hmac_get_name(keys[k]->hmac));
+       strncat(buf, silc_hmac_get_name(keys[k]->hmac), len > 16 ? 16 : len);
+       if (len < 16)
+         for (i = 0; i < 16 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+       
+       strcat(buf, "<hidden>");
+
+       silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", buf);
+      }
+      
+      silc_client_free_channel_private_keys(keys, keys_count);
+    }
+
+    goto out;
+  }
+
+  /* Send command is used to send key agreement */
+  if (!strcasecmp(argv[3], "agreement")) {
+    command = 4;
+
+    if (argc >= 5)
+      hostname = argv[4];
+    if (argc >= 6)
+      port = atoi(argv[5]);
+
+    internal = silc_calloc(1, sizeof(*internal));
+    internal->type = type;
+    internal->server = server;
+    
+    if (!hostname) {
+      if (settings_get_bool("use_auto_addr")) {
+       
+        hostname = (char *)settings_get_str("auto_public_ip");
+
+/* If the hostname isn't set, treat this case as if auto_public_ip wasn't
+ * set.
+ */
+        if ((hostname) && (*hostname == '\0')) {
+           hostname = NULL;
+        }
+        else {
+          bindhost = (char *)settings_get_str("auto_bind_ip");
+            
+/* if the bind_ip isn't set, but the public_ip IS, then assume then
+ * public_ip is the same value as the bind_ip.
+ */
+          if ((bindhost) && (*bindhost == '\0')) {
+            bindhost = hostname;
+          }
+           port = settings_get_int("auto_bind_port");
+        }
+      }  /* if use_auto_addr */
+    }
+  }
+
+  /* Start command is used to start key agreement (after receiving the
+     key_agreement client operation). */
+  if (!strcasecmp(argv[3], "negotiate")) {
+    command = 5;
+
+    if (argc >= 5)
+      hostname = argv[4];
+    if (argc >= 6)
+      port = atoi(argv[5]);
+
+    internal = silc_calloc(1, sizeof(*internal));
+    internal->type = type;
+    internal->server = server;
+  }
+
+  if (command == 0) {
+    silc_say(silc_client, conn, SILC_CLIENT_MESSAGE_INFO,
+            "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  if (command == 4 && client_entry) {
+    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT, argv[2]);
+    internal->responder = TRUE;
+    silc_client_send_key_agreement(silc_client, conn, client_entry, hostname, 
+                                  bindhost, port, 120, keyagr_completion, 
+                                  internal);
+    if (!hostname)
+      silc_free(internal);
+    goto out;
+  }
+
+  if (command == 5 && client_entry && hostname) {
+    printformat_module("fe-common/silc", server, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_KEY_AGREEMENT_NEGOTIATE, argv[2]);
+    internal->responder = FALSE;
+    silc_client_perform_key_agreement(silc_client, conn, client_entry, 
+                                     hostname, port, keyagr_completion, 
+                                     internal);
+    goto out;
+  }
+
+ out:
+  silc_free(nickname);
+}
+
+/* Lists locally saved client and server public keys. */
+
+static void command_listkeys(const char *data, SILC_SERVER_REC *server,
+                            WI_ITEM_REC *item)
+{
+
+}
+
+void silc_channels_init(void)
+{
+  signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+  signal_add("server connected", (SIGNAL_FUNC) sig_connected);
+  signal_add("server quit", (SIGNAL_FUNC) sig_server_quit);
+
+  signal_add("silc event join", (SIGNAL_FUNC) event_join);
+  signal_add("silc event leave", (SIGNAL_FUNC) event_leave);
+  signal_add("silc event signoff", (SIGNAL_FUNC) event_signoff);
+  signal_add("silc event topic", (SIGNAL_FUNC) event_topic);
+  signal_add("silc event invite", (SIGNAL_FUNC) event_invite);
+  signal_add("silc event nick", (SIGNAL_FUNC) event_nick);
+  signal_add("silc event cmode", (SIGNAL_FUNC) event_cmode);
+  signal_add("silc event cumode", (SIGNAL_FUNC) event_cumode);
+  signal_add("silc event motd", (SIGNAL_FUNC) event_motd);
+  signal_add("silc event channel_change", (SIGNAL_FUNC) event_channel_change);
+  signal_add("silc event server_signoff", (SIGNAL_FUNC) event_server_signoff);
+  signal_add("silc event kick", (SIGNAL_FUNC) event_kick);
+  signal_add("silc event kill", (SIGNAL_FUNC) event_kill);
+  
+  command_bind("part", MODULE_NAME, (SIGNAL_FUNC) command_part);
+  command_bind("me", MODULE_NAME, (SIGNAL_FUNC) command_me);
+  command_bind("action", MODULE_NAME, (SIGNAL_FUNC) command_action);
+  command_bind("notice", MODULE_NAME, (SIGNAL_FUNC) command_notice);
+  command_bind("away", MODULE_NAME, (SIGNAL_FUNC) command_away);
+  command_bind("key", MODULE_NAME, (SIGNAL_FUNC) command_key);
+  command_bind("listkeys", MODULE_NAME, (SIGNAL_FUNC) command_listkeys);
+
+  silc_nicklist_init();
+}
+
+void silc_channels_deinit(void)
+{
+  signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
+  signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
+  signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
+
+  signal_remove("silc event join", (SIGNAL_FUNC) event_join);
+  signal_remove("silc event leave", (SIGNAL_FUNC) event_leave);
+  signal_remove("silc event signoff", (SIGNAL_FUNC) event_signoff);
+  signal_remove("silc event topic", (SIGNAL_FUNC) event_topic);
+  signal_remove("silc event invite", (SIGNAL_FUNC) event_invite);
+  signal_remove("silc event nick", (SIGNAL_FUNC) event_nick);
+  signal_remove("silc event cmode", (SIGNAL_FUNC) event_cmode);
+  signal_remove("silc event cumode", (SIGNAL_FUNC) event_cumode);
+  signal_remove("silc event motd", (SIGNAL_FUNC) event_motd);
+  signal_remove("silc event channel_change", 
+               (SIGNAL_FUNC) event_channel_change);
+  signal_remove("silc event server_signoff", 
+               (SIGNAL_FUNC) event_server_signoff);
+  signal_remove("silc event kick", (SIGNAL_FUNC) event_kick);
+  signal_remove("silc event kill", (SIGNAL_FUNC) event_kill);
+  
+  command_unbind("part", (SIGNAL_FUNC) command_part);
+  command_unbind("me", (SIGNAL_FUNC) command_me);
+  command_unbind("action", (SIGNAL_FUNC) command_action);
+  command_unbind("notice", (SIGNAL_FUNC) command_notice);
+  command_unbind("away", (SIGNAL_FUNC) command_away);
+  command_unbind("key", (SIGNAL_FUNC) command_key);
+  command_unbind("listkeys", (SIGNAL_FUNC) command_listkeys);
+
+  silc_nicklist_deinit();
+}
diff --git a/apps/irssi/src/silc/core/silc-channels.h b/apps/irssi/src/silc/core/silc-channels.h
new file mode 100644 (file)
index 0000000..8a286b7
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __SILC_CHANNELS_H
+#define __SILC_CHANNELS_H
+
+#include "chat-protocols.h"
+#include "channels.h"
+#include "silc-servers.h"
+
+/* Returns SILC_CHANNEL_REC if it's SILC channel, NULL if it isn't. */
+#define SILC_CHANNEL(channel) \
+       PROTO_CHECK_CAST(CHANNEL(channel), SILC_CHANNEL_REC, chat_type, "SILC")
+#define IS_SILC_CHANNEL(channel) \
+       (SILC_CHANNEL(channel) ? TRUE : FALSE)
+#define silc_channel_find(server, name) \
+       SILC_CHANNEL(channel_find(SERVER(server), name))
+
+#define STRUCT_SERVER_REC SILC_SERVER_REC
+typedef struct {
+#include "channel-rec.h"
+  GSList *banlist;             /* list of bans */
+  GSList *ebanlist;            /* list of ban exceptions */
+  GSList *invitelist;          /* invite list */
+  SilcChannelEntry entry;
+} SILC_CHANNEL_REC;
+
+void silc_channels_init(void);
+void silc_channels_deinit(void);
+
+/* Create new SILC channel record */
+SILC_CHANNEL_REC *silc_channel_create(SILC_SERVER_REC *server,
+                                     const char *name, int automatic);
+SILC_CHANNEL_REC *silc_channel_find_entry(SILC_SERVER_REC *server,
+                                         SilcChannelEntry entry);
+
+#endif
diff --git a/apps/irssi/src/silc/core/silc-core.c b/apps/irssi/src/silc/core/silc-core.c
new file mode 100644 (file)
index 0000000..a9c4137
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+
+  silc-core.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "module.h"
+#include "chat-protocols.h"
+#include "args.h"
+
+#include "chatnets.h"
+#include "servers-setup.h"
+#include "channels-setup.h"
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "silc-nicklist.h"
+#include "version_internal.h"
+#include "version.h"
+
+#include "signals.h"
+#include "levels.h"
+#include "settings.h"
+#include "fe-common/core/printtext.h"
+#include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
+#include "fe-common/silc/module-formats.h"
+
+/* Command line option variables */
+static bool opt_create_keypair = FALSE;
+static bool opt_list_ciphers = FALSE;
+static bool opt_list_hash = FALSE;
+static bool opt_list_hmac = FALSE;
+static bool opt_list_pkcs = FALSE;
+static bool opt_version = FALSE;
+static char *opt_debug = FALSE;
+static char *opt_pkcs = NULL;
+static char *opt_keyfile = NULL;
+static int opt_bits = 0;
+
+static int idletag;
+
+SilcClient silc_client = NULL;
+extern SilcClientOperations ops;
+extern bool silc_debug;
+extern bool silc_debug_hexdump;
+#ifdef SILC_SIM
+/* SIM (SILC Module) table */
+SilcSimContext **sims = NULL;
+uint32 sims_count = 0;
+#endif
+
+static int my_silc_scheduler(void)
+{
+  silc_client_run_one(silc_client);
+  return 1;
+}
+
+static CHATNET_REC *create_chatnet(void)
+{
+  return g_malloc0(sizeof(CHATNET_REC));
+}
+
+static SERVER_SETUP_REC *create_server_setup(void)
+{
+  return g_malloc0(sizeof(SERVER_SETUP_REC));
+}
+
+static CHANNEL_SETUP_REC *create_channel_setup(void)
+{
+  return g_malloc0(sizeof(CHANNEL_SETUP_REC));
+}
+
+static SERVER_CONNECT_REC *create_server_connect(void)
+{
+  return g_malloc0(sizeof(SILC_SERVER_CONNECT_REC));
+}
+
+static void destroy_server_connect(SERVER_CONNECT_REC *conn)
+{
+
+}
+
+/* Checks user information and saves them to the config file it they
+   do not exist there already. */
+
+static void silc_init_userinfo(void)
+{
+  const char *set, *nick, *user_name;
+  char *str;   
+        
+  /* check if nick/username/realname wasn't read from setup.. */
+  set = settings_get_str("real_name");
+  if (set == NULL || *set == '\0') {
+    str = g_getenv("SILCNAME");
+    if (!str)
+      str = g_getenv("IRCNAME");
+    settings_set_str("real_name",
+                    str != NULL ? str : g_get_real_name());
+  }
+  /* username */
+  user_name = settings_get_str("user_name");
+  if (user_name == NULL || *user_name == '\0') {
+    str = g_getenv("SILCUSER");
+    if (!str)
+      str = g_getenv("IRCUSER");
+    settings_set_str("user_name",
+                    str != NULL ? str : g_get_user_name());
+    
+    user_name = settings_get_str("user_name");
+  }
+
+  /* nick */
+  nick = settings_get_str("nick");
+  if (nick == NULL || *nick == '\0') {
+    str = g_getenv("SILCNICK");
+    if (!str)
+      str = g_getenv("IRCNICK");
+    settings_set_str("nick", str != NULL ? str : user_name);
+    
+    nick = settings_get_str("nick");
+  }
+                
+  /* alternate nick */
+  set = settings_get_str("alternate_nick");
+  if (set == NULL || *set == '\0') {
+    if (strlen(nick) < 9)
+      str = g_strconcat(nick, "_", NULL);
+    else { 
+      str = g_strdup(nick);
+      str[strlen(str)-1] = '_';
+    }
+    settings_set_str("alternate_nick", str);
+    g_free(str);
+  }
+
+  /* host name */
+  set = settings_get_str("hostname");
+  if (set == NULL || *set == '\0') {
+    str = g_getenv("SILCHOST");
+    if (!str)
+      str = g_getenv("IRCHOST");
+    if (str != NULL)
+      settings_set_str("hostname", str);
+  }
+}
+
+/* Log callbacks */
+
+static bool silc_log_misc(SilcLogType type, char *message, void *context)
+{
+  fprintf(stderr, "%s\n", message);
+  return TRUE;
+}
+
+static void silc_nickname_format_parse(const char *nickname,
+                                      char **ret_nickname)
+{
+  silc_parse_userfqdn(nickname, ret_nickname, NULL);
+}
+
+static void silc_register_cipher(SilcClient client, const char *cipher)
+{
+  int i;
+
+  if (cipher) {
+    for (i = 0; silc_default_ciphers[i].name; i++)
+      if (!strcmp(silc_default_ciphers[i].name, cipher)) {
+       silc_cipher_register(&silc_default_ciphers[i]);
+       break;
+      }
+    
+    if (!silc_cipher_is_supported(cipher)) {
+      SILC_LOG_ERROR(("Unknown cipher `%s'", cipher));
+      exit(1);
+    }
+  }
+
+  /* Register other defaults */
+  silc_cipher_register_default();
+}
+
+static void silc_register_hash(SilcClient client, const char *hash)
+{
+  int i;
+
+  if (hash) {
+    for (i = 0; silc_default_hash[i].name; i++)
+      if (!strcmp(silc_default_hash[i].name, hash)) {
+       silc_hash_register(&silc_default_hash[i]);
+       break;
+      }
+    
+    if (!silc_hash_is_supported(hash)) {
+      SILC_LOG_ERROR(("Unknown hash function `%s'", hash));
+      exit(1);
+    }
+  }
+
+  /* Register other defaults */
+  silc_hash_register_default();
+}
+
+static void silc_register_hmac(SilcClient client, const char *hmac)
+{
+  int i;
+
+  if (hmac) {
+    for (i = 0; silc_default_hmacs[i].name; i++)
+      if (!strcmp(silc_default_hmacs[i].name, hmac)) {
+       silc_hmac_register(&silc_default_hmacs[i]);
+       break;
+      }
+    
+    if (!silc_hmac_is_supported(hmac)) {
+      SILC_LOG_ERROR(("Unknown HMAC `%s'", hmac));
+      exit(1);
+    }
+  }
+
+  /* Register other defaults */
+  silc_hmac_register_default();
+}
+
+/* Finalize init. Init finish signal calls this. */
+
+void silc_core_init_finish(SERVER_REC *server)
+{
+  CHAT_PROTOCOL_REC *rec;
+  SilcClientParams params;
+  const char *def_cipher, *def_hash, *def_hmac;
+
+  if (opt_create_keypair == TRUE) {
+    /* Create new key pair and exit */
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+    silc_client_create_key_pair(opt_pkcs, opt_bits, 
+                               NULL, NULL, NULL, NULL, NULL);
+    exit(0);
+  }
+
+  if (opt_keyfile) {
+    /* Dump the key */
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+    silc_client_show_key(opt_keyfile);
+    exit(0);
+  }
+
+  if (opt_list_ciphers) {
+    silc_cipher_register_default();
+    silc_client_list_ciphers();
+    exit(0);
+  }
+
+  if (opt_list_hash) {
+    silc_hash_register_default();
+    silc_client_list_hash_funcs();
+    exit(0);
+  }
+
+  if (opt_list_hmac) {
+    silc_hmac_register_default();
+    silc_client_list_hmacs();
+    exit(0);
+  }
+
+  if (opt_list_pkcs) {
+    silc_pkcs_register_default();
+    silc_client_list_pkcs();
+    exit(0);
+  }
+
+  if (opt_version) {
+    printf("SILC Secure Internet Live Conferencing, version %s "
+          "(base: SILC Toolkit %s)\n", silc_dist_version, silc_version);
+    printf("(c) 1997 - 2001 Pekka Riikonen <priikone@silcnet.org>\n");
+    exit(0); 
+  }
+
+  if (opt_debug) {
+    silc_debug = TRUE;
+    silc_debug_hexdump = TRUE;
+    silc_log_set_debug_string(opt_debug);
+    silc_log_set_callback(SILC_LOG_INFO, silc_log_misc, NULL);
+    silc_log_set_callback(SILC_LOG_WARNING, silc_log_misc, NULL);
+    silc_log_set_callback(SILC_LOG_ERROR, silc_log_misc, NULL);
+    silc_log_set_callback(SILC_LOG_FATAL, silc_log_misc, NULL);
+#ifndef SILC_DEBUG
+    fprintf(stdout, 
+           "Run-time debugging is not enabled. To enable it recompile\n"
+           "the client with --enable-debug configuration option.\n");
+#endif
+  }
+
+  /* Do some irssi initializing */
+  settings_add_bool("server", "skip_motd", FALSE);
+  settings_add_str("server", "alternate_nick", NULL);
+  
+  /* Initialize the auto_addr variables Is "server" the best choice for
+   * this?  No existing category seems to apply.
+   */
+  settings_add_bool("server", "use_auto_addr", FALSE);
+  settings_add_str("server", "auto_bind_ip", "");
+  settings_add_str("server", "auto_public_ip", "");
+  settings_add_int("server", "auto_bind_port", 0);
+                               
+  silc_init_userinfo();
+
+  /* Initialize client parameters */
+  memset(&params, 0, sizeof(params));
+  strcat(params.nickname_format, "%n@%h%a");
+  params.nickname_parse = silc_nickname_format_parse;
+
+  /* Allocate SILC client */
+  silc_client = silc_client_alloc(&ops, &params, NULL, silc_version_string);
+
+  /* Crypto settings */
+  settings_add_str("server", "crypto_default_cipher", SILC_DEFAULT_CIPHER);
+  settings_add_str("server", "crypto_default_hash", SILC_DEFAULT_HASH);
+  settings_add_str("server", "crypto_default_hmac", SILC_DEFAULT_HMAC);
+
+  /* Get the ciphers and stuff from config file */
+  def_cipher = settings_get_str("crypto_default_cipher");
+  def_hash = settings_get_str("crypto_default_hash");
+  def_hmac = settings_get_str("crypto_default_hmac");
+  silc_register_cipher(silc_client, def_cipher);
+  silc_register_hash(silc_client, def_hash);
+  silc_register_hmac(silc_client, def_hmac);
+  silc_pkcs_register_default();
+
+  /* Get user information */
+  silc_client->username = g_strdup(settings_get_str("user_name"));
+  silc_client->nickname = g_strdup(settings_get_str("nick"));
+  silc_client->hostname = silc_net_localhost();
+  silc_client->realname = g_strdup(settings_get_str("real_name"));
+
+  /* Check ~/.silc directory and public and private keys */
+  if (silc_client_check_silc_dir() == FALSE) {
+    idletag = -1;
+    return;
+  }
+
+  /* Load public and private key */
+  if (silc_client_load_keys(silc_client) == FALSE) {
+    idletag = -1;
+    return;
+  }
+
+  /* Initialize the SILC client */
+  if (!silc_client_init(silc_client)) {
+    idletag = -1;
+    return;
+  }
+
+  /* Register SILC to the irssi */
+  rec = g_new0(CHAT_PROTOCOL_REC, 1);
+  rec->name = "SILC";
+  rec->fullname = "Secure Internet Live Conferencing";
+  rec->chatnet = "silcnet";
+  rec->create_chatnet = create_chatnet;
+  rec->create_server_setup = create_server_setup;
+  rec->create_channel_setup = create_channel_setup;
+  rec->create_server_connect = create_server_connect;
+  rec->destroy_server_connect = destroy_server_connect;
+  rec->server_connect = (SERVER_REC *(*) (SERVER_CONNECT_REC *))
+    silc_server_connect; 
+  rec->channel_create = (CHANNEL_REC *(*) (SERVER_REC *, const char *, int))
+    silc_channel_create;
+  rec->query_create = (QUERY_REC *(*) (const char *, const char *, int))
+    silc_query_create;
+  
+  chat_protocol_register(rec);
+  g_free(rec);
+
+  silc_server_init();
+  silc_channels_init();
+  silc_queries_init();
+
+  idletag = g_timeout_add(5, (GSourceFunc) my_silc_scheduler, NULL);
+}
+
+/* Init SILC. Called from src/fe-text/silc.c */
+
+void silc_core_init(void)
+{
+  static struct poptOption options[] = {
+    { "create-key-pair", 'C', POPT_ARG_NONE, &opt_create_keypair, 0, 
+      "Create new public key pair", NULL },
+    { "pkcs", 0, POPT_ARG_STRING, &opt_pkcs, 0, 
+      "Set the PKCS of the public key pair", "PKCS" },
+    { "bits", 0, POPT_ARG_INT, &opt_bits, 0, 
+      "Set the length of the public key pair", "VALUE" },
+    { "show-key", 'S', POPT_ARG_STRING, &opt_keyfile, 0, 
+      "Show the contents of the public key", "FILE" },
+    { "list-ciphers", 'c', POPT_ARG_NONE, &opt_list_ciphers, 0,
+      "List supported ciphers", NULL },
+    { "list-hash-funcs", 'H', POPT_ARG_NONE, &opt_list_hash, 0,
+      "List supported hash functions", NULL },
+    { "list-hmacs", 'M', POPT_ARG_NONE, &opt_list_hmac, 0,
+      "List supported HMACs", NULL },
+    { "list-pkcs", 'P', POPT_ARG_NONE, &opt_list_pkcs, 0,
+      "List supported PKCSs", NULL },
+    { "debug", 'd', POPT_ARG_STRING, &opt_debug, 0,
+      "Enable debugging", "STRING" },
+    { "version", 'V', POPT_ARG_NONE, &opt_version, 0,
+      "Show version", NULL },
+    { NULL, '\0', 0, NULL }
+  };
+
+  signal_add("irssi init finished", (SIGNAL_FUNC) silc_core_init_finish);
+
+  args_register(options);
+}
+
+/* Deinit SILC. Called from src/fe-text/silc.c */
+
+void silc_core_deinit(void)
+{
+  if (idletag != -1) {
+    signal_emit("chat protocol deinit", 1,
+               chat_protocol_find("SILC"));
+    
+    silc_server_deinit();
+    silc_channels_deinit();
+    silc_queries_deinit();
+    
+    chat_protocol_unregister("SILC");
+    
+    g_source_remove(idletag);
+  }
+  
+  g_free(silc_client->username);
+  g_free(silc_client->realname);
+  silc_client_free(silc_client);
+}
diff --git a/apps/irssi/src/silc/core/silc-core.h b/apps/irssi/src/silc/core/silc-core.h
new file mode 100644 (file)
index 0000000..8736fab
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef __SILC_CORE_H
+#define __SILC_CORE_H
+
+#include "clientutil.h"
+
+/* Default client configuration file. This can be overridden at the
+   compilation time. Otherwise, use default. This can be overridden on
+   command line as well. */
+#ifndef SILC_CLIENT_CONFIG_FILE
+#define SILC_CLIENT_CONFIG_FILE "/etc/silc/silc.conf"
+#endif
+
+/* Default user configuration file. This file is searched from user's
+   home directory. This may override global configuration settings. */
+#define SILC_CLIENT_HOME_CONFIG_FILE "~/.silc/silc.conf"
+
+/* Default public and private key file names */
+#define SILC_CLIENT_PUBLIC_KEY_NAME "public_key.pub"
+#define SILC_CLIENT_PRIVATE_KEY_NAME "private_key.prv"
+
+/* Default key expiration time, one year. */
+#define SILC_CLIENT_KEY_EXPIRES 365
+
+/* Default settings for creating key pair */
+#define SILC_CLIENT_DEF_PKCS "rsa"
+#define SILC_CLIENT_DEF_PKCS_LEN 1024
+
+extern SilcClient silc_client;
+
+#ifdef SILC_SIM
+/* SIM (SILC Module) table */
+extern SilcSimContext **sims;
+extern uint32 sims_count;
+#endif
+
+#define IS_SILC_ITEM(rec) (IS_SILC_CHANNEL(rec) || IS_SILC_QUERY(rec))
+
+#endif
diff --git a/apps/irssi/src/silc/core/silc-nicklist.c b/apps/irssi/src/silc/core/silc-nicklist.c
new file mode 100644 (file)
index 0000000..2a517db
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ silc-nicklist.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+#include "servers.h"
+
+#include "silc-channels.h"
+#include "silc-nicklist.h"
+
+SILC_NICK_REC *silc_nicklist_insert(SILC_CHANNEL_REC *channel,
+                                   SilcChannelUser user, int send_massjoin)
+{
+  SILC_NICK_REC *rec;
+
+  g_return_val_if_fail(IS_SILC_CHANNEL(channel), NULL);
+  g_return_val_if_fail(user != NULL, NULL);
+
+  rec = g_new0(SILC_NICK_REC, 1);
+  rec->nick = g_strdup(user->client->nickname);
+  rec->host = g_strdup(user->client->username);
+  rec->silc_user = user;
+  rec->unique_id = user->client;
+
+  if (user->mode & SILC_CHANNEL_UMODE_CHANOP) 
+    rec->op = TRUE;
+  if (user->mode & SILC_CHANNEL_UMODE_CHANFO) 
+    rec->founder = TRUE;
+  rec->send_massjoin = send_massjoin;
+
+  nicklist_insert(CHANNEL(channel), (NICK_REC *) rec);
+  return rec;
+}
+
+SILC_NICK_REC *silc_nicklist_find(SILC_CHANNEL_REC *channel,
+                                 SilcClientEntry client)
+{
+  if (!client || !client->nickname)
+    return NULL;
+
+  return (SILC_NICK_REC *)nicklist_find_unique(CHANNEL(channel),
+                                              client->nickname, client);
+}
+
+#define isnickchar(a) \
+    (isalnum((int) (a)) || (a) == '`' || (a) == '-' || (a) == '_' || \
+    (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
+    (a) == '|' || (a) == '\\' || (a) == '^')
+
+/* Remove all "extra" characters from `nick'. Like _nick_ -> nick */
+char *silc_nick_strip(const char *nick)
+{
+  char *stripped, *spos;
+
+  g_return_val_if_fail(nick != NULL, NULL);
+  
+  spos = stripped = g_strdup(nick);
+  while (isnickchar(*nick)) {
+    if (isalnum((int) *nick))
+      *spos++ = *nick;
+    nick++;
+  }
+  if ((unsigned char) *nick >= 128)
+    *spos++ = *nick; /* just add it so that nicks won't match.. */
+  *spos = '\0';
+
+  return stripped;
+}
+
+/* Check is `msg' is meant for `nick'. */
+int silc_nick_match(const char *nick, const char *msg)
+{
+  char *stripnick, *stripmsg;
+  int ret, len;
+
+  g_return_val_if_fail(nick != NULL, FALSE);
+  g_return_val_if_fail(msg != NULL, FALSE);
+
+  len = strlen(nick);
+  if (g_strncasecmp(msg, nick, len) == 0 && !isalnum((int) msg[len]))
+    return TRUE;
+  
+  stripnick = silc_nick_strip(nick);
+  stripmsg = silc_nick_strip(msg);
+  
+  len = strlen(stripnick);
+  ret = len > 0 && g_strncasecmp(stripmsg, stripnick, len) == 0 &&
+    !isalnum((int) stripmsg[len]) &&
+    (unsigned char) stripmsg[len] < 128;
+  
+  g_free(stripnick);
+  g_free(stripmsg);
+
+  return ret;
+}
+
+static const char *get_nick_flags(void)
+{
+  static char flags[3] = { '@', '+', '\0' };
+  return flags;
+}
+
+static void sig_connected(SILC_SERVER_REC *server)
+{
+  if (IS_SILC_SERVER(server))
+    server->get_nick_flags = (void *) get_nick_flags;
+}
+
+void silc_change_nick(SILC_SERVER_REC *server, const char *newnick)
+{
+  server_change_nick((SERVER_REC *)server, newnick);
+}
+
+void silc_nicklist_init(void)
+{
+  signal_add("server connected", (SIGNAL_FUNC) sig_connected);
+}
+
+void silc_nicklist_deinit(void)
+{
+  signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
+}
diff --git a/apps/irssi/src/silc/core/silc-nicklist.h b/apps/irssi/src/silc/core/silc-nicklist.h
new file mode 100644 (file)
index 0000000..b113947
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef __SILC_NICKLIST_H
+#define __SILC_NICKLIST_H
+
+#include "nicklist.h"
+
+typedef struct {
+#include "nick-rec.h"
+  SilcChannelUser silc_user;
+  unsigned int founder:1;
+} SILC_NICK_REC;
+
+SILC_NICK_REC *silc_nicklist_insert(SILC_CHANNEL_REC *channel,
+                                   SilcChannelUser user, int send_massjoin);
+
+SILC_NICK_REC *silc_nicklist_find(SILC_CHANNEL_REC *channel,
+                                  SilcClientEntry client);
+
+/* Check if `msg' is meant for `nick'. */
+int silc_nick_match(const char *nick, const char *msg);
+void silc_change_nick(SILC_SERVER_REC *server, const char *newnick);
+void silc_nicklist_init(void);
+void silc_nicklist_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/silc/core/silc-queries.c b/apps/irssi/src/silc/core/silc-queries.c
new file mode 100644 (file)
index 0000000..6717cb3
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ silc-queries.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+#include "misc.h"
+
+#include "silc-queries.h"
+
+QUERY_REC *silc_query_create(const char *server_tag,
+                            const char *nick, int automatic)
+{
+  QUERY_REC *rec;
+
+  g_return_val_if_fail(nick != NULL, NULL);
+
+  rec = g_new0(QUERY_REC, 1);
+  rec->chat_type = SILC_PROTOCOL;
+  rec->name = g_strdup(nick);
+  rec->server_tag = g_strdup(server_tag);
+  query_init(rec, automatic);
+  return rec;
+}
+
+void silc_queries_init(void)
+{
+}
+
+void silc_queries_deinit(void)
+{
+}
diff --git a/apps/irssi/src/silc/core/silc-queries.h b/apps/irssi/src/silc/core/silc-queries.h
new file mode 100644 (file)
index 0000000..9443b59
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __SILC_QUERIES_H
+#define __SILC_QUERIES_H
+
+#include "chat-protocols.h"
+#include "queries.h"
+#include "silc-servers.h"
+
+/* Returns SILC_QUERY_REC if it's SILC query, NULL if it isn't. */
+#define SILC_QUERY(query) \
+       PROTO_CHECK_CAST(QUERY(query), QUERY_REC, chat_type, "SILC")
+#define IS_SILC_QUERY(query) \
+       (SILC_QUERY(query) ? TRUE : FALSE)
+#define silc_query_find(server, name) \
+       query_find(SERVER(server), name)
+
+QUERY_REC *silc_query_create(const char *server_tag,
+                            const char *nick, int automatic);
+void silc_queries_init(void);
+void silc_queries_deinit(void);
+
+#endif
diff --git a/apps/irssi/src/silc/core/silc-servers-reconnect.c b/apps/irssi/src/silc/core/silc-servers-reconnect.c
new file mode 100644 (file)
index 0000000..79a7b69
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ silc-servers-reconnect.c : irssi
+
+    Copyright (C) 2000 Timo Sirainen
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+#include "signals.h"
+
+#include "silc-servers.h"
+
+static void sig_server_reconnect_save_status(SILC_SERVER_CONNECT_REC *conn,
+                                            SILC_SERVER_REC *server)
+{
+       if (!IS_SILC_SERVER_CONNECT(conn) || !IS_SILC_SERVER(server))
+               return;
+
+       g_free_not_null(conn->channels);
+       conn->channels = silc_server_get_channels(server);
+}
+
+static void sig_server_connect_copy(SERVER_CONNECT_REC **dest,
+                                   SILC_SERVER_CONNECT_REC *src)
+{
+       SILC_SERVER_CONNECT_REC *rec;
+
+       g_return_if_fail(dest != NULL);
+       if (!IS_SILC_SERVER_CONNECT(src))
+               return;
+
+       rec = g_new0(SILC_SERVER_CONNECT_REC, 1);
+       rec->chat_type = SILC_PROTOCOL;
+       *dest = (SERVER_CONNECT_REC *) rec;
+}
+
+void silc_servers_reconnect_init(void)
+{
+       signal_add("server reconnect save status", (SIGNAL_FUNC) sig_server_reconnect_save_status);
+       signal_add("server connect copy", (SIGNAL_FUNC) sig_server_connect_copy);
+}
+
+void silc_servers_reconnect_deinit(void)
+{
+       signal_remove("server reconnect save status", (SIGNAL_FUNC) sig_server_reconnect_save_status);
+       signal_remove("server connect copy", (SIGNAL_FUNC) sig_server_connect_copy);
+}
diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c
new file mode 100644 (file)
index 0000000..1569b4d
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+  silc-server.c : irssi
+
+  Copyright (C) 2000 - 2001 Timo Sirainen
+                            Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "module.h"
+
+#include "net-nonblock.h"
+#include "net-sendbuffer.h"
+#include "signals.h"
+#include "servers.h"
+#include "commands.h"
+#include "levels.h"
+#include "modules.h"
+#include "rawlog.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "servers-setup.h"
+
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "silc-nicklist.h"
+#include "window-item-def.h"
+
+#include "fe-common/core/printtext.h"
+#include "fe-common/silc/module-formats.h"
+
+void silc_servers_reconnect_init(void);
+void silc_servers_reconnect_deinit(void);
+
+static void silc_send_channel(SILC_SERVER_REC *server,
+                             char *channel, char *msg)
+{
+  SILC_CHANNEL_REC *rec;
+  
+  rec = silc_channel_find(server, channel);
+  if (rec == NULL || rec->entry == NULL)
+    return;
+  
+  silc_client_send_channel_message(silc_client, server->conn, rec->entry, 
+                                  NULL, 0, msg, strlen(msg), TRUE);
+}
+
+typedef struct {
+  char *nick;
+  char *msg;
+  SILC_SERVER_REC *server;
+} PRIVMSG_REC;
+
+/* Callback function that sends the private message if the client was
+   resolved from the server. */
+
+static void silc_send_msg_clients(SilcClient client,
+                                 SilcClientConnection conn,
+                                 SilcClientEntry *clients,
+                                 uint32 clients_count,
+                                 void *context)
+{
+  PRIVMSG_REC *rec = context;
+  SILC_SERVER_REC *server = rec->server;
+  SilcClientEntry target;
+  char *nickname = NULL;
+
+  if (!clients_count) {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+             "%s: There is no such client", rec->nick);
+  } else {
+    if (clients_count > 1) {
+      silc_parse_userfqdn(rec->nick, &nickname, NULL);
+
+      /* Find the correct one. The rec->nick might be a formatted nick
+        so this will find the correct one. */
+      clients = silc_client_get_clients_local(silc_client, server->conn, 
+                                             nickname, rec->nick, 
+                                             &clients_count);
+      if (!clients) {
+       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+                 "%s: There is no such client", rec->nick);
+       silc_free(nickname);
+       goto out;
+      }
+      silc_free(nickname);
+    }
+
+    target = clients[0];
+
+    /* Still check for exact math for nickname, this compares the
+       real (formatted) nickname and the nick (maybe formatted) that
+       use gave. This is to assure that `nick' does not match 
+       `nick@host'. */
+    if (strcasecmp(rec->nick, clients[0]->nickname)) {
+      printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+               "%s: There is no such client", rec->nick);
+      goto out;
+    }
+
+    /* Send the private message */
+    silc_client_send_private_message(client, conn, target, 0,
+                                    rec->msg, strlen(rec->msg),
+                                    TRUE);
+  }
+  
+ out:
+  g_free(rec->nick);
+  g_free(rec->msg);
+  g_free(rec);
+}
+
+static void silc_send_msg(SILC_SERVER_REC *server, char *nick, char *msg)
+{
+  PRIVMSG_REC *rec;
+  SilcClientEntry *clients;
+  uint32 clients_count;
+  char *nickname = NULL;
+
+  if (!silc_parse_userfqdn(nick, &nickname, NULL)) {
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_BAD_NICK, nick);
+    return;
+  }
+
+  /* Find client entry */
+  clients = silc_client_get_clients_local(silc_client, server->conn, 
+                                         nickname, nick, &clients_count);
+  if (!clients) {
+    rec = g_new0(PRIVMSG_REC, 1);
+    rec->nick = g_strdup(nick);
+    rec->msg = g_strdup(msg);
+    rec->server = server;
+
+    /* Could not find client with that nick, resolve it from server. */
+    silc_client_get_clients(silc_client, server->conn,
+                           nickname, NULL, silc_send_msg_clients, rec);
+    silc_free(nickname);
+    return;
+  }
+
+  /* Send the private message directly */
+  silc_free(nickname);
+  silc_client_send_private_message(silc_client, server->conn, 
+                                  clients[0], 0, msg, strlen(msg), TRUE);
+}
+
+static int isnickflag_func(char flag)
+{
+  return flag == '@' || flag == '+';
+}
+
+static int ischannel_func(SERVER_REC *server, const char *data)
+{
+  return FALSE;
+}
+
+const char *get_nick_flags(void)
+{
+  return "@\0\0";
+}
+
+static void send_message(SILC_SERVER_REC *server, char *target,
+                        char *msg, int target_type)
+{
+  g_return_if_fail(server != NULL);
+  g_return_if_fail(target != NULL);
+  g_return_if_fail(msg != NULL);
+
+  if (target_type == SEND_TARGET_CHANNEL)
+    silc_send_channel(server, target, msg);
+  else
+    silc_send_msg(server, target, msg);
+}
+
+static void sig_connected(SILC_SERVER_REC *server)
+{
+  SilcClientConnection conn;
+  int fd;
+
+  if (!IS_SILC_SERVER(server))
+    return;
+
+  conn = silc_client_add_connection(silc_client,
+                                   server->connrec->address,
+                                   server->connrec->port,
+                                   server);
+  server->conn = conn;
+       
+  fd = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
+  silc_client_start_key_exchange(silc_client, conn, fd);
+
+  server->ftp_sessions = silc_dlist_init();
+  server->isnickflag = isnickflag_func;
+  server->ischannel = ischannel_func;
+  server->get_nick_flags = get_nick_flags;
+  server->send_message = (void *) send_message;
+}
+
+static void sig_disconnected(SILC_SERVER_REC *server)
+{
+  if (!IS_SILC_SERVER(server))
+    return;
+
+  silc_dlist_uninit(server->ftp_sessions);
+
+  if (server->conn && server->conn->sock != NULL) {
+    silc_client_close_connection(silc_client, NULL, server->conn);
+    
+    /* SILC closes the handle */
+    g_io_channel_unref(net_sendbuffer_handle(server->handle));
+    net_sendbuffer_destroy(server->handle, FALSE);
+    server->handle = NULL;
+  }
+}
+
+SILC_SERVER_REC *silc_server_connect(SILC_SERVER_CONNECT_REC *conn)
+{
+  SILC_SERVER_REC *server;
+
+  g_return_val_if_fail(IS_SILC_SERVER_CONNECT(conn), NULL);
+  if (conn->address == NULL || *conn->address == '\0') 
+    return NULL;
+  if (conn->nick == NULL || *conn->nick == '\0') {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, 
+             "Cannot connect: nickname is not set");
+    return NULL;
+  }
+
+  server = g_new0(SILC_SERVER_REC, 1);
+  server->chat_type = SILC_PROTOCOL;
+  server->connrec = conn;
+  if (server->connrec->port <= 0) 
+    server->connrec->port = 706;
+
+  server_connect_ref(SERVER_CONNECT(conn));
+
+  if (!server_start_connect((SERVER_REC *) server)) {
+    server_connect_unref(SERVER_CONNECT(conn));
+    g_free(server);
+    return NULL;
+  }
+
+  return server;
+}
+
+/* Return a string of all channels in server in server->channels_join() 
+   format */
+
+char *silc_server_get_channels(SILC_SERVER_REC *server)
+{
+  GSList *tmp;
+  GString *chans;
+  char *ret;
+
+  g_return_val_if_fail(server != NULL, FALSE);
+
+  chans = g_string_new(NULL);
+  for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+    CHANNEL_REC *channel = tmp->data;
+    
+    g_string_sprintfa(chans, "%s,", channel->name);
+  }
+
+  if (chans->len > 0)
+    g_string_truncate(chans, chans->len-1);
+
+  ret = chans->str;
+  g_string_free(chans, FALSE);
+  
+  return ret;
+}
+
+/* Syntaxes of all SILC commands for HELP files (the help file generation
+   will snoop these from here). */
+
+/* SYNTAX: BAN <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
+/* SYNTAX: CMODE <channel> +|-<modes> [{ <arguments>}] */
+/* SYNTAX: CUMODE <channel> +|-<modes> <nickname>[@<hostname>] [-pubkey|<passwd>] */
+/* SYNTAX: GETKEY <nickname or server name> */
+/* SYNTAX: INVITE <channel> [<nickname>[@hostname>] */
+/* SYNTAX: INVITE <channel> [+|-[<nickname>[@<server>[!<username>[@hostname>]]]]] */
+/* SYNTAX: KEY MSG <nickname> set|unset|list|agreement|negotiate [<arguments>] */
+/* SYNTAX: KEY CHANNEL <channel> set|unset|list [<arguments>] */
+/* SYNTAX: KICK <channel> <nickname>[@<hostname>] [<comment>] */
+/* SYNTAX: KILL <nickname>[@<hostname>] [<comment>] */
+/* SYNTAX: OPER <username> [-pubkey] */
+/* SYNTAX: SILCOPER <username> [-pubkey] */
+/* SYNTAX: TOPIC <channel> [<topic>] */
+/* SYNTAX: UMODE +|-<modes> */
+/* SYNTAX: WHOIS <nickname>[@<hostname>] [<count>] */
+/* SYNTAX: WHOWAS <nickname>[@<hostname>] [<count>] */
+/* SYNTAX: CLOSE <server> [<port>] */
+/* SYNTAX: SHUTDOWN */
+/* SYNTAX: MOTD [<server>] */
+/* SYNTAX: LIST [<channel>] */
+/* SYNTAX: ME <message> */
+/* SYNTAX: ACTION <channel> <message> */
+/* SYNTAX: AWAY [<message>] */
+/* SYNTAX: INFO [<server>] */
+/* SYNTAX: NICK <nickname> */
+/* SYNTAX: NOTICE <message> */
+/* SYNTAX: PART [<channel>] */
+/* SYNTAX: PING */
+/* SYNTAX: SCONNECT <server> [<port>] */
+/* SYNTAX: USERS <channel> */
+/* SYNTAX: FILE SEND <filepath> <nickname> [<local IP> [<local port>]] */
+/* SYNTAX: FILE RECEIVE [<nickname>] */
+/* SYNTAX: FILE CLOSE [<nickname>] */
+/* SYNTAX: FILE */
+/* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <hmac>] [-founder <-pubkey|passwd>] */
+
+void silc_command_exec(SILC_SERVER_REC *server,
+                      const char *command, const char *args)
+{
+  uint32 argc = 0;
+  unsigned char **argv;
+  uint32 *argv_lens, *argv_types;
+  char *data, *tmpcmd;
+  SilcClientCommand cmd;
+  SilcClientCommandContext ctx;
+
+  g_return_if_fail(server != NULL);
+
+  tmpcmd = g_strdup(command); 
+  g_strup(tmpcmd);
+  cmd = silc_client_command_find(silc_client, tmpcmd);
+  g_free(tmpcmd);
+  if (cmd == NULL)
+    return;
+  
+  /* Now parse all arguments */
+  data = g_strconcat(command, " ", args, NULL);
+  silc_parse_command_line(data, &argv, &argv_lens,
+                         &argv_types, &argc, cmd->max_args);
+  g_free(data);
+
+  /* Allocate command context. This and its internals must be free'd
+     by the command routine receiving it. */
+  ctx = silc_client_command_alloc();
+  ctx->client = silc_client;
+  ctx->conn = server->conn;
+  ctx->command = cmd;
+  ctx->argc = argc;
+  ctx->argv = argv;
+  ctx->argv_lens = argv_lens;
+  ctx->argv_types = argv_types;
+  
+  /* Execute command */
+  silc_client_command_call(cmd, ctx);
+}
+
+/* Generic command function to call any SILC command directly. */
+
+static void command_self(const char *data, SILC_SERVER_REC *server,
+                        WI_ITEM_REC *item)
+{
+  if (!IS_SILC_SERVER(server) || !server->connected) {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Not connected to server");
+    return;
+  }
+
+  if (IS_SILC_CHANNEL(item)) {
+    SILC_CHANNEL_REC *chanrec;
+    chanrec = silc_channel_find(server, item->name);
+    if (chanrec)
+      server->conn->current_channel = chanrec->entry;
+  }
+
+  silc_command_exec(server, current_command, data);
+  signal_stop();
+}
+
+/* SCONNECT command.  Calls actually SILC's CONNECT command since Irssi
+   has CONNECT command for other purposes. */
+
+static void command_sconnect(const char *data, SILC_SERVER_REC *server)
+{
+  if (!IS_SILC_SERVER(server) || !server->connected) {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Not connected to server");
+    return;
+  }
+
+  silc_command_exec(server, "CONNECT", data);
+  signal_stop();
+}
+
+/* FILE command */
+
+SILC_TASK_CALLBACK(silc_client_file_close_later)
+{
+  FtpSession ftp = (FtpSession)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  silc_client_file_close(silc_client, ftp->conn, ftp->session_id);
+  silc_free(ftp->filepath);
+  silc_free(ftp);
+}
+
+static void silc_client_file_monitor(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientMonitorStatus status,
+                                    SilcClientFileError error,
+                                    uint64 offset,
+                                    uint64 filesize,
+                                    SilcClientEntry client_entry,
+                                    uint32 session_id,
+                                    const char *filepath,
+                                    void *context)
+{
+  SILC_SERVER_REC *server = (SILC_SERVER_REC *)context;
+  FtpSession ftp;
+  char fsize[32];
+
+  snprintf(fsize, sizeof(fsize) - 1, "%llu", ((filesize + 1023) / 1024));
+
+  silc_dlist_start(server->ftp_sessions);
+  while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
+    if (ftp->session_id == session_id) {
+      if (!ftp->filepath && filepath)
+       ftp->filepath = strdup(filepath);
+      break;
+    }
+  }
+
+  if (ftp == SILC_LIST_END)
+    return;
+
+  if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
+    if (error == SILC_CLIENT_FILE_NO_SUCH_FILE)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_ERROR_NO_SUCH_FILE, 
+                        client_entry->nickname, 
+                        filepath ? filepath : "[N/A]");
+    else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED)
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_ERROR_PERMISSION_DENIED, 
+                        client_entry->nickname);
+    else
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_ERROR, client_entry->nickname);
+    silc_schedule_task_add(silc_client->schedule, 0,
+                          silc_client_file_close_later, ftp,
+                          1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    if (ftp == server->current_session)
+      server->current_session = NULL;
+    silc_dlist_del(server->ftp_sessions, ftp);
+  }
+
+  if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) {
+    printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                      SILCTXT_FILE_KEY_EXCHANGE, client_entry->nickname);
+  }
+
+  /* Save some transmission data */
+  if (offset && filesize) {
+    unsigned long delta = time(NULL) - ftp->starttime;
+
+    ftp->percent = ((double)offset / (double)filesize) * (double)100.0;
+    if (delta)
+      ftp->kps = (double)((offset / (double)delta) + 1023) / (double)1024;
+    else
+      ftp->kps = (double)(offset + 1023) / (double)1024;
+    ftp->offset = offset;
+    ftp->filesize = filesize;
+  }
+
+  if (status == SILC_CLIENT_FILE_MONITOR_SEND) {
+    if (offset == 0) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_TRANSMIT, filepath, fsize,
+                        client_entry->nickname);
+      ftp->starttime = time(NULL);
+    }
+    if (offset == filesize) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_TRANSMITTED, filepath, fsize,
+                        client_entry->nickname, ftp->kps);
+      silc_schedule_task_add(silc_client->schedule, 0,
+                            silc_client_file_close_later, ftp,
+                            1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      if (ftp == server->current_session)
+       server->current_session = NULL;
+      silc_dlist_del(server->ftp_sessions, ftp);
+    }
+  }
+
+  if (status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
+    if (offset == 0) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_RECEIVE, filepath, fsize,
+                        client_entry->nickname);
+      ftp->starttime = time(NULL);
+    }
+
+    if (offset == filesize) {
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_RECEIVED, filepath, fsize,
+                        client_entry->nickname, ftp->kps);
+      silc_schedule_task_add(silc_client->schedule, 0,
+                            silc_client_file_close_later, ftp,
+                            1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      if (ftp == server->current_session)
+       server->current_session = NULL;
+      silc_dlist_del(server->ftp_sessions, ftp);
+    }
+  }
+}
+
+typedef struct {
+  SILC_SERVER_REC *server;
+  char *data;
+  char *nick;
+  WI_ITEM_REC *item;
+} *FileGetClients;
+
+static void silc_client_command_file_get_clients(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcClientEntry *clients,
+                                                uint32 clients_count,
+                                                void *context)
+{
+  FileGetClients internal = (FileGetClients)context;
+
+  if (!clients) {
+    printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown nick: %s", 
+             internal->nick);
+    silc_free(internal->data);
+    silc_free(internal->nick);
+    silc_free(internal);
+    return;
+  }
+
+  signal_emit("command file", 3, internal->data, internal->server,
+             internal->item);
+
+  silc_free(internal->data);
+  silc_free(internal->nick);
+  silc_free(internal);
+}
+
+static void command_file(const char *data, SILC_SERVER_REC *server,
+                        WI_ITEM_REC *item)
+{
+  SilcClientConnection conn;
+  SilcClientEntry *entrys, client_entry;
+  SilcClientFileError ret;
+  uint32 entry_count;
+  char *nickname = NULL, *tmp;
+  unsigned char **argv;
+  uint32 argc;
+  uint32 *argv_lens, *argv_types;
+  int type = 0;
+  FtpSession ftp;
+  char *local_ip = NULL;
+  uint32 local_port = 0;
+  uint32 session_id;
+
+  if (!server || !IS_SILC_SERVER(server) || !server->connected)
+    cmd_return_error(CMDERR_NOT_CONNECTED);
+
+  conn = server->conn;
+
+  /* Now parse all arguments */
+  tmp = g_strconcat("FILE", " ", data, NULL);
+  silc_parse_command_line(tmp, &argv, &argv_lens, &argv_types, &argc, 6);
+  g_free(tmp);
+
+  if (argc == 1)
+    type = 4;
+
+  if (argc >= 2) {
+    if (!strcasecmp(argv[1], "send"))
+      type = 1;
+    if (!strcasecmp(argv[1], "receive"))
+      type = 2;
+    if (!strcasecmp(argv[1], "close"))
+      type = 3;
+  }
+  
+  if (type == 0)
+    cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+  switch (type) {
+  case 1:
+    if (argc < 4)
+      cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+    /* Parse the typed nickname. */
+    if (!silc_parse_userfqdn(argv[3], &nickname, NULL)) {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[3]);
+      goto out;
+    }
+    
+    /* Find client entry */
+    entrys = silc_client_get_clients_local(silc_client, conn, nickname,
+                                          argv[3], &entry_count);
+    if (!entrys) {
+      FileGetClients inter = silc_calloc(1, sizeof(*inter));
+      inter->server = server;
+      inter->data = strdup(data);
+      inter->nick = strdup(nickname);
+      inter->item = item;
+      silc_client_get_clients(silc_client, conn, nickname, argv[3],
+                             silc_client_command_file_get_clients, inter);
+      goto out;
+    }
+    client_entry = entrys[0];
+    silc_free(entrys);
+
+    if (argc >= 5)
+      local_ip = argv[4];
+    if (argc >= 6)
+      local_port = atoi(argv[5]);
+
+    ret = 
+      silc_client_file_send(silc_client, conn, silc_client_file_monitor, 
+                           server, local_ip, local_port, 
+                           client_entry, argv[2], &session_id);
+    if (ret == SILC_CLIENT_FILE_OK) {
+      ftp = silc_calloc(1, sizeof(*ftp));
+      ftp->session_id = session_id;
+
+      printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                        SILCTXT_FILE_SEND, client_entry->nickname,
+                        argv[2]);
+
+      ftp->client_entry = client_entry;
+      ftp->filepath = strdup(argv[2]);
+      ftp->conn = conn;
+      ftp->send = TRUE;
+      silc_dlist_add(server->ftp_sessions, ftp);
+      server->current_session = ftp;
+    } else {
+      if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+                          client_entry->nickname);
+      if (ret == SILC_CLIENT_FILE_NO_SUCH_FILE)
+       printformat_module("fe-common/silc", NULL, NULL, MSGLEVEL_CRAP,
+                          SILCTXT_FILE_ERROR_NO_SUCH_FILE, 
+                          client_entry->nickname, argv[2]);
+    }
+
+    break;
+
+  case 2:
+    /* Parse the typed nickname. */
+    if (argc >= 3) {
+      if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
+       goto out;
+      }
+    
+      /* Find client entry */
+      entrys = silc_client_get_clients_local(silc_client, conn, nickname,
+                                            argv[2], &entry_count);
+      if (!entrys) {
+       FileGetClients inter = silc_calloc(1, sizeof(*inter));
+       inter->server = server;
+       inter->data = strdup(data);
+       inter->nick = strdup(nickname);
+       inter->item = item;
+       silc_client_get_clients(silc_client, conn, nickname, argv[2],
+                               silc_client_command_file_get_clients, inter);
+       goto out;
+      }
+      client_entry = entrys[0];
+      silc_free(entrys);
+    } else {
+      if (!server->current_session) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_FILE_NA);
+       goto out;
+      }
+
+      ret = silc_client_file_receive(silc_client, conn, 
+                                    silc_client_file_monitor, server,
+                                    server->current_session->session_id);
+      if (ret != SILC_CLIENT_FILE_OK) {
+       if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+         printformat_module("fe-common/silc", server, NULL,
+                            MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+                            server->current_session->client_entry->nickname);
+       else
+         printformat_module("fe-common/silc", server, NULL,
+                            MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+                            server->current_session->client_entry->nickname);
+      }
+
+      goto out;
+    }
+
+    silc_dlist_start(server->ftp_sessions);
+    while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
+      if (ftp->client_entry == client_entry && !ftp->filepath) {
+       ret = silc_client_file_receive(silc_client, conn, 
+                                      silc_client_file_monitor, server,
+                                      ftp->session_id);
+       if (ret != SILC_CLIENT_FILE_OK) {
+         if (ret == SILC_CLIENT_FILE_ALREADY_STARTED)
+           printformat_module("fe-common/silc", server, NULL,
+                              MSGLEVEL_CRAP, SILCTXT_FILE_ALREADY_STARTED,
+                              client_entry->nickname);
+         else
+           printformat_module("fe-common/silc", server, NULL,
+                              MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+                              client_entry->nickname);
+       }
+       break;
+      }
+    }
+
+    if (ftp == SILC_LIST_END) {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+                        client_entry->nickname);
+      goto out;
+    }
+    break;
+
+  case 3:
+    /* Parse the typed nickname. */
+    if (argc >= 3) {
+      if (!silc_parse_userfqdn(argv[2], &nickname, NULL)) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_BAD_NICK, argv[2]);
+       goto out;
+      }
+    
+      /* Find client entry */
+      entrys = silc_client_get_clients_local(silc_client, conn, nickname,
+                                            argv[2], &entry_count);
+      if (!entrys) {
+       FileGetClients inter = silc_calloc(1, sizeof(*inter));
+       inter->server = server;
+       inter->data = strdup(data);
+       inter->nick = strdup(nickname);
+       inter->item = item;
+       silc_client_get_clients(silc_client, conn, nickname, argv[2],
+                               silc_client_command_file_get_clients, inter);
+       goto out;
+      }
+      client_entry = entrys[0];
+      silc_free(entrys);
+    } else {
+      if (!server->current_session) {
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_FILE_NA);
+       goto out;
+      }
+      silc_client_file_close(silc_client, conn, 
+                            server->current_session->session_id);
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
+                        server->current_session->client_entry->nickname,
+                        server->current_session->filepath ?
+                        server->current_session->filepath : "[N/A]");
+      silc_dlist_del(server->ftp_sessions, server->current_session);
+      silc_free(server->current_session->filepath);
+      silc_free(server->current_session);
+      server->current_session = NULL;
+      goto out;
+    }
+
+    silc_dlist_start(server->ftp_sessions);
+    while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
+      if (ftp->client_entry == client_entry) {
+       silc_client_file_close(silc_client, conn, ftp->session_id);
+       printformat_module("fe-common/silc", server, NULL,
+                          MSGLEVEL_CRAP, SILCTXT_FILE_CLOSED,
+                          client_entry->nickname,
+                          ftp->filepath ? ftp->filepath : "[N/A]");
+       if (ftp == server->current_session)
+         server->current_session = NULL;
+       silc_dlist_del(server->ftp_sessions, ftp);
+       silc_free(ftp->filepath);
+       silc_free(ftp);
+       break;
+      }
+    }
+
+    if (ftp == SILC_LIST_END) {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_FILE_CLIENT_NA,
+                        client_entry->nickname);
+      goto out;
+    }
+    break;
+
+  case 4:
+
+    if (!silc_dlist_count(server->ftp_sessions)) {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_FILE_NA);
+      goto out;
+    }
+
+    printformat_module("fe-common/silc", server, NULL,
+                      MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_HEADER);
+
+    silc_dlist_start(server->ftp_sessions);
+    while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
+      printformat_module("fe-common/silc", server, NULL,
+                        MSGLEVEL_CRAP, SILCTXT_FILE_SHOW_LINE,
+                        ftp->client_entry->nickname, 
+                        ftp->send ? "send" : "receive",
+                        (uint32)(ftp->offset + 1023) / 1024,
+                        (uint32)(ftp->filesize + 1023) / 1024,
+                        ftp->percent, ftp->kps,
+                        ftp->filepath ? ftp->filepath : "[N/A]");
+    }
+
+    break;
+
+  default:
+    break;
+  }
+
+ out:
+  silc_free(nickname);
+}
+
+void silc_server_init(void)
+{
+  silc_servers_reconnect_init();
+
+  signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
+  signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+  command_bind("whois", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("whowas", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("nick", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("topic", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("cmode", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("cumode", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("users", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("list", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("ban", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("oper", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("silcoper", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("umode", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("invite", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("kill", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("kick", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("info", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("ping", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("motd", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("close", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("shutdown", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("getkey", MODULE_NAME, (SIGNAL_FUNC) command_self);
+  command_bind("sconnect", MODULE_NAME, (SIGNAL_FUNC) command_sconnect);
+  command_bind("file", MODULE_NAME, (SIGNAL_FUNC) command_file);
+
+  command_set_options("connect", "+silcnet");
+}
+
+void silc_server_deinit(void)
+{
+  silc_servers_reconnect_deinit();
+
+  signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
+  signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
+  command_unbind("whois", (SIGNAL_FUNC) command_self);
+  command_unbind("whowas", (SIGNAL_FUNC) command_self);
+  command_unbind("nick", (SIGNAL_FUNC) command_self);
+  command_unbind("topic", (SIGNAL_FUNC) command_self);
+  command_unbind("cmode", (SIGNAL_FUNC) command_self);
+  command_unbind("cumode", (SIGNAL_FUNC) command_self);
+  command_unbind("users", (SIGNAL_FUNC) command_self);
+  command_unbind("list", (SIGNAL_FUNC) command_self);
+  command_unbind("oper", (SIGNAL_FUNC) command_self);
+  command_unbind("silcoper", (SIGNAL_FUNC) command_self);
+  command_unbind("umode", (SIGNAL_FUNC) command_self);
+  command_unbind("invite", (SIGNAL_FUNC) command_self);
+  command_unbind("kill", (SIGNAL_FUNC) command_self);
+  command_unbind("kick", (SIGNAL_FUNC) command_self);
+  command_unbind("info", (SIGNAL_FUNC) command_self);
+  command_unbind("ping", (SIGNAL_FUNC) command_self);
+  command_unbind("motd", (SIGNAL_FUNC) command_self);
+  command_unbind("ban", (SIGNAL_FUNC) command_self);
+  command_unbind("close", (SIGNAL_FUNC) command_self);
+  command_unbind("shutdown", (SIGNAL_FUNC) command_self);
+  command_unbind("getkey", (SIGNAL_FUNC) command_self);
+  command_unbind("sconnect", (SIGNAL_FUNC) command_sconnect);
+  command_unbind("file", (SIGNAL_FUNC) command_file);
+}
+
+void silc_server_free_ftp(SILC_SERVER_REC *server,
+                         SilcClientEntry client_entry)
+{
+  FtpSession ftp;
+
+  silc_dlist_start(server->ftp_sessions);
+  while ((ftp = silc_dlist_get(server->ftp_sessions)) != SILC_LIST_END) {
+    if (ftp->client_entry == client_entry) {
+      silc_dlist_del(server->ftp_sessions, ftp);
+      silc_free(ftp->filepath);
+      silc_free(ftp);
+    }
+  }
+}
diff --git a/apps/irssi/src/silc/core/silc-servers.h b/apps/irssi/src/silc/core/silc-servers.h
new file mode 100644 (file)
index 0000000..c90b84f
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef __SILC_SERVER_H
+#define __SILC_SERVER_H
+
+#include "chat-protocols.h"
+#include "servers.h"
+
+/* returns SILC_SERVER_REC if it's SILC server, NULL if it isn't */
+#define SILC_SERVER(server) \
+       PROTO_CHECK_CAST(SERVER(server), SILC_SERVER_REC, chat_type, "SILC")
+#define SILC_SERVER_CONNECT(conn) \
+       PROTO_CHECK_CAST(SERVER_CONNECT(conn), SILC_SERVER_CONNECT_REC, \
+                        chat_type, "SILC")
+#define IS_SILC_SERVER(server) \
+       (SILC_SERVER(server) ? TRUE : FALSE)
+#define IS_SILC_SERVER_CONNECT(conn) \
+       (SILC_SERVER_CONNECT(conn) ? TRUE : FALSE)
+
+/* all strings should be either NULL or dynamically allocated */
+/* address and nick are mandatory, rest are optional */
+typedef struct {
+#include "server-connect-rec.h"
+} SILC_SERVER_CONNECT_REC;
+
+typedef struct {
+  SilcClientEntry client_entry;
+  SilcClientConnection conn;
+  uint32 session_id;
+  char *filepath;
+  bool send;
+
+  long starttime;              /* Start time of transfer */
+  double kps;                  /* Kilos per second */
+  uint64 offset;               /* Current offset */
+  uint64 filesize;             /* Total file size */
+  uint32 percent;              /* Percent of current transmission */
+} *FtpSession;
+
+#define STRUCT_SERVER_CONNECT_REC SILC_SERVER_CONNECT_REC
+typedef struct {
+#include "server-rec.h"
+  /* Command sending queue */
+  int cmdcount;                /* number of commands in `cmdqueue'. Can be more than
+                          there actually is, to make flood control remember
+                          how many messages can be sent before starting the
+                          flood control */
+  int cmd_last_split;  /* Last command wasn't sent entirely to server.
+                          First item in `cmdqueue' should be re-sent. */
+  GSList *cmdqueue;
+  GTimeVal last_cmd;   /* last time command was sent to server */
+  
+  GSList *idles;       /* Idle queue - send these commands to server
+                          if there's nothing else to do */
+
+  SilcDList ftp_sessions;
+  FtpSession current_session;
+  
+  gpointer chanqueries;
+  SilcClientConnection conn;
+  uint32 umode;
+} SILC_SERVER_REC;
+
+SILC_SERVER_REC *silc_server_connect(SILC_SERVER_CONNECT_REC *conn);
+
+/* Return a string of all channels in server in server->channels_join() 
+   format */
+char *silc_server_get_channels(SILC_SERVER_REC *server);
+void silc_command_exec(SILC_SERVER_REC *server,
+                      const char *command, const char *args);
+void silc_server_init(void);
+void silc_server_deinit(void);
+void silc_server_free_ftp(SILC_SERVER_REC *server,
+                         SilcClientEntry client_entry);
+
+#endif
diff --git a/apps/irssi/src/silc/silc.c b/apps/irssi/src/silc/silc.c
deleted file mode 100644 (file)
index de2bde0..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/* this file is automatically generated by configure - don't change */
-void silc_core_init(void); void silc_core_deinit(void);
-void silc_init(void) { silc_core_init();  }
-void silc_deinit(void) {  silc_core_deinit(); }
index 1b358e63308cf34102a79e80b4b32e86b6367b90..ced6a4b1bc62c90b35ab99450b0f3c06db7b5fbf 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-bin_PROGRAMS = silc
+#bin_PROGRAMS = silc
+#silc_SOURCES = \
+#      silc.c \
+#      clientconfig.c \
+#      clientutil.c \
+#      local_command.c \
+#      screen.c \
+#      client_ops.c
+#silc_DEPENDENCIES = ../lib/libsilcclient.a ../lib/libsilc.a
 
-silc_SOURCES = \
-       silc.c \
-       client.c \
-       command.c \
-       command_reply.c \
-       clientconfig.c \
-       clientutil.c \
-       protocol.c \
-       screen.c
+LIBS = $(SILC_COMMON_LIBS)
+LDADD = -L. -L.. -L../lib -lsilcclient
 
-LDADD = -L. -L.. -L../lib -lsilc -lcurses
+ADD_INCLUDES = $(CURSES_INCLUDEDIR)
 
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../lib/silccore -I../lib/silccrypt \
-       -I../lib/silcmath -I../lib/silcske -I../lib/silcsim \
-       -I../includes \
-       -I../lib/silcmath/gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
diff --git a/apps/silc/README b/apps/silc/README
new file mode 100644 (file)
index 0000000..d8fe44b
--- /dev/null
@@ -0,0 +1,5 @@
+This directory includes the old SILC Client.  It was the first SILC
+client ever made.  However, it is now obsolete and not supported.  It
+is provided here merely as an example code.  Do not try to compile it.
+
+ -Pekka
diff --git a/apps/silc/client.c b/apps/silc/client.c
deleted file mode 100644 (file)
index 8014179..0000000
+++ /dev/null
@@ -1,2371 +0,0 @@
-/*
-
-  client.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "clientincludes.h"
-
-/* Static function prototypes */
-static int silc_client_bad_keys(unsigned char key);
-static void silc_client_process_message(SilcClient client);
-static char *silc_client_parse_command(unsigned char *buffer);
-
-/* Static task callback prototypes */
-SILC_TASK_CALLBACK(silc_client_update_clock);
-SILC_TASK_CALLBACK(silc_client_run_commands);
-SILC_TASK_CALLBACK(silc_client_process_key_press);
-SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
-SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
-SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
-SILC_TASK_CALLBACK(silc_client_packet_process);
-SILC_TASK_CALLBACK(silc_client_packet_parse);
-
-SilcClientWindow silc_client_create_main_window(SilcClient client);
-SilcClientWindow silc_client_add_window(SilcClient client,
-                                       int is_current);
-void silc_client_packet_parse_type(SilcClient client, 
-                                  SilcSocketConnection sock,
-                                  SilcPacketContext *packet);
-void silc_client_private_message_process(SilcClient client,
-                                        SilcSocketConnection sock,
-                                        SilcPacketContext *packet);
-
-/* Definitions from version.h */
-extern char *silc_version;
-extern char *silc_name;
-extern char *silc_fullname;
-
-/* Allocates new client object. This has to be done before client may
-   work. After calling this one must call silc_client_init to initialize
-   the client. */
-
-int silc_client_alloc(SilcClient *new_client)
-{
-
-  *new_client = silc_calloc(1, sizeof(**new_client));
-  if (*new_client == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new client object"));
-    return FALSE;
-  }
-
-  (*new_client)->input_buffer = NULL;
-  (*new_client)->screen = NULL;
-  (*new_client)->windows = NULL;
-  (*new_client)->windows_count = 0;
-  (*new_client)->current_win = NULL;
-
-  return TRUE;
-}
-
-/* Free's client object */
-
-void silc_client_free(SilcClient client)
-{
-  if (client) {
-    silc_free(client);
-  }
-}
-
-/* Initializes the client. This makes all the necessary steps to make
-   the client ready to be run. One must call silc_client_run to run the
-   client. */
-
-int silc_client_init(SilcClient client)
-{
-
-  SILC_LOG_DEBUG(("Initializing client"));
-  assert(client);
-
-  client->username = silc_get_username();
-  client->realname = silc_get_real_name();
-
-  /* Register all configured ciphers, PKCS and hash functions. */
-  client->config->client = (void *)client;
-  silc_client_config_register_ciphers(client->config);
-  silc_client_config_register_pkcs(client->config);
-  silc_client_config_register_hashfuncs(client->config);
-
-  /* Initialize hash functions for client to use */
-  silc_hash_alloc("md5", &client->md5hash);
-  silc_hash_alloc("sha1", &client->sha1hash);
-
-  /* Initialize none cipher */
-  silc_cipher_alloc("none", &client->none_cipher);
-
-  /* Initialize random number generator */
-  client->rng = silc_rng_alloc();
-  silc_rng_init(client->rng);
-  silc_math_primegen_init(); /* XXX */
-
-#if 0
-  {
-    SilcCipher twofish;
-    unsigned char *src, *dst, *dec;
-    SilcBuffer packet;
-    int payload_len;
-
-    payload_len = 4 + strlen("pekka riikonen");
-    packet = silc_buffer_alloc(payload_len);
-    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-    silc_buffer_format(packet,
-                      SILC_STR_UI_SHORT(payload_len),
-                      SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
-                      SILC_STR_UI_XNSTRING("pekka riikonen", 
-                                           strlen("pekka riikonen")),
-                      SILC_STR_END);
-
-    silc_cipher_alloc("twofish", &twofish);
-    twofish->cipher->set_key(twofish->context, "1234567890123456", 16);
-    twofish->set_iv(twofish, "6543210987654321");
-    SILC_LOG_HEXDUMP(("source: len %d", packet->len), 
-                    packet->data, packet->len );
-    silc_packet_encrypt(twofish, packet, packet->len);
-    SILC_LOG_HEXDUMP(("encrypted"), packet->data, packet->len);
-    silc_packet_decrypt(twofish, packet, packet->len);
-    SILC_LOG_HEXDUMP(("decrypted"), packet->data, packet->len);
-
-  }
-
-  {
-    SilcCipher cipher1, cipher2;
-    unsigned char *src, *dst, *dec;
-    int len = strlen("12345678901234561234567890123456123456789012345612345678901234561234567890123456");
-
-    src = silc_calloc(len + 1, sizeof(unsigned char));
-    dst = silc_calloc(len + 1, sizeof(unsigned char));
-    dec = silc_calloc(len + 1, sizeof(unsigned char));
-
-    memcpy(src, "12345678901234561234567890123456123456789012345612345678901234561234567890123456", len);
-    
-    silc_cipher_alloc("twofish", &cipher1);
-    cipher1->cipher->set_key(cipher1->context, "1234567890123456", 128);
-    cipher1->set_iv(cipher1, "6543210987654321");
-
-    silc_cipher_alloc("twofish", &cipher2);
-    cipher2->cipher->set_key(cipher2->context, "1234567890123456", 128);
-    cipher2->set_iv(cipher2, "6543210987654321");
-
-    SILC_LOG_HEXDUMP(("source: %d", len), src, len);
-    cipher1->cipher->encrypt(cipher1->context, src, src, len, cipher1->iv);
-    SILC_LOG_HEXDUMP(("encrypted"), src, len);
-    cipher2->set_iv(cipher2, "6543210987654321");
-    cipher2->cipher->decrypt(cipher2->context, src, src, len, cipher2->iv);
-    SILC_LOG_HEXDUMP(("decrypted"), src, len);
-
-  }
-#endif
-
-  /* Register the task queues. In SILC we have by default three task queues. 
-     One task queue for non-timeout tasks which perform different kind of 
-     I/O on file descriptors, timeout task queue for timeout tasks, and,
-     generic non-timeout task queue whose tasks apply to all connections. */
-  silc_task_queue_alloc(&client->io_queue, TRUE);
-  if (!client->io_queue) {
-    goto err0;
-  }
-  silc_task_queue_alloc(&client->timeout_queue, TRUE);
-  if (!client->timeout_queue) {
-    goto err1;
-  }
-  silc_task_queue_alloc(&client->generic_queue, TRUE);
-  if (!client->generic_queue) {
-    goto err1;
-  }
-
-  /* Initialize the scheduler */
-  silc_schedule_init(client->io_queue, client->timeout_queue, 
-                    client->generic_queue, 5000);
-
-  /* Register the main task that is used in client. This received
-     the key pressings. */
-  if (silc_task_register(client->io_queue, fileno(stdin), 
-                        silc_client_process_key_press,
-                        (void *)client, 0, 0, 
-                        SILC_TASK_FD,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    goto err2;
-  }
-
-  /* Register timeout task that updates clock every minute. */
-  if (silc_task_register(client->timeout_queue, 0,
-                        silc_client_update_clock,
-                        (void *)client, 
-                        silc_client_time_til_next_min(), 0,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_LOW) == NULL) {
-    goto err2;
-  }
-
-  if (client->config->commands) {
-    /* Run user configured commands with timeout */
-    if (silc_task_register(client->timeout_queue, 0,
-                          silc_client_run_commands,
-                          (void *)client, 0, 1,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW) == NULL) {
-      goto err2;
-    }
-  }
-
-  /* Allocate the input buffer used to save typed characters */
-  client->input_buffer = silc_buffer_alloc(SILC_SCREEN_INPUT_WIN_SIZE);
-  silc_buffer_pull_tail(client->input_buffer, 
-                       SILC_BUFFER_END(client->input_buffer));
-
-  /* Initialize the screen */
-  client->screen = silc_screen_init();
-  silc_client_create_main_window(client);
-  client->screen->input_buffer = client->input_buffer->data;
-  silc_screen_print_coordinates(client->screen, 0);
-
-  return TRUE;
-
- err0:
-  silc_task_queue_free(client->timeout_queue);
- err1:
-  silc_task_queue_free(client->io_queue);
- err2:
-  return FALSE;
-}
-
-/* Stops the client. This is called to stop the client and thus to stop
-   the program. */
-
-void silc_client_stop(SilcClient client)
-{
-  SILC_LOG_DEBUG(("Stopping client"));
-
-  /* Stop the scheduler, although it might be already stopped. This
-     doesn't hurt anyone. This removes all the tasks and task queues,
-     as well. */
-  silc_schedule_stop();
-  silc_schedule_uninit();
-
-  SILC_LOG_DEBUG(("Client client"));
-}
-
-/* Runs the client. */
-
-void silc_client_run(SilcClient client)
-{
-  SILC_LOG_DEBUG(("Running client"));
-
-  /* Start the scheduler, the heart of the SILC client. When this returns
-     the program will be terminated. */
-  silc_schedule();
-}
-
-/* Creates the main window used in SILC client. This is called always
-   at the initialization of the client. If user wants to create more
-   than one windows a new windows are always created by calling 
-   silc_client_add_window. */
-
-SilcClientWindow silc_client_create_main_window(SilcClient client)
-{
-  SilcClientWindow win;
-  void *screen;
-
-  SILC_LOG_DEBUG(("Creating main window"));
-
-  assert(client->screen != NULL);
-
-  win = silc_calloc(1, sizeof(*win));
-  if (win == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new window"));
-    return NULL;
-  }
-
-  client->screen->u_stat_line.program_name = silc_name;
-  client->screen->u_stat_line.program_version = silc_version;
-
-  /* Add the pointers */
-  win->nickname = silc_get_username();
-  win->local_id = NULL;
-  win->local_id_data = NULL;
-  win->local_id_data_len = 0;
-  win->remote_host = NULL;
-  win->remote_port = -1;
-  win->sock = NULL;
-
-  /* Create the actual screen */
-  screen = (void *)silc_screen_create_output_window(client->screen);
-  silc_screen_create_input_window(client->screen);
-  silc_screen_init_upper_status_line(client->screen);
-  silc_screen_init_output_status_line(client->screen);
-  win->screen = screen;
-
-  client->screen->bottom_line->nickname = win->nickname;
-  silc_screen_print_bottom_line(client->screen, 0);
-
-  /* Add the window to windows table */
-  client->windows = silc_calloc(1, sizeof(*client->windows));
-  client->windows[client->windows_count] = win;
-  client->windows_count = 1;
-
-  /* Automatically becomes the current active window */
-  client->current_win = win;
-
-  return win;
-}
-
-/* Allocates and adds new window to the client. This allocates new
-   physical window and internal window for connection specific data. 
-   All the connection specific data is always saved into a window
-   since connection is always associated to a active window. */
-
-SilcClientWindow silc_client_add_window(SilcClient client,
-                                       int is_current)
-{
-  SilcClientWindow win;
-
-  assert(client->screen != NULL);
-
-  win = silc_calloc(1, sizeof(*win));
-  if (win == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new window"));
-    return NULL;
-  }
-
-  /* Add the pointers */
-  win->screen = silc_screen_add_output_window(client->screen);
-  win->sock = NULL;
-
-  /* Add the window to windows table */
-  client->windows = silc_realloc(client->windows, sizeof(*client->windows)
-                                * (client->windows_count + 1));
-  client->windows[client->windows_count] = win;
-  client->windows_count++;
-
-  if (is_current == TRUE)
-    client->current_win = win;
-
-  return win;
-}
-
-/* The main task on SILC client. This processes the key pressings user
-   has made. */
-
-SILC_TASK_CALLBACK(silc_client_process_key_press)
-{
-  SilcClient client = (SilcClient)context;
-  int c;
-
-  /* There is data pending in stdin, this gets it directly */
-  c = wgetch(client->screen->input_win);
-  if (silc_client_bad_keys(c))
-    return;
-
-  SILC_LOG_DEBUG(("Pressed key: %d", c));
-
-  switch(c) {
-    /* 
-     * Special character handling
-     */
-  case KEY_UP: 
-  case KEY_DOWN:
-    break;
-  case KEY_RIGHT:
-    /* Right arrow */
-    SILC_LOG_DEBUG(("RIGHT"));
-    silc_screen_input_cursor_right(client->screen);
-    break;
-  case KEY_LEFT:
-    /* Left arrow */
-    SILC_LOG_DEBUG(("LEFT"));
-    silc_screen_input_cursor_left(client->screen);
-    break;
-  case KEY_BACKSPACE:
-  case KEY_DC:
-  case '\177':
-  case '\b':
-    /* Backspace */
-    silc_screen_input_backspace(client->screen);
-    break;
-  case '\011':
-    /* Tabulator */
-    break;
-  case KEY_IC:
-    /* Insert switch. Turns on/off insert on input window */
-    silc_screen_input_insert(client->screen);
-    break;
-  case CTRL('j'):
-  case '\r':
-    /* Enter, Return. User pressed enter we are ready to
-       process the message. */
-    silc_client_process_message(client);
-    silc_screen_input_reset(client->screen);
-    break;
-  case CTRL('l'):
-    /* Refresh screen, Ctrl^l */
-    silc_screen_refresh_all(client->screen);
-    break;
-  case CTRL('a'):
-  case KEY_HOME:
-  case KEY_BEG:
-    /* Beginning, Home */
-    silc_screen_input_cursor_home(client->screen);
-    break;
-  case CTRL('e'):
-  case KEY_END:
-    /* End */
-    silc_screen_input_cursor_end(client->screen);
-    break;
-  case KEY_LL:
-    /* End */
-    break;
-  case CTRL('g'):
-    /* Bell, Ctrl^g */
-    beep();
-    break;
-  case KEY_DL:
-  case CTRL('u'):
-    /* Delete line */
-    break;
-  default:
-    /* 
-     * Other characters 
-     */
-    if (c < 32) {
-      /* Control codes are printed as reversed */
-      c = (c & 127) | 64;
-      wattron(client->screen->input_win, A_REVERSE);
-      silc_screen_input_print(client->screen, c);
-      wattroff(client->screen->input_win, A_REVERSE);
-    } else  {
-      /* Normal character */
-      silc_screen_input_print(client->screen, c);
-    }
-  }
-
-  silc_screen_print_coordinates(client->screen, 0);
-  silc_screen_refresh_win(client->screen->input_win);
-}
-
-static int silc_client_bad_keys(unsigned char key)
-{
-  /* these are explained in curses.h */
-  switch(key) {
-  case KEY_SF:
-  case KEY_SR:
-  case KEY_NPAGE:
-  case KEY_PPAGE:
-  case KEY_PRINT:
-  case KEY_A1:
-  case KEY_A3:
-  case KEY_B2:
-  case KEY_C1:
-  case KEY_C3:
-  case KEY_UNDO:
-  case KEY_EXIT:
-  case '\v':           /* VT */
-  case '\E':           /* we ignore ESC */
-    return TRUE;
-  default: 
-    return FALSE; 
-  }
-}
-
-/* Processes messages user has typed on the screen. This either sends
-   a packet out to network or if command were written executes it. */
-
-static void silc_client_process_message(SilcClient client)
-{
-  unsigned char *data;
-  unsigned int len;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  data = client->input_buffer->data;
-  len = strlen(data);
-
-  if (data[0] == '/' && data[1] != ' ') {
-    /* Command */
-    unsigned int argc = 0;
-    unsigned char **argv, *tmpcmd;
-    unsigned int *argv_lens, *argv_types;
-    SilcClientCommand *cmd;
-    SilcClientCommandContext ctx;
-
-    /* Get the command */
-    tmpcmd = silc_client_parse_command(data);
-
-    /* Find command match */
-    for (cmd = silc_command_list; cmd->name; cmd++) {
-      if (!strcmp(cmd->name, tmpcmd))
-       break;
-    }
-
-    if (cmd->name == NULL) {
-      silc_say(client, "Invalid command: %s", tmpcmd);
-      silc_free(tmpcmd);
-      goto out;
-    }
-
-    /* Now parse all arguments */
-    silc_client_parse_command_line(data, &argv, &argv_lens, 
-                                  &argv_types, &argc, cmd->max_args);
-    silc_free(tmpcmd);
-
-    SILC_LOG_DEBUG(("Exeuting command: %s", cmd->name));
-
-    /* Allocate command context. This and its internals must be free'd 
-       by the command routine receiving it. */
-    ctx = silc_calloc(1, sizeof(*ctx));
-    ctx->client = client;
-    ctx->sock = client->current_win->sock;
-    ctx->argc = argc;
-    ctx->argv = argv;
-    ctx->argv_lens = argv_lens;
-    ctx->argv_types = argv_types;
-
-    /* Execute command */
-    (*cmd->cb)(ctx);
-
-  } else {
-    /* Normal message to a channel */
-    if (len && client->current_win->current_channel &&
-       client->current_win->current_channel->on_channel == TRUE) {
-      silc_print(client, "> %s", data);
-      silc_client_packet_send_to_channel(client, 
-                                        client->current_win->sock,
-                                        client->current_win->current_channel,
-                                        data, strlen(data), TRUE);
-    }
-  }
-
- out:
-  /* Clear the input buffer */
-  silc_buffer_clear(client->input_buffer);
-  silc_buffer_pull_tail(client->input_buffer, 
-                       SILC_BUFFER_END(client->input_buffer));
-}
-
-/* Returns the command fetched from user typed command line */
-
-static char *silc_client_parse_command(unsigned char *buffer)
-{
-  char *ret;
-  const char *cp = buffer;
-  int len;
-
-  len = strcspn(cp, " ");
-  ret = silc_to_upper((char *)++cp);
-  ret[len - 1] = 0;
-
-  return ret;
-}
-
-/* Parses user typed command line. At most `max_args' is taken. Rest
-   of the line will be allocated as the last argument if there are more
-   than `max_args' arguments in the line. Note that the command name
-   is counted as one argument and is saved. */
-
-void silc_client_parse_command_line(unsigned char *buffer, 
-                                   unsigned char ***parsed,
-                                   unsigned int **parsed_lens,
-                                   unsigned int **parsed_types,
-                                   unsigned int *parsed_num,
-                                   unsigned int max_args)
-{
-  int i, len = 0;
-  int argc = 0;
-  const char *cp = buffer;
-
-  /* Take the '/' away */
-  cp++;
-
-  *parsed = silc_calloc(1, sizeof(**parsed));
-  *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
-
-  /* Get the command first */
-  len = strcspn(cp, " ");
-  (*parsed)[0] = silc_to_upper((char *)cp);
-  (*parsed_lens)[0] = len;
-  cp += len + 1;
-  argc++;
-
-  /* Parse arguments */
-  if (strchr(cp, ' ') || strlen(cp) != 0) {
-    for (i = 1; i < max_args; i++) {
-
-      if (i != max_args - 1)
-       len = strcspn(cp, " ");
-      else
-       len = strlen(cp);
-      
-      *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
-      *parsed_lens = silc_realloc(*parsed_lens, 
-                                 sizeof(**parsed_lens) * (argc + 1));
-      (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
-      memcpy((*parsed)[argc], cp, len);
-      (*parsed_lens)[argc] = len;
-      argc++;
-
-      cp += len;
-      if (strlen(cp) == 0)
-       break;
-      else
-       cp++;
-    }
-  }
-
-  /* Save argument types. Protocol defines all argument types but
-     this implementation makes sure that they are always in correct
-     order hence this simple code. */
-  *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
-  for (i = 0; i < argc; i++)
-    (*parsed_types)[i] = i;
-
-  *parsed_num = argc;
-}
-
-/* Updates clock on the screen every minute. */
-
-SILC_TASK_CALLBACK(silc_client_update_clock)
-{
-  SilcClient client = (SilcClient)context;
-
-  /* Update the clock on the screen */
-  silc_screen_print_clock(client->screen);
-
-  /* Re-register this same task */
-  silc_task_register(qptr, 0, silc_client_update_clock, context, 
-                    silc_client_time_til_next_min(), 0,
-                    SILC_TASK_TIMEOUT,
-                    SILC_TASK_PRI_LOW);
-
-  silc_screen_refresh_win(client->screen->input_win);
-}
-
-/* Runs commands user configured in configuration file. This is
-   called when initializing client. */
-
-SILC_TASK_CALLBACK(silc_client_run_commands)
-{
-  SilcClient client = (SilcClient)context;
-  SilcClientConfigSectionCommand *cs;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  cs = client->config->commands;
-  while(cs) {
-    unsigned int argc = 0;
-    unsigned char **argv, *tmpcmd;
-    unsigned int *argv_lens, *argv_types;
-    SilcClientCommand *cmd;
-    SilcClientCommandContext ctx;
-
-    /* Get the command */
-    tmpcmd = silc_client_parse_command(cs->command);
-
-    for (cmd = silc_command_list; cmd->name; cmd++) {
-      if (!strcmp(cmd->name, tmpcmd))
-       break;
-    }
-    
-    if (cmd->name == NULL) {
-      silc_say(client, "Invalid command: %s", tmpcmd);
-      silc_free(tmpcmd);
-      continue;
-    }
-    
-    /* Now parse all arguments */
-    silc_client_parse_command_line(cs->command, &argv, &argv_lens, 
-                                  &argv_types, &argc, cmd->max_args);
-    silc_free(tmpcmd);
-
-    SILC_LOG_DEBUG(("Exeuting command: %s", cmd->name));
-
-    /* Allocate command context. This and its internals must be free'd 
-       by the command routine receiving it. */
-    ctx = silc_calloc(1, sizeof(*ctx));
-    ctx->client = client;
-    ctx->sock = client->current_win->sock;
-    ctx->argc = argc;
-    ctx->argv = argv;
-    ctx->argv_lens = argv_lens;
-    ctx->argv_types = argv_types;
-
-    /* Execute command */
-    (*cmd->cb)(ctx);
-
-    cs = cs->next;
-  }
-}
-
-/* Internal context for connection process. This is needed as we
-   doing asynchronous connecting. */
-typedef struct {
-  SilcClient client;
-  SilcTask task;
-  int sock;
-  char *host;
-  int port;
-  int tries;
-} SilcClientInternalConnectContext;
-
-static int 
-silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
-{
-  int sock;
-
-  /* XXX In the future we should give up this non-blocking connect all
-     together and use threads instead. */
-  /* Create connection to server asynchronously */
-  sock = silc_net_create_connection_async(ctx->port, ctx->host);
-  if (sock < 0)
-    return -1;
-
-  /* Register task that will receive the async connect and will
-     read the result. */
-  ctx->task = silc_task_register(ctx->client->io_queue, sock, 
-                                silc_client_connect_to_server_start,
-                                (void *)ctx, 0, 0, 
-                                SILC_TASK_FD,
-                                SILC_TASK_PRI_NORMAL);
-  silc_task_reset_iotype(ctx->task, SILC_TASK_WRITE);
-  silc_schedule_set_listen_fd(sock, ctx->task->iomask);
-
-  ctx->sock = sock;
-
-  return sock;
-}
-
-/* Connects to remote server */
-
-int silc_client_connect_to_server(SilcClient client, int port,
-                                 char *host)
-{
-  SilcClientInternalConnectContext *ctx;
-
-  SILC_LOG_DEBUG(("Connecting to port %d of server %s",
-                 port, host));
-
-  silc_say(client, "Connecting to port %d of server %s", port, host);
-
-  client->current_win->remote_host = strdup(host);
-  client->current_win->remote_port = port;
-
-  /* Allocate internal context for connection process. This is
-     needed as we are doing async connecting. */
-  ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->client = client;
-  ctx->host = strdup(host);
-  ctx->port = port;
-  ctx->tries = 0;
-
-  /* Do the actual connecting process */
-  return silc_client_connect_to_server_internal(ctx);
-}
-
-/* Start of the connection to the remote server. This is called after
-   succesful TCP/IP connection has been established to the remote host. */
-
-SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
-{
-  SilcClientInternalConnectContext *ctx =
-    (SilcClientInternalConnectContext *)context;
-  SilcClient client = ctx->client;
-  SilcProtocol protocol;
-  SilcClientKEInternalContext *proto_ctx;
-  int opt, opt_len = sizeof(opt);
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Check the socket status as it might be in error */
-  getsockopt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
-  if (opt != 0) {
-    if (ctx->tries < 2) {
-      /* Connection failed but lets try again */
-      silc_say(ctx->client, "Could not connect to server %s: %s",
-              ctx->host, strerror(opt));
-      silc_say(client, "Connecting to port %d of server %s resumed", 
-              ctx->port, ctx->host);
-
-      /* Unregister old connection try */
-      silc_schedule_unset_listen_fd(fd);
-      silc_net_close_connection(fd);
-      silc_task_unregister(client->io_queue, ctx->task);
-
-      /* Try again */
-      silc_client_connect_to_server_internal(ctx);
-      ctx->tries++;
-    } else {
-      /* Connection failed and we won't try anymore */
-      silc_say(ctx->client, "Could not connect to server %s: %s",
-              ctx->host, strerror(opt));
-      silc_schedule_unset_listen_fd(fd);
-      silc_net_close_connection(fd);
-      silc_task_unregister(client->io_queue, ctx->task);
-      silc_free(ctx);
-    }
-    return;
-  }
-
-  silc_schedule_unset_listen_fd(fd);
-  silc_task_unregister(client->io_queue, ctx->task);
-  silc_free(ctx);
-
-  /* Allocate new socket connection object */
-  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, 
-                   (void *)client->current_win, 
-                   &client->current_win->sock);
-  if (client->current_win->sock == NULL) {
-    silc_say(client, "Error: Could not allocate connection socket");
-    silc_net_close_connection(fd);
-    return;
-  }
-  client->current_win->sock->hostname = client->current_win->remote_host;
-  client->current_win->sock->port = client->current_win->remote_port;
-
-  /* Allocate internal Key Exchange context. This is sent to the
-     protocol as context. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = (void *)client;
-  proto_ctx->sock = client->current_win->sock;
-  proto_ctx->rng = client->rng;
-  proto_ctx->responder = FALSE;
-
-  /* Perform key exchange protocol. silc_client_connect_to_server_final
-     will be called after the protocol is finished. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
-                     &protocol, (void *)proto_ctx,
-                     silc_client_connect_to_server_second);
-  if (!protocol) {
-    silc_say(client, "Error: Could not start authentication protocol");
-    return;
-  }
-  client->current_win->sock->protocol = protocol;
-
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it will 
-     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  context = (void *)client;
-  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd);
-
-  /* Execute the protocol */
-  protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
-}
-
-/* Second part of the connecting to the server. This executed 
-   authentication protocol. */
-
-SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcSocketConnection sock = NULL;
-  SilcClientConnAuthInternalContext *proto_ctx;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
-    /* Error occured during protocol */
-    SILC_LOG_DEBUG(("Error during KE protocol"));
-    silc_protocol_free(protocol);
-    if (ctx->packet)
-      silc_buffer_free(ctx->packet);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    silc_free(ctx);
-    sock->protocol = NULL;
-    return;
-  }
-
-  /* Allocate internal context for the authentication protocol. This
-     is sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->client = (void *)client;
-  proto_ctx->sock = sock = ctx->sock;
-  proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
-
-  /* Resolve the authentication method to be used in this connection */
-  proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
-  if (client->config->conns) {
-    SilcClientConfigSectionConnection *conn = NULL;
-
-    /* Check if we find a match from user configured connections */
-    conn = silc_client_config_find_connection(client->config,
-                                             sock->hostname,
-                                             sock->port);
-    if (conn) {
-      /* Match found. Use the configured authentication method */
-      proto_ctx->auth_meth = conn->auth_meth;
-      if (conn->auth_data) {
-       proto_ctx->auth_data = strdup(conn->auth_data);
-       proto_ctx->auth_data_len = strlen(conn->auth_data);
-      }
-    } else {
-      /* No match found. Resolve by sending AUTH_REQUEST to server */
-      proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
-    }
-  } else {
-    /* XXX Resolve by sending AUTH_REQUEST to server */
-    proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_NONE;
-  }
-
-  /* Free old protocol as it is finished now */
-  silc_protocol_free(protocol);
-  if (ctx->packet)
-    silc_buffer_free(ctx->packet);
-  silc_free(ctx);
-  /* silc_free(ctx->keymat....); */
-  sock->protocol = NULL;
-
-  /* Allocate the authentication protocol. This is allocated here
-     but we won't start it yet. We will be receiving party of this
-     protocol thus we will wait that connecting party will make
-     their first move. */
-  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
-                     &sock->protocol, (void *)proto_ctx, 
-                     silc_client_connect_to_server_final);
-
-  /* Execute the protocol */
-  sock->protocol->execute(client->timeout_queue, 0, sock->protocol, fd, 0, 0);
-}
-
-/* Finalizes the connection to the remote SILC server. This is called
-   after authentication protocol has been completed. This send our
-   user information to the server to receive our client ID from
-   server. */
-
-SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx = 
-    (SilcClientConnAuthInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcClientWindow win = (SilcClientWindow)ctx->sock->user_data;
-  SilcBuffer packet;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
-    /* Error occured during protocol */
-    SILC_LOG_DEBUG(("Error during authentication protocol"));
-    silc_protocol_free(protocol);
-    if (ctx->auth_data)
-      silc_free(ctx->auth_data);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    silc_free(ctx);
-    win->sock->protocol = NULL;
-    return;
-  }
-
-  /* Send NEW_CLIENT packet to the server. We will become registered
-     to the SILC network after sending this packet and we will receive
-     client ID from the server. */
-  packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + 
-                            strlen(client->realname));
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(strlen(client->username)),
-                    SILC_STR_UI_XNSTRING(client->username,
-                                         strlen(client->username)),
-                    SILC_STR_UI_SHORT(strlen(client->realname)),
-                    SILC_STR_UI_XNSTRING(client->realname,
-                                         strlen(client->realname)),
-                    SILC_STR_END);
-
-  /* Send the packet */
-  silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
-                         NULL, 0, NULL, NULL, 
-                         packet->data, packet->len, TRUE);
-  silc_buffer_free(packet);
-
-  silc_say(client, "Connected to port %d of host %s",
-          win->remote_port, win->remote_host);
-
-  client->screen->bottom_line->connection = win->remote_host;
-  silc_screen_print_bottom_line(client->screen, 0);
-
-  silc_protocol_free(protocol);
-  if (ctx->auth_data)
-    silc_free(ctx->auth_data);
-  if (ctx->ske)
-    silc_ske_free(ctx->ske);
-  silc_free(ctx);
-  win->sock->protocol = NULL;
-}
-
-typedef struct {
-  SilcPacketContext *packetdata;
-  SilcSocketConnection sock;
-  SilcClient client;
-} SilcClientInternalPacket;
-
-SILC_TASK_CALLBACK(silc_client_packet_process)
-{
-  SilcClient client = (SilcClient)context;
-  SilcSocketConnection sock = NULL;
-  int ret, packetlen, paddedlen;
-
-  SILC_LOG_DEBUG(("Processing packet"));
-
-  SILC_CLIENT_GET_SOCK(client, fd, sock);
-  if (sock == NULL)
-    return;
-
-  /* Packet sending */
-  if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
-
-    if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
-
-    /* Write the packet out to the connection */
-    ret = silc_packet_write(fd, sock->outbuf);
-
-    /* If returned -2 could not write to connection now, will do
-       it later. */
-    if (ret == -2)
-      return;
-    
-    /* Error */
-    if (ret == -1)
-      SILC_LOG_ERROR(("Packet dropped"));
-
-    /* The packet has been sent and now it is time to set the connection
-       back to only for input. When there is again some outgoing data 
-       available for this connection it will be set for output as well. 
-       This call clears the output setting and sets it only for input. */
-    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd);
-    SILC_UNSET_OUTBUF_PENDING(sock);
-
-    return;
-  }
-
-  /* Packet receiving */
-  if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
-
-    /* Allocate the incoming data buffer if not done already. */
-    if (!sock->inbuf)
-      sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
-
-    /* Read some data from connection */
-    ret = silc_packet_read(fd, sock->inbuf);
-    
-    /* If returned -2 data was not available now, will read it later. */
-    if (ret == -2)
-      return;
-    
-    /* Error */
-    if (ret == -1) {
-      SILC_LOG_ERROR(("Packet dropped"));
-      return;
-    }
-    
-    /* EOF */
-    if (ret == 0) {
-      SILC_LOG_DEBUG(("Read EOF"));
-
-      /* If connection is disconnecting already we will finally
-        close the connection */
-      if (SILC_IS_DISCONNECTING(sock)) {
-       silc_client_close_connection(client, sock);
-       return;
-      }
-      
-      silc_say(client, "Connection closed: premature EOF");
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
-
-      silc_client_close_connection(client, sock);
-      return;
-    }
-
-    /* Check whether we received a whole packet. If reading went without
-       errors we either read a whole packet or the read packet is 
-       incorrect and will be dropped. */
-    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-    if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) {
-      SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-      silc_buffer_clear(sock->inbuf);
-      return;
-    }
-    
-    /* Decrypt a packet coming from server connection */
-    if (sock->type == SILC_SOCKET_TYPE_SERVER ||
-       sock->type == SILC_SOCKET_TYPE_ROUTER) {
-      SilcClientWindow win = (SilcClientWindow)sock->user_data;
-      SilcClientInternalPacket *packet;
-      int mac_len = 0;
-
-      if (win->hmac)
-       mac_len = win->hmac->hash->hash->hash_len;
-
-      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-       /* Received possibly many packets at once */
-
-       while(sock->inbuf->len > 0) {
-         SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-         if (sock->inbuf->len < paddedlen) {
-           SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-           return;
-         }
-
-         paddedlen += 2;
-         packet = silc_calloc(1, sizeof(*packet));
-         packet->client = client;
-         packet->sock = sock;
-         packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-         packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
-         silc_buffer_pull_tail(packet->packetdata->buffer, 
-                               SILC_BUFFER_END(packet->packetdata->buffer));
-         silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
-                         paddedlen + mac_len);
-
-         SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
-         SILC_LOG_DEBUG(("Packet from server %s, "
-                         "server type %d, packet length %d", 
-                         win->remote_host, win->remote_type, paddedlen));
-
-         /* If this packet is for the current active connection we will
-            parse the packet right away to get it quickly on the screen.
-            Otherwise, it will be parsed with a timeout as the data is
-            for inactive window (which might not be visible at all). */
-         if (SILC_CLIENT_IS_CURRENT_WIN(client, win)) {
-           /* Parse it real soon */
-           silc_task_register(client->timeout_queue, fd, 
-                              silc_client_packet_parse,
-                              (void *)packet, 0, 1, 
-                              SILC_TASK_TIMEOUT,
-                              SILC_TASK_PRI_NORMAL);
-         } else {
-           /* Parse the packet with timeout */
-           silc_task_register(client->timeout_queue, fd, 
-                              silc_client_packet_parse,
-                              (void *)packet, 0, 200000, 
-                              SILC_TASK_TIMEOUT,
-                              SILC_TASK_PRI_NORMAL);
-         }
-
-         /* Pull the packet from inbuf thus we'll get the next one
-            in the inbuf. */
-         silc_buffer_pull(sock->inbuf, paddedlen);
-         if (win->hmac)
-           silc_buffer_pull(sock->inbuf, mac_len);
-       }
-       silc_buffer_clear(sock->inbuf);
-       return;
-      } else {
-       /* Received one packet */
-       
-       SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
-                        sock->inbuf->data, sock->inbuf->len);
-       SILC_LOG_DEBUG(("Packet from server %s, "
-                       "server type %d, packet length %d", 
-                       win->remote_host, win->remote_type, paddedlen));
-       
-       packet = silc_calloc(1, sizeof(*packet));
-       packet->client = client;
-       packet->sock = sock;
-       packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-       packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-       silc_buffer_clear(sock->inbuf);
-
-       /* If this packet is for the current active connection we will
-          parse the packet right away to get it quickly on the screen.
-          Otherwise, it will be parsed with a timeout as the data is
-          for inactive window (which might not be visible at all). */
-       if (SILC_CLIENT_IS_CURRENT_WIN(client, win)) {
-         /* Parse it real soon */
-         silc_task_register(client->timeout_queue, fd, 
-                            silc_client_packet_parse,
-                            (void *)packet, 0, 1, 
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
-         return;
-       } else {
-         /* Parse the packet with timeout */
-         silc_task_register(client->timeout_queue, fd, 
-                            silc_client_packet_parse,
-                            (void *)packet, 0, 200000, 
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
-         return;
-       }
-      }
-    }
-  }
-  
-  SILC_LOG_ERROR(("Weird, nothing happened - ignoring"));
-}
-
-/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
-   after packet has been totally decrypted and parsed. */
-
-static int silc_client_packet_check_mac(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer buffer)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-
-  /* Check MAC */
-  if (win->hmac) {
-    int headlen = buffer->data - buffer->head, mac_len;
-    unsigned char *packet_mac, mac[32];
-    
-    SILC_LOG_DEBUG(("Verifying MAC"));
-
-    mac_len = win->hmac->hash->hash->hash_len;
-
-    silc_buffer_push(buffer, headlen);
-
-    /* Take mac from packet */
-    packet_mac = buffer->tail;
-    
-    /* Make MAC and compare */
-    memset(mac, 0, sizeof(mac));
-    silc_hmac_make_with_key(win->hmac, 
-                           buffer->data, buffer->len,
-                           win->hmac_key, win->hmac_key_len, mac);
-#if 0
-    SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len);
-    SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len);
-#endif
-    if (memcmp(mac, packet_mac, mac_len)) {
-      SILC_LOG_DEBUG(("MAC failed"));
-      return FALSE;
-    }
-    
-    SILC_LOG_DEBUG(("MAC is Ok"));
-    memset(mac, 0, sizeof(mac));
-
-    silc_buffer_pull(buffer, headlen);
-  }
-  
-  return TRUE;
-}
-
-/* Decrypts rest of the packet (after decrypting just the SILC header).
-   After calling this function the packet is ready to be parsed by calling 
-   silc_packet_parse. */
-
-static int silc_client_packet_decrypt_rest(SilcClient client, 
-                                          SilcSocketConnection sock,
-                                          SilcBuffer buffer)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  unsigned int mac_len = 0;
-  
-  /* Decrypt */
-  if (win && win->receive_key) {
-
-    /* Pull MAC from packet before decryption */
-    if (win->hmac) {
-      mac_len = win->hmac->hash->hash->hash_len;
-      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, mac_len);
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-
-    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
-
-    /* Decrypt rest of the packet */
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    silc_packet_decrypt(win->receive_key, buffer, buffer->len);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
-                    buffer->data, buffer->len);
-  }
-
-  return TRUE;
-}
-
-/* Decrypts rest of the SILC Packet header that has been decrypted partly
-   already. This decrypts the padding of the packet also.  After calling 
-   this function the packet is ready to be parsed by calling function 
-   silc_packet_parse. This is used in special packet reception. */
-
-static int silc_client_packet_decrypt_rest_special(SilcClient client, 
-                                                 SilcSocketConnection sock,
-                                                 SilcBuffer buffer)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  unsigned int mac_len = 0;
-
-  /* Decrypt rest of the header plus padding */
-  if (win && win->receive_key) {
-    unsigned short truelen, len1, len2, padlen;
-
-    /* Pull MAC from packet before decryption */
-    if (win->hmac) {
-      mac_len = win->hmac->hash->hash->hash_len;
-      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, mac_len);
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-  
-    SILC_LOG_DEBUG(("Decrypting rest of the header"));
-
-    SILC_GET16_MSB(len1, &buffer->data[4]);
-    SILC_GET16_MSB(len2, &buffer->data[6]);
-
-    truelen = SILC_PACKET_HEADER_LEN + len1 + len2;
-    padlen = SILC_PACKET_PADLEN(truelen);
-    len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    SILC_LOG_HEXDUMP(("XXX"), buffer->data, buffer->len);
-    silc_packet_decrypt(win->receive_key, buffer, len1);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    SILC_LOG_HEXDUMP(("XXX"), buffer->data, buffer->len);
-  }
-
-  return TRUE;
-}
-
-/* Parses whole packet, received earlier. */
-
-SILC_TASK_CALLBACK(silc_client_packet_parse)
-{
-  SilcClientInternalPacket *packet = (SilcClientInternalPacket *)context;
-  SilcBuffer buffer = packet->packetdata->buffer;
-  SilcClient client = packet->client;
-  SilcSocketConnection sock = packet->sock;
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  int ret;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Decrypt start of the packet header */
-  if (win && win->receive_key)
-    silc_packet_decrypt(win->receive_key, buffer, SILC_PACKET_MIN_HEADER_LEN);
-
-  /* If the packet type is not any special type lets decrypt rest
-     of the packet here. */
-  if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE &&
-      buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) {
-  normal:
-    /* Normal packet, decrypt rest of the packet */
-    if (!silc_client_packet_decrypt_rest(client, sock, buffer))
-      goto out;
-
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet->packetdata);
-    if (ret == SILC_PACKET_NONE)
-      goto out;
-
-    /* Check MAC */
-    if (!silc_client_packet_check_mac(client, sock, buffer))
-      goto out;
-  } else {
-    /* If private message key is not set for private message it is
-       handled as normal packet. Go back up. */
-    if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-       !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
-      goto normal;
-
-    /* Packet requires special handling, decrypt rest of the header.
-       This only decrypts. This does not do any MAC checking, it must
-       be done individually later when doing the special processing. */
-    silc_client_packet_decrypt_rest_special(client, sock, buffer);
-
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet->packetdata);
-    if (ret == SILC_PACKET_NONE)
-      goto out;
-  }
-
-  /* Parse the incoming packet type */
-  silc_client_packet_parse_type(client, sock, packet->packetdata);
-
- out:
-  silc_buffer_clear(packet->packetdata->buffer);
-  silc_free(packet->packetdata);
-  silc_free(packet);
-}
-
-/* Parses the packet type and calls what ever routines the packet type
-   requires. This is done for all incoming packets. */
-
-void silc_client_packet_parse_type(SilcClient client, 
-                                  SilcSocketConnection sock,
-                                  SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcPacketType type = packet->type;
-
-  SILC_LOG_DEBUG(("Parsing packet type %d", type));
-
-  /* Parse the packet type */
-  switch(type) {
-  case SILC_PACKET_DISCONNECT:
-    silc_client_disconnected_by_server(client, sock, buffer);
-    break;
-  case SILC_PACKET_SUCCESS:
-    /*
-     * Success received for something. For now we can have only
-     * one protocol for connection executing at once hence this
-     * success message is for whatever protocol is executing currently.
-     */
-    if (sock->protocol) {
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
-    break;
-  case SILC_PACKET_FAILURE:
-    /*
-     * Failure received for some protocol. Set the protocol state to 
-     * error and call the protocol callback. This fill cause error on
-     * protocol and it will call the final callback.
-     */
-    if (sock->protocol) {
-      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
-    break;
-  case SILC_PACKET_REJECT:
-    break;
-
-  case SILC_PACKET_NOTIFY:
-    /*
-     * Received notify message 
-     */
-    silc_client_notify_by_server(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_ERROR:
-    /*
-     * Received error message
-     */
-    silc_client_error_by_server(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_CHANNEL_MESSAGE:
-    /*
-     * Received message to (from, actually) a channel
-     */
-    silc_client_channel_message(client, sock, packet);
-    break;
-  case SILC_PACKET_CHANNEL_KEY:
-    /*
-     * Received key for a channel. By receiving this key the client will be
-     * able to talk to the channel it has just joined. This can also be
-     * a new key for existing channel as keys expire peridiocally.
-     */
-    silc_client_receive_channel_key(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_PRIVATE_MESSAGE:
-    /*
-     * Received private message
-     */
-    {
-      SilcClientCommandReplyContext ctx;
-      ctx = silc_calloc(1, sizeof(*ctx));
-      ctx->client = client;
-      ctx->sock = sock;
-      ctx->context = buffer;   /* kludge */
-      silc_client_command_reply_msg((void *)ctx);
-    }
-    break;
-  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
-    /*
-     * Received private message key
-     */
-    break;
-
-  case SILC_PACKET_COMMAND_REPLY:
-    /*
-     * Recived reply for a command
-     */
-    silc_client_command_reply_process(client, sock, buffer);
-    break;
-
-  case SILC_PACKET_KEY_EXCHANGE:
-    if (sock->protocol) {
-      SilcClientKEInternalContext *proto_ctx = 
-       (SilcClientKEInternalContext *)sock->protocol->context;
-
-      proto_ctx->packet = buffer;
-
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
-                     "protocol active, packet dropped."));
-
-      /* XXX Trigger KE protocol?? Rekey actually! */
-    }
-    break;
-
-  case SILC_PACKET_KEY_EXCHANGE_1:
-    if (sock->protocol) {
-
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
-                     "protocol active, packet dropped."));
-    }
-    break;
-  case SILC_PACKET_KEY_EXCHANGE_2:
-    if (sock->protocol) {
-      SilcClientKEInternalContext *proto_ctx = 
-       (SilcClientKEInternalContext *)sock->protocol->context;
-
-      if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
-
-      proto_ctx->packet = buffer;
-
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(client->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    } else {
-      SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
-                     "protocol active, packet dropped."));
-    }
-    break;
-
-  case SILC_PACKET_NEW_ID:
-    {
-      /*
-       * Received new ID from server. This packet is received at
-       * the connection to the server.  New ID is also received when 
-       * user changes nickname but in that case the new ID is received
-       * as command reply and not as this packet type.
-       */
-      unsigned char *id_string;
-      unsigned short id_type;
-      
-      silc_buffer_unformat(buffer,
-                          SILC_STR_UI_SHORT(&id_type),
-                          SILC_STR_UI16_STRING_ALLOC(&id_string),
-                          SILC_STR_END);
-      
-      if ((SilcIdType)id_type != SILC_ID_CLIENT)
-       break;
-
-      silc_client_receive_new_id(client, sock, id_string);
-      silc_free(id_string);
-      break;
-    }
-
-  default:
-    SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
-    break;
-  }
-}
-
-/* Internal routine that sends packet or marks packet to be sent. This
-   is used directly only in special cases. Normal cases should use
-   silc_server_packet_send. Returns < 0 on error. */
-
-static int silc_client_packet_send_real(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       int force_send)
-{
-  /* Send now if forced to do so */
-  if (force_send == TRUE) {
-    int ret;
-    SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
-    ret = silc_packet_write(sock->sock, sock->outbuf);
-
-    if (ret == -1)
-      SILC_LOG_ERROR(("Packet dropped"));
-    if (ret != -2)
-      return ret;
-
-    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
-  }  
-
-  SILC_LOG_DEBUG(("Packet in queue"));
-
-  /* Mark that there is some outgoing data available for this connection. 
-     This call sets the connection both for input and output (the input
-     is set always and this call keeps the input setting, actually). 
-     Actual data sending is performed by silc_client_packet_process. */
-  SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(sock->sock);
-
-  /* Mark to socket that data is pending in outgoing buffer. This flag
-     is needed if new data is added to the buffer before the earlier
-     put data is sent to the network. */
-  SILC_SET_OUTBUF_PENDING(sock);
-
-  return 0;
-}
-
-/* Prepare outgoing data buffer for packet sending. */
-
-static void silc_client_packet_send_prepare(SilcClient client,
-                                           SilcSocketConnection sock,
-                                           unsigned int header_len,
-                                           unsigned int padlen,
-                                           unsigned int data_len)
-{
-  int totlen, oldlen;
-
-  totlen = header_len + padlen + data_len;
-
-  /* Prepare the outgoing buffer for packet sending. */
-  if (!sock->outbuf) {
-    /* Allocate new buffer. This is done only once per connection. */
-    SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
-    
-    sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
-    silc_buffer_pull_tail(sock->outbuf, totlen);
-    silc_buffer_pull(sock->outbuf, header_len + padlen);
-  } else {
-    if (SILC_IS_OUTBUF_PENDING(sock)) {
-      /* There is some pending data in the buffer. */
-
-      if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
-       SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
-       /* XXX: not done yet */
-      }
-      oldlen = sock->outbuf->len;
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
-    } else {
-      /* Buffer is free for use */
-      silc_buffer_clear(sock->outbuf);
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen);
-    }
-  }
-}
-
-/* Sends packet. This doesn't actually send the packet instead it assembles
-   it and marks it to be sent. However, if force_send is TRUE the packet
-   is sent immediately. if dst_id, cipher and hmac are NULL those parameters
-   will be derived from sock argument. Otherwise the valid arguments sent
-   are used. */
-
-void silc_client_packet_send(SilcClient client, 
-                            SilcSocketConnection sock,
-                            SilcPacketType type, 
-                            void *dst_id,
-                            SilcIdType dst_id_type,
-                            SilcCipher cipher,
-                            SilcHmac hmac,
-                            unsigned char *data, 
-                            unsigned int data_len, 
-                            int force_send)
-{
-  SilcPacketContext packetdata;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
-
-  /* Get data used in the packet sending, keys and stuff */
-  if ((!cipher || !hmac || !dst_id) && sock->user_data) {
-    if (!cipher && ((SilcClientWindow)sock->user_data)->send_key)
-      cipher = ((SilcClientWindow)sock->user_data)->send_key;
-    if (!hmac && ((SilcClientWindow)sock->user_data)->hmac) {
-      hmac = ((SilcClientWindow)sock->user_data)->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = ((SilcClientWindow)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcClientWindow)sock->user_data)->hmac_key_len;
-    }
-    if (!dst_id && ((SilcClientWindow)sock->user_data)->remote_id) {
-      dst_id = ((SilcClientWindow)sock->user_data)->remote_id;
-      dst_id_type = SILC_ID_SERVER;
-    }
-  }
-
-  /* Set the packet context pointers */
-  packetdata.flags = 0;
-  packetdata.type = type;
-  if (((SilcClientWindow)sock->user_data)->local_id_data)
-    packetdata.src_id = ((SilcClientWindow)sock->user_data)->local_id_data;
-  else 
-    packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  if (dst_id) {
-    packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type);
-    packetdata.dst_id_len = silc_id_get_len(dst_id_type);
-    packetdata.dst_id_type = dst_id_type;
-  } else {
-    packetdata.dst_id = NULL;
-    packetdata.dst_id_len = 0;
-    packetdata.dst_id_type = SILC_ID_NONE;
-  }
-  packetdata.rng = client->rng;
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_client_packet_send_prepare(client, sock, 
-                                 SILC_PACKET_HEADER_LEN +
-                                 packetdata.src_id_len + 
-                                 packetdata.dst_id_len,
-                                 packetdata.padlen,
-                                 data_len);
-
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Compute MAC of the packet */
-  if (hmac) {
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-  }
-
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
-
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
-
-  SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-}
-
-/* Sends packet to a channel. Packet to channel is always encrypted
-   differently from "normal" packets. SILC header of the packet is 
-   encrypted with the next receiver's key and the rest of the packet is
-   encrypted with the channel specific key. Padding and HMAC is computed
-   with the next receiver's key. */
-
-void silc_client_packet_send_to_channel(SilcClient client, 
-                                       SilcSocketConnection sock,
-                                       SilcChannelEntry channel,
-                                       unsigned char *data, 
-                                       unsigned int data_len, 
-                                       int force_send)
-{
-  int i;
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  SilcBuffer payload;
-  SilcPacketContext packetdata;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  unsigned char *id_string;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  if (!channel || !channel->key) {
-    silc_say(client, "Cannot talk to channel: key does not exist");
-    return;
-  }
-
-  /* Generate IV */
-  if (!channel->iv)
-    for (i = 0; i < 16; i++)
-      channel->iv[i] = silc_rng_get_byte(client->rng);
-  else
-    silc_hash_make(client->md5hash, channel->iv, 16, channel->iv);
-
-  /* Encode the channel payload */
-  payload = silc_channel_encode_payload(strlen(win->nickname), win->nickname,
-                                       data_len, data, 16, channel->iv, 
-                                       client->rng);
-  if (!payload) {
-    silc_say(client, 
-            "Error: Could not create packet to be sent to the channel");
-    return;
-  }
-
-  /* Get data used in packet header encryption, keys and stuff. Rest
-     of the packet (the payload) is, however, encrypted with the 
-     specified channel key. */
-  cipher = win->send_key;
-  hmac = win->hmac;
-  mac_len = hmac->hash->hash->hash_len;
-  hmac_key = win->hmac_key;
-  hmac_key_len = win->hmac_key_len;
-  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-
-  /* Set the packet context pointers. The destination ID is always
-     the Channel ID of the channel. Server and router will handle the
-     distribution of the packet. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = win->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  packetdata.dst_id = id_string;
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = client->rng;
-  packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_client_packet_send_prepare(client, sock, 
-                                 SILC_PACKET_HEADER_LEN +
-                                 packetdata.src_id_len + 
-                                 packetdata.dst_id_len,
-                                 packetdata.padlen,
-                                 payload->len);
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Encrypt payload of the packet. This is encrypted with the channel key. */
-  channel->channel_key->cipher->encrypt(channel->channel_key->context,
-                                       payload->data, payload->data,
-                                       payload->len - 16, /* -IV_LEN */
-                                       channel->iv);
-
-  SILC_LOG_HEXDUMP(("XXX"), payload->data, payload->len);
-      
-  /* Put the actual encrypted payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, payload->data, payload->len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Compute MAC of the packet */
-  silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                         hmac_key, hmac_key_len, mac);
-  silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-  memset(mac, 0, sizeof(mac));
-
-      SILC_LOG_HEXDUMP(("XXX"), sock->outbuf->data, sock->outbuf->len);
-      
-  /* Encrypt the header and padding of the packet. This is encrypted 
-     with normal session key shared with our server. */
-  silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  /* Pull MAC into the visible data area */
-  silc_buffer_pull_tail(sock->outbuf, mac_len);
-
-  SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-  silc_buffer_free(payload);
-  silc_free(id_string);
-}
-
-/* Sends private message to remote client. If private message key has
-   not been set with this client then the message will be encrypted using
-   normal session keys. Private messages are special packets in SILC
-   network hence we need this own function for them. This is similiar
-   to silc_client_packet_send_to_channel except that we send private
-   message. */
-
-void silc_client_packet_send_private_message(SilcClient client,
-                                            SilcSocketConnection sock,
-                                            SilcClientEntry client_entry,
-                                            unsigned char *data, 
-                                            unsigned int data_len, 
-                                            int force_send)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  SilcBuffer buffer;
-  SilcPacketContext packetdata;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  unsigned int nick_len;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Sending private message"));
-
-  /* Create private message payload */
-  nick_len = strlen(client->current_win->nickname);
-  buffer = silc_buffer_alloc(2 + nick_len + data_len);
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-  silc_buffer_format(buffer,
-                    SILC_STR_UI_SHORT(nick_len),
-                    SILC_STR_UI_XNSTRING(client->current_win->nickname,
-                                         nick_len),
-                    SILC_STR_UI_XNSTRING(data, data_len),
-                    SILC_STR_END);
-
-  /* If we don't have private message specific key then private messages
-     are just as any normal packet thus call normal packet sending.  If
-     the key exist then the encryption process is a bit different and
-     will be done in the rest of this function. */
-  if (!client_entry->send_key) {
-    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
-                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
-                           buffer->data, buffer->len, force_send);
-    goto out;
-  }
-
-  /* We have private message specific key */
-
-  /* Get data used in the encryption */
-  cipher = client_entry->send_key;
-  hmac = win->hmac;
-  mac_len = hmac->hash->hash->hash_len;
-  hmac_key = win->hmac_key;
-  hmac_key_len = win->hmac_key_len;
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
-  packetdata.src_id = win->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.src_id_type = SILC_ID_CLIENT;
-  if (client_entry)
-    packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  else
-    packetdata.dst_id = win->local_id_data;
-  packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
-  packetdata.dst_id_type = SILC_ID_CLIENT;
-  packetdata.rng = client->rng;
-  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_client_packet_send_prepare(client, sock, 
-                                 SILC_PACKET_HEADER_LEN +
-                                 packetdata.src_id_len + 
-                                 packetdata.dst_id_len,
-                                 packetdata.padlen,
-                                 buffer->len);
-
-  packetdata.buffer = sock->outbuf;
-
-  /* Encrypt payload of the packet. Encrypt with private message specific
-     key if it exist, otherwise with session key. */
-  cipher->cipher->encrypt(cipher->context, buffer->data, buffer->data,
-                         buffer->len, cipher->iv);
-      
-  /* Put the actual encrypted payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
-
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
-
-  /* Compute MAC of the packet */
-  silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                         hmac_key, hmac_key_len, mac);
-  silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-  memset(mac, 0, sizeof(mac));
-
-  SILC_LOG_HEXDUMP(("XXX"), sock->outbuf->data, sock->outbuf->len);
-      
-  /* Encrypt the header and padding of the packet. */
-  silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                     packetdata.src_id_len + packetdata.dst_id_len +
-                     packetdata.padlen);
-
-  /* Pull MAC into the visible data area */
-  silc_buffer_pull_tail(sock->outbuf, mac_len);
-
-  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
-
-  /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send);
-  silc_free(packetdata.dst_id);
-
- out:
-  silc_free(buffer);
-}     
-
-/* Closes connection to remote end. Free's all allocated data except
-   for some information such as nickname etc. that are valid at all time. */
-
-void silc_client_close_connection(SilcClient client,
-                                 SilcSocketConnection sock)
-{
-  SilcClientWindow win;
-  int i;
-
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
-
-  /* Unregister all tasks */
-  silc_task_unregister_by_fd(client->io_queue, sock->sock);
-  silc_task_unregister_by_fd(client->timeout_queue, sock->sock);
-
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-
-  silc_say(client, "Closed connection to host %s", sock->hostname ?
-          sock->hostname : sock->ip);
-
-  /* Free everything */
-  if (sock->user_data) {
-    win = (SilcClientWindow)sock->user_data;
-
-    /* Clear ID caches */
-    for (i = 0; i < 96; i++)
-      silc_idcache_del_all(&win->client_id_cache[i], 
-                          win->client_id_cache_count[i]);
-    for (i = 0; i < 96; i++)
-      silc_idcache_del_all(&win->channel_id_cache[i], 
-                          win->channel_id_cache_count[i]);
-
-    /* Free data */
-    if (win->remote_host)
-      silc_free(win->remote_host);
-    if (win->local_id)
-      silc_free(win->local_id);
-    if (win->local_id_data)
-      silc_free(win->local_id_data);
-    if (win->send_key)
-      silc_cipher_free(win->send_key);
-    if (win->receive_key)
-      silc_cipher_free(win->receive_key);
-    if (win->public_key)
-      silc_pkcs_free(win->public_key);
-    if (win->hmac)
-      silc_hmac_free(win->hmac);
-    if (win->hmac_key) {
-      memset(win->hmac_key, 0, win->hmac_key_len);
-      silc_free(win->hmac_key);
-    }
-
-    win->sock = NULL;
-    win->remote_port = 0;
-    win->remote_type = 0;
-    win->send_key = NULL;
-    win->receive_key = NULL;
-    win->public_key = NULL;
-    win->hmac = NULL;
-    win->hmac_key = NULL;
-    win->hmac_key_len = 0;
-    win->local_id = NULL;
-    win->local_id_data = NULL;
-    win->remote_host = NULL;
-  }
-
-  if (sock->protocol) {
-    silc_protocol_free(sock->protocol);
-    sock->protocol = NULL;
-  }
-  silc_socket_free(sock);
-}
-
-/* Called when we receive disconnection packet from server. This 
-   closes our end properly and displays the reason of the disconnection
-   on the screen. */
-
-void silc_client_disconnected_by_server(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer message)
-{
-  char *msg;
-
-  SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
-
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
-  silc_say(client, msg);
-  silc_free(msg);
-
-  SILC_SET_DISCONNECTED(sock);
-  silc_client_close_connection(client, sock);
-}
-
-/* Received error message from server. Display it on the screen. 
-   We don't take any action what so ever of the error message. */
-
-void silc_client_error_by_server(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcBuffer message)
-{
-  char *msg;
-
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
-  silc_say(client, msg);
-  silc_free(msg);
-}
-
-/* Received notify message from server */
-
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcBuffer message)
-{
-  char *msg;
-
-  msg = silc_calloc(message->len + 1, sizeof(char));
-  memcpy(msg, message->data, message->len);
-  silc_say(client, msg);
-  silc_free(msg);
-}
-
-/* Processes the received new Client ID from server. Old Client ID is
-   deleted from cache and new one is added. */
-
-void silc_client_receive_new_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               unsigned char *id_string)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  char *nickname = win->nickname;
-
-#define CIDC(x) win->client_id_cache[(x) - 32]
-#define CIDCC(x) win->client_id_cache_count[(x) - 32]
-
-  /* Delete old ID from ID cache */
-  silc_idcache_del_by_id(CIDC(nickname[0]), CIDCC(nickname[0]),
-                        SILC_ID_CLIENT, win->local_id);
-  
-  /* Save the new ID */
-  if (win->local_id)
-    silc_free(win->local_id);
-  win->local_id = silc_id_str2id(id_string, SILC_ID_CLIENT);
-  if (win->local_id_data)
-    silc_free(win->local_id_data);
-  win->local_id_data = 
-    silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
-  memcpy(win->local_id_data, id_string, SILC_ID_CLIENT_LEN);
-  win->local_id_data_len = SILC_ID_CLIENT_LEN;
-  if (!win->local_entry)
-    win->local_entry = silc_calloc(1, sizeof(*win->local_entry));
-  win->local_entry->nickname = win->nickname;
-  win->local_entry->id = win->local_id;
-  
-  /* Put it to the ID cache */
-  CIDCC(nickname[0]) = silc_idcache_add(&CIDC(nickname[0]), 
-                                       CIDCC(nickname[0]),
-                                       win->nickname, SILC_ID_CLIENT, 
-                                       win->local_id, 
-                                       (void *)win->local_entry);
-#undef CIDC
-#undef CIDCC
-}
-
-/* Processed received Channel ID for a channel. This is called when client
-   joins to channel and server replies with channel ID. The ID is cached. */
-
-void silc_client_new_channel_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               char *channel_name,
-                               unsigned char *id_string)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  SilcChannelID *id;
-  SilcChannelEntry channel;
-
-  SILC_LOG_DEBUG(("New channel ID"));
-
-#define CIDC(x) win->channel_id_cache[(x) - 32]
-#define CIDCC(x) win->channel_id_cache_count[(x) - 32]
-
-  id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
-  channel = silc_calloc(1, sizeof(*channel));
-  channel->channel_name = channel_name;
-  channel->id = id;
-  win->current_channel = channel;
-  
-  /* Put it to the ID cache */
-  CIDCC(channel_name[0]) = silc_idcache_add(&CIDC(channel_name[0]), 
-                                           CIDCC(channel_name[0]),
-                                           channel_name, SILC_ID_CHANNEL, 
-                                           id, (void *)channel);
-#undef CIDC
-#undef CIDCC
-}
-
-/* Processes received key for channel. The received key will be used
-   to protect the traffic on the channel for now on. Client must receive
-   the key to the channel before talking on the channel is possible. 
-   This is the key that server has generated, this is not the channel
-   private key, it is entirely local setting. */
-
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet)
-{
-  int i;
-  unsigned char *id_string, *key, *cipher;
-  unsigned int key_len;
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  SilcChannelID *id;
-  SilcIDCache *id_cache = NULL;
-  SilcChannelEntry channel;
-  SilcChannelKeyPayload payload;
-
-  SILC_LOG_DEBUG(("Received key for channel"));
-  
-#define CIDC(x) win->channel_id_cache[(x)]
-#define CIDCC(x) win->channel_id_cache_count[(x)]
-
-  payload = silc_channel_key_parse_payload(packet);
-  if (!payload)
-    return;
-
-  id_string = silc_channel_key_get_id(payload, NULL);
-  if (!id_string) {
-    silc_channel_key_free_payload(payload);
-    return;
-  }
-  id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
-
-  /* Find channel. XXX: This is bad and slow. */ 
-  for (i = 0; i < 96; i++) {
-    if (CIDC(i) == NULL)
-      continue;
-    if (silc_idcache_find_by_id(CIDC(i), CIDCC(i), (void *)id, 
-                               SILC_ID_CHANNEL, &id_cache))
-      break;
-  }
-
- if (!id_cache)
-    goto out;
-
-  /* Save the key */
-  key = silc_channel_key_get_key(payload, &key_len);
-  cipher = silc_channel_key_get_cipher(payload, NULL);
-
-  channel = (SilcChannelEntry)id_cache->context;
-  channel->key_len = key_len;
-  channel->key = silc_calloc(key_len, sizeof(*channel->key));
-  memcpy(channel->key, key, key_len);
-
-  silc_cipher_alloc(cipher, &channel->channel_key);
-  if (!channel->channel_key) {
-    silc_say(client, "Cannot talk to channel: unsupported cipher %s", cipher);
-    goto out;
-  }
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, key_len);
-
-  /* Client is now joined to the channel */
-  channel->on_channel = TRUE;
-
- out:
-  silc_free(id);
-  silc_channel_key_free_payload(payload);
-#undef CIDC
-#undef CIDCC
-}
-
-/* Process received message to a channel (or from a channel, really). This
-   decrypts the channel message with channel specific key and parses the
-   channel payload. Finally it displays the message on the screen. */
-
-void silc_client_channel_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet)
-{
-  int i;
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelPayload payload = NULL;
-  SilcChannelID *id = NULL;
-  SilcChannelEntry channel;
-  SilcIDCache *id_cache = NULL;
-
-#define CIDC(x) win->channel_id_cache[(x)]
-#define CIDCC(x) win->channel_id_cache_count[(x)]
-
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL)
-    goto out;
-
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-
-  /* Find the channel entry from channels on this window */
-  for (i = 0; i < 96; i++) {
-    if (CIDC(i) == NULL)
-      continue;
-    if (silc_idcache_find_by_id(CIDC(i), CIDCC(i), (void *)id, 
-                               SILC_ID_CHANNEL, &id_cache))
-      break;
-  }
-
-  if (!id_cache)
-    goto out;
-
-  channel = (SilcChannelEntry)id_cache->context;
-
-  /* Decrypt the channel message payload. Push the IV out of the way,
-     since it is not encrypted (after pushing buffer->tail has the IV). */
-  silc_buffer_push_tail(buffer, 16);
-  channel->channel_key->cipher->decrypt(channel->channel_key->context,
-                                       buffer->data, buffer->data,
-                                       buffer->len, buffer->tail);
-  silc_buffer_pull_tail(buffer, 16);
-
-  /* Parse the channel message payload */
-  payload = silc_channel_parse_payload(buffer);
-  if (!payload)
-    goto out;
-
-  /* Display the message on screen */
-  if (packet->src_id_type == SILC_ID_CLIENT)
-    /* Message from client */
-    silc_print(client, "<%s> %s", silc_channel_get_nickname(payload, NULL),
-              silc_channel_get_data(payload, NULL));
-  else
-    /* Message from server */
-    silc_say(client, "%s", silc_channel_get_data(payload, NULL));
-
- out:
-  if (id)
-    silc_free(id);
-  if (payload)
-    silc_channel_free_payload(payload);
-#undef CIDC
-#undef CIDCC
-}
diff --git a/apps/silc/client.h b/apps/silc/client.h
deleted file mode 100644 (file)
index 661bd83..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
-
-  client.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef CLIENT_H
-#define CLIENT_H
-
-/* Window structure used in client to associate all the important
-   connection (window) specific data to this structure. How the window
-   actually appears on the screen in handeled by the silc_screen*
-   routines in screen.c. */
-typedef struct {
-  /*
-   * Local data 
-   */
-  char *nickname;
-
-  /* Local client ID for this connection */
-  SilcClientID *local_id;
-
-  /* Decoded local ID so that the above defined ID would not have
-     to be decoded for every packet. */
-  unsigned char *local_id_data;
-  unsigned int local_id_data_len;
-
-  /* Own client entry. */
-  SilcClientEntry local_entry;
-
-  /*
-   * Remote data 
-   */
-  char *remote_host;
-  int remote_port;
-  int remote_type;
-
-  /* Remote client ID for this connection */
-  SilcClientID *remote_id;
-
-  /* Remote local ID so that the above defined ID would not have
-     to be decoded for every packet. */
-  unsigned char *remote_id_data;
-  unsigned int remote_id_data_len;
-
-  /*
-   * Common data 
-   */
-  /* Keys */
-  SilcCipher send_key;
-  SilcCipher receive_key;
-  SilcPKCS public_key;
-  SilcHmac hmac;
-  unsigned char *hmac_key;
-  unsigned int hmac_key_len;
-
-  /* Client ID and Channel ID cache. Messages transmitted in SILC network
-     are done using different unique ID's. These are the cache for
-     thoses ID's used in the communication. */
-  SilcIDCache *client_id_cache[96];
-  unsigned int client_id_cache_count[96];
-  SilcIDCache *channel_id_cache[96];
-  unsigned int channel_id_cache_count[96];
-  SilcIDCache *server_id_cache;
-  unsigned int server_id_cache_count;
-
-  /* Current channel on window. All channel's are saved (allocated) into
-     the cache entries. */
-  SilcChannelEntry current_channel;
-
-  /* Socket connection object for this connection (window). This
-     object will have a back-pointer to this window object for fast
-     referencing (sock->user_data). */
-  SilcSocketConnection sock;
-
-  /* The actual physical screen. This data is handled by the
-     screen handling routines. */
-  void *screen;
-} *SilcClientWindow;
-
-typedef struct {
-  char *username;
-  char *realname;
-
-  /* SILC client task queues */
-  SilcTaskQueue io_queue;
-  SilcTaskQueue timeout_queue;
-  SilcTaskQueue generic_queue;
-
-  /* Input buffer that holds the characters user types. This is
-     used only to store the typed chars for a while. */
-  SilcBuffer input_buffer;
-
-  /* Table of windows in client. All the data, including connection
-     specific data, is saved in here. */
-  SilcClientWindow *windows;
-  unsigned int windows_count;
-
-  /* Currently active window. This is pointer to the window table 
-     defined above. This must never be free'd directly. */
-  SilcClientWindow current_win;
-
-  /* The SILC client screen object */
-  SilcScreen screen;
-
-  /* Generic cipher and hash objects */
-  SilcCipher none_cipher;
-  SilcHash md5hash;
-  SilcHash sha1hash;
-  SilcHmac md5hmac;
-  SilcHmac sha1hmac;
-
-  /* Configuration object */
-  SilcClientConfig config;
-
-  /* Random Number Generator */
-  SilcRng rng;
-
-#ifdef SILC_SIM
-  /* SIM (SILC Module) table */
-  SilcSimContext **sim;
-  unsigned int sim_count;
-#endif
-} SilcClientObject;
-
-typedef SilcClientObject *SilcClient;
-
-/* Macros */
-
-#ifndef CTRL
-#define CTRL(x) ((x) & 0x1f)   /* Ctrl+x */
-#endif
-
-/* Registers generic task for file descriptor for reading from network and
-   writing to network. As being generic task the actual task is allocated 
-   only once and after that the same task applies to all registered fd's. */
-#define SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd)                     \
-do {                                                                   \
-  SilcTask tmptask = silc_task_register(client->generic_queue, (fd),   \
-                                       silc_client_packet_process,     \
-                                       context, 0, 0,                  \
-                                       SILC_TASK_GENERIC,              \
-                                       SILC_TASK_PRI_NORMAL);          \
-  silc_task_set_iotype(tmptask, SILC_TASK_WRITE);                      \
-} while(0)
-
-#define SILC_CLIENT_SET_CONNECTION_FOR_INPUT(fd)               \
-do {                                                           \
-  silc_schedule_set_listen_fd((fd), (1L << SILC_TASK_READ));   \
-} while(0)                                                     \
-     
-#define SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(fd)              \
-do {                                                           \
-  silc_schedule_set_listen_fd((fd), ((1L << SILC_TASK_READ) |  \
-                                    (1L << SILC_TASK_WRITE))); \
-} while(0)
-
-/* Finds socket connection object by file descriptor */
-#define SILC_CLIENT_GET_SOCK(__x, __fd, __sock)                \
-do {                                                   \
-  int __i;                                             \
-                                                       \
-  for (__i = 0; __i < (__x)->windows_count; __i++)     \
-    if ((__x)->windows[__i]->sock->sock == (__fd))     \
-      break;                                           \
-                                                       \
-  if (__i >= (__x)->windows_count)                     \
-    (__sock) = NULL;                                   \
- (__sock) = (__x)->windows[__i]->sock;                 \
-} while(0)
-
-/* Returns TRUE if windows is currently active window */
-#define SILC_CLIENT_IS_CURRENT_WIN(__x, __win) ((__x)->current_win == (__win))
-
-/* Prototypes */
-int silc_client_alloc(SilcClient *new_client);
-void silc_client_free(SilcClient client);
-int silc_client_init(SilcClient client);
-void silc_client_stop(SilcClient client);
-void silc_client_run(SilcClient client);
-void silc_client_parse_command_line(unsigned char *buffer, 
-                                   unsigned char ***parsed,
-                                   unsigned int **parsed_lens,
-                                   unsigned int **parsed_types,
-                                   unsigned int *parsed_num,
-                                   unsigned int max_args);
-int silc_client_connect_to_server(SilcClient client, int port,
-                                 char *host);
-void silc_client_packet_send(SilcClient client, 
-                            SilcSocketConnection sock,
-                            SilcPacketType type, 
-                            void *dst_id,
-                            SilcIdType dst_id_type,
-                            SilcCipher cipher,
-                            SilcHmac hmac,
-                            unsigned char *data, 
-                            unsigned int data_len, 
-                            int force_send);
-void silc_client_packet_send_to_channel(SilcClient client, 
-                                       SilcSocketConnection sock,
-                                       SilcChannelEntry channel,
-                                       unsigned char *data, 
-                                       unsigned int data_len, 
-                                       int force_send);
-void silc_client_packet_send_private_message(SilcClient client,
-                                            SilcSocketConnection sock,
-                                            SilcClientEntry client_entry,
-                                            unsigned char *data, 
-                                            unsigned int data_len, 
-                                            int force_send);
-void silc_client_close_connection(SilcClient client,
-                                 SilcSocketConnection sock);
-void silc_client_disconnected_by_server(SilcClient client,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer message);
-void silc_client_error_by_server(SilcClient client,
-                                SilcSocketConnection sock,
-                                SilcBuffer message);
-void silc_client_notify_by_server(SilcClient client,
-                                 SilcSocketConnection sock,
-                                 SilcBuffer message);
-void silc_client_receive_new_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               unsigned char *id_string);
-void silc_client_new_channel_id(SilcClient client,
-                               SilcSocketConnection sock,
-                               char *channel_name,
-                               unsigned char *id_string);
-void silc_client_receive_channel_key(SilcClient client,
-                                    SilcSocketConnection sock,
-                                    SilcBuffer packet);
-void silc_client_channel_message(SilcClient client, 
-                                SilcSocketConnection sock, 
-                                SilcPacketContext *packet);
-#endif
diff --git a/apps/silc/client_ops.c b/apps/silc/client_ops.c
new file mode 100644 (file)
index 0000000..c027c14
--- /dev/null
@@ -0,0 +1,1342 @@
+/*
+
+  client_ops.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "clientincludes.h"
+
+static bool 
+silc_verify_public_key_internal(SilcClient client, SilcClientConnection conn,
+                               SilcSocketType conn_type, unsigned char *pk, 
+                               uint32 pk_len, SilcSKEPKType pk_type)
+{
+  int i;
+  char file[256], filename[256], *fingerprint;
+  struct passwd *pw;
+  struct stat st;
+  char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER ||
+                  conn_type == SILC_SOCKET_TYPE_ROUTER) ? 
+                 "server" : "client");
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    silc_say(client, conn, "We don't support %s public key type %d", 
+            entity, pk_type);
+    return FALSE;
+  }
+
+  pw = getpwuid(getuid());
+  if (!pw)
+    return FALSE;
+
+  memset(filename, 0, sizeof(filename));
+  memset(file, 0, sizeof(file));
+
+  if (conn_type == SILC_SOCKET_TYPE_SERVER ||
+      conn_type == SILC_SOCKET_TYPE_ROUTER) {
+    snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, 
+            conn->sock->hostname, conn->sock->port);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+  } else {
+    /* Replace all whitespaces with `_'. */
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    for (i = 0; i < strlen(fingerprint); i++)
+      if (fingerprint[i] == ' ')
+       fingerprint[i] = '_';
+    
+    snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint);
+    snprintf(filename, sizeof(filename) - 1, "%s/.silc/%skeys/%s", 
+            pw->pw_dir, entity, file);
+    silc_free(fingerprint);
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+
+    silc_say(client, conn, "Received %s public key", entity);
+    silc_say(client, conn, "Fingerprint for the %s key is", entity);
+    silc_say(client, conn, "%s", fingerprint);
+
+    /* Ask user to verify the key and save it */
+    if (silc_client_ask_yes_no(client, 
+       "Would you like to accept the key (y/n)? "))
+      {
+       /* Save the key for future checking */
+       silc_pkcs_save_public_key_data(filename, pk, pk_len, 
+                                      SILC_PKCS_FILE_PEM);
+       silc_free(fingerprint);
+       return TRUE;
+      }
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    /* Load the key file */
+    if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                    SILC_PKCS_FILE_BIN)) {
+       silc_say(client, conn, "Received %s public key", entity);
+       silc_say(client, conn, "Fingerprint for the %s key is", entity);
+       silc_say(client, conn, "%s", fingerprint);
+       silc_say(client, conn, "Could not load your local copy of the %s key",
+                entity);
+       if (silc_client_ask_yes_no(client, 
+          "Would you like to accept the key anyway (y/n)? "))
+         {
+           /* Save the key for future checking */
+           unlink(filename);
+           silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                          SILC_PKCS_FILE_PEM);
+           silc_free(fingerprint);
+           return TRUE;
+         }
+       
+       silc_free(fingerprint);
+       return FALSE;
+      }
+  
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "Your local copy of the %s key is malformed",
+              entity);
+      if (silc_client_ask_yes_no(client, 
+         "Would you like to accept the key anyway (y/n)? "))
+       {
+         /* Save the key for future checking */
+         unlink(filename);
+         silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                        SILC_PKCS_FILE_PEM);
+         silc_free(fingerprint);
+         return TRUE;
+       }
+
+      silc_free(fingerprint);
+      return FALSE;
+    }
+
+    if (memcmp(encpk, pk, encpk_len)) {
+      silc_say(client, conn, "Received %s public key", entity);
+      silc_say(client, conn, "Fingerprint for the %s key is", entity);
+      silc_say(client, conn, "%s", fingerprint);
+      silc_say(client, conn, "%s key does not match with your local copy",
+              entity);
+      silc_say(client, conn, 
+              "It is possible that the key has expired or changed");
+      silc_say(client, conn, "It is also possible that some one is performing "
+                      "man-in-the-middle attack");
+      
+      /* Ask user to verify the key and save it */
+      if (silc_client_ask_yes_no(client, 
+         "Would you like to accept the key anyway (y/n)? "))
+       {
+         /* Save the key for future checking */
+         unlink(filename);
+         silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                        SILC_PKCS_FILE_PEM);
+         silc_free(fingerprint);
+         return TRUE;
+       }
+
+      silc_say(client, conn, "Will not accept the %s key", entity);
+      silc_free(fingerprint);
+      return FALSE;
+    }
+
+    /* Local copy matched */
+    silc_free(fingerprint);
+    return TRUE;
+  }
+
+  silc_say(client, conn, "Will not accept the %s key", entity);
+  silc_free(fingerprint);
+  return FALSE;
+}
+
+void silc_say(SilcClient client, SilcClientConnection conn, 
+             char *msg, ...)
+{
+  va_list vp;
+  char message[2048];
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  memset(message, 0, sizeof(message));
+  strncat(message, "\n***  ", 5);
+
+  va_start(vp, msg);
+  vsprintf(message + 5, msg, vp);
+  va_end(vp);
+  
+  /* Print the message */
+  silc_print_to_window(app->screen->output_win[0], message);
+}
+
+/* Prints a message with three star (*) sign before the actual message
+   on the current output window. This is used to print command outputs
+   and error messages. */
+
+void silc_op_say(SilcClient client, SilcClientConnection conn, 
+                SilcClientMessageType type, char *msg, ...)
+{
+  va_list vp;
+  char message[2048];
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  memset(message, 0, sizeof(message));
+  strncat(message, "\n***  ", 5);
+
+  va_start(vp, msg);
+  vsprintf(message + 5, msg, vp);
+  va_end(vp);
+  
+  /* Print the message */
+  silc_print_to_window(app->screen->output_win[0], message);
+}
+
+/* Message for a channel. The `sender' is the nickname of the sender 
+   received in the packet. The `channel_name' is the name of the channel. */
+
+void silc_channel_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcChannelEntry channel,
+                         SilcMessageFlags flags, char *msg)
+{
+  /* Message from client */
+  if (conn && !strcmp(conn->current_channel->channel_name, 
+                     channel->channel_name))
+    if (flags & SILC_MESSAGE_FLAG_ACTION)
+      silc_print(client, "* %s %s", sender ? sender->nickname : "[<unknown>]", 
+                msg);
+    else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+      silc_print(client, "- %s %s", sender ? sender->nickname : "[<unknown>]", 
+                msg);
+    else
+      silc_print(client, "<%s> %s", sender ? sender->nickname : "[<unknown>]", 
+                msg);
+  else
+    if (flags & SILC_MESSAGE_FLAG_ACTION)
+      silc_print(client, "* %s:%s %s", sender ? sender->nickname : 
+                "[<unknown>]",
+                channel->channel_name, msg);
+    else if (flags & SILC_MESSAGE_FLAG_NOTICE)
+      silc_print(client, "- %s:%s %s", sender ? sender->nickname : 
+                "[<unknown>]",
+                channel->channel_name, msg);
+    else
+      silc_print(client, "<%s:%s> %s", sender ? sender->nickname : 
+                "[<unknown>]",
+                channel->channel_name, msg);
+}
+
+/* Private message to the client. The `sender' is the nickname of the
+   sender received in the packet. */
+
+void silc_private_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcMessageFlags flags,
+                         char *msg)
+{
+  silc_print(client, "*%s* %s", sender->nickname, msg);
+}
+
+
+/* Notify message to the client. The notify arguments are sent in the
+   same order as servers sends them. The arguments are same as received
+   from the server except for ID's.  If ID is received application receives
+   the corresponding entry to the ID. For example, if Client ID is received
+   application receives SilcClientEntry.  Also, if the notify type is
+   for channel the channel entry is sent to application (even if server
+   does not send it). */
+
+void silc_notify(SilcClient client, SilcClientConnection conn, 
+                SilcNotifyType type, ...)
+{
+  SilcClientInternal app = (SilcClientInternal)client->application;
+  va_list vp;
+  char message[4096];
+  SilcClientEntry client_entry, client_entry2;
+  SilcChannelEntry channel_entry;
+  char *tmp = NULL;
+  uint32 tmp_int;
+
+  va_start(vp, type);
+
+  memset(message, 0, sizeof(message));
+
+  /* Get arguments (defined by protocol in silc-pp-01 -draft) */
+  switch(type) {
+  case SILC_NOTIFY_TYPE_NONE:
+    tmp = va_arg(vp, char *);
+    if (!tmp)
+      return;
+    strcpy(message, tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_INVITE:
+    (void)va_arg(vp, SilcChannelEntry);
+    tmp = va_arg(vp, char *);
+    client_entry = va_arg(vp, SilcClientEntry);
+    snprintf(message, sizeof(message), "%s invites you to channel %s", 
+            client_entry->nickname, tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_JOIN:
+    client_entry = va_arg(vp, SilcClientEntry);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+    snprintf(message, sizeof(message), "%s (%s) has joined channel %s", 
+            client_entry->nickname, client_entry->username, 
+            channel_entry->channel_name);
+    if (client_entry == conn->local_entry) {
+      SilcChannelUser chu;
+
+      silc_list_start(channel_entry->clients);
+      while ((chu = silc_list_get(channel_entry->clients)) != SILC_LIST_END) {
+       if (chu->client == client_entry) {
+         if (app->screen->bottom_line->mode)
+           silc_free(app->screen->bottom_line->mode);
+         app->screen->bottom_line->mode = silc_client_chumode_char(chu->mode);
+         silc_screen_print_bottom_line(app->screen, 0);
+         break;
+       }
+      }
+    }
+    break;
+
+  case SILC_NOTIFY_TYPE_LEAVE:
+    client_entry = va_arg(vp, SilcClientEntry);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+    if (client_entry->server)
+      snprintf(message, sizeof(message), "%s@%s has left channel %s", 
+              client_entry->nickname, client_entry->server, 
+              channel_entry->channel_name);
+    else
+      snprintf(message, sizeof(message), "%s has left channel %s", 
+              client_entry->nickname, channel_entry->channel_name);
+    break;
+
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    client_entry = va_arg(vp, SilcClientEntry);
+    tmp = va_arg(vp, char *);
+    if (client_entry->server)
+      snprintf(message, sizeof(message), "Signoff: %s@%s %s%s%s", 
+              client_entry->nickname, client_entry->server,
+              tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+    else
+      snprintf(message, sizeof(message), "Signoff: %s %s%s%s", 
+              client_entry->nickname,
+              tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    client_entry = va_arg(vp, SilcClientEntry);
+    tmp = va_arg(vp, char *);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+    if (client_entry->server)
+      snprintf(message, sizeof(message), "%s@%s set topic on %s: %s", 
+              client_entry->nickname, client_entry->server,
+              channel_entry->channel_name, tmp);
+    else
+      snprintf(message, sizeof(message), "%s set topic on %s: %s", 
+              client_entry->nickname, channel_entry->channel_name, tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    client_entry = va_arg(vp, SilcClientEntry);
+    client_entry2 = va_arg(vp, SilcClientEntry);
+    if (client_entry->server && client_entry2->server)
+      snprintf(message, sizeof(message), "%s@%s is known as %s@%s", 
+              client_entry->nickname, client_entry->server,
+              client_entry2->nickname, client_entry2->server);
+    else
+      snprintf(message, sizeof(message), "%s is known as %s", 
+              client_entry->nickname, client_entry2->nickname);
+    break;
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    client_entry = va_arg(vp, SilcClientEntry);
+    tmp_int = va_arg(vp, uint32);
+    (void)va_arg(vp, char *);
+    (void)va_arg(vp, char *);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+    
+    tmp = silc_client_chmode(tmp_int, 
+                            channel_entry->channel_key->cipher->name,
+                            channel_entry->hmac->hmac->name);
+    
+    if (tmp) {
+      if (client_entry) {
+       snprintf(message, sizeof(message), "%s changed channel mode to +%s",
+                client_entry->nickname, tmp);
+      } else {
+       snprintf(message, sizeof(message), 
+                "channel mode was changed to +%s (forced by router)",
+                tmp);
+      }
+    } else {
+      if (client_entry) {
+       snprintf(message, sizeof(message), "%s removed all channel modes", 
+                client_entry->nickname);
+      } else {
+       snprintf(message, sizeof(message), 
+                "Removed all channel modes (forced by router)");
+      }
+    }
+
+    if (app->screen->bottom_line->channel_mode)
+      silc_free(app->screen->bottom_line->channel_mode);
+    app->screen->bottom_line->channel_mode = tmp;
+    silc_screen_print_bottom_line(app->screen, 0);
+    break;
+
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    client_entry = va_arg(vp, SilcClientEntry);
+    tmp_int = va_arg(vp, uint32);
+    tmp = silc_client_chumode(tmp_int);
+    client_entry2 = va_arg(vp, SilcClientEntry);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+    if (tmp)
+      snprintf(message, sizeof(message), "%s changed %s's mode to +%s", 
+              client_entry->nickname, client_entry2->nickname, tmp);
+    else
+      snprintf(message, sizeof(message), "%s removed %s's modes", 
+              client_entry->nickname, client_entry2->nickname);
+    if (client_entry2 == conn->local_entry) {
+      if (app->screen->bottom_line->mode)
+       silc_free(app->screen->bottom_line->mode);
+      app->screen->bottom_line->mode = silc_client_chumode_char(tmp_int);
+      silc_screen_print_bottom_line(app->screen, 0);
+    }
+    silc_free(tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    {
+      char line[256];
+      int i;
+      tmp = va_arg(vp, unsigned char *);
+
+      i = 0;
+      while(tmp[i] != 0) {
+       if (tmp[i++] == '\n') {
+         memset(line, 0, sizeof(line));
+         strncat(line, tmp, i - 1);
+         tmp += i;
+         
+         silc_say(client, conn, "%s", line);
+         
+         if (!strlen(tmp))
+           break;
+         i = 0;
+       }
+      }
+    }
+    return;
+
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    return;
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    client_entry = va_arg(vp, SilcClientEntry);
+    tmp = va_arg(vp, char *);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+
+    if (client_entry == conn->local_entry) {
+      snprintf(message, sizeof(message), 
+              "You have been kicked off channel %s %s%s%s", 
+              conn->current_channel->channel_name,
+              tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+    } else {
+      snprintf(message, sizeof(message), 
+              "%s%s%s has been kicked off channel %s %s%s%s", 
+              client_entry->nickname, 
+              client_entry->server ? "@" : "",
+              client_entry->server ? client_entry->server : "",
+              conn->current_channel->channel_name,
+              tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+    }
+    break;
+
+  case SILC_NOTIFY_TYPE_KILLED:
+    client_entry = va_arg(vp, SilcClientEntry);
+    tmp = va_arg(vp, char *);
+    channel_entry = va_arg(vp, SilcChannelEntry);
+
+    if (client_entry == conn->local_entry) {
+      snprintf(message, sizeof(message), 
+              "You have been killed from the SILC Network %s%s%s", 
+              tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+    } else {
+      snprintf(message, sizeof(message), 
+              "%s%s%s has been killed from the SILC Network %s%s%s", 
+              client_entry->nickname, 
+              client_entry->server ? "@" : "",
+              client_entry->server ? client_entry->server : "",
+              tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+    }
+    break;
+
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    {
+      SilcClientEntry *clients;
+      uint32 clients_count;
+      int i;
+
+      (void)va_arg(vp, void *);
+      clients = va_arg(vp, SilcClientEntry *);
+      clients_count = va_arg(vp, uint32);
+
+      for (i = 0; i < clients_count; i++) {
+       if (clients[i]->server)
+         snprintf(message, sizeof(message), "Server signoff: %s@%s %s%s%s", 
+                  clients[i]->nickname, clients[i]->server,
+                  tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+       else
+         snprintf(message, sizeof(message), "Server signoff: %s %s%s%s", 
+                  clients[i]->nickname,
+                  tmp ? "(" : "", tmp ? tmp : "", tmp ? ")" : "");
+       silc_print(client, "*** %s", message);
+       memset(message, 0, sizeof(message));
+      }
+      return;
+    }
+
+  default:
+    break;
+  }
+
+  silc_print(client, "*** %s", message);
+}
+
+/* Command handler. This function is called always in the command function.
+   If error occurs it will be called as well. `conn' is the associated
+   client connection. `cmd_context' is the command context that was
+   originally sent to the command. `success' is FALSE if error occured
+   during command. `command' is the command being processed. It must be
+   noted that this is not reply from server. This is merely called just
+   after application has called the command. Just to tell application
+   that the command really was processed. */
+
+void silc_command(SilcClient client, SilcClientConnection conn, 
+                 SilcClientCommandContext cmd_context, int success,
+                 SilcCommand command)
+{
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  if (!success)
+    return;
+
+  switch(command)
+    {
+       
+    case SILC_COMMAND_QUIT:
+      app->screen->bottom_line->channel = NULL;
+      silc_screen_print_bottom_line(app->screen, 0);
+      break;
+
+    case SILC_COMMAND_LEAVE:
+      /* We won't talk anymore on this channel */
+      silc_say(client, conn, "You have left channel %s", 
+              conn->current_channel->channel_name);
+      break;
+
+    }
+}
+
+/* We've resolved all clients we don't know about, now just print the
+   users from the channel on the screen. */
+
+void silc_client_show_users(SilcClient client,
+                           SilcClientConnection conn,
+                           SilcClientEntry *clients,
+                           uint32 clients_count,
+                           void *context)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+  SilcChannelUser chu;
+  int k = 0, len1 = 0, len2 = 0;
+  char *name_list = NULL;
+
+  if (!clients)
+    return;
+
+  silc_list_start(channel->clients);
+  while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+    char *m, *n = chu->client->nickname;
+    if (!n)
+      continue;
+
+    len2 = strlen(n);
+    len1 += len2;
+    
+    name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
+    
+    m = silc_client_chumode_char(chu->mode);
+    if (m) {
+      memcpy(name_list + (len1 - len2), m, strlen(m));
+      len1 += strlen(m);
+      silc_free(m);
+    }
+    
+    memcpy(name_list + (len1 - len2), n, len2);
+    name_list[len1] = 0;
+    
+    if (k == silc_list_count(channel->clients) - 1)
+      break;
+    memcpy(name_list + len1, " ", 1);
+    len1++;
+    k++;
+  }
+
+  silc_say(client, conn, "Users on %s: %s", channel->channel_name, 
+                  name_list);
+  silc_free(name_list);
+}
+
+/* Command reply handler. This function is called always in the command reply
+   function. If error occurs it will be called as well. Normal scenario
+   is that it will be called after the received command data has been parsed
+   and processed. The function is used to pass the received command data to
+   the application. 
+
+   `conn' is the associated client connection. `cmd_payload' is the command
+   payload data received from server and it can be ignored. It is provided
+   if the application would like to re-parse the received command data,
+   however, it must be noted that the data is parsed already by the library
+   thus the payload can be ignored. `success' is FALSE if error occured.
+   In this case arguments are not sent to the application. `command' is the
+   command reply being processed. The function has variable argument list
+   and each command defines the number and type of arguments it passes to the
+   application (on error they are not sent). */
+
+void silc_command_reply(SilcClient client, SilcClientConnection conn,
+                       SilcCommandPayload cmd_payload, int success,
+                       SilcCommand command, SilcCommandStatus status, ...)
+{
+  SilcClientInternal app = (SilcClientInternal)client->application;
+  SilcChannelUser chu;
+  va_list vp;
+
+  va_start(vp, status);
+
+  switch(command)
+    {
+    case SILC_COMMAND_WHOIS:
+      {
+       char buf[1024], *nickname, *username, *realname;
+       int len;
+       uint32 idle, mode;
+       SilcBuffer channels;
+
+       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+         char *tmp;
+         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                          3, NULL);
+         if (tmp)
+           silc_say(client, conn, "%s: %s", tmp,
+                            silc_client_command_status_message(status));
+         else
+           silc_say(client, conn, "%s",
+                            silc_client_command_status_message(status));
+         break;
+       }
+
+       if (!success)
+         return;
+
+       (void)va_arg(vp, SilcClientEntry);
+       nickname = va_arg(vp, char *);
+       username = va_arg(vp, char *);
+       realname = va_arg(vp, char *);
+       channels = va_arg(vp, SilcBuffer);
+       mode = va_arg(vp, uint32);
+       idle = va_arg(vp, uint32);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (nickname) {
+         len = strlen(nickname);
+         strncat(buf, nickname, len);
+         strncat(buf, " is ", 4);
+       }
+       
+       if (username) {
+         strncat(buf, username, strlen(username));
+       }
+       
+       if (realname) {
+         strncat(buf, " (", 2);
+         strncat(buf, realname, strlen(realname));
+         strncat(buf, ")", 1);
+       }
+
+       silc_say(client, conn, "%s", buf);
+
+       if (channels) {
+         SilcDList list = silc_channel_payload_parse_list(channels);
+         if (list) {
+           SilcChannelPayload entry;
+
+           memset(buf, 0, sizeof(buf));
+           strcat(buf, "on channels: ");
+
+           silc_dlist_start(list);
+           while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+             char *m = silc_client_chumode_char(silc_channel_get_mode(entry));
+             uint32 name_len;
+             char *name = silc_channel_get_name(entry, &name_len);
+
+             if (m)
+               strncat(buf, m, strlen(m));
+             strncat(buf, name, name_len);
+             strncat(buf, " ", 1);
+             silc_free(m);
+           }
+
+           silc_say(client, conn, "%s", buf);
+           silc_channel_payload_list_free(list);
+         }
+       }
+
+       if (mode) {
+         if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
+             (mode & SILC_UMODE_ROUTER_OPERATOR))
+           silc_say(client, conn, "%s is %s", nickname,
+                            (mode & SILC_UMODE_SERVER_OPERATOR) ?
+                            "Server Operator" :
+                            (mode & SILC_UMODE_ROUTER_OPERATOR) ?
+                            "SILC Operator" : "[Unknown mode]");
+
+         if (mode & SILC_UMODE_GONE)
+           silc_say(client, conn, "%s is gone", nickname);
+       }
+
+       if (idle && nickname)
+         silc_say(client, conn, "%s has been idle %d %s",
+                          nickname,
+                          idle > 60 ? (idle / 60) : idle,
+                          idle > 60 ? "minutes" : "seconds");
+      }
+      break;
+
+    case SILC_COMMAND_WHOWAS:
+      {
+       char buf[1024], *nickname, *username, *realname;
+       int len;
+
+       if (status == SILC_STATUS_ERR_NO_SUCH_NICK ||
+           status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+         char *tmp;
+         tmp = silc_argument_get_arg_type(silc_command_get_args(cmd_payload),
+                                          3, NULL);
+         if (tmp)
+           silc_say(client, conn, "%s: %s", tmp,
+                            silc_client_command_status_message(status));
+         else
+           silc_say(client, conn, "%s",
+                            silc_client_command_status_message(status));
+         break;
+       }
+
+       if (!success)
+         return;
+
+       (void)va_arg(vp, SilcClientEntry);
+       nickname = va_arg(vp, char *);
+       username = va_arg(vp, char *);
+       realname = va_arg(vp, char *);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (nickname) {
+         len = strlen(nickname);
+         strncat(buf, nickname, len);
+         strncat(buf, " was ", 5);
+       }
+       
+       if (username) {
+         strncat(buf, username, strlen(nickname));
+       }
+       
+       if (realname) {
+         strncat(buf, " (", 2);
+         strncat(buf, realname, strlen(realname));
+         strncat(buf, ")", 1);
+       }
+
+       silc_say(client, conn, "%s", buf);
+      }
+      break;
+
+    case SILC_COMMAND_INVITE:
+      {
+       SilcChannelEntry channel;
+       char *invite_list;
+
+       if (!success)
+         return;
+       
+       channel = va_arg(vp, SilcChannelEntry);
+       invite_list = va_arg(vp, char *);
+
+       if (invite_list)
+         silc_say(client, conn, "%s invite list: %s", channel->channel_name,
+                  invite_list);
+       else
+         silc_say(client, conn, "%s invite list not set", 
+                  channel->channel_name);
+      }
+      break;
+
+    case SILC_COMMAND_JOIN:
+      {
+       uint32 mode;
+       char *topic;
+       SilcBuffer client_id_list;
+       uint32 list_count;
+       SilcChannelEntry channel;
+
+       if (!success)
+         return;
+
+       app->screen->bottom_line->channel = va_arg(vp, char *);
+       channel = va_arg(vp, SilcChannelEntry);
+       mode = va_arg(vp, uint32);
+       (void)va_arg(vp, uint32);
+       (void)va_arg(vp, unsigned char *);
+       (void)va_arg(vp, unsigned char *);
+       (void)va_arg(vp, unsigned char *);
+       topic = va_arg(vp, char *);
+       (void)va_arg(vp, unsigned char *);
+       list_count = va_arg(vp, uint32);
+       client_id_list = va_arg(vp, SilcBuffer);
+
+       if (topic)
+         silc_say(client, conn, "Topic for %s: %s", 
+                          app->screen->bottom_line->channel, topic);
+       
+       app->screen->bottom_line->channel_mode = 
+         silc_client_chmode(mode,
+                            channel->channel_key->cipher->name,
+                            channel->hmac->hmac->name);
+       silc_screen_print_bottom_line(app->screen, 0);
+
+       /* Resolve the client information */
+       silc_client_get_clients_by_list(client, conn, list_count,
+                                       client_id_list,
+                                       silc_client_show_users, channel);
+      }
+      break;
+
+    case SILC_COMMAND_NICK:
+      {
+       SilcClientEntry entry;
+
+       if (!success)
+         return;
+
+       entry = va_arg(vp, SilcClientEntry);
+       silc_say(client, conn, "Your current nickname is %s", entry->nickname);
+       app->screen->bottom_line->nickname = entry->nickname;
+       silc_screen_print_bottom_line(app->screen, 0);
+      }
+      break;
+
+    case SILC_COMMAND_LIST:
+      {
+       char *topic, *name;
+       int usercount;
+       unsigned char buf[256], tmp[16];
+       int i, len;
+
+       if (!success)
+         return;
+
+       (void)va_arg(vp, SilcChannelEntry);
+       name = va_arg(vp, char *);
+       topic = va_arg(vp, char *);
+       usercount = va_arg(vp, int);
+
+       if (status == SILC_STATUS_LIST_START ||
+           status == SILC_STATUS_OK)
+         silc_say(client, conn, 
+         "  Channel                                  Users     Topic");
+
+       memset(buf, 0, sizeof(buf));
+       strncat(buf, "  ", 2);
+       len = strlen(name);
+       strncat(buf, name, len > 40 ? 40 : len);
+       if (len < 40)
+         for (i = 0; i < 40 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+
+       memset(tmp, 0, sizeof(tmp));
+       if (usercount) {
+         snprintf(tmp, sizeof(tmp), "%d", usercount);
+         strcat(buf, tmp);
+       }
+       len = strlen(tmp);
+       if (len < 10)
+         for (i = 0; i < 10 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+
+       if (topic) {
+         len = strlen(topic);
+         strncat(buf, topic, len);
+       }
+
+       silc_say(client, conn, "%s", buf);
+      }
+      break;
+
+    case SILC_COMMAND_UMODE:
+      {
+       uint32 mode;
+
+       if (!success)
+         return;
+
+       mode = va_arg(vp, uint32);
+
+       if (!mode && app->screen->bottom_line->umode) {
+         silc_free(app->screen->bottom_line->umode);
+         app->screen->bottom_line->umode = NULL;
+       }
+
+       if (mode & SILC_UMODE_SERVER_OPERATOR) {
+         if (app->screen->bottom_line->umode)
+           silc_free(app->screen->bottom_line->umode);
+         app->screen->bottom_line->umode = strdup("Server Operator");;
+       }
+
+       if (mode & SILC_UMODE_ROUTER_OPERATOR) {
+         if (app->screen->bottom_line->umode)
+           silc_free(app->screen->bottom_line->umode);
+         app->screen->bottom_line->umode = strdup("SILC Operator");;
+       }
+
+       silc_screen_print_bottom_line(app->screen, 0);
+      }
+      break;
+
+    case SILC_COMMAND_OPER:
+      if (status == SILC_STATUS_OK) {
+       conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
+       if (app->screen->bottom_line->umode)
+         silc_free(app->screen->bottom_line->umode);
+       app->screen->bottom_line->umode = strdup("Server Operator");;
+       silc_screen_print_bottom_line(app->screen, 0);
+      }
+      break;
+
+    case SILC_COMMAND_SILCOPER:
+      if (status == SILC_STATUS_OK) {
+       conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
+       if (app->screen->bottom_line->umode)
+         silc_free(app->screen->bottom_line->umode);
+       app->screen->bottom_line->umode = strdup("SILC Operator");;
+       silc_screen_print_bottom_line(app->screen, 0);
+      }
+      break;
+
+    case SILC_COMMAND_USERS:
+      {
+       SilcChannelEntry channel;
+       int line_len;
+       char *line;
+       
+       if (!success)
+         return;
+
+       channel = va_arg(vp, SilcChannelEntry);
+
+       /* There are two ways to do this, either parse the list (that
+          the command_reply sends (just take it with va_arg()) or just
+          traverse the channel's client list.  I'll do the latter.  See
+          JOIN command reply for example for the list. */
+
+       silc_say(client, conn, "Users on %s", channel->channel_name);
+       
+       line = silc_calloc(1024, sizeof(*line));
+       line_len = 1024;
+       silc_list_start(channel->clients);
+       while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
+         SilcClientEntry e = chu->client;
+         int i, len1;
+         char *m, tmp[80];
+
+         memset(line, 0, line_len);
+
+         if (chu->client == conn->local_entry) {
+           /* Update status line */
+           if (app->screen->bottom_line->mode)
+             silc_free(app->screen->bottom_line->mode);
+           app->screen->bottom_line->mode = 
+             silc_client_chumode_char(chu->mode);
+           silc_screen_print_bottom_line(app->screen, 0);
+         }
+
+         if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
+           silc_free(line);
+           line_len += strlen(e->nickname) + strlen(e->server) + 100;
+           line = silc_calloc(line_len, sizeof(*line));
+         }
+
+         memset(tmp, 0, sizeof(tmp));
+         m = silc_client_chumode_char(chu->mode);
+
+         strncat(line, " ", 1);
+         strncat(line, e->nickname, strlen(e->nickname));
+         strncat(line, e->server ? "@" : "", 1);
+         
+         len1 = 0;
+         if (e->server)
+           len1 = strlen(e->server);
+         strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
+         
+         len1 = strlen(line);
+         if (len1 >= 30) {
+           memset(&line[29], 0, len1 - 29);
+         } else {
+           for (i = 0; i < 30 - len1 - 1; i++)
+             strcat(line, " ");
+         }
+         
+         if (e->mode & SILC_UMODE_GONE)
+           strcat(line, "  G");
+         else
+           strcat(line, "  H");
+         strcat(tmp, m ? m : "");
+         strncat(line, tmp, strlen(tmp));
+         
+         if (strlen(tmp) < 5)
+           for (i = 0; i < 5 - strlen(tmp); i++)
+             strcat(line, " ");
+         
+         strcat(line, e->username ? e->username : "");
+         
+         silc_say(client, conn, "%s", line);
+         
+         if (m)
+           silc_free(m);
+       }
+       
+       silc_free(line);
+      }
+      break;
+
+    case SILC_COMMAND_BAN:
+      {
+       SilcChannelEntry channel;
+       char *ban_list;
+
+       if (!success)
+         return;
+       
+       channel = va_arg(vp, SilcChannelEntry);
+       ban_list = va_arg(vp, char *);
+
+       if (ban_list)
+         silc_say(client, conn, "%s ban list: %s", channel->channel_name,
+                  ban_list);
+       else
+         silc_say(client, conn, "%s ban list not set", channel->channel_name);
+      }
+      break;
+
+    case SILC_COMMAND_GETKEY:
+      {
+       SilcIdType id_type;
+       void *entry;
+       SilcPublicKey public_key;
+       unsigned char *pk;
+       uint32 pk_len;
+
+       id_type = va_arg(vp, uint32);
+       entry = va_arg(vp, void *);
+       public_key = va_arg(vp, SilcPublicKey);
+
+       pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+
+       if (id_type == SILC_ID_CLIENT) {
+         silc_verify_public_key_internal(client, conn, 
+                                         SILC_SOCKET_TYPE_CLIENT,
+                                         pk, pk_len, SILC_SKE_PK_TYPE_SILC);
+       }
+
+       silc_free(pk);
+      }
+
+    case SILC_COMMAND_TOPIC:
+      {
+       SilcChannelEntry channel;
+       char *topic;
+
+       if (!success)
+         return;
+       
+       channel = va_arg(vp, SilcChannelEntry);
+       topic = va_arg(vp, char *);
+
+       if (topic)
+         silc_say(client, conn, 
+                  "Topic on channel %s: %s", channel->channel_name,
+                  topic);
+      }
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* Called to indicate that connection was either successfully established
+   or connecting failed.  This is also the first time application receives
+   the SilcClientConnection objecet which it should save somewhere. */
+
+void silc_connect(SilcClient client, SilcClientConnection conn, int success)
+{
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  if (success) {
+    app->screen->bottom_line->connection = conn->remote_host;
+    silc_screen_print_bottom_line(app->screen, 0);
+    app->conn = conn;
+  }
+}
+
+/* Called to indicate that connection was disconnected to the server. */
+
+void silc_disconnect(SilcClient client, SilcClientConnection conn)
+{
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  app->screen->bottom_line->connection = NULL;
+  silc_screen_print_bottom_line(app->screen, 0);
+  app->conn = NULL;
+}
+
+/* Asks passphrase from user on the input line. */
+
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context)
+{
+  SilcClientInternal app = (SilcClientInternal)conn->client->application;
+  char pass1[256], pass2[256];
+  int try = 3;
+
+  while(try) {
+
+    /* Print prompt */
+    wattroff(app->screen->input_win, A_INVIS);
+    silc_screen_input_print_prompt(app->screen, "Passphrase: ");
+    wattron(app->screen->input_win, A_INVIS);
+    
+    /* Get string */
+    memset(pass1, 0, sizeof(pass1));
+    wgetnstr(app->screen->input_win, pass1, sizeof(pass1));
+    
+    /* Print retype prompt */
+    wattroff(app->screen->input_win, A_INVIS);
+    silc_screen_input_print_prompt(app->screen, "Retype passphrase: ");
+    wattron(app->screen->input_win, A_INVIS);
+    
+    /* Get string */
+    memset(pass2, 0, sizeof(pass2));
+    wgetnstr(app->screen->input_win, pass2, sizeof(pass2));
+
+    if (!strncmp(pass1, pass2, strlen(pass2)))
+      break;
+
+    try--;
+  }
+
+  wattroff(app->screen->input_win, A_INVIS);
+  silc_screen_input_reset(app->screen);
+
+  /* Deliver the passphrase to the library */
+  completion(pass1, strlen(pass1), context);
+
+  memset(pass1, 0, sizeof(pass1));
+  memset(pass2, 0, sizeof(pass2));
+}
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the key may be saved as trusted public key for later use. The 
+   `completion' must be called after the public key has been verified. */
+
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context)
+{
+  if (silc_verify_public_key_internal(client, conn, conn_type, pk,
+                                     pk_len, pk_type)) {
+    completion(TRUE, context);
+    return;
+  }
+
+  completion(FALSE, context);
+}
+
+/* Find authentication method and authentication data by hostname and
+   port. The hostname may be IP address as well. The found authentication
+   method and authentication data is returned to `auth_meth', `auth_data'
+   and `auth_data_len'. The function returns TRUE if authentication method
+   is found and FALSE if not. `conn' may be NULL. */
+
+int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                        char *hostname, uint16 port,
+                        SilcProtocolAuthMeth *auth_meth,
+                        unsigned char **auth_data,
+                        uint32 *auth_data_len)
+{
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  if (app->config && app->config->conns) {
+    SilcClientConfigSectionConnection *conn = NULL;
+
+    /* Check if we find a match from user configured connections */
+    conn = silc_client_config_find_connection(app->config,
+                                             hostname,
+                                             port);
+    if (conn) {
+      /* Match found. Use the configured authentication method */
+      *auth_meth = conn->auth_meth;
+
+      if (conn->auth_data) {
+       *auth_data = strdup(conn->auth_data);
+       *auth_data_len = strlen(conn->auth_data);
+      }
+
+      return TRUE;
+    }
+  }
+
+  *auth_meth = SILC_AUTH_NONE;
+  *auth_data = NULL;
+  *auth_data_len = 0;
+
+  return TRUE;
+}
+
+/* Notifies application that failure packet was received.  This is called
+   if there is some protocol active in the client.  The `protocol' is the
+   protocol context.  The `failure' is opaque pointer to the failure
+   indication.  Note, that the `failure' is protocol dependant and application
+   must explicitly cast it to correct type.  Usually `failure' is 32 bit
+   failure type (see protocol specs for all protocol failure types). */
+
+void silc_failure(SilcClient client, SilcClientConnection conn, 
+                 SilcProtocol protocol, void *failure)
+{
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+    SilcSKEStatus status = (SilcSKEStatus)failure;
+    
+    if (status == SILC_SKE_STATUS_BAD_VERSION)
+      silc_say(client, conn, 
+              "You are running incompatible client version (it may be "
+              "too old or too new)");
+    if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
+      silc_say(client, conn, "Server does not support your public key type");
+    if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
+      silc_say(client, conn, 
+              "Server does not support one of your proposed KE group");
+    if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
+      silc_say(client, conn, 
+              "Server does not support one of your proposed cipher");
+    if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
+      silc_say(client, conn, 
+              "Server does not support one of your proposed PKCS");
+    if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
+      silc_say(client, conn, 
+              "Server does not support one of your proposed hash function");
+    if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
+      silc_say(client, conn, 
+              "Server does not support one of your proposed HMAC");
+    if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
+      silc_say(client, conn, "Incorrect signature");
+  }
+
+  if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+    uint32 err = (uint32)failure;
+
+    if (err == SILC_AUTH_FAILED)
+      silc_say(client, conn, "Authentication failed");
+  }
+}
+
+/* Asks whether the user would like to perform the key agreement protocol.
+   This is called after we have received an key agreement packet or an
+   reply to our key agreement packet. This returns TRUE if the user wants
+   the library to perform the key agreement protocol and FALSE if it is not
+   desired (application may start it later by calling the function
+   silc_client_perform_key_agreement). */
+
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                      SilcClientEntry client_entry, char *hostname,
+                      int port,
+                      SilcKeyAgreementCallback *completion,
+                      void **context)
+{
+  char host[256];
+
+  /* We will just display the info on the screen and return FALSE and user
+     will have to start the key agreement with a command. */
+
+  if (hostname) {
+    memset(host, 0, sizeof(host));
+    snprintf(host, sizeof(host) - 1, "(%s on port %d)", hostname, port); 
+  }
+
+  silc_say(client, conn, "%s wants to perform key agreement %s",
+          client_entry->nickname, hostname ? host : "");
+
+  *completion = NULL;
+  *context = NULL;
+
+  return FALSE;
+}
+
+/* SILC client operations */
+SilcClientOperations ops = {
+  silc_op_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connect,
+  silc_disconnect,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_failure,
+  silc_key_agreement,
+};
diff --git a/apps/silc/client_ops.h b/apps/silc/client_ops.h
new file mode 100644 (file)
index 0000000..eb084ca
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+
+  client_ops.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_OPS_H
+#define CLIENT_OPS_H
+
+void silc_say(SilcClient client, SilcClientConnection conn, char *msg, ...);
+void silc_op_say(SilcClient client, SilcClientConnection conn, 
+                SilcClientMessageType type, char *msg, ...);
+void silc_channel_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, 
+                         SilcChannelEntry channel, 
+                         SilcMessageFlags flags, char *msg);
+void silc_private_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, 
+                         SilcMessageFlags flags, char *msg);
+void silc_notify(SilcClient client, SilcClientConnection conn, 
+                SilcNotifyType type, ...);
+void silc_command(SilcClient client, SilcClientConnection conn, 
+                 SilcClientCommandContext cmd_context, int success,
+                 SilcCommand command);
+void silc_command_reply(SilcClient client, SilcClientConnection conn,
+                       SilcCommandPayload cmd_payload, int success,
+                       SilcCommand command, SilcCommandStatus status, ...);
+void silc_connect(SilcClient client, SilcClientConnection conn, int success);
+void silc_disconnect(SilcClient client, SilcClientConnection conn);
+void silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context);
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context);
+int silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                        char *hostname, uint16 port,
+                        SilcProtocolAuthMeth *auth_meth,
+                        unsigned char **auth_data,
+                        uint32 *auth_data_len);
+void silc_failure(SilcClient client, SilcClientConnection conn, 
+                 SilcProtocol protocol, void *failure);
+int silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                      SilcClientEntry client_entry, char *hostname,
+                      int port,
+                      SilcKeyAgreementCallback *completion,
+                      void **context);
+#endif
index e55157b30fdee38ec48eb3c322a87f1b2314e70a..2fc31faf06b09d4f5833e2537f3af38a5ca3e766 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "clientincludes.h"
 #include "clientconfig.h"
@@ -36,9 +29,11 @@ SilcClientConfigSection silc_client_config_sections[] = {
   { "[cipher]", 
     SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 },
   { "[pkcs]", 
-    SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 2 },
+    SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 1 },
   { "[hash]", 
     SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
+  { "[hmac]", 
+    SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC, 3 },
   { "[connection]", 
     SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
   { "[commands]", 
@@ -60,11 +55,6 @@ SilcClientConfig silc_client_config_alloc(char *filename)
   SILC_LOG_DEBUG(("Allocating new configuration object"));
 
   new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    fprintf(stderr, "Could not allocate new configuration object");
-    return NULL;
-  }
-
   new->filename = filename;
 
   /* Open configuration file and parse it */
@@ -107,7 +97,7 @@ int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer,
                             SilcClientConfigParse *return_config)
 {
   int i, begin;
-  unsigned int linenum;
+  int linenum;
   char line[1024], *cp;
   SilcClientConfigSection *cptr = NULL;
   SilcClientConfigParse parse = *return_config, first = NULL;
@@ -155,7 +145,7 @@ int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer,
       
       /* Check for matching sections */
       for (cptr = silc_client_config_sections; cptr->section; cptr++)
-       if (!strcmp(cp, cptr->section))
+       if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
          break;
 
       if (!cptr->section) {
@@ -284,28 +274,28 @@ int silc_client_config_parse_lines(SilcClientConfig config,
       if (ret < 0)
        break;
 
-      /* Get block length */
+      /* Get key length */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
        break;
       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher block length not defined\n",
+       fprintf(stderr, "%s:%d: Cipher key length not defined\n",
                config->filename, pc->linenum);
        break;
       }
-      config->cipher->block_len = atoi(tmp);
+      config->cipher->key_len = atoi(tmp);
       silc_free(tmp);
 
-      /* Get key length */
+      /* Get block length */
       ret = silc_config_get_token(line, &tmp);
       if (ret < 0)
        break;
       if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher key length not defined\n",
+       fprintf(stderr, "%s:%d: Cipher block length not defined\n",
                config->filename, pc->linenum);
        break;
       }
-      config->cipher->key_len = atoi(tmp);
+      config->cipher->block_len = atoi(tmp);
       silc_free(tmp);
 
       check = TRUE;
@@ -337,18 +327,6 @@ int silc_client_config_parse_lines(SilcClientConfig config,
        break;
       }
 
-      /* Get key length */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: PKCS key length not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      config->pkcs->key_len = atoi(tmp);
-      silc_free(tmp);
-
       check = TRUE;
       break;
 
@@ -411,6 +389,57 @@ int silc_client_config_parse_lines(SilcClientConfig config,
       check = TRUE;
       break;
 
+    case SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC:
+
+      if (!config->hmac) {
+       config->hmac = silc_calloc(1, sizeof(*config->hmac));
+       config->hmac->next = NULL;
+       config->hmac->prev = NULL;
+      } else {
+       if (!config->hmac->next) {
+         config->hmac->next = 
+           silc_calloc(1, sizeof(*config->hmac->next));
+         config->hmac->next->next = NULL;
+         config->hmac->next->prev = config->hmac;
+         config->hmac = config->hmac->next;
+       }
+      }
+
+      /* Get HMAC name */
+      ret = silc_config_get_token(line, &config->hmac->alg_name);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: HMAC name not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+      
+      /* Get Hash function name */
+      ret = silc_config_get_token(line, &config->hmac->sim_name);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: Hash function name not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+
+      /* Get MAC length */
+      ret = silc_config_get_token(line, &tmp);
+      if (ret < 0)
+       break;
+      if (ret == 0) {
+       fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
+               config->filename, pc->linenum);
+       break;
+      }
+      config->hmac->key_len = atoi(tmp);
+      silc_free(tmp);
+
+      check = TRUE;
+      break;
+
     case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION:
 
       if (!config->conns) {
@@ -447,10 +476,10 @@ int silc_client_config_parse_lines(SilcClientConfig config,
        }
 
        if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD))
-         config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+         config->conns->auth_meth = SILC_AUTH_PASSWORD;
 
        if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
-         config->conns->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+         config->conns->auth_meth = SILC_AUTH_PUBLIC_KEY;
 
        silc_free(tmp);
       }
@@ -490,7 +519,9 @@ int silc_client_config_parse_lines(SilcClientConfig config,
       /* Get command line (this may include parameters as well. They
         will be parsed later with standard command parser when
         executing particular command.) */
-      config->commands->command = strdup(line->data);
+      config->commands->command = silc_calloc(strlen(line->data), 
+                                             sizeof(char));
+      memcpy(config->commands->command, line->data, strlen(line->data) - 1);
       if (ret < 0)
        break;
 
@@ -523,6 +554,8 @@ int silc_client_config_parse_lines(SilcClientConfig config,
     config->pkcs = config->pkcs->prev;
   while (config->hash_func && config->hash_func->prev)
     config->hash_func = config->hash_func->prev;
+  while (config->hmac && config->hmac->prev)
+    config->hmac = config->hmac->prev;
   while (config->conns && config->conns->prev)
     config->conns = config->conns->prev;
   while (config->commands && config->commands->prev)
@@ -536,33 +569,43 @@ int silc_client_config_parse_lines(SilcClientConfig config,
 /* Registers configured ciphers. These can then be allocated by the
    client when needed. */
 
-void silc_client_config_register_ciphers(SilcClientConfig config)
+bool silc_client_config_register_ciphers(SilcClientConfig config)
 {
   SilcClientConfigSectionAlg *alg;
-  SilcClient client = (SilcClient)config->client;
+  SilcClientInternal app = (SilcClientInternal)config->client;
+  SilcClient client = app->client;
 
   SILC_LOG_DEBUG(("Registering configured ciphers"));
 
+  if (!config->cipher)
+    return FALSE;
+
   alg = config->cipher;
   while(alg) {
 
     if (!alg->sim_name) {
-      /* Crypto module is supposed to be built in. Nothing to be done
-        here except to test that the cipher really is built in. */
-      SilcCipher tmp = NULL;
+      /* Crypto module is supposed to be built in. Get the pointer to the
+        built in cipher and register it. */
+      int i;
+
+      for (i = 0; silc_default_ciphers[i].name; i++)
+       if (!strcmp(silc_default_ciphers[i].name, alg->alg_name)) {
+         silc_cipher_register(&silc_default_ciphers[i]);
+         break;
+       }
 
-      if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
-       SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name));
+      if (!silc_cipher_is_supported(alg->alg_name)) {
+       SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
        silc_client_stop(client);
        exit(1);
       }
-      silc_cipher_free(tmp);
 
 #ifdef SILC_SIM
     } else {
       /* Load (try at least) the crypto SIM module */
       SilcCipherObject cipher;
       SilcSimContext *sim;
+      char *alg_name;
 
       memset(&cipher, 0, sizeof(cipher));
       cipher.name = alg->alg_name;
@@ -573,34 +616,40 @@ void silc_client_config_register_ciphers(SilcClientConfig config)
       sim->type = SILC_SIM_CIPHER;
       sim->libname = alg->sim_name;
 
+      alg_name = strdup(alg->alg_name);
+      if (strchr(alg_name, '-'))
+       *strchr(alg_name, '-') = '\0';
+
       if ((silc_sim_load(sim))) {
        cipher.set_key = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
+         silc_sim_getsym(sim, silc_sim_symname(alg_name, 
                                                SILC_CIPHER_SIM_SET_KEY));
        SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
        cipher.set_key_with_string = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
-                                               SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
+         silc_sim_getsym(sim, silc_sim_symname(alg_name, 
+                                        SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
        SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
        cipher.encrypt = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_ENCRYPT_CBC));
        SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
         cipher.decrypt = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_DECRYPT_CBC));
        SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
         cipher.context_len = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_CONTEXT_LEN));
        SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
 
        /* Put the SIM to the table of all SIM's in client */
-       client->sim = silc_realloc(client->sim,
-                                  sizeof(*client->sim) * 
-                                  (client->sim_count + 1));
-       client->sim[client->sim_count] = sim;
-       client->sim_count++;
+       app->sim = silc_realloc(app->sim,
+                                  sizeof(*app->sim) * 
+                                  (app->sim_count + 1));
+       app->sim[app->sim_count] = sim;
+       app->sim_count++;
+
+       silc_free(alg_name);
       } else {
        SILC_LOG_ERROR(("Error configuring ciphers"));
        silc_client_stop(client);
@@ -614,60 +663,74 @@ void silc_client_config_register_ciphers(SilcClientConfig config)
 
     alg = alg->next;
   }
+
+  return TRUE;
 }
 
 /* Registers configured PKCS's. */
-/* XXX: This really doesn't do anything now since we have statically
-   registered our PKCS's. This should be implemented when PKCS works
-   as SIM's. This checks now only that the PKCS user requested is 
-   really out there. */
 
-void silc_client_config_register_pkcs(SilcClientConfig config)
+bool silc_client_config_register_pkcs(SilcClientConfig config)
 {
   SilcClientConfigSectionAlg *alg = config->pkcs;
-  SilcClient client = (SilcClient)config->client;
-  SilcPKCS tmp = NULL;
+  SilcClientInternal app = (SilcClientInternal)config->client;
+  SilcClient client = app->client;
 
   SILC_LOG_DEBUG(("Registering configured PKCS"));
 
-  while(alg) {
+  if (!alg)
+    return FALSE;
 
-    if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) {
-      SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name));
+  while(alg) {
+    int i;
+    
+    for (i = 0; silc_default_pkcs[i].name; i++)
+      if (!strcmp(silc_default_pkcs[i].name, alg->alg_name)) {
+       silc_pkcs_register(&silc_default_pkcs[i]);
+       break;
+      }
+    
+    if (!silc_pkcs_is_supported(alg->alg_name)) {
+      SILC_LOG_ERROR(("Unknown PKCS `%s'", alg->alg_name));
       silc_client_stop(client);
       exit(1);
     }
-    silc_free(tmp);
 
     alg = alg->next;
   }
+
+  return TRUE;
 }
 
-/* Registers configured hash functions. These can then be allocated by the
+/* Registers configured hash funtions. These can then be allocated by the
    client when needed. */
 
-void silc_client_config_register_hashfuncs(SilcClientConfig config)
+bool silc_client_config_register_hashfuncs(SilcClientConfig config)
 {
   SilcClientConfigSectionAlg *alg;
-  SilcClient client = (SilcClient)config->client;
+  SilcClientInternal app = (SilcClientInternal)config->client;
+  SilcClient client = app->client;
 
   SILC_LOG_DEBUG(("Registering configured hash functions"));
 
+  if (!config->hash_func)
+    return FALSE;
+
   alg = config->hash_func;
   while(alg) {
-
     if (!alg->sim_name) {
-      /* Hash module is supposed to be built in. Nothing to be done
-        here except to test that the hash function really is built in. */
-      SilcHash tmp = NULL;
-
-      if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
-       SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name));
+      int i;
+      
+      for (i = 0; silc_default_hash[i].name; i++)
+       if (!strcmp(silc_default_hash[i].name, alg->alg_name)) {
+         silc_hash_register(&silc_default_hash[i]);
+         break;
+       }
+      
+      if (!silc_hash_is_supported(alg->alg_name)) {
+       SILC_LOG_ERROR(("Unknown hash function `%s'", alg->alg_name));
        silc_client_stop(client);
        exit(1);
       }
-      silc_free(tmp);
-
 #ifdef SILC_SIM
     } else {
       /* Load (try at least) the hash SIM module */
@@ -702,26 +765,63 @@ void silc_client_config_register_hashfuncs(SilcClientConfig config)
        SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
 
        /* Put the SIM to the table of all SIM's in client */
-       client->sim = silc_realloc(client->sim,
-                                  sizeof(*client->sim) * 
-                                  (client->sim_count + 1));
-       client->sim[client->sim_count] = sim;
-       client->sim_count++;
+       app->sim = silc_realloc(app->sim,
+                                  sizeof(*app->sim) * 
+                                  (app->sim_count + 1));
+       app->sim[app->sim_count] = sim;
+       app->sim_count++;
       } else {
        SILC_LOG_ERROR(("Error configuring hash functions"));
        silc_client_stop(client);
        exit(1);
       }
 
-      /* Register the cipher */
+      /* Register the hash function */
       silc_hash_register(&hash);
 #endif
     }
-
     alg = alg->next;
   }
+
+  return TRUE;
 }
 
+/* Registers configured HMACs. These can then be allocated by the
+   client when needed. */
+
+bool silc_client_config_register_hmacs(SilcClientConfig config)
+{
+  SilcClientConfigSectionAlg *alg;
+  SilcClientInternal app = (SilcClientInternal)config->client;
+  SilcClient client = app->client;
+
+  SILC_LOG_DEBUG(("Registering configured HMACs"));
+
+  if (!config->hmac)
+    return FALSE;
+
+  alg = config->hmac;
+  while(alg) {
+    SilcHmacObject hmac;
+    
+    if (!silc_hash_is_supported(alg->sim_name)) {
+      SILC_LOG_ERROR(("Unknown hash function `%s' for HMAC `%s'", 
+                     alg->sim_name, alg->alg_name));
+      silc_client_stop(client);
+      exit(1);
+    }
+    
+    /* Register the HMAC */
+    memset(&hmac, 0, sizeof(hmac));
+    hmac.name = alg->alg_name;
+    hmac.len = alg->key_len;
+    silc_hmac_register(&hmac);
+
+    alg = alg->next;
+  }
+
+  return TRUE;
+}
 
 SilcClientConfigSectionConnection *
 silc_client_config_find_connection(SilcClientConfig config, 
index f2c7a4afa97119a6b846ed12bf22c990eba32a3f..7644c030c2b5e5858a3508a9ee1599f239ec190b 100644 (file)
@@ -25,8 +25,8 @@
 typedef struct SilcClientConfigSectionAlgStruct {
   char *alg_name;
   char *sim_name;
-  unsigned int block_len;
-  unsigned int key_len;
+  uint32 block_len;
+  uint32 key_len;
   struct SilcClientConfigSectionAlgStruct *next;
   struct SilcClientConfigSectionAlgStruct *prev;
 #define SILC_CLIENT_CONFIG_MODNAME "builtin"
@@ -37,7 +37,7 @@ typedef struct SilcClientConfigSectionConnectionStruct {
   char *host;
   int auth_meth;
   char *auth_data;
-  unsigned short port;
+  uint16 port;
   struct SilcClientConfigSectionConnectionStruct *next;
   struct SilcClientConfigSectionConnectionStruct *prev;
 #define SILC_CLIENT_CONFIG_AUTH_METH_PASSWD "passwd"
@@ -69,6 +69,7 @@ typedef struct {
   SilcClientConfigSectionAlg *cipher;
   SilcClientConfigSectionAlg *pkcs;
   SilcClientConfigSectionAlg *hash_func;
+  SilcClientConfigSectionAlg *hmac;
   SilcClientConfigSectionConnection *conns;
   SilcClientConfigSectionCommand *commands;
 } SilcClientConfigObject;
@@ -81,6 +82,7 @@ typedef enum {
   SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER,
   SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS,
   SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION,
+  SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC,
   SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION,
   SILC_CLIENT_CONFIG_SECTION_TYPE_COMMAND = 253, /* Special section */
 } SilcClientConfigSectionType;
@@ -89,7 +91,7 @@ typedef enum {
 typedef struct {
   const char *section;
   SilcClientConfigSectionType type;
-  unsigned int maxfields;
+  int maxfields;
 } SilcClientConfigSection;
 
 /* List of all possible config sections in SILC client */
@@ -99,7 +101,7 @@ extern SilcClientConfigSection silc_client_config_sections[];
    from a file to this structure before parsing it further. */
 typedef struct SilcClientConfigParseStruct {
   SilcBuffer line;
-  unsigned int linenum;
+  int linenum;
   SilcClientConfigSection *section;
   struct SilcClientConfigParseStruct *next;
   struct SilcClientConfigParseStruct *prev;
@@ -112,11 +114,12 @@ int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer,
                             SilcClientConfigParse *return_config);
 int silc_client_config_parse_lines(SilcClientConfig config, 
                                   SilcClientConfigParse parse_config);
-int silc_client_config_check_sections(unsigned int checkmask);
+int silc_client_config_check_sections(uint32 checkmask);
 void silc_client_config_setlogfiles(SilcClientConfig config);
-void silc_client_config_register_ciphers(SilcClientConfig config);
-void silc_client_config_register_pkcs(SilcClientConfig config);
-void silc_client_config_register_hashfuncs(SilcClientConfig config);
+bool silc_client_config_register_ciphers(SilcClientConfig config);
+bool silc_client_config_register_pkcs(SilcClientConfig config);
+bool silc_client_config_register_hashfuncs(SilcClientConfig config);
+bool silc_client_config_register_hmacs(SilcClientConfig config);
 SilcClientConfigSectionConnection *
 silc_client_config_find_connection(SilcClientConfig config, 
                                   char *host, int port);
similarity index 80%
rename from includes/clientincludes.h
rename to apps/silc/clientincludes.h
index 479c000732249771b22daefaca7770e2cc3e44f5..f6b56f144d9d02b8c592d9a1bc6f56428b9dbf38 100644 (file)
 #ifndef CLIENTINCLUDES_H
 #define CLIENTINCLUDES_H
 
-#include <curses.h>
-#include <paths.h>
-#include <sys/param.h>
-#include <pwd.h>
+#include "silcdefs.h"
 
 /* Generic includes */
 #include "silcincludes.h"
+#include "clientlibincludes.h"
+
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+#include <sys/param.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
 
 /* SILC Client includes */
-#include "idlist.h"
 #include "screen.h"
 #include "clientconfig.h"
-#include "client.h"
+#include "local_command.h"
 #include "clientutil.h"
-#include "protocol.h"
-#include "command.h"
-#include "command_reply.h"
 #include "silc.h"
+#include "client_ops.h"
 
 #endif
index 21043795c0249108bc680645c5583a99488b9461..1bcc9c8bc3fde0d889130218400c74f69c6c3ed3 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "clientincludes.h"
 
-/* Internal routine used to print lines to window. This can split the
+/* Routine used to print lines to window. This can split the
    line neatly if a word would overlap the line. */
 
-static void silc_print_to_window(WINDOW *win, char *message)
+void silc_print_to_window(WINDOW *win, char *message)
 {
   int str_len, len;
 
@@ -62,36 +55,14 @@ static void silc_print_to_window(WINDOW *win, char *message)
   wrefresh(win);
 }
 
-/* Prints a message with three star (*) sign before the actual message
-   on the current output window. This is used to print command outputs
-   and error messages. */
-/* XXX Change to accept SilcClientWindow and use output window 
-   from there (the pointer to the output window must be added to the
-   SilcClientWindow object. */
-
-void silc_say(SilcClient client, char *msg, ...)
-{
-  va_list vp;
-  char message[1024];
-  
-  memset(message, 0, sizeof(message));
-  strncat(message, "\n***  ", 5);
-
-  va_start(vp, msg);
-  vsprintf(message + 5, msg, vp);
-  va_end(vp);
-  
-  /* Print the message */
-  silc_print_to_window(client->screen->output_win[0], message);
-}
-
 /* Prints message to the screen. This is used to print the messages
    user is typed and message that came on channels. */
 
 void silc_print(SilcClient client, char *msg, ...)
 {
   va_list vp;
-  char message[1024];
+  char message[2048];
+  SilcClientInternal app = client->application;
   
   memset(message, 0, sizeof(message));
   strncat(message, "\n ", 2);
@@ -101,7 +72,7 @@ void silc_print(SilcClient client, char *msg, ...)
   va_end(vp);
   
   /* Print the message */
-  silc_print_to_window(client->screen->output_win[0], message);
+  silc_print_to_window(app->screen->output_win[0], message);
 }
 
 /* Returns user's mail path */
@@ -110,8 +81,13 @@ char *silc_get_mail_path()
 {
   char pathbuf[MAXPATHLEN];
   char *path;
+
+#ifndef _PATH_MAILDIR
+#define _PATH_MAILDIR "/var/mail"
+#endif
   
-  if ((path = (char *)getenv("MAIL")) != 0) {
+  path = getenv("MAIL");
+  if (path) {
     strncpy(pathbuf, path, strlen(path));
   } else {
     strcpy(pathbuf, _PATH_MAILDIR);
@@ -138,7 +114,7 @@ int silc_get_number_of_emails()
     fprintf(stderr, "Couldn't open mail file (%s).\n", filename);
   } else {
     while((fscanf(tl, "%s", data)) != EOF) { 
-      if(!strcmp(data, "Subject:"))
+      if(!strcmp(data, "From:"))
        num++;
     }
     
@@ -148,53 +124,6 @@ int silc_get_number_of_emails()
   return num;
 }
 
-/* Returns the username of the user. If the global variable LOGNAME
-   does not exists we will get the name from the password file. */
-
-char *silc_get_username()
-{
-  char *logname = NULL;
-  
-  logname = strdup(getenv("LOGNAME"));
-  if (!logname) {
-    logname = getlogin();
-    if (!logname) {
-      struct passwd *pw;
-
-      pw = getpwuid(getuid());
-      if (!pw) {
-       fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
-       return NULL;
-      }
-      
-      logname = strdup(pw->pw_name);
-    }
-  }
-  
-  return logname;
-}                          
-
-/* Returns the real name of ther user. */
-
-char *silc_get_real_name()
-{
-  char *realname = NULL;
-  struct passwd *pw;
-    
-  pw = getpwuid(getuid());
-  if (!pw) {
-    fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
-    return NULL;
-  }
-
-  if (strchr(pw->pw_gecos, ','))
-    *strchr(pw->pw_gecos, ',') = 0;
-
-  realname = strdup(pw->pw_gecos);
-
-  return realname;
-}
-
 /* Returns time til next minute changes. Used to update the clock when
    needed. */
 
@@ -209,48 +138,39 @@ int silc_client_time_til_next_min()
   return 60 - min->tm_sec;
 }
 
-/* Asks passphrase from user on the input line. */
+/* Asks yes/no from user on the input line. Returns TRUE on "yes" and
+   FALSE on "no". */
 
-char *silc_client_ask_passphrase(SilcClient client)
+int silc_client_ask_yes_no(SilcClient client, char *prompt)
 {
-  char pass1[256], pass2[256];
-  char *ret;
-  int try = 3;
-
-  while(try) {
-
-    /* Print prompt */
-    wattroff(client->screen->input_win, A_INVIS);
-    silc_screen_input_print_prompt(client->screen, "Passphrase: ");
-    wattron(client->screen->input_win, A_INVIS);
-    
-    /* Get string */
-    memset(pass1, 0, sizeof(pass1));
-    wgetnstr(client->screen->input_win, pass1, sizeof(pass1));
-    
-    /* Print retype prompt */
-    wattroff(client->screen->input_win, A_INVIS);
-    silc_screen_input_print_prompt(client->screen, "Retype passphrase: ");
-    wattron(client->screen->input_win, A_INVIS);
-    
-    /* Get string */
-    memset(pass2, 0, sizeof(pass2));
-    wgetnstr(client->screen->input_win, pass2, sizeof(pass2));
-
-    if (!strncmp(pass1, pass2, strlen(pass2)))
-      break;
-
-    try--;
+  SilcClientInternal app = (SilcClientInternal)client->application;
+  char answer[4];
+  int ret;
+
+ again:
+  silc_screen_input_reset(app->screen);
+
+  /* Print prompt */
+  wattroff(app->screen->input_win, A_INVIS);
+  silc_screen_input_print_prompt(app->screen, prompt);
+
+  /* Get string */
+  memset(answer, 0, sizeof(answer));
+  echo();
+  wgetnstr(app->screen->input_win, answer, sizeof(answer));
+  if (!strncasecmp(answer, "yes", strlen(answer)) ||
+      !strncasecmp(answer, "y", strlen(answer))) {
+    ret = TRUE;
+  } else if (!strncasecmp(answer, "no", strlen(answer)) ||
+            !strncasecmp(answer, "n", strlen(answer))) {
+    ret = FALSE;
+  } else {
+    silc_say(client, app->conn, "Type yes or no");
+    goto again;
   }
+  noecho();
 
-  ret = silc_calloc(strlen(pass1), sizeof(char));
-  memcpy(ret, pass1, strlen(pass1));
-
-  memset(pass1, 0, sizeof(pass1));
-  memset(pass2, 0, sizeof(pass2));
-
-  wattroff(client->screen->input_win, A_INVIS);
-  silc_screen_input_reset(client->screen);
+  silc_screen_input_reset(app->screen);
 
   return ret;
 }
@@ -259,21 +179,27 @@ char *silc_client_ask_passphrase(SilcClient client)
 
 void silc_client_list_ciphers()
 {
-
+  char *ciphers = silc_cipher_get_supported();
+  fprintf(stdout, "%s\n", ciphers);
+  silc_free(ciphers);
 }
 
 /* Lists supported (builtin) hash functions */
 
 void silc_client_list_hash_funcs()
 {
-
+  char *hash = silc_hash_get_supported();
+  fprintf(stdout, "%s\n", hash);
+  silc_free(hash);
 }
 
 /* Lists supported PKCS algorithms */
 
 void silc_client_list_pkcs()
 {
-
+  char *pkcs = silc_pkcs_get_supported();
+  fprintf(stdout, "%s\n", pkcs);
+  silc_free(pkcs);
 }
 
 /* Displays input prompt on command line and takes input data from user */
@@ -286,7 +212,7 @@ char *silc_client_get_input(const char *prompt)
   fd = open("/dev/tty", O_RDONLY);
   if (fd < 0) {
     fprintf(stderr, "silc: %s\n", strerror(errno));
-    exit(1);
+    return NULL;
   }
 
   memset(input, 0, sizeof(input));
@@ -296,7 +222,7 @@ char *silc_client_get_input(const char *prompt)
 
   if ((read(fd, input, sizeof(input))) < 0) {
     fprintf(stderr, "silc: %s\n", strerror(errno));
-    exit(1);
+    return NULL;
   }
 
   if (strlen(input) <= 1)
@@ -323,7 +249,7 @@ char *silc_client_get_passphrase(const char *prompt)
   fd = open("/dev/tty", O_RDONLY);
   if (fd < 0) {
     fprintf(stderr, "silc: %s\n", strerror(errno));
-    exit(1);
+    return NULL;
   }
 
   signal(SIGINT, SIG_IGN);
@@ -343,7 +269,7 @@ char *silc_client_get_passphrase(const char *prompt)
 
   if ((read(fd, input, sizeof(input))) < 0) {
     fprintf(stderr, "silc: %s\n", strerror(errno));
-    exit(1);
+    return NULL;
   }
 
   if (strlen(input) <= 1) {
@@ -367,18 +293,52 @@ char *silc_client_get_passphrase(const char *prompt)
 #endif
 }
 
+/* Returns identifier string for public key generation. */
+
+char *silc_client_create_identifier()
+{
+  char *username = NULL, *realname = NULL;
+  char hostname[256], email[256];
+  
+  /* Get realname */
+  realname = silc_get_real_name();
+
+  /* Get hostname */
+  memset(hostname, 0, sizeof(hostname));
+  gethostname(hostname, sizeof(hostname));
+
+  /* Get username (mandatory) */
+  username = silc_get_username();
+  if (!username)
+    return NULL;
+
+  /* Create default email address, whether it is right or not */
+  snprintf(email, sizeof(email), "%s@%s", username, hostname);
+
+  return silc_pkcs_encode_identifier(username, hostname, realname, email,
+                                    NULL, NULL);
+}
+
 /* Creates new public key and private key pair. This is used only
    when user wants to create new key pair from command line. */
 
-void silc_client_create_key_pair(char *pkcs_name, int bits)
+int silc_client_create_key_pair(char *pkcs_name, int bits,
+                               char *public_key, char *private_key,
+                               char *identifier, 
+                               SilcPublicKey *ret_pub_key,
+                               SilcPrivateKey *ret_prv_key)
 {
   SilcPKCS pkcs;
+  SilcPublicKey pub_key;
+  SilcPrivateKey prv_key;
   SilcRng rng;
   unsigned char *key;
-  unsigned int key_len;
+  uint32 key_len;
+  char line[256];
   char *pkfile = NULL, *prvfile = NULL;
 
-  printf("\
+  if (!pkcs_name || !public_key || !private_key)
+    printf("\
 New pair of keys will be created.  Please, answer to following questions.\n\
 ");
 
@@ -396,6 +356,11 @@ New pair of keys will be created.  Please, answer to following questions.\n\
     }
   }
 
+  if (!silc_pkcs_is_supported(pkcs_name)) {
+    fprintf(stderr, "Unknown PKCS `%s'", pkcs_name);
+    return FALSE;
+  }
+
   if (!bits) {
     char *length = NULL;
     length = 
@@ -406,39 +371,401 @@ New pair of keys will be created.  Please, answer to following questions.\n\
       bits = atoi(length);
   }
 
+  if (!identifier) {
+    char *def = silc_client_create_identifier();
+
+    memset(line, 0, sizeof(line));
+    if (def)
+      snprintf(line, sizeof(line), "Identifier [%s]: ", def);
+    else
+      snprintf(line, sizeof(line),
+              "Identifier (eg. UN=jon, HN=jon.dummy.com, "
+              "RN=Jon Johnson, E=jon@dummy.com): ");
+
+    while (!identifier) {
+      identifier = silc_client_get_input(line);
+      if (!identifier && def)
+       identifier = strdup(def);
+    }
+
+    if (def)
+      silc_free(def);
+  }
+
   rng = silc_rng_alloc();
   silc_rng_init(rng);
-  silc_math_primegen_init();
-
- again_pk:
-  pkfile = silc_client_get_input("Public key filename: ");
-  if (!pkfile) {
-    printf("Public key filename must be defined\n");
-    goto again_pk;
+  silc_rng_global_init(rng);
+
+  if (!public_key) {
+    memset(line, 0, sizeof(line));
+    snprintf(line, sizeof(line), "Public key filename [%s] ", 
+            SILC_CLIENT_PUBLIC_KEY_NAME);
+    pkfile = silc_client_get_input(line);
+    if (!pkfile)
+      pkfile = SILC_CLIENT_PUBLIC_KEY_NAME;
+  } else {
+    pkfile = public_key;
   }
 
- again_prv:
-  prvfile = silc_client_get_input("Private key filename: ");
-  if (!prvfile) {
-    printf("Private key filename must be defined\n");
-    goto again_prv;
+  if (!private_key) {
+    memset(line, 0, sizeof(line));
+    snprintf(line, sizeof(line), "Public key filename [%s] ", 
+            SILC_CLIENT_PRIVATE_KEY_NAME);
+    prvfile = silc_client_get_input(line);
+    if (!prvfile)
+      prvfile = SILC_CLIENT_PRIVATE_KEY_NAME;
+  } else {
+    prvfile = private_key;
   }
 
   /* Generate keys */
   silc_pkcs_alloc(pkcs_name, &pkcs);
   pkcs->pkcs->init(pkcs->context, bits, rng);
 
-  /* Save keys into file */
+  /* Save public key into file */
   key = silc_pkcs_get_public_key(pkcs, &key_len);
-  silc_pkcs_save_public_key(pkcs, pkfile, key, key_len);
+  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
+                                      key, key_len);
+  silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
+  if (ret_pub_key)
+    *ret_pub_key = pub_key;
+
   memset(key, 0, sizeof(key_len));
   silc_free(key);
+
+  /* Save private key into file */
   key = silc_pkcs_get_private_key(pkcs, &key_len);
-  silc_pkcs_save_private_key(pkcs, prvfile, key, key_len, "");
+  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
+
+  silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
+  if (ret_prv_key)
+    *ret_prv_key = prv_key;
+
+  printf("Public key has been saved into `%s'.\n", pkfile);
+  printf("Private key has been saved into `%s'.\n", prvfile);
+  printf("Press <Enter> to continue...\n");
+  getchar();
+
   memset(key, 0, sizeof(key_len));
   silc_free(key);
 
-  silc_math_primegen_uninit();
   silc_rng_free(rng);
   silc_pkcs_free(pkcs);
+
+  return TRUE;
+}
+
+/* This checks stats for various SILC files and directories. First it 
+   checks if ~/.silc directory exist and is owned by the correct user. If 
+   it doesn't exist, it will create the directory. After that it checks if
+   user's Public and Private key files exists and that they aren't expired.
+   If they doesn't exist or they are expired, they will be (re)created
+   after return. */
+
+int silc_client_check_silc_dir()
+{
+  char filename[256], file_public_key[256], file_private_key[256];
+  char servfilename[256], clientfilename[256];
+  char *identifier;
+  struct stat st;
+  struct passwd *pw;
+  int firstime = FALSE;
+  time_t curtime, modtime;
+
+  SILC_LOG_DEBUG(("Checking ~./silc directory"));
+
+  memset(filename, 0, sizeof(filename));
+  memset(file_public_key, 0, sizeof(file_public_key));
+  memset(file_private_key, 0, sizeof(file_private_key));
+
+  pw = getpwuid(getuid());
+  if (!pw) {
+    fprintf(stderr, "silc: %s\n", strerror(errno));
+    return FALSE;
+  }
+
+  identifier = silc_client_create_identifier();
+
+  /* We'll take home path from /etc/passwd file to be sure. */
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/", pw->pw_dir);
+  snprintf(servfilename, sizeof(servfilename) - 1, "%s/.silc/serverkeys", 
+          pw->pw_dir);
+  snprintf(clientfilename, sizeof(clientfilename) - 1, "%s/.silc/clientkeys", 
+          pw->pw_dir);
+
+  /*
+   * Check ~/.silc directory
+   */
+  if ((stat(filename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(filename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", filename);
+         return FALSE;
+       }
+
+       /* Directory was created. First time running SILC */
+       firstime = TRUE;
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               filename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  } else {
+    
+    /* Check the owner of the dir */
+    if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
+      fprintf(stderr, "You don't seem to own `%s' directory\n",
+             filename);
+      return FALSE;
+    }
+    
+    /* Check the permissions of the dir */
+    if ((st.st_mode & 0777) != 0755) {
+      if ((chmod(filename, 0755)) == -1) {
+       fprintf(stderr, "Permissions for `%s' directory must be 0755\n", 
+               filename);
+       return FALSE;
+      }
+    }
+  }
+
+  /*
+   * Check ~./silc/serverkeys directory
+   */
+  if ((stat(servfilename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(servfilename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", servfilename);
+         return FALSE;
+       }
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               servfilename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+  
+  /*
+   * Check ~./silc/clientkeys directory
+   */
+  if ((stat(clientfilename, &st)) == -1) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {
+      if (pw->pw_uid == geteuid()) {
+       if ((mkdir(clientfilename, 0755)) == -1) {
+         fprintf(stderr, "Couldn't create `%s' directory\n", clientfilename);
+         return FALSE;
+       }
+      } else {
+       fprintf(stderr, "Couldn't create `%s' directory due to a wrong uid!\n",
+               clientfilename);
+       return FALSE;
+      }
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+  
+  /*
+   * Check Public and Private keys
+   */
+  snprintf(file_public_key, sizeof(file_public_key) - 1, "%s%s", 
+          filename, SILC_CLIENT_PUBLIC_KEY_NAME);
+  snprintf(file_private_key, sizeof(file_private_key) - 1, "%s%s", 
+          filename, SILC_CLIENT_PRIVATE_KEY_NAME);
+  
+  /* If running SILC first time */
+  if (firstime) {
+    fprintf(stdout, "Running SILC for the first time\n");
+    silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                               SILC_CLIENT_DEF_PKCS_LEN,
+                               file_public_key, file_private_key, 
+                               identifier, NULL, NULL);
+    return TRUE;
+  }
+  
+  if ((stat(file_public_key, &st)) == -1) {
+    /* If file doesn't exist */
+    if (errno == ENOENT) {
+      fprintf(stdout, "Your public key doesn't exist\n");
+      silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                                 SILC_CLIENT_DEF_PKCS_LEN,
+                                 file_public_key, 
+                                 file_private_key, identifier, NULL, NULL);
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+
+  if ((stat(file_private_key, &st)) == -1) {
+    /* If file doesn't exist */
+    if (errno == ENOENT) {
+      fprintf(stdout, "Your private key doesn't exist\n");
+      silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                                 SILC_CLIENT_DEF_PKCS_LEN,
+                                 file_public_key, 
+                                 file_private_key, identifier, NULL, NULL);
+    } else {
+      fprintf(stderr, "%s\n", strerror(errno));
+      return FALSE;
+    }
+  }
+    
+  /* Check the owner of the public key */
+  if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
+    fprintf(stderr, "You don't seem to own your public key!?\n");
+    return FALSE;
+  }
+  
+  /* Check the owner of the private key */
+  if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { 
+    fprintf(stderr, "You don't seem to own your private key!?\n");
+    return FALSE;
+  }
+    
+  /* Check the permissions for the private key */
+  if ((st.st_mode & 0777) != 0600) {
+    fprintf(stderr, "Wrong permissions in your private key file `%s'!\n"
+           "Trying to change them ... ", file_private_key);
+    if ((chmod(file_private_key, 0600)) == -1) {
+      fprintf(stderr,
+             "Failed to change permissions for private key file!\n" 
+             "Permissions for your private key file must be 0600.\n");
+      return FALSE;
+    }
+    fprintf(stderr, "Done.\n\n");
+  }
+
+  /* See if the key has expired. */
+  modtime = st.st_mtime;       /* last modified */
+  curtime = time(0) - modtime;
+    
+  /* 86400 is seconds in a day. */
+  if (curtime >= (86400 * SILC_CLIENT_KEY_EXPIRES)) {
+    fprintf(stdout, 
+           "--------------------------------------------------\n"
+           "Your private key has expired and needs to be\n" 
+           "recreated.  This will be done automatically now.\n"
+           "Your new key will expire in %d days from today.\n"
+           "--------------------------------------------------\n",
+           SILC_CLIENT_KEY_EXPIRES);
+
+    silc_client_create_key_pair(SILC_CLIENT_DEF_PKCS, 
+                               SILC_CLIENT_DEF_PKCS_LEN,
+                               file_public_key, 
+                               file_private_key, identifier, NULL, NULL);
+  }
+  
+  if (identifier)
+    silc_free(identifier);
+
+  return TRUE;
+}
+
+/* Loads public and private key from files. */
+
+int silc_client_load_keys(SilcClient client)
+{
+  char filename[256];
+  struct passwd *pw;
+
+  SILC_LOG_DEBUG(("Loading public and private keys"));
+
+  pw = getpwuid(getuid());
+  if (!pw)
+    return FALSE;
+
+  memset(filename, 0, sizeof(filename));
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
+          pw->pw_dir, SILC_CLIENT_PRIVATE_KEY_NAME);
+
+  if (silc_pkcs_load_private_key(filename, &client->private_key,
+                                SILC_PKCS_FILE_BIN) == FALSE)
+    if (silc_pkcs_load_private_key(filename, &client->private_key,
+                                  SILC_PKCS_FILE_PEM) == FALSE)
+      return FALSE;
+
+  memset(filename, 0, sizeof(filename));
+  snprintf(filename, sizeof(filename) - 1, "%s/.silc/%s", 
+          pw->pw_dir, SILC_CLIENT_PUBLIC_KEY_NAME);
+
+  if (silc_pkcs_load_public_key(filename, &client->public_key,
+                               SILC_PKCS_FILE_PEM) == FALSE)
+    if (silc_pkcs_load_public_key(filename, &client->public_key,
+                                 SILC_PKCS_FILE_BIN) == FALSE)
+      return FALSE;
+
+  return TRUE;
+}
+
+/* Dumps the public key on screen. Used from the command line option. */
+
+int silc_client_show_key(char *keyfile)
+{
+  SilcPublicKey public_key;
+  SilcPublicKeyIdentifier ident;
+  char *fingerprint;
+  unsigned char *pk;
+  uint32 pk_len;
+  SilcPKCS pkcs;
+  int key_len = 0;
+
+  if (silc_pkcs_load_public_key(keyfile, &public_key,
+                               SILC_PKCS_FILE_PEM) == FALSE)
+    if (silc_pkcs_load_public_key(keyfile, &public_key,
+                                 SILC_PKCS_FILE_BIN) == FALSE) {
+      fprintf(stderr, "Could not load public key file `%s'\n", keyfile);
+      return FALSE;
+    }
+
+  ident = silc_pkcs_decode_identifier(public_key->identifier);
+
+  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+
+  if (silc_pkcs_alloc(public_key->name, &pkcs)) {
+    key_len = silc_pkcs_public_key_set(pkcs, public_key);
+    silc_pkcs_free(pkcs);
+  }
+
+  printf("Public key file    : %s\n", keyfile);
+  printf("Algorithm          : %s\n", public_key->name);
+  if (key_len)
+    printf("Key length (bits)  : %d\n", key_len);
+  if (ident->realname)
+    printf("Real name          : %s\n", ident->realname);
+  if (ident->username)
+    printf("Username           : %s\n", ident->username);
+  if (ident->host)
+    printf("Hostname           : %s\n", ident->host);
+  if (ident->email)
+    printf("Email              : %s\n", ident->email);
+  if (ident->org)
+    printf("Organization       : %s\n", ident->org);
+  if (ident->country)
+    printf("Country            : %s\n", ident->country);
+  printf("Fingerprint (SHA1) : %s\n", fingerprint); 
+
+  fflush(stdout);
+
+  silc_free(fingerprint);
+  silc_free(pk);
+  silc_pkcs_public_key_free(public_key);
+  silc_pkcs_free_identifier(ident);
+
+  return TRUE;
 }
index e18cf2d71eef0a5c8f576d3057137e1a8403773c..b42151989e49b0aac8559a3f4b644c301599a314 100644 (file)
 #define CLIENTUTIL_H
 
 /* Prototypes */
-void silc_say(SilcClient client, char *msg, ...);
+void silc_print_to_window(WINDOW *win, char *message);
 void silc_print(SilcClient client, char *msg, ...);
 char *silc_get_mail_path();
 int silc_get_number_of_emails();
-char *silc_get_username();
-char *silc_get_real_name();
 int silc_client_time_til_next_min();
-char *silc_client_ask_passphrase(SilcClient client);
+int silc_client_ask_yes_no(SilcClient client, char *prompt);
 char *silc_client_get_input(const char *prompt);
 char *silc_client_get_passphrase(const char *prompt);
 void silc_client_list_ciphers();
 void silc_client_list_hash_funcs();
 void silc_client_list_pkcs();
-void silc_client_create_key_pair(char *pkcs_name, int bits);
+char *silc_client_create_identifier();
+int silc_client_create_key_pair(char *pkcs_name, int bits,
+                               char *public_key, char *private_key,
+                               char *identifier, 
+                               SilcPublicKey *ret_pub_key,
+                               SilcPrivateKey *ret_prv_key);
+int silc_client_check_silc_dir();
+int silc_client_load_keys(SilcClient client);
+int silc_client_show_key(char *keyfile);
 
 #endif
diff --git a/apps/silc/command.c b/apps/silc/command.c
deleted file mode 100644 (file)
index 96c8e3c..0000000
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
-
-  command.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "clientincludes.h"
-
-/* Client command list. */
-SilcClientCommand silc_command_list[] =
-{
-  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
-                 SILC_CF_LAG | SILC_CF_REG, 3),
-  SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
-  SILC_CLIENT_CMD(kill, KILL, "KILL", 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(oper, OPER, "OPER",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(restart, RESTART, "RESTART",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(die, DIE, "DIE",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
-  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
-  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
-  SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
-
-  /*
-   * Local. client specific commands
-   */
-  SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
-  SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
-  SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
-  SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
-  SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
-  SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
-
-  { NULL, 0, NULL, 0},
-};
-
-/* List of pending commands. */
-SilcClientCommandPending *silc_command_pending = NULL;
-
-/* Add new pending command to the list of pending commands. Currently
-   pending commands are executed from command replies, thus we can
-   execute any command after receiving some specific command reply.
-
-   The argument `reply_cmd' is the command reply from where the callback
-   function is to be called, thus, it IS NOT the command to be executed.
-
-   XXX: If needed in the future this support may be extended for
-   commands as well, when any command could be executed after executing
-   some specific command. */
-
-void silc_client_command_pending(SilcCommand reply_cmd,
-                                SilcClientCommandCallback callback,
-                                void *context)
-{
-  SilcClientCommandPending *reply, *r;
-
-  reply = silc_calloc(1, sizeof(*reply));
-  reply->reply_cmd = reply_cmd;
-  reply->context = context;
-  reply->callback = callback;
-
-  if (silc_command_pending == NULL) {
-    silc_command_pending = reply;
-    return;
-  }
-
-  for (r = silc_command_pending; r; r = r->next) {
-    if (r->next == NULL) {
-      r->next = reply;
-      break;
-    }
-  }
-}
-
-/* Deletes pending command by reply command type. */
-
-void silc_client_command_pending_del(SilcCommand reply_cmd)
-{
-  SilcClientCommandPending *r, *tmp;
-  
-  if (silc_command_pending) {
-    if (silc_command_pending->reply_cmd == reply_cmd) {
-      silc_free(silc_command_pending);
-      silc_command_pending = NULL;
-      return;
-    }
-
-    for (r = silc_command_pending; r; r = r->next) {
-      if (r->next && r->next->reply_cmd == reply_cmd) {
-       tmp = r->next;
-       r->next = r->next->next;
-       silc_free(tmp);
-       break;
-      }
-    }
-  }
-}
-
-/* Free command context and its internals */
-
-static void silc_client_command_free(SilcClientCommandContext cmd)
-{
-  int i;
-
-  if (cmd) {
-    for (i = 0; i < cmd->argc; i++)
-      silc_free(cmd->argv[i]);
-    silc_free(cmd);
-  }
-}
-
-/* Command WHOIS. This command is used to query information about 
-   specific user. */
-
-SILC_CLIENT_CMD_FUNC(whois)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcBuffer buffer;
-
-  if (cmd->argc < 2 || cmd->argc > 3) {
-    silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
-    goto out;
-  }
-
-  if (!cmd->client->current_win->sock) {
-    silc_say(cmd->client, 
-            "You are not connected to a server, use /SERVER to connect");
-    goto out;
-  }
-
-  buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types);
-  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
-
- out:
-  silc_client_command_free(cmd);
-}
-
-SILC_CLIENT_CMD_FUNC(whowas)
-{
-}
-
-/* Command IDENTIFY. This command is used to query information about 
-   specific user, especially ID's. */
-
-SILC_CLIENT_CMD_FUNC(identify)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcBuffer buffer;
-
-  if (cmd->argc < 2 || cmd->argc > 3) {
-    silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
-    goto out;
-  }
-
-  if (!cmd->client->current_win->sock) {
-    silc_say(cmd->client, 
-            "You are not connected to a server, use /SERVER to connect");
-    goto out;
-  }
-
-  buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types);
-  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
-
- out:
-  silc_client_command_free(cmd);
-}
-
-/* Command NICK. Shows current nickname/sets new nickname on current
-   window. */
-
-SILC_CLIENT_CMD_FUNC(nick)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientWindow win = NULL;
-  SilcBuffer buffer;
-
-  if (!cmd->sock) {
-    silc_say(cmd->client, 
-            "You are not connected to a server, use /SERVER to connect");
-    goto out;
-  }
-
-  /* Show current nickname */
-  if (cmd->argc < 2) {
-    if (cmd->sock) {
-      silc_say(cmd->client, "Your nickname is %s on server %s", 
-              win->nickname, win->remote_host);
-    } else {
-      silc_say(cmd->client, "Your nickname is %s", win->nickname);
-    }
-    goto out;
-  }
-
-  win = (SilcClientWindow)cmd->sock->user_data;
-
-  /* Set new nickname */
-  buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types);
-  silc_client_packet_send(cmd->client, cmd->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
-  if (win->nickname)
-    silc_free(win->nickname);
-  win->nickname = strdup(cmd->argv[1]);
-
- out:
-  silc_client_command_free(cmd);
-}
-
-/* Command SERVER. Connects to remote SILC server. This is local command. */
-
-SILC_CLIENT_CMD_FUNC(server)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  int len, port;
-  char *hostname;
-
-  if (cmd->argc < 2) {
-    /* Show current servers */
-    if (!cmd->client->current_win->sock) {
-      silc_say(cmd->client, "You are not connected to any server");
-      silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
-      goto out;
-    }
-
-    goto out;
-  }
-
-  /* See if port is included and then extract it */
-  if (strchr(cmd->argv[1], ':')) {
-    len = strcspn(cmd->argv[1], ":");
-    hostname = silc_calloc(len + 1, sizeof(char));
-    memcpy(hostname, cmd->argv[1], len);
-    port = atoi(cmd->argv[1] + 1 + len);
-  } else {
-    hostname = cmd->argv[1];
-    /* XXX */
-    port = 334;
-  }
-
-  /* Connect asynchronously to not to block user interface */
-  silc_client_connect_to_server(cmd->client, port, hostname);
-
- out:
-  silc_client_command_free(cmd);
-}
-
-SILC_CLIENT_CMD_FUNC(list)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(topic)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(invite)
-{
-}
-
-/* Command QUIT. Closes connection with current server. */
-SILC_CLIENT_CMD_FUNC(quit)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcBuffer buffer;
-
-  if (!cmd->client->current_win->sock) {
-    silc_say(cmd->client, 
-            "You are not connected to a server, use /SERVER to connect");
-    goto out;
-  }
-
-  buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, 
-                                      ++cmd->argv, ++cmd->argv_lens,
-                                      ++cmd->argv_types);
-  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
-
-  /* Close connection */
-  silc_client_close_connection(cmd->client, cmd->sock);
-  cmd->client->screen->bottom_line->connection = NULL;
-  silc_screen_print_bottom_line(cmd->client->screen, 0);
-
-  silc_client_command_free(cmd);
-}
-
-SILC_CLIENT_CMD_FUNC(kill)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(info)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(connect)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(ping)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(oper)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(trace)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(notice)
-{
-}
-
-/* Command JOIN. Joins to a channel. */
-
-SILC_CLIENT_CMD_FUNC(join)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientWindow win = NULL;
-  SilcIDCache *id_cache = NULL;
-  SilcBuffer buffer;
-
-#define CIDC(x) win->channel_id_cache[(x) - 32]
-#define CIDCC(x) win->channel_id_cache_count[(x) - 32]
-
-  if (cmd->argc < 2) {
-    /* Show channels currently joined to */
-    if (!cmd->client->current_win->sock) {
-      silc_say(cmd->client, "No current channel for this window");
-      silc_say(cmd->client, 
-              "You are not connected to a server, use /SERVER to connect");
-      goto out;
-
-    }
-
-    goto out;
-  }
-
-  if (!cmd->client->current_win->sock) {
-    silc_say(cmd->client, 
-            "You are not connected to a server, use /SERVER to connect");
-    goto out;
-  }
-
-  win = (SilcClientWindow)cmd->sock->user_data;
-
-  /* See if we have joined to the requested channel already */
-  silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]), 
-                           cmd->argv[1], &id_cache);
-
-  if (id_cache) {
-    silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
-    win->current_channel = (SilcChannelEntry)id_cache->context;
-    cmd->client->screen->bottom_line->channel = cmd->argv[1];
-    silc_screen_print_bottom_line(cmd->client->screen, 0);
-    goto out;
-  }
-
-  /* Send JOIN command to the server */
-  buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
-                                      cmd->argc - 1, ++cmd->argv,
-                                      ++cmd->argv_lens, ++cmd->argv_types);
-  silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
-                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
-                         buffer->data, buffer->len, TRUE);
-  silc_buffer_free(buffer);
-  cmd->argv--;
-  cmd->argv_lens--;
-  cmd->argv_types--;
-
- out:
-  silc_client_command_free(cmd);
-#undef CIDC
-#undef CIDCC
-}
-
-SILC_CLIENT_CMD_FUNC(motd)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(umode)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(cmode)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(kick)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(restart)
-{
-}
-SILC_CLIENT_CMD_FUNC(close)
-{
-}
-SILC_CLIENT_CMD_FUNC(die)
-{
-}
-SILC_CLIENT_CMD_FUNC(silcoper)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(leave)
-{
-}
-
-SILC_CLIENT_CMD_FUNC(names)
-{
-}
-
-/*
- * Local commands
- */
-
-/* HELP command. This is local command and shows help on SILC */
-
-SILC_CLIENT_CMD_FUNC(help)
-{
-
-}
-
-/* CLEAR command. This is local command and clears current output window */
-
-SILC_CLIENT_CMD_FUNC(clear)
-{
-  SilcClient client = (SilcClient)context;
-
-  assert(client->current_win != NULL);
-  wclear((WINDOW *)client->current_win->screen);
-  wrefresh((WINDOW *)client->current_win->screen);
-}
-
-/* VERSION command. This is local command and shows version of the client */
-
-SILC_CLIENT_CMD_FUNC(version)
-{
-
-}
-
-/* Command MSG. Sends private message to user or list of users. */
-/* XXX supports only one destination */
-
-SILC_CLIENT_CMD_FUNC(msg)
-{
-  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
-  SilcClientWindow win = NULL;
-  SilcClient client = cmd->client;
-  SilcBuffer buffer;
-  SilcIDCache *id_cache;
-  unsigned int nick_len;
-
-  if (cmd->argc < 3) {
-    silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
-    goto out;
-  }
-
-  if (!cmd->client->current_win->sock) {
-    silc_say(cmd->client, 
-            "You are not connected to a server, use /SERVER to connect");
-    goto out;
-  }
-
-  win = (SilcClientWindow)cmd->sock->user_data;
-
-#define CIDC(x) win->client_id_cache[(x) - 32], \
-                win->client_id_cache_count[(x) - 32]
-
-  /* Find ID from cache */
-  if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1], 
-                               &id_cache) == FALSE) {
-    SilcClientCommandContext ctx;
-    char ident[512];
-
-    SILC_LOG_DEBUG(("Requesting Client ID from server"));
-
-    /* No ID found. Do query from the server. The query is done by 
-       sending simple IDENTIFY command to the server. */
-    ctx = silc_calloc(1, sizeof(*ctx));
-    ctx->client = client;
-    ctx->sock = cmd->sock;
-    memset(ident, 0, sizeof(ident));
-    snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
-    silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
-                                  &ctx->argv_types, &ctx->argc, 2);
-    silc_client_command_identify(ctx);
-
-    /* Mark this command to be pending command and to be executed after
-       we have received the IDENTIFY reply from server. */
-    silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
-                               silc_client_command_msg, context);
-    return;
-  }
-
-  /* Display the message for our eyes. */
-  silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
-
-  /* Send the private message */
-  silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
-                                         cmd->argv[2], cmd->argv_lens[2],
-                                         TRUE);
- out:
-  silc_client_command_free(cmd);
-#undef CIDC
-}
-
-SILC_CLIENT_CMD_FUNC(away)
-{
-}
diff --git a/apps/silc/command.h b/apps/silc/command.h
deleted file mode 100644 (file)
index 19db809..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
-
-  command.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef COMMAND_H
-#define COMMAND_H
-
-/* 
-   Structure holding one command and pointer to its function. 
-
-   SilcCommandCb cb
-
-       Callback function called when this command is executed.
-
-   SilcCommand cmd
-
-       The actual command. These are defined in silccore/silccommand.h
-
-   char *name
-
-       Logical name of the command. This is the visible command name
-       that user uses when calling command. Eg. NICK.
-
-   SilcCommandFlag flags
-
-       Flags for the command. These set how command behaves on different
-       situations. Server sets these flags as well, but to be sure
-       that our client never sends wrong commands we preserve the
-       flags on client side as well.
-
-       XXX: We preserve these so that we define them but currently we
-       don't check the flags at all.
-
-*/
-typedef struct {
-  SilcCommandCb cb;
-  SilcCommand cmd;
-  char *name;
-  SilcCommandFlag flags;
-  unsigned int max_args;
-} SilcClientCommand;
-
-/* All client commands */
-extern SilcClientCommand silc_command_list[];
-
-/* Client command callback function. This included into Command Context, 
-   and if it is defined it will be executed when executing the command. */
-typedef void (*SilcClientCommandCallback)(void *context);
-
-/* Context sent as argument to all commands */
-typedef struct {
-  SilcClient client;
-  SilcSocketConnection sock;
-  unsigned int argc;
-  unsigned char **argv;
-  unsigned int *argv_lens;
-  unsigned int *argv_types;
-} *SilcClientCommandContext;
-
-/* Structure holding pending commands. If command is pending it will be
-   executed after command reply has been executed. */
-/* XXX This support may added for commands as well and not just command
-   replies, if needed later. */
-typedef struct SilcClientCommandPendingStruct {
-  SilcCommand reply_cmd;
-  void *context;
-  SilcClientCommandCallback callback;
-
-  struct SilcClientCommandPendingStruct *next;
-} SilcClientCommandPending;
-
-/* List of pending commands */
-extern SilcClientCommandPending *silc_command_pending;
-
-/* Macros */
-
-/* Macro used for command declaration in command list structure */
-#define SILC_CLIENT_CMD(func, cmd, name, flags, args) \
-{ silc_client_command_##func, SILC_COMMAND_##cmd, name, flags, args }
-
-/* Macro used to declare command functions */
-#define SILC_CLIENT_CMD_FUNC(func) \
-void silc_client_command_##func(void *context)
-
-/* Checks for pending commands */
-#define SILC_CLIENT_COMMAND_CHECK_PENDING(ctx)         \
-do {                                                   \
-  if (silc_command_pending) {                          \
-    SilcClientCommandPending *r;                       \
-    SilcCommand cmd;                                   \
-                                                       \
-    cmd = silc_command_get(payload);                   \
-    for (r = silc_command_pending; r; r = r->next) {   \
-      if (r->reply_cmd == cmd) {                       \
-       ctx->context = r->context;                      \
-       ctx->callback = r->callback;                    \
-       break;                                          \
-      }                                                        \
-    }                                                  \
-  }                                                    \
-} while(0)
-
-/* Executed pending command */
-#define SILC_CLIENT_COMMAND_EXEC_PENDING(ctx, cmd)     \
-do {                                                   \
-  if (ctx->callback) {                                 \
-    (*ctx->callback)(ctx->context);                    \
-    silc_client_command_pending_del((cmd));            \
-  }                                                    \
-} while(0)
-
-/* Prototypes */
-void silc_client_command_pending(SilcCommand reply_cmd,
-                                SilcClientCommandCallback callback,
-                                void *context);
-void silc_client_command_pending_del(SilcCommand reply_cmd);
-SILC_CLIENT_CMD_FUNC(whois);
-SILC_CLIENT_CMD_FUNC(whowas);
-SILC_CLIENT_CMD_FUNC(identify);
-SILC_CLIENT_CMD_FUNC(nick);
-SILC_CLIENT_CMD_FUNC(server);
-SILC_CLIENT_CMD_FUNC(list);
-SILC_CLIENT_CMD_FUNC(topic);
-SILC_CLIENT_CMD_FUNC(invite);
-SILC_CLIENT_CMD_FUNC(quit);
-SILC_CLIENT_CMD_FUNC(kill);
-SILC_CLIENT_CMD_FUNC(info);
-SILC_CLIENT_CMD_FUNC(connect);
-SILC_CLIENT_CMD_FUNC(ping);
-SILC_CLIENT_CMD_FUNC(oper);
-SILC_CLIENT_CMD_FUNC(join);
-SILC_CLIENT_CMD_FUNC(motd);
-SILC_CLIENT_CMD_FUNC(umode);
-SILC_CLIENT_CMD_FUNC(cmode);
-SILC_CLIENT_CMD_FUNC(kick);
-SILC_CLIENT_CMD_FUNC(restart);
-SILC_CLIENT_CMD_FUNC(close);
-SILC_CLIENT_CMD_FUNC(die);
-SILC_CLIENT_CMD_FUNC(silcoper);
-SILC_CLIENT_CMD_FUNC(leave);
-SILC_CLIENT_CMD_FUNC(names);
-SILC_CLIENT_CMD_FUNC(help);
-SILC_CLIENT_CMD_FUNC(clear);
-SILC_CLIENT_CMD_FUNC(version);
-SILC_CLIENT_CMD_FUNC(msg);
-SILC_CLIENT_CMD_FUNC(away);
-
-#endif
diff --git a/apps/silc/command_reply.c b/apps/silc/command_reply.c
deleted file mode 100644 (file)
index 491808e..0000000
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
-
-  command_reply.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * Command reply functions are "the otherside" of the command functions.
- * Reply to a command sent by server is handled by these functions.
- */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "clientincludes.h"
-
-/* Client command reply list. */
-SilcClientCommandReply silc_command_reply_list[] =
-{
-  SILC_CLIENT_CMD_REPLY(whois, WHOIS),
-  SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
-  SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
-  SILC_CLIENT_CMD_REPLY(nick, NICK),
-  SILC_CLIENT_CMD_REPLY(list, LIST),
-  SILC_CLIENT_CMD_REPLY(topic, TOPIC),
-  SILC_CLIENT_CMD_REPLY(invite, INVITE),
-  SILC_CLIENT_CMD_REPLY(quit, QUIT),
-  SILC_CLIENT_CMD_REPLY(kill, KILL),
-  SILC_CLIENT_CMD_REPLY(info, INFO),
-  SILC_CLIENT_CMD_REPLY(away, AWAY),
-  SILC_CLIENT_CMD_REPLY(connect, CONNECT),
-  SILC_CLIENT_CMD_REPLY(ping, PING),
-  SILC_CLIENT_CMD_REPLY(oper, OPER),
-  SILC_CLIENT_CMD_REPLY(join, JOIN),
-  SILC_CLIENT_CMD_REPLY(motd, MOTD),
-  SILC_CLIENT_CMD_REPLY(umode, UMODE),
-  SILC_CLIENT_CMD_REPLY(cmode, CMODE),
-  SILC_CLIENT_CMD_REPLY(kick, KICK),
-  SILC_CLIENT_CMD_REPLY(restart, RESTART),
-  SILC_CLIENT_CMD_REPLY(close, CLOSE),
-  SILC_CLIENT_CMD_REPLY(die, DIE),
-  SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
-  SILC_CLIENT_CMD_REPLY(leave, LEAVE),
-  SILC_CLIENT_CMD_REPLY(names, LEAVE),
-
-  { NULL, 0 },
-};
-
-/* Status message structure. Messages are defined below. */
-typedef struct {
-  SilcCommandStatus status;
-  char *message;
-} SilcCommandStatusMessage;
-
-/* Status messages returned by the server */
-#define STAT(x) SILC_STATUS_ERR_##x
-const SilcCommandStatusMessage silc_command_status_messages[] = {
-
-  { STAT(NO_SUCH_NICK),      "No such nickname" },
-  { STAT(NO_SUCH_CHANNEL),   "No such channel" },
-  { STAT(NO_SUCH_SERVER),    "No such server" },
-  { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
-  { STAT(NO_RECIPIENT),      "No recipient given" },
-  { STAT(UNKNOWN_COMMAND),   "Unknown command" },
-  { STAT(WILDCARDS),         "Unknown command" },
-  { STAT(NO_CLIENT_ID),      "No Client ID given" },
-  { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
-  { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
-  { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
-  { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
-  { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
-  { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
-  { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
-  { STAT(USER_ON_CHANNEL),   "User already on channel" },
-  { STAT(NOT_REGISTERED),    "You have not registered" },
-  { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
-  { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
-  { STAT(PERM_DENIED),       "Your host is not among the privileged" },
-  { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
-  { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
-  { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
-  { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
-  { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
-  { STAT(UNKNOWN_MODE),    "Unknown mode" },
-  { STAT(NOT_YOU),         "Cannot change mode for other users" },
-  { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
-  { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
-  { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
-  { STAT(BAD_NICKNAME),    "Bad nickname" },
-  { STAT(BAD_CHANNEL),     "Bad channel name" },
-  { STAT(AUTH_FAILED),     "Authentication failed" },
-
-  { 0, NULL }
-};
-
-/* Process received command reply. */
-
-void silc_client_command_reply_process(SilcClient client,
-                                      SilcSocketConnection sock,
-                                      SilcBuffer buffer)
-{
-  SilcClientCommandReplyContext ctx;
-  SilcCommandPayload payload;
-
-  /* Get command reply payload from packet */
-  payload = silc_command_parse_payload(buffer);
-  if (!payload) {
-    /* Silently ignore bad reply packet */
-    SILC_LOG_DEBUG(("Bad command reply packet"));
-    return;
-  }
-  
-  /* Allocate command reply context. This must be free'd by the
-     command reply routine receiving it. */
-  ctx = silc_calloc(1, sizeof(*ctx));
-  ctx->client = client;
-  ctx->sock = sock;
-  ctx->payload = payload;
-      
-  /* Check for pending commands and mark to be exeucted */
-  SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
-  
-  /* Execute command reply */
-  SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
-}
-
-/* Returns status message string */
-
-static char *
-silc_client_command_status_message(SilcCommandStatus status)
-{
-  int i;
-
-  for (i = 0; silc_command_status_messages[i].message; i++) {
-    if (silc_command_status_messages[i].status == status)
-      break;
-  }
-
-  if (silc_command_status_messages[i].message == NULL)
-    return NULL;
-
-  return silc_command_status_messages[i].message;
-}
-
-/* Free command reply context and its internals. */
-
-void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
-{
-  if (cmd) {
-    silc_command_free_payload(cmd->payload);
-    silc_free(cmd);
-  }
-}
-
-/* Received reply for WHOIS command. This maybe called several times
-   for one WHOIS command as server may reply with list of results. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(whois)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClient client = cmd->client;
-  SilcCommandStatus status;
-  unsigned char *tmp;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK) {
-    if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
-      tmp += 2;
-      silc_say(cmd->client, "%s: %s", tmp,
-              silc_client_command_status_message(status));
-      goto out;
-    } else {
-      silc_say(cmd->client, "%s", silc_client_command_status_message(status));
-      goto out;
-    }
-  }
-
-  /* Display one whois reply */
-  if (status == SILC_STATUS_OK) {
-    char buf[256];
-    int argc, len;
-    unsigned char *id_data;
-    char *nickname = NULL, *username = NULL;
-    char *realname = NULL;
-    void *id;
-
-    memset(buf, 0, sizeof(buf));
-
-    argc = silc_command_get_arg_num(cmd->payload);
-    id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
-
-    nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
-    if (nickname) {
-      strncat(buf, nickname, len);
-      strncat(buf, " is ", 4);
-    }
-
-    username = silc_command_get_arg_type(cmd->payload, 4, &len);
-    if (username) {
-      strncat(buf, username, len);
-    }
-
-    realname = silc_command_get_arg_type(cmd->payload, 5, &len);
-    if (realname) {
-      strncat(buf, " (", 2);
-      strncat(buf, realname, len);
-      strncat(buf, ")", 1);
-    }
-
-#if 0
-    /* Save received Client ID to ID cache */
-    /* XXX Maybe should not be saved as /MSG will get confused */
-    id = silc_id_str2id(id_data, SILC_ID_CLIENT);
-    client->current_win->client_id_cache_count[(int)nickname[0] - 32] =
-    silc_idcache_add(&client->current_win->
-                    client_id_cache[(int)nickname[0] - 32],
-                    client->current_win->
-                    client_id_cache_count[(int)nickname[0] - 32],
-                    strdup(nickname), SILC_ID_CLIENT, id, NULL);
-#endif
-
-    silc_say(cmd->client, "%s", buf);
-   }
-
-  if (status == SILC_STATUS_LIST_START) {
-
-  }
-
-  if (status == SILC_STATUS_LIST_END) {
-
-  }
-
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
-
- out:
-  silc_client_command_reply_free(cmd);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(whowas)
-{
-}
-
-/* Received reply for IDENTIFY command. This maybe called several times
-   for one IDENTIFY command as server may reply with list of results. 
-   This is totally silent and does not print anything on screen. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(identify)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
-  SilcClientEntry client_entry;
-  SilcCommandStatus status;
-  unsigned char *tmp;
-
-  SILC_LOG_DEBUG(("Start"));
-
-#define CIDC(x) win->client_id_cache[(x) - 32]
-#define CIDCC(x) win->client_id_cache_count[(x) - 32]
-
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK) {
-    if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
-      tmp += 2;
-      silc_say(cmd->client, "%s: %s", tmp,
-              silc_client_command_status_message(status));
-      goto out;
-    } else {
-      silc_say(cmd->client, "%s", silc_client_command_status_message(status));
-      goto out;
-    }
-  }
-
-  /* Display one whois reply */
-  if (status == SILC_STATUS_OK) {
-    unsigned char *id_data;
-    char *nickname;
-
-    id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
-    nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
-
-    /* Allocate client entry */
-    client_entry = silc_calloc(1, sizeof(*client_entry));
-    client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
-    client_entry->nickname = strdup(nickname);
-
-    /* Save received Client ID to ID cache */
-    CIDCC(nickname[0]) =
-      silc_idcache_add(&CIDC(nickname[0]), CIDCC(nickname[0]),
-                      client_entry->nickname, SILC_ID_CLIENT, 
-                      client_entry->id, client_entry);
-  }
-
-  if (status == SILC_STATUS_LIST_START) {
-
-  }
-
-  if (status == SILC_STATUS_LIST_END) {
-
-  }
-
-  SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
-
- out:
-  silc_client_command_reply_free(cmd);
-#undef CIDC
-#undef CIDCC
-}
-
-/* Received reply for command NICK. If everything went without errors
-   we just received our new Client ID. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(nick)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
-  SilcCommandStatus status;
-  unsigned char *tmp, *id_string;
-  int argc;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK) {
-    silc_say(cmd->client, "Cannot set nickname: %s", 
-            silc_client_command_status_message(status));
-    goto out;
-  }
-
-  argc = silc_command_get_arg_num(cmd->payload);
-  if (argc < 2 || argc > 2) {
-    silc_say(cmd->client, "Cannot set nickname: bad reply to command");
-    goto out;
-  }
-
-  /* Take received Client ID */
-  id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
-  silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
-
-  /* Update nickname on screen */
-  cmd->client->screen->bottom_line->nickname = win->nickname;
-  silc_screen_print_bottom_line(cmd->client->screen, 0);
-
- out:
-  silc_client_command_reply_free(cmd);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(list)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(topic)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(invite)
-{
-}
-SILC_CLIENT_CMD_REPLY_FUNC(quit)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(kill)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(info)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(away)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(connect)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(ping)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(oper)
-{
-}
-
-/* Received reply for JOIN command. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(join)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClient client = cmd->client;
-  SilcCommandStatus status;
-  unsigned int argc;
-  unsigned char *id_string;
-  char *topic, *tmp, *channel_name;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK) {
-    silc_say(cmd->client, "%s", silc_client_command_status_message(status));
-    goto out;
-  }
-
-  argc = silc_command_get_arg_num(cmd->payload);
-  if (argc < 3 || argc > 4) {
-    silc_say(cmd->client, "Cannot join channel: Bad reply packet");
-    goto out;
-  }
-
-  /* Get channel name */
-  tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
-  channel_name = strdup(tmp);
-
-  /* Get channel ID */
-  id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
-
-  /* Get topic */
-  topic = silc_command_get_arg_type(cmd->payload, 4, NULL);
-
-  /* Save received Channel ID */
-  silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, id_string);
-
-  /* Print channel name on screen */
-  client->screen->bottom_line->channel = channel_name;
-  silc_screen_print_bottom_line(client->screen, 0);
-
-  if (topic)
-    silc_say(client, "Topic for %s: %s", channel_name, topic);
-
- out:
-  silc_client_command_reply_free(cmd);
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(motd)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(umode)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(cmode)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(kick)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(restart)
-{
-}
-SILC_CLIENT_CMD_REPLY_FUNC(close)
-{
-}
-SILC_CLIENT_CMD_REPLY_FUNC(die)
-{
-}
-SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(leave)
-{
-}
-
-SILC_CLIENT_CMD_REPLY_FUNC(names)
-{
-}
-
-/* Private message received. This processes the private message and
-   finally displays it on the screen. */
-
-SILC_CLIENT_CMD_REPLY_FUNC(msg)
-{
-  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-  SilcClient client = cmd->client;
-  SilcBuffer buffer = (SilcBuffer)cmd->context;
-  unsigned short nick_len;
-  unsigned char *nickname, *message;
-  SilcIDCache *id_cache;
-  unsigned char *id_string;
-  void *id;
-
-  /* Get nickname */
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
-                      SILC_STR_END);
-  silc_buffer_pull(buffer, 2 + nick_len);
-
-#if 0
-  /* Get ID of the sender */
-  id_string = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char *));
-  silc_buffer_push(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
-  memcpy(id_string, buffer->data, SILC_ID_CLIENT_LEN);
-  silc_buffer_pull(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
-  id = silc_id_str2id(id_string, SILC_ID_CLIENT);
-  silc_free(id_string);
-
-  /* Nickname should be verified if we don't have it in the cache */
-  if (silc_idcache_find_by_data(client->current_win->
-                               client_id_cache[nickname[0] - 32],
-                               client->current_win->
-                               client_id_cache_count[nickname[0] - 32],
-                               nickname, &id_cache) == FALSE) {
-
-    SilcClientCommandContext ctx;
-    char whois[255];
-
-    /* Private message from unknown source, try to resolve it. */
-
-
-    return;
-  }
-#endif
-     
-  message = silc_calloc(buffer->len + 1, sizeof(char));
-  memcpy(message, buffer->data, buffer->len);
-  silc_print(client, "*%s* %s", nickname, message);
-  memset(message, 0, buffer->len);
-  silc_free(message);
-}
diff --git a/apps/silc/idlist.h b/apps/silc/idlist.h
deleted file mode 100644 (file)
index 9c769e9..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-
-  idlist.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef IDLIST_H
-#define IDLIST_H
-
-/* Client entry context. When client receives information about new client
-   (it receives its ID, for example, by IDENTIFY request) we create new
-   client entry. This entry also includes the private message keys if
-   they are used. */
-typedef struct SilcClientEntryStruct {
-  char *nickname;
-  SilcClientID *id;
-
-  /* Keys, these are defined if private message key has been defined 
-     with the remote client. */
-  SilcCipher send_key;
-  SilcCipher receive_key;
-} SilcClientEntryObject;
-
-typedef SilcClientEntryObject *SilcClientEntry;
-
-/* Channel entry context. This is allocate for every channel client has
-   joined to. This includes for example the channel specific keys */
-/* XXX channel_key is the server generated key. Later this context must 
-   include the channel private key. */
-typedef struct SilcChannelEntryStruct {
-  char *channel_name;
-  SilcChannelID *id;
-  int on_channel;
-
-  /* Channel keys */
-  SilcCipher channel_key;
-  unsigned char *key;
-  unsigned int key_len;
-  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
-} SilcChannelEntryObject;
-
-typedef SilcChannelEntryObject *SilcChannelEntry;
-
-#endif
diff --git a/apps/silc/local_command.c b/apps/silc/local_command.c
new file mode 100644 (file)
index 0000000..116f12f
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+
+  local_command.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "clientincludes.h"
+#include "client_internal.h"
+
+/* Local commands. */
+SilcClientCommand silc_local_command_list[] =
+{
+  SILC_CLIENT_LCMD(help, HELP, "HELP", 0, 2),
+  SILC_CLIENT_LCMD(clear, CLEAR, "CLEAR", 0, 1),
+  SILC_CLIENT_LCMD(version, VERSION, "VERSION", 0, 1),
+  SILC_CLIENT_LCMD(server, SERVER, "SERVER", 0, 2),
+  SILC_CLIENT_LCMD(msg, MSG, "MSG", 0, 3),
+  SILC_CLIENT_LCMD(away, AWAY, "AWAY", 0, 2),
+  SILC_CLIENT_LCMD(key, KEY, "KEY", 0, 7),
+  SILC_CLIENT_LCMD(me, ME, "ME", 0, 3),
+  SILC_CLIENT_LCMD(notice, NOTICE, "NOTICE", 0, 3),
+
+  { NULL, 0, NULL, 0, 0 },
+};
+
+/* Finds and returns a pointer to the command list. Return NULL if the
+   command is not found. */
+
+SilcClientCommand *silc_client_local_command_find(const char *name)
+{
+  SilcClientCommand *cmd;
+
+  for (cmd = silc_local_command_list; cmd->name; cmd++) {
+    if (!strcmp(cmd->name, name))
+      return cmd;
+  }
+
+  return NULL;
+}
+
+/* HELP command. This is local command and shows help on SILC */
+
+SILC_CLIENT_LCMD_FUNC(help)
+{
+
+}
+
+/* CLEAR command. This is local command and clears current output window */
+
+SILC_CLIENT_LCMD_FUNC(clear)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+
+  silc_client_command_free(cmd);
+}
+
+/* VERSION command. This is local command and shows version of the client */
+
+SILC_CLIENT_LCMD_FUNC(version)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  extern char *silc_version;
+  extern char *silc_name;
+  extern char *silc_fullname;
+
+  silc_say(client, cmd->conn,
+          "%s (%s) version %s", silc_name, silc_fullname,
+          silc_version);
+
+  silc_client_command_free(cmd);
+}
+
+/* Command MSG. Sends private message to user or list of users. Note that
+   private messages are not really commands, they are message packets,
+   however, on user interface it is convenient to show them as commands
+   as that is the common way of sending private messages (like in IRC). */
+
+SILC_CLIENT_LCMD_FUNC(msg)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcClientEntry client_entry = NULL;
+  uint32 num = 0;
+  char *nickname = NULL, *server = NULL;
+
+  if (!cmd->conn) {
+    silc_say(client, conn,
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    silc_say(client, conn, "Usage: /MSG <nickname> <message>");
+    goto out;
+  }
+
+  /* Parse the typed nickname. */
+  if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
+    silc_say(client, conn, "Bad nickname");
+    goto out;
+  }
+
+  /* Find client entry */
+  client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
+                                       TRUE);
+  if (!client_entry) {
+    /* Client entry not found, it was requested thus mark this to be
+       pending command. */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, 
+                               NULL, silc_client_local_command_msg, context);
+    return;
+  }
+
+  /* Display the message for our eyes. */
+  silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
+
+  /* Send the private message */
+  silc_client_send_private_message(client, conn, client_entry, 0,
+                                  cmd->argv[2], cmd->argv_lens[2],
+                                  TRUE);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+
+/* Command SERVER. Connects to remote SILC server. This is local command. */
+
+SILC_CLIENT_LCMD_FUNC(server)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  int i = 0, len, port;
+  char *hostname;
+
+  if (cmd->argc < 2) {
+    /* Show current servers */
+
+    if (!cmd->conn) {
+      silc_say(client, conn, "You are not connected to any server");
+      silc_say(client, conn, "Usage: /SERVER [<server>[:<port>]]");
+      goto out;
+    }
+
+    silc_say(client, conn, "Current server: %s on %d %s", 
+            conn->remote_host, conn->remote_port,
+            conn->remote_info ? conn->remote_info : "");
+    
+    silc_say(client, conn, "Server list:");
+    for (i = 0; i < client->conns_count; i++) {
+      silc_say(client, conn, " [%d] %s on %d %s", i + 1,
+              client->conns[i]->remote_host, 
+              client->conns[i]->remote_port,
+              client->conns[i]->remote_info ? 
+              client->conns[i]->remote_info : "");
+    }
+
+    goto out;
+  }
+
+  /* See if port is included and then extract it */
+  if (strchr(cmd->argv[1], ':')) {
+    len = strcspn(cmd->argv[1], ":");
+    hostname = silc_calloc(len + 1, sizeof(char));
+    memcpy(hostname, cmd->argv[1], len);
+    port = atoi(cmd->argv[1] + 1 + len);
+  } else {
+    hostname = cmd->argv[1];
+    port = 706;
+  }
+
+#if 0
+  if (conn && conn->remote_host) {
+    if (!strcmp(hostname, conn->remote_host) && port == conn->remote_port) {
+      silc_say(client, conn, "You are already connected to that server");
+      goto out;
+    }
+
+    /* Close connection */
+    cmd->client->ops->disconnect(cmd->client, cmd->conn);
+    silc_client_close_connection(cmd->client, cmd->conn->sock);
+  }
+#endif
+
+  /* Connect asynchronously to not to block user interface */
+  silc_client_connect_to_server(cmd->client, port, hostname, NULL);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Local command AWAY. Client replies with away message to whomever sends
+   private message to the client if the away message is set. If this is
+   given without arguments the away message is removed. */
+
+SILC_CLIENT_LCMD_FUNC(away)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcClientInternal app = (SilcClientInternal)client->application;
+  unsigned char modebuf[4];
+  SilcBuffer idp, buffer;
+
+  if (!cmd->conn) {
+    silc_say(client, conn,
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  if (cmd->argc == 1) {
+    conn->local_entry->mode &= ~SILC_UMODE_GONE;
+
+    if (conn->away) {
+      silc_free(conn->away->away);
+      silc_free(conn->away);
+      conn->away = NULL;
+      app->screen->bottom_line->away = FALSE;
+
+      silc_say(client, conn, "Away message removed");
+      silc_screen_print_bottom_line(app->screen, 0);
+    }
+  } else {
+    conn->local_entry->mode |= SILC_UMODE_GONE;
+  
+    if (conn->away)
+      silc_free(conn->away->away);
+    else
+      conn->away = silc_calloc(1, sizeof(*conn->away));
+    
+    app->screen->bottom_line->away = TRUE;
+    conn->away->away = strdup(cmd->argv[1]);
+
+    silc_say(client, conn, "Away message set: %s", conn->away->away);
+    silc_screen_print_bottom_line(app->screen, 0);
+  }
+
+  /* Send the UMODE command to se myself as gone */
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+  SILC_PUT32_MSB(conn->local_entry->mode, modebuf);
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_UMODE, 
+                                         ++conn->cmd_ident, 2, 
+                                         1, idp->data, idp->len, 
+                                         2, modebuf, sizeof(modebuf));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
+                         NULL, 0, NULL, NULL, buffer->data, 
+                         buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+typedef struct {
+  int type;                    /* 1 = msg, 2 = channel */
+} *KeyInternal;
+
+static SilcSKEKeyMaterial *curr_key = NULL;
+
+/* Key agreement callback that is called after the key agreement protocol
+   has been performed. This is called also if error occured during the
+   key agreement protocol. The `key' is the allocated key material and
+   the caller is responsible of freeing it. The `key' is NULL if error
+   has occured. The application can freely use the `key' to whatever
+   purpose it needs. See lib/silcske/silcske.h for the definition of
+   the SilcSKEKeyMaterial structure. */
+
+static void keyagr_completion(SilcClient client,
+                             SilcClientConnection conn,
+                             SilcClientEntry client_entry,
+                             SilcKeyAgreementStatus status,
+                             SilcSKEKeyMaterial *key,
+                             void *context)
+{
+  KeyInternal i = (KeyInternal)context;
+
+  curr_key = NULL;
+
+  switch(status) {
+  case SILC_KEY_AGREEMENT_OK:
+    silc_say(client, conn, "Key agreement compeleted successfully with %s",
+            client_entry->nickname);;
+
+    if (i->type == 1) {
+      /* Set the private key for this client */
+      silc_client_del_private_message_key(client, conn, client_entry);
+      silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                             NULL, key, FALSE);
+      silc_say(client, conn, "The private messages with the %s are now protected with the private key", client_entry->nickname);
+      silc_ske_free_key_material(key);
+    }
+    
+    break;
+    
+  case SILC_KEY_AGREEMENT_ERROR:
+    silc_say(client, conn, "Error occured during key agreement with %s",
+            client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_FAILURE:
+    silc_say(client, conn, "The key agreement failed with %s",
+            client_entry->nickname);
+    break;
+    
+  case SILC_KEY_AGREEMENT_TIMEOUT:
+    silc_say(client, conn, "Timeout during key agreement. The key agreement was not performed with %s",
+            client_entry->nickname);
+    break;
+    
+  default:
+    break;
+  } 
+
+  if (i)
+    silc_free(i);
+}
+
+/* Local command KEY. This command is used to set and unset private
+   keys for channels, set and unset private keys for private messages
+   with remote clients and to send key agreement requests and
+   negotiate the key agreement protocol with remote client.  The
+   key agreement is supported only to negotiate private message keys,
+   it currently cannot be used to negotiate private keys for channels,
+   as it is not convenient for that purpose. */
+
+SILC_CLIENT_LCMD_FUNC(key)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcClientEntry client_entry = NULL;
+  SilcChannelEntry channel_entry = NULL;
+  uint32 num = 0;
+  char *nickname = NULL, *server = NULL;
+  int command = 0, port = 0, type = 0;
+  char *hostname = NULL;
+  KeyInternal internal = NULL;
+
+  if (!cmd->conn) {
+    silc_say(client, conn,
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  if (cmd->argc < 4) {
+    silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  /* Get type */
+  if (!strcasecmp(cmd->argv[1], "msg"))
+    type = 1;
+  if (!strcasecmp(cmd->argv[1], "channel"))
+    type = 2;
+
+  if (type == 0) {
+    silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  if (type == 1) {
+    if (cmd->argv[2][0] == '*') {
+      nickname = "*";
+    } else {
+      /* Parse the typed nickname. */
+      if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
+       silc_say(client, conn, "Bad nickname");
+       goto out;
+      }
+      
+      /* Find client entry */
+      client_entry = silc_idlist_get_client(client, conn, nickname, 
+                                           server, num, TRUE);
+      if (!client_entry) {
+       /* Client entry not found, it was requested thus mark this to be
+          pending command. */
+       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                   conn->cmd_ident, 
+                                   NULL, silc_client_local_command_key, 
+                                   context);
+       return;
+      }
+    }
+  }
+
+  if (type == 2) {
+    /* Get channel entry */
+    char *name;
+
+    if (cmd->argv[2][0] == '*') {
+      if (!conn->current_channel) {
+       silc_say(cmd->client, conn, "You are not on any channel");
+       goto out;
+      }
+      name = conn->current_channel->channel_name;
+    } else {
+      name = cmd->argv[2];
+    }
+
+    channel_entry = silc_client_get_channel(client, conn, name);
+    if (!channel_entry) {
+      silc_say(client, conn, "You are not on that channel");
+      goto out;
+    }
+  }
+
+  /* Set command */
+  if (!strcasecmp(cmd->argv[3], "set")) {
+    command = 1;
+
+    if (cmd->argc == 4) {
+      if (curr_key && type == 1 && client_entry) {
+       silc_client_del_private_message_key(client, conn, client_entry);
+       silc_client_add_private_message_key_ske(client, conn, client_entry,
+                                               NULL, curr_key, FALSE);
+       goto out;
+      }
+    }
+
+    if (cmd->argc >= 5) {
+      if (type == 1 && client_entry) {
+       /* Set private message key */
+       
+       silc_client_del_private_message_key(client, conn, client_entry);
+
+       if (cmd->argc >= 6)
+         silc_client_add_private_message_key(client, conn, client_entry,
+                                             cmd->argv[5], cmd->argv[4],
+                                             cmd->argv_lens[4],
+                                             (cmd->argv[4][0] == '*' ?
+                                              TRUE : FALSE), FALSE);
+       else
+         silc_client_add_private_message_key(client, conn, client_entry,
+                                             NULL, cmd->argv[4],
+                                             cmd->argv_lens[4],
+                                             (cmd->argv[4][0] == '*' ?
+                                              TRUE : FALSE), FALSE);
+
+       /* Send the key to the remote client so that it starts using it
+          too. */
+       silc_client_send_private_message_key(client, conn, client_entry, TRUE);
+      } else if (type == 2) {
+       /* Set private channel key */
+       char *cipher = NULL, *hmac = NULL;
+
+       if (!(channel_entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+         silc_say(client, conn, 
+                  "Private key mode is not set on this channel");
+         goto out;
+       }
+
+       if (cmd->argc >= 6)
+         cipher = cmd->argv[5];
+       if (cmd->argc >= 7)
+         hmac = cmd->argv[6];
+
+       if (!silc_client_add_channel_private_key(client, conn, channel_entry,
+                                                cipher, hmac,
+                                                cmd->argv[4],
+                                                cmd->argv_lens[4])) {
+         silc_say(client, conn, "Could not add channel private key");
+         goto out;
+       }
+      }
+    }
+
+    goto out;
+  }
+  
+  /* Unset command */
+  if (!strcasecmp(cmd->argv[3], "unset")) {
+    command = 2;
+
+    if (type == 1 && client_entry) {
+      /* Unset private message key */
+      silc_client_del_private_message_key(client, conn, client_entry);
+    } else if (type == 2) {
+      /* Unset channel key(s) */
+      SilcChannelPrivateKey *keys;
+      uint32 keys_count;
+      int number;
+
+      if (cmd->argc == 4)
+       silc_client_del_channel_private_keys(client, conn, channel_entry);
+
+      if (cmd->argc > 4) {
+       number = atoi(cmd->argv[4]);
+       keys = silc_client_list_channel_private_keys(client, conn, 
+                                                    channel_entry,
+                                                    &keys_count);
+       if (!keys)
+         goto out;
+
+       if (!number || number > keys_count) {
+         silc_client_free_channel_private_keys(keys, keys_count);
+         goto out;
+       }
+
+       silc_client_del_channel_private_key(client, conn, channel_entry,
+                                           keys[number - 1]);
+       silc_client_free_channel_private_keys(keys, keys_count);
+      }
+
+      goto out;
+    }
+  }
+
+  /* List command */
+  if (!strcasecmp(cmd->argv[3], "list")) {
+    command = 3;
+
+    if (type == 1) {
+      SilcPrivateMessageKeys keys;
+      uint32 keys_count;
+      int k, i, len;
+      char buf[1024];
+
+      keys = silc_client_list_private_message_keys(client, conn, 
+                                                  &keys_count);
+      if (!keys)
+       goto out;
+
+      /* list the private message key(s) */
+      if (nickname[0] == '*') {
+       silc_say(client, conn, "Private message keys");
+       silc_say(client, conn, 
+                "  Client                         Cipher         Key");
+       for (k = 0; k < keys_count; k++) {
+         memset(buf, 0, sizeof(buf));
+         strncat(buf, "  ", 2);
+         len = strlen(keys[k].client_entry->nickname);
+         strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+         if (len < 30)
+           for (i = 0; i < 30 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+         
+         len = strlen(keys[k].cipher);
+         strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+         if (len < 14)
+           for (i = 0; i < 14 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+
+         if (keys[k].key)
+           strcat(buf, "<hidden>");
+         else
+           strcat(buf, "*generated*");
+
+         silc_say(client, conn, "%s", buf);
+       }
+      } else {
+       silc_say(client, conn, "Private message key", 
+                client_entry->nickname);
+       silc_say(client, conn, 
+                "  Client                         Cipher         Key");
+       for (k = 0; k < keys_count; k++) {
+         if (keys[k].client_entry != client_entry)
+           continue;
+
+         memset(buf, 0, sizeof(buf));
+         strncat(buf, "  ", 2);
+         len = strlen(keys[k].client_entry->nickname);
+         strncat(buf, keys[k].client_entry->nickname, len > 30 ? 30 : len);
+         if (len < 30)
+           for (i = 0; i < 30 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+         
+         len = strlen(keys[k].cipher);
+         strncat(buf, keys[k].cipher, len > 14 ? 14 : len);
+         if (len < 14)
+           for (i = 0; i < 14 - len; i++)
+             strcat(buf, " ");
+         strcat(buf, " ");
+
+         if (keys[k].key)
+           strcat(buf, "<hidden>");
+         else
+           strcat(buf, "*generated*");
+
+         silc_say(client, conn, "%s", buf);
+       }
+      }
+
+      silc_client_free_private_message_keys(keys, keys_count);
+    } else if (type == 2) {
+      SilcChannelPrivateKey *keys;
+      uint32 keys_count;
+      int k, i, len;
+      char buf[1024];
+
+      keys = silc_client_list_channel_private_keys(client, conn, channel_entry,
+                                                  &keys_count);
+      if (!keys)
+       goto out;
+
+      silc_say(client, conn, "Channel %s private keys", 
+              channel_entry->channel_name);
+      silc_say(client, conn, 
+              "  Cipher           Hmac             Key");
+      for (k = 0; k < keys_count; k++) {
+       memset(buf, 0, sizeof(buf));
+       strncat(buf, "  ", 2);
+
+       len = strlen(keys[k]->cipher->cipher->name);
+       strncat(buf, keys[k]->cipher->cipher->name, len > 16 ? 16 : len);
+       if (len < 16)
+         for (i = 0; i < 16 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+       
+       len = strlen(keys[k]->hmac->hmac->name);
+       strncat(buf, keys[k]->hmac->hmac->name, len > 16 ? 16 : len);
+       if (len < 16)
+         for (i = 0; i < 16 - len; i++)
+           strcat(buf, " ");
+       strcat(buf, " ");
+       
+       strcat(buf, "<hidden>");
+
+       silc_say(client, conn, "%s", buf);
+      }
+      
+      silc_client_free_channel_private_keys(keys, keys_count);
+    }
+
+    goto out;
+  }
+
+  /* Send command is used to send key agreement */
+  if (!strcasecmp(cmd->argv[3], "agreement")) {
+    command = 4;
+
+    if (cmd->argc >= 5)
+      hostname = cmd->argv[4];
+    if (cmd->argc >= 6)
+      port = atoi(cmd->argv[5]);
+
+    internal = silc_calloc(1, sizeof(*internal));
+    internal->type = type;
+  }
+
+  /* Start command is used to start key agreement (after receiving the
+     key_agreement client operation). */
+  if (!strcasecmp(cmd->argv[3], "negotiate")) {
+    command = 5;
+
+    if (cmd->argc >= 5)
+      hostname = cmd->argv[4];
+    if (cmd->argc >= 6)
+      port = atoi(cmd->argv[5]);
+
+    internal = silc_calloc(1, sizeof(*internal));
+    internal->type = type;
+  }
+
+  if (command == 0) {
+    silc_say(client, conn, "Usage: /KEY msg|channel <nickname|channel> "
+            "set|unset|agreement|negotiate [<arguments>]");
+    goto out;
+  }
+
+  if (command == 4 && client_entry) {
+    silc_say(client, conn, "Sending key agreement to %s", cmd->argv[2]);
+    silc_client_send_key_agreement(client, conn, client_entry, hostname, 
+                                  port, 120, keyagr_completion, internal);
+    goto out;
+  }
+
+  if (command == 5 && client_entry) {
+    silc_say(client, conn, "Starting key agreement with %s", cmd->argv[2]);
+    silc_client_perform_key_agreement(client, conn, client_entry, hostname, 
+                                     port, keyagr_completion, internal);
+    goto out;
+  }
+
+ out:
+  if (nickname)
+    silc_free(nickname);
+  if (server)
+    silc_free(server);
+  silc_client_command_free(cmd);
+}
+
+/* Sends an action to the channel.  Equals CTCP's ACTION (IRC's /ME) 
+   command. */
+
+SILC_CLIENT_LCMD_FUNC(me)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcChannelEntry channel_entry;
+  char *name;
+
+  if (!cmd->conn) {
+    silc_say(client, conn,
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    silc_say(client, conn, "Usage: /ME <channel> <action message>");
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      silc_say(cmd->client, conn, "You are not on any channel");
+      goto out;
+    }
+    name = conn->current_channel->channel_name;
+  } else {
+    name = cmd->argv[1];
+  }
+
+  channel_entry = silc_client_get_channel(client, conn, name);
+  if (!channel_entry) {
+    silc_say(client, conn, "You are not on that channel");
+    goto out;
+  }
+
+  /* Send the action message */
+  silc_client_send_channel_message(client, conn, channel_entry, NULL,
+                                  SILC_MESSAGE_FLAG_ACTION, 
+                                  cmd->argv[2], cmd->argv_lens[2], TRUE);
+
+  silc_print(client, "* %s %s", conn->nickname, cmd->argv[2]);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Sends an notice to the channel.  */
+
+SILC_CLIENT_LCMD_FUNC(notice)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcChannelEntry channel_entry;
+  char *name;
+
+  if (!cmd->conn) {
+    silc_say(client, conn,
+            "You are not connected to a server, use /SERVER to connect");
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    silc_say(client, conn, "Usage: /NOTICE <channel> <message>");
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      silc_say(cmd->client, conn, "You are not on any channel");
+      goto out;
+    }
+    name = conn->current_channel->channel_name;
+  } else {
+    name = cmd->argv[1];
+  }
+
+  channel_entry = silc_client_get_channel(client, conn, name);
+  if (!channel_entry) {
+    silc_say(client, conn, "You are not on that channel");
+    goto out;
+  }
+
+  /* Send the action message */
+  silc_client_send_channel_message(client, conn, channel_entry, NULL,
+                                  SILC_MESSAGE_FLAG_NOTICE, 
+                                  cmd->argv[2], cmd->argv_lens[2], TRUE);
+
+  silc_print(client, "- %s %s", conn->nickname, cmd->argv[2]);
+
+ out:
+  silc_client_command_free(cmd);
+}
diff --git a/apps/silc/local_command.h b/apps/silc/local_command.h
new file mode 100644 (file)
index 0000000..134e0fd
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+
+  local_command.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef LOCAL_COMMAND_H
+#define LOCAL_COMMAND_H
+
+/* All local commands */
+extern SilcClientCommand silc_local_command_list[];
+
+/* Local commands */
+#define SILC_LOCAL_COMMAND_HELP                1
+#define SILC_LOCAL_COMMAND_CLEAR       2
+#define SILC_LOCAL_COMMAND_VERSION     3
+#define SILC_LOCAL_COMMAND_SERVER       4
+#define SILC_LOCAL_COMMAND_MSG                 5
+#define SILC_LOCAL_COMMAND_AWAY                6
+#define SILC_LOCAL_COMMAND_KEY         7
+#define SILC_LOCAL_COMMAND_ME          8
+#define SILC_LOCAL_COMMAND_NOTICE              9
+
+/* Macros */
+
+/* Macro used for command declaration in command list structure */
+#define SILC_CLIENT_LCMD(func, cmd, name, flags, args) \
+{ silc_client_local_command_##func, SILC_LOCAL_COMMAND_##cmd, \
+  name, flags, args }
+
+/* Macro used to declare command functions */
+#define SILC_CLIENT_LCMD_FUNC(func) \
+void silc_client_local_command_##func(void *context)
+
+/* Prototypes */
+SilcClientCommand *silc_client_local_command_find(const char *name);
+SILC_CLIENT_LCMD_FUNC(help);
+SILC_CLIENT_LCMD_FUNC(clear);
+SILC_CLIENT_LCMD_FUNC(version);
+SILC_CLIENT_LCMD_FUNC(msg);
+SILC_CLIENT_LCMD_FUNC(server);
+SILC_CLIENT_LCMD_FUNC(away);
+SILC_CLIENT_LCMD_FUNC(key);
+SILC_CLIENT_LCMD_FUNC(me);
+SILC_CLIENT_LCMD_FUNC(notice);
+
+#endif
diff --git a/apps/silc/protocol.c b/apps/silc/protocol.c
deleted file mode 100644 (file)
index ba89d9d..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
-
-  protocol.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * Client side of the protocols.
- */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "clientincludes.h"
-
-SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
-SILC_TASK_CALLBACK(silc_client_protocol_channel_auth);
-SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
-
-/* SILC client protocol list */
-const SilcProtocolObject silc_protocol_list[] =
-{
-  { SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
-    silc_client_protocol_connection_auth },
-  { SILC_PROTOCOL_CLIENT_CHANNEL_AUTH, 
-    silc_client_protocol_channel_auth },
-  { SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
-    silc_client_protocol_key_exchange },
-
-  { SILC_PROTOCOL_CLIENT_NONE, NULL },
-};
-
-/*
- * Key Exhange protocol functions
- */
-
-static void silc_client_protocol_ke_send_packet(SilcSKE ske,
-                                               SilcBuffer packet,
-                                               SilcPacketType type,
-                                               void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  /* Send the packet immediately */
-  silc_client_packet_send(client, ske->sock, type, NULL, 0, NULL, NULL,
-                         packet->data, packet->len, TRUE);
-
-}
-
-static void silc_client_protocol_ke_phase1_cb(SilcSKE ske,
-                                             void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  SILC_LOG_DEBUG(("Start"));
-
-}
-
-static void silc_client_protocol_ke_finish_cb(SilcSKE ske,
-                                             void *context)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  SILC_LOG_DEBUG(("Start"));
-
-}
-
-/* Sets the negotiated key material into use for particular connection. */
-
-static void silc_client_protocol_ke_set_keys(SilcSKE ske,
-                                            SilcSocketConnection sock,
-                                            SilcSKEKeyMaterial *keymat,
-                                            SilcCipher cipher,
-                                            SilcPKCS pkcs,
-                                            SilcHash hash)
-{
-  SilcClientWindow win = (SilcClientWindow)sock->user_data;
-  SilcHash nhash;
-
-  SILC_LOG_DEBUG(("Setting new keys into use"));
-
-  /* Allocate cipher to be used in the communication */
-  silc_cipher_alloc(cipher->cipher->name, &win->send_key);
-  silc_cipher_alloc(cipher->cipher->name, &win->receive_key);
-
-  win->send_key->cipher->set_key(win->send_key->context, 
-                                keymat->send_enc_key, 
-                                keymat->enc_key_len);
-  win->send_key->set_iv(win->send_key, keymat->send_iv);
-  win->receive_key->cipher->set_key(win->receive_key->context, 
-                                   keymat->receive_enc_key, 
-                                   keymat->enc_key_len);
-  win->receive_key->set_iv(win->receive_key, keymat->receive_iv);
-
-  /* Allocate PKCS to be used */
-#if 0
-  /* XXX Do we ever need to allocate PKCS for the connection??
-     If yes, we need to change KE protocol to get the initiators
-     public key. */
-  silc_pkcs_alloc(pkcs->pkcs->name, &win->public_Key);
-  silc_pkcs_set_public_key(win->public_key, ske->ke2_payload->pk_data, 
-                          ske->ke2_payload->pk_len);
-#endif
-
-  /* Save HMAC key to be used in the communication. */
-  silc_hash_alloc(hash->hash->name, &nhash);
-  silc_hmac_alloc(nhash, &win->hmac);
-  win->hmac_key_len = keymat->hmac_key_len;
-  win->hmac_key = silc_calloc(win->hmac_key_len,
-                                   sizeof(unsigned char));
-  memcpy(win->hmac_key, keymat->hmac_key, keymat->hmac_key_len);
-
-}
-
-/* Performs key exchange protocol. This is used for both initiator
-   and responder key exchange. This may be called recursively. */
-
-SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
-    (SilcClientKEInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-  SilcSKEStatus status;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
-    protocol->state = SILC_PROTOCOL_STATE_START;
-
-  switch(protocol->state) {
-  case SILC_PROTOCOL_STATE_START:
-    {
-      /*
-       * Start Protocol
-       */
-      SilcSKE ske;
-
-      /* Allocate Key Exchange object */
-      ske = silc_ske_alloc();
-      ctx->ske = ske;
-      
-      if (ctx->responder == TRUE) {
-#if 0
-       SilcBuffer start_payload;
-
-
-       /* Start the key exchange by processing the received security
-          properties packet from initiator. */
-       status = silc_ske_responder_start(ske, ctx->rng, ctx->sock,
-                                         start_payload,
-                                         silc_client_protocol_ke_send_packet,
-                                         context);
-#endif
-      } else {
-       SilcSKEStartPayload *start_payload;
-
-       /* Assemble security properties. */
-       silc_ske_assemble_security_properties(ske, &start_payload);
-
-       /* Start the key exchange by sending our security properties
-          to the remote end. */
-       status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
-                                         start_payload,
-                                         silc_client_protocol_ke_send_packet,
-                                         context);
-      }
-
-      if (status != SILC_SKE_STATUS_OK) {
-       switch(status) {
-         
-       default:
-         break;
-       }
-      }
-
-      /* Advance the state of the protocol. */
-      protocol->state++;
-    }
-    break;
-  case 2:
-    {
-      /* 
-       * Phase 1 
-       */
-      if (ctx->responder == TRUE) {
-#if 0
-       status = 
-         silc_ske_responder_phase_1(ctx->ske, 
-                                    ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
-#endif
-      } else {
-       /* Call Phase-1 function. This processes the Key Exchange Start
-          paylaod reply we just got from the responder. The callback
-          function will receive the processed payload where we will
-          save it. */
-       status = 
-         silc_ske_initiator_phase_1(ctx->ske,
-                                    ctx->packet,
-                                    silc_client_protocol_ke_phase1_cb,
-                                    context);
-      }
-
-      switch(status) {
-      default:
-       break;
-      }
-
-      /* Advance the state of the protocol and call the next state. */
-      protocol->state++;
-      protocol->execute(client->timeout_queue, 0, protocol, fd, 0, 0);
-    }
-    break;
-  case 3:
-    {
-      /* 
-       * Phase 2 
-       */
-      if (ctx->responder == TRUE) {
-#if 0
-       status = 
-         silc_ske_responder_phase_2(ctx->ske, 
-                                    ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
-#endif
-      } else {
-       /* Call the Phase-2 function. This creates Diffie Hellman
-          key exchange parameters and sends our public part inside
-          Key Exhange 1 Payload to the responder. */
-       status = 
-         silc_ske_initiator_phase_2(ctx->ske,
-                                    silc_client_protocol_ke_send_packet,
-                                    context);
-      }
-
-      switch(status) {
-      default:
-       break;
-      }
-
-      /* Advance the state of the protocol. */
-      protocol->state++;
-    }
-    break;
-  case 4:
-    {
-      /* 
-       * Finish protocol
-       */
-      if (ctx->responder == TRUE) {
-#if 0
-       status = 
-         silc_ske_responder_phase_2(ctx->ske, 
-                                    ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
-#endif
-      } else {
-       /* Finish the protocol. This verifies the Key Exchange 2 payload
-          sent by responder. */
-       status = 
-         silc_ske_initiator_finish(ctx->ske,
-                                   ctx->packet,
-                                   silc_client_protocol_ke_finish_cb,
-                                   context);
-      }
-
-      switch(status) {
-      default:
-       break;
-      }
-      
-      /* Send Ok to the other end. We will end the protocol as server
-        sends Ok to us when we will take the new keys into use. */
-      silc_ske_end(ctx->ske, silc_client_protocol_ke_send_packet, context);
-      
-      /* End the protocol on the next round */
-      protocol->state = SILC_PROTOCOL_STATE_END;
-    }
-    break;
-  case SILC_PROTOCOL_STATE_END:
-    {
-      /* 
-       * End protocol
-       */
-      SilcSKEKeyMaterial *keymat;
-
-      /* Process the key material */
-      keymat = silc_calloc(1, sizeof(*keymat));
-      silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat);
-
-      /* Take the negotiated keys into use. */
-      silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
-                                      ctx->ske->prop->cipher,
-                                      ctx->ske->prop->pkcs,
-                                      ctx->ske->prop->hash);
-
-      /* Protocol has ended, call the final callback */
-      if (protocol->final_callback)
-       protocol->execute_final(client->timeout_queue, 0, protocol, fd);
-      else
-       silc_protocol_free(protocol);
-    }
-    break;
-  case SILC_PROTOCOL_STATE_ERROR:
-    
-    /* On error the final callback is always called. */
-    /*    protocol->final_callback(pptr, context);*/
-    break;
-  case SILC_PROTOCOL_STATE_UNKNOWN:
-    break;
-  }
-}
-
-/*
- * Connection Authentication protocol functions
- */
-
-SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx = 
-    (SilcClientConnAuthInternalContext *)protocol->context;
-  SilcClient client = (SilcClient)ctx->client;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
-    protocol->state = SILC_PROTOCOL_STATE_START;
-
-  switch(protocol->state) {
-  case SILC_PROTOCOL_STATE_START:
-    {
-      /* 
-       * Start protocol. We send authentication data to the server
-       * to be authenticated.
-       */
-      SilcBuffer packet;
-      int payload_len = 0;
-      unsigned char *auth_data = NULL;
-      unsigned int auth_data_len = 0;
-
-      switch(ctx->auth_meth) {
-      case SILC_PROTOCOL_CONN_AUTH_NONE:
-       /* No authentication required */
-       break;
-
-      case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
-       /* Password authentication */
-       if (ctx->auth_data && ctx->auth_data_len) {
-         auth_data = ctx->auth_data;
-         auth_data_len = ctx->auth_data_len;
-         break;
-       }
-
-       silc_say(client, "Password authentication required by server %s",
-                ctx->sock->hostname);
-       auth_data = silc_client_ask_passphrase(client);
-       auth_data_len = strlen(auth_data);
-       break;
-
-      case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
-#if 0
-
-#endif
-       break;
-      }
-
-      payload_len = 4 + auth_data_len;
-      packet = silc_buffer_alloc(payload_len);
-      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-      silc_buffer_format(packet,
-                        SILC_STR_UI_SHORT(payload_len),
-                        SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
-                        SILC_STR_UI_XNSTRING(auth_data, auth_data_len),
-                        SILC_STR_END);
-
-      /* Send the packet to server */
-      silc_client_packet_send(client, ctx->sock,
-                             SILC_PACKET_CONNECTION_AUTH,
-                             NULL, 0, NULL, NULL,
-                             packet->data, packet->len, TRUE);
-
-      if (auth_data) {
-       memset(auth_data, 0, auth_data_len);
-       silc_free(auth_data);
-      }
-      silc_buffer_free(packet);
-      
-      /* Next state is end of protocol */
-      protocol->state = SILC_PROTOCOL_STATE_END;
-    }
-    break;
-
-  case SILC_PROTOCOL_STATE_END:
-    {
-      /* 
-       * End protocol. Nothing special to be done here.
-       */
-
-      /* Protocol has ended, call the final callback */
-      if (protocol->final_callback)
-       protocol->execute_final(client->timeout_queue, 0, protocol, fd);
-      else
-       silc_protocol_free(protocol);
-    }
-    break;
-
-  case SILC_PROTOCOL_STATE_ERROR:
-    {
-      /* 
-       * Error
-       */
-
-      /* Error in protocol. Send FAILURE packet. Although I don't think
-        this could ever happen on client side. */
-      silc_client_packet_send(client, ctx->sock, SILC_PACKET_FAILURE,
-                             NULL, 0, NULL, NULL, NULL, 0, TRUE);
-
-      /* On error the final callback is always called. */
-      if (protocol->final_callback)
-       protocol->execute_final(client->timeout_queue, 0, protocol, fd);
-      else
-       silc_protocol_free(protocol);
-    }
-    break;
-  case SILC_PROTOCOL_STATE_UNKNOWN:
-    break;
-  }
-}
-
-SILC_TASK_CALLBACK(silc_client_protocol_channel_auth)
-{
-}
diff --git a/apps/silc/protocol.h b/apps/silc/protocol.h
deleted file mode 100644 (file)
index 1c40ef3..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-
-  protocol.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
-
-/* SILC client protocol types */
-#define SILC_PROTOCOL_CLIENT_NONE 0
-#define SILC_PROTOCOL_CLIENT_CONNECTION_AUTH 1
-#define SILC_PROTOCOL_CLIENT_CHANNEL_AUTH 2
-#define SILC_PROTOCOL_CLIENT_KEY_EXCHANGE 3
-/* #define SILC_PROTOCOL_CLIENT_MAX 255 */
-
-/* Internal context for key exchange protocol */
-typedef struct {
-  void *client;
-  SilcSocketConnection sock;
-  SilcRng rng;
-  int responder;
-  SilcBuffer packet;
-  SilcSKE ske;
-} SilcClientKEInternalContext;
-
-/* Internal context for connection authentication protocol */
-typedef struct {
-  void *client;
-  SilcSocketConnection sock;
-
-  /* SKE object from Key Exchange protocol. */
-  SilcSKE ske;
-
-  /* Auth method that must be used. This is resolved before this
-     connection authentication protocol is started. */
-  unsigned int auth_meth;
-
-  /* Authentication data if we alreay know it. This is filled before
-     starting the protocol if we know the authentication data. Otherwise
-     these are and remain NULL. */
-  unsigned char *auth_data;
-  unsigned int auth_data_len;
-
-  SilcTask timeout_task;
-} SilcClientConnAuthInternalContext;
-
-/* Prototypes */
-
-#endif
diff --git a/apps/silc/pubkey.pub b/apps/silc/pubkey.pub
deleted file mode 100644 (file)
index 7dc0bfd..0000000
Binary files a/apps/silc/pubkey.pub and /dev/null differ
index f3f44567fe182f29d627662f358ce87ef49b3e8d..8fb8a8104c783451788845f54b1712d6d3856d22 100644 (file)
  * old version of the SILC client dating back to 1997.
  */
 /* XXX: Input line handling is really buggy! */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "clientincludes.h"
 
@@ -38,16 +31,7 @@ SilcScreen silc_screen_init()
 {
   SilcScreen new;
 
-  new = silc_malloc(sizeof(*new));
-  if (new == NULL) {
-    SILC_LOG_ERROR(("Could not create new screen object"));
-    return NULL;
-  }
-
-  new->output_win_count = 0;
-  new->input_pos = 0;
-  new->cursor_pos = 0;
-  new->virtual_window = 0;
+  new = silc_calloc(1, sizeof(*new));
   new->insert = TRUE;
 
   initscr();
@@ -68,7 +52,7 @@ WINDOW *silc_screen_create_output_window(SilcScreen screen)
 {
   assert(screen != NULL);
 
-  screen->output_win = silc_malloc(sizeof(*screen->output_win) * 1);
+  screen->output_win = silc_calloc(1, sizeof(*screen->output_win));
   screen->output_win_count = 1;
   screen->output_win[0] = newwin(LINES - 3, COLS, 1, 0);
   scrollok(screen->output_win[0], TRUE);
@@ -111,15 +95,20 @@ void silc_screen_create_input_window(SilcScreen screen)
 
 void silc_screen_init_upper_status_line(SilcScreen screen)
 {
-  int i;
-  int justify;
-  
   assert(screen != NULL);
 
   /* Create upper status line */
   screen->upper_stat_line = newwin(0, COLS, 0, 0);
   scrollok(screen->upper_stat_line, FALSE);
   wattrset(screen->upper_stat_line, A_REVERSE);
+
+  silc_screen_print_upper_stat_line(screen);
+}
+
+void silc_screen_print_upper_stat_line(SilcScreen screen)
+{
+  int i;
+  int justify;
   
   /* Print empty line */
   for (i = 0; i < COLS - 1; i++)
@@ -130,13 +119,6 @@ void silc_screen_init_upper_status_line(SilcScreen screen)
   mvwprintw(screen->upper_stat_line, 0, 1, "%s %s", 
            screen->u_stat_line.program_name, 
            screen->u_stat_line.program_version);
-  /*
-  mvwprintw(screen->upper_stat_line, 0, justify, "[Your Connection: %s]", 
-           stat.uconnect_status[stat.uconnect]);
-  mvwprintw(screen->upper_stat_line, 0, 
-           (justify + justify + justify), "[SILC: %s]", 
-           stat.silc_status[stat.silc]);
-  */
 
   /* Prints clock on upper stat line */        
   silc_screen_print_clock(screen);
@@ -225,6 +207,25 @@ void silc_screen_print_bottom_line(SilcScreen screen, int win_index)
            SILC_SCREEN_MAX_CHANNEL_LEN : len);
   }
 
+  if (line->channel_mode) {
+    len = strlen(line->channel_mode);
+    strncat(buf, " (+", 3);
+    strncat(buf, line->channel_mode, len > SILC_SCREEN_MAX_CHANNEL_LEN ?
+           SILC_SCREEN_MAX_CHANNEL_LEN : len);
+    strncat(buf, ")", 2);
+  }
+
+  if (line->umode) {
+    len = strlen(line->umode);
+    strncat(buf, " [", 2);
+    strncat(buf, line->umode, len > SILC_SCREEN_MAX_UMODE_LEN ?
+           SILC_SCREEN_MAX_UMODE_LEN : len);
+    strncat(buf, "]", 2);
+  }
+
+  if (line->away)
+    strncat(buf, " (away)", 8);
+
   wattrset(screen->output_stat_line[win_index], A_REVERSE);
 
   for (i = 0; i < COLS - 10; i++)
@@ -244,15 +245,20 @@ void silc_screen_refresh_all(SilcScreen screen)
 
   assert(screen != NULL);
 
-  redrawwin(screen->upper_stat_line);
+  wclear(screen->upper_stat_line);
+  silc_screen_print_upper_stat_line(screen);
+
+  wclear(screen->output_stat_line[0]);
+  silc_screen_print_bottom_line(screen, 0);
+  silc_screen_print_coordinates(screen, 0);
 
   for (i = 0; i < screen->output_win_count; i++) {
+    wclear(screen->output_win[i]);
     wrefresh(screen->output_win[i]);
-    redrawwin(screen->output_win[i]);
   }
 
+  wclear(screen->input_win);
   wrefresh(screen->input_win);
-  redrawwin(screen->input_win);
 }
 
 /* Refreshes a window */
@@ -309,9 +315,9 @@ void silc_screen_input_backspace(SilcScreen screen)
       screen->virtual_window--;
       
       waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS);
-      screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5)) + 1;
-      screen->input_end = ((screen->virtual_window + 1) * (COLS - 5)) + 1;
-      screen->cursor_pos = (COLS - 5) + 1;
+      screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5));
+      screen->input_end = ((screen->virtual_window + 1) * (COLS - 5));
+      screen->cursor_pos = (COLS - 5);
       wrefresh(win);
     }
   }
@@ -413,8 +419,8 @@ void silc_screen_input_cursor_left(SilcScreen screen)
       screen->virtual_window--;
       
       waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS);
-      screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5)) + 1;
-      screen->cursor_pos = (COLS - 5) + 1;
+      screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5));
+      screen->cursor_pos = (COLS - 5);
       wrefresh(win);
     }
   }
index 25d083b06278c54c619f6351dd2b294a413faf60..d98d1b6cc0b66fd2c9c7b7aeb995cf696744de04 100644 (file)
@@ -26,6 +26,9 @@ typedef struct {
   char *nickname;
   char *connection;
   char *channel;
+  char *channel_mode;
+  char *umode;
+  int away;
 } *SilcScreenBottomLine;
 
 typedef struct {
@@ -35,14 +38,14 @@ typedef struct {
   /* Output windows */
   WINDOW **output_win;
   WINDOW **output_stat_line;
-  unsigned int output_win_count;
+  uint32 output_win_count;
 
   /* Input window at the bottom of the screen */
   WINDOW *input_win;
   unsigned char *input_buffer;
-  unsigned int input_pos;
-  unsigned int input_end;
-  unsigned int cursor_pos;
+  uint32 input_pos;
+  uint32 input_end;
+  uint32 cursor_pos;
   int virtual_window;
 
   /* Bottom line on screen */
@@ -53,8 +56,8 @@ typedef struct {
 
   /* XXX */
   struct upper_status_line {
-    char *program_name;
-    char *program_version;
+    const char *program_name;
+    const char *program_version;
   } u_stat_line;
 
 } SilcScreenObject;
@@ -75,6 +78,9 @@ typedef SilcScreenObject *SilcScreen;
 /* Maximum length of connection name that will be shown on the screen */
 #define SILC_SCREEN_MAX_CONN_LEN 20
 
+/* Maximum length of user mode that will be shown on the screen */
+#define SILC_SCREEN_MAX_UMODE_LEN 20
+
 /* Macros */
 
 /* Macro used to insert typed character into the buffer. The character
@@ -103,6 +109,7 @@ WINDOW *silc_screen_create_output_window(SilcScreen screen);
 WINDOW *silc_screen_add_output_window(SilcScreen screen);
 void silc_screen_create_input_window(SilcScreen screen);
 void silc_screen_init_upper_status_line(SilcScreen screen);
+void silc_screen_print_upper_stat_line(SilcScreen screen);
 void silc_screen_init_output_status_line(SilcScreen screen);
 void silc_screen_print_clock(SilcScreen screen);
 void silc_screen_print_coordinates(SilcScreen screen, int win_index);
index e510ad2dbcd2a85256324ff9fe27e9d5ea193fff..47f39dd983e9b8f40a07c206bb30dd668c6204f0 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "clientincludes.h"
 #include "version.h"
 
+/* Static function prototypes */
+static int silc_client_bad_keys(unsigned char key);
+static void silc_client_clear_input(SilcClientInternal app);
+static void silc_client_process_message(SilcClientInternal app);
+static char *silc_client_parse_command(unsigned char *buffer);
+
+void silc_client_create_main_window(SilcClientInternal app);
+
+/* Static task callback prototypes */
+SILC_TASK_CALLBACK(silc_client_update_clock);
+SILC_TASK_CALLBACK(silc_client_run_commands);
+SILC_TASK_CALLBACK(silc_client_process_key_press);
+
 /* Long command line options */
 static struct option long_opts[] = 
 {
@@ -42,6 +48,7 @@ static struct option long_opts[] =
   { "private-key", 1, NULL, 'k' },
   { "config-file", 1, NULL, 'f' },
   { "no-silcrc", 0, NULL, 'q' },
+  { "debug", 0, NULL, 'd' },
   { "help", 0, NULL, 'h' },
   { "version", 0, NULL, 'V' },
   { "list-ciphers", 0, NULL, 1 },
@@ -52,6 +59,7 @@ static struct option long_opts[] =
   { "create-key-pair", 0, NULL, 'C' },
   { "pkcs", 1, NULL, 10 },
   { "bits", 1, NULL, 11 },
+  { "show-key", 1, NULL, 'S' },
 
   { NULL, 0, NULL, 0 }
 };
@@ -65,12 +73,17 @@ static char *opt_cipher = NULL;
 static char *opt_public_key = NULL;
 static char *opt_private_key = NULL;
 static char *opt_config_file = NULL;
-static int opt_no_silcrc = FALSE;
+static bool opt_no_silcrc = FALSE;
 
-static int opt_create_keypair = FALSE;
+static bool opt_create_keypair = FALSE;
+static bool opt_show_key = FALSE;
 static char *opt_pkcs = NULL;
+static char *opt_keyfile = NULL;
 static int opt_bits = 0;
 
+/* SILC Client operations */
+extern SilcClientOperations ops;
+
 /* Prints out the usage of silc client */
 
 void usage()
@@ -88,6 +101,7 @@ Usage: silc [options]\n\
   -k, --private-key=FILE       Private key used in SILC\n\
   -f, --config-file=FILE       Alternate configuration file\n\
   -q, --no-silcrc              Don't load ~/.silcrc on startup\n\
+  -d, --debug                  Enable debugging\n\
   -h, --help                   Display this help message\n\
   -V, --version                Display version\n\
       --list-ciphers           List supported ciphers\n\
@@ -98,6 +112,7 @@ Usage: silc [options]\n\
   -C, --create-key-pair        Create new public key pair\n\
       --pkcs=PKCS              Set the PKCS of the public key pair\n\
       --bits=VALUE             Set length of the public key pair\n\
+  -S, --show-key=FILE          Show the contents of the public key\n\
 \n");
 }
 
@@ -106,13 +121,15 @@ int main(int argc, char **argv)
   int opt, option_index = 1;
   int ret;
   SilcClient silc = NULL;
-  SilcClientConfig config = NULL;
-  
+  SilcClientInternal app = NULL;
+
+  silc_debug = FALSE;
+
   if (argc > 1) 
     {
       while ((opt = 
              getopt_long(argc, argv,
-                         "s:p:n:c:b:k:f:qhVC",
+                         "s:p:n:c:b:k:f:qdhVCS:",
                          long_opts, &option_index)) != EOF)
        {
          switch(opt) 
@@ -155,6 +172,9 @@ int main(int argc, char **argv)
            case 'q':
              opt_no_silcrc = TRUE;
              break;
+           case 'd':
+             silc_debug = TRUE;
+             break;
            case 'h':
              usage();
              exit(0);
@@ -194,6 +214,11 @@ SILC Secure Internet Live Conferencing, version %s\n",
              if (optarg)
                opt_bits = atoi(optarg);
              break;
+           case 'S':
+             opt_show_key = TRUE;
+             if (optarg)
+               opt_keyfile = strdup(optarg);
+             break;
 
            default:
              exit(0);
@@ -214,36 +239,125 @@ SILC Secure Internet Live Conferencing, version %s\n",
   signal(SIGFPE, SIG_DFL);
   //  signal(SIGINT, SIG_IGN);
   
-  /* Default configuration file */
-  if (!opt_config_file)
-    opt_config_file = strdup(SILC_CLIENT_CONFIG_FILE);
-
-  /* Read global configuration file. */
-  config = silc_client_config_alloc(opt_config_file);
-  if (config == NULL)
-    goto fail;
+#ifdef SOCKS
+  /* Init SOCKS */
+  SOCKSinit(argv[0]);
+#endif
 
   if (opt_create_keypair == TRUE) {
     /* Create new key pair and exit */
-    silc_client_create_key_pair(opt_pkcs, opt_bits);
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+    silc_client_create_key_pair(opt_pkcs, opt_bits, 
+                               NULL, NULL, NULL, NULL, NULL);
+    silc_free(opt_pkcs);
     exit(0);
   }
 
-  /* Read local configuration file */
+  if (opt_show_key == TRUE) {
+    /* Dump the key */
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+    silc_client_show_key(opt_keyfile);
+    silc_free(opt_keyfile);
+    exit(0);
+  }
+
+  /* Default configuration file */
+  if (!opt_config_file)
+    opt_config_file = strdup(SILC_CLIENT_CONFIG_FILE);
 
+  /* Allocate internal application context */
+  app = silc_calloc(1, sizeof(*app));
 
   /* Allocate new client */
-  ret = silc_client_alloc(&silc);
-  if (ret == FALSE)
+  app->client = silc = silc_client_alloc(&ops, app, silc_version_string);
+  if (!silc)
     goto fail;
 
-  /* Initialize the client */
-  silc->config = config;
+  /* Read global configuration file. */
+  app->config = silc_client_config_alloc(opt_config_file);
+
+  /* XXX Read local configuration file */
+
+  /* Get user information */
+  silc->username = silc_get_username();
+  silc->hostname = silc_net_localhost();
+  silc->realname = silc_get_real_name();
+
+  /* Register all configured ciphers, PKCS and hash functions. */
+  if (app->config) {
+    app->config->client = (void *)app;
+    if (!silc_client_config_register_ciphers(app->config))
+      silc_cipher_register_default();
+    if (!silc_client_config_register_pkcs(app->config))
+      silc_pkcs_register_default();
+    if (!silc_client_config_register_hashfuncs(app->config))
+      silc_hash_register_default();
+    if (!silc_client_config_register_hmacs(app->config))
+      silc_hmac_register_default();
+  } else {
+    /* Register default ciphers, pkcs, hash funtions and hmacs. */
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+  }
+
+  /* Check ~/.silc directory and public and private keys */
+  if (silc_client_check_silc_dir() == FALSE)
+    goto fail;
+
+  /* Load public and private key */
+  if (silc_client_load_keys(silc) == FALSE)
+    goto fail;
+
+  /* Initialize the client. This initializes the client library and
+     sets everything ready for silc_client_run. */
   ret = silc_client_init(silc);
   if (ret == FALSE)
     goto fail;
 
-  /* Run the client */
+  /* Register the main task that is used in client. This receives
+     the key pressings. */
+  silc_task_register(silc->io_queue, fileno(stdin), 
+                    silc_client_process_key_press,
+                    (void *)silc, 0, 0, 
+                    SILC_TASK_FD,
+                    SILC_TASK_PRI_NORMAL);
+
+  /* Register timeout task that updates clock every minute. */
+  silc_task_register(silc->timeout_queue, 0,
+                    silc_client_update_clock,
+                    (void *)silc, 
+                    silc_client_time_til_next_min(), 0,
+                    SILC_TASK_TIMEOUT,
+                    SILC_TASK_PRI_LOW);
+
+  if (app->config && app->config->commands) {
+    /* Run user configured commands with timeout */
+    silc_task_register(silc->timeout_queue, 0,
+                      silc_client_run_commands,
+                      (void *)silc, 0, 1,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_LOW);
+  }
+
+  /* Allocate the input buffer used to save typed characters */
+  app->input_buffer = silc_buffer_alloc(SILC_SCREEN_INPUT_WIN_SIZE);
+  silc_buffer_pull_tail(app->input_buffer, 
+                       SILC_BUFFER_END(app->input_buffer));
+
+  /* Initialize the screen */
+  silc_client_create_main_window(app);
+  silc_screen_print_coordinates(app->screen, 0);
+
+  /* Run the client. When this returns the application will be
+     terminated. */
   silc_client_run(silc);
 
   /* Stop the client. This probably has been done already but it
@@ -254,9 +368,332 @@ SILC Secure Internet Live Conferencing, version %s\n",
   exit(0);
 
  fail:
-  if (config)
-    silc_client_config_free(config);
+  if (opt_config_file)
+    silc_free(opt_config_file);
+  if (app->config)
+    silc_client_config_free(app->config);
   if (silc)
     silc_client_free(silc);
   exit(1);
 }
+
+/* Creates the main window used in SILC client. This is called always
+   at the initialization of the client. If user wants to create more
+   than one windows a new windows are always created by calling 
+   silc_client_add_window. */
+
+void silc_client_create_main_window(SilcClientInternal app)
+{
+  void *screen;
+
+  SILC_LOG_DEBUG(("Creating main window"));
+
+  app->screen = silc_screen_init();
+  app->screen->input_buffer = app->input_buffer->data;
+  app->screen->u_stat_line.program_name = silc_name;
+  app->screen->u_stat_line.program_version = silc_version;
+
+  /* Create the actual screen */
+  screen = (void *)silc_screen_create_output_window(app->screen);
+  silc_screen_create_input_window(app->screen);
+  silc_screen_init_upper_status_line(app->screen);
+  silc_screen_init_output_status_line(app->screen);
+
+  app->screen->bottom_line->nickname = silc_get_username();
+  silc_screen_print_bottom_line(app->screen, 0);
+}
+
+/* The main task on SILC client. This processes the key pressings user
+   has made. */
+
+SILC_TASK_CALLBACK(silc_client_process_key_press)
+{
+  SilcClient client = (SilcClient)context;
+  SilcClientInternal app = (SilcClientInternal)client->application;
+  int c;
+
+  /* There is data pending in stdin, this gets it directly */
+  c = wgetch(app->screen->input_win);
+  if (silc_client_bad_keys(c))
+    return;
+
+  SILC_LOG_DEBUG(("Pressed key: %d", c));
+
+  switch(c) {
+    /* 
+     * Special character handling
+     */
+  case KEY_UP: 
+  case KEY_DOWN:
+    break;
+  case KEY_RIGHT:
+    /* Right arrow */
+    SILC_LOG_DEBUG(("RIGHT"));
+    silc_screen_input_cursor_right(app->screen);
+    break;
+  case KEY_LEFT:
+    /* Left arrow */
+    SILC_LOG_DEBUG(("LEFT"));
+    silc_screen_input_cursor_left(app->screen);
+    break;
+  case KEY_BACKSPACE:
+  case KEY_DC:
+  case '\177':
+  case '\b':
+    /* Backspace */
+    silc_screen_input_backspace(app->screen);
+    break;
+  case '\011':
+    /* Tabulator */
+    break;
+  case KEY_IC:
+    /* Insert switch. Turns on/off insert on input window */
+    silc_screen_input_insert(app->screen);
+    break;
+  case CTRL('j'):
+  case '\r':
+    /* Enter, Return. User pressed enter we are ready to
+       process the message. */
+    silc_client_process_message(app);
+    break;
+  case CTRL('l'):
+    /* Refresh screen, Ctrl^l */
+    silc_screen_refresh_all(app->screen);
+    break;
+  case CTRL('a'):
+  case KEY_HOME:
+#ifdef KEY_BEG
+  case KEY_BEG:
+#endif
+    /* Beginning, Home */
+    silc_screen_input_cursor_home(app->screen);
+    break;
+  case CTRL('e'):
+#ifdef KEY_END
+  case KEY_END:
+#endif
+  case KEY_LL:
+    /* End */
+    silc_screen_input_cursor_end(app->screen);
+    break;
+  case CTRL('g'):
+    /* Bell, Ctrl^g */
+    beep();
+    break;
+  case KEY_DL:
+  case CTRL('u'):
+    /* Delete line */
+    silc_client_clear_input(app);
+    break;
+  default:
+    /* 
+     * Other characters 
+     */
+    if (c < 32) {
+      /* Control codes are printed as reversed */
+      c = (c & 127) | 64;
+      wattron(app->screen->input_win, A_REVERSE);
+      silc_screen_input_print(app->screen, c);
+      wattroff(app->screen->input_win, A_REVERSE);
+    } else  {
+      /* Normal character */
+      silc_screen_input_print(app->screen, c);
+    }
+  }
+
+  silc_screen_print_coordinates(app->screen, 0);
+  silc_screen_refresh_win(app->screen->input_win);
+}
+
+static int silc_client_bad_keys(unsigned char key)
+{
+  /* these are explained in curses.h */
+  switch(key) {
+  case KEY_SF:
+  case KEY_SR:
+  case KEY_NPAGE:
+  case KEY_PPAGE:
+  case KEY_PRINT:
+  case KEY_A1:
+  case KEY_A3:
+  case KEY_B2:
+  case KEY_C1:
+  case KEY_C3:
+#ifdef KEY_UNDO
+  case KEY_UNDO:
+#endif
+#ifdef KEY_EXIT
+  case KEY_EXIT:
+#endif
+  case '\v':           /* VT */
+  case '\E':           /* we ignore ESC */
+    return TRUE;
+  default: 
+    return FALSE; 
+  }
+}
+
+/* Clears input buffer */
+
+static void silc_client_clear_input(SilcClientInternal app)
+{
+  silc_buffer_clear(app->input_buffer);
+  silc_buffer_pull_tail(app->input_buffer,
+                       SILC_BUFFER_END(app->input_buffer));
+  silc_screen_input_reset(app->screen);
+}
+
+/* Processes messages user has typed on the screen. This either sends
+   a packet out to network or if command were written executes it. */
+
+static void silc_client_process_message(SilcClientInternal app)
+{
+  unsigned char *data;
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  data = app->input_buffer->data;
+  len = strlen(data);
+
+  if (data[0] == '/' && data[1] != ' ') {
+    /* Command */
+    uint32 argc = 0;
+    unsigned char **argv, *tmpcmd;
+    uint32 *argv_lens, *argv_types;
+    SilcClientCommand *cmd;
+    SilcClientCommandContext ctx;
+
+    /* Get the command */
+    tmpcmd = silc_client_parse_command(data);
+    cmd = silc_client_local_command_find(tmpcmd);
+    if (!cmd && (cmd = silc_client_command_find(tmpcmd)) == NULL) {
+      silc_say(app->client, app->current_win, "Invalid command: %s", tmpcmd);
+      silc_free(tmpcmd);
+      goto out;
+    }
+
+    /* Now parse all arguments */
+    silc_parse_command_line(data + 1, &argv, &argv_lens, 
+                           &argv_types, &argc, cmd->max_args);
+    silc_free(tmpcmd);
+
+    SILC_LOG_DEBUG(("Executing command: %s", cmd->name));
+
+    /* Allocate command context. This and its internals must be free'd 
+       by the command routine receiving it. */
+    ctx = silc_client_command_alloc();
+    ctx->client = app->client;
+    ctx->conn = app->conn;
+    ctx->command = cmd;
+    ctx->argc = argc;
+    ctx->argv = argv;
+    ctx->argv_lens = argv_lens;
+    ctx->argv_types = argv_types;
+
+    /* Execute command */
+    (*cmd->cb)(ctx);
+
+  } else {
+    /* Normal message to a channel */
+    if (len && app->conn && app->conn->current_channel &&
+       app->conn->current_channel->on_channel == TRUE) {
+      silc_print(app->client, "> %s", data);
+      silc_client_send_channel_message(app->client, 
+                                      app->conn,
+                                      app->conn->current_channel, NULL,
+                                      0, data, strlen(data), TRUE);
+    }
+  }
+
+ out:
+  /* Clear the input buffer */
+  silc_client_clear_input(app);
+}
+
+/* Returns the command fetched from user typed command line */
+
+static char *silc_client_parse_command(unsigned char *buffer)
+{
+  char *ret;
+  const char *cp = buffer;
+  int len;
+
+  len = strcspn(cp, " ");
+  ret = silc_to_upper((char *)++cp);
+  ret[len - 1] = 0;
+
+  return ret;
+}
+
+/* Updates clock on the screen every minute. */
+
+SILC_TASK_CALLBACK(silc_client_update_clock)
+{
+  SilcClient client = (SilcClient)context;
+  SilcClientInternal app = (SilcClientInternal)client->application;
+
+  /* Update the clock on the screen */
+  silc_screen_print_clock(app->screen);
+
+  /* Re-register this same task */
+  silc_task_register(qptr, 0, silc_client_update_clock, context, 
+                    silc_client_time_til_next_min(), 0,
+                    SILC_TASK_TIMEOUT,
+                    SILC_TASK_PRI_LOW);
+
+  silc_screen_refresh_win(app->screen->input_win);
+}
+
+/* Runs commands user configured in configuration file. This is
+   called when initializing client. */
+
+SILC_TASK_CALLBACK(silc_client_run_commands)
+{
+  SilcClient client = (SilcClient)context;
+  SilcClientInternal app = (SilcClientInternal)client->application;
+  SilcClientConfigSectionCommand *cs;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  cs = app->config->commands;
+  while(cs) {
+    uint32 argc = 0;
+    unsigned char **argv, *tmpcmd;
+    uint32 *argv_lens, *argv_types;
+    SilcClientCommand *cmd;
+    SilcClientCommandContext ctx;
+
+    /* Get the command */
+    tmpcmd = silc_client_parse_command(cs->command);
+    cmd = silc_client_local_command_find(tmpcmd);
+    if (!cmd && (cmd = silc_client_command_find(tmpcmd)) == NULL) {
+      silc_say(client, app->conn, "Invalid command: %s", tmpcmd);
+      silc_free(tmpcmd);
+      continue;
+    }
+    
+    /* Now parse all arguments */
+    silc_parse_command_line(cs->command + 1, &argv, &argv_lens, 
+                           &argv_types, &argc, cmd->max_args);
+    silc_free(tmpcmd);
+
+    SILC_LOG_DEBUG(("Executing command: %s", cmd->name));
+
+    /* Allocate command context. This and its internals must be free'd 
+       by the command routine receiving it. */
+    ctx = silc_client_command_alloc();
+    ctx->client = client;
+    ctx->conn = app->conn;
+    ctx->command = cmd;
+    ctx->argc = argc;
+    ctx->argv = argv;
+    ctx->argv_lens = argv_lens;
+    ctx->argv_types = argv_types;
+
+    /* Execute command */
+    (*cmd->cb)(ctx);
+
+    cs = cs->next;
+  }
+}
index baa83b349de6e9db3a35b664c7e7735b6aed74fe..a5d75d90a0733efff92d655e2f06e407483c021e 100644 (file)
    home directory. This may override global configuration settings. */
 #define SILC_CLIENT_HOME_CONFIG_FILE ".silcrc"
 
+/* Default public and private key file names */
+#define SILC_CLIENT_PUBLIC_KEY_NAME "public_key.pub"
+#define SILC_CLIENT_PRIVATE_KEY_NAME "private_key.prv"
+
+/* Default key expiration time, one year. */
+#define SILC_CLIENT_KEY_EXPIRES 365
+
+/* Default settings for creating key pair */
+#define SILC_CLIENT_DEF_PKCS "rsa"
+#define SILC_CLIENT_DEF_PKCS_LEN 1024
+
+/* XXX This is entirely temporary structure until UI is written again. */
+typedef struct {
+  /* Input buffer that holds the characters user types. This is
+     used only to store the typed chars for a while. */
+  SilcBuffer input_buffer;
+
+  /* The SILC client screen object */
+  SilcScreen screen;
+
+  /* Current physical window */
+  void *current_win;
+
+  SilcClientConnection conn;
+
+  /* Configuration object */
+  SilcClientConfig config;
+
+#ifdef SILC_SIM
+  /* SIM (SILC Module) table */
+  SilcSimContext **sim;
+  uint32 sim_count;
+#endif
+
+  /* The allocated client */
+  SilcClient client;
+} *SilcClientInternal;
+
+/* Macros */
+
+#ifndef CTRL
+#define CTRL(x) ((x) & 0x1f)   /* Ctrl+x */
+#endif
+
 #endif
diff --git a/apps/silc/testi.conf b/apps/silc/testi.conf
deleted file mode 100644 (file)
index 72dbaed..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-[cipher]
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[hash]
-md5::64:16
-sha1::64:20
-
-#[pkcs]
-#rsa::1024
-#dss::1024
-
-[connection]
-#lassi.kuo.fi.ssh.com:passwd::1333
-
-[commands]
-#/server lassi.kuo.fi.ssh.com:1333
-#/server lassi:1334
-#/server leevi:1333
diff --git a/apps/silc/testi2.conf b/apps/silc/testi2.conf
deleted file mode 100644 (file)
index e1fa600..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-[cipher]
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[hash]
-md5::64:16
-sha1::64:20
-
-#[pkcs]
-#rsa::1024
-#dss::1024
-
-[connection]
-#lassi.kuo.fi.ssh.com:passwd::1333
-
-[commands]
-#/server lassi:1333
-/server lassi:1334
-#/server leevi:1333
index 385b8ccdc2ba6786cc1b0f871408b83a077c1e75..3322c946c71ce69a95d1a4e0a521a8a4f75f8768 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-bin_PROGRAMS = silcd
+sbin_PROGRAMS = silcd
 
 silcd_SOURCES = \
        protocol.c \
        route.c \
-       server.c \
+       packet_send.c \
+       packet_receive.c \
        idlist.c \
        command.c \
        command_reply.c \
+       silcd.c \
+       server.c \
+       server_util.c \
+       server_backup.c \
        serverconfig.c \
        serverid.c \
-       silcd.c \
        server_version.c
 
-LDADD = -L. -L.. -L../lib -lsilc
+silcd_DEPENDENCIES = ../lib/libsilc.a
+
+LIBS = $(SILC_COMMON_LIBS)
+LDADD =
 
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../lib/silccore -I../lib/silccrypt \
-       -I../lib/silcmath -I../lib/silcske -I../lib/silcsim \
-       -I../includes \
-       -I../lib/silcmath/gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
index b9a0cc63b90f069216bf6bc42f1a58079f75f260..b4d5bcee37adf53669b7e5e77dde3ac515eb6dae 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
+static int silc_server_is_registered(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcServerCommandContext cmd,
+                                    SilcCommand command);
+static void 
+silc_server_command_send_status_reply(SilcServerCommandContext cmd,
+                                     SilcCommand command,
+                                     SilcCommandStatus status);
+static void 
+silc_server_command_send_status_data(SilcServerCommandContext cmd,
+                                    SilcCommand command,
+                                    SilcCommandStatus status,
+                                    uint32 arg_type,
+                                    const unsigned char *arg,
+                                    uint32 arg_len);
+static bool
+silc_server_command_pending_error_check(SilcServerCommandContext cmd,
+                                       SilcServerCommandReplyContext cmdr,
+                                       SilcCommand command);
+SILC_TASK_CALLBACK(silc_server_command_process_timeout);
+
 /* Server command list. */
 SilcServerCommand silc_command_list[] =
 {
   SILC_SERVER_CMD(whois, WHOIS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(whowas, WHOWAS, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(identify, IDENTIFY, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(list, LIST, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(nick, NICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(list, LIST, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(topic, TOPIC, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(invite, INVITE, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(quit, QUIT, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(kill, KILL, SILC_CF_LAG_STRICT | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(info, INFO, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(connect, CONNECT, 
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
   SILC_SERVER_CMD(ping, PING, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(oper, OPER, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(join, JOIN, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(motd, MOTD, SILC_CF_LAG | SILC_CF_REG),
   SILC_SERVER_CMD(umode, UMODE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(restart, RESTART, 
-                 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(cmode, CMODE, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(cumode, CUMODE, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(kick, KICK, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(ban, BAN, SILC_CF_LAG_STRICT | SILC_CF_REG),
   SILC_SERVER_CMD(close, CLOSE,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
-  SILC_SERVER_CMD(die, DIE, SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER),
+  SILC_SERVER_CMD(shutdown, SHUTDOWN, SILC_CF_LAG | SILC_CF_REG | 
+                 SILC_CF_OPER),
   SILC_SERVER_CMD(silcoper, SILCOPER,
                  SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER),
-  SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG | SILC_CF_REG),
-  SILC_SERVER_CMD(names, NAMES, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(leave, LEAVE, SILC_CF_LAG_STRICT | SILC_CF_REG),
+  SILC_SERVER_CMD(users, USERS, SILC_CF_LAG | SILC_CF_REG),
+  SILC_SERVER_CMD(getkey, GETKEY, SILC_CF_LAG | SILC_CF_REG),
 
   { NULL, 0 },
 };
 
-/* List of pending commands. */
-SilcServerCommandPending *silc_command_pending = NULL;
+/* Performs several checks to the command. It first checks whether this
+   command was called as pending command callback. If it was then it checks
+   whether error occurred in the command reply where the pending command
+   callback was called. 
+
+   It also checks that the requested command includes correct amount
+   of arguments. */
+#define SILC_SERVER_COMMAND_CHECK(command, context, min, max)                \
+do {                                                                         \
+  uint32 _argc;                                                                      \
+                                                                             \
+  SILC_LOG_DEBUG(("Start"));                                                 \
+                                                                             \
+  if (silc_server_command_pending_error_check(cmd, context2, command)) {      \
+    silc_server_command_free(cmd);                                           \
+    return;                                                                  \
+  }                                                                          \
+                                                                             \
+  _argc = silc_argument_get_arg_num(cmd->args);                                      \
+  if (_argc < min) {                                                         \
+    silc_server_command_send_status_reply(cmd, command,                              \
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \
+    silc_server_command_free(cmd);                                           \
+    return;                                                                  \
+  }                                                                          \
+  if (_argc > max) {                                                         \
+    silc_server_command_send_status_reply(cmd, command,                              \
+                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);   \
+    silc_server_command_free(cmd);                                           \
+    return;                                                                  \
+  }                                                                          \
+} while(0)
+
+/* Returns TRUE if the connection is registered. Unregistered connections
+   usually cannot send commands hence the check. */
+
+static int silc_server_is_registered(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcServerCommandContext cmd,
+                                    SilcCommand command)
+{
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  if (!idata)
+    return FALSE;
 
-/* Add new pending command to the list of pending commands. Currently
-   pending commands are executed from command replies, thus we can
-   execute any command after receiving some specific command reply.
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
+    return TRUE;
 
-   The argument `reply_cmd' is the command reply from where the callback
-   function is to be called, thus, it IS NOT the command to be executed. */
+  silc_server_command_send_status_reply(cmd, command,
+                                       SILC_STATUS_ERR_NOT_REGISTERED);
+  return FALSE;
+}
 
-void silc_server_command_pending(SilcCommand reply_cmd,
-                                SilcCommandCb callback,
-                                void *context)
+/* Internal context to hold data when executed command with timeout. */
+typedef struct {
+  SilcServerCommandContext ctx;
+  SilcServerCommand *cmd;
+} *SilcServerCommandTimeout;
+
+/* Timeout callback to process commands with timeout for client. Client's
+   commands are always executed with timeout. */
+
+SILC_TASK_CALLBACK(silc_server_command_process_timeout)
 {
-  SilcServerCommandPending *reply, *r;
+  SilcServerCommandTimeout timeout = (SilcServerCommandTimeout)context;
+  SilcClientEntry client = (SilcClientEntry)timeout->ctx->sock->user_data;
 
-  reply = silc_calloc(1, sizeof(*reply));
-  reply->reply_cmd = reply_cmd;
-  reply->context = context;
-  reply->callback = callback;
+  if (!client) {
+    silc_server_command_free(timeout->ctx);
+    silc_free(timeout);
+  }
+
+  /* Update access time */
+  client->last_command = time(NULL);
+
+  if (!(timeout->cmd->flags & SILC_CF_REG))
+    timeout->cmd->cb(timeout->ctx, NULL);
+  else if (silc_server_is_registered(timeout->ctx->server, 
+                                    timeout->ctx->sock, 
+                                    timeout->ctx, 
+                                    timeout->cmd->cmd))
+    timeout->cmd->cb(timeout->ctx, NULL);
+  else
+    silc_server_command_free(timeout->ctx);
+
+  silc_free(timeout);
+}
+
+/* Processes received command packet. */
 
-  if (silc_command_pending == NULL) {
-    silc_command_pending = reply;
+void silc_server_command_process(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  SilcServerCommandContext ctx;
+  SilcServerCommand *cmd;
+  SilcCommand command;
+
+  /* Allocate command context. This must be free'd by the
+     command routine receiving it. */
+  ctx = silc_server_command_alloc();
+  ctx->server = server;
+  ctx->sock = silc_socket_dup(sock);
+  ctx->packet = silc_packet_context_dup(packet); /* Save original packet */
+  
+  /* Parse the command payload in the packet */
+  ctx->payload = silc_command_payload_parse(packet->buffer->data,
+                                           packet->buffer->len);
+  if (!ctx->payload) {
+    SILC_LOG_ERROR(("Bad command payload, packet dropped"));
+    silc_buffer_free(packet->buffer);
+    silc_packet_context_free(packet);
+    silc_socket_free(ctx->sock);
+    silc_free(ctx);
     return;
   }
+  ctx->args = silc_command_get_args(ctx->payload);
 
-  for (r = silc_command_pending; r; r = r->next) {
-    if (r->next == NULL) {
-      r->next = reply;
+  /* Get the command */
+  command = silc_command_get(ctx->payload);
+  for (cmd = silc_command_list; cmd->cb; cmd++)
+    if (cmd->cmd == command)
       break;
+
+  if (cmd == NULL) {
+    silc_server_command_send_status_reply(ctx, command,
+                                         SILC_STATUS_ERR_UNKNOWN_COMMAND);
+    silc_server_command_free(ctx);
+    return;
+  }
+
+  /* Execute client's commands always with timeout.  Normally they are
+     executed with zero (0) timeout but if client is sending command more
+     frequently than once in 2 seconds, then the timeout may be 0 to 2
+     seconds. */
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    SilcClientEntry client = (SilcClientEntry)sock->user_data;
+    SilcServerCommandTimeout timeout = silc_calloc(1, sizeof(*timeout));
+    int fast;
+
+    timeout->ctx = ctx;
+    timeout->cmd = cmd;
+
+    if (client->last_command && (time(NULL) - client->last_command) < 2) {
+      client->fast_command++;
+      fast = FALSE;
+    } else {
+      client->fast_command = ((client->fast_command - 1) <= 0 ? 0 : 
+                             client->fast_command--);
+      fast = TRUE;
     }
+
+    if (!fast && ((cmd->flags & SILC_CF_LAG_STRICT) ||
+                 (client->fast_command > 5 && cmd->flags & SILC_CF_LAG)))
+      silc_schedule_task_add(server->schedule, sock->sock, 
+                        silc_server_command_process_timeout,
+                        (void *)timeout, 
+                        2 - (time(NULL) - client->last_command), 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    else
+      silc_schedule_task_add(server->schedule, sock->sock, 
+                        silc_server_command_process_timeout,
+                        (void *)timeout, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Execute for server */
+
+  if (!(cmd->flags & SILC_CF_REG))
+    cmd->cb(ctx, NULL);
+  else if (silc_server_is_registered(server, sock, ctx, cmd->cmd))
+    cmd->cb(ctx, NULL);
+  else
+    silc_server_command_free(ctx);
+}
+
+/* Allocate Command Context */
+
+SilcServerCommandContext silc_server_command_alloc()
+{
+  SilcServerCommandContext ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->users++;
+  return ctx;
+}
+
+/* Free's the command context allocated before executing the command */
+
+void silc_server_command_free(SilcServerCommandContext ctx)
+{
+  ctx->users--;
+  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
+                 ctx->users));
+  if (ctx->users < 1) {
+    if (ctx->payload)
+      silc_command_payload_free(ctx->payload);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->sock)
+      silc_socket_free(ctx->sock); /* Decrease reference counter */
+    silc_free(ctx);
+  }
+}
+
+/* Duplicate Command Context by adding reference counter. The context won't
+   be free'd untill it hits zero. */
+
+SilcServerCommandContext 
+silc_server_command_dup(SilcServerCommandContext ctx)
+{
+  ctx->users++;
+  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
+                 ctx->users));
+  return ctx;
+}
+
+/* Add new pending command to be executed when reply to a command has been
+   received. The `reply_cmd' is the command that will call the `callback'
+   with `context' when reply has been received.  It can be SILC_COMMAND_NONE
+   to match any command with the `ident'.  If `ident' is non-zero
+   the `callback' will be executed when received reply with command
+   identifier `ident'. If there already exists pending command for the
+   specified command, ident, callback and context this function has no
+   effect. */
+
+bool silc_server_command_pending(SilcServer server,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
+                                SilcCommandCb callback,
+                                void *context)
+{
+  SilcServerCommandPending *reply;
+
+  /* Check whether identical pending already exists for same command,
+     ident, callback and callback context. If it does then it would be
+     error to register it again. */
+  silc_dlist_start(server->pending_commands);
+  while ((reply = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
+       reply->callback == callback && reply->context == context)
+      return FALSE;
   }
+
+  reply = silc_calloc(1, sizeof(*reply));
+  reply->reply_cmd = reply_cmd;
+  reply->ident = ident;
+  reply->context = context;
+  reply->callback = callback;
+  silc_dlist_add(server->pending_commands, reply);
+
+  return TRUE;
 }
 
 /* Deletes pending command by reply command type. */
 
-void silc_server_command_pending_del(SilcCommand reply_cmd)
+void silc_server_command_pending_del(SilcServer server,
+                                    SilcCommand reply_cmd,
+                                    uint16 ident)
 {
-  SilcServerCommandPending *r, *tmp;
-  
-  if (silc_command_pending) {
-    if (silc_command_pending->reply_cmd == reply_cmd) {
-      silc_free(silc_command_pending);
-      silc_command_pending = NULL;
-      return;
-    }
+  SilcServerCommandPending *r;
 
-    for (r = silc_command_pending; r; r = r->next) {
-      if (r->next && r->next->reply_cmd == reply_cmd) {
-       tmp = r->next;
-       r->next = r->next->next;
-       silc_free(tmp);
-       break;
-      }
+  silc_dlist_start(server->pending_commands);
+  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if (r->reply_cmd == reply_cmd && r->ident == ident) {
+      silc_dlist_del(server->pending_commands, r);
+      break;
     }
   }
 }
 
-/* Free's the command context allocated before executing the command */
+/* Checks for pending commands and marks callbacks to be called from
+   the command reply function. Returns TRUE if there were pending command. */
 
-static void silc_server_command_free(SilcServerCommandContext cmd)
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+                                 SilcServerCommandReplyContext ctx,
+                                 SilcCommand command, 
+                                 uint16 ident,
+                                 uint32 *callbacks_count)
 {
-  if (cmd) {
-    silc_command_free_payload(cmd->payload);
-    silc_free(cmd);
+  SilcServerCommandPending *r;
+  SilcServerCommandPendingCallbacks callbacks = NULL;
+  int i = 0;
+
+  silc_dlist_start(server->pending_commands);
+  while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
+    if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
+       && r->ident == ident) {
+      callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+      callbacks[i].context = r->context;
+      callbacks[i].callback = r->callback;
+      ctx->ident = ident;
+      i++;
+    }
   }
+
+  *callbacks_count = i;
+  return callbacks;
 }
 
-/* Sends command status message as command reply packet. */
+/* Sends simple status message as command reply packet */
 
 static void 
-silc_server_command_send_status_msg(SilcServerCommandContext cmd,
-                                   SilcCommand command,
-                                   SilcCommandStatus status,
-                                   unsigned char *msg,
-                                   unsigned int msg_len)
+silc_server_command_send_status_reply(SilcServerCommandContext cmd,
+                                     SilcCommand command,
+                                     SilcCommandStatus status)
 {
-  SilcBuffer sp_buf, buffer;
+  SilcBuffer buffer;
 
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
-  sp_buf = silc_command_encode_status_payload(status, msg, msg_len);
-  buffer = silc_command_encode_payload_va(command, 1, 
-                                         sp_buf->data, sp_buf->len);
+  buffer = 
+    silc_command_reply_payload_encode_va(command, status, 
+                                        silc_command_get_ident(cmd->payload),
+                                        0);
   silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
   silc_buffer_free(buffer);
-  silc_buffer_free(sp_buf);
 }
 
-/* Sends simple status message as command reply packet */
+/* Sends command status reply with one extra argument. The argument
+   type must be sent as argument. */
 
 static void 
-silc_server_command_send_status_reply(SilcServerCommandContext cmd,
-                                     SilcCommand command,
-                                     SilcCommandStatus status)
+silc_server_command_send_status_data(SilcServerCommandContext cmd,
+                                    SilcCommand command,
+                                    SilcCommandStatus status,
+                                    uint32 arg_type,
+                                    const unsigned char *arg,
+                                    uint32 arg_len)
 {
-  SilcBuffer sp_buf, buffer;
+  SilcBuffer buffer;
 
   SILC_LOG_DEBUG(("Sending command status %d", status));
 
-  sp_buf = silc_command_encode_status_payload(status, NULL, 0);
-  buffer = silc_command_encode_payload_va(command, 1, 
-                                         sp_buf->data, sp_buf->len);
+  buffer = 
+    silc_command_reply_payload_encode_va(command, status, 
+                                        silc_command_get_ident(cmd->payload),
+                                        1, arg_type, arg, arg_len);
   silc_server_packet_send(cmd->server, cmd->sock,
                          SILC_PACKET_COMMAND_REPLY, 0, 
                          buffer->data, buffer->len, FALSE);
   silc_buffer_free(buffer);
-  silc_buffer_free(sp_buf);
 }
 
-/* Server side of command WHOIS. Processes user's query and sends found 
-   results as command replies back to the client. */
+/* This function can be called to check whether in the command reply
+   an error occurred. This function has no effect if this is called
+   when the command function was not called as pending command callback. 
+   This returns TRUE if error had occurred. */
 
-SILC_SERVER_CMD_FUNC(whois)
+static bool
+silc_server_command_pending_error_check(SilcServerCommandContext cmd,
+                                       SilcServerCommandReplyContext cmdr,
+                                       SilcCommand command)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  char *tmp, *nick = NULL, *server = NULL;
-  unsigned int argc, count = 0, len;
-  SilcClientList *entry;
-  SilcBuffer sp_buf, packet;
-  unsigned char *id_string;
+  SilcCommandStatus status;
+
+  if (!cmd->pending || !cmdr)
+    return FALSE;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmdr->args, 1, NULL));
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END) {
+    SilcBuffer buffer;
+
+    /* Send the same command reply payload */
+    silc_command_set_ident(cmdr->payload, 
+                          silc_command_get_ident(cmd->payload));
+    buffer = silc_command_payload_encode_payload(cmdr->payload);
+    silc_server_packet_send(cmd->server, cmd->sock,
+                           SILC_PACKET_COMMAND_REPLY, 0, 
+                           buffer->data, buffer->len, FALSE);
+    silc_buffer_free(buffer);
+    return TRUE;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  return FALSE;
+}
 
-  argc = silc_command_get_arg_num(cmd->payload);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
-    goto out;
-  }
+/******************************************************************************
 
-  /* Get the nickname@server string and parse it. */
-  tmp = silc_command_get_first_arg(cmd->payload, NULL);
-  if (tmp) {
-    if (strchr(tmp, '@')) {
-      len = strcspn(tmp, "@");
-      nick = silc_calloc(len + 1, sizeof(char));
-      memcpy(nick, tmp, len);
-      server = silc_calloc(strlen(tmp) - len, sizeof(char));
-      memcpy(server, tmp + len + 1, strlen(tmp) - len - 1);
+                              WHOIS Functions
+
+******************************************************************************/
+
+static int
+silc_server_command_whois_parse(SilcServerCommandContext cmd,
+                               SilcClientID ***client_id,
+                               uint32 *client_id_count,
+                               char **nickname,
+                               char **server_name,
+                               int *count,
+                               SilcCommand command)
+{
+  unsigned char *tmp;
+  uint32 len;
+  uint32 argc = silc_argument_get_arg_num(cmd->args);
+  int i, k;
+
+  /* If client ID is in the command it must be used instead of nickname */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (!tmp) {
+    /* No ID, get the nickname@server string and parse it. */
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    if (tmp) {
+      silc_parse_userfqdn(tmp, nickname, server_name);
     } else {
-      nick = strdup(tmp);
+      silc_server_command_send_status_reply(cmd, command,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      return FALSE;
     }
   } else {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
+    /* Command includes ID, we must use that.  Also check whether the command
+       has more than one ID set - take them all. */
+
+    *client_id = silc_calloc(1, sizeof(**client_id));
+    (*client_id)[0] = silc_id_payload_parse_id(tmp, len);
+    if ((*client_id)[0] == NULL) {
+      silc_free(*client_id);
+      silc_server_command_send_status_reply(cmd, command,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      return FALSE;
+    }
+    *client_id_count = 1;
+
+    /* Take all ID's from the command packet */
+    if (argc > 1) {
+      for (k = 1, i = 1; i < argc; i++) {
+       tmp = silc_argument_get_arg_type(cmd->args, i + 3, &len);
+       if (tmp) {
+         *client_id = silc_realloc(*client_id, sizeof(**client_id) *
+                                   (*client_id_count + 1));
+         (*client_id)[k] = silc_id_payload_parse_id(tmp, len);
+         if ((*client_id)[k] == NULL) {
+           /* Cleanup all and fail */
+           for (i = 0; i < *client_id_count; i++)
+             silc_free((*client_id)[i]);
+           silc_free(*client_id);
+           silc_server_command_send_status_reply(
+                                        cmd, command,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+           return FALSE;
+         }
+         (*client_id_count)++;
+         k++;
+       }
+      }
+    }
   }
 
   /* Get the max count of reply messages allowed */
-  if (argc == 2) {
-    tmp = silc_command_get_next_arg(cmd->payload, NULL);
-    if (!tmp) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOIS,
-                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
-      if (nick)
-       silc_free(nick);
-      if (server)
-       silc_free(server);
-      goto out;
-    }
-    count = atoi(tmp);
-  }
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (tmp)
+    *count = atoi(tmp);
+  else
+    *count = 0;
 
-  /* Then, make the query from our local client list */
-  entry = silc_idlist_find_client_by_nickname(cmd->server->local_list->clients,
-                                             nick, server);
-  if (!entry) {
+  return TRUE;
+}
+
+/* Resolve context used by both WHOIS and IDENTIFY commands */
+typedef struct {
+  SilcServerEntry router;
+  uint16 ident;
+  unsigned char **res_argv;
+  uint32 *res_argv_lens;
+  uint32 *res_argv_types;
+  uint32 res_argc;
+} *SilcServerResolveContext;
+
+static bool
+silc_server_command_whois_check(SilcServerCommandContext cmd,
+                               SilcClientEntry *clients,
+                               uint32 clients_count)
+{
+  SilcServer server = cmd->server;
+  SilcClientEntry entry;
+  SilcServerResolveContext resolve = NULL, r = NULL;
+  uint32 resolve_count = 0;
+  int i, k;
+  bool no_res = TRUE;
 
-    /* If we are normal server and are connected to a router we will
-       make global query from the router. */
-    if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) {
+  SILC_LOG_DEBUG(("Start"));
 
-      goto ok;
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
+
+    if ((entry->nickname && entry->username && entry->userinfo) ||
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      if (!entry->router)
+       continue;
+
+      /* If we are normal server, and we've not resolved this client from
+        router and it is global client, we'll check whether it is on some
+        channel.  If not then we cannot be sure about its validity, and
+        we'll resolve it from router. */
+      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
+         entry->connection || silc_hash_table_count(entry->channels))
+       continue;
     }
-    
-    /* If we are router then we will check our global list as well. */
-    if (cmd->server->server_type == SILC_ROUTER) {
-      entry =
-       silc_idlist_find_client_by_nickname(cmd->server->global_list->clients,
-                                           nick, server);
-      if (!entry) {
-       silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
-                                           SILC_STATUS_ERR_NO_SUCH_NICK,
-                                           tmp, strlen(tmp));
-       goto out;
+
+    /* We need to resolve this entry since it is not complete */
+
+    if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+      /* The entry is being resolved (and we are not the resolver) so attach
+        to the command reply and we're done with this one. */
+      silc_server_command_pending(server, SILC_COMMAND_NONE, 
+                                 entry->resolve_cmd_ident,
+                                 silc_server_command_whois,
+                                 silc_server_command_dup(cmd));
+      no_res = FALSE;
+    } else {
+      if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+       /* We've resolved this and it still is not ready.  We'll return
+          and are that this will be handled again after it is resolved. */
+       for (i = 0; i < resolve_count; i++) {
+         for (k = 0; k < r->res_argc; k++)
+           silc_free(r->res_argv[k]);
+         silc_free(r->res_argv);
+         silc_free(r->res_argv_lens);
+         silc_free(r->res_argv_types);
+       }
+       silc_free(resolve);
+       return FALSE;
+      } else {
+       /* We'll resolve this client */
+       SilcBuffer idp;
+
+       r = NULL;
+       for (k = 0; k < resolve_count; k++) {
+         if (resolve[k].router == entry->router) {
+           r = &resolve[k];
+           break;
+         }
+       }
+
+       if (!r) {
+         resolve = silc_realloc(resolve, sizeof(*resolve) * 
+                                (resolve_count + 1));
+         r = &resolve[resolve_count];
+         memset(r, 0, sizeof(*r));
+         r->router = entry->router;
+         r->ident = ++server->cmd_ident;
+         resolve_count++;
+       }
+
+       r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
+                                  (r->res_argc + 1));
+       r->res_argv_lens = silc_realloc(r->res_argv_lens, 
+                                       sizeof(*r->res_argv_lens) *
+                                       (r->res_argc + 1));
+       r->res_argv_types = silc_realloc(r->res_argv_types, 
+                                        sizeof(*r->res_argv_types) *
+                                        (r->res_argc + 1));
+       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+       r->res_argv[r->res_argc] = silc_calloc(idp->len, 
+                                              sizeof(**r->res_argv));
+       memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
+       r->res_argv_lens[r->res_argc] = idp->len;
+       r->res_argv_types[r->res_argc] = r->res_argc + 3;
+       r->res_argc++;
+       silc_buffer_free(idp);
+
+       entry->resolve_cmd_ident = r->ident;
+       entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+       entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
       }
-      goto ok;
     }
+  }
 
-    silc_server_command_send_status_msg(cmd, SILC_COMMAND_WHOIS,
-                                       SILC_STATUS_ERR_NO_SUCH_NICK,
-                                       tmp, strlen(tmp));
-    goto out;
+  /* Do the resolving */
+  for (i = 0; i < resolve_count; i++) {
+    SilcBuffer res_cmd;
+
+    r = &resolve[i];
+
+    /* Send WHOIS request. We send WHOIS since we're doing the requesting
+       now anyway so make it a good one. */
+    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+                                         r->res_argc, r->res_argv, 
+                                         r->res_argv_lens,
+                                         r->res_argv_types, 
+                                         r->ident);
+    silc_server_packet_send(server, r->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           res_cmd->data, res_cmd->len, FALSE);
+
+    /* Reprocess this packet after received reply */
+    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                               r->ident,
+                               silc_server_command_whois,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+
+    silc_buffer_free(res_cmd);
+    for (k = 0; k < r->res_argc; k++)
+      silc_free(r->res_argv[k]);
+    silc_free(r->res_argv);
+    silc_free(r->res_argv_lens);
+    silc_free(r->res_argv_types);
+    no_res = FALSE;
   }
+  silc_free(resolve);
 
- ok:
-  /* XXX, works only for local server info */
+  return no_res;
+}
 
-  /* Send WHOIS reply */
-  id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
-  tmp = silc_command_get_first_arg(cmd->payload, NULL),
-  sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
+static void
+silc_server_command_whois_send_reply(SilcServerCommandContext cmd,
+                                    SilcClientEntry *clients,
+                                    uint32 clients_count,
+                                    int count,
+                                    const char *nickname,
+                                    SilcClientID **client_ids)
+{
+  SilcServer server = cmd->server;
+  char *tmp;
+  int i, k, len, valid_count;
+  SilcBuffer packet, idp, channels;
+  SilcClientEntry entry;
+  SilcCommandStatus status;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char nh[256], uh[256];
+  unsigned char idle[4], mode[4];
+  unsigned char *fingerprint;
+  SilcSocketConnection hsock;
+
+  /* Process only valid clients and ignore those that are not registered. */
+  valid_count = 0;
+  for (i = 0; i < clients_count; i++) {
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      valid_count++;
+    else
+      clients[i] = NULL;
+  }
 
-  /* XXX */
-  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
-    char nh[256], uh[256];
-    SilcSocketConnection hsock;
+  if (!valid_count) {
+    /* No valid clients found, send error reply */
+    if (nickname) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nickname, strlen(nickname));
+    } else if (client_ids && client_ids[0]) {
+      SilcBuffer idp = silc_id_payload_encode(client_ids[0], SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
+    return;
+  }
 
+  /* Start processing found clients. */
+  if (valid_count > 1)
+    status = SILC_STATUS_LIST_START;
+  else
+    status = SILC_STATUS_OK;
+
+  for (i = 0, k = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
+
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+    if (valid_count > 1 && k == valid_count - 1)
+      status = SILC_STATUS_LIST_END;
+    if (count && k - 1 == count)
+      status = SILC_STATUS_LIST_END;
+
+    /* Send WHOIS reply */
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+    tmp = silc_argument_get_first_arg(cmd->args, NULL);
+    
     memset(uh, 0, sizeof(uh));
     memset(nh, 0, sizeof(nh));
-
+    memset(idle, 0, sizeof(idle));
+    
     strncat(nh, entry->nickname, strlen(entry->nickname));
-    strncat(nh, "@", 1);
-    len = entry->router ? strlen(entry->router->server_name) :
-      strlen(cmd->server->server_name);
-    strncat(nh, entry->router ? entry->router->server_name :
-           cmd->server->server_name, len);
-
+    if (!strchr(entry->nickname, '@')) {
+      strncat(nh, "@", 1);
+      if (entry->servername) {
+       strncat(nh, entry->servername, strlen(entry->servername));
+      } else {
+       len = entry->router ? strlen(entry->router->server_name) :
+         strlen(server->server_name);
+       strncat(nh, entry->router ? entry->router->server_name :
+               server->server_name, len);
+      }
+    }
+      
     strncat(uh, entry->username, strlen(entry->username));
-    strncat(uh, "@", 1);
-    hsock = (SilcSocketConnection)entry->connection;
-    len = hsock->hostname ? strlen(hsock->hostname) : strlen(hsock->ip);
-    strncat(uh, hsock->hostname ? hsock->hostname : hsock->ip, len);
+    if (!strchr(entry->username, '@')) {
+      strncat(uh, "@", 1);
+      hsock = (SilcSocketConnection)entry->connection;
+      len = strlen(hsock->hostname);
+      strncat(uh, hsock->hostname, len);
+    }
 
-    /* XXX */
-    if (entry->userinfo)
-      packet = 
-        silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 5, 
-                                      sp_buf->data, sp_buf->len,
-                                      id_string, SILC_ID_CLIENT_LEN,
-                                      nh, strlen(nh),
-                                      uh, strlen(uh),
-                                      entry->userinfo, 
-                                      strlen(entry->userinfo));
+    channels = silc_server_get_client_channel_list(server, entry);
+
+    if (entry->data.fingerprint[0] != 0 && entry->data.fingerprint[1] != 0)
+      fingerprint = entry->data.fingerprint;
     else
-      packet = 
-        silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4,
-                                      sp_buf->data, sp_buf->len,
-                                      id_string, SILC_ID_CLIENT_LEN,
-                                      nh, strlen(nh),
-                                      uh, strlen(uh));
+      fingerprint = NULL;
+      
+    SILC_PUT32_MSB(entry->mode, mode);
 
-  } else {
-    /* XXX */
-    packet = 
-      silc_command_encode_payload_va(SILC_COMMAND_WHOIS, 4, 
-                                    sp_buf->data, sp_buf->len,
-                                    id_string, SILC_ID_CLIENT_LEN,
-                                    entry->nickname, strlen(entry->nickname),
-                                    tmp, strlen(tmp)); /* XXX */
-  }
-  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                         0, packet->data, packet->len, FALSE);
+    if (entry->connection) {
+      SILC_PUT32_MSB((time(NULL) - entry->data.last_receive), idle);
+    }
 
-  silc_free(id_string);
-  silc_buffer_free(packet);
-  silc_free(sp_buf);
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
+                                          status, ident, 8, 
+                                          2, idp->data, idp->len,
+                                          3, nh, strlen(nh),
+                                          4, uh, strlen(uh),
+                                          5, entry->userinfo, 
+                                          strlen(entry->userinfo),
+                                          6, channels ? channels->data : NULL,
+                                          channels ? channels->len : 0,
+                                          7, mode, 4,
+                                          8, idle, 4,
+                                          9, fingerprint,
+                                          fingerprint ? 20 : 0);
+
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                           0, packet->data, packet->len, FALSE);
+    
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+    if (channels)
+      silc_buffer_free(channels);
 
- out:
-  silc_server_command_free(cmd);
+    k++;
+  }
 }
 
-SILC_SERVER_CMD_FUNC(whowas)
+static void 
+silc_server_command_whois_send_router(SilcServerCommandContext cmd)
 {
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send WHOIS command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_whois,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
 }
 
-SILC_SERVER_CMD_FUNC(identify)
+static int
+silc_server_command_whois_process(SilcServerCommandContext cmd)
 {
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  char *tmp, *nick = NULL, *server = NULL;
-  unsigned int argc, count = 0, len;
-  SilcClientList *entry;
-  SilcBuffer sp_buf, packet;
-  unsigned char *id_string;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  argc = silc_command_get_arg_num(cmd->payload);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-  if (argc > 2) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
+  SilcServer server = cmd->server;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0;
+  SilcClientEntry *clients = NULL, entry;
+  SilcClientID **client_id = NULL;
+  uint32 client_id_count = 0, clients_count = 0;
+  int i, ret = 0;
+  bool check_global = FALSE;
+
+  /* Parse the whois request */
+  if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count, 
+                                      &nick, &server_name, &count,
+                                      SILC_COMMAND_WHOIS))
+    return 0;
+
+  /* Send the WHOIS request to the router only if it included nickname.
+     Since nicknames can be expanded into many clients we need to send it
+     to router.  If the WHOIS included only client ID's we will check them
+     first locally since we just might have them. */
+  if (nick && !client_id_count && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending &&
+      !server->standalone) {
+    silc_server_command_whois_send_router(cmd);
+    ret = -1;
     goto out;
   }
 
-  /* Get the nickname@server string and parse it. */
-  tmp = silc_command_get_first_arg(cmd->payload, NULL);
-  if (tmp) {
-    if (strchr(tmp, '@')) {
-      len = strcspn(tmp, "@");
-      nick = silc_calloc(len + 1, sizeof(char));
-      memcpy(nick, tmp, len);
-      server = silc_calloc(strlen(tmp) - len, sizeof(char));
-      memcpy(server, tmp + len + 1, strlen(tmp) - len - 1);
-    } else {
-      nick = strdup(tmp);
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type != SILC_SERVER)
+    check_global = TRUE;
+
+  /* Get all clients matching that ID or nickname from local list */
+  if (client_id_count) {
+    /* Check all Client ID's received in the command packet */
+    for (i = 0; i < client_id_count; i++) {
+      entry = silc_idlist_find_client_by_id(server->local_list, 
+                                           client_id[i], TRUE, NULL);
+      if (!entry && check_global)
+       entry = silc_idlist_find_client_by_id(server->global_list, 
+                                             client_id[i], TRUE, NULL);
+      if (entry) {
+       clients = silc_realloc(clients, sizeof(*clients) * 
+                              (clients_count + 1));
+       clients[clients_count++] = entry;
+      } else {
+       /* If we are normal server and did not send the request first to router
+          do it now, since we do not have the Client ID information. */
+       if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+           server->server_type == SILC_SERVER && !cmd->pending && 
+           !server->standalone) {
+         silc_server_command_whois_send_router(cmd);
+         ret = -1;
+         goto out;
+       }
+      }
     }
   } else {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  /* Get the max count of reply messages allowed */
-  if (argc == 2) {
-    tmp = silc_command_get_next_arg(cmd->payload, NULL);
-    if (!tmp) {
-      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
-                                           SILC_STATUS_ERR_TOO_MANY_PARAMS);
-      goto out;
+    /* Find by nickname */
+    if (!silc_idlist_get_clients_by_hash(server->local_list, 
+                                        nick, server->md5hash,
+                                        &clients, &clients_count))
+      silc_idlist_get_clients_by_nickname(server->local_list, 
+                                         nick, server_name,
+                                         &clients, &clients_count);
+    if (check_global) {
+      if (!silc_idlist_get_clients_by_hash(server->global_list, 
+                                          nick, server->md5hash,
+                                          &clients, &clients_count))
+       silc_idlist_get_clients_by_nickname(server->global_list, 
+                                           nick, server_name,
+                                           &clients, &clients_count);
     }
-    count = atoi(tmp);
   }
-
-  /* Then, make the query from our local client list */
-  entry = silc_idlist_find_client_by_hash(cmd->server->local_list->clients,
-                                         nick, cmd->server->md5hash);
-  if (!entry) {
-
-    /* If we are normal server and are connected to a router we will
-       make global query from the router. */
-    if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone) {
-      SilcBuffer buffer = cmd->packet->buffer;
-
-      /* Forward the received IDENTIFY command to our router */
-      silc_buffer_push(buffer, buffer->data - buffer->head);
-      silc_server_packet_forward(cmd->server, (SilcSocketConnection)
-                                cmd->server->id_entry->router->connection,
-                                buffer->data, buffer->len,
-                                TRUE);
+  
+  if (!clients) {
+    /* If we are normal server and did not send the request first to router
+       do it now, since we do not have the information. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_whois_send_router(cmd);
+      ret = -1;
       goto out;
     }
-    
-    /* If we are router then we will check our global list as well. */
-    if (cmd->server->server_type == SILC_ROUTER) {
-      entry = 
-       silc_idlist_find_client_by_hash(cmd->server->global_list->clients,
-                                       nick, cmd->server->md5hash);
-      if (!entry) {
-       silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
-                                           SILC_STATUS_ERR_NO_SUCH_NICK,
-                                           tmp, strlen(tmp));
-       goto out;
-      }
-      goto ok;
-    }
 
-    silc_server_command_send_status_msg(cmd, SILC_COMMAND_IDENTIFY,
-                                       SILC_STATUS_ERR_NO_SUCH_NICK,
-                                       tmp, strlen(tmp));
+    /* Such client(s) really does not exist in the SILC network. */
+    if (!client_id_count) {
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, nick, strlen(nick));
+    } else {
+      SilcBuffer idp = silc_id_payload_encode(client_id[0], SILC_ID_CLIENT);
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
+                                          SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                          2, idp->data, idp->len);
+      silc_buffer_free(idp);
+    }
     goto out;
   }
 
- ok:
-  /* Send IDENTIFY reply */
-  id_string = silc_id_id2str(entry->id, SILC_ID_CLIENT);
-  tmp = silc_command_get_first_arg(cmd->payload, NULL);
-  sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
-  packet = silc_command_encode_payload_va(SILC_COMMAND_IDENTIFY, 3,
-                                         sp_buf->data, sp_buf->len,
-                                         id_string, SILC_ID_CLIENT_LEN,
-                                         nick, strlen(nick));
-  if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
-    void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
-    silc_server_packet_send_dest(cmd->server, cmd->sock, 
-                                SILC_PACKET_COMMAND_REPLY, 0,
-                                id, cmd->packet->src_id_type,
-                                packet->data, packet->len, FALSE);
-    silc_free(id);
-  } else
-    silc_server_packet_send(cmd->server, cmd->sock, 
-                           SILC_PACKET_COMMAND_REPLY, 0, 
-                           packet->data, packet->len, FALSE);
+  /* Router always finds the client entry if it exists in the SILC network.
+     However, it might be incomplete entry and does not include all the
+     mandatory fields that WHOIS command reply requires. Check for these and
+     make query from the server who owns the client if some fields are 
+     missing. */
+  if (!silc_server_command_whois_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
+  }
 
-  silc_free(id_string);
-  silc_buffer_free(packet);
-  silc_free(sp_buf);
+  /* Send the command reply */
+  silc_server_command_whois_send_reply(cmd, clients, clients_count,
+                                      count, nick, client_id);
 
  out:
-  if (nick)
-    silc_free(nick);
-  if (server)
-    silc_free(server);
-  silc_server_command_free(cmd);
+  if (client_id_count) {
+    for (i = 0; i < client_id_count; i++)
+      silc_free(client_id[i]);
+    silc_free(client_id);
+  }
+  silc_free(clients);
+  silc_free(nick);
+  silc_free(server_name);
+
+  return ret;
 }
 
-/* Checks string for bad characters and returns TRUE if they are found. */
+/* Server side of command WHOIS. Processes user's query and sends found 
+   results as command replies back to the client. */
 
-static int silc_server_command_bad_chars(char *nick)
+SILC_SERVER_CMD_FUNC(whois)
 {
-  if (strchr(nick, '\\')) return TRUE;
-  if (strchr(nick, '\"')) return TRUE;
-  if (strchr(nick, '´')) return TRUE;
-  if (strchr(nick, '`')) return TRUE;
-  if (strchr(nick, '\'')) return TRUE;
-  if (strchr(nick, '*')) return TRUE;
-  if (strchr(nick, '/')) return TRUE;
-  if (strchr(nick, '@')) return TRUE;
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret = 0;
 
-  return FALSE;
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 3328);
+
+  ret = silc_server_command_whois_process(cmd);
+  silc_server_command_free(cmd);
 }
 
-/* Server side of command NICK. Sets nickname for user. Setting
-   nickname causes generation of a new client ID for the client. The
-   new client ID is sent to the client after changing the nickname. */
+/******************************************************************************
 
-SILC_SERVER_CMD_FUNC(nick)
-{
-  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
-  SilcClientList *id_entry = (SilcClientList *)cmd->sock->user_data;
-  SilcServer server = cmd->server;
-  SilcBuffer packet, sp_buf;
-  SilcClientID *new_id;
-  char *id_string;
-  char *nick;
+                              WHOWAS Functions
 
-  SILC_LOG_DEBUG(("Start"));
+******************************************************************************/
 
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+static int
+silc_server_command_whowas_parse(SilcServerCommandContext cmd,
+                                char **nickname,
+                                char **server_name,
+                                int *count)
+{
+  unsigned char *tmp;
+  uint32 len;
 
-  /* Check number of arguments */
-  if (silc_command_get_arg_num(cmd->payload) < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_WHOWAS,
                                          SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
-    goto out;
-  }
-
-  /* Check nickname */
-  nick = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  if (silc_server_command_bad_chars(nick) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
-                                         SILC_STATUS_ERR_BAD_NICKNAME);
-    goto out;
+    return FALSE;
   }
 
-  /* Create new Client ID */
-  silc_id_create_client_id(cmd->server->id, cmd->server->rng, 
-                          cmd->server->md5hash, nick,
-                          &new_id);
+  /* Get the nickname@server string and parse it. */
+  silc_parse_userfqdn(tmp, nickname, server_name);
 
-  /* Send notify about nickname change to our router. We send the new
-     ID and ask to replace it with the old one. */
-  if (cmd->server->server_type == SILC_SERVER && !cmd->server->standalone)
-    silc_server_send_replace_id(server, server->id_entry->router->connection, 
-                               FALSE, id_entry->id,
-                               SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
-                               new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
-
-  /* If we are router we have to distribute the new Client ID to all 
-     routers in SILC. */
-  if (cmd->server->server_type == SILC_ROUTER && !cmd->server->standalone)
-    silc_server_send_replace_id(server, server->id_entry->router->connection,  
-                               TRUE, id_entry->id,
-                               SILC_ID_CLIENT, SILC_ID_CLIENT_LEN,
-                               new_id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
+  /* Get the max count of reply messages allowed */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (tmp)
+    *count = atoi(tmp);
+  else
+    *count = 0;
 
-  /* Remove old cache entry */
-  silc_idcache_del_by_id(LCC(id_entry->nickname[0]),
-                        LCCC(id_entry->nickname[0]), 
-                        SILC_ID_CLIENT, id_entry->id); 
-  
-  /* Free old ID */
-  if (id_entry->id) {
-    memset(id_entry->id, 0, SILC_ID_CLIENT_LEN);
-    silc_free(id_entry->id);
-  }
+  return TRUE;
+}
 
-  /* Save the nickname as this client is our local client */
-  if (id_entry->nickname)
-    silc_free(id_entry->nickname);
+static char
+silc_server_command_whowas_check(SilcServerCommandContext cmd,
+                                SilcClientEntry *clients,
+                                uint32 clients_count)
+{
+  SilcServer server = cmd->server;
+  int i;
+  SilcClientEntry entry;
 
-  id_entry->nickname = strdup(nick);
-  id_entry->id = new_id;
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
 
-  /* Update client cache */
-  LCCC(nick[0]) = silc_idcache_add(&LCC(nick[0]), LCCC(nick[0]),
-                                  id_entry->nickname, SILC_ID_CLIENT, 
-                                  id_entry->id, (void *)id_entry);
+    if (!entry->nickname || !entry->username) {
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
 
-  /* Send the new Client ID as reply command back to client */
-  id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
-  sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
-  packet = silc_command_encode_payload_va(SILC_COMMAND_NICK, 2, 
-                                         sp_buf->data, sp_buf->len,
-                                         id_string, SILC_ID_CLIENT_LEN);
-  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
-                         0, packet->data, packet->len, FALSE);
+      if (!entry->router)
+       continue;
+      
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      /* Send WHOWAS command */
+      silc_server_packet_send(server, entry->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+      
+      /* Reprocess this packet after received reply */
+      silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_whowas, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
 
-  silc_free(id_string);
-  silc_buffer_free(packet);
-  silc_free(sp_buf);
+      silc_buffer_free(tmpbuf);
+      return FALSE;
+    }
+  }
 
- out:
-  silc_server_command_free(cmd);
-#undef LCC
-#undef LCCC
+  return TRUE;
 }
 
+static void
+silc_server_command_whowas_send_reply(SilcServerCommandContext cmd,
+                                     SilcClientEntry *clients,
+                                     uint32 clients_count)
+{
+  SilcServer server = cmd->server;
+  char *tmp;
+  int i, k, count = 0, len;
+  SilcBuffer packet, idp;
+  SilcClientEntry entry = NULL;
+  SilcCommandStatus status;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char nh[256], uh[256];
+  int valid_count;
+
+  status = SILC_STATUS_OK;
+
+  /* Process only entries that are not registered anymore. */
+  valid_count = 0;
+  for (i = 0; i < clients_count; i++) {
+    if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+      clients[i] = NULL;
+    else
+      valid_count++;
+  }
+
+  if (!valid_count) {
+    /* No valid entries found at all, just send error */
+    unsigned char *tmp;
+    
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    if (tmp)
+      silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                          SILC_STATUS_ERR_NO_SUCH_NICK,
+                                          3, tmp, strlen(tmp));
+    return;
+  }
+
+  if (valid_count > 1)
+    status = SILC_STATUS_LIST_START;
+
+  for (i = 0, k = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
+
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+    if (valid_count > 1 && k == valid_count - 1)
+      status = SILC_STATUS_LIST_END;
+    if (count && k - 1 == count)
+      status = SILC_STATUS_LIST_END;
+    if (count && k - 1 > count)
+      break;
+
+    /* Send WHOWAS reply */
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+    tmp = silc_argument_get_first_arg(cmd->args, NULL);
+    memset(uh, 0, sizeof(uh));
+    memset(nh, 0, sizeof(nh));
+
+    strncat(nh, entry->nickname, strlen(entry->nickname));
+    if (!strchr(entry->nickname, '@')) {
+      strncat(nh, "@", 1);
+      if (entry->servername) {
+       strncat(nh, entry->servername, strlen(entry->servername));
+      } else {
+       len = entry->router ? strlen(entry->router->server_name) :
+         strlen(server->server_name);
+       strncat(nh, entry->router ? entry->router->server_name :
+               server->server_name, len);
+      }
+    }
+      
+    strncat(uh, entry->username, strlen(entry->username));
+    if (!strchr(entry->username, '@')) {
+      strncat(uh, "@", 1);
+      strcat(uh, "*private*");
+    }
+      
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_WHOWAS,
+                                          status, ident, 4, 
+                                          2, idp->data, idp->len,
+                                          3, nh, strlen(nh),
+                                          4, uh, strlen(uh),
+                                          5, entry->userinfo, 
+                                          entry->userinfo ? 
+                                          strlen(entry->userinfo) : 0);
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                           0, packet->data, packet->len, FALSE);
+    
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+
+    k++;
+  }
+}
+
+static int
+silc_server_command_whowas_process(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  char *nick = NULL, *server_name = NULL;
+  int count = 0;
+  SilcClientEntry *clients = NULL;
+  uint32 clients_count = 0;
+  int ret = 0;
+  bool check_global = FALSE;
+
+  /* Protocol dictates that we must always send the received WHOWAS request
+     to our router if we are normal server, so let's do it now unless we
+     are standalone. We will not send any replies to the client until we
+     have received reply from the router. */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+      server->server_type == SILC_SERVER && !cmd->pending && 
+      !server->standalone) {
+    SilcBuffer tmpbuf;
+    uint16 old_ident;
+
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+    /* Send WHOWAS command to our router */
+    silc_server_packet_send(server, (SilcSocketConnection)
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_WHOWAS, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_whowas,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_command_set_ident(cmd->payload, old_ident);
+
+    silc_buffer_free(tmpbuf);
+    ret = -1;
+    goto out;
+  }
+
+  /* We are ready to process the command request. Let's search for the
+     requested client and send reply to the requesting client. */
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type != SILC_SERVER)
+    check_global = TRUE;
+
+  /* Parse the whowas request */
+  if (!silc_server_command_whowas_parse(cmd, &nick, &server_name, &count))
+    return 0;
+
+  /* Get all clients matching that nickname from local list */
+  if (!silc_idlist_get_clients_by_nickname(server->local_list, 
+                                          nick, server_name,
+                                          &clients, &clients_count))
+    silc_idlist_get_clients_by_hash(server->local_list, 
+                                   nick, server->md5hash,
+                                   &clients, &clients_count);
+  
+  /* Check global list as well */
+  if (check_global) {
+    if (!silc_idlist_get_clients_by_nickname(server->global_list, 
+                                            nick, server_name,
+                                            &clients, &clients_count))
+      silc_idlist_get_clients_by_hash(server->global_list, 
+                                     nick, server->md5hash,
+                                     &clients, &clients_count);
+  }
+  
+  if (!clients) {
+    /* Such a client really does not exist in the SILC network. */
+    silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOWAS,
+                                        SILC_STATUS_ERR_NO_SUCH_NICK,
+                                        3, nick, strlen(nick));
+    goto out;
+  }
+
+  if (!silc_server_command_whowas_check(cmd, clients, clients_count)) {
+    ret = -1;
+    goto out;
+  }
+
+  /* Send the command reply to the client */
+  silc_server_command_whowas_send_reply(cmd, clients, clients_count);
+
+ out:
+  silc_free(clients);
+  silc_free(nick);
+  silc_free(server_name);
+  return ret;
+}
+
+/* Server side of command WHOWAS. */
+
+SILC_SERVER_CMD_FUNC(whowas)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret = 0;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2);
+
+  ret = silc_server_command_whowas_process(cmd);
+  silc_server_command_free(cmd);
+}
+
+/******************************************************************************
+
+                              IDENTIFY Functions
+
+******************************************************************************/
+
+static void 
+silc_server_command_identify_send_router(SilcServerCommandContext cmd)
+{
+  SilcServer server = cmd->server;
+  SilcBuffer tmpbuf;
+  uint16 old_ident;
+
+  old_ident = silc_command_get_ident(cmd->payload);
+  silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+  tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+  /* Send IDENTIFY command to our router */
+  silc_server_packet_send(server, (SilcSocketConnection)
+                         server->router->connection,
+                         SILC_PACKET_COMMAND, cmd->packet->flags,
+                         tmpbuf->data, tmpbuf->len, TRUE);
+
+  /* Reprocess this packet after received reply from router */
+  silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                             silc_command_get_ident(cmd->payload),
+                             silc_server_command_identify,
+                             silc_server_command_dup(cmd));
+  cmd->pending = TRUE;
+  silc_command_set_ident(cmd->payload, old_ident);
+  silc_buffer_free(tmpbuf);
+}
+
+static int
+silc_server_command_identify_parse(SilcServerCommandContext cmd,
+                                  SilcClientEntry **clients,
+                                  uint32 *clients_count,
+                                  SilcServerEntry **servers,
+                                  uint32 *servers_count,
+                                  SilcChannelEntry **channels,
+                                  uint32 *channels_count,
+                                  uint32 *count)
+{
+  SilcServer server = cmd->server;
+  unsigned char *tmp;
+  uint32 len;
+  uint32 argc = silc_argument_get_arg_num(cmd->args);
+  SilcIDPayload idp;
+  bool check_global = FALSE;
+  void *entry;
+  int i;
+  bool error = FALSE;
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
+    check_global = TRUE;
+  else if (server->server_type != SILC_SERVER)
+    check_global = TRUE;
+
+  /* If ID Payload is in the command it must be used instead of names */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
+  if (!tmp) {
+    /* No ID, get the names. */
+
+    /* If we are normal server and have not resolved information from
+       router yet, do so now. */
+    if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+       server->server_type == SILC_SERVER && !cmd->pending && 
+       !server->standalone) {
+      silc_server_command_identify_send_router(cmd);
+      return -1;
+    }
+
+    /* Try to get nickname@server. */
+    tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+    if (tmp) {
+      char *nick = NULL;
+      char *nick_server = NULL;
+
+      silc_parse_userfqdn(tmp, &nick, &nick_server);
+
+      if (!silc_idlist_get_clients_by_hash(server->local_list, 
+                                          nick, server->md5hash,
+                                          clients, clients_count))
+       silc_idlist_get_clients_by_nickname(server->local_list, 
+                                           nick, nick_server,
+                                           clients, clients_count);
+      if (check_global) {
+       if (!silc_idlist_get_clients_by_hash(server->global_list, 
+                                            nick, server->md5hash,
+                                            clients, clients_count))
+         silc_idlist_get_clients_by_nickname(server->global_list, 
+                                             nick, nick_server,
+                                             clients, clients_count);
+      }
+
+      silc_free(nick);
+      silc_free(nick_server);
+
+      if (!(*clients)) {
+       /* the nickname does not exist, send error reply */
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, tmp, strlen(tmp));
+       return 0;
+      }
+    }
+
+    /* Try to get server name */
+    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+    if (tmp) {
+      entry = silc_idlist_find_server_by_name(server->local_list,
+                                             tmp, TRUE, NULL);
+      if (!entry && check_global)
+       entry = silc_idlist_find_server_by_name(server->global_list,
+                                               tmp, TRUE, NULL);
+      if (entry) {
+       *servers = silc_realloc(*servers, sizeof(**servers) * 
+                               (*servers_count + 1));
+       (*servers)[(*servers_count)++] = entry;
+      }
+
+      if (!(*servers)) {
+       /* the server does not exist, send error reply */
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_SERVER,
+                                            3, tmp, strlen(tmp));
+       return 0;
+      }
+    }
+
+    /* Try to get channel name */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+    if (tmp) {
+      entry = silc_idlist_find_channel_by_name(server->local_list,
+                                              tmp, NULL);
+      if (!entry && check_global)
+       entry = silc_idlist_find_channel_by_name(server->global_list,
+                                                tmp, NULL);
+      if (entry) {
+       *channels = silc_realloc(*channels, sizeof(**channels) * 
+                                (*channels_count + 1));
+       (*channels)[(*channels_count)++] = entry;
+      }
+
+      if (!(*channels)) {
+       /* The channel does not exist, send error reply */
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_CHANNEL,
+                                            3, tmp, strlen(tmp));
+       return 0;
+      }
+    }
+
+    if (!(*clients) && !(*servers) && !(*channels)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_IDENTIFY,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      return 0;
+    }
+  } else {
+    /* Command includes ID, we must use that.  Also check whether the command
+       has more than one ID set - take them all. */
+
+    /* Take all ID's from the command packet */
+    for (i = 0; i < argc; i++) {
+      void *id;
+
+      tmp = silc_argument_get_arg_type(cmd->args, i + 5, &len);
+      if (!tmp)
+       continue;
+      
+      idp = silc_id_payload_parse(tmp, len);
+      if (!idp) {
+       silc_free(*clients);
+       silc_free(*servers);
+       silc_free(*channels);
+       silc_server_command_send_status_reply(
+                                      cmd, SILC_COMMAND_IDENTIFY,
+                                      SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       return 0;
+      }
+
+      id = silc_id_payload_get_id(idp);
+      
+      switch (silc_id_payload_get_type(idp)) {
+       
+      case SILC_ID_CLIENT:
+       entry = (void *)silc_idlist_find_client_by_id(server->local_list, 
+                                                     id, TRUE, NULL);
+       if (!entry && check_global)
+         entry = (void *)silc_idlist_find_client_by_id(server->global_list, 
+                                                       id, TRUE, NULL);
+       if (entry) {
+         *clients = silc_realloc(*clients, sizeof(**clients) * 
+                                 (*clients_count + 1));
+         (*clients)[(*clients_count)++] = (SilcClientEntry)entry;
+       } else {
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                       cmd, SILC_COMMAND_IDENTIFY,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                       2, tmp, len);
+           error = TRUE;
+         }
+       }
+
+       break;
+       
+      case SILC_ID_SERVER:
+       entry = (void *)silc_idlist_find_server_by_id(server->local_list, 
+                                                     id, TRUE, NULL);
+       if (!entry && check_global)
+         entry = (void *)silc_idlist_find_server_by_id(server->global_list, 
+                                                       id, TRUE, NULL);
+       if (entry) {
+         *servers = silc_realloc(*servers, sizeof(**servers) * 
+                                 (*servers_count + 1));
+         (*servers)[(*servers_count)++] = (SilcServerEntry)entry;
+       } else {
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_SERVER_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
+       }
+       break;
+       
+      case SILC_ID_CHANNEL:
+       entry = (void *)silc_idlist_find_channel_by_id(server->local_list, 
+                                                      id, NULL);
+       if (!entry && check_global)
+         entry = (void *)silc_idlist_find_channel_by_id(server->global_list, 
+                                                        id, NULL);
+       if (entry) {
+         *channels = silc_realloc(*channels, sizeof(**channels) * 
+                                  (*channels_count + 1));
+         (*channels)[(*channels_count)++] = (SilcChannelEntry)entry;
+       } else {
+         /* If we are normal server and have not resolved information from
+            router yet, do so now. */
+         if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT && 
+             server->server_type == SILC_SERVER && !cmd->pending && 
+             !server->standalone) {
+           silc_server_command_identify_send_router(cmd);
+           silc_free(*clients);
+           silc_free(*servers);
+           silc_free(*channels);
+           return -1;
+         } else {
+           silc_server_command_send_status_data(
+                                        cmd, SILC_COMMAND_IDENTIFY,
+                                        SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID,
+                                        2, tmp, len);
+           error = TRUE;
+         }
+       }
+       break;
+      }
+
+      silc_free(id);
+    }
+  }
+
+  if (error) {
+    silc_free(*clients);
+    silc_free(*servers);
+    silc_free(*channels);
+    return FALSE;
+  }
+  
+  /* Get the max count of reply messages allowed */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (tmp)
+    *count = atoi(tmp);
+  else
+    *count = 0;
+
+  return 1;
+}
+
+/* Checks that all mandatory fields in client entry are present. If not
+   then send WHOIS request to the server who owns the client. We use
+   WHOIS because we want to get as much information as possible at once. */
+
+static bool
+silc_server_command_identify_check_client(SilcServerCommandContext cmd,
+                                         SilcClientEntry *clients,
+                                         uint32 clients_count)
+{
+  SilcServer server = cmd->server;
+  SilcClientEntry entry;
+  SilcServerResolveContext resolve = NULL, r = NULL;
+  uint32 resolve_count = 0;
+  int i, k;
+  bool no_res = TRUE;
+
+  for (i = 0; i < clients_count; i++) {
+    entry = clients[i];
+    if (!entry)
+      continue;
+
+    if (entry->nickname || 
+       !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      if (!entry->router)
+       continue;
+
+      /* If we are normal server, and we've not resolved this client from
+        router and it is global client, we'll check whether it is on some
+        channel.  If not then we cannot be sure about its validity, and
+        we'll resolve it from router. */
+      if (cmd->server->server_type != SILC_SERVER || cmd->pending ||
+         entry->connection || silc_hash_table_count(entry->channels))
+       continue;
+    }
+
+    /* We need to resolve this entry since it is not complete */
+
+    if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+      /* The entry is being resolved (and we are not the resolver) so attach
+        to the command reply and we're done with this one. */
+      silc_server_command_pending(server, SILC_COMMAND_NONE, 
+                                 entry->resolve_cmd_ident,
+                                 silc_server_command_identify,
+                                 silc_server_command_dup(cmd));
+      no_res = FALSE;
+    } else {
+      if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+       /* We've resolved this and it still is not ready.  We'll return
+          and are that this will be handled again after it is resolved. */
+       for (i = 0; i < resolve_count; i++) {
+         for (k = 0; k < r->res_argc; k++)
+           silc_free(r->res_argv[k]);
+         silc_free(r->res_argv);
+         silc_free(r->res_argv_lens);
+         silc_free(r->res_argv_types);
+       }
+       silc_free(resolve);
+       return FALSE;
+      } else {
+       /* We'll resolve this client */
+       SilcBuffer idp;
+
+       r = NULL;
+       for (k = 0; k < resolve_count; k++) {
+         if (resolve[k].router == entry->router) {
+           r = &resolve[k];
+           break;
+         }
+       }
+
+       if (!r) {
+         resolve = silc_realloc(resolve, sizeof(*resolve) * 
+                                (resolve_count + 1));
+         r = &resolve[resolve_count];
+         memset(r, 0, sizeof(*r));
+         r->router = entry->router;
+         r->ident = ++server->cmd_ident;
+         resolve_count++;
+       }
+
+       r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
+                                  (r->res_argc + 1));
+       r->res_argv_lens = silc_realloc(r->res_argv_lens, 
+                                       sizeof(*r->res_argv_lens) *
+                                       (r->res_argc + 1));
+       r->res_argv_types = silc_realloc(r->res_argv_types, 
+                                        sizeof(*r->res_argv_types) *
+                                        (r->res_argc + 1));
+       idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+       r->res_argv[r->res_argc] = silc_calloc(idp->len, 
+                                              sizeof(**r->res_argv));
+       memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
+       r->res_argv_lens[r->res_argc] = idp->len;
+       r->res_argv_types[r->res_argc] = r->res_argc + 3;
+       r->res_argc++;
+       silc_buffer_free(idp);
+
+       entry->resolve_cmd_ident = r->ident;
+       entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+       entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+      }
+    }
+  }
+
+  /* Do the resolving */
+  for (i = 0; i < resolve_count; i++) {
+    SilcBuffer res_cmd;
+
+    r = &resolve[i];
+
+    /* Send WHOIS request. We send WHOIS since we're doing the requesting
+       now anyway so make it a good one. */
+    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+                                         r->res_argc, r->res_argv, 
+                                         r->res_argv_lens,
+                                         r->res_argv_types, 
+                                         r->ident);
+    silc_server_packet_send(server, r->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           res_cmd->data, res_cmd->len, FALSE);
+
+    /* Reprocess this packet after received reply */
+    silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                               r->ident,
+                               silc_server_command_identify,
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+
+    silc_buffer_free(res_cmd);
+    for (k = 0; k < r->res_argc; k++)
+      silc_free(r->res_argv[k]);
+    silc_free(r->res_argv);
+    silc_free(r->res_argv_lens);
+    silc_free(r->res_argv_types);
+    no_res = FALSE;
+  }
+  silc_free(resolve);
+
+  return no_res;
+}
+
+static void
+silc_server_command_identify_send_reply(SilcServerCommandContext cmd,
+                                       SilcClientEntry *clients,
+                                       uint32 clients_count,
+                                       SilcServerEntry *servers,
+                                       uint32 servers_count,
+                                       SilcChannelEntry *channels,
+                                       uint32 channels_count,
+                                       int count)
+{
+  SilcServer server = cmd->server;
+  int i, k, len, valid_count;
+  SilcBuffer packet, idp;
+  SilcCommandStatus status;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char nh[256], uh[256];
+  SilcSocketConnection hsock;
+
+  status = SILC_STATUS_OK;
+
+  if (clients) {
+    SilcClientEntry entry;
+
+    /* Process only valid entries. */
+    valid_count = 0;
+    for (i = 0; i < clients_count; i++) {
+      if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
+       valid_count++;
+      else
+       clients[i] = NULL;
+    }
+
+    if (!valid_count) {
+      /* No valid entries found at all, just send error */
+      unsigned char *tmp;
+
+      tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+      if (tmp) {
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_NICK,
+                                            3, tmp, strlen(tmp));
+      } else {
+       tmp = silc_argument_get_arg_type(cmd->args, 5, (uint32 *)&len);
+       silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
+                                            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID,
+                                            2, tmp, len);
+      }
+      return;
+    }
+
+    /* Process all valid client entries and send command replies */
+
+    if (valid_count > 1)
+      status = SILC_STATUS_LIST_START;
+
+    for (i = 0, k = 0; i < clients_count; i++) {
+      entry = clients[i];
+      if (!entry)
+       continue;
+
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (valid_count > 1 && k == valid_count - 1 
+         && !servers_count && !channels_count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+
+      /* Send IDENTIFY reply */
+
+      idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+      memset(uh, 0, sizeof(uh));
+      memset(nh, 0, sizeof(nh));
+      strncat(nh, entry->nickname, strlen(entry->nickname));
+      if (!strchr(entry->nickname, '@')) {
+       strncat(nh, "@", 1);
+       if (entry->servername) {
+         strncat(nh, entry->servername, strlen(entry->servername));
+       } else {
+         len = entry->router ? strlen(entry->router->server_name) :
+           strlen(server->server_name);
+         strncat(nh, entry->router ? entry->router->server_name :
+                 server->server_name, len);
+       }
+      }
+
+      if (!entry->username) {
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 2,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh));
+      } else {
+       strncat(uh, entry->username, strlen(entry->username));
+       if (!strchr(entry->username, '@')) {
+         strncat(uh, "@", 1);
+         hsock = (SilcSocketConnection)entry->connection;
+         len = strlen(hsock->hostname);
+         strncat(uh, hsock->hostname, len);
+       }
+       
+       packet = silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                                     status, ident, 3,
+                                                     2, idp->data, idp->len, 
+                                                     3, nh, strlen(nh),
+                                                     4, uh, strlen(uh));
+      }
+      
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+      
+      k++;
+    }
+  }
+
+  if (servers) {
+    SilcServerEntry entry;
+
+    if (status == SILC_STATUS_OK && servers_count > 1)
+      status = SILC_STATUS_LIST_START;
+
+    for (i = 0, k = 0; i < servers_count; i++) {
+      entry = servers[i];
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (servers_count > 1 && k == servers_count - 1 && !channels_count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                            status, ident, 2,
+                                            2, idp->data, idp->len, 
+                                            3, entry->server_name, 
+                                            entry->server_name ? 
+                                            strlen(entry->server_name) : 0);
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+      
+      k++;
+    }
+  }
+
+  if (channels) {
+    SilcChannelEntry entry;
+
+    if (status == SILC_STATUS_OK && channels_count > 1)
+      status = SILC_STATUS_LIST_START;
+
+    for (i = 0, k = 0; i < channels_count; i++) {
+      entry = channels[i];
+      
+      if (k >= 1)
+       status = SILC_STATUS_LIST_ITEM;
+      if (channels_count > 1 && k == channels_count - 1)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 == count)
+       status = SILC_STATUS_LIST_END;
+      if (count && k - 1 > count)
+       break;
+      
+      /* Send IDENTIFY reply */
+      idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+      packet = 
+       silc_command_reply_payload_encode_va(SILC_COMMAND_IDENTIFY,
+                                            status, ident, 2,
+                                            2, idp->data, idp->len, 
+                                            3, entry->channel_name, 
+                                            entry->channel_name ? 
+                                            strlen(entry->channel_name): 0);
+      silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                             0, packet->data, packet->len, FALSE);
+      
+      silc_buffer_free(packet);
+      silc_buffer_free(idp);
+      
+      k++;
+    }
+  }
+}
+
+static int
+silc_server_command_identify_process(SilcServerCommandContext cmd)
+{
+  uint32 count = 0;
+  int ret = 0;
+  SilcClientEntry *clients = NULL;
+  SilcServerEntry *servers = NULL;
+  SilcChannelEntry *channels = NULL;
+  uint32 clients_count = 0, servers_count = 0, channels_count = 0;
+
+  /* Parse the IDENTIFY request */
+  ret = silc_server_command_identify_parse(cmd,
+                                          &clients, &clients_count,
+                                          &servers, &servers_count,
+                                          &channels, &channels_count,
+                                          &count);
+  if (ret < 1)
+    return ret;
+  ret = 0;
+
+  /* Check that all mandatory fields are present and request those data
+     from the server who owns the client if necessary. */
+  if (clients && !silc_server_command_identify_check_client(cmd, clients, 
+                                                           clients_count)) {
+    ret = -1;
+    goto out;
+  }
+
+  /* Send the command reply to the client */
+  silc_server_command_identify_send_reply(cmd, 
+                                         clients, clients_count,
+                                         servers, servers_count,
+                                         channels, channels_count, 
+                                         count);
+
+ out:
+  silc_free(clients);
+  silc_free(servers);
+  silc_free(channels);
+  return ret;
+}
+
+SILC_SERVER_CMD_FUNC(identify)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  int ret = 0;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 3328);
+
+  ret = silc_server_command_identify_process(cmd);
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command NICK. Sets nickname for user. Setting
+   nickname causes generation of a new client ID for the client. The
+   new client ID is sent to the client after changing the nickname. */
+
+SILC_SERVER_CMD_FUNC(nick)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcServer server = cmd->server;
+  SilcBuffer packet, nidp, oidp = NULL;
+  SilcClientID *new_id;
+  uint32 nick_len;
+  char *nick;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  int nickfail = 0;
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_NICK, cmd, 1, 1);
+
+  /* Check nickname */
+  nick = silc_argument_get_arg_type(cmd->args, 1, &nick_len);
+  if (nick_len > 128)
+    nick[128] = '\0';
+  if (silc_server_name_bad_chars(nick, nick_len) == TRUE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_NICK,
+                                         SILC_STATUS_ERR_BAD_NICKNAME);
+    goto out;
+  }
+
+  /* Check for same nickname */
+  if (!strcmp(client->nickname, nick)) {
+    nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+    goto send_reply;
+  }
+
+  /* Create new Client ID */
+  while (!silc_id_create_client_id(cmd->server, cmd->server->id, 
+                                  cmd->server->rng, 
+                                  cmd->server->md5hash, nick,
+                                  &new_id)) {
+    nickfail++;
+    snprintf(&nick[strlen(nick) - 1], 1, "%d", nickfail);
+  }
+
+  /* Send notify about nickname change to our router. We send the new
+     ID and ask to replace it with the old one. If we are router the
+     packet is broadcasted. Send NICK_CHANGE notify. */
+  if (!server->standalone)
+    silc_server_send_notify_nick_change(server, server->router->connection, 
+                                       server->server_type == SILC_SERVER ? 
+                                       FALSE : TRUE, client->id,
+                                       new_id);
+
+  oidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Remove old cache entry */
+  silc_idcache_del_by_context(server->local_list->clients, client);
+
+  /* Free old ID */
+  silc_free(client->id);
+
+  /* Save the nickname as this client is our local client */
+  silc_free(client->nickname);
+
+  client->nickname = strdup(nick);
+  client->id = new_id;
+
+  /* Update client cache */
+  silc_idcache_add(server->local_list->clients, client->nickname, 
+                  client->id, (void *)client, 0, NULL);
+
+  nidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Send NICK_CHANGE notify to the client's channels */
+  silc_server_send_notify_on_channels(server, NULL, client, 
+                                     SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
+                                     oidp->data, oidp->len, 
+                                     nidp->data, nidp->len);
+
+ send_reply:
+  /* Send the new Client ID as reply command back to client */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_NICK, 
+                                               SILC_STATUS_OK, ident, 1, 
+                                               2, nidp->data, nidp->len);
+  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                         0, packet->data, packet->len, FALSE);
+
+  silc_buffer_free(packet);
+  silc_buffer_free(nidp);
+  if (oidp)
+    silc_buffer_free(oidp);
+  
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Sends the LIST command reply */
+
+static void
+silc_server_command_list_send_reply(SilcServerCommandContext cmd,
+                                   SilcChannelEntry *lch, 
+                                   uint32 lch_count,
+                                   SilcChannelEntry *gch,
+                                   uint32 gch_count)
+{
+  int i, k;
+  SilcBuffer packet, idp;
+  SilcChannelEntry entry;
+  SilcCommandStatus status;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char *topic;
+  unsigned char usercount[4];
+  uint32 users;
+  int valid_lcount = 0, valid_rcount = 0;
+
+  for (i = 0; i < lch_count; i++) {
+    if (lch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+      lch[i] = NULL;
+    else
+      valid_lcount++;
+  }
+  for (i = 0; i < gch_count; i++) {
+    if (gch[i]->mode & SILC_CHANNEL_MODE_SECRET)
+      gch[i] = NULL;
+    else
+      valid_rcount++;
+  }
+
+  status = SILC_STATUS_OK;
+  if ((lch_count + gch_count) > 1)
+    status = SILC_STATUS_LIST_START;
+
+  /* Local list */
+  for (i = 0, k = 0; i < lch_count; i++) {
+    entry = lch[i];
+    if (!entry)
+      continue;
+
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+    if (valid_lcount > 1 && k == valid_lcount - 1 && !valid_rcount)
+      status = SILC_STATUS_LIST_END;
+
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+
+    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
+      topic = "*private*";
+      memset(usercount, 0, sizeof(usercount));
+    } else {
+      topic = entry->topic;
+      users = silc_hash_table_count(entry->user_list);
+      SILC_PUT32_MSB(users, usercount);
+    }
+
+    /* Send the reply */
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                          status, ident, 4,
+                                          2, idp->data, idp->len,
+                                          3, entry->channel_name, 
+                                          strlen(entry->channel_name),
+                                          4, topic, topic ? strlen(topic) : 0,
+                                          5, usercount, 4);
+    silc_server_packet_send(cmd->server, cmd->sock, 
+                           SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
+                           packet->len, FALSE);
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+    k++;
+  }
+
+  /* Global list */
+  for (i = 0, k = 0; i < gch_count; i++) {
+    entry = gch[i];
+    if (!entry)
+      continue;
+
+    if (k >= 1)
+      status = SILC_STATUS_LIST_ITEM;
+    if (valid_rcount > 1 && k == valid_rcount - 1)
+      status = SILC_STATUS_LIST_END;
+
+    idp = silc_id_payload_encode(entry->id, SILC_ID_CHANNEL);
+
+    if (entry->mode & SILC_CHANNEL_MODE_PRIVATE) {
+      topic = "*private*";
+      memset(usercount, 0, sizeof(usercount));
+    } else {
+      topic = entry->topic;
+      users = silc_hash_table_count(entry->user_list);
+      SILC_PUT32_MSB(users, usercount);
+    }
+
+    /* Send the reply */
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_LIST, 
+                                          status, ident, 4,
+                                          2, idp->data, idp->len,
+                                          3, entry->channel_name, 
+                                          strlen(entry->channel_name),
+                                          4, topic, topic ? strlen(topic) : 0,
+                                          5, usercount, 4);
+    silc_server_packet_send(cmd->server, cmd->sock, 
+                           SILC_PACKET_COMMAND_REPLY, 0, packet->data, 
+                           packet->len, FALSE);
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+    k++;
+  }
+}
+
+/* Server side of LIST command. This lists the channel of the requested
+   server. Secret channels are not listed. */
+
 SILC_SERVER_CMD_FUNC(list)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcChannelID *channel_id = NULL;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
+  uint32 lch_count = 0, gch_count = 0;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
+
+  /* If we are normal server, send the command to router, since we
+     want to know all channels in the network. */
+  if (!cmd->pending && server->server_type == SILC_SERVER && 
+      !server->standalone) {
+    SilcBuffer tmpbuf;
+    uint16 old_ident;
+    
+    old_ident = silc_command_get_ident(cmd->payload);
+    silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+    tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_COMMAND, cmd->packet->flags,
+                           tmpbuf->data, tmpbuf->len, TRUE);
+
+    /* Reprocess this packet after received reply from router */
+    silc_server_command_pending(server, SILC_COMMAND_LIST, 
+                               silc_command_get_ident(cmd->payload),
+                               silc_server_command_list, 
+                               silc_server_command_dup(cmd));
+    cmd->pending = TRUE;
+    silc_command_set_ident(cmd->payload, old_ident);
+    silc_buffer_free(tmpbuf);
+    goto out;
+  }
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (tmp) {
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+      goto out;
+    }
+  }
+
+  /* Get the channels from local list */
+  lchannels = silc_idlist_get_channels(server->local_list, channel_id,
+                                      &lch_count);
+  
+  /* Get the channels from global list */
+  gchannels = silc_idlist_get_channels(server->global_list, channel_id,
+                                      &gch_count);
+
+  /* Send the reply */
+  silc_server_command_list_send_reply(cmd, lchannels, lch_count, 
+                                     gchannels, gch_count);
+
+  silc_free(lchannels);
+  silc_free(gchannels);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of TOPIC command. Sets topic for channel and/or returns
+   current topic to client. */
+
+SILC_SERVER_CMD_FUNC(topic)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcChannelID *channel_id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer packet, idp;
+  unsigned char *tmp;
+  uint32 argc, tmp_len;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
+
+  argc = silc_argument_get_arg_num(cmd->args);
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+
+  /* Check whether the channel exists */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  if (argc > 1) {
+    /* Get the topic */
+    tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+    if (!tmp) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
+    }
+
+    if (strlen(tmp) > 256) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+      goto out;
+    }
+
+    /* See whether the client is on channel and has rights to change topic */
+    if (!silc_hash_table_find(channel->user_list, client, NULL, 
+                             (void *)&chl)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                           SILC_STATUS_ERR_NOT_ON_CHANNEL);
+      goto out;
+    }
+
+    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+      if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+       goto out;
+      }
+    }
+
+    /* Set the topic for channel */
+    silc_free(channel->topic);
+    channel->topic = strdup(tmp);
+
+    /* Send TOPIC_SET notify type to the network */
+    if (!server->standalone)
+      silc_server_send_notify_topic_set(server, server->router->connection,
+                                       server->server_type == SILC_ROUTER ?
+                                       TRUE : FALSE, channel, 
+                                       client->id, SILC_ID_CLIENT,
+                                       channel->topic);
+
+    idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+    /* Send notify about topic change to all clients on the channel */
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                      SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                      idp->data, idp->len,
+                                      channel->topic, strlen(channel->topic));
+    silc_buffer_free(idp);
+  }
+
+  /* Send the topic to client as reply packet */
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                               SILC_STATUS_OK, ident, 2, 
+                                               2, idp->data, idp->len,
+                                               3, channel->topic, 
+                                               channel->topic ? 
+                                               strlen(channel->topic) : 0);
+  silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+                         0, packet->data, packet->len, FALSE);
+
+  silc_buffer_free(packet);
+  silc_buffer_free(idp);
+  silc_free(channel_id);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of INVITE command. Invites some client to join some channel. 
+   This command is also used to manage the invite list of the channel. */
+
+SILC_SERVER_CMD_FUNC(invite)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcSocketConnection sock = cmd->sock, dest_sock;
+  SilcChannelClientEntry chl;
+  SilcClientEntry sender, dest;
+  SilcClientID *dest_id = NULL;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id = NULL;
+  SilcIDListData idata;
+  SilcBuffer idp, idp2, packet;
+  unsigned char *tmp, *add, *del;
+  uint32 len;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+
+  /* Get the channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* Check whether the sender of this command is on the channel. */
+  sender = (SilcClientEntry)sock->user_data;
+  if (!silc_server_client_on_channel(sender, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Check whether the channel is invite-only channel. If yes then the
+     sender of this command must be at least channel operator. */
+  if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+    silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
+    if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+      goto out;
+    }
+  }
+
+  /* Get destination client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (tmp) {
+    char invite[512];
+    bool resolve;
+
+    dest_id = silc_id_payload_parse_id(tmp, len);
+    if (!dest_id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_CLIENT_ID);
+      goto out;
+    }
+
+    /* Get the client entry */
+    dest = silc_server_get_client_resolve(server, dest_id, &resolve);
+    if (!dest) {
+      if (server->server_type != SILC_SERVER || !resolve) {
+       silc_server_command_send_status_reply(
+                                       cmd, SILC_COMMAND_INVITE,
+                                       SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+       goto out;
+      }
+      
+      /* The client info is being resolved. Reprocess this packet after
+        receiving the reply to the query. */
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                                 server->cmd_ident,
+                                 silc_server_command_invite, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_free(channel_id);
+      silc_free(dest_id);
+      goto out;
+    }
+
+    /* Check whether the requested client is already on the channel. */
+    if (silc_server_client_on_channel(dest, channel)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_USER_ON_CHANNEL);
+      goto out;
+    }
+    
+    /* Get route to the client */
+    dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, &idata);
+    if (!dest_sock) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
+    }
+
+    memset(invite, 0, sizeof(invite));
+    strncat(invite, dest->nickname, strlen(dest->nickname));
+    strncat(invite, "!", 1);
+    strncat(invite, dest->username, strlen(dest->username));
+    if (!strchr(dest->username, '@')) {
+      strncat(invite, "@", 1);
+      strncat(invite, cmd->sock->hostname, strlen(cmd->sock->hostname));
+    }
+
+    len = strlen(invite);
+    if (!channel->invite_list)
+      channel->invite_list = silc_calloc(len + 2, 
+                                        sizeof(*channel->invite_list));
+    else
+      channel->invite_list = silc_realloc(channel->invite_list, 
+                                         sizeof(*channel->invite_list) * 
+                                         (len + 
+                                          strlen(channel->invite_list) + 2));
+    strncat(channel->invite_list, invite, len);
+    strncat(channel->invite_list, ",", 1);
+
+    /* Send notify to the client that is invited to the channel */
+    idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+    idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+    silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id, 
+                                SILC_ID_CLIENT,
+                                SILC_NOTIFY_TYPE_INVITE, 3, 
+                                idp->data, idp->len, 
+                                channel->channel_name, 
+                                strlen(channel->channel_name),
+                                idp2->data, idp2->len);
+    silc_buffer_free(idp);
+    silc_buffer_free(idp2);
+  }
+
+  /* Add the client to the invite list of the channel */
+  add = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (add) {
+    if (!channel->invite_list)
+      channel->invite_list = silc_calloc(len + 2, 
+                                        sizeof(*channel->invite_list));
+    else
+      channel->invite_list = silc_realloc(channel->invite_list, 
+                                         sizeof(*channel->invite_list) * 
+                                         (len + 
+                                          strlen(channel->invite_list) + 2));
+    if (add[len - 1] == ',')
+      add[len - 1] = '\0';
+    
+    strncat(channel->invite_list, add, len);
+    strncat(channel->invite_list, ",", 1);
+  }
+
+  /* Get the invite to be removed and remove it from the list */
+  del = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (del && channel->invite_list) {
+    char *start, *end, *n;
+
+    if (!strncmp(channel->invite_list, del, 
+                strlen(channel->invite_list) - 1)) {
+      silc_free(channel->invite_list);
+      channel->invite_list = NULL;
+    } else {
+      start = strstr(channel->invite_list, del);
+      if (start && strlen(start) >= len) {
+       end = start + len;
+       n = silc_calloc(strlen(channel->invite_list) - len, sizeof(*n));
+       strncat(n, channel->invite_list, start - channel->invite_list);
+       strncat(n, end + 1, ((channel->invite_list + 
+                             strlen(channel->invite_list)) - end) - 1);
+       silc_free(channel->invite_list);
+       channel->invite_list = n;
+      }
+    }
+  }
+
+  /* Send notify to the primary router */
+  if (!server->standalone)
+    silc_server_send_notify_invite(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE, channel,
+                                  sender->id, add, del);
+
+  /* Send command reply */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+
+  if (add || del)
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+                                          SILC_STATUS_OK, ident, 2,
+                                          2, tmp, len,
+                                          3, channel->invite_list,
+                                          channel->invite_list ?
+                                          strlen(channel->invite_list) : 0);
+  else
+    packet = 
+      silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+                                          SILC_STATUS_OK, ident, 1,
+                                          2, tmp, len);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+ out:
+  silc_free(dest_id);
+  silc_free(channel_id);
+  silc_server_command_free(cmd);
+}
+
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  char *signoff;
+} *QuitInternal;
+
+/* Quits connection to client. This gets called if client won't
+   close the connection even when it has issued QUIT command. */
+
+SILC_TASK_CALLBACK(silc_server_command_quit_cb)
+{
+  QuitInternal q = (QuitInternal)context;
+
+  /* Free all client specific data, such as client entry and entires
+     on channels this client may be on. */
+  silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
+                              TRUE, q->signoff);
+  q->sock->user_data = NULL;
+
+  /* Close the connection on our side */
+  silc_server_close_connection(q->server, q->sock);
+
+  silc_free(q->signoff);
+  silc_free(q);
+}
+
+/* Quits SILC session. This is the normal way to disconnect client. */
+SILC_SERVER_CMD_FUNC(quit)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcSocketConnection sock = cmd->sock;
+  QuitInternal q;
+  unsigned char *tmp = NULL;
+  uint32 len = 0;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get destination ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (len > 128)
+    tmp = NULL;
+
+  q = silc_calloc(1, sizeof(*q));
+  q->server = server;
+  q->sock = sock;
+  q->signoff = tmp ? strdup(tmp) : NULL;
+
+  /* We quit the connection with little timeout */
+  silc_schedule_task_add(server->schedule, sock->sock,
+                    silc_server_command_quit_cb, (void *)q,
+                    0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command KILL. This command is used by router operator
+   to remove an client from the SILC Network temporarily. */
+
+SILC_SERVER_CMD_FUNC(kill)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry remote_client;
+  SilcClientID *client_id;
+  unsigned char *tmp, *comment;
+  uint32 tmp_len, tmp_len2;
+  bool local;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* KILL command works only on router */
+  if (server->server_type != SILC_ROUTER) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Check whether client has the permissions. */
+  if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+    goto out;
+  }
+
+  /* Get the client entry */
+  remote_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, TRUE, NULL);
+  local = TRUE;
+  if (!remote_client) {
+    remote_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, TRUE, NULL);
+    local = FALSE;
+    if (!remote_client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
+    }
+  }
+
+  /* Get comment */
+  comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
+  if (tmp_len2 > 128)
+    comment = NULL;
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+                                       SILC_STATUS_OK);
+
+  /* Send the KILL notify packets. First send it to the channel, then
+     to our primary router and then directly to the client who is being
+     killed right now. */
+
+  /* Send KILLED notify to the channels. It is not sent to the client
+     as it will be sent differently destined directly to the client and not
+     to the channel. */
+  silc_server_send_notify_on_channels(server, remote_client, 
+                                     remote_client, SILC_NOTIFY_TYPE_KILLED,
+                                     comment ? 2 : 1,
+                                     tmp, tmp_len,
+                                     comment, comment ? tmp_len2 : 0);
+
+  /* Send KILLED notify to primary route */
+  if (!server->standalone)
+    silc_server_send_notify_killed(server, server->router->connection, TRUE,
+                                  remote_client->id, comment);
+
+  /* Send KILLED notify to the client directly */
+  silc_server_send_notify_killed(server, remote_client->connection ? 
+                                remote_client->connection : 
+                                remote_client->router->connection, FALSE,
+                                remote_client->id, comment);
+
+  /* Remove the client from all channels. This generates new keys to the
+     channels as well. */
+  silc_server_remove_from_channels(server, NULL, remote_client, FALSE, 
+                                  NULL, TRUE);
+
+  /* Remove the client entry, If it is locally connected then we will also
+     disconnect the client here */
+  if (remote_client->connection) {
+    /* Remove locally conneted client */
+    SilcSocketConnection sock = remote_client->connection;
+    silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
+    silc_server_close_connection(server, sock);
+  } else {
+    /* Update statistics */
+    if (remote_client->connection)
+      server->stat.my_clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+    /* Remove remote client */
+    silc_idlist_del_client(local ? server->local_list :
+                          server->global_list, remote_client);
+  }
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command INFO. This sends information about us to 
+   the client. If client requested specific server we will send the 
+   command to that server. */
+
+SILC_SERVER_CMD_FUNC(info)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcBuffer packet, idp;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  char *dest_server, *server_info = NULL, *server_name;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  SilcServerEntry entry = NULL;
+  SilcServerID *server_id = NULL;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
+
+  /* Get server name */
+  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+
+  /* Get Server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp) {
+    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!server_id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                           SILC_STATUS_ERR_NO_SERVER_ID);
+      goto out;
+    }
+  }
+
+  if (server_id) {
+    /* Check whether we have this server cached */
+    entry = silc_idlist_find_server_by_id(server->local_list,
+                                         server_id, TRUE, NULL);
+    if (!entry) {
+      entry = silc_idlist_find_server_by_id(server->global_list,
+                                           server_id, TRUE, NULL);
+      if (!entry && server->server_type != SILC_SERVER) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                             SILC_STATUS_ERR_NO_SUCH_SERVER);
+       goto out;
+      }
+    }
+  }
+
+  /* Some buggy servers has sent request to router about themselves. */
+  if (server->server_type != SILC_SERVER && cmd->sock->user_data == entry)
+    goto out;
+
+  if ((!dest_server && !server_id && !entry) || (entry && 
+                                                entry == server->id_entry) ||
+      (dest_server && !cmd->pending && 
+       !strncasecmp(dest_server, server->server_name, strlen(dest_server)))) {
+    /* Send our reply */
+    char info_string[256];
+
+    memset(info_string, 0, sizeof(info_string));
+    snprintf(info_string, sizeof(info_string), 
+            "location: %s server: %s admin: %s <%s>",
+            server->config->server_info->location,
+            server->config->server_info->server_type,
+            server->config->server_info->admin,
+            server->config->server_info->email);
+
+    server_info = info_string;
+    entry = server->id_entry;
+  } else {
+    /* Check whether we have this server cached */
+    if (!entry && dest_server) {
+      entry = silc_idlist_find_server_by_name(server->global_list,
+                                             dest_server, TRUE, NULL);
+      if (!entry) {
+       entry = silc_idlist_find_server_by_name(server->local_list,
+                                               dest_server, TRUE, NULL);
+      }
+    }
+
+    if (!cmd->pending &&
+       server->server_type != SILC_SERVER && entry && !entry->server_info) {
+      /* Send to the server */
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
+
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      silc_server_packet_send(server, entry->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_INFO, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_info,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    }
+
+    if (!entry && !cmd->pending && !server->standalone) {
+      /* Send to the primary router */
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
+
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_INFO, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_info,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    }
+  }
+
+  silc_free(server_id);
+
+  if (!entry) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+    goto out;
+  }
+
+  idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+  if (!server_info)
+    server_info = entry->server_info;
+  server_name = entry->server_name;
+
+  /* Send the reply */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+                                               SILC_STATUS_OK, ident, 3,
+                                               2, idp->data, idp->len,
+                                               3, server_name, 
+                                               strlen(server_name),
+                                               4, server_info, 
+                                               server_info ? 
+                                               strlen(server_info) : 0);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(packet);
+  silc_buffer_free(idp);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command PING. This just replies to the ping. */
+
+SILC_SERVER_CMD_FUNC(ping)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcServerID *id;
+  uint32 len;
+  unsigned char *tmp;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
+
+  /* Get Server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_ERR_NO_SERVER_ID);
+    goto out;
+  }
+  id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
+  if (!id)
+    goto out;
+
+  if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+    /* Send our reply */
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_OK);
+  } else {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+    goto out;
+  }
+
+  silc_free(id);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Internal routine to join channel. The channel sent to this function
+   has been either created or resolved from ID lists. This joins the sent
+   client to the channel. */
+
+static void silc_server_command_join_channel(SilcServer server, 
+                                            SilcServerCommandContext cmd,
+                                            SilcChannelEntry channel,
+                                            SilcClientID *client_id,
+                                            bool created,
+                                            bool create_key,
+                                            uint32 umode,
+                                            const unsigned char *auth,
+                                            uint32 auth_len)
+{
+  SilcSocketConnection sock = cmd->sock;
+  unsigned char *tmp;
+  uint32 tmp_len, user_count;
+  unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
+  SilcClientEntry client;
+  SilcChannelClientEntry chl;
+  SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char check[512], check2[512];
+  bool founder = FALSE;
+  bool resolve;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!channel)
+    return;
+
+  /* Get the client entry */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    client = (SilcClientEntry)sock->user_data;
+  } else {
+    client = silc_server_get_client_resolve(server, client_id, &resolve);
+    if (!client) {
+      if (cmd->pending)
+       goto out;
+
+      if (!resolve) {
+       silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* The client info is being resolved. Reprocess this packet after
+        receiving the reply to the query. */
+      silc_server_command_pending(server, SILC_COMMAND_WHOIS, 
+                                 server->cmd_ident,
+                                 silc_server_command_join, 
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      goto out;
+    }
+
+    cmd->pending = FALSE;
+  }
+
+  /*
+   * Check founder auth payload if provided.  If client can gain founder
+   * privileges it can override various conditions on joining the channel,
+   * and can have directly the founder mode set on the channel.
+   */
+  if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+    SilcIDListData idata = (SilcIDListData)client;
+
+    if (channel->founder_key && idata->public_key &&
+       silc_pkcs_public_key_compare(channel->founder_key, 
+                                    idata->public_key)) {
+      void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
+                        (void *)channel->founder_passwd : 
+                        (void *)channel->founder_key);
+      uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+                             channel->founder_passwd_len : 0);
+
+      /* Check whether the client is to become founder */
+      if (silc_auth_verify_data(auth, auth_len, channel->founder_method, 
+                               auth_data, auth_data_len,
+                               idata->hash, client->id, SILC_ID_CLIENT)) {
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       founder = TRUE;
+      }
+    }
+  }
+
+  /*
+   * Check channel modes
+   */
+
+  if (!umode) {
+    memset(check, 0, sizeof(check));
+    memset(check2, 0, sizeof(check2));
+    strncat(check, client->nickname, strlen(client->nickname));
+    strncat(check, "!", 1);
+    strncat(check, client->username, strlen(client->username));
+    if (!strchr(client->username, '@')) {
+      strncat(check, "@", 1);
+      strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
+    }
+
+    strncat(check2, client->nickname, strlen(client->nickname));
+    if (!strchr(client->nickname, '@')) {
+      strncat(check2, "@", 1);
+      strncat(check2, server->server_name, strlen(server->server_name));
+    }
+    strncat(check2, "!", 1);
+    strncat(check2, client->username, strlen(client->username));
+    if (!strchr(client->username, '@')) {
+      strncat(check2, "@", 1);
+      strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
+    }
+    
+    /* Check invite list if channel is invite-only channel */
+    if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+      if (!channel->invite_list ||
+         (!silc_string_match(channel->invite_list, check) &&
+          !silc_string_match(channel->invite_list, check2))) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                             SILC_STATUS_ERR_NOT_INVITED);
+       goto out;
+      }
+    }
+
+    /* Check ban list if it exists. If the client's nickname, server,
+       username and/or hostname is in the ban list the access to the
+       channel is denied. */
+    if (channel->ban_list) {
+      if (silc_string_match(channel->ban_list, check) ||
+         silc_string_match(channel->ban_list, check2)) {
+       silc_server_command_send_status_reply(
+                                     cmd, SILC_COMMAND_JOIN,
+                                     SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
+       goto out;
+      }
+    }
+    
+    /* Check user count limit if set. */
+    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
+      if (silc_hash_table_count(channel->user_list) + 1 > 
+         channel->user_limit) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                             SILC_STATUS_ERR_CHANNEL_IS_FULL);
+       goto out;
+      }
+    }
+  }
+
+  /* Check the channel passphrase if set. */
+  if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+    /* Get passphrase */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+    if (tmp) {
+      passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
+      memcpy(passphrase, tmp, tmp_len);
+    }
+  
+    if (!passphrase || !channel->passphrase ||
+        memcmp(passphrase, channel->passphrase, strlen(channel->passphrase))) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                           SILC_STATUS_ERR_BAD_PASSWORD);
+      goto out;
+    }
+  }
+
+  /*
+   * Client is allowed to join to the channel. Make it happen.
+   */
+
+  /* Check whether the client already is on the channel */
+  if (silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_USER_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Generate new channel key as protocol dictates */
+  if (create_key) {
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
+
+    /* Send the channel key. This is broadcasted to the channel but is not
+       sent to the client who is joining to the channel. */
+    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+  }
+
+  /* Join the client to the channel by adding it to channel's user list.
+     Add also the channel to client entry's channels list for fast cross-
+     referencing. */
+  chl = silc_calloc(1, sizeof(*chl));
+  chl->mode = umode;
+  chl->client = client;
+  chl->channel = channel;
+  silc_hash_table_add(channel->user_list, client, chl);
+  silc_hash_table_add(client->channels, channel, chl);
+
+  /* Get users on the channel */
+  silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
+                                  &user_count);
+
+  /* Encode Client ID Payload of the original client who wants to join */
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Encode command reply packet */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  SILC_PUT32_MSB(channel->mode, mode);
+  SILC_PUT32_MSB(created, tmp2);
+  SILC_PUT32_MSB(user_count, tmp3);
+
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp, 
+                                          strlen(channel->channel_key->
+                                                 cipher->name),
+                                          channel->channel_key->cipher->name,
+                                          channel->key_len / 8, channel->key);
+    silc_free(tmp);
+  }
+
+  reply = 
+    silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+                                        SILC_STATUS_OK, ident, 13,
+                                        2, channel->channel_name,
+                                        strlen(channel->channel_name),
+                                        3, chidp->data, chidp->len,
+                                        4, clidp->data, clidp->len,
+                                        5, mode, 4,
+                                        6, tmp2, 4,
+                                        7, keyp ? keyp->data : NULL, 
+                                        keyp ? keyp->len : 0,
+                                        8, channel->ban_list, 
+                                        channel->ban_list ?
+                                        strlen(channel->ban_list) : 0,
+                                        9, channel->invite_list,
+                                        channel->invite_list ?
+                                        strlen(channel->invite_list) : 0,
+                                        10, channel->topic,
+                                        channel->topic ?
+                                        strlen(channel->topic) : 0,
+                                        11, silc_hmac_get_name(channel->hmac),
+                                        strlen(silc_hmac_get_name(channel->
+                                                                  hmac)),
+                                        12, tmp3, 4,
+                                        13, user_list->data, user_list->len,
+                                        14, mode_list->data, 
+                                        mode_list->len);
+
+  /* Send command reply */
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         reply->data, reply->len, FALSE);
+
+  /* Send JOIN notify to locally connected clients on the channel. If
+     we are normal server then router will send or have sent JOIN notify
+     already. However since we've added the client already to our channel
+     we'll ignore it (in packet_receive.c) so we must send it here. If
+     we are router then this will send it to local clients and local
+     servers. */
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                    SILC_NOTIFY_TYPE_JOIN, 2,
+                                    clidp->data, clidp->len,
+                                    chidp->data, chidp->len);
+
+  if (!cmd->pending) {
+    /* Send JOIN notify packet to our primary router */
+    if (!server->standalone)
+      silc_server_send_notify_join(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE, channel, client->id);
+
+    if (keyp)
+      /* Distribute the channel key to all backup routers. */
+      silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
+                             keyp->data, keyp->len, FALSE, TRUE);
+  }
+
+  /* If client became founder by providing correct founder auth data
+     notify the mode change to the channel. */
+  if (founder) {
+    SILC_PUT32_MSB(chl->mode, mode);
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                      clidp->data, clidp->len,
+                                      mode, 4, clidp->data, clidp->len);
+      
+    /* Set CUMODE notify type to network */
+    if (!server->standalone)
+      silc_server_send_notify_cumode(server, server->router->connection,
+                                    server->server_type == SILC_ROUTER ? 
+                                    TRUE : FALSE, channel,
+                                    chl->mode, client->id, SILC_ID_CLIENT,
+                                    client->id);
+  }
+
+  silc_buffer_free(reply);
+  silc_buffer_free(clidp);
+  silc_buffer_free(chidp);
+  silc_buffer_free(keyp);
+  silc_buffer_free(user_list);
+  silc_buffer_free(mode_list);
+
+ out:
+  silc_free(passphrase);
+}
+
+/* Server side of command JOIN. Joins client into requested channel. If 
+   the channel does not exist it will be created. */
+
+SILC_SERVER_CMD_FUNC(join)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  unsigned char *auth;
+  uint32 tmp_len, auth_len;
+  char *tmp, *channel_name = NULL, *cipher, *hmac;
+  SilcChannelEntry channel;
+  uint32 umode = 0;
+  bool created = FALSE, create_key = TRUE;
+  SilcClientID *client_id;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6);
+
+  /* Get channel name */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  channel_name = tmp;
+
+  if (tmp_len > 256)
+    channel_name[255] = '\0';
+
+  if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_BAD_CHANNEL);
+    goto out;
+  }
+
+  /* Get Client ID of the client who is joining to the channel */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get cipher, hmac name and auth payload */
+  cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
+
+  /* See if the channel exists */
+  channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                            channel_name, NULL);
+
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+    client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
+
+    if (!channel || channel->disabled) {
+      /* Channel not found */
+
+      /* If we are standalone server we don't have a router, we just create 
+        the channel by ourselves. */
+      if (server->standalone) {
+       channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                hmac, channel_name, TRUE);
+       if (!channel) {
+         silc_server_command_send_status_reply(
+                                        cmd, SILC_COMMAND_JOIN,
+                                        SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         goto out;
+       }
+       
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       created = TRUE;
+       create_key = FALSE;
+       
+      } else {
+
+       /* The channel does not exist on our server. If we are normal server 
+          we will send JOIN command to our router which will handle the
+          joining procedure (either creates the channel if it doesn't exist 
+          or joins the client to it). */
+       if (server->server_type != SILC_ROUTER) {
+         SilcBuffer tmpbuf;
+         uint16 old_ident;
+
+         /* If this is pending command callback then we've resolved
+            it and it didn't work, return since we've notified the
+            client already in the command reply callback. */
+         if (cmd->pending)
+           goto out;
+         
+         old_ident = silc_command_get_ident(cmd->payload);
+         silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+         tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+         
+         /* Send JOIN command to our router */
+         silc_server_packet_send(server, (SilcSocketConnection)
+                                 server->router->connection,
+                                 SILC_PACKET_COMMAND, cmd->packet->flags,
+                                 tmpbuf->data, tmpbuf->len, TRUE);
+         
+         /* Reprocess this packet after received reply from router */
+         silc_server_command_pending(server, SILC_COMMAND_JOIN, 
+                                     silc_command_get_ident(cmd->payload),
+                                     silc_server_command_join,
+                                     silc_server_command_dup(cmd));
+         cmd->pending = TRUE;
+          silc_command_set_ident(cmd->payload, old_ident);
+         silc_buffer_free(tmpbuf);
+         goto out;
+       }
+       
+       /* We are router and the channel does not seem exist so we will check
+          our global list as well for the channel. */
+       channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                  channel_name, NULL);
+       if (!channel) {
+         /* Channel really does not exist, create it */
+         channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                  hmac, channel_name, TRUE);
+         if (!channel) {
+           silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+           goto out;
+         }
+
+         umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+         created = TRUE;
+         create_key = FALSE;
+       }
+      }
+    }
+  } else {
+    if (!channel) {
+      /* Channel not found */
+
+      /* If the command came from router and we are normal server then
+        something went wrong with the joining as the channel was not found.
+        We can't do anything else but ignore this. */
+      if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
+         server->server_type != SILC_ROUTER)
+       goto out;
+      
+      /* We are router and the channel does not seem exist so we will check
+        our global list as well for the channel. */
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+      if (!channel) {
+       /* Channel really does not exist, create it */
+       channel = silc_server_create_new_channel(server, server->id, cipher, 
+                                                hmac, channel_name, TRUE);
+       if (!channel) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+         goto out;
+       }
+
+       umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+       created = TRUE;
+       create_key = FALSE;
+      }
+    }
+  }
+
+  /* Check whether the channel was created by our router */
+  if (cmd->pending && context2) {
+    SilcServerCommandReplyContext reply = 
+      (SilcServerCommandReplyContext)context2;
+
+    if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
+      tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
+      SILC_GET32_MSB(created, tmp);
+      if (silc_argument_get_arg_type(reply->args, 7, NULL))
+       create_key = FALSE;     /* Router returned the key already */
+    }
+
+    if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+       !silc_hash_table_count(channel->user_list))
+      created = TRUE;
+  }
+
+  /* If the channel does not have global users and is also empty the client
+     will be the channel founder and operator. */
+  if (!channel->global_users && !silc_hash_table_count(channel->user_list))
+    umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
+  /* Join to the channel */
+  silc_server_command_join_channel(server, cmd, channel, client_id,
+                                  created, create_key, umode,
+                                  auth, auth_len);
+
+  silc_free(client_id);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command MOTD. Sends server's current "message of the
+   day" to the client. */
+
+SILC_SERVER_CMD_FUNC(motd)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcBuffer packet, idp;
+  char *motd, *dest_server;
+  uint32 motd_len;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
+
+  /* Get server name */
+  dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  if (!dest_server) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+                                         SILC_STATUS_ERR_NO_SUCH_SERVER);
+    goto out;
+  }
+
+  if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
+    /* Send our MOTD */
+
+    idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
+
+    if (server->config && server->config->server_info &&
+       server->config->server_info->motd_file) {
+      /* Send motd */
+      motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
+      if (!motd)
+       goto out;
+      
+      motd[motd_len] = 0;
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+                                                   SILC_STATUS_OK, ident, 2,
+                                                   2, idp, idp->len,
+                                                   3, motd, motd_len);
+    } else {
+      /* No motd */
+      packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+                                                   SILC_STATUS_OK, ident, 1,
+                                                   2, idp, idp->len);
+    }
+
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                           packet->data, packet->len, FALSE);
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+  } else {
+    SilcServerEntry entry;
+
+    /* Check whether we have this server cached */
+    entry = silc_idlist_find_server_by_name(server->global_list,
+                                           dest_server, TRUE, NULL);
+    if (!entry) {
+      entry = silc_idlist_find_server_by_name(server->local_list,
+                                             dest_server, TRUE, NULL);
+    }
+
+    if (server->server_type != SILC_SERVER && !cmd->pending && 
+       entry && !entry->motd) {
+      /* Send to the server */
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
+
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      silc_server_packet_send(server, entry->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_MOTD, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_motd,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    }
+
+    if (!entry && !cmd->pending && !server->standalone) {
+      /* Send to the primary router */
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
+
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_MOTD, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_motd,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    }
+
+    if (!entry) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+                                           SILC_STATUS_ERR_NO_SUCH_SERVER);
+      goto out;
+    }
+
+    idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
+    packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+                                                 SILC_STATUS_OK, ident, 2,
+                                                 2, idp, idp->len,
+                                                 3, entry->motd,
+                                                 entry->motd ? 
+                                                 strlen(entry->motd) : 0);
+    silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                           packet->data, packet->len, FALSE);
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+  }
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of command UMODE. Client can use this command to set/unset
+   user mode. Client actually cannot set itself to be as server/router
+   operator so this can be used only to unset the modes. */
+
+SILC_SERVER_CMD_FUNC(umode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcBuffer packet;
+  unsigned char *tmp_mask;
+  uint32 mask;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
+
+  /* Get the client's mode mask */
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!tmp_mask) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  SILC_GET32_MSB(mask, tmp_mask);
+
+  /* 
+   * Change the mode 
+   */
+
+  if (mask & SILC_UMODE_SERVER_OPERATOR) {
+    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR)) {
+      /* Cannot operator mode */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                           SILC_STATUS_ERR_PERM_DENIED);
+      goto out;
+    }
+  } else {
+    /* Remove the server operator rights */
+    if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
+      client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+      if (client->connection)
+       server->stat.my_server_ops--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.server_ops--;
+    }
+  }
+
+  if (mask & SILC_UMODE_ROUTER_OPERATOR) {
+    if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+      /* Cannot operator mode */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+                                           SILC_STATUS_ERR_PERM_DENIED);
+      goto out;
+    }
+  } else {
+    /* Remove the router operator rights */
+    if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
+      client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+      if (client->connection)
+       server->stat.my_router_ops--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.router_ops--;
+    }
+  }
+
+  if (mask & SILC_UMODE_GONE) {
+    client->mode |= SILC_UMODE_GONE;
+  } else {
+    if (client->mode & SILC_UMODE_GONE)
+      /* Remove the gone status */
+      client->mode &= ~SILC_UMODE_GONE;
+  }
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, client->mode);
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
+                                               SILC_STATUS_OK, ident, 1,
+                                               2, tmp_mask, 4);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Checks that client has rights to add or remove channel modes. If any
+   of the checks fails FALSE is returned. */
+
+int silc_server_check_cmode_rights(SilcChannelEntry channel,
+                                  SilcChannelClientEntry client,
+                                  uint32 mode)
+{
+  int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
+  int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
+
+  /* Check whether has rights to change anything */
+  if (!is_op && !is_fo)
+    return FALSE;
+
+  /* Check whether has rights to change everything */
+  if (is_op && is_fo)
+    return TRUE;
+
+  /* We know that client is channel operator, check that they are not
+     changing anything that requires channel founder rights. Rest of the
+     modes are available automatically for channel operator. */
+
+  if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+      if (is_op && !is_fo)
+       return FALSE;
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+      if (is_op && !is_fo)
+       return FALSE;
+    }
+  }
+  
+  if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE))
+      if (is_op && !is_fo)
+       return FALSE;
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+      if (is_op && !is_fo)
+       return FALSE;
+    }
+  }
+
+  if (mode & SILC_CHANNEL_MODE_CIPHER) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER))
+      if (is_op && !is_fo)
+       return FALSE;
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+      if (is_op && !is_fo)
+       return FALSE;
+    }
+  }
+  
+  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH))
+      if (is_op && !is_fo)
+       return FALSE;
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+      if (is_op && !is_fo)
+       return FALSE;
+    }
+  }
+  
+  return TRUE;
+}
+
+/* Server side command of CMODE. Changes channel mode */
+
+SILC_SERVER_CMD_FUNC(cmode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcIDListData idata = (SilcIDListData)client;
+  SilcChannelID *channel_id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer packet, cidp;
+  unsigned char *tmp, *tmp_id, *tmp_mask;
+  char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
+  uint32 mode_mask, tmp_len, tmp_len2;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
+
+  /* Get Channel ID */
+  tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+  if (!tmp_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+
+  /* Get the channel mode mask */
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp_mask) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  SILC_GET32_MSB(mode_mask, tmp_mask);
+
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* Check whether this client is on the channel */
+  if (!silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Get entry to the channel user list */
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+
+  /* Check that client has rights to change any requested channel modes */
+  if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+    goto out;
+  }
+
+  /*
+   * Check the modes. Modes that requires nothing special operation are
+   * not checked here.
+   */
+
+  if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
+    /* Channel uses private keys to protect traffic. Client(s) has set the
+       key locally they want to use, server does not know that key. */
+    /* Nothing interesting to do here */
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+      /* The mode is removed and we need to generate and distribute
+        new channel key. Clients are not using private channel keys
+        anymore after this. */
+      
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0))
+       goto out;
+       
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+       
+      cipher = channel->channel_key->cipher->name;
+      hmac = (char *)silc_hmac_get_name(channel->hmac);
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
+    /* User limit is set on channel */
+    uint32 user_limit;
+      
+    /* Get user limit */
+    tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+    if (!tmp) {
+      if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+    } else {
+      SILC_GET32_MSB(user_limit, tmp);
+      channel->user_limit = user_limit;
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+      /* User limit mode is unset. Remove user limit */
+      channel->user_limit = 0;
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
+      /* Passphrase has been set to channel */
+      
+      /* Get the passphrase */
+      tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+      if (!tmp) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* Save the passphrase */
+      passphrase = channel->passphrase = strdup(tmp);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+      /* Passphrase mode is unset. remove the passphrase */
+      if (channel->passphrase) {
+       silc_free(channel->passphrase);
+       channel->passphrase = NULL;
+      }
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
+      /* Cipher to use protect the traffic */
+      SilcCipher newkey, oldkey;
+
+      /* Get cipher */
+      cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
+      if (!cipher) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* Delete old cipher and allocate the new one */
+      if (!silc_cipher_alloc(cipher, &newkey)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+
+      oldkey = channel->channel_key;
+      channel->channel_key = newkey;
+
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->channel_key = oldkey;
+       goto out;
+      }
+
+      /* Remove old channel key for good */
+      silc_cipher_free(oldkey);
+
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+      /* Cipher mode is unset. Remove the cipher and revert back to 
+        default cipher */
+      SilcCipher newkey, oldkey;
+      cipher = channel->cipher;
+
+      /* Delete old cipher and allocate default one */
+      if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, &newkey)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+
+      oldkey = channel->channel_key;
+      channel->channel_key = newkey;
+
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0)) {
+       /* We don't have new key, revert to old one */
+       channel->channel_key = oldkey;
+       goto out;
+      }
+      
+      /* Remove old channel key for good */
+      silc_cipher_free(oldkey);
+
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
+    if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
+      /* HMAC to use protect the traffic */
+      unsigned char hash[32];
+      SilcHmac newhmac;
+
+      /* Get hmac */
+      hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
+      if (!hmac) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                  SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      /* Delete old hmac and allocate the new one */
+      if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
+
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
+                    channel->key_len / 8, hash);
+      silc_hmac_set_key(channel->hmac, hash, 
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
+    }
+  } else {
+    if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
+      /* Hmac mode is unset. Remove the hmac and revert back to 
+        default hmac */
+      SilcHmac newhmac;
+      unsigned char hash[32];
+      hmac = channel->hmac_name;
+
+      /* Delete old hmac and allocate default one */
+      silc_hmac_free(channel->hmac);
+      if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                      SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+       goto out;
+      }
+
+      silc_hmac_free(channel->hmac);
+      channel->hmac = newhmac;
+
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
+                    channel->key_len / 8, 
+                    hash);
+      silc_hmac_set_key(channel->hmac, hash, 
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
+    }
+  }
+
+  if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
+       /* Set the founder authentication */
+       SilcAuthPayload auth;
+       
+       tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
+       if (!tmp) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+         goto out;
+       }
+
+       auth = silc_auth_payload_parse(tmp, tmp_len);
+       if (!auth) {
+         silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+         goto out;
+       }
+
+       /* Save the public key */
+       tmp = silc_pkcs_public_key_encode(idata->public_key, &tmp_len);
+       silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+       silc_free(tmp);
+       
+       channel->founder_method = silc_auth_get_method(auth);
+
+       if (channel->founder_method == SILC_AUTH_PASSWORD) {
+         tmp = silc_auth_get_data(auth, &tmp_len);
+         channel->founder_passwd = 
+           silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
+         memcpy(channel->founder_passwd, tmp, tmp_len);
+         channel->founder_passwd_len = tmp_len;
+       } else {
+         /* Verify the payload before setting the mode */
+         if (!silc_auth_verify(auth, channel->founder_method, 
+                               channel->founder_key, 0, idata->hash,
+                               client->id, SILC_ID_CLIENT)) {
+           silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+                                                 SILC_STATUS_ERR_AUTH_FAILED);
+           goto out;
+         }
+       }
+
+       silc_auth_payload_free(auth);
+      }
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       if (channel->founder_passwd) {
+         silc_free(channel->founder_passwd);
+         channel->founder_passwd = NULL;
+       }
+      }
+    }
+  }
+
+  /* Finally, set the mode */
+  channel->mode = mode_mask;
+
+  /* Send CMODE_CHANGE notify. */
+  cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                    SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
+                                    cidp->data, cidp->len, 
+                                    tmp_mask, 4,
+                                    cipher, cipher ? strlen(cipher) : 0,
+                                    hmac, hmac ? strlen(hmac) : 0,
+                                    passphrase, passphrase ? 
+                                    strlen(passphrase) : 0);
+
+  /* Set CMODE notify type to network */
+  if (!server->standalone)
+    silc_server_send_notify_cmode(server, server->router->connection,
+                                 server->server_type == SILC_ROUTER ? 
+                                 TRUE : FALSE, channel,
+                                 mode_mask, client->id, SILC_ID_CLIENT,
+                                 cipher, hmac, passphrase);
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
+                                               SILC_STATUS_OK, ident, 2,
+                                               2, tmp_id, tmp_len2,
+                                               3, tmp_mask, 4);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(packet);
+  silc_free(channel_id);
+  silc_buffer_free(cidp);
+
+ out:
+  silc_server_command_free(cmd);
+}
+
+/* Server side of CUMODE command. Changes client's mode on a channel. */
+
+SILC_SERVER_CMD_FUNC(cumode)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcIDListData idata = (SilcIDListData)client;
+  SilcChannelID *channel_id;
+  SilcClientID *client_id;
+  SilcChannelEntry channel;
+  SilcClientEntry target_client;
+  SilcChannelClientEntry chl;
+  SilcBuffer packet, idp;
+  unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+  uint32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
+  int notify = FALSE;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
+
+  /* Get Channel ID */
+  tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+  if (!tmp_ch_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp_ch_id, tmp_ch_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* Check whether sender is on the channel */
+  if (!silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Check that client has rights to change other's rights */
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+  sender_mask = chl->mode;
+  
+  /* Get the target client's channel mode mask */
+  tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!tmp_mask) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  SILC_GET32_MSB(target_mask, tmp_mask);
+
+  /* Get target Client ID */
+  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (!tmp_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
+  }
+
+  /* Get target client's entry */
+  target_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, TRUE, NULL);
+  if (!target_client) {
+    target_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, TRUE, NULL);
+  }
+
+  if (target_client != client &&
+      !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
+      !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+    goto out;
+  }
+
+  /* Check whether target client is on the channel */
+  if (target_client != client) {
+    if (!silc_server_client_on_channel(target_client, channel)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+      goto out;
+    }
+
+    /* Get entry to the channel user list */
+    silc_hash_table_find(channel->user_list, target_client, NULL, 
+                        (void *)&chl);
+  }
+
+  /* 
+   * Change the mode 
+   */
+
+  /* If the target client is founder, no one else can change their mode
+     but themselves. */
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && client != target_client) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+    goto out;
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+      /* The client tries to claim the founder rights. */
+      unsigned char *tmp_auth;
+      uint32 tmp_auth_len, auth_len;
+      void *auth;
+      
+      if (target_client != client) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
+
+      if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+         !channel->founder_key || !idata->public_key ||
+         !silc_pkcs_public_key_compare(channel->founder_key, 
+                                       idata->public_key)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
+
+      tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
+      if (!tmp_auth) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+       goto out;
+      }
+
+      auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
+             (void *)channel->founder_passwd : (void *)channel->founder_key);
+      auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+                 channel->founder_passwd_len : 0);
+      
+      if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
+                                channel->founder_method, auth, auth_len,
+                                idata->hash, client->id, SILC_ID_CLIENT)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_AUTH_FAILED);
+       goto out;
+      }
+      
+      sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+      if (target_client == client) {
+       /* Remove channel founder rights from itself */
+       chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+       notify = TRUE;
+      } else {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NOT_YOU);
+       goto out;
+      }
+    }
+  }
+
+  if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
+    /* Promote to operator */
+    if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+         !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+       goto out;
+      }
+
+      chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
+    }
+  } else {
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
+      if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+         !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+       silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+                                             SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+       goto out;
+      }
+
+      /* Demote to normal user */
+      chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+      notify = TRUE;
+    }
+  }
+
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+
+  /* Send notify to channel, notify only if mode was actually changed. */
+  if (notify) {
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, 
+                                      SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+                                      idp->data, idp->len,
+                                      tmp_mask, 4, 
+                                      tmp_id, tmp_len);
+
+    /* Set CUMODE notify type to network */
+    if (!server->standalone)
+      silc_server_send_notify_cumode(server, server->router->connection,
+                                    server->server_type == SILC_ROUTER ? 
+                                    TRUE : FALSE, channel,
+                                    target_mask, client->id, 
+                                    SILC_ID_CLIENT,
+                                    target_client->id);
+  }
+
+  /* Send command reply to sender */
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
+                                               SILC_STATUS_OK, ident, 3,
+                                               2, tmp_mask, 4,
+                                               3, tmp_ch_id, tmp_ch_len,
+                                               4, tmp_id, tmp_len);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(packet);
+  silc_free(channel_id);
+  silc_free(client_id);
+  silc_buffer_free(idp);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(topic)
+/* Server side of KICK command. Kicks client out of channel. */
+
+SILC_SERVER_CMD_FUNC(kick)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcClientEntry target_client;
+  SilcChannelID *channel_id;
+  SilcClientID *client_id;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcBuffer idp;
+  uint32 tmp_len, target_idp_len;
+  unsigned char *tmp, *comment, *target_idp;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 3);
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* Check whether sender is on the channel */
+  if (!silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Check that the kicker is channel operator or channel founder */
+  silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+  if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+    goto out;
+  }
+  
+  /* Get target Client ID */
+  target_idp = silc_argument_get_arg_type(cmd->args, 2, &target_idp_len);
+  if (!target_idp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(target_idp, target_idp_len);
+  if (!client_id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CLIENT_ID);
+    goto out;
+  }
+
+  /* Get target client's entry */
+  target_client = silc_idlist_find_client_by_id(server->local_list, 
+                                               client_id, TRUE, NULL);
+  if (!target_client) {
+    target_client = silc_idlist_find_client_by_id(server->global_list, 
+                                                 client_id, TRUE, NULL);
+  }
+
+  /* Check that the target client is not channel founder. Channel founder
+     cannot be kicked from the channel. */
+  silc_hash_table_find(channel->user_list, target_client, NULL, (void *)&chl);
+  if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_NO_CHANNEL_FOPRIV);
+    goto out;
+  }
+  
+  /* Check whether target client is on the channel */
+  if (!silc_server_client_on_channel(target_client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK,
+                                         SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+    goto out;
+  }
+
+  /* Get comment */
+  tmp_len = 0;
+  comment = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (tmp_len > 128)
+    comment = NULL;
+
+  /* Send command reply to sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_KICK, 
+                                       SILC_STATUS_OK);
+
+  /* Send KICKED notify to local clients on the channel */
+  idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+  silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                    SILC_NOTIFY_TYPE_KICKED, 3,
+                                    target_idp, target_idp_len,
+                                    comment, comment ? strlen(comment) : 0,
+                                    idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  /* Remove the client from the channel. If the channel does not exist
+     after removing the client then the client kicked itself off the channel
+     and we don't have to send anything after that. */
+  if (!silc_server_remove_from_one_channel(server, NULL, channel, 
+                                          target_client, FALSE))
+    goto out;
+
+  /* Send KICKED notify to primary route */
+  if (!server->standalone)
+    silc_server_send_notify_kicked(server, server->router->connection,
+                                  server->server_type == SILC_ROUTER ?
+                                  TRUE : FALSE, channel,
+                                  target_client->id, client->id, comment);
+
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    /* Re-generate channel key */
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
+    
+    /* Send the channel key to the channel. The key of course is not sent
+       to the client who was kicked off the channel. */
+    silc_server_send_channel_key(server, target_client->connection, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
+  }
+
+ out:
+  silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(invite)
+/* Server side of OPER command. Client uses this comand to obtain server
+   operator privileges to this server/router. */
+
+SILC_SERVER_CMD_FUNC(oper)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  uint32 tmp_len;
+  SilcServerConfigSectionAdmin *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_OPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Verify the authentication data */
+  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
+                            admin->auth_data, admin->auth_data_len,
+                            idata->hash, client->id, SILC_ID_CLIENT)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Client is now server operator */
+  client->mode |= SILC_UMODE_SERVER_OPERATOR;
+
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_server_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.server_ops++;
+
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, client->mode);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_OPER,
+                                       SILC_STATUS_OK);
+
+ out:
+  silc_server_command_free(cmd);
 }
 
-/* Quits connection to client. This gets called if client won't
-   close the connection even when it has issued QUIT command. */
+/* Server side of SILCOPER command. Client uses this comand to obtain router
+   operator privileges to this router. */
+
+SILC_SERVER_CMD_FUNC(silcoper)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *username, *auth;
+  uint32 tmp_len;
+  SilcServerConfigSectionAdmin *admin;
+  SilcIDListData idata = (SilcIDListData)client;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SILCOPER, cmd, 1, 2);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  if (server->server_type != SILC_ROUTER) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Get the username */
+  username = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!username) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get the admin configuration */
+  admin = silc_server_config_find_admin(server->config, cmd->sock->ip,
+                                       username, client->nickname);
+  if (!admin) {
+    admin = silc_server_config_find_admin(server->config, cmd->sock->hostname,
+                                         username, client->nickname);
+    if (!admin) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                           SILC_STATUS_ERR_AUTH_FAILED);
+      goto out;
+    }
+  }
+
+  /* Get the authentication payload */
+  auth = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!auth) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Verify the authentication data */
+  if (!silc_auth_verify_data(auth, tmp_len, admin->auth_meth, 
+                            admin->auth_data, admin->auth_data_len,
+                            idata->hash, client->id, SILC_ID_CLIENT)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                         SILC_STATUS_ERR_AUTH_FAILED);
+    goto out;
+  }
+
+  /* Client is now router operator */
+  client->mode |= SILC_UMODE_ROUTER_OPERATOR;
+
+  /* Update statistics */
+  if (client->connection)
+    server->stat.my_router_ops++;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.router_ops++;
 
-SILC_TASK_CALLBACK(silc_server_command_quit_cb)
-{
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
+  /* Send UMODE change to primary router */
+  if (!server->standalone)
+    silc_server_send_notify_umode(server, server->router->connection, TRUE,
+                                 client->id, client->mode);
 
-  /* Free all client specific data, such as client entry and entires
-     on channels this client may be on. */
-  silc_server_free_sock_user_data(server, sock);
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SILCOPER,
+                                       SILC_STATUS_OK);
 
-  /* Close the connection on our side */
-  silc_server_close_connection(server, sock);
+ out:
+  silc_server_command_free(cmd);
 }
 
-/* Quits SILC session. This is the normal way to disconnect client. */
-SILC_SERVER_CMD_FUNC(quit)
+/* Server side command of CONNECT. Connects us to the specified remote
+   server or router. */
+
+SILC_SERVER_CMD_FUNC(connect)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  unsigned char *tmp, *host;
+  uint32 tmp_len;
+  uint32 port = SILC_PORT;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CONNECT, cmd, 1, 2);
 
-  /* We quit the connection with little timeout */
-  silc_task_register(server->timeout_queue, sock->sock,
-                    silc_server_command_quit_cb, server,
-                    0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
+  }
+
+  if (server->server_type == SILC_ROUTER && 
+      client->mode & SILC_UMODE_SERVER_OPERATOR) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                         SILC_STATUS_ERR_NO_ROUTER_PRIV);
+    goto out;
+  }
+
+  /* Get the remote server */
+  host = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!host) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
 
+  /* Create the connection. It is done with timeout and is async. */
+  silc_server_create_connection(server, host, port);
+
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CONNECT,
+                                       SILC_STATUS_OK);
+
+ out:
   silc_server_command_free(cmd);
 }
 
-SILC_SERVER_CMD_FUNC(kill)
-{
-}
+/* Server side of command BAN. This is used to manage the ban list of the
+   channel. To add clients and remove clients from the ban list. */
 
-SILC_SERVER_CMD_FUNC(info)
+SILC_SERVER_CMD_FUNC(ban)
 {
-}
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcBuffer packet;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcChannelID *channel_id = NULL;
+  unsigned char *id, *add, *del;
+  uint32 id_len, tmp_len;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+
+  if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
 
-SILC_SERVER_CMD_FUNC(connect)
-{
-}
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_BAN, cmd, 0, 3);
 
-SILC_SERVER_CMD_FUNC(ping)
-{
-}
+  /* Get Channel ID */
+  id = silc_argument_get_arg_type(cmd->args, 1, &id_len);
+  if (id) {
+    channel_id = silc_id_payload_parse_id(id, id_len);
+    if (!channel_id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+      goto out;
+    }
+  }
 
-SILC_SERVER_CMD_FUNC(oper)
-{
-}
+  /* Get channel entry. The server must know about the channel since the
+     client is expected to be on the channel. */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
 
-typedef struct {
-  char *channel_name;
-  char *nickname;
-  char *username;
-  char *hostname;
-  SilcChannelList *channel;
-  SilcServer server;
-} JoinInternalContext;
+  /* Check whether this client is on the channel */
+  if (!silc_server_client_on_channel(client, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
 
-SILC_TASK_CALLBACK(silc_server_command_join_notify)
-{
-  JoinInternalContext *ctx = (JoinInternalContext *)context;
+  /* Get entry to the channel user list */
+  if (!silc_hash_table_find(channel->user_list, client, NULL, (void *)&chl)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
+  }
 
-  if (ctx->channel->key && ctx->channel->key_len) {
-    silc_server_send_notify_to_channel(ctx->server, ctx->channel,
-                                      "%s (%s@%s) has joined channel %s",
-                                      ctx->nickname, ctx->username,
-                                      ctx->hostname, ctx->channel_name);
-    silc_free(ctx);
-  } else {
-    silc_task_register(ctx->server->timeout_queue, fd,
-                      silc_server_command_join_notify, context,
-                      0, 300000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  /* The client must be at least channel operator. */
+  if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_BAN,
+                                         SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+    goto out;
   }
-}
 
-/* Server side of command JOIN. Joins client into requested channel. If 
-   the channel does not exist it will be created. */
+  /* Get the new ban and add it to the ban list */
+  add = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (add) {
+    if (!channel->ban_list)
+      channel->ban_list = silc_calloc(tmp_len + 2, sizeof(*channel->ban_list));
+    else
+      channel->ban_list = silc_realloc(channel->ban_list, 
+                                      sizeof(*channel->ban_list) * 
+                                      (tmp_len + 
+                                       strlen(channel->ban_list) + 2));
+    if (add[tmp_len - 1] == ',')
+      add[tmp_len - 1] = '\0';
+
+    strncat(channel->ban_list, add, tmp_len);
+    strncat(channel->ban_list, ",", 1);
+  }
 
-SILC_SERVER_CMD_FUNC(join)
+  /* Get the ban to be removed and remove it from the list */
+  del = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (del && channel->ban_list) {
+    char *start, *end, *n;
+
+    if (!strncmp(channel->ban_list, del, strlen(channel->ban_list) - 1)) {
+      silc_free(channel->ban_list);
+      channel->ban_list = NULL;
+    } else {
+      start = strstr(channel->ban_list, del);
+      if (start && strlen(start) >= tmp_len) {
+       end = start + tmp_len;
+       n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
+       strncat(n, channel->ban_list, start - channel->ban_list);
+       strncat(n, end + 1, ((channel->ban_list + strlen(channel->ban_list)) - 
+                            end) - 1);
+       silc_free(channel->ban_list);
+       channel->ban_list = n;
+      }
+    }
+  }
+
+  /* Send the BAN notify type to our primary router. */
+  if (!server->standalone && (add || del))
+    silc_server_send_notify_ban(server, server->router->connection,
+                               server->server_type == SILC_ROUTER ?
+                               TRUE : FALSE, channel, add, del);
+
+  /* Send the reply back to the client */
+  packet = 
+    silc_command_reply_payload_encode_va(SILC_COMMAND_BAN,
+                                        SILC_STATUS_OK, ident, 2,
+                                        2, id, id_len,
+                                        3, channel->ban_list, 
+                                        channel->ban_list ? 
+                                        strlen(channel->ban_list) -1 : 0);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(packet);
+
+ out:
+  silc_free(channel_id);
+  silc_server_command_free(cmd);
+}
+
+/* Server side command of CLOSE. Closes connection to a specified server. */
+SILC_SERVER_CMD_FUNC(close)
 {
   SilcServerCommandContext cmd = (SilcServerCommandContext)context;
   SilcServer server = cmd->server;
-  SilcSocketConnection sock = cmd->sock;
-  SilcBuffer buffer = cmd->packet->buffer;
-  int argc, i, tmp_len;
-  char *tmp, *channel_name = NULL, *cipher = NULL, *id_string = NULL;
-  unsigned char *passphrase;
-  SilcChannelList *channel;
-  SilcServerID *router_id;
-  SilcIDCache *id_cache;
-  SilcBuffer packet, sp_buf;
-  SilcClientList *client;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+  SilcServerEntry server_entry;
+  SilcSocketConnection sock;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  unsigned char *name;
+  uint32 port = SILC_PORT;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CLOSE, cmd, 1, 2);
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
 
-  /* Check number of parameters */
-  argc = silc_command_get_arg_num(cmd->payload);
-  if (argc < 1) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+  /* Check whether client has the permissions. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
     goto out;
   }
-  if (argc > 3) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_TOO_MANY_PARAMS);
+
+  /* Get the remote server */
+  name = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
     goto out;
   }
 
-  /* Get channel name */
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  if (silc_server_command_bad_chars(tmp) == TRUE) {
-    silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
-                                         SILC_STATUS_ERR_BAD_CHANNEL);
+  /* Get port */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (tmp)
+    SILC_GET32_MSB(port, tmp);
+
+  server_entry = silc_idlist_find_server_by_conn(server->local_list,
+                                                name, port, FALSE, NULL);
+  if (!server_entry)
+    server_entry = silc_idlist_find_server_by_conn(server->global_list,
+                                                  name, port, FALSE, NULL);
+  if (!server_entry) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
+                                         SILC_STATUS_ERR_NO_SERVER_ID);
     goto out;
   }
-  channel_name = strdup(tmp);
 
-  /* Get passphrase */
-  tmp = silc_command_get_arg_type(cmd->payload, 2, &tmp_len);
-  if (tmp) {
-    passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
-    memcpy(passphrase, tmp, tmp_len);
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_CLOSE,
+                                       SILC_STATUS_OK);
+
+  /* Close the connection to the server */
+  sock = (SilcSocketConnection)server_entry->connection;
+
+  /* If we shutdown primary router connection manually then don't trigger
+     any reconnect or backup router connections, by setting the router
+     to NULL here. */
+  if (server->router == server_entry) {
+    server->id_entry->router = NULL;
+    server->router = NULL;
+    server->standalone = TRUE;
   }
+  silc_server_free_sock_user_data(server, sock, NULL);
+  silc_server_close_connection(server, sock);
   
-  /* Get cipher name */
-  cipher = silc_command_get_arg_type(cmd->payload, 3, NULL);
+ out:
+  silc_server_command_free(cmd);
+}
 
-  /* See if the channel exists */
-  if (silc_idcache_find_by_data(LCC(channel_name[0]), LCCC(channel_name[0]), 
-                               channel_name, &id_cache) == FALSE) {
-    /* Channel not found */
-    id_cache = NULL;
-
-    /* If we are standalone server we don't have a router, we just create 
-       the channel by  ourselves. */
-    if (server->standalone) {
-      router_id = server->id;
-      channel = silc_server_new_channel(server, router_id, 
-                                       cipher, channel_name);
-      goto join_channel;
-    }
-
-    /* No channel ID found, the channel does not exist on our server.
-       We send JOIN command to our router which will handle the joining
-       procedure (either creates the channel if it doesn't exist or
-       joins the client to it) - if we are normal server. */
-    if (server->server_type == SILC_SERVER) {
-
-      /* Forward the received JOIN command to the router */
-      silc_buffer_push(buffer, buffer->data - buffer->head);
-      silc_server_packet_forward(server, (SilcSocketConnection)
-                                server->id_entry->router->connection,
-                                buffer->data, buffer->len,
-                                TRUE);
-      
-      /* Add the command to be pending. It will be re-executed after
-        router has replied back to us. */
-      cmd->pending = TRUE;
-      silc_server_command_pending(SILC_COMMAND_JOIN, 
-                                 silc_server_command_join, context);
-      return;
-    }
+/* Server side command of SHUTDOWN. Shutdowns the server and closes all
+   active connections. */
+SILC_SERVER_CMD_FUNC(shutdown)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_SHUTDOWN, cmd, 0, 0);
+
+  if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+    goto out;
+
+  /* Check whether client has the permission. */
+  if (client->mode == SILC_UMODE_NONE) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
+                                         SILC_STATUS_ERR_NO_SERVER_PRIV);
+    goto out;
   }
 
-  /* If we are router and the channel does not exist we will check our
-     global list for the channel. */
-  if (!id_cache && server->server_type == SILC_ROUTER) {
+  /* Send reply to the sender */
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_SHUTDOWN,
+                                       SILC_STATUS_OK);
+
+  /* Then, gracefully, or not, bring the server down. */
+  silc_server_stop(server);
+  exit(0);
+
+ out:
+  silc_server_command_free(cmd);
+}
+/* Server side command of LEAVE. Removes client from a channel. */
+
+SILC_SERVER_CMD_FUNC(leave)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcSocketConnection sock = cmd->sock;
+  SilcClientEntry id_entry = (SilcClientEntry)cmd->sock->user_data;
+  SilcChannelID *id = NULL;
+  SilcChannelEntry channel;
+  uint32 len;
+  unsigned char *tmp;
+
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LEAVE, cmd, 1, 2);
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
+  id = silc_id_payload_parse_id(tmp, len);
+  if (!id) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
+  }
 
-    /* Notify all routers about the new channel in SILC network. */
-    if (!server->standalone) {
-#if 0
-      silc_server_send_new_id(server, server->id_entry->router->connection, 
-                             TRUE,
-                             xxx, SILC_ID_CHANNEL, SILC_ID_CHANNEL_LEN);
-#endif
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (!channel) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
     }
+  }
 
+  /* Check whether this client is on the channel */
+  if (!silc_server_client_on_channel(id_entry, channel)) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                         SILC_STATUS_ERR_NOT_ON_CHANNEL);
+    goto out;
   }
 
-  channel = (SilcChannelList *)id_cache->context;
+  /* Notify routers that they should remove this client from their list
+     of clients on the channel. Send LEAVE notify type. */
+  if (!server->standalone)
+    silc_server_send_notify_leave(server, server->router->connection,
+                                 server->server_type == SILC_ROUTER ?
+                                 TRUE : FALSE, channel, id_entry->id);
 
- join_channel:
+  silc_server_command_send_status_reply(cmd, SILC_COMMAND_LEAVE,
+                                       SILC_STATUS_OK);
 
-  /* XXX must check whether the client already is on the channel */
+  /* Remove client from channel */
+  if (!silc_server_remove_from_one_channel(server, sock, channel, id_entry,
+                                          TRUE))
+    /* If the channel does not exist anymore we won't send anything */
+    goto out;
 
-  /* Join the client to the channel */
-  i = channel->user_list_count;
-  channel->user_list = silc_realloc(channel->user_list, 
-                                   sizeof(*channel->user_list) * (i + 1));
-  channel->user_list[i].mode = SILC_CHANNEL_UMODE_NONE;
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    /* Re-generate channel key */
+    if (!silc_server_create_channel_key(server, channel, 0))
+      goto out;
 
-  /* If the JOIN request was forwarded to us we will make a bit slower
-     query to get the client pointer. Otherwise, we get the client pointer
-     real easy. */
-  if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
-    client = (SilcClientList *)sock->user_data;
-    channel->user_list[i].client = client;
-  } else {
-    void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
-    client = silc_idlist_find_client_by_id(server->local_list->clients, id);
-    channel->user_list[i].client = client;
-    silc_free(id);
+    /* Send the channel key */
+    silc_server_send_channel_key(server, NULL, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
   }
-  channel->user_list_count++;
 
-  i = client->channel_count;
-  client->channel = silc_realloc(client->channel, 
-                                sizeof(*client->channel) * (i + 1));
-  client->channel[i] = channel;
-  client->channel_count++;
+ out:
+  silc_free(id);
+  silc_server_command_free(cmd);
+}
 
-  /* Notify router about new user on channel. If we are normal server
-     we send it to our router, if we are router we send it to our
-     primary route. */
-  if (!server->standalone) {
+/* Server side of command USERS. Resolves clients and their USERS currently
+   joined on the requested channel. The list of Client ID's and their modes
+   on the channel is sent back. */
 
-  }
+SILC_SERVER_CMD_FUNC(users)
+{
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcChannelEntry channel;
+  SilcChannelID *id = NULL;
+  SilcBuffer packet, idp;
+  unsigned char *channel_id;
+  uint32 channel_id_len;
+  SilcBuffer client_id_list;
+  SilcBuffer client_mode_list;
+  unsigned char lc[4];
+  uint32 list_count = 0;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  char *channel_name;
 
-  /* Send Channel ID to the client */
-  if (!cmd->pending) {
-    id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-    sp_buf = silc_command_encode_status_payload(SILC_STATUS_OK, NULL, 0);
-    if (!channel->topic)
-      packet = 
-       silc_command_encode_payload_va(SILC_COMMAND_JOIN, 3,
-                                      sp_buf->data, sp_buf->len,
-                                      channel_name, strlen(channel_name),
-                                      id_string, SILC_ID_CHANNEL_LEN);
-    else
-      packet = 
-       silc_command_encode_payload_va(SILC_COMMAND_JOIN, 4,
-                                      sp_buf->data, sp_buf->len,
-                                      channel_name, strlen(channel_name),
-                                      id_string, SILC_ID_CHANNEL_LEN,
-                                      channel->topic, 
-                                      strlen(channel->topic));
-
-    if (cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED) {
-      void *id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
-      silc_server_packet_send_dest(cmd->server, cmd->sock, 
-                                  SILC_PACKET_COMMAND_REPLY, 0,
-                                  id, cmd->packet->src_id_type,
-                                  packet->data, packet->len, FALSE);
-      silc_free(id);
-    } else
-      silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0, 
-                             packet->data, packet->len, FALSE);
-    
-    silc_buffer_free(packet);
-    silc_free(sp_buf);
+  SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_USERS, cmd, 1, 2);
+
+  /* Get Channel ID */
+  channel_id = silc_argument_get_arg_type(cmd->args, 1, &channel_id_len);
+
+  /* Get channel name */
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+
+  if (!channel_id && !channel_name) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                         SILC_STATUS_ERR_NO_CHANNEL_ID);
+    goto out;
   }
 
-  /* Send channel key to the client. Client cannot start transmitting
-     to the channel until we have sent the key. */
-  if (!cmd->pending) {
-    tmp_len = strlen(channel->channel_key->cipher->name);
-    packet = 
-      silc_channel_key_encode_payload(SILC_ID_CHANNEL_LEN, 
-                                     id_string, tmp_len, 
-                                     channel->channel_key->cipher->name,
-                                     channel->key_len, channel->key);
-    
-    silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
-                           packet->data, packet->len, FALSE);
-    silc_buffer_free(packet);
+  if (channel_id) {
+    id = silc_id_payload_parse_id(channel_id, channel_id_len);
+    if (!id) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_CHANNEL_ID);
+      goto out;
+    }
   }
 
-  if (id_string)
-    silc_free(id_string);
+  /* If we are server and we don't know about this channel we will send
+     the command to our router. If we know about the channel then we also
+     have the list of users already. */
+  if (id)
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  else
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+
+  if (!channel || channel->disabled) {
+    if (server->server_type != SILC_ROUTER && !server->standalone &&
+       !cmd->pending) {
+      SilcBuffer tmpbuf;
+      
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+      
+      /* Send USERS command */
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+      
+      /* Reprocess this packet after received reply */
+      silc_server_command_pending(server, SILC_COMMAND_USERS, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_users,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, ident);
+      silc_buffer_free(tmpbuf);
+      silc_free(id);
+      goto out;
+    }
 
-  /* Finally, send notify message to all clients on the channel about
-     new user on the channel. */
-  if (!(cmd->packet->flags & SILC_PACKET_FLAG_FORWARDED)) {
-    if (!cmd->pending) {
-      silc_server_send_notify_to_channel(server, channel,
-                                        "%s (%s@%s) has joined channel %s",
-                                        client->nickname, client->username,
-                                        sock->hostname ? sock->hostname :
-                                        sock->ip, channel_name);
-    } else {
-      /* This is pending command request. Send the notify after we have
-        received the key for the channel from the router. */
-      JoinInternalContext *ctx = silc_calloc(1, sizeof(*ctx));
-      ctx->channel_name = channel_name;
-      ctx->nickname = client->nickname;
-      ctx->username = client->username;
-      ctx->hostname = sock->hostname ? sock->hostname : sock->ip;
-      ctx->channel = channel;
-      ctx->server = server;
-      silc_task_register(server->timeout_queue, sock->sock,
-                        silc_server_command_join_notify, ctx,
-                        0, 100000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+    /* Check the global list as well. */
+    if (id)
+      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    else
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+    if (!channel) {
+      /* Channel really does not exist */
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
     }
   }
 
+  /* If the channel is private or secret do not send anything, unless the
+     user requesting this command is on the channel. */
+  if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    if (channel->mode & (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)
+       && !silc_server_client_on_channel(cmd->sock->user_data, channel)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  } else {
+    if (channel->mode & 
+       (SILC_CHANNEL_MODE_PRIVATE | SILC_CHANNEL_MODE_SECRET)) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_USERS,
+                                           SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+      goto out;
+    }
+  }
+
+  /* Get the users list */
+  silc_server_get_users_on_channel(server, channel, &client_id_list,
+                                  &client_mode_list, &list_count);
+
+  /* List count */
+  SILC_PUT32_MSB(list_count, lc);
+
+  /* Send reply */
+  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_USERS,
+                                               SILC_STATUS_OK, ident, 4,
+                                               2, idp->data, idp->len,
+                                               3, lc, 4,
+                                               4, client_id_list->data,
+                                               client_id_list->len,
+                                               5, client_mode_list->data,
+                                               client_mode_list->len);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+    
+  silc_buffer_free(idp);
+  silc_buffer_free(packet);
+  silc_buffer_free(client_id_list);
+  silc_buffer_free(client_mode_list);
+  silc_free(id);
+
  out:
   silc_server_command_free(cmd);
-#undef LCC
-#undef LCCC
 }
 
-/* Server side of command MOTD. Sends servers current "message of the
-   day" to the client. */
+/* Server side of command GETKEY. This fetches the client's public key
+   from the server where to the client is connected. */
 
-SILC_SERVER_CMD_FUNC(motd)
+SILC_SERVER_CMD_FUNC(getkey)
 {
+  SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+  SilcServer server = cmd->server;
+  SilcBuffer packet;
+  SilcClientEntry client;
+  SilcServerEntry server_entry;
+  SilcClientID *client_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcIDPayload idp = NULL;
+  uint16 ident = silc_command_get_ident(cmd->payload);
+  unsigned char *tmp, *pkdata;
+  uint32 tmp_len, pklen;
+  SilcBuffer pk = NULL;
+  SilcIdType id_type;
+  SilcPublicKey public_key;
 
   SILC_LOG_DEBUG(("Start"));
 
-}
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  if (!tmp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
+  idp = silc_id_payload_parse(tmp, tmp_len);
+  if (!idp) {
+    silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
+                                         SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+    goto out;
+  }
 
-SILC_SERVER_CMD_FUNC(umode)
-{
-}
+  id_type = silc_id_payload_get_type(idp);
+  if (id_type == SILC_ID_CLIENT) {
+    client_id = silc_id_payload_get_id(idp);
+
+    /* If the client is not found from local list there is no chance it
+       would be locally connected client so send the command further. */
+    client = silc_idlist_find_client_by_id(server->local_list, 
+                                          client_id, TRUE, NULL);
+    if (!client)
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, TRUE, NULL);
+    
+    if ((!client && !cmd->pending && !server->standalone) ||
+       (client && !client->connection && !cmd->pending) ||
+       (client && !client->data.public_key && !cmd->pending)) {
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
+      SilcSocketConnection dest_sock;
+      
+      dest_sock = silc_server_get_client_route(server, NULL, 0, 
+                                              client_id, NULL);
+      if (!dest_sock)
+       goto out;
+      
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+      
+      silc_server_packet_send(server, dest_sock,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+      
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_getkey,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    }
 
-SILC_SERVER_CMD_FUNC(cmode)
-{
-}
+    if (!client) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
+                                           SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+      goto out;
+    }
 
-SILC_SERVER_CMD_FUNC(kick)
-{
-}
+    /* The client is locally connected, just get the public key and
+       send it back. If they key does not exist then do not send it, 
+       send just OK reply */
+    public_key = client->data.public_key;
+    if (!public_key) {
+      pkdata = NULL;
+      pklen = 0;
+    } else {
+      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
+      pk = silc_buffer_alloc(4 + tmp_len);
+      silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
+      silc_buffer_format(pk,
+                        SILC_STR_UI_SHORT(tmp_len),
+                        SILC_STR_UI_SHORT(SILC_SKE_PK_TYPE_SILC),
+                        SILC_STR_UI_XNSTRING(tmp, tmp_len),
+                        SILC_STR_END);
+      silc_free(tmp);
+      pkdata = pk->data;
+      pklen = pk->len;
+    }
+  } else if (id_type == SILC_ID_SERVER) {
+    server_id = silc_id_payload_get_id(idp);
+
+    /* If the server is not found from local list there is no chance it
+       would be locally connected server so send the command further. */
+    server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                server_id, TRUE, NULL);
+    if (!server_entry)
+      server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                  server_id, TRUE, NULL);
+    
+    if (server_entry != server->id_entry &&
+       ((!server_entry && !cmd->pending && !server->standalone) ||
+        (server_entry && !server_entry->connection && !cmd->pending &&
+         !server->standalone) ||
+        (server_entry && !server_entry->data.public_key && !cmd->pending &&
+         !server->standalone))) {
+      SilcBuffer tmpbuf;
+      uint16 old_ident;
+      
+      old_ident = silc_command_get_ident(cmd->payload);
+      silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+      tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+      
+      silc_server_packet_send(server, server->router->connection,
+                             SILC_PACKET_COMMAND, cmd->packet->flags,
+                             tmpbuf->data, tmpbuf->len, TRUE);
+      
+      /* Reprocess this packet after received reply from router */
+      silc_server_command_pending(server, SILC_COMMAND_GETKEY, 
+                                 silc_command_get_ident(cmd->payload),
+                                 silc_server_command_getkey,
+                                 silc_server_command_dup(cmd));
+      cmd->pending = TRUE;
+      silc_command_set_ident(cmd->payload, old_ident);
+      silc_buffer_free(tmpbuf);
+      goto out;
+    }
 
-SILC_SERVER_CMD_FUNC(restart)
-{
-}
-SILC_SERVER_CMD_FUNC(close)
-{
-}
-SILC_SERVER_CMD_FUNC(die)
-{
-}
-SILC_SERVER_CMD_FUNC(silcoper)
-{
-}
+    if (!server_entry) {
+      silc_server_command_send_status_reply(cmd, SILC_COMMAND_GETKEY,
+                                           SILC_STATUS_ERR_NO_SUCH_SERVER_ID);
+      goto out;
+    }
 
-SILC_SERVER_CMD_FUNC(leave)
-{
-}
+    /* If they key does not exist then do not send it, send just OK reply */
+    public_key = (!server_entry->data.public_key ? 
+                 (server_entry == server->id_entry ? server->public_key :
+                  NULL) : server_entry->data.public_key);
+    if (!public_key) {
+      pkdata = NULL;
+      pklen = 0;
+    } else {
+      tmp = silc_pkcs_public_key_encode(public_key, &tmp_len);
+      pk = silc_buffer_alloc(4 + tmp_len);
+      silc_buffer_pull_tail(pk, SILC_BUFFER_END(pk));
+      silc_buffer_format(pk,
+                        SILC_STR_UI_SHORT(tmp_len),
+                        SILC_STR_UI_SHORT(SILC_SKE_PK_TYPE_SILC),
+                        SILC_STR_UI_XNSTRING(tmp, tmp_len),
+                        SILC_STR_END);
+      silc_free(tmp);
+      pkdata = pk->data;
+      pklen = pk->len;
+    }
+  } else {
+    goto out;
+  }
 
-SILC_SERVER_CMD_FUNC(names)
-{
+  tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+  packet = silc_command_reply_payload_encode_va(SILC_COMMAND_GETKEY,
+                                               SILC_STATUS_OK, ident, 
+                                               pkdata ? 2 : 1,
+                                               2, tmp, tmp_len,
+                                               3, pkdata, pklen);
+  silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+  if (pk)
+    silc_buffer_free(pk);
+
+ out:
+  if (idp)
+    silc_id_payload_free(idp);
+  silc_free(client_id);
+  silc_free(server_id);
+  silc_server_command_free(cmd);
 }
index d7ed494402f85815512825d49220991aab85bb6e..43195e8a2febdffa16dcfd919ae5971e895c8e54 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -52,25 +52,24 @@ typedef struct {
   SilcServer server;
   SilcSocketConnection sock;
   SilcCommandPayload payload;
+  SilcArgumentPayload args;
   SilcPacketContext *packet;
-  int pending;
+  int pending;                 /* Command is being re-processed when TRUE */
+  int users;                   /* Reference counter */
 } *SilcServerCommandContext;
 
 /* Structure holding pending commands. If command is pending it will be
-   executed after command reply has been received and executed. 
-   Pending commands are used in cases where the original command request
-   had to be forwarded to router. After router replies the pending
-   command is re-executed. */
+   executed after command reply has been received and executed. */
 typedef struct SilcServerCommandPendingStruct {
+  SilcServer server;
   SilcCommand reply_cmd;
-  void *context;
   SilcCommandCb callback;
-
+  void *context;
+  uint16 ident;
   struct SilcServerCommandPendingStruct *next;
 } SilcServerCommandPending;
 
-/* List of pending commands */
-extern SilcServerCommandPending *silc_command_pending;
+#include "command_reply.h"
 
 /* Macros */
 
@@ -78,60 +77,48 @@ extern SilcServerCommandPending *silc_command_pending;
 #define SILC_SERVER_CMD(func, cmd, flags) \
 { silc_server_command_##func, SILC_COMMAND_##cmd, flags }
 
-/* Macro used to declare command functions */
+/* Macro used to declare command functions. The `context' will be the
+   SilcServerCommandContext and the `context2' is the 
+   SilcServerCommandReplyContext if this function is called from the
+   command reply as pending command callback. Otherwise `context2' 
+   is NULL. */
 #define SILC_SERVER_CMD_FUNC(func) \
-void silc_server_command_##func(void *context)
-
-/* Macro used to execute commands */
-#define SILC_SERVER_COMMAND_EXEC(ctx)                          \
-do {                                                           \
-  SilcServerCommand *cmd;                                      \
-                                                               \
-  for (cmd = silc_command_list; cmd->cb; cmd++)                        \
-    if (cmd->cmd == silc_command_get(ctx->payload)) {          \
-      cmd->cb(ctx);                                            \
-      break;                                                   \
-    }                                                          \
-                                                               \
-  if (cmd == NULL) {                                           \
-    SILC_LOG_ERROR(("Unknown command, packet dropped"));       \
-    silc_free(ctx);                                            \
-    return;                                                    \
-  }                                                            \
-} while(0)
-
-/* Checks for pending commands */
-#define SILC_SERVER_COMMAND_CHECK_PENDING(ctx)         \
-do {                                                   \
-  if (silc_command_pending) {                          \
-    SilcServerCommandPending *r;                       \
-    SilcCommand cmd;                                   \
-                                                       \
-    cmd = silc_command_get(payload);                   \
-    for (r = silc_command_pending; r; r = r->next) {   \
-      if (r->reply_cmd == cmd) {                       \
-       ctx->context = r->context;                      \
-       ctx->callback = r->callback;                    \
-       break;                                          \
-      }                                                        \
-    }                                                  \
-  }                                                    \
-} while(0)
-
-/* Executed pending command */
-#define SILC_SERVER_COMMAND_EXEC_PENDING(ctx, cmd)     \
-do {                                                   \
-  if (ctx->callback) {                                 \
-    (*ctx->callback)(ctx->context);                    \
-    silc_server_command_pending_del(cmd);              \
-  }                                                    \
+void silc_server_command_##func(void *context, void *context2)
+
+/* Executed pending command. The first argument to the callback function
+   is the user specified context. The second argument is always the
+   SilcServerCommandReply context. */
+#define SILC_SERVER_PENDING_EXEC(ctx, cmd)                             \
+do {                                                                   \
+  int _i;                                                              \
+  for (_i = 0; _i < ctx->callbacks_count; _i++)                                \
+    if (ctx->callbacks[_i].callback)                                   \
+      (*ctx->callbacks[_i].callback)(ctx->callbacks[_i].context, ctx); \
+  silc_server_command_pending_del(ctx->server, cmd, ctx->ident);       \
 } while(0)
 
 /* Prototypes */
-void silc_server_command_pending(SilcCommand reply_cmd,
+void silc_server_command_process(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet);
+SilcServerCommandContext silc_server_command_alloc();
+void silc_server_command_free(SilcServerCommandContext ctx);
+SilcServerCommandContext 
+silc_server_command_dup(SilcServerCommandContext ctx);
+bool silc_server_command_pending(SilcServer server,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
                                 SilcCommandCb callback,
                                 void *context);
-void silc_server_command_pending_del(SilcCommand reply_cmd);
+void silc_server_command_pending_del(SilcServer server,
+                                    SilcCommand reply_cmd,
+                                    uint16 ident);
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+                                 SilcServerCommandReplyContext ctx,
+                                 SilcCommand command, 
+                                 uint16 ident,
+                                 uint32 *callbacks_count);
 SILC_SERVER_CMD_FUNC(whois);
 SILC_SERVER_CMD_FUNC(whowas);
 SILC_SERVER_CMD_FUNC(identify);
@@ -152,13 +139,15 @@ SILC_SERVER_CMD_FUNC(join);
 SILC_SERVER_CMD_FUNC(motd);
 SILC_SERVER_CMD_FUNC(umode);
 SILC_SERVER_CMD_FUNC(cmode);
+SILC_SERVER_CMD_FUNC(cumode);
 SILC_SERVER_CMD_FUNC(kick);
 SILC_SERVER_CMD_FUNC(ignore);
-SILC_SERVER_CMD_FUNC(restart);
+SILC_SERVER_CMD_FUNC(ban);
 SILC_SERVER_CMD_FUNC(close);
-SILC_SERVER_CMD_FUNC(die);
+SILC_SERVER_CMD_FUNC(shutdown);
 SILC_SERVER_CMD_FUNC(silcoper);
 SILC_SERVER_CMD_FUNC(leave);
-SILC_SERVER_CMD_FUNC(names);
+SILC_SERVER_CMD_FUNC(users);
+SILC_SERVER_CMD_FUNC(getkey);
 
 #endif
index 815a858509f92ab281d2b9c21b5bcccc25938d62..68e6143337f85ff6aaa16c92a911ce1a0d80b35f 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 #include "command_reply.h"
 
+/* All functions that call the COMMAND_CHECK_STATUS or the
+   COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
+
+#define COMMAND_CHECK_STATUS                                             \
+do {                                                                     \
+  SILC_LOG_DEBUG(("Start"));                                             \
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
+  if (status != SILC_STATUS_OK)                                                  \
+    goto out;                                                            \
+} while(0)
+
+#define COMMAND_CHECK_STATUS_LIST                                        \
+do {                                                                     \
+  SILC_LOG_DEBUG(("Start"));                                             \
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
+  if (status != SILC_STATUS_OK &&                                        \
+      status != SILC_STATUS_LIST_START &&                                \
+      status != SILC_STATUS_LIST_ITEM &&                                 \
+      status != SILC_STATUS_LIST_END)                                    \
+    goto out;                                                            \
+} while(0)
+
 /* Server command reply list. Not all commands have reply function as
-   they are never sent as forwarded command packets by server. More
-   maybe added later if need appears. */
+   they are never sent by server. More maybe added later if need appears. */
 SilcServerCommandReply silc_command_reply_list[] =
 {
+  SILC_SERVER_CMD_REPLY(whois, WHOIS),
+  SILC_SERVER_CMD_REPLY(whowas, WHOWAS),
+  SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
+  SILC_SERVER_CMD_REPLY(info, INFO),
+  SILC_SERVER_CMD_REPLY(motd, MOTD),
   SILC_SERVER_CMD_REPLY(join, JOIN),
+  SILC_SERVER_CMD_REPLY(users, USERS),
+  SILC_SERVER_CMD_REPLY(getkey, GETKEY),
+  SILC_SERVER_CMD_REPLY(list, LIST),
 
   { NULL, 0 },
 };
@@ -46,11 +68,16 @@ void silc_server_command_reply_process(SilcServer server,
                                       SilcSocketConnection sock,
                                       SilcBuffer buffer)
 {
+  SilcServerCommandReply *cmd;
   SilcServerCommandReplyContext ctx;
   SilcCommandPayload payload;
+  SilcCommand command;
+  uint16 ident;
+
+  SILC_LOG_DEBUG(("Start"));
 
   /* Get command reply payload from packet */
-  payload = silc_command_parse_payload(buffer);
+  payload = silc_command_payload_parse(buffer->data, buffer->len);
   if (!payload) {
     /* Silently ignore bad reply packet */
     SILC_LOG_DEBUG(("Bad command reply packet"));
@@ -61,14 +88,29 @@ void silc_server_command_reply_process(SilcServer server,
      command reply routine receiving it. */
   ctx = silc_calloc(1, sizeof(*ctx));
   ctx->server = server;
-  ctx->sock = sock;
+  ctx->sock = silc_socket_dup(sock);
   ctx->payload = payload;
+  ctx->args = silc_command_get_args(ctx->payload);
+  ident = silc_command_get_ident(ctx->payload);
       
   /* Check for pending commands and mark to be exeucted */
-  SILC_SERVER_COMMAND_CHECK_PENDING(ctx);
-  
+  ctx->callbacks = 
+    silc_server_command_pending_check(server, ctx, 
+                                     silc_command_get(ctx->payload), 
+                                     ident, &ctx->callbacks_count);
+
   /* Execute command reply */
-  SILC_SERVER_COMMAND_REPLY_EXEC(ctx);
+  command = silc_command_get(ctx->payload);
+  for (cmd = silc_command_reply_list; cmd->cb; cmd++)
+    if (cmd->cmd == command)
+      break;
+
+  if (cmd == NULL || !cmd->cb) {
+    silc_server_command_reply_free(ctx);
+    return;
+  }
+
+  cmd->cb(ctx, NULL);
 }
 
 /* Free command reply context and its internals. */
@@ -76,11 +118,650 @@ void silc_server_command_reply_process(SilcServer server,
 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
 {
   if (cmd) {
-    silc_command_free_payload(cmd->payload);
+    silc_command_payload_free(cmd->payload);
+    if (cmd->sock)
+      silc_socket_free(cmd->sock); /* Decrease the reference counter */
+    silc_free(cmd->callbacks);
     silc_free(cmd);
   }
 }
 
+/* Caches the received WHOIS information. */
+
+static char
+silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
+{
+  SilcServer server = cmd->server;
+  unsigned char *tmp, *id_data;
+  char *nickname, *username, *realname, *servername = NULL;
+  unsigned char *fingerprint;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  char global = FALSE;
+  char *nick;
+  uint32 mode = 0, len, id_len, flen;
+  int expire = 0;
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
+  username = silc_argument_get_arg_type(cmd->args, 4, &len);
+  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
+  if (!id_data || !nickname || !username || !realname) {
+    SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
+                   nickname ? nickname : "",
+                   username ? username : "",
+                   realname ? realname : ""));
+    return FALSE;
+  }
+
+  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  if (tmp)
+    SILC_GET32_MSB(mode, tmp);
+
+  client_id = silc_id_payload_parse_id(id_data, id_len);
+  if (!client_id)
+    return FALSE;
+
+  fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen);
+
+  /* Check if we have this client cached already. */
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id, 
+                                        FALSE, NULL);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, client_id, 
+                                          FALSE, NULL);
+    global = TRUE;
+  }
+
+  if (!client) {
+    /* If router did not find such Client ID in its lists then this must
+       be bogus client or some router in the net is buggy. */
+    if (server->server_type != SILC_SERVER)
+      return FALSE;
+
+    /* Take hostname out of nick string if it includes it. */
+    silc_parse_userfqdn(nickname, &nick, &servername);
+
+    /* We don't have that client anywhere, add it. The client is added
+       to global list since server didn't have it in the lists so it must be 
+       global. */
+    client = silc_idlist_add_client(server->global_list, nick, 
+                                   strdup(username), 
+                                   strdup(realname), client_id, 
+                                   cmd->sock->user_data, NULL, 
+                                   time(NULL) + 300);
+    if (!client) {
+      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+      return FALSE;
+    }
+
+    client->data.status |= 
+      (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED);
+    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+    client->mode = mode;
+    client->servername = servername;
+  } else {
+    /* We have the client already, update the data */
+
+    SILC_LOG_DEBUG(("Updating client data"));
+
+    /* Take hostname out of nick string if it includes it. */
+    silc_parse_userfqdn(nickname, &nick, &servername);
+
+    /* Remove the old cache entry  */
+    silc_idcache_del_by_context(global ? server->global_list->clients :
+                               server->local_list->clients, client);
+
+    silc_free(client->nickname);
+    silc_free(client->username);
+    silc_free(client->userinfo);
+    silc_free(client->servername);
+    
+    client->nickname = nick;
+    client->username = strdup(username);
+    client->userinfo = strdup(realname);
+    client->servername = servername;
+    client->mode = mode;
+    client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+
+    /* If client is global and is not on any channel then add that we'll
+       expire the entry after a while. */
+    if (global && !silc_hash_table_count(client->channels) &&
+       server->server_type == SILC_SERVER)
+      expire = time(NULL) + 300;
+
+    /* Create new cache entry */
+    silc_idcache_add(global ? server->global_list->clients :
+                    server->local_list->clients, nick, client->id, 
+                    client, expire, NULL); 
+    silc_free(client_id);
+  }
+
+  if (fingerprint && flen == sizeof(client->data.fingerprint))
+    memcpy(client->data.fingerprint, fingerprint, flen);
+
+  return TRUE;
+}
+
+/* Reiceved reply for WHOIS command. We sent the whois request to our
+   primary router, if we are normal server, and thus has now received reply
+   to the command. We will figure out what client originally sent us the
+   command and will send the reply to it.  If we are router we will figure
+   out who server sent us the command and send reply to that one. */
+
+SILC_SERVER_CMD_REPLY_FUNC(whois)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  if (!silc_server_command_reply_whois_save(cmd))
+    goto out;
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_server_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
+      cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) {
+    SilcClientEntry client;
+    uint32 tmp_len;
+    unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
+                       "the entry from cache"));
+       client = silc_idlist_find_client_by_id(server->global_list, 
+                                              client_id, FALSE, NULL);
+       if (client) {
+         silc_server_remove_from_channels(server, NULL, client, TRUE, 
+                                          NULL, TRUE);
+         silc_idlist_del_client(server->global_list, client);
+       }
+       silc_free(client_id);
+      }
+    }
+  }
+
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+  silc_server_command_reply_free(cmd);
+}
+
+/* Caches the received WHOWAS information for a short period of time. */
+
+static char
+silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
+{
+  SilcServer server = cmd->server;
+  uint32 len, id_len;
+  unsigned char *id_data;
+  char *nickname, *username, *realname, *servername = NULL;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache = NULL;
+  char *nick;
+  int global = FALSE;
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
+  username = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (!id_data || !nickname || !username)
+    return FALSE;
+
+  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
+
+  client_id = silc_id_payload_parse_id(id_data, id_len);
+  if (!client_id)
+    return FALSE;
+
+  /* Check if we have this client cached already. */
+
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        FALSE, &cache);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, FALSE, &cache);
+    global = TRUE;
+  }
+
+  if (!client) {
+    /* If router did not find such Client ID in its lists then this must
+       be bogus client or some router in the net is buggy. */
+    if (server->server_type != SILC_SERVER)
+      return FALSE;
+
+    /* Take hostname out of nick string if it includes it. */
+    silc_parse_userfqdn(nickname, &nick, &servername);
+
+    /* We don't have that client anywhere, add it. The client is added
+       to global list since server didn't have it in the lists so it must be 
+       global. */
+    client = silc_idlist_add_client(server->global_list, nick,
+                                   strdup(username), strdup(realname), 
+                                   silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                   cmd->sock->user_data, NULL,
+                                   SILC_ID_CACHE_EXPIRE_DEF);
+    if (!client) {
+      SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+      return FALSE;
+    }
+
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; 
+    client->servername = servername;
+  } else {
+    /* We have the client already, update the data */
+
+    /* Take hostname out of nick string if it includes it. */
+    silc_parse_userfqdn(nickname, &nick, &servername);
+
+    silc_free(client->nickname);
+    silc_free(client->username);
+    
+    client->nickname = nick;
+    client->username = strdup(username);
+    client->servername = servername;
+
+    /* Remove the old cache entry and create a new one */
+    silc_idcache_del_by_context(global ? server->global_list->clients :
+                               server->local_list->clients, client);
+    silc_idcache_add(global ? server->global_list->clients :
+                    server->local_list->clients, nick, client->id, 
+                    client, 0, NULL);
+  }
+
+  silc_free(client_id);
+
+  return TRUE;
+}
+
+/* Received reply for WHOWAS command. Cache the client information only for
+   a short period of time. */
+
+SILC_SERVER_CMD_REPLY_FUNC(whowas)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  if (!silc_server_command_reply_whowas_save(cmd))
+    goto out;
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_server_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
+  silc_server_command_reply_free(cmd);
+}
+
+/* Caches the received IDENTIFY information. */
+
+static char
+silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
+{
+  SilcServer server = cmd->server;
+  uint32 len, id_len;
+  unsigned char *id_data;
+  char *name, *info;
+  SilcClientID *client_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcClientEntry client;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel;
+  char global = FALSE;
+  char *nick = NULL;
+  SilcIDPayload idp = NULL;
+  SilcIdType id_type;
+  int expire = 0;
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
+  if (!id_data)
+    return FALSE;
+  idp = silc_id_payload_parse(id_data, id_len);
+  if (!idp)
+    return FALSE;
+
+  name = silc_argument_get_arg_type(cmd->args, 3, &len);
+  info = silc_argument_get_arg_type(cmd->args, 4, &len);
+
+  id_type = silc_id_payload_get_type(idp);
+
+  switch (id_type) {
+  case SILC_ID_CLIENT:
+    client_id = silc_id_payload_get_id(idp);
+    if (!client_id)
+      goto error;
+
+    SILC_LOG_DEBUG(("Received client information"));
+
+    client = silc_idlist_find_client_by_id(server->local_list, 
+                                          client_id, FALSE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->global_list, client_id,
+                                            FALSE, NULL);
+      global = TRUE;
+    }
+    if (!client) {
+      /* If router did not find such Client ID in its lists then this must
+        be bogus client or some router in the net is buggy. */
+      if (server->server_type != SILC_SERVER)
+       goto error;
+
+      /* Take nickname */
+      if (name)
+       silc_parse_userfqdn(name, &nick, NULL);
+
+      /* We don't have that client anywhere, add it. The client is added
+        to global list since server didn't have it in the lists so it must be 
+        global. */
+      client = silc_idlist_add_client(server->global_list, nick, 
+                                     info ? strdup(info) : NULL, NULL,
+                                     client_id, cmd->sock->user_data, NULL,
+                                     time(NULL) + 300);
+      if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+       goto error;
+      }
+      client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+      client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+      client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+    } else {
+      /* We have the client already, update the data */
+      
+      SILC_LOG_DEBUG(("Updating client data"));
+      
+      /* Take nickname */
+      if (name) {
+       silc_parse_userfqdn(name, &nick, NULL);
+
+       /* Remove the old cache entry */
+       silc_idcache_del_by_context(global ? server->global_list->clients :
+                                   server->local_list->clients, client);
+
+       silc_free(client->nickname);
+       client->nickname = nick;
+      }
+      
+      if (info) {
+       silc_free(client->username);
+       client->username = strdup(info);
+      }
+
+      client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+      client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+      
+      if (name) {
+       /* If client is global and is not on any channel then add that we'll
+          expire the entry after a while. */
+       if (global && !silc_hash_table_count(client->channels) &&
+           server->server_type == SILC_SERVER)
+         expire = time(NULL) + 300;
+
+       /* Add new cache entry */
+       silc_idcache_add(global ? server->global_list->clients :
+                        server->local_list->clients, nick, client->id, 
+                        client, expire, NULL);
+      }
+
+      silc_free(client_id);
+    }
+
+    break;
+
+  case SILC_ID_SERVER:
+    if (!name)
+      goto error;
+
+    server_id = silc_id_payload_get_id(idp);
+    if (!server_id)
+      goto error;
+
+    SILC_LOG_DEBUG(("Received server information"));
+
+    server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                server_id, FALSE, NULL);
+    if (!server_entry)
+      server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                  server_id, FALSE, NULL);
+    if (!server_entry) {
+      /* If router did not find such Server ID in its lists then this must
+        be bogus server or some router in the net is buggy. */
+      if (server->server_type != SILC_SERVER)
+       goto error;
+      
+      /* We don't have that server anywhere, add it. */
+      server_entry = silc_idlist_add_server(server->global_list, 
+                                           strdup(name), 0,
+                                           server_id, NULL, NULL);
+      if (!server_entry) {
+       silc_free(server_id);
+       goto error;
+      }
+      server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+      server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+      server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
+      server_id = NULL;
+    }
+
+    silc_free(server_id);
+    break;
+
+  case SILC_ID_CHANNEL:
+    if (!name)
+      goto error;
+
+    channel_id = silc_id_payload_get_id(idp);
+    if (!channel_id)
+      goto error;
+
+    SILC_LOG_DEBUG(("Received channel information"));
+
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              name, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                name, NULL);
+    if (!channel) {
+      /* If router did not find such Channel ID in its lists then this must
+        be bogus channel or some router in the net is buggy. */
+      if (server->server_type != SILC_SERVER)
+       goto error;
+      
+      /* We don't have that server anywhere, add it. */
+      channel = silc_idlist_add_channel(server->global_list, strdup(name),
+                                       SILC_CHANNEL_MODE_NONE, channel_id, 
+                                       server->router, NULL, NULL, 0);
+      if (!channel) {
+       silc_free(channel_id);
+       goto error;
+      }
+      channel_id = NULL;
+    }
+
+    silc_free(channel_id);
+    break;
+  }
+
+  silc_id_payload_free(idp);
+  return TRUE;
+
+ error:
+  silc_id_payload_free(idp);
+  return FALSE;
+}
+
+/* Received reply for forwarded IDENTIFY command. We have received the
+   requested identify information now and we will cache it. After this we
+   will call the pending command so that the requestee gets the information
+   after all. */
+
+SILC_SERVER_CMD_REPLY_FUNC(identify)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  if (!silc_server_command_reply_identify_save(cmd))
+    goto out;
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_server_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
+      cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) {
+    SilcClientEntry client;
+    uint32 tmp_len;
+    unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
+                       "the entry from cache"));
+       client = silc_idlist_find_client_by_id(server->global_list, 
+                                              client_id, FALSE, NULL);
+       if (client) {
+         silc_server_remove_from_channels(server, NULL, client, TRUE, 
+                                          NULL, TRUE);
+         silc_idlist_del_client(server->global_list, client);
+       }
+       silc_free(client_id);
+      }
+    }
+  }
+
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
+  silc_server_command_reply_free(cmd);
+}
+
+/* Received reply fro INFO command. Cache the server and its information */
+
+SILC_SERVER_CMD_REPLY_FUNC(info)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+  SilcServerEntry entry;
+  SilcServerID *server_id;
+  uint32 tmp_len;
+  unsigned char *tmp, *name;
+
+  COMMAND_CHECK_STATUS;
+
+  /* Get Server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+  server_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!server_id)
+    goto out;
+
+  /* Get the name */
+  name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (tmp_len > 256)
+    goto out;
+
+  entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
+                                       FALSE, NULL);
+  if (!entry) {
+    entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
+                                         FALSE, NULL);
+    if (!entry) {
+      /* Add the server to global list */
+      server_id = silc_id_dup(server_id, SILC_ID_SERVER);
+      entry = silc_idlist_add_server(server->global_list, name, 0,
+                                    server_id, NULL, NULL);
+      if (!entry) {
+       silc_free(server_id);
+       goto out;
+      }
+      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+    }
+  }
+
+  /* Get the info string */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+  if (tmp_len > 256)
+    tmp = NULL;
+
+  entry->server_info = tmp ? strdup(tmp) : NULL;
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
+  silc_server_command_reply_free(cmd);
+}
+
+/* Received reply fro MOTD command. */
+
+SILC_SERVER_CMD_REPLY_FUNC(motd)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+  SilcServerEntry entry = NULL;
+  SilcServerID *server_id;
+  uint32 tmp_len;
+  unsigned char *tmp;
+
+  COMMAND_CHECK_STATUS;
+
+  /* Get Server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+  server_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!server_id)
+    goto out;
+
+  entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
+                                       TRUE, NULL);
+  if (!entry) {
+    entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
+                                         TRUE, NULL);
+    if (!entry)
+      goto out;
+  }
+
+  /* Get the motd */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (tmp_len > 256)
+    tmp = NULL;
+
+  entry->motd = tmp;
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
+  silc_server_command_reply_free(cmd);
+
+  if (entry)
+    entry->motd = NULL;
+}
+
 /* Received reply for forwarded JOIN command. Router has created or joined
    the client to the channel. We save some channel information locally
    for future use. */
@@ -89,52 +770,455 @@ SILC_SERVER_CMD_REPLY_FUNC(join)
 {
   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
   SilcServer server = cmd->server;
+  SilcIDCacheEntry cache = NULL;
   SilcCommandStatus status;
   SilcChannelID *id;
-  SilcChannelList *entry;
-  unsigned int argc;
+  SilcClientID *client_id = NULL;
+  SilcChannelEntry entry;
+  SilcHmac hmac = NULL;
+  uint32 id_len, len, list_count;
   unsigned char *id_string;
   char *channel_name, *tmp;
+  uint32 mode, created;
+  SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  COMMAND_CHECK_STATUS;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Get channel name */
+  channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!channel_name)
+    goto out;
+
+  /* Get channel ID */
+  id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
+  if (!id_string)
+    goto out;
+
+  /* Get client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (!tmp)
+    goto out;
+  client_id = silc_id_payload_parse_id(tmp, len);
+  if (!client_id)
+    goto out;
 
-  tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
-  SILC_GET16_MSB(status, tmp);
-  if (status != SILC_STATUS_OK)
+  /* Get mode mask */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (!tmp)
     goto out;
+  SILC_GET32_MSB(mode, tmp);
 
-  /* Get channel name */
-  tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
+  /* Get created boolean value */
+  tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
   if (!tmp)
     goto out;
+  SILC_GET32_MSB(created, tmp);
+  if (created != 0 && created != 1)
+    goto out;
+
+  /* Get channel key */
+  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  if (tmp) {
+    keyp = silc_buffer_alloc(len);
+    silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+    silc_buffer_put(keyp, tmp, len);
+  }
+
+  /* Parse the Channel ID */
+  id = silc_id_payload_parse_id(id_string, id_len);
+  if (!id)
+    goto out;
+
+  /* Get hmac */
+  tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
+  if (tmp) {
+    if (!silc_hmac_alloc(tmp, NULL, &hmac))
+      goto out;
+  }
+
+  /* Get the list count */
+  tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(list_count, tmp);
+
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
+  if (!tmp)
+    goto out;
+
+  client_id_list = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(client_id_list, len);
+  silc_buffer_put(client_id_list, tmp, len);
+
+  /* Get client mode list */
+  tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
+  if (!tmp)
+    goto out;
+
+  client_mode_list = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(client_mode_list, len);
+  silc_buffer_put(client_mode_list, tmp, len);
+
+  /* See whether we already have the channel. */
+  entry = silc_idlist_find_channel_by_name(server->local_list, 
+                                          channel_name, &cache);
+  if (!entry) {
+    /* Add new channel */
+
+    SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
+                   (created == 0 ? "existing" : "created"), channel_name,
+                   silc_id_render(id, SILC_ID_CHANNEL)));
+
+    /* If the channel is found from global list we must move it to the
+       local list. */
+    entry = silc_idlist_find_channel_by_name(server->global_list, 
+                                            channel_name, &cache);
+    if (entry) {
+      if (entry->rekey) {
+       silc_schedule_task_del_by_context(server->schedule, entry->rekey);
+       SILC_LOG_ERROR(("global_list->channels: entry->rekey != NULL, inform Pekka now!!!"));
+      }
+      silc_idlist_del_channel(server->global_list, entry);
+    }
+
+    /* Add the channel to our local list. */
+    entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
+                                   SILC_CHANNEL_MODE_NONE, id, 
+                                   server->router, NULL, hmac, 0);
+    if (!entry) {
+      silc_free(id);
+      goto out;
+    }
+    server->stat.my_channels++;
+  } else {
+    /* The entry exists. */
+
+    /* If ID has changed, then update it to the cache too. */
+    if (!SILC_ID_CHANNEL_COMPARE(entry->id, id))
+      silc_idlist_replace_channel_id(server->local_list, entry->id, id);
+
+    entry->disabled = FALSE;
+
+    /* Remove the founder auth data if the mode is not set but we have
+       them in the entry */
+    if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
+      silc_pkcs_public_key_free(entry->founder_key);
+      silc_free(entry->founder_passwd);
+      entry->founder_passwd = NULL;
+    }
+  }
+
+  if (entry->hmac_name && hmac) {
+    silc_free(entry->hmac_name);
+    entry->hmac_name = strdup(silc_hmac_get_name(hmac));
+  }
+
+  /* Get the ban list */
+  tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
+  if (tmp) {
+    silc_free(entry->ban_list);
+    entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
+    memcpy(entry->ban_list, tmp, len);
+  }
+
+  /* Get the invite list */
+  tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
+  if (tmp) {
+    silc_free(entry->invite_list);
+    entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
+    memcpy(entry->invite_list, tmp, len);
+  }
+
+  /* Get the topic */
+  tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
+  if (tmp) {
+    silc_free(entry->topic);
+    entry->topic = strdup(tmp);
+  }
+
+  /* If channel was not created we know there is global users on the 
+     channel. */
+  entry->global_users = (created == 0 ? TRUE : FALSE);
+
+  /* If channel was just created the mask must be zero */
+  if (!entry->global_users && mode) {
+    SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
+                   "new channel, forcing it to zero", cmd->sock->hostname));
+    mode = 0;
+  }
+
+  /* Save channel mode */
+  entry->mode = mode;
+
+  /* Save channel key */
+  if (keyp) {
+    if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
+      silc_server_save_channel_key(server, keyp, entry);
+    silc_buffer_free(keyp);
+  }
+
+  /* Save the users to the channel */
+  silc_server_save_users_on_channel(server, cmd->sock, entry, 
+                                   client_id, client_id_list,
+                                   client_mode_list, list_count);
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
+  silc_free(client_id);
+  silc_server_command_reply_free(cmd);
+
+  if (client_id_list)
+    silc_buffer_free(client_id_list);
+  if (client_mode_list)
+    silc_buffer_free(client_mode_list);
+}
+
+SILC_SERVER_CMD_REPLY_FUNC(users)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id = NULL;
+  SilcBuffer client_id_list;
+  SilcBuffer client_mode_list;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  uint32 list_count;
+
+  COMMAND_CHECK_STATUS;
 
   /* Get channel ID */
-  id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
-  if (!id_string)
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp)
+    goto out;
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id)
     goto out;
 
-  channel_name = strdup(tmp);
+  /* Get channel entry */
+  channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                          channel_id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      SilcBuffer idp;
+
+      if (server->server_type != SILC_SERVER)
+       goto out;
+
+      idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+      silc_server_send_command(server, server->router->connection,
+                              SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
+                              1, 5, idp->data, idp->len);
+      silc_buffer_free(idp);
+
+      /* Register pending command callback. After we've received the channel
+        information we will reprocess this command reply by re-calling this
+        USERS command reply callback. */
+      silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
+                                 server->cmd_ident,
+                                 silc_server_command_reply_users, cmd);
+      return;
+    }
+  }
+
+  /* Get the list count */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(list_count, tmp);
+
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+  if (!tmp)
+    goto out;
+
+  client_id_list = silc_buffer_alloc(tmp_len);
+  silc_buffer_pull_tail(client_id_list, tmp_len);
+  silc_buffer_put(client_id_list, tmp, tmp_len);
+
+  /* Get client mode list */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
+  if (!tmp)
+    goto out;
+
+  client_mode_list = silc_buffer_alloc(tmp_len);
+  silc_buffer_pull_tail(client_mode_list, tmp_len);
+  silc_buffer_put(client_mode_list, tmp, tmp_len);
+
+  /* Save the users to the channel */
+  silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
+                                   client_id_list, client_mode_list, 
+                                   list_count);
+
+  silc_buffer_free(client_id_list);
+  silc_buffer_free(client_mode_list);
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
+  silc_free(channel_id);
+  silc_server_command_reply_free(cmd);
+}
+
+SILC_SERVER_CMD_REPLY_FUNC(getkey)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+  SilcClientEntry client = NULL;
+  SilcServerEntry server_entry = NULL;
+  SilcClientID *client_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcSKEPKType type;
+  unsigned char *tmp, *pk;
+  uint32 len;
+  uint16 pk_len;
+  SilcIDPayload idp = NULL;
+  SilcIdType id_type;
+  SilcPublicKey public_key = NULL;
+
+  COMMAND_CHECK_STATUS;
 
-  /* Add the channel to our local list. */
-  id = silc_id_str2id(id_string, SILC_ID_CHANNEL);
-  silc_idlist_add_channel(&server->local_list->channels, channel_name, 
-                         SILC_CHANNEL_MODE_NONE, id, 
-                         server->id_entry->router, NULL, &entry);
-  LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]), 
-                                          LCCC(channel_name[0]),
-                                          channel_name, SILC_ID_CHANNEL, 
-                                          (void *)id, (void *)entry);
-  entry->global_users = TRUE;
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+  idp = silc_id_payload_parse(tmp, len);
+  if (!idp)
+    goto out;
+
+  /* Get the public key payload */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (!tmp)
+    goto out;
+
+  /* Decode the public key */
+
+  SILC_GET16_MSB(pk_len, tmp);
+  SILC_GET16_MSB(type, tmp + 2);
+  pk = tmp + 4;
+
+  if (type != SILC_SKE_PK_TYPE_SILC)
+    goto out;
+
+  if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
+    goto out;
+
+  id_type = silc_id_payload_get_type(idp);
+  if (id_type == SILC_ID_CLIENT) {
+    client_id = silc_id_payload_get_id(idp);
+
+    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                          TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, TRUE, NULL);
+      if (!client)
+       goto out;
+    }
+
+    client->data.public_key = public_key;
+  } else if (id_type == SILC_ID_SERVER) {
+    server_id = silc_id_payload_get_id(idp);
+
+    server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
+                                                TRUE, NULL);
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                  server_id, TRUE, NULL);
+      if (!server_entry)
+       goto out;
+    }
+
+    server_entry->data.public_key = public_key;
+  } else {
+    goto out;
+  }
+
+ out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
+  if (idp)
+    silc_id_payload_free(idp);
+  silc_free(client_id);
+  silc_free(server_id);
+  if (public_key)
+    silc_pkcs_public_key_free(public_key);
+  silc_server_command_reply_free(cmd);
+}
+
+SILC_SERVER_CMD_REPLY_FUNC(list)
+{
+  SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
+  SilcServer server = cmd->server;
+  SilcCommandStatus status;
+  SilcChannelID *channel_id = NULL;
+  SilcChannelEntry channel;
+  SilcIDCacheEntry cache;
+  uint32 len;
+  unsigned char *tmp, *name, *topic;
+  uint32 usercount = 0;
+  bool global_list = FALSE;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
+
+  name = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (tmp)
+    SILC_GET32_MSB(usercount, tmp);
+
+  /* Add the channel entry if we do not have it already */
+  channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                            name, &cache);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                              name, &cache);
+    global_list = TRUE;
+  }
+  if (!channel) {
+    /* If router did not find such channel in its lists then this must
+       be bogus channel or some router in the net is buggy. */
+    if (server->server_type != SILC_SERVER)
+      goto out;
+    
+    channel = silc_idlist_add_channel(server->global_list, strdup(name),
+                                     SILC_CHANNEL_MODE_NONE, channel_id, 
+                                     server->router, NULL, NULL, 
+                                     time(NULL) + 60);
+    if (!channel)
+      goto out;
+    channel_id = NULL;
+  } else {
+    /* Found, update expiry */
+    if (global_list && server->server_type == SILC_SERVER)
+      cache->expire = time(NULL) + 60;
+  }
+
+  if (topic) {
+    silc_free(channel->topic);
+    channel->topic = strdup(topic);
+  }
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_server_command_reply_free(cmd);
+    return;
+  }
 
-  /* Execute pending JOIN command so that the client who originally
-     wanted to join the channel will be joined after all. */
-  SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
+  /* Now purge all old entries from the global list, otherwise we'll might
+     have non-existent entries for long periods of time in the cache. */
+  silc_idcache_purge(server->global_list->channels);
 
  out:
+  SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
+  silc_free(channel_id);
   silc_server_command_reply_free(cmd);
-#undef LCC
-#undef LCCC
 }
index 0e67aa287dbb2dddb0119ca1fe7df8f42d28c6e2..97222f34193b5c1c689bd066abc2b8abe2b56f4b 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
 #ifndef COMMAND_REPLY_H
 #define COMMAND_REPLY_H
 
+#include "command.h"
+
 /* Structure holding one command reply and pointer to its function. */
 typedef struct {
   SilcCommandCb cb;
@@ -30,15 +32,23 @@ typedef struct {
 /* All server command replys */
 extern SilcServerCommandReply silc_command_reply_list[];
 
+/* Context holding pending command callbacks. */
+typedef struct {
+  SilcCommandCb callback;
+  void *context;
+} *SilcServerCommandPendingCallbacks;
+
 /* Context sent as argument to all command reply functions */
 typedef struct {
   SilcServer server;
   SilcSocketConnection sock;
   SilcCommandPayload payload;
+  SilcArgumentPayload args;
 
   /* If defined this executes the pending command. */
-  void *context;
-  SilcCommandCb callback;
+  SilcServerCommandPendingCallbacks callbacks;
+  uint32 callbacks_count;
+  uint16 ident;
 } *SilcServerCommandReplyContext;
 
 /* Macros */
@@ -49,29 +59,21 @@ typedef struct {
 
 /* Macro used to declare command reply functions */
 #define SILC_SERVER_CMD_REPLY_FUNC(func) \
-void silc_server_command_reply_##func(void *context)
-
-/* Macro used to execute command replies */
-#define SILC_SERVER_COMMAND_REPLY_EXEC(ctx)            \
-do {                                                   \
-  SilcServerCommandReply *cmd;                         \
-                                                       \
-  for (cmd = silc_command_reply_list; cmd->cb; cmd++)  \
-    if (cmd->cmd == silc_command_get(ctx->payload)) {  \
-      cmd->cb(ctx);                                    \
-      break;                                           \
-    }                                                  \
-                                                       \
-  if (cmd == NULL) {                                   \
-    silc_free(ctx);                                    \
-    return;                                            \
-  }                                                    \
-} while(0)
+void silc_server_command_reply_##func(void *context, void *context2)
 
 /* Prototypes */
+void silc_server_command_reply_free(SilcServerCommandReplyContext cmd);
 void silc_server_command_reply_process(SilcServer server,
                                       SilcSocketConnection sock,
                                       SilcBuffer buffer);
+SILC_SERVER_CMD_REPLY_FUNC(whois);
+SILC_SERVER_CMD_REPLY_FUNC(whowas);
+SILC_SERVER_CMD_REPLY_FUNC(identify);
+SILC_SERVER_CMD_REPLY_FUNC(info);
+SILC_SERVER_CMD_REPLY_FUNC(motd);
 SILC_SERVER_CMD_REPLY_FUNC(join);
+SILC_SERVER_CMD_REPLY_FUNC(users);
+SILC_SERVER_CMD_REPLY_FUNC(getkey);
+SILC_SERVER_CMD_REPLY_FUNC(list);
 
 #endif
index 5f04a26fd691c81ce893bf4a0d34d1128d4b8de2..40b9041caaafee09f3b3d36f3e64168429b7972e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "idlist.h"
 
-/* Adds a new server to the list. The pointer sent as argument is allocated
-   and returned. */
+/******************************************************************************
+
+                             Common functions
+
+******************************************************************************/
+
+/* This function is used to add keys and stuff to common ID entry data
+   structure. */
 
-void silc_idlist_add_server(SilcServerList **list, 
-                           char *server_name, int server_type,
-                           SilcServerID *id, SilcServerList *router,
-                           SilcCipher send_key, SilcCipher receive_key,
-                           SilcPKCS public_key, SilcHmac hmac, 
-                           SilcServerList **new_idlist)
+void silc_idlist_add_data(void *entry, SilcIDListData idata)
 {
-  SilcServerList *last, *idlist;
+  SilcIDListData data = (SilcIDListData)entry;
+  data->send_key = idata->send_key;
+  data->receive_key = idata->receive_key;
+  data->hmac_send = idata->hmac_send;
+  data->hmac_receive = idata->hmac_receive;
+  data->psn_send = idata->psn_send;
+  data->psn_receive = idata->psn_receive;
+  data->hash = idata->hash;
+  data->public_key = idata->public_key;
+  memcpy(data->fingerprint, idata->fingerprint, sizeof(data->fingerprint));
+  data->rekey = idata->rekey;
+  data->last_receive = idata->last_receive;
+  data->last_sent = idata->last_sent;
+  data->status = idata->status;
+
+  data->created = time(0);     /* Update creation time */
+}
 
-  SILC_LOG_DEBUG(("Adding new server to id list"));
+/* Free's all data in the common ID entry data structure. */
 
-  idlist = silc_calloc(1, sizeof(*idlist));
-  if (idlist == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new server list object"));
-    *new_idlist = NULL;
-    return;
+void silc_idlist_del_data(void *entry)
+{
+  SilcIDListData idata = (SilcIDListData)entry;
+  if (idata->send_key)
+    silc_cipher_free(idata->send_key);
+  if (idata->receive_key)
+    silc_cipher_free(idata->receive_key);
+  if (idata->rekey) {
+    if (idata->rekey->send_enc_key) {
+      memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
+      silc_free(idata->rekey->send_enc_key);
+    }
+    silc_free(idata->rekey);
   }
+  if (idata->hmac_send)
+    silc_hmac_free(idata->hmac_send);
+  if (idata->hmac_receive)
+    silc_hmac_free(idata->hmac_receive);
+  if (idata->hash)
+    silc_hash_free(idata->hash);
+  if (idata->public_key)
+    silc_pkcs_public_key_free(idata->public_key);
+}
 
-  /* Set the pointers */
-  idlist->server_name = server_name;
-  idlist->server_type = server_type;
-  idlist->id = id;
-  idlist->router = router;
-  idlist->send_key = send_key;
-  idlist->receive_key = receive_key;
-  idlist->public_key = public_key;
-  idlist->hmac = hmac;
-  idlist->next = idlist;
-  idlist->prev = idlist;
-
-  /* First on the list? */
-  if (!*list) {
-    *list = idlist;
-    *new_idlist = idlist;
-    return;
-  }
+/* Purges ID cache */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge)
+{
+  SilcIDListPurge i = (SilcIDListPurge)context;
 
-  /* Add it to the list */
-  last = (*list)->prev;
-  last->next = idlist;
-  (*list)->prev = idlist;
-  idlist->next = (*list);
-  idlist->prev = last;
+  SILC_LOG_DEBUG(("Start"));
 
-  if (new_idlist)
-    *new_idlist = idlist;
+  silc_idcache_purge(i->cache);
+  silc_schedule_task_add(i->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)i, i->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 }
 
-/* Adds a new client to the client list. This is called when new client 
-   connection is accepted to the server. This adds all the relevant data 
-   about the client and session with it to the list. This list is 
-   referenced for example when sending message to the client. */
-
-void silc_idlist_add_client(SilcClientList **list, char *nickname,
-                           char *username, char *userinfo,
-                           SilcClientID *id, SilcServerList *router,
-                           SilcCipher send_key, SilcCipher receive_key,
-                           SilcPKCS public_key, SilcHmac hmac, 
-                           SilcClientList **new_idlist)
+/******************************************************************************
+
+                          Server entry functions
+
+******************************************************************************/
+
+/* Add new server entry. This adds the new server entry to ID cache and
+   returns the allocated entry object or NULL on error. This is called
+   when new server connects to us. We also add ourselves to cache with
+   this function. */
+
+SilcServerEntry 
+silc_idlist_add_server(SilcIDList id_list, 
+                      char *server_name, int server_type,
+                      SilcServerID *id, SilcServerEntry router,
+                      void *connection)
 {
-  SilcClientList *last, *idlist;
+  SilcServerEntry server;
 
-  SILC_LOG_DEBUG(("Adding new client to id list"));
+  SILC_LOG_DEBUG(("Adding new server entry"));
 
-  idlist = silc_calloc(1, sizeof(*idlist));
-  if (idlist == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new client list object"));
-    return;
-  }
+  server = silc_calloc(1, sizeof(*server));
+  server->server_name = server_name;
+  server->server_type = server_type;
+  server->id = id;
+  server->router = router;
+  server->connection = connection;
 
-  /* Set the pointers */
-  idlist->nickname = nickname;
-  idlist->username = username;
-  idlist->userinfo = userinfo;
-  idlist->id = id;
-  idlist->router = router;
-  idlist->send_key = send_key;
-  idlist->receive_key = receive_key;
-  idlist->public_key = public_key;
-  idlist->hmac = hmac;
-  idlist->next = idlist;
-  idlist->prev = idlist;
-
-  /* First on the list? */
-  if (!(*list)) {
-    *list = idlist;
-    if (new_idlist)
-      *new_idlist = idlist;
-    return;
+  if (!silc_idcache_add(id_list->servers, server->server_name, 
+                       (void *)server->id, (void *)server, 0, NULL)) {
+    silc_free(server);
+    return NULL;
   }
 
-  /* Add it to the list */
-  last = (*list)->prev;
-  last->next = idlist;
-  (*list)->prev = idlist;
-  idlist->next = *list;
-  idlist->prev = last;
-
-  if (new_idlist)
-    *new_idlist = idlist;
+  return server;
 }
 
-/* Free client entry.  This free's everything. */
+/* Finds server by Server ID */
 
-void silc_idlist_del_client(SilcClientList **list, SilcClientList *entry)
+SilcServerEntry
+silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
+                             bool registered, SilcIDCacheEntry *ret_entry)
 {
-  if (entry) {
-    if (entry->nickname)
-      silc_free(entry->nickname);
-    if (entry->username)
-      silc_free(entry->username);
-    if (entry->userinfo)
-      silc_free(entry->userinfo);
-    if (entry->id)
-      silc_free(entry->id);
-    if (entry->send_key)
-      silc_cipher_free(entry->send_key);
-    if (entry->receive_key)
-      silc_cipher_free(entry->receive_key);
-    if (entry->public_key)
-      silc_pkcs_free(entry->public_key);
-    if (entry->hmac)
-      silc_hmac_free(entry->hmac);
-    if (entry->hmac_key) {
-      memset(entry->hmac_key, 0, entry->hmac_key_len);
-      silc_free(entry->hmac_key);
-    }
+  SilcIDCacheEntry id_cache = NULL;
+  SilcServerEntry server;
 
-    /* Last one in list? */
-    if (*list == entry && entry->next == entry) {
-      *list = NULL;
-      silc_free(entry);
-      return;
-    }
+  if (!id)
+    return NULL;
 
-    /* At the start of list? */
-    if (*list == entry && entry->next != entry) {
-      *list = entry->next;
-      entry->next->prev = entry->prev;
-      entry->prev->next = *list;
-      silc_free(entry);
-      return;
-    }
+  SILC_LOG_DEBUG(("Server ID (%s)",
+                 silc_id_render(id, SILC_ID_SERVER)));
 
-    /* Remove from list */
-    entry->prev->next = entry->next;
-    entry->next->prev = entry->prev;
-    silc_free(entry);
-    return;
-  }
+  if (!silc_idcache_find_by_id_one(id_list->servers, (void *)id, 
+                                  &id_cache))
+    return NULL;
+
+  server = (SilcServerEntry)id_cache->context;
+
+  if (ret_entry)
+    *ret_entry = id_cache;
+
+  if (server && registered && 
+      !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
+    return NULL;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return server;
 }
 
-SilcClientList *
-silc_idlist_find_client_by_nickname(SilcClientList *list,
-                                   char *nickname,
-                                   char *server)
+/* Find server by name */
+
+SilcServerEntry
+silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
+                               bool registered, SilcIDCacheEntry *ret_entry)
 {
-  SilcClientList *first, *entry;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcServerEntry server;
 
-  SILC_LOG_DEBUG(("Finding client by nickname"));
+  SILC_LOG_DEBUG(("Server by name `%s'", name));
 
-  if (!list)
+  if (!silc_idcache_find_by_name_one(id_list->servers, name, &id_cache))
     return NULL;
 
-  first = entry = list;
-  if (!strcmp(entry->nickname, nickname)) {
-    SILC_LOG_DEBUG(("Found"));
-    return entry;
+  server = (SilcServerEntry)id_cache->context;
+  
+  if (ret_entry)
+    *ret_entry = id_cache;
+
+  if (server && registered &&
+      !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
+    return NULL;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return server;
+}
+
+/* Find server by connection parameters, hostname and port */
+
+SilcServerEntry
+silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
+                               int port, bool registered,
+                               SilcIDCacheEntry *ret_entry)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcServerEntry server = NULL;
+  SilcSocketConnection sock;
+  SILC_LOG_DEBUG(("Server by hostname %s and port %d", hostname, port));
+
+  if (!silc_idcache_get_all(id_list->servers, &list))
+    return NULL;
+
+  if (!silc_idcache_list_first(list, &id_cache)) {
+    silc_idcache_list_free(list);
+    return NULL;
   }
-  entry = entry->next;
 
-  while(entry != first) {
-    if (!strcmp(entry->nickname, nickname)) {
-      SILC_LOG_DEBUG(("Found"));
-      return entry;
-    }
+  while (id_cache) {
+    server = (SilcServerEntry)id_cache->context;
+    sock = (SilcSocketConnection)server->connection;
+    
+    if (sock && ((sock->hostname && !strcasecmp(sock->hostname, hostname)) ||
+                (sock->ip && !strcasecmp(sock->ip, hostname)))
+       && sock->port == port)
+      break;
+
+    id_cache = NULL;
+    server = NULL;
 
-    entry = entry->next;
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
   }
+  
+  silc_idcache_list_free(list);
+
+  if (ret_entry)
+    *ret_entry = id_cache;
+
+  if (server && registered &&
+      !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
+    return NULL;
 
-  return NULL;
+  SILC_LOG_DEBUG(("Found"));
+
+  return server;
 }
 
-SilcClientList *
-silc_idlist_find_client_by_hash(SilcClientList *list,
-                               char *nickname, SilcHash md5hash)
+/* Replaces old Server ID with new one */ 
+
+SilcServerEntry
+silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
+                             SilcServerID *new_id)
 {
-  SilcClientList *first, *entry;
-  unsigned char hash[16];
+  SilcIDCacheEntry id_cache = NULL;
+  SilcServerEntry server;
 
-  SILC_LOG_DEBUG(("Finding client by nickname hash"));
+  if (!old_id || !new_id)
+    return NULL;
 
-  if (!list)
+  SILC_LOG_DEBUG(("Replacing Server ID"));
+
+  if (!silc_idcache_find_by_id_one(id_list->servers, (void *)old_id, 
+                                  &id_cache))
     return NULL;
 
-  /* Make hash of the nickname */
-  silc_hash_make(md5hash, nickname, strlen(nickname), hash);
+  server = (SilcServerEntry)id_cache->context;
 
-  first = entry = list;
-  if (entry && !SILC_ID_COMPARE_HASH(entry->id, hash)) {
-    SILC_LOG_DEBUG(("Found"));
-    return entry;
-  }
-  entry = entry->next;
+  /* Remove the old entry and add a new one */
 
-  while(entry != first) {
-    if (entry && !SILC_ID_COMPARE_HASH(entry->id, hash)) {
-      SILC_LOG_DEBUG(("Found"));
-      return entry;
-    }
+  silc_idcache_del_by_id(id_list->servers, (void *)server->id);
 
-    entry = entry->next;
-  }
+  silc_free(server->id);
+  server->id = new_id;
+
+  silc_idcache_add(id_list->servers, server->server_name, server->id, 
+                  server, 0, NULL);
 
-  return NULL;
+  SILC_LOG_DEBUG(("Found"));
+
+  return server;
 }
 
-SilcClientList *
-silc_idlist_find_client_by_id(SilcClientList *list, SilcClientID *id)
+/* Removes and free's server entry from ID list */
+
+int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry)
 {
-  SilcClientList *first, *entry;
+  SILC_LOG_DEBUG(("Start"));
 
-  SILC_LOG_DEBUG(("Finding client by Client ID"));
+  if (entry) {
+    /* Remove from cache */
+    if (entry->id)
+      if (!silc_idcache_del_by_id(id_list->servers, (void *)entry->id))
+       return FALSE;
 
-  if (!list)
-    return NULL;
+    /* Free data */
+    silc_free(entry->server_name);
+    silc_free(entry->id);
 
-  first = entry = list;
-  if (entry && !SILC_ID_CLIENT_COMPARE(entry->id, id)) {
-    SILC_LOG_DEBUG(("Found"));
-    return entry;
+    memset(entry, 'F', sizeof(*entry));
+    silc_free(entry);
+    return TRUE;
   }
-  entry = entry->next;
 
-  while(entry != first) {
-    if (entry && !SILC_ID_CLIENT_COMPARE(entry->id, id)) {
-      SILC_LOG_DEBUG(("Found"));
-      return entry;
-    }
+  return FALSE;
+}
+
+/******************************************************************************
+
+                          Client entry functions
+
+******************************************************************************/
+
+/* Add new client entry. This adds the client entry to ID cache system
+   and returns the allocated client entry or NULL on error.  This is
+   called when new client connection is accepted to the server. If The
+   `router' is provided then the all server routines assume that the client
+   is not directly connected local client but it has router set and is
+   remote.  If this is the case then `connection' must be NULL.  If, on the
+   other hand, the `connection' is provided then the client is assumed
+   to be directly connected local client and `router' must be NULL. */
 
-    entry = entry->next;
+SilcClientEntry
+silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
+                      char *userinfo, SilcClientID *id, 
+                      SilcServerEntry router, void *connection,
+                      int expire)
+{
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Adding new client entry"));
+
+  client = silc_calloc(1, sizeof(*client));
+  client->nickname = nickname;
+  client->username = username;
+  client->userinfo = userinfo;
+  client->id = id;
+  client->router = router;
+  client->connection = connection;
+  client->channels = silc_hash_table_alloc(3, silc_hash_ptr, NULL,
+                                          NULL, NULL, NULL, NULL, TRUE);
+
+  if (!silc_idcache_add(id_list->clients, nickname, (void *)client->id, 
+                       (void *)client, expire, NULL)) {
+    silc_hash_table_free(client->channels);
+    silc_free(client);
+    return NULL;
   }
 
-  return NULL;
+  return client;
 }
 
-/* Adds new channel to the list. */
+/* Free client entry. This free's everything and removes the entry
+   from ID cache. Call silc_idlist_del_data before calling this one. */
 
-void silc_idlist_add_channel(SilcChannelList **list, 
-                            char *channel_name, int mode,
-                            SilcChannelID *id, SilcServerList *router,
-                            SilcCipher channel_key,
-                            SilcChannelList **new_idlist)
+int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry)
 {
-  SilcChannelList *last, *idlist;
+  SILC_LOG_DEBUG(("Start"));
 
-  SILC_LOG_DEBUG(("Adding new channel to id list"));
+  if (entry) {
+    /* Remove from cache */
+    if (entry->id)
+      if (!silc_idcache_del_by_context(id_list->clients, entry))
+       return FALSE;
 
-  idlist = silc_calloc(1, sizeof(*idlist));
-  if (idlist == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new channel list object"));
-    return;
-  }
+    /* Free data */
+    silc_free(entry->nickname);
+    silc_free(entry->username);
+    silc_free(entry->userinfo);
+    silc_free(entry->id);
+    silc_hash_table_free(entry->channels);
+
+    memset(entry, 'F', sizeof(*entry));
+    silc_free(entry);
 
-  /* Set the pointers */
-  idlist->channel_name = channel_name;
-  idlist->mode = mode;
-  idlist->id = id;
-  idlist->router = router;
-  idlist->channel_key = channel_key;
-  idlist->next = idlist;
-  idlist->prev = idlist;
-
-  /* First on the list? */
-  if (!*list) {
-    *list = idlist;
-    if (new_idlist)
-      *new_idlist = idlist;
-    return;
+    return TRUE;
   }
 
-  /* Add it to the list */
-  last = (*list)->prev;
-  last->next = idlist;
-  (*list)->prev = idlist;
-  idlist->next = (*list);
-  idlist->prev = last;
+  return FALSE;
+}
+
+/* Returns all clients matching requested nickname. Number of clients is
+   returned to `clients_count'. Caller must free the returned table. */
+
+int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
+                                       char *server, 
+                                       SilcClientEntry **clients,
+                                       uint32 *clients_count)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!silc_idcache_find_by_name(id_list->clients, nickname, &list))
+    return FALSE;
 
-  if (new_idlist)
-    *new_idlist = idlist;
+  *clients = silc_realloc(*clients, 
+                         (silc_idcache_list_count(list) + *clients_count) * 
+                         sizeof(**clients));
+
+  silc_idcache_list_first(list, &id_cache);
+  (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
+
+  while (silc_idcache_list_next(list, &id_cache))
+    (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
+  
+  silc_idcache_list_free(list);
+  
+  SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
+
+  return TRUE;
 }
 
-SilcChannelList *
-silc_idlist_find_channel_by_id(SilcChannelList *list, SilcChannelID *id)
+/* Returns all clients matching requested nickname hash. Number of clients
+   is returned to `clients_count'. Caller must free the returned table. */
+
+int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
+                                   SilcHash md5hash,
+                                   SilcClientEntry **clients,
+                                   uint32 *clients_count)
 {
-  SilcChannelList *first, *entry;
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  unsigned char hash[32];
+  SilcClientID client_id;
 
-  SILC_LOG_DEBUG(("Finding channel by Channel ID"));
+  SILC_LOG_DEBUG(("Start"));
 
-  if (!list)
+  silc_hash_make(md5hash, nickname, strlen(nickname), hash);
+
+  /* As the Client ID is hashed in the ID cache by hashing only the hash
+     from the Client ID, we can do a lookup with only the hash not the
+     other parts of the ID and get all the clients with that hash, ie.
+     with that nickname, as the hash is from the nickname. */
+  memset(&client_id, 0, sizeof(client_id));
+  memcpy(&client_id.hash, hash, sizeof(client_id.hash));
+  if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list))
+    return FALSE;
+
+  *clients = silc_realloc(*clients, 
+                         (silc_idcache_list_count(list) + *clients_count) * 
+                         sizeof(**clients));
+
+  silc_idcache_list_first(list, &id_cache);
+  (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
+
+  while (silc_idcache_list_next(list, &id_cache))
+    (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
+  
+  silc_idcache_list_free(list);
+  
+  SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
+
+  return TRUE;
+}
+
+/* Finds client by Client ID */
+
+SilcClientEntry
+silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
+                             bool registered, SilcIDCacheEntry *ret_entry)
+{
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client;
+
+  if (!id)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Client ID (%s)", 
+                 silc_id_render(id, SILC_ID_CLIENT)));
+
+  /* Do extended search since the normal ID comparison function for
+     Client ID's compares only the hash from the Client ID and not the
+     entire ID. The silc_hash_client_id_compare compares the entire
+     Client ID as we want to find one specific Client ID. */
+  if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache))
+    return NULL;
+
+  client = (SilcClientEntry)id_cache->context;
+
+  if (ret_entry)
+    *ret_entry = id_cache;
+
+  if (client && registered &&
+      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
     return NULL;
 
-  first = entry = list;
-  if (entry && !SILC_ID_CHANNEL_COMPARE(entry->id, id)) {
-    SILC_LOG_DEBUG(("Found"));
-    return entry;
+  SILC_LOG_DEBUG(("Found"));
+
+  return client;
+}
+
+/* Replaces old Client ID with new one */
+
+SilcClientEntry
+silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
+                             SilcClientID *new_id)
+{
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client;
+
+  if (!old_id || !new_id)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Replacing Client ID"));
+
+  /* Do extended search since the normal ID comparison function for
+     Client ID's compares only the hash from the Client ID and not the
+     entire ID. The silc_hash_client_id_compare compares the entire
+     Client ID as we want to find one specific Client ID. */
+  if (!silc_idcache_find_by_id_one_ext(id_list->clients, (void *)old_id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache))
+    return NULL;
+
+  client = (SilcClientEntry)id_cache->context;
+
+  /* Remove the old entry and add a new one */
+
+  silc_idcache_del_by_context(id_list->clients, client);
+
+  silc_free(client->id);
+  client->id = new_id;
+
+  silc_idcache_add(id_list->clients, NULL, client->id, client, 0, NULL);
+
+  SILC_LOG_DEBUG(("Replaced"));
+
+  return client;
+}
+
+/* Client cache entry destructor that is called when the cache is purged. */
+
+void silc_idlist_client_destructor(SilcIDCache cache,
+                                  SilcIDCacheEntry entry)
+{
+  SilcClientEntry client;
+
+  client = (SilcClientEntry)entry->context;
+  if (client) {
+    silc_free(client->nickname);
+    silc_free(client->username);
+    silc_free(client->userinfo);
+    silc_free(client->id);
+    silc_hash_table_free(client->channels);
+
+    memset(client, 'F', sizeof(*client));
+    silc_free(client);
   }
-  entry = entry->next;
+}
+
+/******************************************************************************
+
+                          Channel entry functions
+
+******************************************************************************/
 
-  while(entry != first) {
-    if (entry && !SILC_ID_CHANNEL_COMPARE(entry->id, id)) {
-      SILC_LOG_DEBUG(("Found"));
-      return entry;
+/* Add new channel entry. This add the new channel entry to the ID cache
+   system and returns the allocated entry or NULL on error. */
+
+SilcChannelEntry
+silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
+                       SilcChannelID *id, SilcServerEntry router,
+                       SilcCipher channel_key, SilcHmac hmac,
+                       int expire)
+{
+  SilcChannelEntry channel;
+
+  SILC_LOG_DEBUG(("Adding new channel entry"));
+
+  channel = silc_calloc(1, sizeof(*channel));
+  channel->channel_name = channel_name;
+  channel->mode = mode;
+  channel->id = id;
+  channel->router = router;
+  channel->channel_key = channel_key;
+  channel->hmac = hmac;
+  channel->created = time(0);
+  if (!channel->hmac)
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      silc_free(channel);
+      return NULL;
     }
 
-    entry = entry->next;
+  channel->user_list = silc_hash_table_alloc(3, silc_hash_ptr, NULL, NULL,
+                                            NULL, NULL, NULL, TRUE);
+
+  if (!silc_idcache_add(id_list->channels, channel->channel_name, 
+                       (void *)channel->id, (void *)channel, expire, NULL)) {
+    silc_hmac_free(channel->hmac);
+    silc_hash_table_free(channel->user_list);
+    silc_free(channel);
+    return NULL;
   }
 
-  return NULL;
+  return channel;
+}
+
+/* Foreach callbcak to free all users from the channel when deleting a
+   channel entry. */
+
+static void silc_idlist_del_channel_foreach(void *key, void *context,
+                                           void *user_context)
+{
+  SilcChannelClientEntry chl = (SilcChannelClientEntry)context;
+
+  SILC_LOG_DEBUG(("Removing client %s from channel %s",
+                 chl->client->nickname, chl->channel->channel_name));
+
+  /* Remove the context from the client's channel hash table as that
+     table and channel's user_list hash table share this same context. */
+  silc_hash_table_del(chl->client->channels, chl->channel);
+  silc_free(chl);
 }
 
 /* Free channel entry.  This free's everything. */
 
-void silc_idlist_del_channel(SilcChannelList **list, SilcChannelList *entry)
+int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry)
 {
+  SILC_LOG_DEBUG(("Start"));
+
   if (entry) {
-    if (entry->channel_name)
-      silc_free(entry->channel_name);
+    /* Remove from cache */
     if (entry->id)
-      silc_free(entry->id);
-    if (entry->topic)
-      silc_free(entry->topic);
+      if (!silc_idcache_del_by_id(id_list->channels, (void *)entry->id))
+       return FALSE;
+
+    /* Free all client entrys from the users list. The silc_hash_table_free
+       will free all the entries so they are not freed at the foreach 
+       callback. */
+    silc_hash_table_foreach(entry->user_list, silc_idlist_del_channel_foreach,
+                           NULL);
+    silc_hash_table_free(entry->user_list);
+
+    /* Free data */
+    silc_free(entry->channel_name);
+    silc_free(entry->id);
+    silc_free(entry->topic);
     if (entry->channel_key)
       silc_cipher_free(entry->channel_key);
     if (entry->key) {
-      memset(entry->key, 0, entry->key_len);
+      memset(entry->key, 0, entry->key_len / 8);
       silc_free(entry->key);
     }
-    memset(entry->iv, 0, sizeof(entry->iv));
+    silc_free(entry->cipher);
+    if (entry->hmac)
+      silc_hmac_free(entry->hmac);
+    silc_free(entry->hmac_name);
+    silc_free(entry->rekey);
 
-    if (entry->user_list_count)
-      silc_free(entry->user_list);
+    memset(entry, 'F', sizeof(*entry));
+    silc_free(entry);
+    return TRUE;
+  }
 
-    /* Last one in list? */
-    if (*list == entry && entry->next == entry) {
-      *list = NULL;
-      silc_free(entry);
-      return;
-    }
+  return FALSE;
+}
 
-    /* At the start of list? */
-    if (*list == entry && entry->next != entry) {
-      *list = entry->next;
-      entry->next->prev = entry->prev;
-      entry->prev->next = *list;
-      silc_free(entry);
-      return;
-    }
+/* Finds channel by channel name. Channel names are unique and they
+   are not case-sensitive. */
 
-    /* Remove from list */
-    entry->prev->next = entry->next;
-    entry->next->prev = entry->prev;
-    silc_free(entry);
-    return;
+SilcChannelEntry
+silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
+                                SilcIDCacheEntry *ret_entry)
+{
+  SilcIDCacheEntry id_cache = NULL;
+
+  SILC_LOG_DEBUG(("Channel by name %s", name));
+
+  if (!silc_idcache_find_by_name_one(id_list->channels, name, &id_cache))
+    return NULL;
+
+  if (ret_entry)
+    *ret_entry = id_cache;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return id_cache->context;
+}
+
+/* Finds channel by Channel ID. */
+
+SilcChannelEntry
+silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
+                              SilcIDCacheEntry *ret_entry)
+{
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel;
+
+  if (!id)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Channel ID (%s)",
+                 silc_id_render(id, SILC_ID_CHANNEL)));
+
+  if (!silc_idcache_find_by_id_one(id_list->channels, (void *)id, &id_cache))
+    return NULL;
+
+  channel = (SilcChannelEntry)id_cache->context;
+
+  if (ret_entry)
+    *ret_entry = id_cache;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return channel;
+}
+
+/* Replaces old Channel ID with new one. This is done when router forces
+   normal server to change Channel ID. */
+
+SilcChannelEntry
+silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
+                              SilcChannelID *new_id)
+{
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel;
+
+  if (!old_id || !new_id)
+    return NULL;
+
+  SILC_LOG_DEBUG(("Replacing Channel ID"));
+
+  if (!silc_idcache_find_by_id_one(id_list->channels, (void *)old_id, 
+                                  &id_cache))
+    return NULL;
+
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* Remove the old entry and add a new one */
+
+  silc_idcache_del_by_id(id_list->channels, (void *)channel->id);
+
+  silc_free(channel->id);
+  channel->id = new_id;
+
+  silc_idcache_add(id_list->channels, channel->channel_name, channel->id, 
+                  channel, 0, NULL);
+
+  SILC_LOG_DEBUG(("Replaced"));
+
+  return channel;
+}
+
+/* Returns channels from the ID list. If the `channel_id' is NULL then
+   all channels are returned. */
+
+SilcChannelEntry *
+silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
+                        uint32 *channels_count)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry *channels = NULL;
+  int i = 0;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!channel_id) {
+    if (!silc_idcache_get_all(id_list->channels, &list))
+      return NULL;
+
+    channels = silc_calloc(silc_idcache_list_count(list), sizeof(*channels));
+    
+    i = 0;
+    silc_idcache_list_first(list, &id_cache);
+    channels[i++] = (SilcChannelEntry)id_cache->context;
+    
+    while (silc_idcache_list_next(list, &id_cache))
+      channels[i++] = (SilcChannelEntry)id_cache->context;
+    
+    silc_idcache_list_free(list);
+  } else {
+    if (!silc_idcache_find_by_id_one(id_list->channels, channel_id, &id_cache))
+      return NULL;
+
+    i = 1;
+    channels = silc_calloc(1, sizeof(*channels));
+    channels[0] = (SilcChannelEntry)id_cache->context;
   }
+
+  if (channels_count)
+    *channels_count = i;
+
+  return channels;
 }
index b18b90ddb6dc533d0f59c98a7e39828dfce2ca06..74d054f7b22938b4efa21056b9d699596f01f52a 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #define IDLIST_H
 
 /* Forward declarations */
-typedef struct SilcServerListStruct SilcServerList;
-typedef struct SilcClientListStruct SilcClientList;
-typedef struct SilcChannelListStruct SilcChannelList;
+typedef struct SilcServerEntryStruct *SilcServerEntry;
+typedef struct SilcClientEntryStruct *SilcClientEntry;
+typedef struct SilcChannelEntryStruct *SilcChannelEntry;
+
+/* Context for holding cache information to periodically purge
+   the cache. */
+typedef struct {
+  SilcIDCache cache;
+  SilcSchedule schedule;
+  uint32 timeout;
+} *SilcIDListPurge;
+
+/* Channel key re-key context. */
+typedef struct {
+  void *context;
+  SilcChannelEntry channel;
+  uint32 key_len;
+  SilcTask task;
+} *SilcServerChannelRekey;
+
+/* Generic rekey context for connections */
+typedef struct {
+  /* Current sending encryption key, provided for re-key. The `pfs'
+     is TRUE if the Perfect Forward Secrecy is performed in re-key. */
+  unsigned char *send_enc_key;
+  uint32 enc_key_len;
+  int ske_group;
+  bool pfs;
+  uint32 timeout;
+  void *context;
+} *SilcServerRekey;
+
+/* ID List Entry status type and all the types. */
+typedef uint8 SilcIDListStatus;
+#define SILC_IDLIST_STATUS_NONE         0x00    /* No status */
+#define SILC_IDLIST_STATUS_REGISTERED   0x01    /* Entry is registered */
+#define SILC_IDLIST_STATUS_RESOLVED     0x02    /* Entry info is resolved */
+#define SILC_IDLIST_STATUS_RESOLVING    0x04    /* Entry is being resolved
+                                                  with WHOIS or IDENTIFY */
+#define SILC_IDLIST_STATUS_DISABLED     0x08    /* Entry is disabled */
+
+/*
+   Generic ID list data structure.
+
+   This structure is included in all ID list entries and it includes data
+   pointers that are common to all ID entries.  This structure is always
+   defined to the first field in the ID entries and is used to explicitly
+   type cast to this type without first explicitly casting to correct ID
+   entry type.  Hence, the ID list entry is type casted to this type to
+   get this data from the ID entry (which is usually opaque pointer).
+
+   Note that some of the fields may be NULL.
+
+*/
+typedef struct {
+  /* Send and receive symmetric keys */
+  SilcCipher send_key;
+  SilcCipher receive_key;
+
+  /* HMAC */
+  SilcHmac hmac_send;
+  SilcHmac hmac_receive;
+
+  /* Packet sequence numbers */
+  uint32 psn_send;
+  uint32 psn_receive;
+
+  /* Hash selected in the SKE protocol, NULL if not needed at all */
+  SilcHash hash;
+
+  /* Public key */
+  SilcPublicKey public_key;
+  unsigned char fingerprint[20];
+
+  /* Re-key context */
+  SilcServerRekey rekey;
+
+  long last_receive;           /* Time last received data */
+  long last_sent;              /* Time last sent data */
+
+  unsigned long created;       /* Time when entry was created */
+
+  SilcIDListStatus status;     /* Status mask of the entry */
+} *SilcIDListData, SilcIDListDataStruct;
 
 /* 
-   SILC Server list object.
+   SILC Server entry object.
 
-   This list holds information about servers in SILC network. However, 
-   contents of this list is highly dependent of what kind of server we are 
-   (normal server or router server) and whether the list is used as a local 
-   list or a global list. These factors dictates the contents of this list.
+   This entry holds information about servers in SILC network. However, 
+   contents of this entry is highly dependent of what kind of server we are 
+   (normal server or router server) and whether the entry is used as a local 
+   list or a global list. These factors dictates the contents of this entry.
 
-   This list is defined as follows:
+   This entry is defined as follows:
 
    Server type   List type      Contents
    =======================================================================
@@ -45,12 +126,16 @@ typedef struct SilcChannelListStruct SilcChannelList;
 
    Following short description of the fields:
 
+   SilcIDListDataStruct data
+
+       Generic data structure to hold data common to all ID entries.
+
    char *server_name
 
        Logical name of the server. There is no limit of the length of the
        server name. This is usually the same name as defined in DNS.
 
-   int server_type
+   uint8 server_type
 
        Type of the server. SILC_SERVER or SILC_ROUTER are the possible
        choices for this.
@@ -61,16 +146,23 @@ typedef struct SilcChannelListStruct SilcChannelList;
        the server SILC will ever need. These are also the informations
        that is broadcasted between servers and routers in the SILC network.
 
-   struct SilcServerListStruct *router
+   char *server_info
+   char *motd
+
+       Server info (from INFO command) saved temporarily and motd (from
+       MOTD command) saved temporarily.
+
+   SilcServerEntry router
 
        This is a pointer back to the server list. This is the router server 
        where this server is connected to. If this is the router itself and 
        it doesn't have a route this is NULL.
 
    SilcCipher send_key
-   
    SilcCipher receive_key
 
+       Data sending and receiving keys.
+
    void *connection
 
        A pointer, usually, to the socket list for fast referencing to
@@ -79,37 +171,59 @@ typedef struct SilcChannelListStruct SilcChannelList;
        list.
    
 */
-struct SilcServerListStruct {
+struct SilcServerEntryStruct {
+  /* Generic data structure. DO NOT add anything before this! */
+  SilcIDListDataStruct data;
+
   char *server_name;
-  int server_type;
+  uint8 server_type;
   SilcServerID *id;
+  char *server_info;
+  char *motd;
 
   /* Pointer to the router */
-  struct SilcServerListStruct *router;
-
-  /* Keys */
-  SilcCipher send_key;
-  SilcCipher receive_key;
-  SilcPKCS public_key;
-  SilcHmac hmac;
-  unsigned char *hmac_key;
-  unsigned int hmac_key_len;
+  SilcServerEntry router;
 
   /* Connection data */
   void *connection;
-
-  struct SilcServerListStruct *next;
-  struct SilcServerListStruct *prev;
 };
 
 /* 
-   SILC Client list object.
+   SILC Channel Client entry structure.
+
+   This entry used only by the SilcChannelEntry object and it holds
+   information about current clients (ie. users) on channel. Following
+   short description of the fields:
+
+   SilcClientEntry client
+
+       Pointer to the client list. This is the client currently on channel.
+
+   uint32 mode
+
+       Client's current mode on the channel.
 
-   This list holds information about connected clients ie. users in the SILC
-   network. The contents of this list is depended on whether we are normal 
+   SilcChannelEntry channel
+
+       Back pointer back to channel. As this structure is also used by
+       SilcClientEntry we have this here for fast access to the channel when
+       used by SilcClientEntry.
+
+*/
+typedef struct SilcChannelClientEntryStruct {
+  SilcClientEntry client;
+  uint32 mode;
+  SilcChannelEntry channel;
+} *SilcChannelClientEntry;
+
+/* 
+   SILC Client entry object.
+
+   This entry holds information about connected clients ie. users in the SILC
+   network. The contents of this entrt is depended on whether we are normal 
    server or router server and whether the list is a local or global list.
 
-   This list is defined as follows:
+   This entry is defined as follows:
 
    Server type   List type      Contents
    =======================================================================
@@ -120,10 +234,21 @@ struct SilcServerListStruct {
 
    Following short description of the fields:
 
+   SilcIDListDataStruct data
+
+       Generic data structure to hold data common to all ID entries.
+
+   unsigned char *nickname
+
+       The nickname of the client.
+
+   char *servername
+
+       The name of the server where the client is from. MAy be NULL.
+
    char username
 
-       Client's (meaning user's) real name. This is defined in following 
-       manner:
+       Client's usename. This is defined in the following manner:
 
        Server type   List type      Contents
        ====================================================
@@ -159,31 +284,34 @@ struct SilcServerListStruct {
        nickname. Nickname is not relevant information that would need to be 
        saved as plain.
 
-   int mode
+   uint32 mode
 
        Client's mode.  Client maybe for example server operator or
        router operator (SILC operator).
 
-   SilcServerList *router
+   long last_command
 
-       This is a pointer to the server list. This is the router server whose 
-       cell this client is coming from. This is used to route messages to 
-       this client.
+       Time of last time client executed command. We are strict and will
+       not allow any command to be exeucted more than once in about
+       2 seconds. This is result of normal time().
 
-   SilcCipher session_key
+   uint8 fast_command
 
-       The actual session key established by key exchange protcol between
-       connecting parties. This is used for both encryption and decryption.
+       Counter to check command bursts.  By default, up to 5 commands
+       are allowed before limiting the execution.  See command flags
+       for more detail.
 
-   SilcPKCS public_key
+   SilcServerEntry router
 
-       Public key of the client. This maybe NULL.
+       This is a pointer to the server list. This is the router server whose 
+       cell this client is coming from. This is used to route messages to 
+       this client.
 
-   SilcHmac hmac
-   unsigned char *hmac_key
-   unsigned int hmac_key_len
+   SilcHashTable channels;
 
-       MAC key used to compute MAC's for packets. 
+       All the channels this client has joined.  The context saved in the
+       hash table shares memory with the channel entrys `user_list' hash
+       table.
 
    void *connection
 
@@ -192,65 +320,51 @@ struct SilcServerListStruct {
        but as just said, this is usually pointer to the socket connection
        list.
 
+   uint16 resolve_cmd_ident
+
+       Command identifier for the entry when the entry's data.status
+       is SILC_IDLIST_STATUS_RESOLVING.  If this entry is asked to be
+       resolved when the status is set then the resolver may attach to
+       this command identifier and handle the process after the resolving
+       is over.
+
 */
-struct SilcClientListStruct {
-  char *nickname;
+struct SilcClientEntryStruct {
+  /* Generic data structure. DO NOT add anything before this! */
+  SilcIDListDataStruct data;
+
+  unsigned char *nickname;
+  char *servername;
   char *username;
   char *userinfo;
   SilcClientID *id;
-  int mode;
+  uint32 mode;
+
+  long last_command;
+  uint8 fast_command;
 
   /* Pointer to the router */
-  SilcServerList *router;
+  SilcServerEntry router;
 
-  /* Pointers to channels this client has joined */
-  SilcChannelList **channel;
-  unsigned int channel_count;
-
-  /* Keys */
-  SilcCipher send_key;
-  SilcCipher receive_key;
-  SilcPKCS public_key;
-  SilcHmac hmac;
-  unsigned char *hmac_key;
-  unsigned int hmac_key_len;
+  /* All channels this client has joined */
+  SilcHashTable channels;
 
   /* Connection data */
   void *connection;
 
-  struct SilcClientListStruct *next;
-  struct SilcClientListStruct *prev;
+  /* data.status is RESOLVING and this includes the resolving command 
+     reply identifier. */
+  uint16 resolve_cmd_ident;
 };
 
 /* 
-   SILC Channel Client list structure.
-
-   This list used only by the SilcChannelList object and it holds information 
-   about current clients (ie. users) on channel. Following short description 
-   of the fields:
+   SILC Channel entry object.
 
-   SilcClientList client
-
-       Pointer to the client list. This is the client currently on channel.
-
-   int mode
-
-       Client's current mode on the channel.
-
-*/
-typedef struct SilcChannelClientListStruct {
-  SilcClientList *client;
-  int mode;
-} SilcChannelClientList;
-
-/* 
-   SILC Channel list object.
-
-   This list holds information about channels in SILC network. The contents 
-   of this list is depended on whether we are normal server or router server 
+   This entry holds information about channels in SILC network. The contents 
+   of this entry is depended on whether we are normal server or router server 
    and whether the list is a local or global list.
 
-   This list is defined as follows:
+   This entry is defined as follows:
 
    Server type   List type      Contents
    =======================================================================
@@ -265,16 +379,17 @@ typedef struct SilcChannelClientListStruct {
 
        Logical name of the channel.
 
-   int mode
+   uint32 mode
 
-       Current mode of the channel.
+       Current mode of the channel.  See lib/silccore/silcchannel.h for
+       all modes.
 
    SilcChannelID *id
 
        ID of the channel. This includes all the information SILC will ever
        need.
 
-   int global_users
+   bool global_users
  
        Boolean value to tell whether there are users outside this server
        on this channel. This is set to TRUE if router sends message to
@@ -288,40 +403,97 @@ typedef struct SilcChannelClientListStruct {
 
        Current topic of the channel.
 
-   SilcServerList *router
+   char *cipher
+
+       Default cipher of the channel. If this is NULL then server picks
+       the cipher to be used. This can be set at SILC_COMMAND_JOIN.
+
+   char *hmac_name
+
+       Default hmac of the channel. If this is NULL then server picks
+       the cipher to be used. This can be set at SILC_COMMAND_JOIN.
+
+   SilcPublicKey founder_key
+   SilcAuthMethod founder_method
+   unsigned char *founder_passwd
+   uint32 founder_passwd_len
+
+       If the SILC_CMODE_FOUNDER_AUTH has been set then these will include
+       the founder's public key, authentication method and the password
+       if the method is SILC_AUTH_PASSWORD.  If it is SILC_AUTH_PUBLIC_KEY
+       then the `founder_passwd' is NULL.
+
+   SilcHashTable user_list
+
+       All users joined on this channel.  Note that the context saved to
+       this entry shares memory with the client entrys `channels' hash
+       table.
+
+   SilcServerEntry router
 
        This is a pointer to the server list. This is the router server 
        whose cell this channel belongs to. This is used to route messages 
        to this channel.
 
-   SilcCipher send_key
+   SilcCipher channel_key
 
+       The key of the channel (the cipher actually).
 
-   SilcCipher receive_key
+   unsigned char *key
+   uint32 key_len
+
+       Raw key data of the channel key.
+
+   unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]
+
+       Current initial vector. Initial vector is received always along
+       with the channel packet. By default this is filled with NULL.
+
+   SilcHmac hmac;
+
+       HMAC of the channel.
+
+   SilcServerChannelRekey rekey
+
+       Channel key re-key context.
 
 */
-struct SilcChannelListStruct {
+struct SilcChannelEntryStruct {
   char *channel_name;
-  int mode;
+  uint32 mode;
   SilcChannelID *id;
-  int global_users;
+  bool global_users;
   char *topic;
+  char *cipher;
+  char *hmac_name;
 
-  /* List of users on channel */
-  SilcChannelClientList *user_list;
-  unsigned int user_list_count;
+  SilcPublicKey founder_key;
+  SilcAuthMethod founder_method;
+  unsigned char *founder_passwd;
+  uint32 founder_passwd_len;
+
+  uint32 user_limit;
+  unsigned char *passphrase;
+  char *invite_list;
+  char *ban_list;
+
+  /* All users on this channel */
+  SilcHashTable user_list;
 
   /* Pointer to the router */
-  SilcServerList *router;
+  SilcServerEntry router;
 
   /* Channel keys */
   SilcCipher channel_key;
   unsigned char *key;
-  unsigned int key_len;
+  uint32 key_len;
   unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
+  SilcHmac hmac;
+
+  SilcServerChannelRekey rekey;
 
-  struct SilcChannelListStruct *next;
-  struct SilcChannelListStruct *prev;
+  unsigned long created;
+  bool disabled;
 };
 
 /* 
@@ -329,22 +501,22 @@ struct SilcChannelListStruct {
 
    As for remainder these lists are defined as follows:
 
-   List        Server type   List type      Contents
+   Entry list (cache)  Server type   List type      Contents
    =======================================================================
-   servers     server        local list     Server itself
-   servers     server        global list    NULL
-   servers     router        local list     All servers in cell
-   servers     router        global list    All servers in SILC
+   servers             server        local list     Server itself
+   servers             server        global list    NULL
+   servers             router        local list     All servers in cell
+   servers             router        global list    All servers in SILC
 
-   clients     server        local list     All clients in server
-   clients     server        global list    NULL
-   clients     router        local list     All clients in cell
-   clients     router        global list    All clients in SILC
+   clients             server        local list     All clients in server
+   clients             server        global list    NULL
+   clients             router        local list     All clients in cell
+   clients             router        global list    All clients in SILC
 
-   channels    server        local list     All channels in server
-   channels    server        global list    NULL
-   channels    router        local list     All channels in cell
-   channels    router        global list    All channels in SILC
+   channels            server        local list     All channels in server
+   channels            server        global list    NULL
+   channels            router        local list     All channels in cell
+   channels            router        global list    All channels in SILC
 
    As seen on the list normal server never defines a global list. This is
    because of normal server don't know anything about anything global data,
@@ -352,77 +524,98 @@ struct SilcChannelListStruct {
    other hand, always define local and global lists because routers really
    know all the relevant data in the SILC network.
 
-*/
-typedef struct SilcIDListStruct {
-  SilcServerList *servers;
-  SilcClientList *clients;
-  SilcChannelList *channels;
+   This object is used as local and global list by the server/router.
+   Above table shows how this is defined on different conditions.
 
-  /* ID Caches. Caches are used to perform fast search on the ID's. */
-  SilcIDCache *server_cache[96];
-  unsigned int server_cache_count[96];
-  SilcIDCache *client_cache[96];
-  unsigned int client_cache_count[96];
-  SilcIDCache *channel_cache[96];
-  unsigned int channel_cache_count[96];
-} SilcIDListObject;
+   This object holds pointers to the ID cache system. Every ID cache entry
+   has a specific context pointer to allocated entry (server, client or
+   channel entry).
 
-typedef SilcIDListObject *SilcIDList;
+*/
+typedef struct SilcIDListStruct {
+  SilcIDCache servers;
+  SilcIDCache clients;
+  SilcIDCache channels;
+} *SilcIDList;
 
 /*
-   Temporary ID List object.
+   ID Entry for Unknown connections.
 
-   This is used during authentication phases where we still don't
-   know what kind of connection remote connection is, hence, we
-   will use this structure instead until we know what type of
-   connection remote end is.
+   This is used during authentication phases where we still don't know 
+   what kind of connection remote connection is, hence, we will use this
+   structure instead until we know what type of connection remote end is.
 
-   This is not in any list. This is always individually allocated
-   and used as such.
+   This is not in any list. This is always individually allocated and
+   used as such.
 
 */
 typedef struct {
-  SilcCipher send_key;
-  SilcCipher receive_key;
-  SilcPKCS pkcs;
-
-  SilcHmac hmac;
-  unsigned char *hmac_key;
-  unsigned int hmac_key_len;
-
-  /* SilcComp comp */
-} SilcIDListUnknown;
+  /* Generic data structure. DO NOT add anything before this! */
+  SilcIDListDataStruct data;
+} *SilcUnknownEntry;
 
 /* Prototypes */
-void silc_idlist_add_server(SilcServerList **list, 
-                           char *server_name, int server_type,
-                           SilcServerID *id, SilcServerList *router,
-                           SilcCipher send_key, SilcCipher receive_key,
-                           SilcPKCS public_key, SilcHmac hmac, 
-                           SilcServerList **new_idlist);
-void silc_idlist_add_client(SilcClientList **list, char *nickname,
-                           char *username, char *userinfo,
-                           SilcClientID *id, SilcServerList *router,
-                           SilcCipher send_key, SilcCipher receive_key,
-                           SilcPKCS public_key, SilcHmac hmac, 
-                           SilcClientList **new_idlist);
-void silc_idlist_del_client(SilcClientList **list, SilcClientList *entry);
-SilcClientList *
-silc_idlist_find_client_by_nickname(SilcClientList *list,
-                                   char *nickname,
-                                   char *server);
-SilcClientList *
-silc_idlist_find_client_by_hash(SilcClientList *list,
-                               char *nickname, SilcHash hash);
-SilcClientList *
-silc_idlist_find_client_by_id(SilcClientList *list, SilcClientID *id);
-void silc_idlist_add_channel(SilcChannelList **list, 
-                            char *channel_name, int mode,
-                            SilcChannelID *id, SilcServerList *router,
-                            SilcCipher channel_key,
-                            SilcChannelList **new_idlist);
-SilcChannelList *
-silc_idlist_find_channel_by_id(SilcChannelList *list, SilcChannelID *id);
-void silc_idlist_del_channel(SilcChannelList **list, SilcChannelList *entry);
+void silc_idlist_add_data(void *entry, SilcIDListData idata);
+void silc_idlist_del_data(void *entry);
+SILC_TASK_CALLBACK_GLOBAL(silc_idlist_purge);
+SilcServerEntry 
+silc_idlist_add_server(SilcIDList id_list, 
+                      char *server_name, int server_type,
+                      SilcServerID *id, SilcServerEntry router,
+                      void *connection);
+SilcServerEntry
+silc_idlist_find_server_by_id(SilcIDList id_list, SilcServerID *id,
+                             bool registered, SilcIDCacheEntry *ret_entry);
+SilcServerEntry
+silc_idlist_find_server_by_name(SilcIDList id_list, char *name,
+                               bool registered, SilcIDCacheEntry *ret_entry);
+SilcServerEntry
+silc_idlist_find_server_by_conn(SilcIDList id_list, char *hostname,
+                               int port, bool registered,
+                               SilcIDCacheEntry *ret_entry);
+SilcServerEntry
+silc_idlist_replace_server_id(SilcIDList id_list, SilcServerID *old_id,
+                             SilcServerID *new_id);
+int silc_idlist_del_server(SilcIDList id_list, SilcServerEntry entry);
+SilcClientEntry
+silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, 
+                      char *userinfo, SilcClientID *id, 
+                      SilcServerEntry router, void *connection,
+                      int expire);
+int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry);
+int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname,
+                                       char *server, 
+                                       SilcClientEntry **clients,
+                                       uint32 *clients_count);
+int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname,
+                                   SilcHash md5hash,
+                                   SilcClientEntry **clients,
+                                   uint32 *clients_count);
+SilcClientEntry
+silc_idlist_find_client_by_id(SilcIDList id_list, SilcClientID *id,
+                             bool registered, SilcIDCacheEntry *ret_entry);
+SilcClientEntry
+silc_idlist_replace_client_id(SilcIDList id_list, SilcClientID *old_id,
+                             SilcClientID *new_id);
+void silc_idlist_client_destructor(SilcIDCache cache,
+                                  SilcIDCacheEntry entry);
+SilcChannelEntry
+silc_idlist_add_channel(SilcIDList id_list, char *channel_name, int mode,
+                       SilcChannelID *id, SilcServerEntry router,
+                       SilcCipher channel_key, SilcHmac hmac,
+                       int expire);
+int silc_idlist_del_channel(SilcIDList id_list, SilcChannelEntry entry);
+SilcChannelEntry
+silc_idlist_find_channel_by_name(SilcIDList id_list, char *name,
+                                SilcIDCacheEntry *ret_entry);
+SilcChannelEntry
+silc_idlist_find_channel_by_id(SilcIDList id_list, SilcChannelID *id,
+                              SilcIDCacheEntry *ret_entry);
+SilcChannelEntry
+silc_idlist_replace_channel_id(SilcIDList id_list, SilcChannelID *old_id,
+                              SilcChannelID *new_id);
+SilcChannelEntry *
+silc_idlist_get_channels(SilcIDList id_list, SilcChannelID *channel_id,
+                        uint32 *channels_count);
 
 #endif
diff --git a/apps/silcd/leevi.conf b/apps/silcd/leevi.conf
deleted file mode 100644 (file)
index d94631f..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-[Cipher]
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[HashFunction]
-md5::64:16
-sha1::64:20
-
-#[PKCS]
-#rsa::1024
-#dss::1024
-
-[ServerInfo]
-leevi.kuo.fi.ssh.com:10.2.1.7:Kuopio, Finland:1333
-
-[AdminInfo]
-Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-[ListenPort]
-10.2.1.7:10.2.1.7:1333
-
-[Logging]
-infologfile:silcd.log:10000
-#warninglogfile:/var/log/silcd_warning.log:10000
-errorlogfile:leevi_error.log:10000
-#fatallogfile:/var/log/silcd_error.log:
-
-[ConnectionClass]
-1:100:100:100
-2:200:300:400
-
-[ClientConnection]
-10.2.1.199:passwd:priikone:333:1
-:::1333:1
-
-[AdminConnection]
-10.2.1.199:passwd:priikone:priikone:1
-
-[ServerConnection]
-10.2.1.7:passwd:priikone:1334:1:1
-
-[RouterConnection]
-
-[DenyConnection]
-[RedirectClient]
diff --git a/apps/silcd/leevi2.conf b/apps/silcd/leevi2.conf
deleted file mode 100644 (file)
index 4368321..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-[Cipher]
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[HashFunction]
-md5::64:16
-sha1::64:20
-
-#[PKCS]
-#rsa::1024
-#dss::1024
-
-[ServerInfo]
-leevi.kuo.fi.ssh.com:10.2.1.7:Kuopio, Finland:1334
-
-[AdminInfo]
-Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-[ListenPort]
-10.2.1.7:10.2.1.7:1334
-
-[Logging]
-infologfile:silcd.log:10000
-#warninglogfile:/var/log/silcd_warning.log:10000
-errorlogfile:leevi2_error.log:10000
-#fatallogfile:/var/log/silcd_error.log:
-
-[ConnectionClass]
-1:100:100:100
-2:200:300:400
-
-[ClientConnection]
-10.2.1.199:passwd:priikone:333:1
-:::1333:1
-
-[AdminConnection]
-10.2.1.199:passwd:priikone:priikone:1
-
-[ServerConnection]
-
-[RouterConnection]
-10.2.1.7:passwd:priikone:1333:1:1
-
-[DenyConnection]
-[RedirectClient]
diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c
new file mode 100644 (file)
index 0000000..2f3aa2f
--- /dev/null
@@ -0,0 +1,2605 @@
+/*
+
+  packet_receive.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/*
+ * Server packet routines to handle received packets.
+ */
+/* $Id$ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+extern char *server_version;
+
+/* Received notify packet. Server can receive notify packets from router. 
+   Server then relays the notify messages to clients if needed. */
+
+void silc_server_notify(SilcServer server,
+                       SilcSocketConnection sock,
+                       SilcPacketContext *packet)
+{
+  SilcNotifyPayload payload;
+  SilcNotifyType type;
+  SilcArgumentPayload args;
+  SilcChannelID *channel_id = NULL, *channel_id2;
+  SilcClientID *client_id, *client_id2;
+  SilcServerID *server_id;
+  SilcChannelEntry channel;
+  SilcClientEntry client;
+  SilcServerEntry server_entry;
+  SilcChannelClientEntry chl;
+  SilcIDCacheEntry cache;
+  SilcHashTableList htl;
+  uint32 mode;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  bool local;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  if (!packet->dst_id)
+    return;
+
+  /* If the packet is destined directly to a client then relay the packet
+     before processing it. */
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    SilcIDListData idata;
+    SilcSocketConnection dst_sock;
+
+    /* Get the route to the client */
+    dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                           packet->dst_id_len, NULL, &idata);
+    if (dst_sock)
+      /* Relay the packet */
+      silc_server_relay_packet(server, dst_sock, idata->send_key,
+                              idata->hmac_receive, idata->psn_send++,
+                              packet, TRUE);
+  }
+
+  /* Parse the Notify Payload */
+  payload = silc_notify_payload_parse(packet->buffer->data,
+                                     packet->buffer->len);
+  if (!payload)
+    return;
+
+  /* If we are router and this packet is not already broadcast packet
+     we will broadcast it. The sending socket really cannot be router or
+     the router is buggy. If this packet is coming from router then it must
+     have the broadcast flag set already and we won't do anything. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received Notify packet"));
+    if (packet->dst_id_type == SILC_ID_CHANNEL) {
+      /* Packet is destined to channel */
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+
+      silc_server_packet_send_dest(server, server->router->connection, 
+                                  packet->type,
+                                  packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, packet->buffer->len, 
+                                  FALSE);
+      silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, 
+                                  packet->type, packet->flags,
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, packet->buffer->len, 
+                                  FALSE, TRUE);
+    } else {
+      /* Packet is destined to client or server */
+      silc_server_packet_send(server, server->router->connection, 
+                             packet->type,
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE);
+      silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
+                             packet->type, packet->flags,
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE, TRUE);
+    }
+  }
+
+  type = silc_notify_get_type(payload);
+  args = silc_notify_get_args(payload);
+  if (!args)
+    goto out;
+
+  switch(type) {
+  case SILC_NOTIFY_TYPE_JOIN:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("JOIN notify"));
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* If the the client is not in local list we check global list (ie. the
+       channel will be global channel) and if it does not exist then create
+       entry for the client. */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, server->server_type, 
+                                          NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, server->server_type,
+                                            NULL);
+      if (!client) {
+       /* If router did not find the client the it is bogus */
+       if (server->server_type != SILC_SERVER)
+         goto out;
+
+       client = 
+         silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+                                silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                sock->user_data, NULL, 0);
+       if (!client) {
+         SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+         silc_free(client_id);
+         goto out;
+       }
+
+       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+      }
+    }
+
+    /* Do not process the notify if the client is not registered */
+    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
+      break;
+
+    /* Do not add client to channel if it is there already */
+    if (silc_server_client_on_channel(client, channel)) {
+      SILC_LOG_DEBUG(("Client already on channel"));
+      break;
+    }
+
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    if (server->server_type != SILC_ROUTER && 
+       sock->type == SILC_SOCKET_TYPE_ROUTER)
+      /* The channel is global now */
+      channel->global_users = TRUE;
+
+    SILC_LOG_DEBUG(("Joining to channel %s", channel->channel_name));
+
+    /* JOIN the global client to the channel (local clients (if router 
+       created the channel) is joined in the pending JOIN command). */
+    chl = silc_calloc(1, sizeof(*chl));
+    chl->client = client;
+    chl->channel = channel;
+
+    /* If this is the first one on the channel then it is the founder of
+       the channel. */
+    if (!silc_hash_table_count(channel->user_list))
+      chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
+    silc_hash_table_add(channel->user_list, client, chl);
+    silc_hash_table_add(client->channels, channel, chl);
+    silc_free(client_id);
+
+    break;
+
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("LEAVE notify"));
+
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) { 
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id) {
+      silc_free(channel_id);
+      goto out;
+    }
+
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       silc_free(client_id);
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
+
+    /* Check if on channel */
+    if (!silc_server_client_on_channel(client, channel))
+      break;
+
+    /* Send the leave notify to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    /* Remove the user from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+    break;
+
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    SILC_LOG_DEBUG(("SIGNOFF notify"));
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, &cache);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, &cache);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
+
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
+
+    /* Update statistics */
+    server->stat.clients--;
+    if (server->server_type == SILC_ROUTER)
+      server->stat.cell_clients--;
+    SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+    /* Remove the client from all channels. */
+    silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
+
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+
+    SILC_LOG_DEBUG(("TOPIC SET notify"));
+
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+
+    /* Get the topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+
+    silc_free(channel->topic);
+    channel->topic = strdup(tmp);
+
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+    silc_free(channel_id);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    {
+      /* 
+       * Distribute the notify to local clients on the channel
+       */
+      unsigned char *id, *id2;
+
+      SILC_LOG_DEBUG(("NICK CHANGE notify"));
+      
+      /* Get old client ID */
+      id = silc_argument_get_arg_type(args, 1, &tmp_len);
+      if (!id)
+       goto out;
+      client_id = silc_id_payload_parse_id(id, tmp_len);
+      if (!client_id)
+       goto out;
+      
+      /* Get new client ID */
+      id2 = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (!id2)
+       goto out;
+      client_id2 = silc_id_payload_parse_id(id2, tmp_len);
+      if (!client_id2)
+       goto out;
+      
+      SILC_LOG_DEBUG(("Old Client ID id(%s)", 
+                     silc_id_render(client_id, SILC_ID_CLIENT)));
+      SILC_LOG_DEBUG(("New Client ID id(%s)", 
+                     silc_id_render(client_id2, SILC_ID_CLIENT)));
+
+      /* Replace the Client ID */
+      client = silc_idlist_replace_client_id(server->global_list, client_id,
+                                            client_id2);
+      if (!client)
+       client = silc_idlist_replace_client_id(server->local_list, client_id, 
+                                              client_id2);
+
+      if (client) {
+       /* The nickname is not valid anymore, set it NULL. This causes that
+          the nickname will be queried if someone wants to know it. */
+       if (client->nickname)
+         silc_free(client->nickname);
+       client->nickname = NULL;
+
+       /* Send the NICK_CHANGE notify type to local clients on the channels
+          this client is joined to. */
+       silc_server_send_notify_on_channels(server, NULL, client, 
+                                           SILC_NOTIFY_TYPE_NICK_CHANGE, 2,
+                                           id, tmp_len, 
+                                           id2, tmp_len);
+      }
+
+      silc_free(client_id);
+      if (!client)
+       silc_free(client_id2);
+      break;
+    }
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    
+    SILC_LOG_DEBUG(("CMODE CHANGE notify"));
+      
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_free(channel_id);
+      goto out;
+    }
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Check if mode changed */
+    if (channel->mode == mode)
+      break;
+
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    /* If the channel had private keys set and the mode was removed then
+       we must re-generate and re-distribute a new channel key */
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
+       !(mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0))
+       goto out;
+      
+      /* Send the channel key. This sends it to our local clients and if
+        we are normal server to our router as well. */
+      silc_server_send_channel_key(server, NULL, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
+
+    /* Change mode */
+    channel->mode = mode;
+    silc_free(channel_id);
+
+    /* Get the hmac */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp) {
+      unsigned char hash[32];
+
+      if (channel->hmac)
+       silc_hmac_free(channel->hmac);
+      if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
+       goto out;
+
+      /* Set the HMAC key out of current channel key. The client must do
+        this locally. */
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, 
+                    channel->key_len / 8, 
+                    hash);
+      silc_hmac_set_key(channel->hmac, hash, 
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
+    }
+
+    /* Get the passphrase */
+    tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+    if (tmp) {
+      silc_free(channel->passphrase);
+      channel->passphrase = strdup(tmp);
+    }
+
+    break;
+
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    {
+      /* 
+       * Distribute the notify to local clients on the channel
+       */
+      SilcChannelClientEntry chl2 = NULL;
+      bool notify_sent = FALSE;
+      
+      SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
+      
+      if (!channel_id) {
+       channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                   packet->dst_id_type);
+       if (!channel_id)
+         goto out;
+      }
+
+      /* Get channel entry */
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                                channel_id, NULL);
+       if (!channel) {
+         silc_free(channel_id);
+         goto out;
+       }
+      }
+
+      /* Get the mode */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (!tmp) {
+       silc_free(channel_id);
+       goto out;
+      }
+      
+      SILC_GET32_MSB(mode, tmp);
+      
+      /* Get target client */
+      tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+      if (!tmp)
+       goto out;
+      client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!client_id)
+       goto out;
+      
+      /* Get client entry */
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       client = silc_idlist_find_client_by_id(server->local_list, 
+                                              client_id, TRUE, NULL);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+      silc_free(client_id);
+
+      /* Get entry to the channel user list */
+      silc_hash_table_list(channel->user_list, &htl);
+      while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+       /* If the mode is channel founder and we already find a client 
+          to have that mode on the channel we will enforce the sender
+          to change the channel founder mode away. There can be only one
+          channel founder on the channel. */
+       if (server->server_type == SILC_ROUTER &&
+           mode & SILC_CHANNEL_UMODE_CHANFO &&
+           chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+         SilcBuffer idp;
+         unsigned char cumode[4];
+
+         if (chl->client == client && chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
+         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+         silc_server_send_notify_cumode(server, sock, FALSE, channel, mode,
+                                        client->id, SILC_ID_CLIENT,
+                                        client->id);
+         
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         SILC_PUT32_MSB(mode, cumode);
+         silc_server_send_notify_to_channel(server, sock, channel, FALSE, 
+                                            SILC_NOTIFY_TYPE_CUMODE_CHANGE,
+                                            3, idp->data, idp->len,
+                                            cumode, 4,
+                                            idp->data, idp->len);
+         silc_buffer_free(idp);
+         notify_sent = TRUE;
+
+         /* Force the mode change if we alredy set the mode */
+         if (chl2) {
+           chl2->mode = mode;
+           silc_free(channel_id);
+           silc_hash_table_list_reset(&htl);
+           goto out;
+         }
+       }
+       
+       if (chl->client == client) {
+         if (chl->mode == mode) {
+           notify_sent = TRUE;
+           break;
+         }
+
+         SILC_LOG_DEBUG(("Changing the channel user mode"));
+
+         /* Change the mode */
+         chl->mode = mode;
+         if (!(mode & SILC_CHANNEL_UMODE_CHANFO))
+           break;
+         
+         chl2 = chl;
+       }
+      }
+      silc_hash_table_list_reset(&htl);
+      
+      /* Send the same notify to the channel */
+      if (!notify_sent)
+       silc_server_packet_send_to_channel(server, sock, channel, 
+                                          packet->type, 
+                                          FALSE, packet->buffer->data, 
+                                          packet->buffer->len, FALSE);
+      
+      silc_free(channel_id);
+      break;
+    }
+
+  case SILC_NOTIFY_TYPE_INVITE:
+
+    if (packet->dst_id_type == SILC_ID_CLIENT)
+      goto out;
+
+    SILC_LOG_DEBUG(("INVITE notify"));
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
+
+    /* Get the added invite */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp) {
+      if (!channel->invite_list)
+       channel->invite_list = silc_calloc(tmp_len + 2, 
+                                          sizeof(*channel->invite_list));
+      else
+       channel->invite_list = silc_realloc(channel->invite_list, 
+                                           sizeof(*channel->invite_list) * 
+                                           (tmp_len + 
+                                            strlen(channel->invite_list) + 
+                                            2));
+      if (tmp[tmp_len - 1] == ',')
+       tmp[tmp_len - 1] = '\0';
+      
+      strncat(channel->invite_list, tmp, tmp_len);
+      strncat(channel->invite_list, ",", 1);
+    }
+
+    /* Get the deleted invite */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp && channel->invite_list) {
+      char *start, *end, *n;
+      
+      if (!strncmp(channel->invite_list, tmp, 
+                  strlen(channel->invite_list) - 1)) {
+       silc_free(channel->invite_list);
+       channel->invite_list = NULL;
+      } else {
+       start = strstr(channel->invite_list, tmp);
+       if (start && strlen(start) >= tmp_len) {
+         end = start + tmp_len;
+         n = silc_calloc(strlen(channel->invite_list) - tmp_len, sizeof(*n));
+         strncat(n, channel->invite_list, start - channel->invite_list);
+         strncat(n, end + 1, ((channel->invite_list + 
+                               strlen(channel->invite_list)) - end) - 1);
+         silc_free(channel->invite_list);
+         channel->invite_list = n;
+       }
+      }
+    }
+
+    break;
+
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    /*
+     * Distribute to the local clients on the channel and change the
+     * channel ID.
+     */
+
+    SILC_LOG_DEBUG(("CHANNEL CHANGE"));
+
+    if (sock->type != SILC_SOCKET_TYPE_ROUTER)
+      break;
+
+    /* Get the old Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get the channel entry */
+    channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+
+    /* Send the notify to the channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    /* Get the new Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id2 = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id2)
+      goto out;
+
+    SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
+                   silc_id_render(channel_id, SILC_ID_CHANNEL)));
+    SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+                   silc_id_render(channel_id2, SILC_ID_CHANNEL)));
+
+    /* Replace the Channel ID */
+    if (!silc_idlist_replace_channel_id(server->local_list, channel_id,
+                                       channel_id2))
+      if (!silc_idlist_replace_channel_id(server->global_list, channel_id,
+                                         channel_id2)) {
+       silc_free(channel_id2);
+       channel_id2 = NULL;
+      }
+
+    if (channel_id2) {
+      SilcBuffer users = NULL, users_modes = NULL;
+
+      /* Re-announce this channel which ID was changed. */
+      silc_server_send_new_channel(server, sock, FALSE, channel->channel_name,
+                                  channel->id, 
+                                  silc_id_get_len(channel->id, 
+                                                  SILC_ID_CHANNEL),
+                                  channel->mode);
+
+      /* Re-announce our clients on the channel as the ID has changed now */
+      silc_server_announce_get_channel_users(server, channel, &users,
+                                            &users_modes);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
+      if (users_modes) {
+       silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+       silc_server_packet_send_dest(server, sock,
+                                    SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                    channel->id, SILC_ID_CHANNEL,
+                                    users_modes->data, 
+                                    users_modes->len, FALSE);
+       silc_buffer_free(users_modes);
+      }
+
+      /* Re-announce channel's topic */
+      if (channel->topic) {
+       silc_server_send_notify_topic_set(server, sock,
+                                         server->server_type == SILC_ROUTER ?
+                                         TRUE : FALSE, channel, 
+                                         channel->id, SILC_ID_CHANNEL,
+                                         channel->topic);
+      }
+    }
+
+    silc_free(channel_id);
+
+    break;
+
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    /* 
+     * Remove the server entry and all clients that this server owns.
+     */
+
+    SILC_LOG_DEBUG(("SERVER SIGNOFF notify"));
+
+    /* Get Server ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    server_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!server_id)
+      goto out;
+
+    /* Get server entry */
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, TRUE, NULL);
+    local = TRUE;
+    if (!server_entry) {
+      server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                                  server_id, TRUE, NULL);
+      local = TRUE;
+      if (!server_entry) {
+       /* If we are normal server then we might not have the server. Check
+          whether router was kind enough to send the list of all clients
+          that actually was to be removed. Remove them if the list is
+          available. */
+       if (server->server_type != SILC_ROUTER &&
+           silc_argument_get_arg_num(args) > 1) {
+         int i;
+
+         for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+           /* Get Client ID */
+           tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
+           if (!tmp)
+             continue;
+           client_id = silc_id_payload_parse_id(tmp, tmp_len);
+           if (!client_id)
+             continue;
+
+           /* Get client entry */
+           client = silc_idlist_find_client_by_id(server->global_list, 
+                                                  client_id, TRUE, &cache);
+           local = TRUE;
+           if (!client) {
+             client = silc_idlist_find_client_by_id(server->local_list, 
+                                                    client_id, TRUE, &cache);
+             local = FALSE;
+             if (!client) {
+               silc_free(client_id);
+               continue;
+             }
+           }
+           silc_free(client_id);
+
+           /* Update statistics */
+           server->stat.clients--;
+           if (server->server_type == SILC_ROUTER)
+             server->stat.cell_clients--;
+           SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+           SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+           /* Remove the client from all channels. */
+           silc_server_remove_from_channels(server, NULL, client, 
+                                            TRUE, NULL, FALSE);
+
+           /* Remove the client */
+           silc_idlist_del_client(local ? server->local_list :
+                                  server->global_list, client);
+         }
+       }
+
+       silc_free(server_id);
+       goto out;
+      }
+    }
+    silc_free(server_id);
+
+    /* Free all client entries that this server owns as they will
+       become invalid now as well. */
+    silc_server_remove_clients_by_server(server, server_entry, TRUE);
+
+    /* Remove the server entry */
+    silc_idlist_del_server(local ? server->local_list :
+                          server->global_list, server_entry);
+
+    /* XXX update statistics */
+
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    /* 
+     * Distribute the notify to local clients on the channel
+     */
+    
+    SILC_LOG_DEBUG(("KICKED notify"));
+      
+    if (!channel_id) {
+      channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                                 packet->dst_id_type);
+      if (!channel_id)
+       goto out;
+    }
+
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* If the the client is not in local list we check global list */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+
+    /* Send to channel */
+    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
+    /* Remove the client from channel */
+    silc_server_remove_from_one_channel(server, sock, channel, client, FALSE);
+
+    break;
+
+  case SILC_NOTIFY_TYPE_KILLED:
+    {
+      /* 
+       * Distribute the notify to local clients on channels
+       */
+      unsigned char *id;
+      uint32 id_len;
+    
+      SILC_LOG_DEBUG(("KILLED notify"));
+      
+      /* Get client ID */
+      id = silc_argument_get_arg_type(args, 1, &id_len);
+      if (!id)
+       goto out;
+      client_id = silc_id_payload_parse_id(id, id_len);
+      if (!client_id)
+       goto out;
+
+      /* If the the client is not in local list we check global list */
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       client = silc_idlist_find_client_by_id(server->local_list, 
+                                              client_id, TRUE, NULL);
+       if (!client) {
+         silc_free(client_id);
+         goto out;
+       }
+      }
+      silc_free(client_id);
+
+      /* If the client is one of ours, then close the connection to the
+        client now. This removes the client from all channels as well. */
+      if (packet->dst_id_type == SILC_ID_CLIENT && client->connection) {
+       sock = client->connection;
+       silc_server_free_client_data(server, NULL, client, FALSE, NULL);
+       silc_server_close_connection(server, sock);
+       break;
+      }
+
+      /* Get comment */
+      tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+      if (tmp_len > 128)
+       tmp = NULL;
+
+      /* Send the notify to local clients on the channels except to the
+        client who is killed. */
+      silc_server_send_notify_on_channels(server, client, client,
+                                         SILC_NOTIFY_TYPE_KILLED, 
+                                         tmp ? 2 : 1,
+                                         id, id_len, 
+                                         tmp, tmp_len);
+
+      /* Remove the client from all channels */
+      silc_server_remove_from_channels(server, NULL, client, FALSE, NULL, 
+                                      FALSE);
+
+      break;
+    }
+
+  case SILC_NOTIFY_TYPE_UMODE_CHANGE:
+    /*
+     * Save the mode of the client.
+     */
+
+    SILC_LOG_DEBUG(("UMODE_CHANGE notify"));
+
+    /* Get client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Get client entry */
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->local_list, 
+                                            client_id, TRUE, NULL);
+      if (!client) {
+       silc_free(client_id);
+       goto out;
+      }
+    }
+    silc_free(client_id);
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    SILC_GET32_MSB(mode, tmp);
+
+#define SILC_UMODE_STATS_UPDATE(oper, mod)     \
+do {                                           \
+    if (client->mode & (mod)) {                        \
+      if (!(mode & (mod))) {                   \
+       if (client->connection)                 \
+         server->stat.my_ ## oper ## _ops--;   \
+        if (server->server_type == SILC_ROUTER)        \
+         server->stat. oper ## _ops--;         \
+      }                                                \
+    } else {                                   \
+      if (mode & (mod)) {                      \
+       if (client->connection)                 \
+         server->stat.my_ ## oper ## _ops++;   \
+        if (server->server_type == SILC_ROUTER)        \
+         server->stat. oper ## _ops++;         \
+      }                                                \
+    }                                          \
+} while(0)
+
+    /* Update statistics */
+    SILC_UMODE_STATS_UPDATE(server, SILC_UMODE_SERVER_OPERATOR);
+    SILC_UMODE_STATS_UPDATE(router, SILC_UMODE_ROUTER_OPERATOR);
+
+    /* Save the mode */
+    client->mode = mode;
+
+    break;
+
+  case SILC_NOTIFY_TYPE_BAN:
+    /*
+     * Save the ban
+     */
+
+    SILC_LOG_DEBUG(("BAN notify"));
+    
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+    
+    /* Get channel entry */
+    channel = silc_idlist_find_channel_by_id(server->global_list, 
+                                            channel_id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->local_list, 
+                                              channel_id, NULL);
+      if (!channel) {
+       silc_free(channel_id);
+       goto out;
+      }
+    }
+    silc_free(channel_id);
+
+    /* Get the new ban and add it to the ban list */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp) {
+      if (!channel->ban_list)
+       channel->ban_list = silc_calloc(tmp_len + 2, 
+                                       sizeof(*channel->ban_list));
+      else
+       channel->ban_list = silc_realloc(channel->ban_list, 
+                                        sizeof(*channel->ban_list) * 
+                                        (tmp_len + 
+                                         strlen(channel->ban_list) + 2));
+      strncat(channel->ban_list, tmp, tmp_len);
+      strncat(channel->ban_list, ",", 1);
+    }
+
+    /* Get the ban to be removed and remove it from the list */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (tmp && channel->ban_list) {
+      char *start, *end, *n;
+      
+      if (!strncmp(channel->ban_list, tmp, strlen(channel->ban_list) - 1)) {
+       silc_free(channel->ban_list);
+       channel->ban_list = NULL;
+      } else {
+       start = strstr(channel->ban_list, tmp);
+       if (start && strlen(start) >= tmp_len) {
+         end = start + tmp_len;
+         n = silc_calloc(strlen(channel->ban_list) - tmp_len, sizeof(*n));
+         strncat(n, channel->ban_list, start - channel->ban_list);
+         strncat(n, end + 1, ((channel->ban_list + 
+                               strlen(channel->ban_list)) - end) - 1);
+         silc_free(channel->ban_list);
+         channel->ban_list = n;
+       }
+      }
+    }
+    break;
+
+    /* Ignore rest of the notify types for now */
+  case SILC_NOTIFY_TYPE_NONE:
+  case SILC_NOTIFY_TYPE_MOTD:
+    break;
+  default:
+    break;
+  }
+
+ out:
+  silc_notify_payload_free(payload);
+}
+
+void silc_server_notify_list(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcPacketContext *new;
+  SilcBuffer buffer;
+  uint16 len;
+
+  SILC_LOG_DEBUG(("Processing Notify List"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  /* Make copy of the original packet context, except for the actual
+     data buffer, which we will here now fetch from the original buffer. */
+  new = silc_packet_context_alloc();
+  new->type = SILC_PACKET_NOTIFY;
+  new->flags = packet->flags;
+  new->src_id = packet->src_id;
+  new->src_id_len = packet->src_id_len;
+  new->src_id_type = packet->src_id_type;
+  new->dst_id = packet->dst_id;
+  new->dst_id_len = packet->dst_id_len;
+  new->dst_id_type = packet->dst_id_type;
+
+  buffer = silc_buffer_alloc(1024);
+  new->buffer = buffer;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(len, packet->buffer->data + 2);
+    if (len > packet->buffer->len)
+      break;
+
+    if (len > buffer->truelen) {
+      silc_buffer_free(buffer);
+      buffer = silc_buffer_alloc(1024 + len);
+    }
+
+    silc_buffer_pull_tail(buffer, len);
+    silc_buffer_put(buffer, packet->buffer->data, len);
+
+    /* Process the Notify */
+    silc_server_notify(server, sock, new);
+
+    silc_buffer_push_tail(buffer, len);
+    silc_buffer_pull(packet->buffer, len);
+  }
+
+  silc_buffer_free(buffer);
+  silc_free(new);
+}
+
+/* Received private message. This resolves the destination of the message 
+   and sends the packet. This is used by both server and router.  If the
+   destination is our locally connected client this sends the packet to
+   the client. This may also send the message for further routing if
+   the destination is not in our server (or router). */
+
+void silc_server_private_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT || !packet->dst_id)
+    return;
+
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock) {
+    /* Send IDENTIFY command reply with error status to indicate that
+       such destination ID does not exist or is invalid */
+    SilcBuffer idp = silc_id_payload_encode_data(packet->dst_id,
+                                                packet->dst_id_len,
+                                                packet->dst_id_type);
+    if (!idp)
+      return;
+
+    if (packet->src_id_type == SILC_ID_CLIENT) {
+      SilcClientID *client_id = silc_id_str2id(packet->src_id,
+                                              packet->src_id_len,
+                                              packet->src_id_type);
+      silc_server_send_dest_command_reply(server, sock, 
+                                         client_id, SILC_ID_CLIENT,
+                                         SILC_COMMAND_IDENTIFY,
+                                         SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
+                                         0, 1, 2, idp->data, idp->len);
+      silc_free(client_id);
+    } else {
+      silc_server_send_command_reply(server, sock, SILC_COMMAND_IDENTIFY,
+                                    SILC_STATUS_ERR_NO_SUCH_CLIENT_ID, 
+                                    0, 1, 2, idp->data, idp->len);
+    }
+
+    silc_buffer_free(idp);
+    return;
+  }
+
+  /* Send the private message */
+  silc_server_send_private_message(server, dst_sock, idata->send_key,
+                                  idata->hmac_send, idata->psn_send++,
+                                  packet);
+}
+
+/* Received private message key packet.. This packet is never for us. It is to
+   the client in the packet's destination ID. Sending of this sort of packet
+   equals sending private message, ie. it is sent point to point from
+   one client to another. */
+
+void silc_server_private_message_key(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
+    return;
+
+  if (!packet->dst_id)
+    return;
+
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
+    return;
+
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++, packet, FALSE);
+}
+
+/* Processes incoming command reply packet. The command reply packet may
+   be destined to one of our clients or it may directly for us. We will 
+   call the command reply routine after processing the packet. */
+
+void silc_server_command_reply(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientEntry client = NULL;
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+  SilcClientID *id = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Source must be server or router */
+  if (packet->src_id_type != SILC_ID_SERVER &&
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
+    return;
+
+  if (packet->dst_id_type == SILC_ID_CHANNEL)
+    return;
+
+  if (packet->dst_id_type == SILC_ID_CLIENT) {
+    /* Destination must be one of ours */
+    id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CLIENT);
+    if (!id)
+      return;
+    client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
+    if (!client) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown client"));
+      silc_free(id);
+      return;
+    }
+  }
+
+  if (packet->dst_id_type == SILC_ID_SERVER) {
+    /* For now this must be for us */
+    if (memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+      SILC_LOG_ERROR(("Cannot process command reply to unknown server"));
+      return;
+    }
+  }
+
+  /* Execute command reply locally for the command */
+  silc_server_command_reply_process(server, sock, buffer);
+
+  if (packet->dst_id_type == SILC_ID_CLIENT && client && id) {
+    /* Relay the packet to the client */
+    
+    dst_sock = (SilcSocketConnection)client->connection;
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                    + packet->dst_id_len + packet->padlen);
+    
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+    
+    idata = (SilcIDListData)client;
+    
+    /* Encrypt packet */
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
+                       dst_sock->outbuf, buffer->len);
+    
+    /* Send the packet */
+    silc_server_packet_send_real(server, dst_sock, TRUE);
+
+    silc_free(id);
+  }
+}
+
+/* Process received channel message. The message can be originated from
+   client or server. */
+
+void silc_server_channel_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  SilcChannelEntry channel = NULL;
+  SilcChannelID *id = NULL;
+  void *sender = NULL;
+  void *sender_entry = NULL;
+  bool local = TRUE;
+
+  SILC_LOG_DEBUG(("Processing channel message"));
+
+  /* Sanity checks */
+  if (packet->dst_id_type != SILC_ID_CHANNEL) {
+    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
+    goto out;
+  }
+
+  /* Find channel entry */
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+  if (!id)
+    goto out;
+  channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+  if (!channel) {
+    channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+    if (!channel) {
+      SILC_LOG_DEBUG(("Could not find channel"));
+      goto out;
+    }
+  }
+
+  /* See that this client is on the channel. If the original sender is
+     not client (as it can be server as well) we don't do the check. */
+  sender = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                         packet->src_id_type);
+  if (!sender)
+    goto out;
+  if (packet->src_id_type == SILC_ID_CLIENT) {
+    sender_entry = silc_idlist_find_client_by_id(server->local_list, 
+                                                sender, TRUE, NULL);
+    if (!sender_entry) {
+      local = FALSE;
+      sender_entry = silc_idlist_find_client_by_id(server->global_list, 
+                                                  sender, TRUE, NULL);
+    }
+    if (!sender_entry || !silc_server_client_on_channel(sender_entry, 
+                                                       channel)) {
+      SILC_LOG_DEBUG(("Client not on channel"));
+      goto out;
+    }
+
+    /* If the packet is coming from router, but the client entry is
+       local entry to us then some router is rerouting this to us and it is
+       not allowed. */
+    if (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER && local) {
+      SILC_LOG_DEBUG(("Channel message rerouted to the sender, drop it"));
+      goto out;
+    }
+  }
+
+  /* Distribute the packet to our local clients. This will send the
+     packet for further routing as well, if needed. */
+  silc_server_packet_relay_to_channel(server, sock, channel, sender,
+                                     packet->src_id_type, sender_entry,
+                                     packet->buffer->data,
+                                     packet->buffer->len, FALSE);
+
+ out:
+  if (sender)
+    silc_free(sender);
+  if (id)
+    silc_free(id);
+}
+
+/* Received channel key packet. We distribute the key to all of our locally
+   connected clients on the channel. */
+
+void silc_server_channel_key(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcChannelEntry channel;
+
+  if (packet->src_id_type != SILC_ID_SERVER ||
+      (server->server_type == SILC_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_ROUTER))
+    return;
+
+  /* Save the channel key */
+  channel = silc_server_save_channel_key(server, buffer, NULL);
+  if (!channel)
+    return;
+
+  /* Distribute the key to everybody who is on the channel. If we are router
+     we will also send it to locally connected servers. */
+  silc_server_send_channel_key(server, sock, channel, FALSE);
+  
+  if (server->server_type != SILC_BACKUP_ROUTER) {
+    /* Distribute to local cell backup routers. */
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           SILC_PACKET_CHANNEL_KEY, 0,
+                           buffer->data, buffer->len, FALSE, TRUE);
+  }
+}
+
+/* Received New Client packet and processes it.  Creates Client ID for the
+   client. Client becomes registered after calling this functions. */
+
+SilcClientEntry silc_server_new_client(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientEntry client;
+  SilcClientID *client_id;
+  SilcBuffer reply;
+  SilcIDListData idata;
+  char *username = NULL, *realname = NULL, *id_string;
+  uint16 username_len;
+  uint32 id_len;
+  int ret;
+  char *hostname, *nickname;
+  int nickfail = 0;
+
+  SILC_LOG_DEBUG(("Creating new client"));
+
+  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+    return NULL;
+
+  /* Take client entry */
+  client = (SilcClientEntry)sock->user_data;
+  idata = (SilcIDListData)client;
+
+  /* Remove the old cache entry. */
+  if (!silc_idcache_del_by_context(server->local_list->clients, client)) {
+    SILC_LOG_ERROR(("Lost client's cache entry - bad thing"));
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Unknown client");
+    return NULL;
+  }
+
+  /* Parse incoming packet */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&username, 
+                                                        &username_len),
+                            SILC_STR_UI16_STRING_ALLOC(&realname),
+                            SILC_STR_END);
+  if (ret == -1) {
+    silc_free(username);
+    silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
+
+  if (!username) {
+    silc_free(username);
+    silc_free(realname);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                  "Incomplete client information");
+    return NULL;
+  }
+
+  if (username_len > 128)
+    username[128] = '\0';
+
+  /* Check for bad characters for nickname, and modify the nickname if
+     it includes those. */
+  if (silc_server_name_bad_chars(username, username_len)) {
+    nickname = silc_server_name_modify_bad(username, username_len);
+  } else {
+    nickname = strdup(username);
+  }
+
+  /* Make sanity checks for the hostname of the client. If the hostname
+     is provided in the `username' check that it is the same than the
+     resolved hostname, or if not resolved the hostname that appears in
+     the client's public key. If the hostname is not present then put
+     it from the resolved name or from the public key. */
+  if (strchr(username, '@')) {
+    SilcPublicKeyIdentifier pident;
+    int tlen = strcspn(username, "@");
+    char *phostname = NULL;
+
+    hostname = silc_calloc((strlen(username) - tlen) + 1, sizeof(char));
+    memcpy(hostname, username + tlen + 1, strlen(username) - tlen - 1);
+
+    if (strcmp(sock->hostname, sock->ip) && 
+       strcmp(sock->hostname, hostname)) {
+      silc_free(username);
+      silc_free(hostname);
+      silc_free(realname);
+      silc_server_disconnect_remote(server, sock, 
+                                   "Server closed connection: "
+                                   "Incomplete client information");
+      return NULL;
+    }
+    
+    pident = silc_pkcs_decode_identifier(client->data.public_key->identifier);
+    if (pident) {
+      phostname = strdup(pident->host);
+      silc_pkcs_free_identifier(pident);
+    }
+
+    if (!strcmp(sock->hostname, sock->ip) && 
+       phostname && strcmp(phostname, hostname)) {
+      silc_free(username);
+      silc_free(hostname);
+      silc_free(phostname);
+      silc_free(realname);
+      silc_server_disconnect_remote(server, sock, 
+                                   "Server closed connection: "
+                                   "Incomplete client information");
+      return NULL;
+    }
+    
+    silc_free(phostname);
+  } else {
+    /* The hostname is not present, add it. */
+    char *newusername;
+    /* XXX For now we cannot take the host name from the public key since
+       they are not trusted or we cannot verify them as trusted. Just take
+       what the resolved name or address is. */
+#if 0
+    if (strcmp(sock->hostname, sock->ip)) {
+#endif
+      newusername = silc_calloc(strlen(username) + 
+                               strlen(sock->hostname) + 2,
+                               sizeof(*newusername));
+      strncat(newusername, username, strlen(username));
+      strncat(newusername, "@", 1);
+      strncat(newusername, sock->hostname, strlen(sock->hostname));
+      silc_free(username);
+      username = newusername;
+#if 0
+    } else {
+      SilcPublicKeyIdentifier pident = 
+       silc_pkcs_decode_identifier(client->data.public_key->identifier);
+      
+      if (pident) {
+       newusername = silc_calloc(strlen(username) + 
+                                 strlen(pident->host) + 2,
+                                 sizeof(*newusername));
+       strncat(newusername, username, strlen(username));
+       strncat(newusername, "@", 1);
+       strncat(newusername, pident->host, strlen(pident->host));
+       silc_free(username);
+       username = newusername;
+       silc_pkcs_free_identifier(pident);
+      }
+    }
+#endif
+  }
+
+  /* Create Client ID */
+  while (!silc_id_create_client_id(server, server->id, server->rng, 
+                                  server->md5hash, nickname, &client_id)) {
+    nickfail++;
+    snprintf(&nickname[strlen(nickname) - 1], 1, "%d", nickfail);
+  }
+
+  /* Update client entry */
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+  client->nickname = nickname;
+  client->username = username;
+  client->userinfo = realname ? realname : strdup(" ");
+  client->id = client_id;
+  id_len = silc_id_get_len(client_id, SILC_ID_CLIENT);
+
+  /* Add the client again to the ID cache */
+  silc_idcache_add(server->local_list->clients, client->nickname,
+                  client_id, client, 0, NULL);
+
+  /* Notify our router about new client on the SILC network */
+  if (!server->standalone)
+    silc_server_send_new_id(server, (SilcSocketConnection) 
+                           server->router->connection, 
+                           server->server_type == SILC_ROUTER ? TRUE : FALSE,
+                           client->id, SILC_ID_CLIENT, id_len);
+  
+  /* Send the new client ID to the client. */
+  id_string = silc_id_id2str(client->id, SILC_ID_CLIENT);
+  reply = silc_buffer_alloc(2 + 2 + id_len);
+  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
+  silc_buffer_format(reply,
+                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
+                    SILC_STR_UI_SHORT(id_len),
+                    SILC_STR_UI_XNSTRING(id_string, id_len),
+                    SILC_STR_END);
+  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
+                         reply->data, reply->len, FALSE);
+  silc_free(id_string);
+  silc_buffer_free(reply);
+
+  /* Send some nice info to the client */
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Welcome to the SILC Network %s",
+                          username));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your host is %s, running version %s",
+                          server->config->server_info->server_name,
+                          server_version));
+  if (server->server_type == SILC_ROUTER) {
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("There are %d clients on %d servers in SILC "
+                            "Network", server->stat.clients,
+                            server->stat.servers + 1));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("There are %d clients on %d server in our cell",
+                            server->stat.cell_clients,
+                            server->stat.cell_servers + 1));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("I have %d clients, %d channels, %d servers and "
+                            "%d routers",
+                            server->stat.my_clients, 
+                            server->stat.my_channels,
+                            server->stat.my_servers,
+                            server->stat.my_routers));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("There are %d server operators and %d router "
+                            "operators online",
+                            server->stat.server_ops,
+                            server->stat.router_ops));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("I have %d operators online",
+                            server->stat.my_router_ops +
+                            server->stat.my_server_ops));
+  } else {
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("I have %d clients and %d channels formed",
+                            server->stat.my_clients,
+                            server->stat.my_channels));
+    SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                           ("%d operators online",
+                            server->stat.my_server_ops));
+  }
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your connection is secured with %s cipher, "
+                          "key length %d bits",
+                          idata->send_key->cipher->name,
+                          idata->send_key->cipher->key_len));
+  SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE,
+                         ("Your current nickname is %s",
+                          client->nickname));
+
+  /* Send motd */
+  silc_server_send_motd(server, sock);
+
+  return client;
+}
+
+/* Create new server. This processes received New Server packet and
+   saves the received Server ID. The server is our locally connected
+   server thus we save all the information and save it to local list. 
+   This funtion can be used by both normal server and router server.
+   If normal server uses this it means that its router has connected
+   to the server. If router uses this it means that one of the cell's
+   servers is connected to the router. */
+
+SilcServerEntry silc_server_new_server(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcServerEntry new_server, server_entry;
+  SilcServerID *server_id;
+  SilcIDListData idata;
+  unsigned char *server_name, *id_string;
+  uint16 id_len, name_len;
+  int ret;
+  bool local = TRUE;
+
+  SILC_LOG_DEBUG(("Creating new server"));
+
+  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
+      sock->type != SILC_SOCKET_TYPE_ROUTER)
+    return NULL;
+
+  /* Take server entry */
+  new_server = (SilcServerEntry)sock->user_data;
+  idata = (SilcIDListData)new_server;
+
+  /* Remove the old cache entry */
+  if (!silc_idcache_del_by_context(server->local_list->servers, new_server)) {
+    silc_idcache_del_by_context(server->global_list->servers, new_server);
+    local = FALSE;
+  }
+
+  /* Parse the incoming packet */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&id_string, &id_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&server_name, 
+                                                        &name_len),
+                            SILC_STR_END);
+  if (ret == -1) {
+    if (id_string)
+      silc_free(id_string);
+    if (server_name)
+      silc_free(server_name);
+    return NULL;
+  }
+
+  if (id_len > buffer->len) {
+    silc_free(id_string);
+    silc_free(server_name);
+    return NULL;
+  }
+
+  if (name_len > 256)
+    server_name[255] = '\0';
+
+  /* Get Server ID */
+  server_id = silc_id_str2id(id_string, id_len, SILC_ID_SERVER);
+  if (!server_id) {
+    silc_free(id_string);
+    silc_free(server_name);
+    return NULL;
+  }
+  silc_free(id_string);
+
+  /* Check that we do not have this ID already */
+  server_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                              server_id, TRUE, NULL);
+  if (server_entry) {
+    silc_idcache_del_by_context(server->local_list->servers, server_entry);
+  } else {
+    server_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                                server_id, TRUE, NULL);
+    if (server_entry) 
+      silc_idcache_del_by_context(server->global_list->servers, server_entry);
+  }
+
+  /* Update server entry */
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+  new_server->server_name = server_name;
+  new_server->id = server_id;
+  
+  SILC_LOG_DEBUG(("New server id(%s)",
+                 silc_id_render(server_id, SILC_ID_SERVER)));
+
+  /* Add again the entry to the ID cache. */
+  silc_idcache_add(local ? server->local_list->servers : 
+                  server->global_list->servers, server_name, server_id, 
+                  new_server, 0, NULL);
+
+  /* Distribute the information about new server in the SILC network
+     to our router. If we are normal server we won't send anything
+     since this connection must be our router connection. */
+  if (server->server_type == SILC_ROUTER && !server->standalone &&
+      server->router->connection != sock)
+    silc_server_send_new_id(server, server->router->connection,
+                           TRUE, new_server->id, SILC_ID_SERVER, 
+                           silc_id_get_len(server_id, SILC_ID_SERVER));
+
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_servers++;
+
+  /* Check whether this router connection has been replaced by an
+     backup router. If it has been then we'll disable the server and will
+     ignore everything it will send until the backup router resuming
+     protocol has been completed. */
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      silc_server_backup_replaced_get(server, server_id, NULL)) {
+    /* Send packet to the server indicating that it cannot use this
+       connection as it has been replaced by backup router. */
+    SilcBuffer packet = silc_buffer_alloc(2);
+    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+    silc_buffer_format(packet,
+                      SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_REPLACED),
+                      SILC_STR_UI_CHAR(0),
+                      SILC_STR_END);
+    silc_server_packet_send(server, sock, 
+                           SILC_PACKET_RESUME_ROUTER, 0, 
+                           packet->data, packet->len, TRUE);
+    silc_buffer_free(packet);
+
+    /* Mark the router disabled. The data sent earlier will go but nothing
+       after this does not go to this connection. */
+    idata->status |= SILC_IDLIST_STATUS_DISABLED;
+  } else {
+    /* If it is router announce our stuff to it. */
+    if (sock->type == SILC_SOCKET_TYPE_ROUTER && 
+       server->server_type == SILC_ROUTER) {
+      silc_server_announce_servers(server, FALSE, 0, sock);
+      silc_server_announce_clients(server, 0, sock);
+      silc_server_announce_channels(server, 0, sock);
+    }
+  }
+
+  return new_server;
+}
+
+/* Processes incoming New ID packet. New ID Payload is used to distribute
+   information about newly registered clients and servers. */
+
+static void silc_server_new_id_real(SilcServer server, 
+                                   SilcSocketConnection sock,
+                                   SilcPacketContext *packet,
+                                   int broadcast)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcIDList id_list;
+  SilcServerEntry router, server_entry;
+  SilcSocketConnection router_sock;
+  SilcIDPayload idp;
+  SilcIdType id_type;
+  void *id;
+
+  SILC_LOG_DEBUG(("Processing new ID"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      server->server_type == SILC_SERVER ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  idp = silc_id_payload_parse(buffer->data, buffer->len);
+  if (!idp)
+    return;
+
+  id_type = silc_id_payload_get_type(idp);
+
+  /* Normal server cannot have other normal server connections */
+  server_entry = (SilcServerEntry)sock->user_data;
+  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER &&
+      server_entry->server_type == SILC_SERVER)
+    goto out;
+
+  id = silc_id_payload_get_id(idp);
+  if (!id)
+    goto out;
+
+  /* If the packet is coming from server then use the sender as the
+     origin of the the packet. If it came from router then check the real
+     sender of the packet and use that as the origin. */
+  if (sock->type == SILC_SOCKET_TYPE_SERVER) {
+    id_list = server->local_list;
+    router_sock = sock;
+    router = sock->user_data;
+
+    /* If the sender is backup router and ID is server (and we are not
+       backup router) then switch the entry to global list. */
+    if (server_entry->server_type == SILC_BACKUP_ROUTER && 
+       id_type == SILC_ID_SERVER && 
+       server->id_entry->server_type != SILC_BACKUP_ROUTER) {
+      id_list = server->global_list;
+      router_sock = server->router ? server->router->connection : sock;
+    }
+  } else {
+    void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                    packet->src_id_type);
+    router = silc_idlist_find_server_by_id(server->global_list,
+                                          sender_id, TRUE, NULL);
+    if (!router)
+      router = silc_idlist_find_server_by_id(server->local_list,
+                                            sender_id, TRUE, NULL);
+    silc_free(sender_id);
+    router_sock = sock;
+    id_list = server->global_list;
+  }
+
+  if (!router)
+    goto out;
+
+  switch(id_type) {
+  case SILC_ID_CLIENT:
+    {
+      SilcClientEntry entry;
+
+      /* Check that we do not have this client already */
+      entry = silc_idlist_find_client_by_id(server->global_list, 
+                                           id, server->server_type, 
+                                           NULL);
+      if (!entry)
+       entry = silc_idlist_find_client_by_id(server->local_list, 
+                                             id, server->server_type,
+                                             NULL);
+      if (entry) {
+       SILC_LOG_DEBUG(("Ignoring client that we already have"));
+       goto out;
+      }
+
+      SILC_LOG_DEBUG(("New client id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_CLIENT),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+    
+      /* As a router we keep information of all global information in our
+        global list. Cell wide information however is kept in the local
+        list. */
+      entry = silc_idlist_add_client(id_list, NULL, NULL, NULL, 
+                                    id, router, NULL, 0);
+      if (!entry) {
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+
+       /* Inform the sender that the ID is not usable */
+       silc_server_send_notify_signoff(server, sock, FALSE, id, NULL);
+       goto out;
+      }
+      entry->nickname = NULL;
+      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_clients++;
+      server->stat.clients++;
+    }
+    break;
+
+  case SILC_ID_SERVER:
+    {
+      SilcServerEntry entry;
+
+      /* If the ID is mine, ignore it. */
+      if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+       SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
+       break;
+      }
+
+      /* If the ID is the sender's ID, ignore it (we have it already) */
+      if (SILC_ID_SERVER_COMPARE(id, router->id)) {
+       SILC_LOG_DEBUG(("Ignoring sender's own ID"));
+       break;
+      }
+      
+      /* Check that we do not have this server already */
+      entry = silc_idlist_find_server_by_id(server->global_list, 
+                                           id, server->server_type, 
+                                           NULL);
+      if (!entry)
+       entry = silc_idlist_find_server_by_id(server->local_list, 
+                                             id, server->server_type,
+                                             NULL);
+      if (entry) {
+       SILC_LOG_DEBUG(("Ignoring server that we already have"));
+       goto out;
+      }
+
+      SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
+                     silc_id_render(id, SILC_ID_SERVER),
+                     sock->type == SILC_SOCKET_TYPE_SERVER ?
+                     "Server" : "Router", sock->hostname));
+      
+      /* As a router we keep information of all global information in our 
+        global list. Cell wide information however is kept in the local
+        list. */
+      entry = silc_idlist_add_server(id_list, NULL, 0, id, router, 
+                                    router_sock);
+      if (!entry) {
+       SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
+       goto out;
+      }
+      entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+      
+      if (sock->type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.cell_servers++;
+      server->stat.servers++;
+    }
+    break;
+
+  case SILC_ID_CHANNEL:
+    SILC_LOG_ERROR(("Channel cannot be registered with NEW_ID packet"));
+    goto out;
+    break;
+
+  default:
+    goto out;
+    break;
+  }
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. */
+  if (broadcast && !server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           buffer->data, buffer->len, FALSE);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
+  }
+
+ out:
+  silc_id_payload_free(idp);
+}
+
+
+/* Processes incoming New ID packet. New ID Payload is used to distribute
+   information about newly registered clients and servers. */
+
+void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
+                       SilcPacketContext *packet)
+{
+  silc_server_new_id_real(server, sock, packet, TRUE);
+}
+
+/* Receoved New Id List packet, list of New ID payloads inside one
+   packet. Process the New ID payloads one by one. */
+
+void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcPacketContext *new_id;
+  SilcBuffer idp;
+  uint16 id_len;
+
+  SILC_LOG_DEBUG(("Processing New ID List"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER)
+    return;
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. Broadcast
+     this list packet instead of multiple New ID packets. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, packet->buffer->len, FALSE);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
+  }
+
+  /* Make copy of the original packet context, except for the actual
+     data buffer, which we will here now fetch from the original buffer. */
+  new_id = silc_packet_context_alloc();
+  new_id->type = SILC_PACKET_NEW_ID;
+  new_id->flags = packet->flags;
+  new_id->src_id = packet->src_id;
+  new_id->src_id_len = packet->src_id_len;
+  new_id->src_id_type = packet->src_id_type;
+  new_id->dst_id = packet->dst_id;
+  new_id->dst_id_len = packet->dst_id_len;
+  new_id->dst_id_type = packet->dst_id_type;
+
+  idp = silc_buffer_alloc(256);
+  new_id->buffer = idp;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(id_len, packet->buffer->data + 2);
+    if ((id_len > packet->buffer->len) ||
+       (id_len > idp->truelen))
+      break;
+
+    silc_buffer_pull_tail(idp, 4 + id_len);
+    silc_buffer_put(idp, packet->buffer->data, 4 + id_len);
+
+    /* Process the New ID */
+    silc_server_new_id_real(server, sock, new_id, FALSE);
+
+    silc_buffer_push_tail(idp, 4 + id_len);
+    silc_buffer_pull(packet->buffer, 4 + id_len);
+  }
+
+  silc_buffer_free(idp);
+  silc_free(new_id);
+}
+
+/* Received New Channel packet. Information about new channels in the 
+   network are distributed using this packet. Save the information about
+   the new channel. This usually comes from router but also normal server
+   can send this to notify channels it has when it connects to us. */
+
+void silc_server_new_channel(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet)
+{
+  SilcChannelPayload payload;
+  SilcChannelID *channel_id;
+  char *channel_name;
+  uint32 name_len;
+  unsigned char *id;
+  uint32 id_len;
+  uint32 mode;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel;
+
+  SILC_LOG_DEBUG(("Processing New Channel"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_SERVER)
+    return;
+
+  /* Parse the channel payload */
+  payload = silc_channel_payload_parse(packet->buffer->data,
+                                      packet->buffer->len);
+  if (!payload)
+    return;
+    
+  /* Get the channel ID */
+  channel_id = silc_channel_get_id_parse(payload);
+  if (!channel_id) {
+    silc_channel_payload_free(payload);
+    return;
+  }
+
+  channel_name = silc_channel_get_name(payload, &name_len);
+  if (name_len > 256)
+    channel_name[255] = '\0';
+
+  id = silc_channel_get_id(payload, &id_len);
+
+  server_entry = (SilcServerEntry)sock->user_data;
+
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+    /* Add the channel to global list as it is coming from router. It 
+       cannot be our own channel as it is coming from router. */
+
+    /* Check that we don't already have this channel */
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+    if (!channel) {
+      SILC_LOG_DEBUG(("New channel id(%s) from [Router] %s",
+                     silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                     sock->hostname));
+    
+      silc_idlist_add_channel(server->global_list, strdup(channel_name), 
+                             0, channel_id, sock->user_data, NULL, NULL, 0);
+      server->stat.channels++;
+    }
+  } else {
+    /* The channel is coming from our server, thus it is in our cell
+       we will add it to our local list. */
+    SilcBuffer chk;
+
+    SILC_LOG_DEBUG(("Channel id(%s) from [Server] %s",
+                   silc_id_render(channel_id, SILC_ID_CHANNEL), 
+                   sock->hostname));
+
+    /* Check that we don't already have this channel */
+    channel = silc_idlist_find_channel_by_name(server->local_list, 
+                                              channel_name, NULL);
+    if (!channel)
+      channel = silc_idlist_find_channel_by_name(server->global_list, 
+                                                channel_name, NULL);
+
+    /* If the channel does not exist, then create it. This creates a new
+       key to the channel as well that we will send to the server. */
+    if (!channel) {
+      /* The protocol says that the Channel ID's IP address must be based
+        on the router's IP address.  Check whether the ID is based in our
+        IP and if it is not then create a new ID and enforce the server
+        to switch the ID. */
+      if (server_entry->server_type != SILC_BACKUP_ROUTER &&
+         !SILC_ID_COMPARE(channel_id, server->id, server->id->ip.data_len)) {
+       SilcChannelID *tmp;
+       SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+       
+       if (silc_id_create_channel_id(server, server->id, server->rng, &tmp)) {
+         silc_server_send_notify_channel_change(server, sock, FALSE, 
+                                                channel_id, tmp);
+         silc_free(channel_id);
+         channel_id = tmp;
+       }
+      }
+
+      /* Create the channel with the provided Channel ID */
+      channel = silc_server_create_new_channel_with_id(server, NULL, NULL,
+                                                      channel_name,
+                                                      channel_id, FALSE);
+      if (!channel) {
+       silc_channel_payload_free(payload);
+       silc_free(channel_id);
+       return;
+      }
+
+      /* Get the mode and set it to the channel */
+      channel->mode = silc_channel_get_mode(payload);
+
+      /* Send the new channel key to the server */
+      id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+      id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+      chk = silc_channel_key_payload_encode(id_len, id,
+                                           strlen(channel->channel_key->
+                                                  cipher->name),
+                                           channel->channel_key->cipher->name,
+                                           channel->key_len / 8, 
+                                           channel->key);
+      silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                             chk->data, chk->len, FALSE);
+      silc_buffer_free(chk);
+
+    } else {
+      /* The channel exist by that name, check whether the ID's match.
+        If they don't then we'll force the server to use the ID we have.
+        We also create a new key for the channel. */
+      SilcBuffer users = NULL, users_modes = NULL;
+
+      if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
+       /* They don't match, send CHANNEL_CHANGE notify to the server to
+          force the ID change. */
+       SILC_LOG_DEBUG(("Forcing the server to change Channel ID"));
+       silc_server_send_notify_channel_change(server, sock, FALSE, 
+                                              channel_id, channel->id);
+      }
+
+      /* If the mode is different from what we have then enforce the
+        mode change. */
+      mode = silc_channel_get_mode(payload);
+      if (channel->mode != mode) {
+       SILC_LOG_DEBUG(("Forcing the server to change channel mode"));
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     channel->mode, server->id,
+                                     SILC_ID_SERVER,
+                                     channel->cipher, channel->hmac_name,
+                                     channel->passphrase);
+      }
+
+      /* Create new key for the channel and send it to the server and
+        everybody else possibly on the channel. */
+
+      if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+       if (!silc_server_create_channel_key(server, channel, 0))
+         return;
+       
+       /* Send to the channel */
+       silc_server_send_channel_key(server, sock, channel, FALSE);
+       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+
+       /* Send to the server */
+       chk = silc_channel_key_payload_encode(id_len, id,
+                                             strlen(channel->channel_key->
+                                                    cipher->name),
+                                             channel->channel_key->
+                                             cipher->name,
+                                             channel->key_len / 8, 
+                                             channel->key);
+       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
+                               chk->data, chk->len, FALSE);
+       silc_buffer_free(chk);
+       silc_free(id);
+      }
+
+      silc_free(channel_id);
+
+      /* Since the channel is coming from server and we also know about it
+        then send the JOIN notify to the server so that it see's our
+        users on the channel "joining" the channel. */
+      silc_server_announce_get_channel_users(server, channel, &users,
+                                            &users_modes);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
+      if (users_modes) {
+       silc_buffer_push(users_modes, users_modes->data - users_modes->head);
+       silc_server_packet_send_dest(server, sock,
+                                    SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                    channel->id, SILC_ID_CHANNEL,
+                                    users_modes->data, 
+                                    users_modes->len, FALSE);
+       silc_buffer_free(users_modes);
+      }
+    }
+  }
+
+  silc_channel_payload_free(payload);
+}
+
+/* Received New Channel List packet, list of New Channel List payloads inside
+   one packet. Process the New Channel payloads one by one. */
+
+void silc_server_new_channel_list(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet)
+{
+  SilcPacketContext *new;
+  SilcBuffer buffer;
+  uint16 len1, len2;
+
+  SILC_LOG_DEBUG(("Processing New Channel List"));
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      packet->src_id_type != SILC_ID_SERVER ||
+      server->server_type == SILC_SERVER)
+    return;
+
+  /* If the sender of this packet is server and we are router we need to
+     broadcast this packet to other routers in the network. Broadcast
+     this list packet instead of multiple New Channel packets. */
+  if (!server->standalone && server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_SERVER &&
+      !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
+    SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
+    silc_server_packet_send(server, server->router->connection,
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, packet->buffer->len, FALSE);
+    silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
+                           packet->type, packet->flags,
+                           packet->buffer->data, packet->buffer->len, 
+                           FALSE, TRUE);
+  }
+
+  /* Make copy of the original packet context, except for the actual
+     data buffer, which we will here now fetch from the original buffer. */
+  new = silc_packet_context_alloc();
+  new->type = SILC_PACKET_NEW_CHANNEL;
+  new->flags = packet->flags;
+  new->src_id = packet->src_id;
+  new->src_id_len = packet->src_id_len;
+  new->src_id_type = packet->src_id_type;
+  new->dst_id = packet->dst_id;
+  new->dst_id_len = packet->dst_id_len;
+  new->dst_id_type = packet->dst_id_type;
+
+  buffer = silc_buffer_alloc(512);
+  new->buffer = buffer;
+
+  while (packet->buffer->len) {
+    SILC_GET16_MSB(len1, packet->buffer->data);
+    if ((len1 > packet->buffer->len) ||
+       (len1 > buffer->truelen))
+      break;
+
+    SILC_GET16_MSB(len2, packet->buffer->data + 2 + len1);
+    if ((len2 > packet->buffer->len) ||
+       (len2 > buffer->truelen))
+      break;
+
+    silc_buffer_pull_tail(buffer, 8 + len1 + len2);
+    silc_buffer_put(buffer, packet->buffer->data, 8 + len1 + len2);
+
+    /* Process the New Channel */
+    silc_server_new_channel(server, sock, new);
+
+    silc_buffer_push_tail(buffer, 8 + len1 + len2);
+    silc_buffer_pull(packet->buffer, 8 + len1 + len2);
+  }
+
+  silc_buffer_free(buffer);
+  silc_free(new);
+}
+
+/* Received key agreement packet. This packet is never for us. It is to
+   the client in the packet's destination ID. Sending of this sort of packet
+   equals sending private message, ie. it is sent point to point from
+   one client to another. */
+
+void silc_server_key_agreement(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
+    return;
+
+  if (!packet->dst_id)
+    return;
+
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
+    return;
+
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++,
+                          packet, FALSE);
+}
+
+/* Received connection auth request packet that is used during connection
+   phase to resolve the mandatory authentication method.  This packet can
+   actually be received at anytime but usually it is used only during
+   the connection authentication phase. Now, protocol says that this packet
+   can come from client or server, however, we support only this coming
+   from client and expect that server always knows what authentication
+   method to use. */
+
+void silc_server_connection_auth_request(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet)
+{
+  SilcServerConfigSectionClient *client = NULL;
+  uint16 conn_type;
+  int ret, port;
+  SilcAuthMethod auth_meth;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type && packet->src_id_type != SILC_ID_CLIENT)
+    return;
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_SHORT(&conn_type),
+                            SILC_STR_UI_SHORT(NULL),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
+
+  if (conn_type != SILC_SOCKET_TYPE_CLIENT)
+    return;
+
+  /* Get the authentication method for the client */
+  auth_meth = SILC_AUTH_NONE;
+  port = server->sockets[server->sock]->port; /* Listenning port */
+  client = silc_server_config_find_client(server->config,
+                                              sock->ip,
+                                              port);
+  if (!client)
+    client = silc_server_config_find_client(server->config,
+                                                sock->hostname,
+                                                port);
+  if (client)
+    auth_meth = client->auth_meth;
+         
+  /* Send it back to the client */
+  silc_server_send_connection_auth_request(server, sock,
+                                          conn_type,
+                                          auth_meth);
+}
+
+/* Received REKEY packet. The sender of the packet wants to regenerate
+   its session keys. This starts the REKEY protocol. */
+
+void silc_server_rekey(SilcServer server,
+                      SilcSocketConnection sock,
+                      SilcPacketContext *packet)
+{
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = TRUE;
+  proto_ctx->pfs = idata->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+                     &protocol, proto_ctx, silc_server_rekey_final);
+  sock->protocol = protocol;
+
+  if (proto_ctx->pfs == FALSE)
+    /* Run the protocol */
+    silc_protocol_execute(protocol, server->schedule, 0, 0);
+}
+
+/* Received file transger packet. This packet is never for us. It is to
+   the client in the packet's destination ID. Sending of this sort of packet
+   equals sending private message, ie. it is sent point to point from
+   one client to another. */
+
+void silc_server_ftp(SilcServer server,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcSocketConnection dst_sock;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (packet->src_id_type != SILC_ID_CLIENT ||
+      packet->dst_id_type != SILC_ID_CLIENT)
+    return;
+
+  if (!packet->dst_id)
+    return;
+
+  /* Get the route to the client */
+  dst_sock = silc_server_get_client_route(server, packet->dst_id,
+                                         packet->dst_id_len, NULL, &idata);
+  if (!dst_sock)
+    return;
+
+  /* Relay the packet */
+  silc_server_relay_packet(server, dst_sock, idata->send_key,
+                          idata->hmac_send, idata->psn_send++,
+                          packet, FALSE);
+}
diff --git a/apps/silcd/packet_receive.h b/apps/silcd/packet_receive.h
new file mode 100644 (file)
index 0000000..847c131
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+
+  packet_receive.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef PACKET_RECEIVE_H
+#define PACKET_RECEIVE_H
+
+/* Prototypes */
+
+void silc_server_notify(SilcServer server,
+                       SilcSocketConnection sock,
+                       SilcPacketContext *packet);
+void silc_server_notify_list(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet);
+void silc_server_private_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet);
+void silc_server_private_message_key(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet);
+void silc_server_command_reply(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet);
+void silc_server_channel_message(SilcServer server,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet);
+void silc_server_channel_key(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet);
+SilcClientEntry silc_server_new_client(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet);
+SilcServerEntry silc_server_new_server(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet);
+void silc_server_new_channel(SilcServer server,
+                            SilcSocketConnection sock,
+                            SilcPacketContext *packet);
+void silc_server_new_channel_list(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet);
+void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
+                       SilcPacketContext *packet);
+void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
+                            SilcPacketContext *packet);
+void silc_server_remove_id(SilcServer server,
+                          SilcSocketConnection sock,
+                          SilcPacketContext *packet);
+void silc_server_remove_id_list(SilcServer server,
+                               SilcSocketConnection sock,
+                               SilcPacketContext *packet);
+void silc_server_key_agreement(SilcServer server,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet);
+void silc_server_connection_auth_request(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet);
+void silc_server_rekey(SilcServer server,
+                      SilcSocketConnection sock,
+                      SilcPacketContext *packet);
+void silc_server_ftp(SilcServer server,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet);
+
+#endif
diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c
new file mode 100644 (file)
index 0000000..d2d2151
--- /dev/null
@@ -0,0 +1,1891 @@
+/*
+
+  packet_send.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/*
+ * Server packet routines to send packets. 
+ */
+/* $Id$ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+/* Routine that sends packet or marks packet to be sent. This is used
+   directly only in special cases. Normal cases should use
+   silc_server_packet_send. Returns < 0 error. */
+
+int silc_server_packet_send_real(SilcServer server,
+                                SilcSocketConnection sock,
+                                bool force_send)
+{
+  int ret;
+
+  /* If disconnecting, ignore the data */
+  if (SILC_IS_DISCONNECTING(sock))
+    return -1;
+
+  /* If rekey protocol is active we must assure that all packets are
+     sent through packet queue. */
+  if (SILC_SERVER_IS_REKEY(sock))
+    force_send = FALSE;
+
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
+    force_send = FALSE;
+
+  /* Send the packet */
+  ret = silc_packet_send(sock, force_send);
+  if (ret != -2)
+    return ret;
+
+  /* Mark that there is some outgoing data available for this connection. 
+     This call sets the connection both for input and output (the input
+     is set always and this call keeps the input setting, actually). 
+     Actual data sending is performed by silc_server_packet_process. */
+  SILC_SET_CONNECTION_FOR_OUTPUT(server->schedule, sock->sock);
+
+  /* Mark to socket that data is pending in outgoing buffer. This flag
+     is needed if new data is added to the buffer before the earlier
+     put data is sent to the network. */
+  SILC_SET_OUTBUF_PENDING(sock);
+
+  return 0;
+}
+
+/* Assembles a new packet to be sent out to network. This doesn't actually
+   send the packet but creates the packet and fills the outgoing data
+   buffer and marks the packet ready to be sent to network. However, If 
+   argument force_send is TRUE the packet is sent immediately and not put 
+   to queue. Normal case is that the packet is not sent immediately. */
+
+void silc_server_packet_send(SilcServer server,
+                            SilcSocketConnection sock, 
+                            SilcPacketType type, 
+                            SilcPacketFlags flags,
+                            unsigned char *data, 
+                            uint32 data_len,
+                            bool force_send)
+{
+  void *dst_id = NULL;
+  SilcIdType dst_id_type = SILC_ID_NONE;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  if (!sock)
+    return;
+
+  /* If disconnecting, ignore the data */
+  if (SILC_IS_DISCONNECTING(sock))
+    return;
+
+  /* If entry is disabled do not sent anything. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
+  /* Get data used in the packet sending, keys and stuff */
+  switch(sock->type) {
+  case SILC_SOCKET_TYPE_CLIENT:
+    if (sock->user_data) {
+      dst_id = ((SilcClientEntry)sock->user_data)->id;
+      dst_id_type = SILC_ID_CLIENT;
+    }
+    break;
+  case SILC_SOCKET_TYPE_SERVER:
+  case SILC_SOCKET_TYPE_ROUTER:
+    if (sock->user_data) {
+      dst_id = ((SilcServerEntry)sock->user_data)->id;
+      dst_id_type = SILC_ID_SERVER;
+    }
+    break;
+  default:
+    break;
+  }
+
+  silc_server_packet_send_dest(server, sock, type, flags, dst_id,
+                              dst_id_type, data, data_len, force_send);
+}
+
+/* Assembles a new packet to be sent out to network. This doesn't actually
+   send the packet but creates the packet and fills the outgoing data
+   buffer and marks the packet ready to be sent to network. However, If 
+   argument force_send is TRUE the packet is sent immediately and not put 
+   to queue. Normal case is that the packet is not sent immediately. 
+   Destination information is sent as argument for this function. */
+
+void silc_server_packet_send_dest(SilcServer server,
+                                 SilcSocketConnection sock, 
+                                 SilcPacketType type, 
+                                 SilcPacketFlags flags,
+                                 void *dst_id,
+                                 SilcIdType dst_id_type,
+                                 unsigned char *data, 
+                                 uint32 data_len,
+                                 bool force_send)
+{
+  SilcPacketContext packetdata;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
+  uint32 sequence = 0;
+  unsigned char *dst_id_data = NULL;
+  uint32 dst_id_len = 0;
+  int block_len = 0;
+
+  /* If disconnecting, ignore the data */
+  if (SILC_IS_DISCONNECTING(sock))
+    return;
+
+  /* If entry is disabled do not sent anything. */
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+
+  if (dst_id) {
+    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
+    dst_id_len = silc_id_get_len(dst_id, dst_id_type);
+  }
+
+  if (idata) {
+    cipher = idata->send_key;
+    hmac = idata->hmac_send;
+    sequence = idata->psn_send++;
+    block_len = silc_cipher_get_block_len(cipher);
+  }
+
+  /* Set the packet context pointers */
+  packetdata.type = type;
+  packetdata.flags = flags;
+  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
+  packetdata.src_id_len = silc_id_get_len(server->id, server->id_type);
+  packetdata.src_id_type = server->id_type;
+  packetdata.dst_id = dst_id_data;
+  packetdata.dst_id_len = dst_id_len;
+  packetdata.dst_id_type = dst_id_type;
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          data_len);
+
+  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the data to the buffer */
+  if (data && data_len)
+    silc_buffer_put(sock->outbuf, data, data_len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata, cipher);
+
+  /* Encrypt the packet */
+  silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, 
+                   sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, force_send);
+
+  if (packetdata.src_id)
+    silc_free(packetdata.src_id);
+  if (packetdata.dst_id)
+    silc_free(packetdata.dst_id);
+}
+
+/* Assembles a new packet to be sent out to network. This doesn't actually
+   send the packet but creates the packet and fills the outgoing data
+   buffer and marks the packet ready to be sent to network. However, If 
+   argument force_send is TRUE the packet is sent immediately and not put 
+   to queue. Normal case is that the packet is not sent immediately. 
+   The source and destination information is sent as argument for this
+   function. */
+
+void silc_server_packet_send_srcdest(SilcServer server,
+                                    SilcSocketConnection sock, 
+                                    SilcPacketType type, 
+                                    SilcPacketFlags flags,
+                                    void *src_id,
+                                    SilcIdType src_id_type,
+                                    void *dst_id,
+                                    SilcIdType dst_id_type,
+                                    unsigned char *data, 
+                                    uint32 data_len,
+                                    bool force_send)
+{
+  SilcPacketContext packetdata;
+  SilcIDListData idata;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
+  uint32 sequence = 0;
+  unsigned char *dst_id_data = NULL;
+  uint32 dst_id_len = 0;
+  unsigned char *src_id_data = NULL;
+  uint32 src_id_len = 0;
+  int block_len = 0;
+
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+
+  /* Get data used in the packet sending, keys and stuff */
+  idata = (SilcIDListData)sock->user_data;
+
+  if (dst_id) {
+    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
+    dst_id_len = silc_id_get_len(dst_id, dst_id_type);
+  }
+
+  if (src_id) {
+    src_id_data = silc_id_id2str(src_id, src_id_type);
+    src_id_len = silc_id_get_len(src_id, src_id_type);
+  }
+
+  if (idata) {
+    cipher = idata->send_key;
+    hmac = idata->hmac_send;
+    sequence = idata->psn_send++;
+    block_len = silc_cipher_get_block_len(cipher);
+  }
+
+  /* Set the packet context pointers */
+  packetdata.type = type;
+  packetdata.flags = flags;
+  packetdata.src_id = src_id_data;
+  packetdata.src_id_len = src_id_len;
+  packetdata.src_id_type = src_id_type;
+  packetdata.dst_id = dst_id_data;
+  packetdata.dst_id_len = dst_id_len;
+  packetdata.dst_id_type = dst_id_type;
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          data_len);
+
+  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the data to the buffer */
+  if (data && data_len)
+    silc_buffer_put(sock->outbuf, data, data_len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata, cipher);
+
+  /* Encrypt the packet */
+  silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Outgoing packet (%d), len %d", sequence, 
+                   sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, force_send);
+
+  if (packetdata.src_id)
+    silc_free(packetdata.src_id);
+  if (packetdata.dst_id)
+    silc_free(packetdata.dst_id);
+}
+
+/* Broadcast received packet to our primary route. This function is used
+   by router to further route received broadcast packet. It is expected
+   that the broadcast flag from the packet is checked before calling this
+   function. This does not test or set the broadcast flag. */
+
+void silc_server_packet_broadcast(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcIDListData idata;
+  void *id;
+
+  SILC_LOG_DEBUG(("Broadcasting received broadcast packet"));
+
+  /* If the packet is originated from our primary route we are
+     not allowed to send the packet. */
+  id = silc_id_str2id(packet->src_id, packet->src_id_len, packet->src_id_type);
+  if (id && !SILC_ID_SERVER_COMPARE(id, server->router->id)) {
+    idata = (SilcIDListData)sock->user_data;
+
+    silc_buffer_push(buffer, buffer->data - buffer->head);
+    silc_packet_send_prepare(sock, 0, 0, buffer->len); 
+    silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
+                       sock->outbuf, sock->outbuf->len);
+
+    SILC_LOG_HEXDUMP(("Broadcasted packet (%d), len %d", idata->psn_send - 1,
+                     sock->outbuf->len),
+                    sock->outbuf->data, sock->outbuf->len);
+
+    /* Now actually send the packet */
+    silc_server_packet_send_real(server, sock, TRUE);
+    silc_free(id);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Will not broadcast to primary route since it is the "
+                 "original sender of this packet"));
+  silc_free(id);
+}
+
+/* Routes received packet to `sock'. This is used to route the packets that
+   router receives but are not destined to it. */
+
+void silc_server_packet_route(SilcServer server,
+                             SilcSocketConnection sock,
+                             SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcIDListData idata;
+
+  SILC_LOG_DEBUG(("Routing received packet"));
+
+  idata = (SilcIDListData)sock->user_data;
+
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+  silc_packet_send_prepare(sock, 0, 0, buffer->len); 
+  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+  silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++,
+                     sock->outbuf, sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Routed packet (%d), len %d", idata->psn_send - 1, 
+                   sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, TRUE);
+}
+
+/* This routine can be used to send a packet to table of clients provided
+   in `clients'. If `route' is FALSE the packet is routed only to local
+   clients (for server locally connected, and for router local cell). */
+
+void silc_server_packet_send_clients(SilcServer server,
+                                    SilcClientEntry *clients,
+                                    uint32 clients_count,
+                                    SilcPacketType type, 
+                                    SilcPacketFlags flags,
+                                    bool route,
+                                    unsigned char *data, 
+                                    uint32 data_len,
+                                    bool force_send)
+{
+  SilcSocketConnection sock = NULL;
+  SilcClientEntry client = NULL;
+  SilcServerEntry *routed = NULL;
+  uint32 routed_count = 0;
+  bool gone = FALSE;
+  int i, k;
+
+  SILC_LOG_DEBUG(("Sending packet to list of clients"));
+
+  /* Send to all clients in table */
+  for (i = 0; i < clients_count; i++) {
+    client = clients[i];
+
+    /* If client has router set it is not locally connected client and
+       we will route the message to the router set in the client. Though,
+       send locally connected server in all cases. */
+    if (server->server_type == SILC_ROUTER && client->router && 
+       ((!route && client->router->router == server->id_entry) || route)) {
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       continue;
+
+      /* Route only once to router */
+      sock = (SilcSocketConnection)client->router->connection;
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         continue;
+       gone = TRUE;
+      }
+
+      /* Send the packet */
+      silc_server_packet_send_dest(server, sock, type, flags,
+                                  client->router->id, SILC_ID_SERVER,
+                                  data, data_len, force_send);
+
+      /* Mark this route routed already */
+      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+      routed[routed_count++] = client->router;
+      continue;
+    }
+
+    if (client->router)
+      continue;
+
+    /* Send to locally connected client */
+    sock = (SilcSocketConnection)client->connection;
+    silc_server_packet_send_dest(server, sock, type, flags,
+                                client->id, SILC_ID_CLIENT,
+                                data, data_len, force_send);
+  }
+
+  silc_free(routed);
+}
+
+/* Internal routine to actually create the channel packet and send it
+   to network. This is common function in channel message sending. If
+   `channel_message' is TRUE this encrypts the message as it is strictly
+   a channel message. If FALSE normal encryption process is used. */
+
+static void
+silc_server_packet_send_to_channel_real(SilcServer server,
+                                       SilcSocketConnection sock,
+                                       SilcPacketContext *packet,
+                                       SilcCipher cipher,
+                                       SilcHmac hmac,
+                                       uint32 sequence,
+                                       unsigned char *data,
+                                       uint32 data_len,
+                                       bool channel_message,
+                                       bool force_send)
+{
+  int block_len;
+
+  if (!sock)
+    return;
+
+  packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packet->src_id_len + packet->dst_id_len;
+
+  block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+  if (channel_message)
+    packet->padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                                        packet->src_id_len +
+                                        packet->dst_id_len), block_len);
+  else
+    packet->padlen = SILC_PACKET_PADLEN(packet->truelen, block_len);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packet->src_id_len + 
+                          packet->dst_id_len,
+                          packet->padlen,
+                          data_len);
+
+  packet->buffer = sock->outbuf;
+
+  /* Put the data to buffer, assemble and encrypt the packet. The packet
+     is encrypted with normal session key shared with the client, unless
+     the `channel_message' is TRUE. */
+  silc_buffer_put(sock->outbuf, data, data_len);
+  silc_packet_assemble(packet, cipher);
+  if (channel_message)
+    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
+                       SILC_PACKET_HEADER_LEN + packet->src_id_len + 
+                       packet->dst_id_len + packet->padlen);
+  else
+    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
+                       sock->outbuf->len);
+    
+  SILC_LOG_HEXDUMP(("Channel packet (%d), len %d", sequence, 
+                   sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_server_packet_send_real(server, sock, force_send);
+}
+
+/* This routine is used by the server to send packets to channel. The 
+   packet sent with this function is distributed to all clients on
+   the channel. Usually this is used to send notify messages to the
+   channel, things like notify about new user joining to the channel. 
+   If `route' is FALSE then the packet is sent only locally and will not
+   be routed anywhere (for router locally means cell wide). If `sender'
+   is provided then the packet is not sent to that connection since it
+   originally came from it. If `send_to_clients' is FALSE then the
+   packet is not sent clients, only servers. */
+
+void silc_server_packet_send_to_channel(SilcServer server,
+                                       SilcSocketConnection sender,
+                                       SilcChannelEntry channel,
+                                       SilcPacketType type,
+                                       bool route,
+                                       unsigned char *data,
+                                       uint32 data_len,
+                                       bool force_send)
+{
+  SilcSocketConnection sock = NULL;
+  SilcPacketContext packetdata;
+  SilcClientEntry client = NULL;
+  SilcServerEntry *routed = NULL;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcIDListData idata;
+  uint32 routed_count = 0;
+  bool gone = FALSE;
+  int k;
+
+  /* This doesn't send channel message packets */
+  assert(type != SILC_PACKET_CHANNEL_MESSAGE);
+  
+  SILC_LOG_DEBUG(("Sending packet to channel"));
+
+  /* Set the packet context pointers. */
+  packetdata.flags = 0;
+  packetdata.type = type;
+  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
+  packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
+  packetdata.src_id_type = SILC_ID_SERVER;
+  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+  packetdata.dst_id_type = SILC_ID_CHANNEL;
+
+  /* If there are global users in the channel we will send the message
+     first to our router for further routing. */
+  if (route && server->server_type != SILC_ROUTER && !server->standalone &&
+      channel->global_users) {
+    SilcServerEntry router;
+
+    /* Get data used in packet header encryption, keys and stuff. */
+    router = server->router;
+    sock = (SilcSocketConnection)router->connection;
+    idata = (SilcIDListData)router;
+    
+    if (sock != sender) {
+      SILC_LOG_DEBUG(("Sending packet to router for routing"));
+      
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             idata->send_key, 
+                                             idata->hmac_send, 
+                                             idata->psn_send++,
+                                             data, data_len, FALSE, 
+                                             force_send);
+    }
+  }
+
+  routed = silc_calloc(silc_hash_table_count(channel->user_list), 
+                      sizeof(*routed));
+
+  /* Send the message to clients on the channel's client list. */
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    client = chl->client;
+    if (!client)
+      continue;
+
+    /* If client has router set it is not locally connected client and
+       we will route the message to the router set in the client. Though,
+       send locally connected server in all cases. */
+    if (server->server_type == SILC_ROUTER && client->router && 
+       ((!route && client->router->router == server->id_entry) || route)) {
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       continue;
+
+      /* Get data used in packet header encryption, keys and stuff. */
+      sock = (SilcSocketConnection)client->router->connection;
+      idata = (SilcIDListData)client->router;
+
+      if (sender && sock == sender)
+       continue;
+
+      /* Route only once to router */
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         continue;
+       gone = TRUE;
+      }
+
+      /* Send the packet */
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             idata->send_key, 
+                                             idata->hmac_send, 
+                                             idata->psn_send++,
+                                             data, data_len, FALSE, 
+                                             force_send);
+
+      /* Mark this route routed already */
+      routed[routed_count++] = client->router;
+      continue;
+    }
+
+    if (client->router)
+      continue;
+
+    /* Send to locally connected client */
+
+    /* Get data used in packet header encryption, keys and stuff. */
+    sock = (SilcSocketConnection)client->connection;
+    idata = (SilcIDListData)client;
+    
+    if (sender && sock == sender)
+      continue;
+
+    /* Send the packet */
+    silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                           idata->send_key, 
+                                           idata->hmac_send, 
+                                           idata->psn_send++, 
+                                           data, data_len, FALSE, 
+                                           force_send);
+  }
+
+  silc_hash_table_list_reset(&htl);
+  silc_free(routed);
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
+}
+
+/* This checks whether the relayed packet came from router. If it did
+   then we'll need to encrypt it with the channel key. This is called
+   from the silc_server_packet_relay_to_channel. */
+
+static bool
+silc_server_packet_relay_to_channel_encrypt(SilcServer server,
+                                           SilcSocketConnection sock,
+                                           SilcChannelEntry channel,
+                                           unsigned char *data,
+                                           unsigned int data_len)
+{
+  /* If we are router and the packet came from router and private key
+     has not been set for the channel then we must encrypt the packet
+     as it was decrypted with the session key shared between us and the
+     router which sent it. This is so, because cells does not share the
+     same channel key. */
+  if (server->server_type == SILC_ROUTER &&
+      sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY) &&
+      channel->channel_key) {
+    SilcBuffer chp;
+    uint32 iv_len, i;
+    uint16 dlen, flags;
+
+    iv_len = silc_cipher_get_block_len(channel->channel_key);
+    if (channel->iv[0] == '\0')
+      for (i = 0; i < iv_len; i++) channel->iv[i] = 
+                                    silc_rng_get_byte(server->rng);
+    else
+      silc_hash_make(server->md5hash, channel->iv, iv_len, channel->iv);
+    
+    /* Encode new payload. This encrypts it also. */
+    SILC_GET16_MSB(flags, data);
+    SILC_GET16_MSB(dlen, data + 2);
+
+    if (dlen > data_len) {
+      SILC_LOG_WARNING(("Corrupted channel message, cannot relay it"));
+      return FALSE;
+    }
+
+    chp = silc_channel_message_payload_encode(flags, dlen, data + 4,
+                                             iv_len, channel->iv,
+                                             channel->channel_key,
+                                             channel->hmac);
+    memcpy(data, chp->data, chp->len);
+    silc_buffer_free(chp);
+  }
+
+  return TRUE;
+}
+
+/* This routine is explicitly used to relay messages to some channel.
+   Packets sent with this function we have received earlier and are
+   totally encrypted. This just sends the packet to all clients on
+   the channel. If the sender of the packet is someone on the channel 
+   the message will not be sent to that client. The SILC Packet header
+   is encrypted with the session key shared between us and the client.
+   MAC is also computed before encrypting the header. Rest of the
+   packet will be untouched. */
+
+void silc_server_packet_relay_to_channel(SilcServer server,
+                                        SilcSocketConnection sender_sock,
+                                        SilcChannelEntry channel,
+                                        void *sender, 
+                                        SilcIdType sender_type,
+                                        void *sender_entry,
+                                        unsigned char *data,
+                                        uint32 data_len,
+                                        bool force_send)
+{
+  bool found = FALSE;
+  SilcSocketConnection sock = NULL;
+  SilcPacketContext packetdata;
+  SilcClientEntry client = NULL;
+  SilcServerEntry *routed = NULL;
+  SilcChannelClientEntry chl;
+  uint32 routed_count = 0;
+  SilcIDListData idata;
+  SilcHashTableList htl;
+  bool gone = FALSE;
+  int k;
+
+  SILC_LOG_DEBUG(("Relaying packet to channel"));
+
+  /* This encrypts the packet, if needed. It will be encrypted if
+     it came from the router thus it needs to be encrypted with the
+     channel key. If the channel key does not exist, then we know we
+     don't have a single local user on the channel. */
+  if (!silc_server_packet_relay_to_channel_encrypt(server, sender_sock,
+                                                  channel, data,
+                                                  data_len))
+    return;
+
+  /* Set the packet context pointers. */
+  packetdata.flags = 0;
+  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+  packetdata.src_id = silc_id_id2str(sender, sender_type);
+  packetdata.src_id_len = silc_id_get_len(sender, sender_type);
+  packetdata.src_id_type = sender_type;
+  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+  packetdata.dst_id_type = SILC_ID_CHANNEL;
+
+  /* If there are global users in the channel we will send the message
+     first to our router for further routing. */
+  if (server->server_type != SILC_ROUTER && !server->standalone &&
+      channel->global_users) {
+    SilcServerEntry router = server->router;
+
+    /* Check that the sender is not our router. */
+    if (sender_sock != (SilcSocketConnection)router->connection) {
+
+      /* Get data used in packet header encryption, keys and stuff. */
+      sock = (SilcSocketConnection)router->connection;
+      idata = (SilcIDListData)router;
+
+      SILC_LOG_DEBUG(("Sending channel message to router for routing"));
+
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             idata->send_key, 
+                                             idata->hmac_send, 
+                                             idata->psn_send++, 
+                                             data, data_len, TRUE, 
+                                             force_send);
+    }
+  }
+
+  routed = silc_calloc(silc_hash_table_count(channel->user_list), 
+                      sizeof(*routed));
+
+  /* Mark that to the route the original sender if from is not routed */
+  if (sender_type == SILC_ID_CLIENT) {
+    client = (SilcClientEntry)sender_entry;
+    if (client->router) {
+      routed[routed_count++] = client->router;
+      SILC_LOG_DEBUG(("************* router %s", 
+                     silc_id_render(client->router->id, SILC_ID_SERVER)));
+    }
+  }
+
+  /* Send the message to clients on the channel's client list. */
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    client = chl->client;
+    if (!client)
+      continue;
+
+    /* Do not send to the sender */
+    if (!found && client == sender_entry) {
+      found = TRUE;
+      continue;
+    }
+
+    /* If the client has set router it means that it is not locally
+       connected client and we will route the packet further. */
+    if (server->server_type == SILC_ROUTER && client->router) {
+
+      /* Sender maybe server as well so we want to make sure that
+        we won't send the message to the server it came from. */
+      if (!found && SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
+       found = TRUE;
+       routed[routed_count++] = client->router;
+       continue;
+      }
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       continue;
+       
+      /* Get data used in packet header encryption, keys and stuff. */
+      sock = (SilcSocketConnection)client->router->connection;
+      idata = (SilcIDListData)client->router;
+
+      /* Do not send to the sender. Check first whether the true
+        sender's router is same as this client's router. Also check
+        if the sender socket is the same as this client's router
+        socket. */
+      if (sender_entry &&
+         ((SilcClientEntry)sender_entry)->router == client->router)
+       continue;
+      if (sender_sock && sock == sender_sock)
+       continue;
+
+      SILC_LOG_DEBUG(("Relaying packet to client ID(%s) %s (%s)", 
+                     silc_id_render(client->id, SILC_ID_CLIENT),
+                     sock->hostname, sock->ip));
+
+      /* Mark this route routed already. */
+      routed[routed_count++] = client->router;
+       
+      /* If the remote connection is router then we'll decrypt the
+        channel message and re-encrypt it with the session key shared
+        between us and the remote router. This is done because the
+        channel keys are cell specific and we have different channel
+        key than the remote router has. */
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         continue;
+
+       SILC_LOG_DEBUG(("Remote is router, encrypt with session key"));
+       gone = TRUE;
+
+       /* If private key mode is not set then decrypt the packet
+          and re-encrypt it */
+       if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+         unsigned char *tmp = silc_calloc(data_len, sizeof(*data));
+         memcpy(tmp, data, data_len);
+
+         /* Decrypt the channel message (we don't check the MAC) */
+         if (channel->channel_key &&
+             !silc_channel_message_payload_decrypt(tmp, data_len, 
+                                                   channel->channel_key,
+                                                   NULL)) {
+           memset(tmp, 0, data_len);
+           silc_free(tmp);
+           continue;
+         }
+
+         /* Now re-encrypt and send it to the router */
+         silc_server_packet_send_srcdest(server, sock, 
+                                         SILC_PACKET_CHANNEL_MESSAGE, 0,
+                                         sender, sender_type,
+                                         channel->id, SILC_ID_CHANNEL,
+                                         tmp, data_len, force_send);
+
+         /* Free the copy of the channel message */
+         memset(tmp, 0, data_len);
+         silc_free(tmp);
+       } else {
+         /* Private key mode is set, we don't have the channel key, so
+            just re-encrypt the entire packet and send it to the router. */
+         silc_server_packet_send_srcdest(server, sock, 
+                                         SILC_PACKET_CHANNEL_MESSAGE, 0,
+                                         sender, sender_type,
+                                         channel->id, SILC_ID_CHANNEL,
+                                         data, data_len, force_send);
+       }
+       continue;
+      }
+
+      /* Send the packet (to normal server) */
+      silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                             idata->send_key, 
+                                             idata->hmac_send, 
+                                             idata->psn_send++, 
+                                             data, data_len, TRUE, 
+                                             force_send);
+
+      continue;
+    }
+
+    if (client->router)
+      continue;
+
+    /* Get data used in packet header encryption, keys and stuff. */
+    sock = (SilcSocketConnection)client->connection;
+    idata = (SilcIDListData)client;
+
+    if (sender_sock && sock == sender_sock)
+      continue;
+
+    SILC_LOG_DEBUG(("Sending packet to client ID(%s) %s (%s)", 
+                   silc_id_render(client->id, SILC_ID_CLIENT),
+                   sock->hostname, sock->ip));
+
+    /* Send the packet */
+    silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                           idata->send_key, 
+                                           idata->hmac_send, 
+                                           idata->psn_send++, 
+                                           data, data_len, TRUE, 
+                                           force_send);
+  }
+
+  silc_hash_table_list_reset(&htl);
+  silc_free(routed);
+  silc_free(packetdata.src_id);
+  silc_free(packetdata.dst_id);
+}
+
+/* This function is used to send packets strictly to all local clients
+   on a particular channel.  This is used for example to distribute new
+   channel key to all our locally connected clients on the channel. 
+   The packets are always encrypted with the session key shared between
+   the client, this means these are not _to the channel_ but _to the client_
+   on the channel. */
+
+void silc_server_packet_send_local_channel(SilcServer server,
+                                          SilcChannelEntry channel,
+                                          SilcPacketType type,
+                                          SilcPacketFlags flags,
+                                          unsigned char *data,
+                                          uint32 data_len,
+                                          bool force_send)
+{
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcSocketConnection sock = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Send the message to clients on the channel's client list. */
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    if (chl->client && !chl->client->router) {
+      sock = (SilcSocketConnection)chl->client->connection;
+
+      /* Send the packet to the client */
+      silc_server_packet_send_dest(server, sock, type, flags, chl->client->id,
+                                  SILC_ID_CLIENT, data, data_len,
+                                  force_send);
+    }
+  }
+  silc_hash_table_list_reset(&htl);
+}
+
+/* Routine used to send (relay, route) private messages to some destination.
+   If the private message key does not exist then the message is re-encrypted,
+   otherwise we just pass it along. This really is not used to send new
+   private messages (as server does not send them) but to relay received
+   private messages. */
+
+void silc_server_send_private_message(SilcServer server,
+                                     SilcSocketConnection dst_sock,
+                                     SilcCipher cipher,
+                                     SilcHmac hmac,
+                                     uint32 sequence,
+                                     SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+
+  /* Re-encrypt and send if private messge key does not exist */
+  if (!(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY)) {
+
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                    + packet->dst_id_len + packet->padlen);
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+    /* Re-encrypt packet */
+    silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, buffer->len);
+
+    /* Send the packet */
+    silc_server_packet_send_real(server, dst_sock, FALSE);
+
+  } else {
+    /* Key exist so encrypt just header and send it */
+    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                    + packet->dst_id_len + packet->padlen);
+    silc_packet_send_prepare(dst_sock, 0, 0, buffer->len);
+    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
+
+    /* Encrypt header */
+    silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, 
+                       SILC_PACKET_HEADER_LEN + packet->src_id_len + 
+                       packet->dst_id_len + packet->padlen);
+
+    silc_server_packet_send_real(server, dst_sock, FALSE);
+  }
+}
+
+/* Sends current motd to client */
+
+void silc_server_send_motd(SilcServer server,
+                          SilcSocketConnection sock)
+{
+  char *motd, *motd_file = NULL;
+  uint32 motd_len;
+
+  if (server->config)
+    motd_file = server->config->server_info->motd_file;
+
+  if (motd_file) {
+    motd = silc_file_readfile(motd_file, &motd_len);
+    if (!motd)
+      return;
+
+    silc_server_send_notify(server, sock, FALSE, SILC_NOTIFY_TYPE_MOTD, 1,
+                           motd, motd_len);
+    silc_free(motd);
+  }
+}
+
+/* Sends error message. Error messages may or may not have any 
+   implications. */
+
+void silc_server_send_error(SilcServer server,
+                           SilcSocketConnection sock,
+                           const char *fmt, ...)
+{
+  va_list ap;
+  unsigned char buf[4096];
+
+  memset(buf, 0, sizeof(buf));
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  va_end(ap);
+
+  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
+                         buf, strlen(buf), FALSE);
+}
+
+/* Sends notify message. If format is TRUE the variable arguments are
+   formatted and the formatted string is sent as argument payload. If it is
+   FALSE then each argument is sent as separate argument and their format
+   in the argument list must be { argument data, argument length }. */
+
+void silc_server_send_notify(SilcServer server,
+                            SilcSocketConnection sock,
+                            bool broadcast,
+                            SilcNotifyType type,
+                            uint32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer packet;
+
+  va_start(ap, argc);
+
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
+                         packet->data, packet->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0,
+                           packet->data, packet->len, FALSE, TRUE);
+
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Sends notify message and gets the arguments from the `args' Argument
+   Payloads. */
+
+void silc_server_send_notify_args(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 SilcNotifyType type,
+                                 uint32 argc,
+                                 SilcBuffer args)
+{
+  SilcBuffer packet;
+
+  packet = silc_notify_payload_encode_args(type, argc, args);
+  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+}
+
+/* Send CHANNEL_CHANGE notify type. This tells the receiver to replace the
+   `old_id' with the `new_id'. */
+
+void silc_server_send_notify_channel_change(SilcServer server,
+                                           SilcSocketConnection sock,
+                                           bool broadcast,
+                                           SilcChannelID *old_id,
+                                           SilcChannelID *new_id)
+{
+  SilcBuffer idp1, idp2;
+
+  idp1 = silc_id_payload_encode((void *)old_id, SILC_ID_CHANNEL);
+  idp2 = silc_id_payload_encode((void *)new_id, SILC_ID_CHANNEL);
+
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_CHANNEL_CHANGE,
+                         2, idp1->data, idp1->len, idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Send NICK_CHANGE notify type. This tells the receiver to replace the
+   `old_id' with the `new_id'. */
+
+void silc_server_send_notify_nick_change(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        bool broadcast,
+                                        SilcClientID *old_id,
+                                        SilcClientID *new_id)
+{
+  SilcBuffer idp1, idp2;
+
+  idp1 = silc_id_payload_encode((void *)old_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode((void *)new_id, SILC_ID_CLIENT);
+
+  silc_server_send_notify(server, sock, broadcast, 
+                         SILC_NOTIFY_TYPE_NICK_CHANGE,
+                         2, idp1->data, idp1->len, idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Sends JOIN notify type. This tells that new client by `client_id' ID
+   has joined to the `channel'. */
+
+void silc_server_send_notify_join(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 SilcChannelEntry channel,
+                                 SilcClientID *client_id)
+{
+  SilcBuffer idp1, idp2;
+
+  idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+  silc_server_send_notify(server, sock, broadcast, SILC_NOTIFY_TYPE_JOIN,
+                         2, idp1->data, idp1->len,
+                         idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Sends LEAVE notify type. This tells that `client_id' has left the
+   `channel'. The Notify packet is always destined to the channel. */
+
+void silc_server_send_notify_leave(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  bool broadcast,
+                                  SilcChannelEntry channel,
+                                  SilcClientID *client_id)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_LEAVE,
+                              1, idp->data, idp->len);
+  silc_buffer_free(idp);
+}
+
+/* Sends CMODE_CHANGE notify type. This tells that `client_id' changed the
+   `channel' mode to `mode. The Notify packet is always destined to
+   the channel. */
+
+void silc_server_send_notify_cmode(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  bool broadcast,
+                                  SilcChannelEntry channel,
+                                  uint32 mode_mask,
+                                  void *id, SilcIdType id_type,
+                                  char *cipher, char *hmac,
+                                  char *passphrase)
+{
+  SilcBuffer idp;
+  unsigned char mode[4];
+
+  idp = silc_id_payload_encode((void *)id, id_type);
+  SILC_PUT32_MSB(mode_mask, mode);
+
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE,
+                              5, idp->data, idp->len,
+                              mode, 4,
+                              cipher, cipher ? strlen(cipher) : 0,
+                              hmac, hmac ? strlen(hmac) : 0,
+                              passphrase, passphrase ? 
+                              strlen(passphrase) : 0);
+  silc_buffer_free(idp);
+}
+
+/* Sends CUMODE_CHANGE notify type. This tells that `client_id' changed the
+   `target' client's mode on `channel'. The notify packet is always
+   destined to the channel. */
+
+void silc_server_send_notify_cumode(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   uint32 mode_mask,
+                                   void *id, SilcIdType id_type,
+                                   SilcClientID *target)
+{
+  SilcBuffer idp1, idp2;
+  unsigned char mode[4];
+
+  idp1 = silc_id_payload_encode((void *)id, id_type);
+  idp2 = silc_id_payload_encode((void *)target, SILC_ID_CLIENT);
+  SILC_PUT32_MSB(mode_mask, mode);
+
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, 
+                              SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3, 
+                              idp1->data, idp1->len,
+                              mode, 4,
+                              idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Sends SIGNOFF notify type. This tells that `client_id' client has
+   left SILC network. This function is used only between server and router
+   traffic. This is not used to send the notify to the channel for
+   client. The `message may be NULL. */
+
+void silc_server_send_notify_signoff(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    bool broadcast,
+                                    SilcClientID *client_id,
+                                    const char *message)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_SIGNOFF,
+                         message ? 2 : 1, idp->data, idp->len,
+                         message, message ? strlen(message): 0);
+  silc_buffer_free(idp);
+}
+
+/* Sends TOPIC_SET notify type. This tells that `id' changed
+   the `channel's topic to `topic'. The Notify packet is always destined
+   to the channel. This function is used to send the topic set notifies
+   between routers. */
+
+void silc_server_send_notify_topic_set(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      bool broadcast,
+                                      SilcChannelEntry channel,
+                                      void *id, SilcIdType id_type,
+                                      char *topic)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode(id, id_type);
+  silc_server_send_notify_dest(server, sock, broadcast,
+                              (void *)channel->id, SILC_ID_CHANNEL,
+                              SILC_NOTIFY_TYPE_TOPIC_SET,
+                              topic ? 2 : 1, 
+                              idp->data, idp->len, 
+                              topic, topic ? strlen(topic) : 0);
+  silc_buffer_free(idp);
+}
+
+/* Send KICKED notify type. This tells that the `client_id' on `channel'
+   was kicked off the channel.  The `comment' may indicate the reason
+   for the kicking. This function is used only between server and router
+   traffic. */
+
+void silc_server_send_notify_kicked(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   SilcClientID *client_id,
+                                   SilcClientID *kicker,
+                                   char *comment)
+{
+  SilcBuffer idp1;
+  SilcBuffer idp2;
+
+  idp1 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  idp2 = silc_id_payload_encode((void *)kicker, SILC_ID_CLIENT);
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
+                              SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_KICKED, 3,
+                              idp1->data, idp1->len,
+                              comment, comment ? strlen(comment) : 0,
+                              idp2->data, idp2->len);
+  silc_buffer_free(idp1);
+  silc_buffer_free(idp2);
+}
+
+/* Send KILLED notify type. This tells that the `client_id' client was
+   killed from the network.  The `comment' may indicate the reason
+   for the killing. */
+
+void silc_server_send_notify_killed(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcClientID *client_id,
+                                   char *comment)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify_dest(server, sock, broadcast, (void *)client_id,
+                              SILC_ID_CLIENT, SILC_NOTIFY_TYPE_KILLED,
+                              comment ? 2 : 1, idp->data, idp->len,
+                              comment, comment ? strlen(comment) : 0);
+  silc_buffer_free(idp);
+}
+
+/* Sends UMODE_CHANGE notify type. This tells that `client_id' client's
+   user mode in the SILC Network was changed. This function is used to
+   send the packet between routers as broadcast packet. */
+
+void silc_server_send_notify_umode(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  bool broadcast,
+                                  SilcClientID *client_id,
+                                  uint32 mode_mask)
+{
+  SilcBuffer idp;
+  unsigned char mode[4];
+
+  idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  SILC_PUT32_MSB(mode_mask, mode);
+
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_UMODE_CHANGE, 2,
+                         idp->data, idp->len, 
+                         mode, 4);
+  silc_buffer_free(idp);
+}
+
+/* Sends BAN notify type. This tells that ban has been either `add'ed
+   or `del'eted on the `channel. This function is used to send the packet
+   between routers as broadcast packet. */
+
+void silc_server_send_notify_ban(SilcServer server,
+                                SilcSocketConnection sock,
+                                bool broadcast,
+                                SilcChannelEntry channel,
+                                char *add, char *del)
+{
+  SilcBuffer idp;
+
+  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_BAN, 3,
+                         idp->data, idp->len,
+                         add, add ? strlen(add) : 0,
+                         del, del ? strlen(del) : 0);
+  silc_buffer_free(idp);
+}
+
+/* Sends INVITE notify type. This tells that invite has been either `add'ed
+   or `del'eted on the `channel.  The sender of the invite is the `client_id'.
+   This function is used to send the packet between routers as broadcast
+   packet. */
+
+void silc_server_send_notify_invite(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   SilcClientID *client_id,
+                                   char *add, char *del)
+{
+  SilcBuffer idp, idp2;
+
+  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+  idp2 = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
+  silc_server_send_notify(server, sock, broadcast,
+                         SILC_NOTIFY_TYPE_INVITE, 5,
+                         idp->data, idp->len,
+                         channel->channel_name, strlen(channel->channel_name),
+                         idp2->data, idp2->len,
+                         add, add ? strlen(add) : 0,
+                         del, del ? strlen(del) : 0);
+  silc_buffer_free(idp);
+  silc_buffer_free(idp2);
+}
+
+/* Sends notify message destined to specific entity. */
+
+void silc_server_send_notify_dest(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 void *dest_id,
+                                 SilcIdType dest_id_type,
+                                 SilcNotifyType type,
+                                 uint32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer packet;
+
+  va_start(ap, argc);
+
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_packet_send_dest(server, sock, SILC_PACKET_NOTIFY, 
+                              broadcast ? SILC_PACKET_FLAG_BROADCAST : 0,
+                              dest_id, dest_id_type,
+                              packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Sends notify message to a channel. The notify message sent is 
+   distributed to all clients on the channel. If `route_notify' is TRUE
+   then the notify may be routed to primary route or to some other routers.
+   If FALSE it is assured that the notify is sent only locally. If `sender'
+   is provided then the packet is not sent to that connection since it
+   originally came from it. */
+
+void silc_server_send_notify_to_channel(SilcServer server,
+                                       SilcSocketConnection sender,
+                                       SilcChannelEntry channel,
+                                       bool route_notify,
+                                       SilcNotifyType type,
+                                       uint32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer packet;
+
+  va_start(ap, argc);
+
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_packet_send_to_channel(server, sender, channel, 
+                                    SILC_PACKET_NOTIFY, route_notify,
+                                    packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Send notify message to all channels the client has joined. It is quaranteed
+   that the message is sent only once to a client (ie. if a client is joined
+   on two same channel it will receive only one notify message). Also, this
+   sends only to local clients (locally connected if we are server, and to
+   local servers if we are router). If `sender' is provided the packet is
+   not sent to that client at all. */
+
+void silc_server_send_notify_on_channels(SilcServer server,
+                                        SilcClientEntry sender,
+                                        SilcClientEntry client,
+                                        SilcNotifyType type,
+                                        uint32 argc, ...)
+{
+  int k;
+  SilcSocketConnection sock = NULL;
+  SilcPacketContext packetdata;
+  SilcClientEntry c;
+  SilcClientEntry *sent_clients = NULL;
+  uint32 sent_clients_count = 0;
+  SilcServerEntry *routed = NULL;
+  uint32 routed_count = 0;
+  SilcHashTableList htl, htl2;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl, chl2;
+  SilcIDListData idata;
+  SilcBuffer packet;
+  unsigned char *data;
+  uint32 data_len;
+  bool force_send = FALSE;
+  va_list ap;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!silc_hash_table_count(client->channels))
+    return;
+
+  va_start(ap, argc);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  data = packet->data;
+  data_len = packet->len;
+
+  /* Set the packet context pointers. */
+  packetdata.flags = 0;
+  packetdata.type = SILC_PACKET_NOTIFY;
+  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
+  packetdata.src_id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
+  packetdata.src_id_type = SILC_ID_SERVER;
+
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    channel = chl->channel;
+
+    /* Send the message to all clients on the channel's client list. */
+    silc_hash_table_list(channel->user_list, &htl2);
+    while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+      c = chl2->client;
+      
+      if (sender && c == sender)
+       continue;
+
+      /* Check if we have sent the packet to this client already */
+      for (k = 0; k < sent_clients_count; k++)
+       if (sent_clients[k] == c)
+         break;
+      if (k < sent_clients_count)
+       continue;
+
+      /* If we are router and if this client has router set it is not
+        locally connected client and we will route the message to the
+        router set in the client. */
+      if (c && c->router && server->server_type == SILC_ROUTER) {
+       /* Check if we have sent the packet to this route already */
+       for (k = 0; k < routed_count; k++)
+         if (routed[k] == c->router)
+           break;
+       if (k < routed_count)
+         continue;
+       
+       /* Get data used in packet header encryption, keys and stuff. */
+       sock = (SilcSocketConnection)c->router->connection;
+       idata = (SilcIDListData)c->router;
+
+       {
+         SILC_LOG_DEBUG(("*****************"));
+         SILC_LOG_DEBUG(("client->router->id %s",
+                         silc_id_render(c->router->id, SILC_ID_SERVER)));
+         SILC_LOG_DEBUG(("client->router->connection->user_data->id %s",
+                         silc_id_render(((SilcServerEntry)sock->user_data)->id, SILC_ID_SERVER)));
+       }
+
+       packetdata.dst_id = silc_id_id2str(c->router->id, SILC_ID_SERVER);
+       packetdata.dst_id_len = silc_id_get_len(c->router->id, SILC_ID_SERVER);
+       packetdata.dst_id_type = SILC_ID_SERVER;
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               idata->send_key, 
+                                               idata->hmac_send, 
+                                               idata->psn_send++, 
+                                               data, data_len, FALSE, 
+                                               force_send);
+       
+       silc_free(packetdata.dst_id);
+
+       /* We want to make sure that the packet is routed to same router
+          only once. Mark this route as sent route. */
+       routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+       routed[routed_count++] = c->router;
+       continue;
+      }
+
+      if (c && c->router)
+       continue;
+
+      /* Send to locally connected client */
+      if (c) {
+       
+       /* Get data used in packet header encryption, keys and stuff. */
+       sock = (SilcSocketConnection)c->connection;
+       idata = (SilcIDListData)c;
+       
+       packetdata.dst_id = silc_id_id2str(c->id, SILC_ID_CLIENT);
+       packetdata.dst_id_len = silc_id_get_len(c->id, SILC_ID_CLIENT);
+       packetdata.dst_id_type = SILC_ID_CLIENT;
+
+       /* Send the packet */
+       silc_server_packet_send_to_channel_real(server, sock, &packetdata,
+                                               idata->send_key, 
+                                               idata->hmac_send, 
+                                               idata->psn_send++, 
+                                               data, data_len, FALSE, 
+                                               force_send);
+
+       silc_free(packetdata.dst_id);
+
+       /* Make sure that we send the notify only once per client. */
+       sent_clients = silc_realloc(sent_clients, sizeof(*sent_clients) * 
+                                   (sent_clients_count + 1));
+       sent_clients[sent_clients_count++] = c;
+      }
+    }
+    silc_hash_table_list_reset(&htl2);
+  }
+
+  silc_hash_table_list_reset(&htl);
+  silc_free(routed);
+  silc_free(sent_clients);
+  silc_free(packetdata.src_id);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Sends New ID Payload to remote end. The packet is used to distribute
+   information about new registered clients, servers, channel etc. usually
+   to routers so that they can keep these information up to date. 
+   If the argument `broadcast' is TRUE then the packet is sent as
+   broadcast packet. */
+
+void silc_server_send_new_id(SilcServer server,
+                            SilcSocketConnection sock,
+                            bool broadcast,
+                            void *id, SilcIdType id_type, 
+                            uint32 id_len)
+{
+  SilcBuffer idp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  idp = silc_id_payload_encode(id, id_type);
+  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
+                         idp->data, idp->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_ID, 0,
+                           idp->data, idp->len, FALSE, TRUE);
+
+  silc_buffer_free(idp);
+}
+
+/* Send New Channel Payload to notify about newly created channel in the
+   SILC network. Router uses this to notify other routers in the network 
+   about new channel. This packet is broadcasted by router. */
+
+void silc_server_send_new_channel(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 char *channel_name,
+                                 void *channel_id, 
+                                 uint32 channel_id_len,
+                                 uint32 mode)
+{
+  SilcBuffer packet;
+  unsigned char *cid;
+  uint32 name_len = strlen(channel_name);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  cid = silc_id_id2str(channel_id, SILC_ID_CHANNEL);
+  if (!cid)
+    return;
+
+  /* Encode the channel payload */
+  packet = silc_channel_payload_encode(channel_name, name_len,
+                                      cid, channel_id_len, mode);
+
+  silc_server_packet_send(server, sock, SILC_PACKET_NEW_CHANNEL, 
+                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
+                         packet->data, packet->len, FALSE);
+
+  /* Send to backup routers if this is being broadcasted to primary
+     router. */
+  if (server->server_type == SILC_ROUTER &&
+      server->router && server->router->connection &&
+      sock == server->router->connection && broadcast)
+    silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+                           packet->data, packet->len, FALSE, TRUE);
+
+  silc_free(cid);
+  silc_buffer_free(packet);
+}
+
+/* Send Channel Key payload to distribute the new channel key. Normal server
+   sends this to router when new client joins to existing channel. Router
+   sends this to the local server who sent the join command in case where
+   the channel did not exist yet. Both normal and router servers uses this
+   also to send this to locally connected clients on the channel. This
+   must not be broadcasted packet. Routers do not send this to each other. 
+   If `sender is provided then the packet is not sent to that connection since
+   it originally came from it. */
+
+void silc_server_send_channel_key(SilcServer server,
+                                 SilcSocketConnection sender,
+                                 SilcChannelEntry channel,
+                                 unsigned char route)
+{
+  SilcBuffer packet;
+  unsigned char *chid;
+  uint32 tmp_len;
+  SILC_LOG_DEBUG(("Sending key to channel %s", channel->channel_name));
+  chid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+  if (!chid)
+    return;
+  /* Encode channel key packet */
+  tmp_len = strlen(channel->channel_key->cipher->name);
+  packet = silc_channel_key_payload_encode(silc_id_get_len(channel->id,
+                                                          SILC_ID_CHANNEL),
+                                          chid, tmp_len,
+                                           channel->channel_key->cipher->name,
+                                           channel->key_len / 8, channel->key);
+  silc_server_packet_send_to_channel(server, sender, channel, 
+                                    SILC_PACKET_CHANNEL_KEY,
+                                     route, packet->data, packet->len, 
+                                    FALSE);
+  silc_buffer_free(packet);
+  silc_free(chid);
+}
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct form in correct order. */
+
+void silc_server_send_command(SilcServer server, 
+                             SilcSocketConnection sock,
+                             SilcCommand command, 
+                             uint16 ident,
+                             uint32 argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+
+  va_start(ap, argc);
+
+  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND, 0,
+                         packet->data, packet->len, TRUE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Generic function to send any command reply. The arguments must be sent
+   already encoded into correct form in correct order. */
+
+void silc_server_send_command_reply(SilcServer server, 
+                                   SilcSocketConnection sock,
+                                   SilcCommand command, 
+                                   SilcCommandStatus status,
+                                   uint16 ident,
+                                   uint32 argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+
+  va_start(ap, argc);
+
+  packet = silc_command_reply_payload_encode_vap(command, status, ident, 
+                                                argc, ap);
+  silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+                         packet->data, packet->len, TRUE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Generic function to send any command reply. The arguments must be sent
+   already encoded into correct form in correct order. */
+
+void silc_server_send_dest_command_reply(SilcServer server, 
+                                        SilcSocketConnection sock,
+                                        void *dst_id,
+                                        SilcIdType dst_id_type,
+                                        SilcCommand command, 
+                                        SilcCommandStatus status,
+                                        uint16 ident,
+                                        uint32 argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+
+  va_start(ap, argc);
+
+  packet = silc_command_reply_payload_encode_vap(command, status, ident, 
+                                                argc, ap);
+  silc_server_packet_send_dest(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+                              dst_id, dst_id_type, packet->data, 
+                              packet->len, TRUE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
+
+/* Send the heartbeat packet. */
+
+void silc_server_send_heartbeat(SilcServer server,
+                               SilcSocketConnection sock)
+{
+  silc_server_packet_send(server, sock, SILC_PACKET_HEARTBEAT, 0,
+                         NULL, 0, FALSE);
+}
+
+/* Generic function to relay packet we've received. This is used to relay
+   packets to a client but generally can be used to other purposes as well. */
+
+void silc_server_relay_packet(SilcServer server,
+                             SilcSocketConnection dst_sock,
+                             SilcCipher cipher,
+                             SilcHmac hmac,
+                             uint32 sequence,
+                             SilcPacketContext *packet,
+                             bool force_send)
+{
+  silc_buffer_push(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                  + packet->dst_id_len + packet->padlen);
+
+  silc_packet_send_prepare(dst_sock, 0, 0, packet->buffer->len);
+  silc_buffer_put(dst_sock->outbuf, packet->buffer->data, packet->buffer->len);
+  
+  /* Re-encrypt packet */
+  silc_packet_encrypt(cipher, hmac, sequence, dst_sock->outbuf, 
+                     packet->buffer->len);
+  
+  /* Send the packet */
+  silc_server_packet_send_real(server, dst_sock, force_send);
+
+  silc_buffer_pull(packet->buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
+                  + packet->dst_id_len + packet->padlen);
+}
+
+/* Routine used to send the connection authentication packet. */
+
+void silc_server_send_connection_auth_request(SilcServer server,
+                                             SilcSocketConnection sock,
+                                             uint16 conn_type,
+                                             SilcAuthMethod auth_meth)
+{
+  SilcBuffer packet;
+
+  packet = silc_buffer_alloc(4);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(conn_type),
+                    SILC_STR_UI_SHORT(auth_meth),
+                    SILC_STR_END);
+
+  silc_server_packet_send(server, sock, SILC_PACKET_CONNECTION_AUTH_REQUEST,
+                         0, packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+}
+
+/* Purge the outgoing packet queue to the network if there is data. This
+   function can be used to empty the packet queue. It is guaranteed that
+   after this function returns the outgoing data queue is empty. */
+
+void silc_server_packet_queue_purge(SilcServer server,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    server->stat.packets_sent++;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+}
diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h
new file mode 100644 (file)
index 0000000..58fa37b
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+
+  packet_send.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef PACKET_SEND_H
+#define PACKET_SEND_H
+
+/* Prototypes */
+
+int silc_server_packet_send_real(SilcServer server,
+                                SilcSocketConnection sock,
+                                bool force_send);
+void silc_server_packet_send(SilcServer server,
+                            SilcSocketConnection sock, 
+                            SilcPacketType type, 
+                            SilcPacketFlags flags,
+                            unsigned char *data, 
+                            uint32 data_len,
+                            bool force_send);
+void silc_server_packet_send_dest(SilcServer server,
+                                 SilcSocketConnection sock, 
+                                 SilcPacketType type, 
+                                 SilcPacketFlags flags,
+                                 void *dst_id,
+                                 SilcIdType dst_id_type,
+                                 unsigned char *data, 
+                                 uint32 data_len,
+                                 bool force_send);
+void silc_server_packet_send_srcdest(SilcServer server,
+                                    SilcSocketConnection sock, 
+                                    SilcPacketType type, 
+                                    SilcPacketFlags flags,
+                                    void *src_id,
+                                    SilcIdType src_id_type,
+                                    void *dst_id,
+                                    SilcIdType dst_id_type,
+                                    unsigned char *data, 
+                                    uint32 data_len,
+                                    bool force_send);
+void silc_server_packet_broadcast(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet);
+void silc_server_packet_route(SilcServer server,
+                             SilcSocketConnection sock,
+                             SilcPacketContext *packet);
+void silc_server_packet_send_clients(SilcServer server,
+                                    SilcClientEntry *clients,
+                                    uint32 clients_count,
+                                    SilcPacketType type, 
+                                    SilcPacketFlags flags,
+                                    bool route,
+                                    unsigned char *data, 
+                                    uint32 data_len,
+                                    bool force_send);
+void silc_server_packet_send_to_channel(SilcServer server,
+                                       SilcSocketConnection sender,
+                                       SilcChannelEntry channel,
+                                       SilcPacketType type,
+                                       bool route,
+                                       unsigned char *data,
+                                       uint32 data_len,
+                                       bool force_send);
+void silc_server_packet_relay_to_channel(SilcServer server,
+                                        SilcSocketConnection sender_sock,
+                                        SilcChannelEntry channel,
+                                        void *sender, 
+                                        SilcIdType sender_type,
+                                        void *sender_entry,
+                                        unsigned char *data,
+                                        uint32 data_len,
+                                        bool force_send);
+void silc_server_packet_send_local_channel(SilcServer server,
+                                          SilcChannelEntry channel,
+                                          SilcPacketType type,
+                                          SilcPacketFlags flags,
+                                          unsigned char *data,
+                                          uint32 data_len,
+                                          bool force_send);
+void silc_server_send_private_message(SilcServer server,
+                                     SilcSocketConnection dst_sock,
+                                     SilcCipher cipher,
+                                     SilcHmac hmac,
+                                     uint32 sequence,
+                                     SilcPacketContext *packet);
+void silc_server_send_motd(SilcServer server,
+                          SilcSocketConnection sock);
+void silc_server_send_error(SilcServer server,
+                           SilcSocketConnection sock,
+                           const char *fmt, ...);
+void silc_server_send_notify(SilcServer server,
+                            SilcSocketConnection sock,
+                            bool broadcast,
+                            SilcNotifyType type,
+                            uint32 argc, ...);
+void silc_server_send_notify_args(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 SilcNotifyType type,
+                                 uint32 argc,
+                                 SilcBuffer args);
+void silc_server_send_notify_channel_change(SilcServer server,
+                                           SilcSocketConnection sock,
+                                           bool broadcast,
+                                           SilcChannelID *old_id,
+                                           SilcChannelID *new_id);
+void silc_server_send_notify_nick_change(SilcServer server,
+                                        SilcSocketConnection sock,
+                                        bool broadcast,
+                                        SilcClientID *old_id,
+                                        SilcClientID *new_id);
+void silc_server_send_notify_join(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 SilcChannelEntry channel,
+                                 SilcClientID *client_id);
+void silc_server_send_notify_leave(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  bool broadcast,
+                                  SilcChannelEntry channel,
+                                  SilcClientID *client_id);
+void silc_server_send_notify_cmode(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  bool broadcast,
+                                  SilcChannelEntry channel,
+                                  uint32 mode_mask,
+                                  void *id, SilcIdType id_type,
+                                  char *cipher, char *hmac,
+                                  char *passphrase);
+void silc_server_send_notify_cumode(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   uint32 mode_mask,
+                                   void *id, SilcIdType id_type,
+                                   SilcClientID *target);
+void silc_server_send_notify_signoff(SilcServer server,
+                                    SilcSocketConnection sock,
+                                    bool broadcast,
+                                    SilcClientID *client_id,
+                                    const char *message);
+void silc_server_send_notify_topic_set(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      bool broadcast,
+                                      SilcChannelEntry channel,
+                                      void *id, SilcIdType id_type,
+                                      char *topic);
+void silc_server_send_notify_kicked(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   SilcClientID *client_id,
+                                   SilcClientID *kicker,
+                                   char *comment);
+void silc_server_send_notify_killed(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcClientID *client_id,
+                                   char *comment);
+void silc_server_send_notify_umode(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  bool broadcast,
+                                  SilcClientID *client_id,
+                                  uint32 mode_mask);
+void silc_server_send_notify_ban(SilcServer server,
+                                SilcSocketConnection sock,
+                                bool broadcast,
+                                SilcChannelEntry channel,
+                                char *add, char *del);
+void silc_server_send_notify_invite(SilcServer server,
+                                   SilcSocketConnection sock,
+                                   bool broadcast,
+                                   SilcChannelEntry channel,
+                                   SilcClientID *client_id,
+                                   char *add, char *del);
+void silc_server_send_notify_dest(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 void *dest_id,
+                                 SilcIdType dest_id_type,
+                                 SilcNotifyType type,
+                                 uint32 argc, ...);
+void silc_server_send_notify_to_channel(SilcServer server,
+                                       SilcSocketConnection sender,
+                                       SilcChannelEntry channel,
+                                       bool route_notify,
+                                       SilcNotifyType type,
+                                       uint32 argc, ...);
+void silc_server_send_notify_on_channels(SilcServer server,
+                                        SilcClientEntry sender,
+                                        SilcClientEntry client,
+                                        SilcNotifyType type,
+                                        uint32 argc, ...);
+void silc_server_send_new_id(SilcServer server,
+                            SilcSocketConnection sock,
+                            bool broadcast,
+                            void *id, SilcIdType id_type, 
+                            uint32 id_len);
+void silc_server_send_new_channel(SilcServer server,
+                                 SilcSocketConnection sock,
+                                 bool broadcast,
+                                 char *channel_name,
+                                 void *channel_id, 
+                                 uint32 channel_id_len,
+                                 uint32 mode);
+void silc_server_send_channel_key(SilcServer server,
+                                 SilcSocketConnection sender,
+                                 SilcChannelEntry channel,
+                                 unsigned char route);
+void silc_server_send_command(SilcServer server, 
+                             SilcSocketConnection sock,
+                             SilcCommand command, 
+                             uint16 ident,
+                             uint32 argc, ...);
+void silc_server_send_command_reply(SilcServer server, 
+                                   SilcSocketConnection sock,
+                                   SilcCommand command, 
+                                   SilcCommandStatus status,
+                                   uint16 ident,
+                                   uint32 argc, ...);
+void silc_server_send_dest_command_reply(SilcServer server, 
+                                        SilcSocketConnection sock,
+                                        void *dst_id,
+                                        SilcIdType dst_id_type,
+                                        SilcCommand command, 
+                                        SilcCommandStatus status,
+                                        uint16 ident,
+                                        uint32 argc, ...);
+void silc_server_send_heartbeat(SilcServer server,
+                               SilcSocketConnection sock);
+void silc_server_relay_packet(SilcServer server,
+                             SilcSocketConnection dst_sock,
+                             SilcCipher cipher,
+                             SilcHmac hmac,
+                             uint32 sequence,
+                             SilcPacketContext *packet,
+                             bool force_send);
+void silc_server_send_connection_auth_request(SilcServer server,
+                                             SilcSocketConnection sock,
+                                             uint16 conn_type,
+                                             SilcAuthMethod auth_meth);
+void silc_server_packet_queue_purge(SilcServer server,
+                                   SilcSocketConnection sock);
+
+#endif
index 60d491705041f334956f622a56042bcf5fedee93..a7a1b707d7d1a39000626dbf555ac8f20a52cd2d 100644 (file)
@@ -2,9 +2,9 @@
 
   protocol.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 /*
  * Server side of the protocols.
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
 SILC_TASK_CALLBACK(silc_server_protocol_connection_auth);
-SILC_TASK_CALLBACK(silc_server_protocol_channel_auth);
 SILC_TASK_CALLBACK(silc_server_protocol_key_exchange);
+SILC_TASK_CALLBACK(silc_server_protocol_rekey);
 
-/* SILC client protocol list */
-const SilcProtocolObject silc_protocol_list[] =
-{
-  { SILC_PROTOCOL_SERVER_CONNECTION_AUTH, 
-    silc_server_protocol_connection_auth },
-  { SILC_PROTOCOL_SERVER_CHANNEL_AUTH, 
-    silc_server_protocol_channel_auth },
-  { SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-    silc_server_protocol_key_exchange },
-
-  { SILC_PROTOCOL_SERVER_NONE, NULL },
-};
+extern char *silc_version_string;
 
 /*
  * Key Exhange protocol functions
  */
 
+static bool 
+silc_verify_public_key_internal(SilcServer server, SilcSocketConnection sock,
+                               SilcSocketType conn_type,
+                               unsigned char *pk, uint32 pk_len, 
+                               SilcSKEPKType pk_type)
+{
+  char file[256], filename[256], *fingerprint;
+  struct stat st;
+
+  if (pk_type != SILC_SKE_PK_TYPE_SILC) {
+    SILC_LOG_WARNING(("We don't support %s (%s) port %d public key type %d", 
+                     sock->hostname, sock->ip, sock->port, pk_type));
+    return FALSE;
+  }
+
+  /* Accept client keys without verification */
+  if (conn_type == SILC_SOCKET_TYPE_CLIENT) {
+    SILC_LOG_DEBUG(("Accepting client public key without verification"));
+    return TRUE;
+  }
+
+  /* XXX For now, accept server keys without verification too. We are
+     currently always doing mutual authentication so the proof of posession
+     of the private key is verified, and if server is authenticated in
+     conn auth protocol with public key we MUST have the key already. */
+  return TRUE;
+  /* Rest is unreachable code! */
+  
+  memset(filename, 0, sizeof(filename));
+  memset(file, 0, sizeof(file));
+  snprintf(file, sizeof(file) - 1, "serverkey_%s_%d.pub", sock->hostname, 
+          sock->port);
+  snprintf(filename, sizeof(filename) - 1, SILC_ETCDIR "/serverkeys/%s", 
+          file);
+
+  /* Create serverkeys directory if it doesn't exist. */
+  if (stat(SILC_ETCDIR "/serverkeys", &st) < 0) {
+    /* If dir doesn't exist */
+    if (errno == ENOENT) {  
+      if (mkdir(SILC_ETCDIR "/serverkeys", 0755) < 0) {
+       SILC_LOG_ERROR(("Couldn't create `%s' directory\n", 
+                       SILC_ETCDIR "/serverkeys"));
+       return TRUE;
+      }
+    } else {
+      SILC_LOG_ERROR(("%s\n", strerror(errno)));
+      return TRUE;
+    }
+  }
+
+  /* Take fingerprint of the public key */
+  fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+  SILC_LOG_DEBUG(("Received server %s (%s) port %d public key (%s)", 
+                 sock->hostname, sock->ip, sock->port, fingerprint));
+  silc_free(fingerprint);
+
+  /* Check whether this key already exists */
+  if (stat(filename, &st) < 0) {
+    /* We don't have it, then cache it. */
+    SILC_LOG_DEBUG(("New public key from server"));
+
+    silc_pkcs_save_public_key_data(filename, pk, pk_len, 
+                                  SILC_PKCS_FILE_PEM);
+    return TRUE;
+  } else {
+    /* The key already exists, verify it. */
+    SilcPublicKey public_key;
+    unsigned char *encpk;
+    uint32 encpk_len;
+
+    SILC_LOG_DEBUG(("We have the public key saved locally"));
+
+    /* Load the key file */
+    if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(filename, &public_key, 
+                                    SILC_PKCS_FILE_BIN)) {
+       SILC_LOG_WARNING(("Could not load local copy of the %s (%s) port %d "
+                         "server public key", sock->hostname, sock->ip, 
+                         sock->port));
+
+       /* Save the key for future checking */
+       unlink(filename);
+       silc_pkcs_save_public_key_data(filename, pk, pk_len,
+                                      SILC_PKCS_FILE_PEM);
+       return TRUE;
+      }
+  
+    /* Encode the key data */
+    encpk = silc_pkcs_public_key_encode(public_key, &encpk_len);
+    if (!encpk) {
+      SILC_LOG_WARNING(("Local copy of the server %s (%s) port %d public key "
+                       "is malformed", sock->hostname, sock->ip, sock->port));
+
+      /* Save the key for future checking */
+      unlink(filename);
+      silc_pkcs_save_public_key_data(filename, pk, pk_len, SILC_PKCS_FILE_PEM);
+      return TRUE;
+    }
+
+    if (memcmp(pk, encpk, encpk_len)) {
+      SILC_LOG_WARNING(("%s (%s) port %d server public key does not match "
+                       "with local copy", sock->hostname, sock->ip, 
+                       sock->port));
+      SILC_LOG_WARNING(("It is possible that the key has expired or changed"));
+      SILC_LOG_WARNING(("It is also possible that some one is performing "
+                       "man-in-the-middle attack"));
+      SILC_LOG_WARNING(("Will not accept the server %s (%s) port %d public "
+                       "key",
+                       sock->hostname, sock->ip, sock->port));
+      return FALSE;
+    }
+
+    /* Local copy matched */
+    return TRUE;
+  }
+}
+
+/* Callback that is called when we have received KE2 payload from
+   responder. We try to verify the public key now. */
+
+static void 
+silc_server_protocol_ke_verify_key(SilcSKE ske,
+                                  unsigned char *pk_data,
+                                  uint32 pk_len,
+                                  SilcSKEPKType pk_type,
+                                  void *context,
+                                  SilcSKEVerifyCbCompletion completion,
+                                  void *completion_context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerKEInternalContext *ctx = 
+    (SilcServerKEInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_verify_public_key_internal(server, ctx->sock, 
+                                     (ctx->responder == FALSE ?
+                                      SILC_SOCKET_TYPE_ROUTER:
+                                      ctx->sconfig ? SILC_SOCKET_TYPE_SERVER :
+                                      ctx->rconfig ? SILC_SOCKET_TYPE_ROUTER :
+                                      SILC_SOCKET_TYPE_CLIENT),
+                                     pk_data, pk_len, pk_type))
+    completion(ske, SILC_SKE_STATUS_OK, completion_context);
+  else
+    completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, 
+              completion_context);
+}
+
 /* Packet sending callback. This function is provided as packet sending
    routine to the Key Exchange functions. */
 
@@ -73,65 +205,228 @@ static void silc_server_protocol_ke_send_packet(SilcSKE ske,
 
 /* Sets the negotiated key material into use for particular connection. */
 
-static void silc_server_protocol_ke_set_keys(SilcSKE ske,
-                                            SilcSocketConnection sock,
-                                            SilcSKEKeyMaterial *keymat,
-                                            SilcCipher cipher,
-                                            SilcPKCS pkcs,
-                                            SilcHash hash,
-                                            int is_responder)
+int silc_server_protocol_ke_set_keys(SilcServer server,
+                                    SilcSKE ske,
+                                    SilcSocketConnection sock,
+                                    SilcSKEKeyMaterial *keymat,
+                                    SilcCipher cipher,
+                                    SilcPKCS pkcs,
+                                    SilcHash hash,
+                                    SilcHmac hmac,
+                                    SilcSKEDiffieHellmanGroup group,
+                                    bool is_responder)
 {
-  SilcIDListUnknown *conn_data;
-  SilcHash nhash;
+  SilcUnknownEntry conn_data;
+  SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Setting new key into use"));
 
   conn_data = silc_calloc(1, sizeof(*conn_data));
+  idata = (SilcIDListData)conn_data;
 
   /* Allocate cipher to be used in the communication */
-  silc_cipher_alloc(cipher->cipher->name, &conn_data->send_key);
-  silc_cipher_alloc(cipher->cipher->name, &conn_data->receive_key);
+  if (!silc_cipher_alloc(cipher->cipher->name, &idata->send_key)) {
+    silc_free(conn_data);
+    return FALSE;
+  }
+  if (!silc_cipher_alloc(cipher->cipher->name, &idata->receive_key)) {
+    silc_free(conn_data);
+    return FALSE;
+  }
   
+  if (!silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, 
+                      &idata->hmac_send)) {
+    silc_cipher_free(idata->send_key);
+    silc_cipher_free(idata->receive_key);
+    silc_free(conn_data);
+    return FALSE;
+  }
+
+  if (!silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, 
+                      &idata->hmac_receive)) {
+    silc_cipher_free(idata->send_key);
+    silc_cipher_free(idata->receive_key);
+    silc_hmac_free(idata->hmac_send);
+    silc_free(conn_data);
+    return FALSE;
+  }
+
   if (is_responder == TRUE) {
-    conn_data->send_key->cipher->set_key(conn_data->send_key->context, 
-                                        keymat->receive_enc_key, 
-                                        keymat->enc_key_len);
-    conn_data->send_key->set_iv(conn_data->send_key, keymat->receive_iv);
-    conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, 
-                                           keymat->send_enc_key, 
-                                           keymat->enc_key_len);
-    conn_data->receive_key->set_iv(conn_data->receive_key, keymat->send_iv);
-    
+    silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
+    silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
+    silc_hmac_set_key(idata->hmac_send, keymat->receive_hmac_key, 
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(idata->hmac_receive, keymat->send_hmac_key, 
+                     keymat->hmac_key_len);
   } else {
-    conn_data->send_key->cipher->set_key(conn_data->send_key->context, 
-                                        keymat->send_enc_key, 
-                                        keymat->enc_key_len);
-    conn_data->send_key->set_iv(conn_data->send_key, keymat->send_iv);
-    conn_data->receive_key->cipher->set_key(conn_data->receive_key->context, 
-                                           keymat->receive_enc_key, 
-                                           keymat->enc_key_len);
-    conn_data->receive_key->set_iv(conn_data->receive_key, keymat->receive_iv);
+    silc_cipher_set_key(idata->send_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->send_key, keymat->send_iv);
+    silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
+    silc_hmac_set_key(idata->hmac_send, keymat->send_hmac_key, 
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(idata->hmac_receive, keymat->receive_hmac_key, 
+                     keymat->hmac_key_len);
   }
 
-  /* Allocate PKCS to be used */
+  idata->rekey = silc_calloc(1, sizeof(*idata->rekey));
+  idata->rekey->send_enc_key = 
+    silc_calloc(keymat->enc_key_len / 8,
+               sizeof(*idata->rekey->send_enc_key));
+  memcpy(idata->rekey->send_enc_key, 
+        keymat->send_enc_key, keymat->enc_key_len / 8);
+  idata->rekey->enc_key_len = keymat->enc_key_len / 8;
+
+  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
+    idata->rekey->pfs = TRUE;
+  idata->rekey->ske_group = silc_ske_group_get_number(group);
+
+  /* Save the hash */
+  if (!silc_hash_alloc(hash->hash->name, &idata->hash)) {
+    silc_cipher_free(idata->send_key);
+    silc_cipher_free(idata->receive_key);
+    silc_hmac_free(idata->hmac_send);
+    silc_hmac_free(idata->hmac_receive);
+    silc_free(conn_data);
+    return FALSE;
+  }
+
+  /* Save the remote host's public key */
+  silc_pkcs_public_key_decode(ske->ke1_payload->pk_data, 
+                             ske->ke1_payload->pk_len, &idata->public_key);
+  if (ske->prop->flags & SILC_SKE_SP_FLAG_MUTUAL)
+    silc_hash_make(server->sha1hash, ske->ke1_payload->pk_data,
+                  ske->ke1_payload->pk_len, idata->fingerprint);
+
+  sock->user_data = (void *)conn_data;
+
+  SILC_LOG_INFO(("%s (%s) security properties: %s %s %s", 
+                sock->hostname, sock->ip,
+                idata->send_key->cipher->name,
+                (char *)silc_hmac_get_name(idata->hmac_send),
+                idata->hash->hash->name));
+
+  return TRUE;
+}
+
+/* Check remote host version string */
+
+SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
+                                    uint32 len, void *context)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  char *cp;
+  int maj = 0, min = 0, build = 0, maj2 = 0, min2 = 0, build2 = 0;
+
+  SILC_LOG_INFO(("%s (%s) is version %s", ske->sock->hostname,
+                ske->sock->ip, version));
+
+  /* Check for initial version string */
+  if (!strstr(version, "SILC-1.0-"))
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  /* Check software version */
+
+  cp = version + 9;
+  if (!cp)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  maj = atoi(cp);
+  cp = strchr(cp, '.');
+  if (cp) {
+    min = atoi(cp + 1);
+    cp++;
+  }
+  if (cp) {
+    cp = strchr(cp, '.');
+    if (cp)
+      build = atoi(cp + 1);
+  }
+
+  cp = silc_version_string + 9;
+  if (!cp)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  maj2 = atoi(cp);
+  cp = strchr(cp, '.');
+  if (cp) {
+    min2 = atoi(cp + 1);
+    cp++;
+  }
+  if (cp) {
+    cp = strchr(cp, '.');
+    if (cp)
+      build2 = atoi(cp + 1);
+  }
+
+  if (maj != maj2)
+    status = SILC_SKE_STATUS_BAD_VERSION;
 #if 0
-  /* XXX Do we ever need to allocate PKCS for the connection??
-     If yes, we need to change KE protocol to get the initiators
-     public key. */
-  silc_pkcs_alloc(pkcs->pkcs->name, &conn_data->pkcs);
-  silc_pkcs_set_public_key(conn_data->pkcs, ske->ke2_payload->pk_data, 
-                          ske->ke2_payload->pk_len);
+  if (min > min2)
+    status = SILC_SKE_STATUS_BAD_VERSION;
 #endif
 
-  /* Save HMAC key to be used in the communication. */
-  silc_hash_alloc(hash->hash->name, &nhash);
-  silc_hmac_alloc(nhash, &conn_data->hmac);
-  conn_data->hmac_key_len = keymat->hmac_key_len;
-  conn_data->hmac_key = silc_calloc(conn_data->hmac_key_len,
-                                   sizeof(unsigned char));
-  memcpy(conn_data->hmac_key, keymat->hmac_key, keymat->hmac_key_len);
+  /* XXX < 0.6 is not allowed */
+  if (maj == 0 && min < 5)
+    status = SILC_SKE_STATUS_BAD_VERSION;
 
-  sock->user_data = (void *)conn_data;
+  /* XXX backward support for 0.6.1 */
+  if (maj == 0 && min == 6 && build < 2)
+    ske->backward_version = 1;
+
+  return status;
+}
+
+/* Callback that is called by the SKE to indicate that it is safe to
+   continue the execution of the protocol. This is used only if we are
+   initiator.  Is given as argument to the silc_ske_initiator_finish or
+   silc_ske_responder_phase_2 functions. This is called due to the fact
+   that the public key verification process is asynchronous and we must
+   not continue the protocl until the public key has been verified and
+   this callback is called. */
+
+static void silc_server_protocol_ke_continue(SilcSKE ske, void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerKEInternalContext *ctx = 
+    (SilcServerKEInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
+                     silc_ske_map_status(ske->status)));
+    SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
+                   silc_ske_map_status(ske->status)));
+    
+    protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute(protocol, server->schedule, 0, 300000);
+    return;
+  }
+
+  /* Send Ok to the other end. We will end the protocol as responder
+     sends Ok to us when we will take the new keys into use. */
+  if (ctx->responder == FALSE) {
+    silc_ske_end(ctx->ske);
+
+    /* End the protocol on the next round */
+    protocol->state = SILC_PROTOCOL_STATE_END;
+  }
+
+  /* Advance protocol state and call the next state if we are responder. 
+     This happens when this callback was sent to silc_ske_responder_phase_2
+     function. */
+  if (ctx->responder == TRUE) {
+    protocol->state++;
+    silc_protocol_execute(protocol, server->schedule, 0, 100000);
+  }
 }
 
 /* Performs key exchange protocol. This is used for both initiator
@@ -144,7 +439,7 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSKEStatus status = 0;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -164,41 +459,52 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
       /* Allocate Key Exchange object */
       ske = silc_ske_alloc();
       ctx->ske = ske;
+      ske->rng = server->rng;
+      
+      silc_ske_set_callbacks(ske, silc_server_protocol_ke_send_packet, NULL,
+                            silc_server_protocol_ke_verify_key,
+                            silc_server_protocol_ke_continue,
+                            silc_ske_check_version, context);
       
       if (ctx->responder == TRUE) {
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
        status = silc_ske_responder_start(ske, ctx->rng, ctx->sock,
-                                         ctx->packet, NULL, NULL);
+                                         silc_version_string,
+                                         ctx->packet->buffer, TRUE);
       } else {
        SilcSKEStartPayload *start_payload;
 
        /* Assemble security properties. */
-       silc_ske_assemble_security_properties(ske, &start_payload);
+       silc_ske_assemble_security_properties(ske, SILC_SKE_SP_FLAG_MUTUAL, 
+                                             silc_version_string,
+                                             &start_payload);
 
        /* Start the key exchange by sending our security properties
           to the remote end. */
        status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
-                                         start_payload,
-                                         silc_server_protocol_ke_send_packet,
-                                         context);
+                                         start_payload);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
+       SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
+                         silc_ske_map_status(status)));
+       SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
+                       silc_ske_map_status(status)));
 
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+       silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
       }
 
       /* Advance protocol state and call the next state if we are responder */
       protocol->state++;
       if (ctx->responder == TRUE)
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000);
+       silc_protocol_execute(protocol, server->schedule, 0, 100000);
     }
     break;
   case 2:
@@ -208,37 +514,35 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        */
       if (ctx->responder == TRUE) {
        /* Sends the selected security properties to the initiator. */
-       status = 
-         silc_ske_responder_phase_1(ctx->ske, 
-                                    ctx->ske->start_payload,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
+       status = silc_ske_responder_phase_1(ctx->ske, 
+                                           ctx->ske->start_payload);
       } else {
        /* Call Phase-1 function. This processes the Key Exchange Start
           paylaod reply we just got from the responder. The callback
           function will receive the processed payload where we will
           save it. */
-       status = 
-         silc_ske_initiator_phase_1(ctx->ske,
-                                    ctx->packet,
-                                    NULL, NULL);
+       status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet->buffer);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
+       SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
+                         silc_ske_map_status(status)));
+       SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
+                       silc_ske_map_status(status)));
 
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+       silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
       }
 
       /* Advance protocol state and call next state if we are initiator */
       protocol->state++;
       if (ctx->responder == FALSE)
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000);
+       silc_protocol_execute(protocol, server->schedule, 0, 100000);
     }
     break;
   case 3:
@@ -249,34 +553,33 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
       if (ctx->responder == TRUE) {
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
-          Hellman algorithm. */
-       status = 
-         silc_ske_responder_phase_2(ctx->ske, ctx->packet, NULL, NULL);
+          Hellman algorithm. The silc_server_protocol_ke_continue
+          will be called after the public key has been verified. */
+       status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
       } else {
        /* Call the Phase-2 function. This creates Diffie Hellman
           key exchange parameters and sends our public part inside
           Key Exhange 1 Payload to the responder. */
-       status = 
-         silc_ske_initiator_phase_2(ctx->ske,
-                                    silc_server_protocol_ke_send_packet,
-                                    context);
+       status = silc_ske_initiator_phase_2(ctx->ske,
+                                           server->public_key,
+                                           server->private_key);
+       protocol->state++;
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
+       SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
+                         silc_ske_map_status(status)));
+       SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
+                       silc_ske_map_status(status)));
 
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+       silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
       }
-
-      /* Advance protocol state and call the next state if we are responder */
-      protocol->state++;
-      if (ctx->responder == TRUE)
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 100000);
     }
     break;
   case 4:
@@ -285,109 +588,133 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
        * Finish protocol
        */
       if (ctx->responder == TRUE) {
-       unsigned char *pk, *prv;
-       unsigned int pk_len, prv_len;
-
-       /* Get our public key to be sent to the initiator */
-       pk = silc_pkcs_get_public_key(server->public_key, &pk_len);
-
-       /* Get out private key to sign some data. */
-       prv = silc_pkcs_get_private_key(server->public_key, &prv_len);
-
        /* This creates the key exchange material and sends our
           public parts to the initiator inside Key Exchange 2 Payload. */
-       status = 
-         silc_ske_responder_finish(ctx->ske, 
-                                   pk, pk_len, prv, prv_len,
-                                   SILC_SKE_PK_TYPE_SILC,
-                                   silc_server_protocol_ke_send_packet,
-                                   context);
-
-       memset(pk, 0, pk_len);
-       memset(prv, 0, prv_len);
-       silc_free(pk);
-       silc_free(prv);
+       status = silc_ske_responder_finish(ctx->ske, 
+                                          server->public_key, 
+                                          server->private_key,
+                                          SILC_SKE_PK_TYPE_SILC);
+
+       /* End the protocol on the next round */
+       protocol->state = SILC_PROTOCOL_STATE_END;
       } else {
        /* Finish the protocol. This verifies the Key Exchange 2 payload
-          sent by responder. */
-       status = 
-         silc_ske_initiator_finish(ctx->ske,
-                                   ctx->packet, NULL, NULL);
+          sent by responder. The silc_server_protocol_ke_continue will
+          be called after the public key has been verified. */
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
       }
 
+      /* Return now if the procedure is pending. */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
       if (status != SILC_SKE_STATUS_OK) {
-       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
-                         status));
-       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
-                       status));
+       SILC_LOG_WARNING(("Error (%s) during Key Exchange protocol",
+                         silc_ske_map_status(status)));
+       SILC_LOG_DEBUG(("Error (%s) during Key Exchange protocol",
+                       silc_ske_map_status(status)));
 
        protocol->state = SILC_PROTOCOL_STATE_ERROR;
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+       silc_protocol_execute(protocol, server->schedule, 0, 300000);
        return;
       }
-
-      /* Send Ok to the other end. We will end the protocol as responder
-        sends Ok to us when we will take the new keys into use. */
-      if (ctx->responder == FALSE)
-       silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
-
-      /* End the protocol on the next round */
-      protocol->state = SILC_PROTOCOL_STATE_END;
     }
     break;
+
   case SILC_PROTOCOL_STATE_END:
     {
       /* 
        * End protocol
        */
       SilcSKEKeyMaterial *keymat;
-
-      /* Send Ok to the other end if we are responder. If we are 
-        initiator we have sent this already. */
-      if (ctx->responder == TRUE)
-       silc_ske_end(ctx->ske, silc_server_protocol_ke_send_packet, context);
+      int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
+      int hash_len = ctx->ske->prop->hash->hash->hash_len;
 
       /* Process the key material */
       keymat = silc_calloc(1, sizeof(*keymat));
-      silc_ske_process_key_material(ctx->ske, 16, (16 * 8), 16, keymat);
+      status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
+                                            keymat);
+      if (status != SILC_SKE_STATUS_OK) {
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       silc_protocol_execute(protocol, server->schedule, 0, 300000);
+       silc_ske_free_key_material(keymat);
+       return;
+      }
+      ctx->keymat = keymat;
 
-      /* Take the new keys into use. */
-      silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, keymat,
-                                      ctx->ske->prop->cipher,
-                                      ctx->ske->prop->pkcs,
-                                      ctx->ske->prop->hash,
-                                      ctx->responder);
+      /* Send Ok to the other end if we are responder. If we are initiator
+        we have sent this already. */
+      if (ctx->responder == TRUE)
+       silc_ske_end(ctx->ske);
 
       /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
       if (ctx->timeout_task)
-       silc_task_unregister(server->timeout_queue, ctx->timeout_task);
+       silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
+      /* Assure that after calling final callback there cannot be pending
+        executions for this protocol anymore. This just unregisters any 
+        timeout callbacks for this protocol. */
+      silc_protocol_cancel(protocol, server->schedule);
 
       /* Call the final callback */
       if (protocol->final_callback)
-       protocol->execute_final(server->timeout_queue, 0, protocol, fd);
+       silc_protocol_execute_final(protocol, server->schedule);
       else
        silc_protocol_free(protocol);
     }
     break;
+
   case SILC_PROTOCOL_STATE_ERROR:
     /*
      * Error occured
      */
 
+    /* Send abort notification */
+    silc_ske_abort(ctx->ske, ctx->ske->status);
+
     /* Unregister the timeout task since the protocol has ended. 
        This was the timeout task to be executed if the protocol is
        not completed fast enough. */
     if (ctx->timeout_task)
-      silc_task_unregister(server->timeout_queue, ctx->timeout_task);
+      silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
+    /* Assure that after calling final callback there cannot be pending
+       executions for this protocol anymore. This just unregisters any 
+       timeout callbacks for this protocol. */
+    silc_protocol_cancel(protocol, server->schedule);
 
     /* On error the final callback is always called. */
     if (protocol->final_callback)
-      protocol->execute_final(server->timeout_queue, 0, protocol, fd);
+      silc_protocol_execute_final(protocol, server->schedule);
     else
       silc_protocol_free(protocol);
     break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * We have received failure from remote
+     */
+
+    /* Unregister the timeout task since the protocol has ended. 
+       This was the timeout task to be executed if the protocol is
+       not completed fast enough. */
+    if (ctx->timeout_task)
+      silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
+    /* Assure that after calling final callback there cannot be pending
+       executions for this protocol anymore. This just unregisters any 
+       timeout callbacks for this protocol. */
+    silc_protocol_cancel(protocol, server->schedule);
+    
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
   case SILC_PROTOCOL_STATE_UNKNOWN:
     break;
   }
@@ -397,6 +724,96 @@ SILC_TASK_CALLBACK(silc_server_protocol_key_exchange)
  * Connection Authentication protocol functions
  */
 
+static int 
+silc_server_password_authentication(SilcServer server, char *remote_auth, 
+                                   char *local_auth)
+{
+  if (!remote_auth || !local_auth)
+    return FALSE;
+
+  if (!memcmp(remote_auth, local_auth, strlen(local_auth)))
+    return TRUE;
+
+  return FALSE;
+}
+
+static int
+silc_server_public_key_authentication(SilcServer server,
+                                     SilcPublicKey pub_key,
+                                     unsigned char *sign,
+                                     uint32 sign_len,
+                                     SilcSKE ske)
+{
+  SilcPKCS pkcs;
+  int len;
+  SilcBuffer auth;
+
+  if (!pub_key || !sign)
+    return FALSE;
+
+  silc_pkcs_alloc(pub_key->name, &pkcs);
+  if (!silc_pkcs_public_key_set(pkcs, pub_key)) {
+    silc_pkcs_free(pkcs);
+    return FALSE;
+  }
+
+  /* Make the authentication data. Protocol says it is HASH plus
+     KE Start Payload. */
+  len = ske->hash_len + ske->start_payload_copy->len;
+  auth = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(auth, len);
+  silc_buffer_format(auth,
+                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
+                                         ske->start_payload_copy->len),
+                    SILC_STR_END);
+
+  /* Verify signature */
+  if (silc_pkcs_verify_with_hash(pkcs, ske->prop->hash, sign, sign_len, 
+                                auth->data, auth->len)) {
+    silc_pkcs_free(pkcs);
+    silc_buffer_free(auth);
+    return TRUE;
+  }
+
+  silc_pkcs_free(pkcs);
+  silc_buffer_free(auth);
+  return FALSE;
+}
+
+static int
+silc_server_get_public_key_auth(SilcServer server,
+                               unsigned char *auth_data,
+                               uint32 *auth_data_len,
+                               SilcSKE ske)
+{
+  int len;
+  SilcPKCS pkcs;
+  SilcBuffer auth;
+
+  pkcs = server->pkcs;
+
+  /* Make the authentication data. Protocol says it is HASH plus
+     KE Start Payload. */
+  len = ske->hash_len + ske->start_payload_copy->len;
+  auth = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(auth, len);
+  silc_buffer_format(auth,
+                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
+                                         ske->start_payload_copy->len),
+                    SILC_STR_END);
+
+  if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data, 
+                              auth->len, auth_data, auth_data_len)) {
+    silc_buffer_free(auth);
+    return TRUE;
+  }
+
+  silc_buffer_free(auth);
+  return FALSE;
+}
+
 /* Performs connection authentication protocol. If responder, we 
    authenticate the remote data received. If initiator, we will send
    authentication data to the remote end. */
@@ -426,22 +843,31 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
        /*
         * We are receiving party
         */
-       unsigned short payload_len;
-       unsigned short conn_type;
-       unsigned char *auth_data;
+       int ret;
+       uint16 payload_len;
+       uint16 conn_type;
+       unsigned char *auth_data = NULL;
+
+       SILC_LOG_INFO(("Performing authentication protocol for %s (%s)",
+                      ctx->sock->hostname, ctx->sock->ip));
 
        /* Parse the received authentication data packet. The received
           payload is Connection Auth Payload. */
-       silc_buffer_unformat(ctx->packet,
-                            SILC_STR_UI_SHORT(&payload_len),
-                            SILC_STR_UI_SHORT(&conn_type),
-                            SILC_STR_END);
+       ret = silc_buffer_unformat(ctx->packet->buffer,
+                                  SILC_STR_UI_SHORT(&payload_len),
+                                  SILC_STR_UI_SHORT(&conn_type),
+                                  SILC_STR_END);
+       if (ret == -1) {
+         SILC_LOG_DEBUG(("Bad payload in authentication packet"));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
        
-       if (payload_len != ctx->packet->len) {
-         SILC_LOG_ERROR(("Bad payload in authentication packet"));
+       if (payload_len != ctx->packet->buffer->len) {
          SILC_LOG_DEBUG(("Bad payload in authentication packet"));
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
          return;
        }
        
@@ -450,21 +876,24 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
        if (conn_type < SILC_SOCKET_TYPE_CLIENT || 
            conn_type > SILC_SOCKET_TYPE_ROUTER) {
          SILC_LOG_ERROR(("Bad connection type %d", conn_type));
-         SILC_LOG_DEBUG(("Bad connection type %d", conn_type));
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
-         protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 300000);
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
          return;
        }
        
        if (payload_len > 0) {
          /* Get authentication data */
-         silc_buffer_pull(ctx->packet, 4);
-         silc_buffer_unformat(ctx->packet,
-                              SILC_STR_UI_XNSTRING_ALLOC(&auth_data, 
-                                                         payload_len),
-                              SILC_STR_END);
-       } else {
-         auth_data = NULL;
+         silc_buffer_pull(ctx->packet->buffer, 4);
+         ret = silc_buffer_unformat(ctx->packet->buffer,
+                                    SILC_STR_UI_XNSTRING_ALLOC(&auth_data, 
+                                                               payload_len),
+                                    SILC_STR_END);
+         if (ret == -1) {
+           SILC_LOG_DEBUG(("Bad payload in authentication packet"));
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, server->schedule, 0, 300000);
+           return;
+         }
        }
 
        /* 
@@ -477,310 +906,199 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
 
        /* Remote end is client */
        if (conn_type == SILC_SOCKET_TYPE_CLIENT) {
-         SilcConfigServerSectionClientConnection *client = NULL;
-         client = 
-           silc_config_server_find_client_conn(server->config,
-                                               ctx->sock->ip,
-                                               ctx->sock->port);
-         if (!client)
-           client = 
-             silc_config_server_find_client_conn(server->config,
-                                                 ctx->sock->hostname,
-                                                 ctx->sock->port);
+         SilcServerConfigSectionClient *client = ctx->cconfig;
          
          if (client) {
            switch(client->auth_meth) {
-           case SILC_PROTOCOL_CONN_AUTH_NONE:
+           case SILC_AUTH_NONE:
              /* No authentication required */
              SILC_LOG_DEBUG(("No authentication required"));
              break;
              
-           case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
+           case SILC_AUTH_PASSWORD:
              /* Password authentication */
              SILC_LOG_DEBUG(("Password authentication"));
-             if (auth_data) {
-               if (!memcmp(client->auth_data, auth_data, strlen(auth_data))) {
-                 memset(auth_data, 0, payload_len);
-                 silc_free(auth_data);
-                 auth_data = NULL;
-                 break;
-               }
-             }
+             ret = silc_server_password_authentication(server, auth_data,
+                                                       client->auth_data);
+
+             if (ret)
+               break;
 
              /* Authentication failed */
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
+             silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
-             protocol->execute(server->timeout_queue, 0
-                               protocol, fd, 0, 300000);
+             silc_protocol_execute(protocol, server->schedule
+                                   0, 300000);
              return;
              break;
              
-           case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
+           case SILC_AUTH_PUBLIC_KEY:
              /* Public key authentication */
              SILC_LOG_DEBUG(("Public key authentication"));
-             if (auth_data) {
-               SilcIDListUnknown *conn_data;
-               SilcPKCS pkcs;
-               
-               conn_data = (SilcIDListUnknown *)ctx->sock->user_data;
-               
-               /* Load public key from file */
-               if (silc_pkcs_load_public_key(client->auth_data,
-                                             &pkcs) == FALSE) {
-                 
-                 /* Authentication failed */
-                 SILC_LOG_ERROR(("Authentication failed "
-                                 "- could not read public key file"));
-                 memset(auth_data, 0, payload_len);
-                 silc_free(auth_data);
-                 auth_data = NULL;
-                 protocol->state = SILC_PROTOCOL_STATE_ERROR;
-                 protocol->execute(server->timeout_queue, 0, 
-                                   protocol, fd, 0, 300000);
-                 return;
-               }
-               
-               /* Verify hash value HASH from KE protocol */
-               if (pkcs->pkcs->verify(pkcs->context,
-                                      auth_data, payload_len,
-                                      ctx->ske->hash, 
-                                      ctx->ske->hash_len)
-                   == TRUE) {
-                 silc_pkcs_free(pkcs);
-                 break;
-               }
-             }
+             ret = silc_server_public_key_authentication(server, 
+                                                         client->auth_data,
+                                                         auth_data,
+                                                         payload_len, 
+                                                         ctx->ske);
+
+             if (ret)
+               break;
 
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
+             silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
-             protocol->execute(server->timeout_queue, 0
-                               protocol, fd, 0, 300000);
+             silc_protocol_execute(protocol, server->schedule
+                                   0, 300000);
              return;
            }
          } else {
-           SILC_LOG_DEBUG(("No configuration for remote connection"));
-           SILC_LOG_ERROR(("Remote connection not configured"));
+           SILC_LOG_DEBUG(("No configuration for remote client connection"));
+           SILC_LOG_ERROR(("Remote client connection not configured"));
            SILC_LOG_ERROR(("Authentication failed"));
-           memset(auth_data, 0, payload_len);
            silc_free(auth_data);
-           auth_data = NULL;
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           protocol->execute(server->timeout_queue, 0
-                             protocol, fd, 0, 300000);
+           silc_protocol_execute(protocol, server->schedule
+                                 0, 300000);
            return;
          }
        }
        
        /* Remote end is server */
        if (conn_type == SILC_SOCKET_TYPE_SERVER) {
-         SilcConfigServerSectionServerConnection *serv = NULL;
-         serv = 
-           silc_config_server_find_server_conn(server->config,
-                                               ctx->sock->ip,
-                                               ctx->sock->port);
-         if (!serv)
-           serv = 
-             silc_config_server_find_server_conn(server->config,
-                                                 ctx->sock->hostname,
-                                                 ctx->sock->port);
+         SilcServerConfigSectionServer *serv = ctx->sconfig;
          
          if (serv) {
            switch(serv->auth_meth) {
-           case SILC_PROTOCOL_CONN_AUTH_NONE:
+           case SILC_AUTH_NONE:
              /* No authentication required */
              SILC_LOG_DEBUG(("No authentication required"));
              break;
              
-           case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
+           case SILC_AUTH_PASSWORD:
              /* Password authentication */
              SILC_LOG_DEBUG(("Password authentication"));
-             if (auth_data) {
-               if (!memcmp(serv->auth_data, auth_data, strlen(auth_data))) {
-                 memset(auth_data, 0, payload_len);
-                 silc_free(auth_data);
-                 auth_data = NULL;
-                 break;
-               }
-             }
+             ret = silc_server_password_authentication(server, auth_data,
+                                                       serv->auth_data);
+
+             if (ret)
+               break;
              
              /* Authentication failed */
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
+             silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
-             protocol->execute(server->timeout_queue, 0
-                               protocol, fd, 0, 300000);
+             silc_protocol_execute(protocol, server->schedule
+                                   0, 300000);
              return;
              break;
-             
-           case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
+
+           case SILC_AUTH_PUBLIC_KEY:
              /* Public key authentication */
              SILC_LOG_DEBUG(("Public key authentication"));
-             if (auth_data) {
-               SilcIDListUnknown *conn_data;
-               SilcPKCS pkcs;
-               
-               conn_data = (SilcIDListUnknown *)ctx->sock->user_data;
-               
-               /* Load public key from file */
-               if (silc_pkcs_load_public_key(serv->auth_data,
-                                             &pkcs) == FALSE) {
-                 
-                 /* Authentication failed */
-                 SILC_LOG_ERROR(("Authentication failed "
-                                 "- could not read public key file"));
-                 memset(auth_data, 0, payload_len);
-                 silc_free(auth_data);
-                 auth_data = NULL;
-                 protocol->state = SILC_PROTOCOL_STATE_ERROR;
-                 protocol->execute(server->timeout_queue, 0, 
-                                   protocol, fd, 0, 300000);
-                 return;
-               }
-               
-               /* Verify hash value HASH from KE protocol */
-               if (pkcs->pkcs->verify(pkcs->context,
-                                      auth_data, payload_len,
-                                      ctx->ske->hash, 
-                                      ctx->ske->hash_len)
-                   == TRUE) {
-                 silc_pkcs_free(pkcs);
-                 break;
-               }
-             }
+             ret = silc_server_public_key_authentication(server, 
+                                                         serv->auth_data,
+                                                         auth_data,
+                                                         payload_len, 
+                                                         ctx->ske);
+                                                         
+             if (ret)
+               break;
 
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
+             silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
-             protocol->execute(server->timeout_queue, 0
-                               protocol, fd, 0, 300000);
+             silc_protocol_execute(protocol, server->schedule
+                                   0, 300000);
              return;
            }
          } else {
-           SILC_LOG_DEBUG(("No configuration for remote connection"));
-           SILC_LOG_ERROR(("Remote connection not configured"));
+           SILC_LOG_DEBUG(("No configuration for remote server connection"));
+           SILC_LOG_ERROR(("Remote server connection not configured"));
            SILC_LOG_ERROR(("Authentication failed"));
-           memset(auth_data, 0, payload_len);
-           silc_free(auth_data);
-           auth_data = NULL;
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           protocol->execute(server->timeout_queue, 0, 
-                             protocol, fd, 0, 300000);
+           silc_protocol_execute(protocol, server->schedule, 
+                                 0, 300000);
+           silc_free(auth_data);
            return;
          }
        }
        
        /* Remote end is router */
        if (conn_type == SILC_SOCKET_TYPE_ROUTER) {
-         SilcConfigServerSectionServerConnection *serv = NULL;
-         serv = 
-           silc_config_server_find_router_conn(server->config,
-                                               ctx->sock->ip,
-                                               ctx->sock->port);
-         if (!serv)
-           serv = 
-             silc_config_server_find_router_conn(server->config,
-                                                 ctx->sock->hostname,
-                                                 ctx->sock->port);
-         
+         SilcServerConfigSectionRouter *serv = ctx->rconfig;
+
          if (serv) {
            switch(serv->auth_meth) {
-           case SILC_PROTOCOL_CONN_AUTH_NONE:
+           case SILC_AUTH_NONE:
              /* No authentication required */
              SILC_LOG_DEBUG(("No authentication required"));
              break;
              
-           case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
+           case SILC_AUTH_PASSWORD:
              /* Password authentication */
              SILC_LOG_DEBUG(("Password authentication"));
-             if (auth_data) {
-               if (!memcmp(serv->auth_data, auth_data, strlen(auth_data))) {
-                 memset(auth_data, 0, payload_len);
-                 silc_free(auth_data);
-                 auth_data = NULL;
-                 break;
-               }
-             }
+             ret = silc_server_password_authentication(server, auth_data,
+                                                       serv->auth_data);
+
+             if (ret)
+               break;
              
              /* Authentication failed */
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
+             silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
-             protocol->execute(server->timeout_queue, 0
-                               protocol, fd, 0, 300000);
+             silc_protocol_execute(protocol, server->schedule
+                                   0, 300000);
              return;
              break;
              
-           case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
+           case SILC_AUTH_PUBLIC_KEY:
              /* Public key authentication */
              SILC_LOG_DEBUG(("Public key authentication"));
-             if (auth_data) {
-               SilcIDListUnknown *conn_data;
-               SilcPKCS pkcs;
-               
-               conn_data = (SilcIDListUnknown *)ctx->sock->user_data;
-               
-               /* Load public key from file */
-               if (silc_pkcs_load_public_key(serv->auth_data,
-                                             &pkcs) == FALSE) {
-                 
-                 /* Authentication failed */
-                 SILC_LOG_ERROR(("Authentication failed "
-                                 "- could not read public key file"));
-                 memset(auth_data, 0, payload_len);
-                 silc_free(auth_data);
-                 auth_data = NULL;
-                 protocol->state = SILC_PROTOCOL_STATE_ERROR;
-                 protocol->execute(server->timeout_queue, 0, 
-                                   protocol, fd, 0, 300000);
-                 return;
-               }
-               
-               /* Verify hash value HASH from KE protocol */
-               if (pkcs->pkcs->verify(pkcs->context,
-                                      auth_data, payload_len,
-                                      ctx->ske->hash, 
-                                      ctx->ske->hash_len)
-                   == TRUE) {
-                 silc_pkcs_free(pkcs);
-                 break;
-               }
-             }
-
+             ret = silc_server_public_key_authentication(server, 
+                                                         serv->auth_data,
+                                                         auth_data,
+                                                         payload_len, 
+                                                         ctx->ske);
+                                                         
+             if (ret)
+               break;
+             
              SILC_LOG_ERROR(("Authentication failed"));
              SILC_LOG_DEBUG(("Authentication failed"));
+             silc_free(auth_data);
              protocol->state = SILC_PROTOCOL_STATE_ERROR;
-             protocol->execute(server->timeout_queue, 0
-                               protocol, fd, 0, 300000);
+             silc_protocol_execute(protocol, server->schedule
+                                   0, 300000);
              return;
            }
          } else {
-           SILC_LOG_DEBUG(("No configuration for remote connection"));
-           SILC_LOG_ERROR(("Remote connection not configured"));
+           SILC_LOG_DEBUG(("No configuration for remote router connection"));
+           SILC_LOG_ERROR(("Remote router connection not configured"));
            SILC_LOG_ERROR(("Authentication failed"));
-           memset(auth_data, 0, payload_len);
            silc_free(auth_data);
-           auth_data = NULL;
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
-           protocol->execute(server->timeout_queue, 0
-                             protocol, fd, 0, 300000);
+           silc_protocol_execute(protocol, server->schedule
+                                 0, 300000);
            return;
          }
        }
        
-       if (auth_data) {
-         memset(auth_data, 0, payload_len);
-         silc_free(auth_data);
-       }
-       
+       silc_free(auth_data);
+
        /* Save connection type. This is later used to create the
           ID for the connection. */
        ctx->conn_type = conn_type;
          
        /* Advance protocol state. */
        protocol->state = SILC_PROTOCOL_STATE_END;
-       protocol->execute(server->timeout_queue, 0, protocol, fd, 0, 0);
+       silc_protocol_execute(protocol, server->schedule, 0, 0);
 
       } else {
        /* 
@@ -791,30 +1109,33 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
        SilcBuffer packet;
        int payload_len = 0;
        unsigned char *auth_data = NULL;
-       unsigned int auth_data_len = 0;
+       uint32 auth_data_len = 0;
        
        switch(ctx->auth_meth) {
-       case SILC_PROTOCOL_CONN_AUTH_NONE:
+       case SILC_AUTH_NONE:
          /* No authentication required */
          break;
          
-       case SILC_PROTOCOL_CONN_AUTH_PASSWORD:
+       case SILC_AUTH_PASSWORD:
          /* Password authentication */
          if (ctx->auth_data && ctx->auth_data_len) {
-           auth_data = ctx->auth_data;
+           auth_data = strdup(ctx->auth_data);
            auth_data_len = ctx->auth_data_len;
            break;
          }
-
-         /* No authentication data exits. Ask interactively from user. */
-         /* XXX */
-
          break;
          
-       case SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY:
-         /* Public key authentication */
-         /* XXX TODO */
-         break;
+       case SILC_AUTH_PUBLIC_KEY:
+         {
+           unsigned char sign[1024];
+
+           /* Public key authentication */
+           silc_server_get_public_key_auth(server, sign, &auth_data_len,
+                                           ctx->ske);
+           auth_data = silc_calloc(auth_data_len, sizeof(*auth_data));
+           memcpy(auth_data, sign, auth_data_len);
+           break;
+         }
        }
        
        payload_len = 4 + auth_data_len;
@@ -851,20 +1172,28 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
       /* 
        * End protocol
        */
+      unsigned char ok[4];
 
-      /* Succesfully authenticated */
-      silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS, 
-                             0, NULL, 0, TRUE);
+      SILC_PUT32_MSB(SILC_AUTH_OK, ok);
+
+      /* Authentication successful */
+      silc_server_packet_send(server, ctx->sock, SILC_PACKET_SUCCESS,
+                             0, ok, 4, TRUE);
 
       /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
       if (ctx->timeout_task)
-       silc_task_unregister(server->timeout_queue, ctx->timeout_task);
+       silc_schedule_task_del(server->schedule, ctx->timeout_task);
 
+      /* Assure that after calling final callback there cannot be pending
+        executions for this protocol anymore. This just unregisters any 
+        timeout callbacks for this protocol. */
+      silc_protocol_cancel(protocol, server->schedule);
+    
       /* Protocol has ended, call the final callback */
       if (protocol->final_callback)
-       protocol->execute_final(server->timeout_queue, 0, protocol, fd);
+       silc_protocol_execute_final(protocol, server->schedule);
       else
        silc_protocol_free(protocol);
     }
@@ -872,31 +1201,501 @@ SILC_TASK_CALLBACK(silc_server_protocol_connection_auth)
   case SILC_PROTOCOL_STATE_ERROR:
     {
       /*
-       * Error 
+       * Error. Send notify to remote.
        */
+      unsigned char error[4];
+
+      SILC_PUT32_MSB(SILC_AUTH_FAILED, error);
 
       /* Authentication failed */
       silc_server_packet_send(server, ctx->sock, SILC_PACKET_FAILURE,
-                             0, NULL, 0, TRUE);
+                             0, error, 4, TRUE);
 
       /* Unregister the timeout task since the protocol has ended. 
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
       if (ctx->timeout_task)
-       silc_task_unregister(server->timeout_queue, ctx->timeout_task);
+       silc_schedule_task_del(server->schedule, ctx->timeout_task);
 
+      /* Assure that after calling final callback there cannot be pending
+        executions for this protocol anymore. This just unregisters any 
+        timeout callbacks for this protocol. */
+      silc_protocol_cancel(protocol, server->schedule);
+    
       /* On error the final callback is always called. */
       if (protocol->final_callback)
-       protocol->execute_final(server->timeout_queue, 0, protocol, fd);
+       silc_protocol_execute_final(protocol, server->schedule);
       else
        silc_protocol_free(protocol);
     }
     break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * We have received failure from remote
+     */
+
+    /* Unregister the timeout task since the protocol has ended. 
+       This was the timeout task to be executed if the protocol is
+       not completed fast enough. */
+    if (ctx->timeout_task)
+      silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
+    /* Assure that after calling final callback there cannot be pending
+       executions for this protocol anymore. This just unregisters any 
+       timeout callbacks for this protocol. */
+    silc_protocol_cancel(protocol, server->schedule);
+    
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+}
+
+/*
+ * Re-key protocol routines
+ */
+
+/* Actually takes the new keys into use. */
+
+static void 
+silc_server_protocol_rekey_validate(SilcServer server,
+                                   SilcServerRekeyInternalContext *ctx,
+                                   SilcIDListData idata,
+                                   SilcSKEKeyMaterial *keymat,
+                                   bool send)
+{
+  if (ctx->responder == TRUE) {
+    if (send) {
+      silc_cipher_set_key(idata->send_key, keymat->receive_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(idata->send_key, keymat->receive_iv);
+      silc_hmac_set_key(idata->hmac_send, keymat->receive_hmac_key, 
+                       keymat->hmac_key_len);
+    } else {
+      silc_cipher_set_key(idata->receive_key, keymat->send_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(idata->receive_key, keymat->send_iv);
+      silc_hmac_set_key(idata->hmac_receive, keymat->send_hmac_key, 
+                       keymat->hmac_key_len);
+    }
+  } else {
+    if (send) {
+      silc_cipher_set_key(idata->send_key, keymat->send_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(idata->send_key, keymat->send_iv);
+      silc_hmac_set_key(idata->hmac_send, keymat->send_hmac_key, 
+                       keymat->hmac_key_len);
+    } else {
+      silc_cipher_set_key(idata->receive_key, keymat->receive_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(idata->receive_key, keymat->receive_iv);
+      silc_hmac_set_key(idata->hmac_receive, keymat->receive_hmac_key, 
+                       keymat->hmac_key_len);
+    }
+  }
+
+  /* Save the current sending encryption key */
+  if (!send) {
+    memset(idata->rekey->send_enc_key, 0, idata->rekey->enc_key_len);
+    silc_free(idata->rekey->send_enc_key);
+    idata->rekey->send_enc_key = 
+      silc_calloc(keymat->enc_key_len / 8,
+                 sizeof(*idata->rekey->send_enc_key));
+    memcpy(idata->rekey->send_enc_key, keymat->send_enc_key, 
+          keymat->enc_key_len / 8);
+    idata->rekey->enc_key_len = keymat->enc_key_len / 8;
+  }
+}
+
+/* This function actually re-generates (when not using PFS) the keys and
+   takes them into use. */
+
+void silc_server_protocol_rekey_generate(SilcServer server,
+                                        SilcServerRekeyInternalContext *ctx,
+                                        bool send)
+{
+  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(idata->send_key);
+  uint32 hash_len = idata->hash->hash->hash_len;
+
+  SILC_LOG_DEBUG(("Generating new %s session keys (no PFS)",
+                 send ? "sending" : "receiving"));
+
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(idata->rekey->send_enc_key,
+                                    idata->rekey->enc_key_len,
+                                    16, key_len, hash_len, 
+                                    idata->hash, keymat);
+
+  /* Set the keys into use */
+  silc_server_protocol_rekey_validate(server, ctx, idata, keymat, send);
+
+  silc_ske_free_key_material(keymat);
+}
+
+/* This function actually re-generates (with PFS) the keys and
+   takes them into use. */
+
+void 
+silc_server_protocol_rekey_generate_pfs(SilcServer server,
+                                       SilcServerRekeyInternalContext *ctx,
+                                       bool send)
+{
+  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(idata->send_key);
+  uint32 hash_len = idata->hash->hash->hash_len;
+  unsigned char *tmpbuf;
+  uint32 klen;
+
+  SILC_LOG_DEBUG(("Generating new %s session keys (with PFS)",
+                 send ? "sending" : "receiving"));
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
+
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, 
+                                    idata->hash, keymat);
+
+  /* Set the keys into use */
+  silc_server_protocol_rekey_validate(server, ctx, idata, keymat, send);
+
+  memset(tmpbuf, 0, klen);
+  silc_free(tmpbuf);
+  silc_ske_free_key_material(keymat);
+}
+
+/* Packet sending callback. This function is provided as packet sending
+   routine to the Key Exchange functions. */
+
+static void 
+silc_server_protocol_rekey_send_packet(SilcSKE ske,
+                                      SilcBuffer packet,
+                                      SilcPacketType type,
+                                      void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx = 
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+
+  /* Send the packet immediately */
+  silc_server_packet_send(server, ctx->sock,
+                         type, 0, packet->data, packet->len, FALSE);
+}
+
+/* Performs re-key as defined in the SILC protocol specification. */
+
+SILC_TASK_CALLBACK(silc_server_protocol_rekey)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx = 
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+  SilcIDListData idata = (SilcIDListData)ctx->sock->user_data;
+  SilcSKEStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  SILC_LOG_DEBUG(("State=%d", protocol->state));
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    {
+      /* 
+       * Start protocol.
+       */
+
+      if (ctx->responder == TRUE) {
+       /*
+        * We are receiving party
+        */
+
+       if (ctx->pfs == TRUE) {
+         /* 
+          * Use Perfect Forward Secrecy, ie. negotiate the key material
+          * using the SKE protocol.
+          */
+
+         if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+           /* Error in protocol */
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, server->schedule, 0, 300000);
+           return;
+         }
+
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = server->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_group_get_by_number(idata->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         silc_ske_set_callbacks(ctx->ske, 
+                                silc_server_protocol_rekey_send_packet, 
+                                NULL, NULL, NULL, silc_ske_check_version,
+                                context);
+      
+         status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+                             silc_ske_map_status(status)));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, server->schedule, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+         silc_protocol_execute(protocol, server->schedule, 0, 0);
+       } else {
+         /*
+          * Do normal and simple re-key.
+          */
+
+         /* Send the REKEY_DONE to indicate we will take new keys into use */
+         silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+                                 0, NULL, 0, FALSE);
+
+         /* After we send REKEY_DONE we must set the sending encryption
+            key to the new key since all packets after this packet must
+            encrypted with the new key. */
+         silc_server_protocol_rekey_generate(server, ctx, TRUE);
+
+         /* The protocol ends in next stage. */
+         protocol->state = SILC_PROTOCOL_STATE_END;
+       }
+      
+      } else {
+       /*
+        * We are the initiator of this protocol
+        */
+
+       /* Start the re-key by sending the REKEY packet */
+       silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY,
+                               0, NULL, 0, FALSE);
+
+       if (ctx->pfs == TRUE) {
+         /* 
+          * Use Perfect Forward Secrecy, ie. negotiate the key material
+          * using the SKE protocol.
+          */
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = server->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_group_get_by_number(idata->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         silc_ske_set_callbacks(ctx->ske, 
+                                silc_server_protocol_rekey_send_packet, 
+                                NULL, NULL, NULL, silc_ske_check_version,
+                                context);
+      
+         status = silc_ske_initiator_phase_2(ctx->ske, NULL, NULL);
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+                             silc_ske_map_status(status)));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, server->schedule, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+       } else {
+         /*
+          * Do normal and simple re-key.
+          */
+
+         /* Send the REKEY_DONE to indicate we will take new keys into use 
+            now. */ 
+         silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+                                 0, NULL, 0, FALSE);
+
+         /* After we send REKEY_DONE we must set the sending encryption
+            key to the new key since all packets after this packet must
+            encrypted with the new key. */
+         silc_server_protocol_rekey_generate(server, ctx, TRUE);
+
+         /* The protocol ends in next stage. */
+         protocol->state = SILC_PROTOCOL_STATE_END;
+       }
+      }
+    }
+    break;
+
+  case 2:
+    /*
+     * Second state, used only when oding re-key with PFS.
+     */
+    if (ctx->responder == TRUE) {
+      if (ctx->pfs == TRUE) {
+       /*
+        * Send our KE packe to the initiator now that we've processed
+        * the initiator's KE packet.
+        */
+       status = silc_ske_responder_finish(ctx->ske, NULL, NULL, 
+                                          SILC_SKE_PK_TYPE_SILC);
+       if (status != SILC_SKE_STATUS_OK) {
+         SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+                           silc_ske_map_status(status)));
+         
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+      }
+
+    } else {
+      if (ctx->pfs == TRUE) {
+       /*
+        * The packet type must be KE packet
+        */
+       if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+         /* Error in protocol */
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+       
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
+       if (status != SILC_SKE_STATUS_OK) {
+         SILC_LOG_WARNING(("Error (%s) during Re-key (PFS)",
+                           silc_ske_map_status(status)));
+         
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, server->schedule, 0, 300000);
+         return;
+       }
+      }
+    }
+
+    /* Send the REKEY_DONE to indicate we will take new keys into use 
+       now. */ 
+    silc_server_packet_send(server, ctx->sock, SILC_PACKET_REKEY_DONE,
+                           0, NULL, 0, FALSE);
+    
+    /* After we send REKEY_DONE we must set the sending encryption
+       key to the new key since all packets after this packet must
+       encrypted with the new key. */
+    silc_server_protocol_rekey_generate_pfs(server, ctx, TRUE);
+
+    /* The protocol ends in next stage. */
+    protocol->state = SILC_PROTOCOL_STATE_END;
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    /* 
+     * End protocol
+     */
+
+    if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+      /* Error in protocol */
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute(protocol, server->schedule, 0, 300000);
+      return;
+    }
+
+    /* We received the REKEY_DONE packet and all packets after this is
+       encrypted with the new key so set the decryption key to the new key */
+    silc_server_protocol_rekey_generate(server, ctx, FALSE);
+
+    /* Assure that after calling final callback there cannot be pending
+       executions for this protocol anymore. This just unregisters any 
+       timeout callbacks for this protocol. */
+    silc_protocol_cancel(protocol, server->schedule);
+    
+    /* Protocol has ended, call the final callback */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    /*
+     * Error occured
+     */
+
+    if (ctx->pfs == TRUE) {
+      /* Send abort notification */
+      silc_ske_abort(ctx->ske, ctx->ske->status);
+    }
+
+    /* Assure that after calling final callback there cannot be pending
+       executions for this protocol anymore. This just unregisters any 
+       timeout callbacks for this protocol. */
+    silc_protocol_cancel(protocol, server->schedule);
+    
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * We have received failure from remote
+     */
+
+    /* Assure that after calling final callback there cannot be pending
+       executions for this protocol anymore. This just unregisters any 
+       timeout callbacks for this protocol. */
+    silc_protocol_cancel(protocol, server->schedule);
+    
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
   case SILC_PROTOCOL_STATE_UNKNOWN:
     break;
   }
+
 }
 
-SILC_TASK_CALLBACK(silc_server_protocol_channel_auth)
+/* Registers protocols used in server. */
+
+void silc_server_protocols_register(void)
+{
+  silc_protocol_register(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
+                        silc_server_protocol_connection_auth);
+  silc_protocol_register(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
+                        silc_server_protocol_key_exchange);
+  silc_protocol_register(SILC_PROTOCOL_SERVER_REKEY,
+                        silc_server_protocol_rekey);
+  silc_protocol_register(SILC_PROTOCOL_SERVER_BACKUP,
+                        silc_server_protocol_backup);
+}
+
+/* Unregisters protocols */
+
+void silc_server_protocols_unregister(void)
 {
+  silc_protocol_unregister(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
+                          silc_server_protocol_connection_auth);
+  silc_protocol_unregister(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
+                          silc_server_protocol_key_exchange);
+  silc_protocol_unregister(SILC_PROTOCOL_SERVER_REKEY,
+                          silc_server_protocol_rekey);
+  silc_protocol_unregister(SILC_PROTOCOL_SERVER_BACKUP,
+                          silc_server_protocol_backup);
 }
index b9505cf43f9118854034914fa109ec3bbe355a8a..303fdc930eaf2f0e9c62b26a37d3435bdb432e7a 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #define PROTOCOL_H
 
 /* SILC client protocol types */
-#define SILC_PROTOCOL_SERVER_NONE 0
-#define SILC_PROTOCOL_SERVER_CONNECTION_AUTH 1
-#define SILC_PROTOCOL_SERVER_CHANNEL_AUTH 2
-#define SILC_PROTOCOL_SERVER_KEY_EXCHANGE 3
-/* #define SILC_PROTOCOL_SERVER_MAX 255 */
+#define SILC_PROTOCOL_SERVER_NONE               0
+#define SILC_PROTOCOL_SERVER_CONNECTION_AUTH    1
+#define SILC_PROTOCOL_SERVER_KEY_EXCHANGE       2
+#define SILC_PROTOCOL_SERVER_REKEY              3
+#define SILC_PROTOCOL_SERVER_BACKUP             4
+/* #define SILC_PROTOCOL_SERVER_MAX             255 */
 
 /* Internal context for Key Exchange protocol. */
 typedef struct {
   void *server;
+  void *context;
   SilcSocketConnection sock;
   SilcRng rng;
 
   /* TRUE if we are receiveing part of the protocol */
-  int responder;
+  bool responder;
 
   /* Destinations ID taken from authenticataed packet so that we can
      get the destinations ID. */
   void *dest_id;
   SilcIdType dest_id_type;
 
+  /* Pointer to the configurations. */
+  void *cconfig;
+  void *sconfig;
+  void *rconfig;
+
   SilcTask timeout_task;
-  SilcBuffer packet;
+  SilcPacketContext *packet;
   SilcSKE ske;
+  SilcSKEKeyMaterial *keymat;
 } SilcServerKEInternalContext;
 
 /* Internal context for connection authentication protocol */
 typedef struct {
   void *server;
+  void *context;
   SilcSocketConnection sock;
 
   /* TRUE if we are receiving part of the protocol */
-  int responder;
+  bool responder;
 
   /* SKE object from Key Exchange protocol. */
   SilcSKE ske;
@@ -61,23 +70,51 @@ typedef struct {
   /* Auth method that must be used. This is resolved before this
      connection authentication protocol is started. Used when we are
      initiating. */
-  unsigned int auth_meth;
+  uint32 auth_meth;
 
   /* Authentication data if we alreay know it. This is filled before
      starting the protocol if we know the authentication data. Otherwise
      these are and remain NULL. Used when we are initiating. */
-  unsigned char *auth_data;
-  unsigned int auth_data_len;
+  void *auth_data;
+  uint32 auth_data_len;
 
   /* Destinations ID from KE protocol context */
   void *dest_id;
   SilcIdType dest_id_type;
 
+  /* Pointer to the configurations. */
+  void *cconfig;
+  void *sconfig;
+  void *rconfig;
+
   SilcTask timeout_task;
-  SilcBuffer packet;
-  unsigned short conn_type;
+  SilcPacketContext *packet;
+  uint16 conn_type;
 } SilcServerConnAuthInternalContext;
 
+/* Internal context for the rekey protocol */
+typedef struct {
+  void *server;
+  void *context;
+  SilcSocketConnection sock;
+  bool responder;                  /* TRUE if we are receiving party */
+  bool pfs;                        /* TRUE if PFS is to be used */
+  SilcSKE ske;                     /* Defined if PFS is used */
+  SilcPacketContext *packet;
+} SilcServerRekeyInternalContext;
+
 /* Prototypes */
+void silc_server_protocols_register(void);
+void silc_server_protocols_unregister(void);
+int silc_server_protocol_ke_set_keys(SilcServer server,
+                                    SilcSKE ske,
+                                    SilcSocketConnection sock,
+                                    SilcSKEKeyMaterial *keymat,
+                                    SilcCipher cipher,
+                                    SilcPKCS pkcs,
+                                    SilcHash hash,
+                                    SilcHmac hmac,
+                                    SilcSKEDiffieHellmanGroup group,
+                                    bool is_responder);
 
 #endif
diff --git a/apps/silcd/pubkey.pub b/apps/silcd/pubkey.pub
deleted file mode 100644 (file)
index cf4ed9b..0000000
Binary files a/apps/silcd/pubkey.pub and /dev/null differ
index 43b60d380b639ce4de0f8cad10525a58d94eb641..eac09e64d42c27a03b350c79178b290170e11247 100644 (file)
  * routes. If route entry doesn't exist for a specific destination, server
  * uses primary route (default route).
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* XXX Adding two ID's with same IP number replaces the old entry thus
+   gives wrong route. Thus, now disabled until figured out a better way
+   to do this or when removed the whole thing. This could be removed
+   because entry->router->connection gives always the most optimal route
+   for the ID anyway (unless new routes (faster perhaps) are established
+   after receiving this ID, this we don't know however). */
+/* $Id$ */
 
 #include "serverincludes.h"
+#include "server_internal.h"
 #include "route.h"
 
 /* Route cache hash table */
@@ -40,8 +40,8 @@ SilcServerRouteTable silc_route_cache[SILC_SERVER_ROUTE_SIZE];
 /* Adds new route to the route cache. The argument `index' is the
    index value generated by silc_server_route_hash. */
 
-void silc_server_route_add(unsigned int index, unsigned int dest,
-                          SilcServerList *router)
+void silc_server_route_add(uint32 index, unsigned int dest,
+                          SilcServerEntry router)
 {
   silc_route_cache[index].dest = dest;
   silc_route_cache[index].router = router;
@@ -50,10 +50,10 @@ void silc_server_route_add(unsigned int index, unsigned int dest,
 /* Checksk whether destination has a specific router. Returns the
    router data if found, NULL otherwise. */
 
-SilcServerList *silc_server_route_check(unsigned int dest, 
-                                       unsigned short port)
+SilcServerEntry silc_server_route_check(uint32 dest, 
+                                       uint16 port)
 {
-  unsigned int index;
+  uint32 index;
 
   index = silc_server_route_hash(dest, port);
 
@@ -63,3 +63,45 @@ SilcServerList *silc_server_route_check(unsigned int dest,
 
   return NULL;
 }
+
+/* Returns the connection object for the fastest route for the given ID.
+   If we are normal server then this just returns our primary route. If
+   we are router we will do route lookup. */
+
+SilcSocketConnection silc_server_route_get(SilcServer server, void *id,
+                                          SilcIdType id_type)
+{
+  if (server->server_type == SILC_ROUTER) {
+    uint32 dest = 0;
+    uint16 port = 0;
+    SilcServerEntry router = NULL;
+#if 0
+
+    switch(id_type) {
+    case SILC_ID_CLIENT:
+      dest = ((SilcClientID *)id)->ip.s_addr;
+      port = server->id->port;
+      break;
+    case SILC_ID_SERVER:
+      dest = ((SilcServerID *)id)->ip.s_addr;
+      port = ((SilcServerID *)id)->port;
+      break;
+    case SILC_ID_CHANNEL:
+      dest = ((SilcChannelID *)id)->ip.s_addr;
+      port = ((SilcChannelID *)id)->port;
+      break;
+    default:
+      return NULL;
+    }
+
+#endif
+
+    router = silc_server_route_check(dest, port);
+    if (!router)
+      return (SilcSocketConnection)server->id_entry->router->connection;
+
+    return (SilcSocketConnection)router->connection;
+  }
+
+  return (SilcSocketConnection)server->id_entry->router->connection;
+}
index aec76e69ff7201e0de735fa3d2bb74312d971a6b..077831d647720964f2e768caaa84abb8b50591dc 100644 (file)
 
    Following short description of the fields.
 
-   unsigned int dest
+   uint32 dest
 
        Destination IPv4 address.  Can be used to quickly check whether
        the found route entry is what the caller wanted.
 
-   SilcServerList *router
+   SilcServerEntry router
 
        Pointer to the router specific data.
 
 */
 typedef struct {
-  unsigned int dest;
-  SilcServerList *router;
+  uint32 dest;
+  SilcServerEntry router;
 } SilcServerRouteTable;
 
 /* Route cache hash table */
@@ -55,10 +55,10 @@ extern SilcServerRouteTable silc_route_cache[SILC_SERVER_ROUTE_SIZE];
    `port' argument may be zero (0) if it doesn't exist.  This has been
    taken from Linux kernel's route cache code. */
 extern inline
-unsigned int silc_server_route_hash(unsigned int addr, 
-                                   unsigned short port)
+uint32 silc_server_route_hash(unsigned int addr, 
+                                   uint16 port)
 {
-  unsigned int hash;
+  uint32 hash;
   
   hash = ((addr & 0xf0f0f0f0) >> 4) | ((addr & 0x0f0f0f0f) << 4);
   hash ^= port;
@@ -69,9 +69,11 @@ unsigned int silc_server_route_hash(unsigned int addr,
 }
 
 /* Prototypes */
-void silc_server_route_add(unsigned int index, unsigned int dest,
-                          SilcServerList *router);
-SilcServerList *silc_server_route_check(unsigned int dest, 
-                                       unsigned short port);
+void silc_server_route_add(uint32 index, unsigned int dest,
+                          SilcServerEntry router);
+SilcServerEntry silc_server_route_check(uint32 dest, 
+                                       uint16 port);
+SilcSocketConnection silc_server_route_get(SilcServer server, void *id,
+                                          SilcIdType id_type);
 
 #endif
index 636b428fdeb2ff2c698d0e4904be130d5b5e9e7d..f0ed13f8e6b1950302bfdb8045d0f1e150ffcdb1 100644 (file)
@@ -2,9 +2,9 @@
 
   server.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
  * servicing the SILC connections. This is also a SILC router as a router 
  * is also normal server.
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
 /* Static prototypes */
+SILC_TASK_CALLBACK(silc_server_connect_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
@@ -42,25 +36,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
 SILC_TASK_CALLBACK(silc_server_packet_process);
-SILC_TASK_CALLBACK(silc_server_packet_parse);
+SILC_TASK_CALLBACK(silc_server_packet_parse_real);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
-
-/* XXX */
-void silc_server_packet_parse_type(SilcServer server, 
-                                  SilcSocketConnection sock,
-                                  SilcPacketContext *packet);
-
-static int silc_server_packet_check_mac(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer buffer);
-static int silc_server_packet_decrypt_rest(SilcServer server, 
-                                          SilcSocketConnection sock,
-                                          SilcBuffer buffer);
-static int silc_server_packet_decrypt_rest_special(SilcServer server, 
-                                                  SilcSocketConnection sock,
-                                                  SilcBuffer buffer);
-
-extern char server_version[];
+SILC_TASK_CALLBACK(silc_server_failure_callback);
+SILC_TASK_CALLBACK(silc_server_rekey_callback);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -69,27 +48,21 @@ extern char server_version[];
 
 int silc_server_alloc(SilcServer *new_server)
 {
+  SilcServer server;
+
   SILC_LOG_DEBUG(("Allocating new server object"));
 
-  *new_server = silc_calloc(1, sizeof(**new_server));
-  if (*new_server == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new server object"));
-    return FALSE;
-  }
+  server = silc_calloc(1, sizeof(*server));
+  server->server_type = SILC_SERVER;
+  server->standalone = TRUE;
+  server->local_list = silc_calloc(1, sizeof(*server->local_list));
+  server->global_list = silc_calloc(1, sizeof(*server->global_list));
+  server->pending_commands = silc_dlist_init();
+#ifdef SILC_SIM
+  server->sim = silc_dlist_init();
+#endif
 
-  /* Set default values */
-  (*new_server)->server_name = NULL;
-  (*new_server)->server_type = SILC_SERVER;
-  (*new_server)->standalone = FALSE;
-  (*new_server)->id = NULL;
-  (*new_server)->io_queue = NULL;
-  (*new_server)->timeout_queue = NULL;
-  (*new_server)->local_list = silc_calloc(1, sizeof(SilcIDListObject));
-  (*new_server)->global_list = silc_calloc(1, sizeof(SilcIDListObject));
-  (*new_server)->rng = NULL;
-  (*new_server)->md5hash = NULL;
-  (*new_server)->sha1hash = NULL;
-  /*  (*new_server)->public_key = NULL;*/
+  *new_server = server;
 
   return TRUE;
 }
@@ -100,14 +73,31 @@ int silc_server_alloc(SilcServer *new_server)
 void silc_server_free(SilcServer server)
 {
   if (server) {
-    if (server->local_list)
-      silc_free(server->local_list);
-    if (server->global_list)
-      silc_free(server->global_list);
+#ifdef SILC_SIM
+    SilcSimContext *sim;
+#endif
+
+    silc_free(server->local_list);
+    silc_free(server->global_list);
     if (server->rng)
       silc_rng_free(server->rng);
 
-    silc_math_primegen_uninit(); /* XXX */
+    if (server->pkcs)
+      silc_pkcs_free(server->pkcs);
+
+#ifdef SILC_SIM
+    while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
+      silc_dlist_del(server->sim, sim);
+      silc_sim_free(sim);
+    }
+    silc_dlist_uninit(server->sim);
+#endif
+
+    silc_free(server->params);
+
+    if (server->pending_commands)
+      silc_dlist_uninit(server->pending_commands);
+
     silc_free(server);
   }
 }
@@ -123,89 +113,93 @@ int silc_server_init(SilcServer server)
 {
   int *sock = NULL, sock_count, i;
   SilcServerID *id;
-  SilcServerList *id_entry;
-  SilcHashObject hash;
+  SilcServerEntry id_entry;
+  SilcIDListPurge purge;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
   assert(server->config);
 
-  /* Set log files where log message should be saved. */
-  server->config->server = server;
-  silc_config_server_setlogfiles(server->config);
+  /* Set public and private keys */
+  if (!server->config->server_info ||
+      !server->config->server_info->public_key || 
+      !server->config->server_info->private_key) {
+    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
+    return FALSE;
+  }
+  server->public_key = server->config->server_info->public_key;
+  server->private_key = server->config->server_info->private_key;
+
+  /* XXX After server is made as Silc Server Library this can be given
+     as argument, for now this is hard coded */
+  server->params = silc_calloc(1, sizeof(*server->params));
+  server->params->retry_count = SILC_SERVER_RETRY_COUNT;
+  server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
+  server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
+  server->params->retry_keep_trying = FALSE;
+  server->params->protocol_timeout = 60;
+  server->params->require_reverse_mapping = FALSE;
+
   /* Register all configured ciphers, PKCS and hash functions. */
-  silc_config_server_register_ciphers(server->config);
-  silc_config_server_register_pkcs(server->config);
-  silc_config_server_register_hashfuncs(server->config);
+  if (!silc_server_config_register_ciphers(server))
+    silc_cipher_register_default();
+  if (!silc_server_config_register_pkcs(server))
+    silc_pkcs_register_default();
+  if (!silc_server_config_register_hashfuncs(server))
+    silc_hash_register_default();
+  if (!silc_server_config_register_hmacs(server))
+    silc_hmac_register_default();
 
   /* Initialize random number generator for the server. */
   server->rng = silc_rng_alloc();
   silc_rng_init(server->rng);
-  silc_math_primegen_init(); /* XXX */
+  silc_rng_global_init(server->rng);
 
   /* Initialize hash functions for server to use */
   silc_hash_alloc("md5", &server->md5hash);
   silc_hash_alloc("sha1", &server->sha1hash);
 
-  /* Initialize none cipher */
-  silc_cipher_alloc("none", &server->none_cipher);
-
-  /* XXXXX Generate RSA key pair */
-  {
-    unsigned char *public_key;
-    unsigned char *private_key;
-    unsigned int pk_len, prv_len;
-
-    if (silc_pkcs_alloc("rsa", &server->public_key) == FALSE) {
-      SILC_LOG_ERROR(("Could not create RSA key pair"));
-      goto err0;
-    }
-
-    if (server->public_key->pkcs->init(server->public_key->context, 
-                                      1024, server->rng) == FALSE) {
-      SILC_LOG_ERROR(("Could not generate RSA key pair"));
-      goto err0;
-    }
-
-    public_key = 
-      server->public_key->pkcs->get_public_key(server->public_key->context,
-                                              &pk_len);
-    private_key = 
-      server->public_key->pkcs->get_private_key(server->public_key->context,
-                                               &prv_len);
-
-    SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
-    SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
-
-    /* XXX Save keys */
-    silc_pkcs_save_public_key(server->public_key, "pubkey.pub",
-                             public_key,  pk_len);
+  /* Allocate PKCS context for local public and private keys */
+  silc_pkcs_alloc(server->public_key->name, &server->pkcs);
+  silc_pkcs_public_key_set(server->pkcs, server->public_key);
+  silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
-    memset(public_key, 0, pk_len);
-    memset(private_key, 0, prv_len);
-    silc_free(public_key);
-    silc_free(private_key);
-  }
-
-  /* Create a listening server. Note that our server can listen on
-     multiple ports. All listeners are created here and now. */
-  /* XXX Still check this whether to use server_info or listen_port. */
+  /* Create a listening server. Note that our server can listen on multiple
+     ports. All listeners are created here and now. */
   sock_count = 0;
-  while(server->config->listen_port) {
+  while (1) {
     int tmp;
 
-    tmp = silc_net_create_server(server->config->listen_port->port,
-                                server->config->listen_port->host);
-    if (tmp < 0)
+    tmp = silc_net_create_server(server->config->server_info->port,
+                                server->config->server_info->server_ip);
+
+    if (tmp < 0) {
+      SILC_LOG_ERROR(("Could not create server listener: %s on %hd",
+                     server->config->server_info->server_ip,
+                     server->config->server_info->port));
       goto err0;
+    }
 
-    sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
+    sock = silc_realloc(sock, sizeof(*sock) * (sock_count + 1));
     sock[sock_count] = tmp;
-    server->config->listen_port = server->config->listen_port->next;
     sock_count++;
+    break;
   }
 
+  /* Initialize ID caches */
+  server->local_list->clients = 
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
+  server->local_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
+  server->local_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+
+  /* These are allocated for normal server as well as these hold some 
+     global information that the server has fetched from its router. For 
+     router these are used as they are supposed to be used on router. */
+  server->global_list->clients = 
+    silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor);
+  server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
+  server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+
   /* Allocate the entire socket list that is used in server. Eventually 
      all connections will have entry in this table (it is a table of 
      pointers to the actual object that is allocated individually 
@@ -220,13 +214,36 @@ int silc_server_init(SilcServer server)
     silc_net_set_socket_nonblock(sock[i]);
     server->sock = sock[i];
     
+    /* Add ourselves also to the socket table. The entry allocated above
+       is sent as argument for fast referencing in the future. */
+    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+    server->sockets[sock[i]] = newsocket;
+    
+    /* Perform name and address lookups to resolve the listenning address
+       and port. */
+    if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname, 
+                                     &newsocket->ip)) {
+      if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
+         !newsocket->ip) {
+       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                       newsocket->hostname ? newsocket->hostname :
+                       newsocket->ip ? newsocket->ip : ""));
+       server->stat.conn_failures++;
+       goto err0;
+      }
+      if (!newsocket->hostname)
+       newsocket->hostname = strdup(newsocket->ip);
+    }
+    newsocket->port = silc_net_get_local_port(sock[i]);
+
     /* Create a Server ID for the server. */
-    silc_id_create_server_id(sock[i], server->rng, &id);
-    if (!id) {
+    silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+    if (!id)
       goto err0;
-    }
     
     server->id = id;
+    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
+    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
     server->id_type = SILC_ID_SERVER;
     server->server_name = server->config->server_info->server_name;
 
@@ -234,77 +251,99 @@ int silc_server_init(SilcServer server)
        beacuse we haven't established a route yet. It will be done later. 
        For now, NULL is sent as router. This allocates new entry to
        the ID list. */
-    silc_idlist_add_server(&server->local_list->servers, 
-                          server->config->server_info->server_name,
-                          server->server_type, server->id, NULL,
-                          server->send_key, server->receive_key,
-                          NULL, NULL, &id_entry);
-    if (!id_entry)
+    id_entry = 
+      silc_idlist_add_server(server->local_list,
+                            server->config->server_info->server_name,
+                            server->server_type, server->id, NULL, NULL);
+    if (!id_entry) {
+      SILC_LOG_ERROR(("Could not add ourselves to cache"));
       goto err0;
+    }
+    id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
     
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry, 
-                     &newsocket);
-    if (!newsocket)
-      goto err0;
-
-    server->sockets[sock[i]] = newsocket;
-
     /* Put the allocated socket pointer also to the entry allocated above 
        for fast back-referencing to the socket list. */
-    id_entry->connection = (void *)server->sockets[sock[i]];
+    newsocket->user_data = (void *)id_entry;
+    id_entry->connection = (void *)newsocket;
     server->id_entry = id_entry;
   }
 
-  /* Register the task queues. In SILC we have by default three task queues. 
-     One task queue for non-timeout tasks which perform different kind of 
-     I/O on file descriptors, timeout task queue for timeout tasks, and,
-     generic non-timeout task queue whose tasks apply to all connections. */
-  silc_task_queue_alloc(&server->io_queue, TRUE);
-  if (!server->io_queue) {
+  /* Register protocols */
+  silc_server_protocols_register();
+
+  /* Initialize the scheduler. */
+  server->schedule = silc_schedule_init(SILC_SERVER_MAX_CONNECTIONS);
+  if (!server->schedule)
     goto err0;
-  }
-  silc_task_queue_alloc(&server->timeout_queue, TRUE);
-  if (!server->timeout_queue) {
-    goto err1;
-  }
-  silc_task_queue_alloc(&server->generic_queue, TRUE);
-  if (!server->generic_queue) {
-    goto err1;
-  }
 
-  /* Initialize the scheduler */
-  silc_schedule_init(server->io_queue, server->timeout_queue, 
-                    server->generic_queue, 
-                    SILC_SERVER_MAX_CONNECTIONS);
-  
-  /* Add the first task to the queue. This is task that is executed by
+  /* Add the first task to the scheduler. This is task that is executed by
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  if (silc_task_register(server->timeout_queue, sock[0], 
+  silc_schedule_task_add(server->schedule, sock[0], 
                         silc_server_connect_to_router,
                         (void *)server, 0, 1,
                         SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    goto err2;
-  }
+                        SILC_TASK_PRI_NORMAL);
+
+  /* Add listener task to the scheduler. This task receives new connections
+     to the server. This task remains on the queue until the end of the 
+     program. */
+  silc_schedule_task_add(server->schedule, sock[0],
+                        silc_server_accept_new_connection,
+                        (void *)server, 0, 0, 
+                        SILC_TASK_FD,
+                        SILC_TASK_PRI_NORMAL);
+  server->listenning = TRUE;
+
+  /* Send log file configuration */
+  silc_server_config_setlogfiles(server->config, server->schedule);
 
   /* If server connections has been configured then we must be router as
      normal server cannot have server connections, only router connections. */
-  if (server->config->servers)
+  if (server->config->servers) {
+    SilcServerConfigSectionServer *ptr = server->config->servers;
+
     server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
+    }
+  }
+
+  /* Register the ID Cache purge task. This periodically purges the ID cache
+     and removes the expired cache entries. */
+
+  /* Clients local list */
+  purge = silc_calloc(1, sizeof(*purge));
+  purge->cache = server->local_list->clients;
+  purge->schedule = server->schedule;
+  purge->timeout = 600;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, purge->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+  /* Clients global list */
+  purge = silc_calloc(1, sizeof(*purge));
+  purge->cache = server->global_list->clients;
+  purge->schedule = server->schedule;
+  purge->timeout = 300;
+  silc_schedule_task_add(purge->schedule, 0, 
+                        silc_idlist_purge,
+                        (void *)purge, purge->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
   return TRUE;
 
- err2:
-  silc_task_queue_free(server->timeout_queue);
- err1:
-  silc_task_queue_free(server->io_queue);
  err0:
   for (i = 0; i < sock_count; i++)
     silc_net_close_server(sock[i]);
@@ -312,6 +351,139 @@ int silc_server_init(SilcServer server)
   return FALSE;
 }
 
+/* Fork server to background */
+
+void silc_server_daemonise(SilcServer server)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Forking SILC server to background"));
+
+  i = fork();
+
+  if (i < 0) {
+    SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
+    exit(1);
+  }
+  else if (i) {
+    if (geteuid())
+      SILC_LOG_DEBUG(("Server started as user"));
+    else
+      SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+    exit(0);
+  }
+  setsid();
+}
+
+/* Drop root privligies. If this cannot be done, die. */
+
+void silc_server_drop(SilcServer server)
+{
+  /* Are we executing silcd as root or a regular user? */
+  if (!geteuid()) {
+    struct passwd *pw;
+    struct group *gr;
+    char *user, *group;
+
+    if (!server->config->server_info->user || !server->config->server_info->group) {
+      fprintf(stderr, "Error:"
+       "\tSILC server must not be run as root.  For the security of your\n"
+       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
+       "\tuser account.  Modify the [Identity] configuration section to run\n"
+       "\tthe server as non-root user.\n");
+      exit(1);
+    }
+
+    /* Get the values given for user and group in configuration file */
+    user=server->config->server_info->user;
+    group=server->config->server_info->group;
+
+    /* Check whether the user/group information is text */ 
+    if (atoi(user)!=0 || atoi(group)!=0) {
+      SILC_LOG_DEBUG(("Invalid user and/or group information"));
+      SILC_LOG_DEBUG(("User and/or group given as number"));
+      fprintf(stderr, "Invalid user and/or group information\n");
+      fprintf(stderr, "Please assign them as names, not numbers\n");
+      exit(1);
+    }
+
+    /* Catch the nasty incident of string "0" returning 0 from atoi */
+    if (strcmp("0", user)==0 || strcmp("0", group)==0) {
+      SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
+      fprintf(stderr, "User and/or group configured to 0. Exiting\n");
+      exit(1);
+    }
+
+    if (!(pw=getpwnam(user))) {
+      fprintf(stderr, "No such user %s found\n", user);
+      exit(1);
+    }
+
+    if (!(gr=getgrnam(group))) {
+      fprintf(stderr, "No such group %s found\n", group);
+      exit(1);
+    }
+
+    /* Check whether user and/or group is set to root. If yes, exit
+       immediately. Otherwise, setgid and setuid server to user.group */
+    if (gr->gr_gid==0 || pw->pw_uid==0) {
+      fprintf(stderr, "Error:"
+       "\tSILC server must not be run as root.  For the security of your\n"
+       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
+       "\tuser account.  Modify the [Identity] configuration section to run\n"
+       "\tthe server as non-root user.\n");
+      exit(1);
+    } else {
+      SILC_LOG_DEBUG(("Changing to group %s", group));
+      if (setgid(gr->gr_gid)==0) {
+        SILC_LOG_DEBUG(("Setgid to %s", group));
+      } else {
+        SILC_LOG_DEBUG(("Setgid to %s failed", group));
+        fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
+                group);
+        exit(1);
+      }
+#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
+      if (setgroups(0, NULL)!=0) {
+        SILC_LOG_DEBUG(("Setgroups to NULL failed"));
+        fprintf(stderr, "Tried to setgroups NULL but failed. Exiting\n");
+        exit(1);
+      }
+      if (initgroups(user, gr->gr_gid)!=0) {
+        SILC_LOG_DEBUG(("Initgroups to user %s (gid=%d) failed", user, gr->gr_gid));
+        fprintf(stderr, "Tried to initgroups %s (gid=%d) but no such user. Exiting\n",
+                user, gr->gr_gid);
+        exit(1);
+      }
+#endif
+      SILC_LOG_DEBUG(("Changing to user %s", user));
+      if (setuid(pw->pw_uid)==0) {
+        SILC_LOG_DEBUG(("Setuid to %s", user));
+      } else {
+        SILC_LOG_DEBUG(("Setuid to %s failed", user));
+        fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
+                user);
+        exit(1);
+      }
+    }
+  }
+}
+
+/* The heart of the server. This runs the scheduler thus runs the server. 
+   When this returns the server has been stopped and the program will
+   be terminated. */
+
+void silc_server_run(SilcServer server)
+{
+  SILC_LOG_DEBUG(("Running server"));
+
+  SILC_LOG_INFO(("SILC Server started"));
+
+  /* Start the scheduler, the heart of the SILC server. When this returns
+     the program will be terminated. */
+  silc_schedule(server->schedule);
+}
+
 /* Stops the SILC server. This function is used to shutdown the server. 
    This is usually called after the scheduler has returned. After stopping 
    the server one should call silc_server_free. */
@@ -320,26 +492,161 @@ void silc_server_stop(SilcServer server)
 {
   SILC_LOG_DEBUG(("Stopping server"));
 
-  /* Stop the scheduler, although it might be already stopped. This
-     doesn't hurt anyone. This removes all the tasks and task queues,
-     as well. */
-  silc_schedule_stop();
-  silc_schedule_uninit();
+  if (server->schedule) {
+    silc_schedule_stop(server->schedule);
+    silc_schedule_uninit(server->schedule);
+    server->schedule = NULL;
+  }
+
+  silc_server_protocols_unregister();
 
   SILC_LOG_DEBUG(("Server stopped"));
 }
 
-/* The heart of the server. This runs the scheduler thus runs the server. */
+/* Function that is called when the network connection to a router has
+   been established.  This will continue with the key exchange protocol
+   with the remote router. */
 
-void silc_server_run(SilcServer server)
+void silc_server_start_key_exchange(SilcServer server,
+                                   SilcServerConnection sconn,
+                                   int sock)
 {
-  SILC_LOG_DEBUG(("Running server"));
+  SilcSocketConnection newsocket;
+  SilcProtocol protocol;
+  SilcServerKEInternalContext *proto_ctx;
+  void *context;
 
-  /* Start the scheduler, the heart of the SILC server. When this returns
-     the program will be terminated. */
-  silc_schedule();
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* Create socket connection for the connection. Even though we
+     know that we are connecting to a router we will mark the socket
+     to be unknown connection until we have executed authentication
+     protocol. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+  newsocket->hostname = strdup(sconn->remote_host);
+  newsocket->ip = strdup(sconn->remote_host);
+  newsocket->port = sconn->remote_port;
+  sconn->sock = newsocket;
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->context = (void *)sconn;
+  proto_ctx->sock = newsocket;
+  proto_ctx->rng = server->rng;
+  proto_ctx->responder = FALSE;
+      
+  /* Perform key exchange protocol. silc_server_connect_to_router_second
+     will be called after the protocol is finished. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
+                     &protocol, proto_ctx,
+                     silc_server_connect_to_router_second);
+  newsocket->protocol = protocol;
+      
+  /* Register a timeout task that will be executed if the protocol
+     is not executed within set limit. */
+  proto_ctx->timeout_task = 
+    silc_schedule_task_add(server->schedule, sock, 
+                      silc_server_timeout_remote,
+                      server, server->params->protocol_timeout,
+                      server->params->protocol_timeout_usec,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_LOW);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as 
+     well. However, this doesn't set the scheduler for outgoing traffic,
+     it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)server;
+  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+  
+  /* Run the protocol */
+  silc_protocol_execute(protocol, server->schedule, 0, 0);
+}
+
+/* Timeout callback that will be called to retry connecting to remote
+   router. This is used by both normal and router server. This will wait
+   before retrying the connecting. The timeout is generated by exponential
+   backoff algorithm. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+
+  SILC_LOG_INFO(("Retrying connecting to a router"));
+
+  /* Calculate next timeout */
+  if (sconn->retry_count >= 1) {
+    sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+    if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
+      sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
+  } else {
+    sconn->retry_timeout = server->params->retry_interval_min;
+  }
+  sconn->retry_count++;
+  sconn->retry_timeout = sconn->retry_timeout +
+    silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+
+  /* If we've reached max retry count, give up. */
+  if (sconn->retry_count > server->params->retry_count && 
+      server->params->retry_keep_trying == FALSE) {
+    SILC_LOG_ERROR(("Could not connect to router, giving up"));
+    silc_free(sconn->remote_host);
+    silc_free(sconn);
+    return;
+  }
+
+  /* Wait one before retrying */
+  silc_schedule_task_add(server->schedule, fd, silc_server_connect_router,
+                        context, sconn->retry_timeout, 
+                        server->params->retry_interval_min_usec,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
 }
 
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{    
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  int sock;
+
+  SILC_LOG_INFO(("Connecting to the %s %s on port %d", 
+                (sconn->backup ? "backup router" : "router"), 
+                sconn->remote_host, sconn->remote_port));
+
+  server->router_connect = time(0);
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(server->config->server_info->server_ip,
+                                   sconn->remote_port, 
+                                   sconn->remote_host);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not connect to router %s:%d",
+                   sconn->remote_host, sconn->remote_port));
+    if (!sconn->no_reconnect)
+      silc_schedule_task_add(server->schedule, fd, 
+                            silc_server_connect_to_router_retry,
+                            context, 0, 1, SILC_TASK_TIMEOUT, 
+                            SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
+}
+  
 /* This function connects to our primary router or if we are a router this
    establishes all our primary routes. This is called at the start of the
    server to do authentication and key exchange with our router - called
@@ -348,154 +655,53 @@ void silc_server_run(SilcServer server)
 SILC_TASK_CALLBACK(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
-  int sock;
+  SilcServerConnection sconn;
+  SilcServerConfigSectionRouter *ptr;
 
   SILC_LOG_DEBUG(("Connecting to router(s)"));
 
-  /* if we are normal SILC server we need to connect to our cell's
-     router. */
   if (server->server_type == SILC_SERVER) {
-    SilcProtocol protocol;
-    SilcServerKEInternalContext *proto_ctx;
-
-    /* Create connection to the router, if configured. */
-    if (server->config->routers) {
-      sock = silc_net_create_connection(server->config->routers->port, 
-                                       server->config->routers->host);
-      if (sock < 0) {
-       SILC_LOG_ERROR(("Could not connect to router"));
-       silc_schedule_stop();
-       return;
-      }
-
-      /* Set socket options */
-      silc_net_set_socket_nonblock(sock);
-      silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-      /* Create socket connection for the connection. Even though we
-        know that we are connecting to a router we will mark the socket
-        to be unknown connection until we have executed authentication
-        protocol. */
-      silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-      server->sockets[sock] = newsocket;
-      newsocket->hostname = server->config->routers->host;
-      newsocket->port = server->config->routers->port;
-
-      /* Allocate internal protocol context. This is sent as context
-        to the protocol. */
-      proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-      proto_ctx->server = context;
-      proto_ctx->sock = newsocket;
-      proto_ctx->rng = server->rng;
-      proto_ctx->responder = FALSE;
-      
-      /* Perform key exchange protocol. silc_server_connect_to_router_second
-        will be called after the protocol is finished. */
-      silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                         &protocol, proto_ctx,
-                         silc_server_connect_to_router_second);
-      newsocket->protocol = protocol;
-      
-      /* Register a timeout task that will be executed if the protocol
-        is not executed within 15 seconds. For now, this is a hard coded 
-        limit. After 15 secs the connection will be closed if the key 
-        exchange protocol has not been executed. */
-      proto_ctx->timeout_task = 
-       silc_task_register(server->timeout_queue, sock, 
-                          silc_server_timeout_remote,
-                          context, 15, 0,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW);
-
-      /* Register the connection for network input and output. This sets
-        that scheduler will listen for incoming packets for this connection 
-        and sets that outgoing packets may be sent to this connection as 
-        well. However, this doesn't set the scheduler for outgoing traffic,
-        it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-        later when outgoing data is available. */
-      SILC_REGISTER_CONNECTION_FOR_IO(sock);
-      
-      /* Run the protocol */
-      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
-      return;
-    }
+    SILC_LOG_DEBUG(("We are normal server"));
+  } else if (server->server_type == SILC_ROUTER) {
+    SILC_LOG_DEBUG(("We are router"));
+  } else {
+    SILC_LOG_DEBUG(("We are backup router/normal server"));
   }
-  
-  /* if we are a SILC router we need to establish all of our primary
-     routes. */
-  if (server->server_type == SILC_ROUTER) {
-    SilcConfigServerSectionServerConnection *ptr;
 
-    /* Create the connections to all our routes */
-    ptr = server->config->routers;
-    while (ptr) {
-      SilcProtocol protocol;
-      SilcServerKEInternalContext *proto_ctx;
-
-      /* Create the connection to the remote end */
-      sock = silc_net_create_connection(ptr->port, ptr->host);
-      if (sock < 0) {
-       SILC_LOG_ERROR(("Could not connect to router"));
-       silc_schedule_stop();
-       return;
+  /* Create the connections to all our routes */
+  ptr = server->config->routers;
+  while (ptr) {
+    
+    SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+                   ptr->backup_router ? "Backup router" : "Router",
+                   ptr->initiator ? "Initiator" : "Responder",
+                   ptr->host, ptr->port));
+
+    if (ptr->initiator) {
+      /* Allocate connection object for hold connection specific stuff. */
+      sconn = silc_calloc(1, sizeof(*sconn));
+      sconn->server = server;
+      sconn->remote_host = strdup(ptr->host);
+      sconn->remote_port = ptr->port;
+      sconn->backup = ptr->backup_router;
+      if (sconn->backup) {
+       sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
+       sconn->backup_replace_port = ptr->backup_replace_port;
       }
 
-      /* Set socket options */
-      silc_net_set_socket_nonblock(sock);
-      silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-      /* Create socket connection for the connection. Even though we
-        know that we are connecting to a router we will mark the socket
-        to be unknown connection until we have executed authentication
-        protocol. */
-      silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-      server->sockets[sock] = newsocket;
-      newsocket->hostname = ptr->host;
-      newsocket->port = ptr->port;
-
-      /* Allocate internal protocol context. This is sent as context
-        to the protocol. */
-      proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-      proto_ctx->server = context;
-      proto_ctx->sock = newsocket;
-      proto_ctx->rng = server->rng;
-      proto_ctx->responder = FALSE;
-      
-      /* Perform key exchange protocol. silc_server_connect_to_router_final
-        will be called after the protocol is finished. */
-      silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                         &protocol, proto_ctx,
-                         silc_server_connect_to_router_second);
-      newsocket->protocol = protocol;
-
-      /* Register a timeout task that will be executed if the protocol
-        is not executed within 15 seconds. For now, this is a hard coded 
-        limit. After 15 secs the connection will be closed if the key 
-        exchange protocol has not been executed. */
-      proto_ctx->timeout_task = 
-       silc_task_register(server->timeout_queue, sock, 
-                          silc_server_timeout_remote,
-                          context, 15, 0,
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_LOW);
-
-      /* Register the connection for network input and output. This sets
-        that scheduler will listen for incoming packets for this connection 
-        and sets that outgoing packets may be sent to this connection as 
-        well. However, this doesn't set the scheduler for outgoing traffic,
-        it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-        later when outgoing data is available. */
-      SILC_REGISTER_CONNECTION_FOR_IO(sock);
-      
-      /* Run the protocol */
-      protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
-
-      if (!ptr->next)
-       return;
+      if (!server->router_conn && !sconn->backup)
+       server->router_conn = sconn;
 
-      ptr = ptr->next;
+      silc_schedule_task_add(server->schedule, fd, 
+                            silc_server_connect_router,
+                            (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                            SILC_TASK_PRI_NORMAL);
     }
+
+    if (!ptr->next)
+      return;
+    
+    ptr = ptr->next;
   }
 
   SILC_LOG_DEBUG(("No router(s), server will be standalone"));
@@ -503,17 +709,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   /* There wasn't a configured router, we will continue but we don't
      have a connection to outside world.  We will be standalone server. */
   server->standalone = TRUE;
-
-  /* Add a task to the queue. This task receives new connections to the 
-     server. This task remains on the queue until the end of the program. */
-  if (silc_task_register(server->io_queue, fd, 
-                        silc_server_accept_new_connection,
-                        (void *)server, 0, 0, 
-                        SILC_TASK_FD,
-                        SILC_TASK_PRI_NORMAL) == NULL) {
-    silc_schedule_stop();
-    return;
-  }
 }
 
 /* Second part of connecting to router(s). Key exchange protocol has been
@@ -525,64 +720,104 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = NULL;
+  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
+  SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
+  SilcServerConfigSectionRouter *conn = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
     return;
   }
   
+  /* We now have the key material as the result of the key exchange
+     protocol. Take the key material into use. Free the raw key material
+     as soon as we've set them into use. */
+  if (!silc_server_protocol_ke_set_keys(server, ctx->ske, 
+                                       ctx->sock, ctx->keymat,
+                                       ctx->ske->prop->cipher,
+                                       ctx->ske->prop->pkcs,
+                                       ctx->ske->prop->hash,
+                                       ctx->ske->prop->hmac,
+                                       ctx->ske->prop->group,
+                                       ctx->responder)) {
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    silc_ske_free_key_material(ctx->keymat);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    return;
+  }    
+  silc_ske_free_key_material(ctx->keymat);
+
   /* Allocate internal context for the authentication protocol. This
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->context = (void *)sconn;
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;      /* Save SKE object from previous protocol */
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
 
-  /* Resolve the authentication method used in this connection */
-  proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
-  if (server->config->routers) {
-    SilcConfigServerSectionServerConnection *conn = NULL;
-
-    /* Check if we find a match from user configured connections */
-    conn = silc_config_server_find_router_conn(server->config,
-                                              sock->hostname,
-                                              sock->port);
-    if (conn) {
-      /* Match found. Use the configured authentication method */
-      proto_ctx->auth_meth = conn->auth_meth;
-      if (conn->auth_data) {
-       proto_ctx->auth_data = strdup(conn->auth_data);
-       proto_ctx->auth_data_len = strlen(conn->auth_data);
-      }
-    } else {
-      /* No match found. */
-      /* XXX */
+  /* Resolve the authentication method used in this connection. Check if 
+     we find a match from user configured connections */
+  conn = silc_server_config_find_router_conn(server->config,
+                                            sock->hostname,
+                                            sock->port);
+  if (conn) {
+    /* Match found. Use the configured authentication method */
+    proto_ctx->auth_meth = conn->auth_meth;
+    if (conn->auth_data) {
+      proto_ctx->auth_data = strdup(conn->auth_data);
+      proto_ctx->auth_data_len = strlen(conn->auth_data);
     }
   } else {
-    /* XXX */
+    SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
+                   sock->hostname, sock->ip, sock->port));
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    return;
   }
 
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -598,15 +833,14 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
      this timelimit the connection will be terminated. Currently
      this is 15 seconds and is hard coded limit (XXX). */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
+    silc_schedule_task_add(server->schedule, sock->sock, 
                       silc_server_timeout_remote,
                       (void *)server, 15, 0,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_LOW);
 
   /* Run the protocol */
-  sock->protocol->execute(server->timeout_queue, 0, 
-                         sock->protocol, sock->sock, 0, 0);
+  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
 }
 
 /* Finalizes the connection to router. Registers a server task to the
@@ -618,54 +852,46 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   SilcServerConnAuthInternalContext *ctx = 
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
+  SilcServerConnection sconn = (SilcServerConnection)ctx->context;
   SilcSocketConnection sock = ctx->sock;
-  SilcServerList *id_entry;
-  SilcIDListUnknown *conn_data;
+  SilcServerEntry id_entry;
   SilcBuffer packet;
+  SilcServerHBContext hb_context;
   unsigned char *id_string;
+  uint32 id_len;
+  SilcIDListData idata;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    silc_protocol_free(protocol);
-    if (ctx->packet)
-      silc_buffer_free(ctx->packet);
-    if (ctx->ske)
-      silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
-    silc_free(ctx);
-    sock->protocol = NULL;
+    silc_free(ctx->dest_id);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
-    return;
+    goto out;
   }
 
   /* Add a task to the queue. This task receives new connections to the 
      server. This task remains on the queue until the end of the program. */
-  if (!server->listenning) {
-    if (silc_task_register(server->io_queue, server->sock, 
+  if (!server->listenning && !sconn->backup) {
+    silc_schedule_task_add(server->schedule, server->sock, 
                           silc_server_accept_new_connection,
                           (void *)server, 0, 0, 
                           SILC_TASK_FD,
-                          SILC_TASK_PRI_NORMAL) == NULL) {
-      silc_schedule_stop();
-      return;
-    } else {
-      server->listenning = TRUE;
-    }
+                          SILC_TASK_PRI_NORMAL);
+    server->listenning = TRUE;
   }
 
   /* Send NEW_SERVER packet to the router. We will become registered
      to the SILC network after sending this packet. */
   id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN + 
-                            strlen(server->server_name));
+  id_len = silc_id_get_len(server->id, SILC_ID_SERVER);
+  packet = silc_buffer_alloc(2 + 2 + id_len + strlen(server->server_name));
   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
   silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN),
+                    SILC_STR_UI_SHORT(id_len),
+                    SILC_STR_UI_XNSTRING(id_string, id_len),
                     SILC_STR_UI_SHORT(strlen(server->server_name)),
                     SILC_STR_UI_XNSTRING(server->server_name,
                                          strlen(server->server_name)),
@@ -677,120 +903,273 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   silc_buffer_free(packet);
   silc_free(id_string);
 
-  SILC_LOG_DEBUG(("Connected to router %s", sock->hostname));
+  SILC_LOG_INFO(("Connected to router %s", sock->hostname));
+
+  /* Check that we do not have this ID already */
+  id_entry = silc_idlist_find_server_by_id(server->local_list, 
+                                          ctx->dest_id, TRUE, NULL);
+  if (id_entry) {
+    silc_idcache_del_by_context(server->local_list->servers, id_entry);
+  } else {
+    id_entry = silc_idlist_find_server_by_id(server->global_list, 
+                                            ctx->dest_id, TRUE, NULL);
+    if (id_entry) 
+      silc_idcache_del_by_context(server->global_list->servers, id_entry);
+  }
+
+  SILC_LOG_DEBUG(("New server id(%s)",
+                 silc_id_render(ctx->dest_id, SILC_ID_SERVER)));
 
-  /* Add the connected router to local server list */
-  server->standalone = FALSE;
-  conn_data = (SilcIDListUnknown *)sock->user_data;
-  silc_idlist_add_server(&server->local_list->servers, 
-                        sock->hostname ? sock->hostname : sock->ip,
-                        SILC_ROUTER, ctx->dest_id, NULL,
-                        conn_data->send_key, conn_data->receive_key,
-                        conn_data->pkcs, conn_data->hmac, &id_entry);
+  /* Add the connected router to global server list */
+  id_entry = silc_idlist_add_server(server->global_list, 
+                                   strdup(sock->hostname),
+                                   SILC_ROUTER, ctx->dest_id, NULL, sock);
+  if (!id_entry) {
+    silc_free(ctx->dest_id);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Authentication failed");
+    goto out;
+  }
 
-  id_entry->hmac_key = conn_data->hmac_key;
-  id_entry->hmac_key_len = conn_data->hmac_key_len;
-  id_entry->connection = sock;
+  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
+  silc_free(sock->user_data);
   sock->user_data = (void *)id_entry;
   sock->type = SILC_SOCKET_TYPE_ROUTER;
-  server->id_entry->router = id_entry;
+  idata = (SilcIDListData)sock->user_data;
+  idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+
+  /* Perform keepalive. The `hb_context' will be freed automatically
+     when finally calling the silc_socket_free function. XXX hardcoded 
+     timeout!! */
+  hb_context = silc_calloc(1, sizeof(*hb_context));
+  hb_context->server = server;
+  silc_socket_set_heartbeat(sock, 300, hb_context,
+                           silc_server_perform_heartbeat,
+                           server->schedule);
+
+  /* Register re-key timeout */
+  idata->rekey->timeout = 3600; /* XXX hardcoded */
+  idata->rekey->context = (void *)server;
+  silc_schedule_task_add(server->schedule, sock->sock, 
+                        silc_server_rekey_callback,
+                        (void *)sock, idata->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  if (!sconn->backup) {
+    /* Mark this router our primary router if we're still standalone */
+    if (server->standalone) {
+      server->id_entry->router = id_entry;
+      server->router = id_entry;
+      server->standalone = FALSE;
+    
+      /* If we are router then announce our possible servers. */
+      if (server->server_type == SILC_ROUTER)
+       silc_server_announce_servers(server, FALSE, 0, 
+                                    server->router->connection);
+
+      /* Announce our clients and channels to the router */
+      silc_server_announce_clients(server, 0, server->router->connection);
+      silc_server_announce_channels(server, 0, server->router->connection);
+    }
+  } else {
+    /* Add this server to be our backup router */
+    silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
+                          sconn->backup_replace_port, FALSE);
+  }
+
+  sock->protocol = NULL;
 
-  /* Free the temporary connection data context from key exchange */
-  silc_free(conn_data);
+  /* Call the completion callback to indicate that we've connected to
+     the router */
+  if (sconn->callback)
+    (*sconn->callback)(server, id_entry, sconn->callback_context);
+
+ out:
+  /* Free the temporary connection data context */
+  if (sconn) {
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+  }
+  if (sconn == server->router_conn)
+    server->router_conn = NULL;
 
   /* Free the protocol object */
+  if (sock->protocol == protocol)
+    sock->protocol = NULL;
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_free(ctx->auth_data);
   silc_free(ctx);
-  sock->protocol = NULL;
 }
 
-/* Accepts new connections to the server. Accepting new connections are
-   done in three parts to make it async. */
+/* Host lookup callbcak that is called after the incoming connection's
+   IP and FQDN lookup is performed. This will actually check the acceptance
+   of the incoming connection and will register the key exchange protocol
+   for this connection. */
 
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+static void 
+silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
+                                        void *context)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
   SilcServerKEInternalContext *proto_ctx;
-  int sock;
+  void *cconfig, *sconfig, *rconfig;
+  SilcServerConfigSectionDeny *deny;
+  int port;
 
-  SILC_LOG_DEBUG(("Accepting new connection"));
+  SILC_LOG_DEBUG(("Start"));
 
-  sock = silc_net_accept_connection(server->sock);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+  /* Check whether we could resolve both IP and FQDN. */
+  if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
+                   server->params->require_reverse_mapping)) {
+    SILC_LOG_ERROR(("IP/DNS lookup failed %s",
+                   sock->hostname ? sock->hostname :
+                   sock->ip ? sock->ip : ""));
+    server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, sock,
+                                 "Server closed connection: Unknown host");
     return;
   }
 
-  /* Check max connections */
-  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    if (server->config->redirect) {
-      /* XXX Redirecting connection to somewhere else now?? */
-      /*silc_server_send_notify("Server is full, trying to redirect..."); */
-    } else {
-      SILC_LOG_ERROR(("Refusing connection, server is full"));
-    }
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it
+     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
+
+  SILC_LOG_INFO(("Incoming connection from %s (%s)", sock->hostname,
+                sock->ip));
+
+  port = server->sockets[server->sock]->port; /* Listenning port */
+
+  /* Check whether this connection is denied to connect to us. */
+  deny = silc_server_config_find_denied(server->config, sock->ip, port);
+  if (!deny)
+    deny = silc_server_config_find_denied(server->config, sock->hostname,
+                                         port);
+  if (deny) {
+    /* The connection is denied */
+    SILC_LOG_INFO(("Connection %s (%s) is denied", 
+                   sock->hostname, sock->ip));
+    silc_server_disconnect_remote(server, sock, deny->reason ?
+                                 deny->reason :
+                                 "Server closed connection: "
+                                 "Connection refused");
+    server->stat.conn_failures++;
     return;
   }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* We don't create a ID yet, since we don't know what type of connection
-     this is yet. But, we do add the connection to the socket table. */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-  server->sockets[sock] = newsocket;
-
-  /* XXX This MUST be done async as this will block the entire server
-     process. Either we have to do our own resolver stuff or in the future
-     we can use threads. */
-  /* Perform mandatory name and address lookups for the remote host. */
-  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
-  if (!newsocket->ip || !newsocket->hostname) {
-    SILC_LOG_DEBUG(("IP lookup/DNS lookup failed"));
-    SILC_LOG_ERROR(("IP lookup/DNS lookup failed"));
+  /* Check whether we have configred this sort of connection at all. We
+     have to check all configurations since we don't know what type of
+     connection this is. */
+  if (!(cconfig = silc_server_config_find_client(server->config,
+                                                     sock->ip, port)))
+    cconfig = silc_server_config_find_client(server->config,
+                                                 sock->hostname, 
+                                                 port);
+  if (!(sconfig = silc_server_config_find_server_conn(server->config,
+                                                    sock->ip, 
+                                                    port)))
+    sconfig = silc_server_config_find_server_conn(server->config,
+                                                 sock->hostname,
+                                                 port);
+  if (!(rconfig = silc_server_config_find_router_conn(server->config,
+                                                    sock->ip, port)))
+    rconfig = silc_server_config_find_router_conn(server->config,
+                                                 sock->hostname, 
+                                                 port);
+  if (!cconfig && !sconfig && !rconfig) {
+    SILC_LOG_INFO(("Connection %s (%s) is not allowed", 
+                   sock->hostname, sock->ip));
+    silc_server_disconnect_remote(server, sock, 
+                                 "Server closed connection: "
+                                 "Connection refused");
+    server->stat.conn_failures++;
     return;
   }
 
+  /* The connection is allowed */
+
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = context;
-  proto_ctx->sock = newsocket;
+  proto_ctx->sock = sock;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = TRUE;
+  proto_ctx->cconfig = cconfig;
+  proto_ctx->sconfig = sconfig;
+  proto_ctx->rconfig = rconfig;
 
   /* Prepare the connection for key exchange protocol. We allocate the
      protocol but will not start it yet. The connector will be the
      initiator of the protocol thus we will wait for initiation from 
      there before we start the protocol. */
+  server->stat.auth_attempts++;
   silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &newsocket->protocol, proto_ctx, 
+                     &sock->protocol, proto_ctx, 
                      silc_server_accept_new_connection_second);
 
   /* Register a timeout task that will be executed if the connector
-     will not start the key exchange protocol within 15 seconds. For
-     now, this is a hard coded limit. After 15 secs the connection will
+     will not start the key exchange protocol within 60 seconds. For
+     now, this is a hard coded limit. After 60 secs the connection will
      be closed if the key exchange protocol has not been started. */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, newsocket->sock, 
-                      silc_server_timeout_remote,
-                      context, 15, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+    silc_schedule_task_add(server->schedule, sock->sock, 
+                          silc_server_timeout_remote,
+                          context, 60, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+}
 
-  /* Register the connection for network input and output. This sets
-     that scheduler will listen for incoming packets for this connection 
-     and sets that outgoing packets may be sent to this connection as well.
-     However, this doesn't set the scheduler for outgoing traffic, it
-     will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
-     later when outgoing data is available. */
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+/* Accepts new connections to the server. Accepting new connections are
+   done in three parts to make it async. */
+
+SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+{
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection newsocket;
+  int sock;
+
+  SILC_LOG_DEBUG(("Accepting new connection"));
+
+  server->stat.conn_attempts++;
+
+  sock = silc_net_accept_connection(server->sock);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Check max connections */
+  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* We don't create a ID yet, since we don't know what type of connection
+     this is yet. But, we do add the connection to the socket table. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+
+  /* Perform asynchronous host lookup. This will lookup the IP and the
+     FQDN of the remote connection. After the lookup is done the connection
+     is accepted further. */
+  silc_socket_host_lookup(newsocket, TRUE, 
+                         silc_server_accept_new_connection_lookup, context, 
+                         server->schedule);
 }
 
 /* Second part of accepting new connection. Key exchange protocol has been
@@ -805,41 +1184,77 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
   SilcServerKEInternalContext *ctx = 
     (SilcServerKEInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
-  SilcSocketConnection sock = NULL;
+  SilcSocketConnection sock = ctx->sock;
   SilcServerConnAuthInternalContext *proto_ctx;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    silc_ske_free_key_material(ctx->keymat);
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Key exchange failed");
+    server->stat.auth_failures++;
     return;
   }
 
+  /* We now have the key material as the result of the key exchange
+     protocol. Take the key material into use. Free the raw key material
+     as soon as we've set them into use. */
+  if (!silc_server_protocol_ke_set_keys(server, ctx->ske, 
+                                       ctx->sock, ctx->keymat,
+                                       ctx->ske->prop->cipher,
+                                       ctx->ske->prop->pkcs,
+                                       ctx->ske->prop->hash,
+                                       ctx->ske->prop->hmac,
+                                       ctx->ske->prop->group,
+                                       ctx->responder)) {
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    silc_ske_free_key_material(ctx->keymat);
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx->dest_id);
+    silc_free(ctx);
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
+    silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                                 "Key exchange failed");
+    server->stat.auth_failures++;
+    return;
+  }    
+  silc_ske_free_key_material(ctx->keymat);
+
   /* Allocate internal context for the authentication protocol. This
      is sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock = server->sockets[fd];
+  proto_ctx->sock = sock;
   proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
   proto_ctx->responder = TRUE;
   proto_ctx->dest_id_type = ctx->dest_id_type;
   proto_ctx->dest_id = ctx->dest_id;
+  proto_ctx->cconfig = ctx->cconfig;
+  proto_ctx->sconfig = ctx->sconfig;
+  proto_ctx->rconfig = ctx->rconfig;
 
   /* Free old protocol as it is finished now */
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   silc_free(ctx);
   sock->protocol = NULL;
 
@@ -855,11 +1270,11 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
      this timelimit the connection will be terminated. Currently
      this is 60 seconds and is hard coded limit (XXX). */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, sock->sock, 
-                      silc_server_timeout_remote,
-                      (void *)server, 60, 0,
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_LOW);
+    silc_schedule_task_add(server->schedule, sock->sock, 
+                          silc_server_timeout_remote,
+                          (void *)server, 60, 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
 }
 
 /* Final part of accepting new connection. The connection has now
@@ -873,120 +1288,189 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     (SilcServerConnAuthInternalContext *)protocol->context;
   SilcServer server = (SilcServer)ctx->server;
   SilcSocketConnection sock = ctx->sock;
+  SilcServerHBContext hb_context;
+  SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
+  void *id_entry;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
+    sock->protocol = NULL;
     if (ctx->packet)
-      silc_buffer_free(ctx->packet);
+      silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
-    if (ctx->dest_id)
-      silc_free(ctx->dest_id);
+    silc_free(ctx->dest_id);
     silc_free(ctx);
-    sock->protocol = NULL;
+    silc_schedule_task_del_by_callback(server->schedule,
+                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
                                  "Authentication failed");
+    server->stat.auth_failures++;
     return;
   }
 
-  sock->type = ctx->conn_type;
-  switch(sock->type) {
+  entry->data.last_receive = time(NULL);
+
+  switch (ctx->conn_type) {
   case SILC_SOCKET_TYPE_CLIENT:
     {
-      SilcClientList *id_entry = NULL;
-      SilcIDListUnknown *conn_data = sock->user_data;
+      SilcClientEntry client;
 
       SILC_LOG_DEBUG(("Remote host is client"));
+      SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
+                    sock->ip));
+
+      /* Add the client to the client ID cache. The nickname and Client ID
+        and other information is created after we have received NEW_CLIENT
+        packet from client. */
+      client = silc_idlist_add_client(server->local_list, 
+                                     NULL, NULL, NULL, NULL, NULL, sock, 0);
+      if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to cache"));
+       silc_free(sock->user_data);
+       silc_server_disconnect_remote(server, sock, 
+                                     "Server closed connection: "
+                                     "Authentication failed");
+       server->stat.auth_failures++;
+       goto out;
+      }
 
-      /* Add the client to the client ID list. We have not created the
-        client ID for the client yet. This is done when client registers
-        itself by sending NEW_CLIENT packet. */
-      silc_idlist_add_client(&server->local_list->clients, 
-                            NULL, NULL, NULL, NULL, NULL,
-                            conn_data->send_key, conn_data->receive_key, 
-                            conn_data->pkcs, conn_data->hmac, &id_entry);
-
-      id_entry->hmac_key = conn_data->hmac_key;
-      id_entry->hmac_key_len = conn_data->hmac_key_len;
-      id_entry->connection = sock;
-
-      /* Free the temporary connection data context from key exchange */
-      silc_free(conn_data);
+      /* Statistics */
+      server->stat.my_clients++;
+      server->stat.clients++;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_clients++;
 
-      /* Mark the entry to the ID list to the socket connection for
-        fast referencing in the future. */
-      sock->user_data = (void *)id_entry;
+      id_entry = (void *)client;
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
     {
-      SilcServerList *id_entry;
-      SilcIDListUnknown *conn_data = sock->user_data;
-      
+      SilcServerEntry new_server;
+      /* XXX FIXME: Now server and router has different table, so this is probably broken. */
+      SilcServerConfigSectionRouter *conn =
+       ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
+       ctx->sconfig : ctx->rconfig;
+
       SILC_LOG_DEBUG(("Remote host is %s", 
-                     sock->type == SILC_SOCKET_TYPE_SERVER ? 
-                     "server" : "router"));
-      
-      /* Add the server to the ID list. We don't have the server's ID
-        yet but we will receive it after the server sends NEW_SERVER
-        packet to us. */
-      silc_idlist_add_server(&server->local_list->servers, NULL,
-                            sock->type == SILC_SOCKET_TYPE_SERVER ?
-                            SILC_SERVER : SILC_ROUTER, NULL, NULL,
-                            conn_data->send_key, conn_data->receive_key,
-                            conn_data->pkcs, conn_data->hmac, &id_entry);
-
-      id_entry->hmac_key = conn_data->hmac_key;
-      id_entry->hmac_key_len = conn_data->hmac_key_len;
-      id_entry->connection = sock;
-
-      /* Free the temporary connection data context from key exchange */
-      silc_free(conn_data);
-
-      /* Mark the entry to the ID list to the socket connection for
-        fast referencing in the future. */
-      sock->user_data = (void *)id_entry;
-
-      /* There is connection to other server now, if it is router then
-        we will have connection to outside world.  If we are router but
-        normal server connected to us then we will remain standalone,
-        if we are standlone. */
-      if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
+                     ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
+                     "server" : (conn->backup_router ? 
+                                 "backup router" : "router")));
+      SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
+                    sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? 
+                    "server" : (conn->backup_router ? 
+                                "backup router" : "router")));
+
+      /* Add the server into server cache. The server name and Server ID
+        is updated after we have received NEW_SERVER packet from the
+        server. We mark ourselves as router for this server if we really
+        are router. */
+      new_server = 
+       silc_idlist_add_server((ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                               server->local_list : (conn->backup_router ?
+                                                     server->local_list :
+                                                     server->global_list)),
+                              NULL,
+                              (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                               SILC_SERVER : SILC_ROUTER), 
+                              NULL, 
+                              (ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
+                               server->id_entry : (conn->backup_router ? 
+                                                   server->id_entry : NULL)),
+                              sock);
+      if (!new_server) {
+       SILC_LOG_ERROR(("Could not add new server to cache"));
+       silc_free(sock->user_data);
+       silc_server_disconnect_remote(server, sock, 
+                                     "Server closed connection: "
+                                     "Authentication failed");
+       server->stat.auth_failures++;
+       goto out;
+      }
+
+      /* Statistics */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER)
+       server->stat.my_servers++;
+      else
+       server->stat.my_routers++;
+      server->stat.servers++;
+
+      id_entry = (void *)new_server;
+
+      /* If the incoming connection is router and marked as backup router
+        then add it to be one of our backups */
+      if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && conn->backup_router) {
+       silc_server_backup_add(server, new_server, conn->backup_replace_ip,
+                              conn->backup_replace_port, conn->backup_local);
+
+       /* Change it back to SERVER type since that's what it really is. */
+       if (conn->backup_local)
+         ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
+
+       new_server->server_type = SILC_BACKUP_ROUTER;
+      }
+
+      /* Check whether this connection is to be our primary router connection
+        if we do not already have the primary route. */
+      if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
+       if (silc_server_config_is_primary_route(server->config) &&
+           !conn->initiator)
+         break;
+
        SILC_LOG_DEBUG(("We are not standalone server anymore"));
        server->standalone = FALSE;
+       if (!server->id_entry->router) {
+         server->id_entry->router = id_entry;
+         server->router = id_entry;
+       }
       }
+
       break;
     }
   default:
+    goto out;
     break;
   }
 
+  sock->type = ctx->conn_type;
+
+  /* Add the common data structure to the ID entry. */
+  silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
+
+  /* Add to sockets internal pointer for fast referencing */
+  silc_free(sock->user_data);
+  sock->user_data = id_entry;
+
   /* Connection has been fully established now. Everything is ok. */
   SILC_LOG_DEBUG(("New connection authenticated"));
 
+  /* Perform keepalive. The `hb_context' will be freed automatically
+     when finally calling the silc_socket_free function. XXX hardcoded 
+     timeout!! */
+  hb_context = silc_calloc(1, sizeof(*hb_context));
+  hb_context->server = server;
+  silc_socket_set_heartbeat(sock, 400, hb_context,
+                           silc_server_perform_heartbeat,
+                           server->schedule);
+
+ out:
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->packet)
-    silc_buffer_free(ctx->packet);
+    silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
-  if (ctx->dest_id)
-    silc_free(ctx->dest_id);
+  silc_free(ctx->dest_id);
   silc_free(ctx);
   sock->protocol = NULL;
 }
 
-typedef struct {
-  SilcPacketContext *packetdata;
-  SilcServer server;
-  SilcSocketConnection sock;
-  SilcCipher cipher;
-  SilcHmac hmac;
-} SilcServerInternalPacket;
-
 /* This function is used to read packets from network and send packets to
    network. This is usually a generic task. */
 
@@ -994,35 +1478,52 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 {
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = server->sockets[fd];
-  int ret, packetlen, paddedlen;
+  SilcIDListData idata;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
+  uint32 sequence = 0;
+  int ret;
+
+  if (!sock)
+    return;
 
   SILC_LOG_DEBUG(("Processing packet"));
 
   /* Packet sending */
+
   if (type == SILC_TASK_WRITE) {
-    SILC_LOG_DEBUG(("Writing data to connection"));
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
+
+    server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, 
-                      sock->outbuf->data - sock->outbuf->head);
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
-    /* Write the packet out to the connection */
-    ret = silc_packet_write(fd, sock->outbuf);
+    /* Send the packet */
+    ret = silc_packet_send(sock, TRUE);
 
     /* If returned -2 could not write to connection now, will do
        it later. */
     if (ret == -2)
       return;
-    
-    /* Error */
-    if (ret == -1)
-      SILC_LOG_ERROR(("Could not write, packet dropped"));
 
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet to connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
+      return;
+    }
+    
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
        available for this connection it will be set for output as well. 
        This call clears the output setting and sets it only for input. */
-    SILC_SET_CONNECTION_FOR_INPUT(fd);
+    SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
     SILC_UNSET_OUTBUF_PENDING(sock);
 
     silc_buffer_clear(sock->outbuf);
@@ -1030,539 +1531,244 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   }
 
   /* Packet receiving */
-  if (type == SILC_TASK_READ) {
-    SILC_LOG_DEBUG(("Reading data from connection"));
 
-    /* Allocate the incoming data buffer if not done already. */
-    if (!sock->inbuf)
-      sock->inbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
+  /* Read some data from connection */
+  ret = silc_packet_receive(sock);
+  if (ret < 0) {
 
-    /* Read some data from connection */
-    ret = silc_packet_read(fd, sock->inbuf);
-    
-    /* If returned -2 data was not available now, will read it later. */
-    if (ret == -2)
-      return;
-    
-    /* Error */
-    if (ret == -1) {
-      SILC_LOG_ERROR(("Could not read, packet dropped"));
-      return;
-    }
-    
-    /* EOF */
-    if (ret == 0) {
-      SILC_LOG_DEBUG(("Read EOF"));
-      
-      /* If connection is disconnecting already we will finally
-        close the connection */
-      if (SILC_IS_DISCONNECTING(sock)) {
-       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock);
-       silc_server_close_connection(server, sock);
-       return;
-      }
-      
-      SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+    if (ret == -1)
+      SILC_LOG_ERROR(("Error receiving packet from connection "
+                     "%s:%d [%s] %s", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router"), strerror(errno)));
+    return;
+  }    
 
+  /* EOF */
+  if (ret == 0) {
+    SILC_LOG_DEBUG(("Read EOF"));
+      
+    /* If connection is disconnecting already we will finally
+       close the connection */
+    if (SILC_IS_DISCONNECTING(sock)) {
       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock);
+       silc_server_free_sock_user_data(server, sock, NULL);
       silc_server_close_connection(server, sock);
       return;
     }
-
-    /* If connection is disconnecting or disconnected we will ignore
-       what we read. */
-    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
-      SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
-      return;
-    }
-
-    /* Check whether we received a whole packet. If reading went without
-       errors we either read a whole packet or the read packet is 
-       incorrect and will be dropped. */
-    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-    if (sock->inbuf->len < paddedlen || (packetlen < SILC_PACKET_MIN_LEN)) {
-      SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-      silc_buffer_clear(sock->inbuf);
-      silc_server_disconnect_remote(server, sock, "Incorrect packet");
-      return;
-    }
-
-    /* Decrypt a packet coming from client. */
-    if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
-      SilcClientList *clnt = (SilcClientList *)sock->user_data;
-      SilcServerInternalPacket *packet;
-      int mac_len = 0;
       
-      if (clnt->hmac)
-       mac_len = clnt->hmac->hash->hash->hash_len;
-
-      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-       /* Received possibly many packets at once */
-
-       while(sock->inbuf->len > 0) {
-         SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-         if (sock->inbuf->len < paddedlen) {
-           SILC_LOG_DEBUG(("Receive incorrect packet, dropped"));
-           return;
-         }
-
-         paddedlen += 2;
-         packet = silc_calloc(1, sizeof(*packet));
-         packet->server = server;
-         packet->sock = sock;
-         packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-         packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
-         silc_buffer_pull_tail(packet->packetdata->buffer, 
-                               SILC_BUFFER_END(packet->packetdata->buffer));
-         silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
-                         paddedlen + mac_len);
-         if (clnt) {
-           packet->cipher = clnt->receive_key;
-           packet->hmac = clnt->hmac;
-         }
+    SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+    SILC_SET_DISCONNECTING(sock);
 
-         SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
-
-         /* Parse the packet with timeout */
-         silc_task_register(server->timeout_queue, fd, 
-                            silc_server_packet_parse,
-                            (void *)packet, 0, 100000, 
+    if (sock->user_data) {
+      char tmp[128];
+      if (silc_socket_get_error(sock, tmp, sizeof(tmp) - 1))
+       silc_server_free_sock_user_data(server, sock, tmp);
+      else
+       silc_server_free_sock_user_data(server, sock, NULL);
+    } else if (server->router_conn && server->router_conn->sock == sock &&
+            !server->router && server->standalone)
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_connect_to_router, 
+                            server, 1, 0,
                             SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
 
-         /* Pull the packet from inbuf thus we'll get the next one
-            in the inbuf. */
-         silc_buffer_pull(sock->inbuf, paddedlen);
-         if (clnt->hmac)
-           silc_buffer_pull(sock->inbuf, mac_len);
-       }
-       silc_buffer_clear(sock->inbuf);
-       return;
-      } else {
-       SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
-                        sock->inbuf->data, sock->inbuf->len);
-       
-       SILC_LOG_DEBUG(("Packet from client, length %d", paddedlen));
-       
-       packet = silc_calloc(1, sizeof(*packet));
-       packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-       packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-       packet->server = server;
-       packet->sock = sock;
-       if (clnt) {
-         packet->cipher = clnt->receive_key;
-         packet->hmac = clnt->hmac;
-       }
-       silc_buffer_clear(sock->inbuf);
-       
-       /* The packet is ready to be parsed now. However, this is a client 
-          connection so we will parse the packet with timeout. */
-       silc_task_register(server->timeout_queue, fd, 
-                          silc_server_packet_parse,
-                          (void *)packet, 0, 100000, 
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_NORMAL);
-       return;
-      }
-    }
-    
-    /* Decrypt a packet coming from server connection */
-    if (sock->type == SILC_SOCKET_TYPE_SERVER ||
-       sock->type == SILC_SOCKET_TYPE_ROUTER) {
-      SilcServerList *srvr = (SilcServerList *)sock->user_data;
-      SilcServerInternalPacket *packet;
-      int mac_len = 0;
-      
-      if (srvr->hmac)
-       mac_len = srvr->hmac->hash->hash->hash_len;
+    silc_server_close_connection(server, sock);
+    return;
+  }
 
-      if (sock->inbuf->len - 2 > (paddedlen + mac_len)) {
-       /* Received possibly many packets at once */
+  /* If connection is disconnecting or disconnected we will ignore
+     what we read. */
+  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+    SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
+    return;
+  }
 
-       while(sock->inbuf->len > 0) {
-         SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
-         if (sock->inbuf->len < paddedlen) {
-           SILC_LOG_DEBUG(("Received incorrect packet, dropped"));
-           return;
-         }
+  server->stat.packets_received++;
 
-         paddedlen += 2;
-         packet = silc_calloc(1, sizeof(*packet));
-         packet->server = server;
-         packet->sock = sock;
-         packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-         packet->packetdata->buffer = silc_buffer_alloc(paddedlen + mac_len);
-         silc_buffer_pull_tail(packet->packetdata->buffer, 
-                               SILC_BUFFER_END(packet->packetdata->buffer));
-         silc_buffer_put(packet->packetdata->buffer, sock->inbuf->data, 
-                         paddedlen + mac_len);
-         if (srvr) {
-           packet->cipher = srvr->receive_key;
-           packet->hmac = srvr->hmac;
-         }
+  /* Get keys and stuff from ID entry */
+  idata = (SilcIDListData)sock->user_data;
+  if (idata) {
+    cipher = idata->receive_key;
+    hmac = idata->hmac_receive;
+    sequence = idata->psn_receive;
+  }
+  /* Process the packet. This will call the parser that will then
+     decrypt and parse the packet. */
+  ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                                   TRUE : FALSE, cipher, hmac, sequence, 
+                                   silc_server_packet_parse, server);
+
+  /* If this socket connection is not authenticated yet and the packet
+     processing failed we will drop the connection since it can be
+     a malicious flooder. */
+  if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE &&
+      (!sock->protocol || sock->protocol->protocol->type ==
+       SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) {
+    SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock));
+    SILC_SET_DISCONNECTING(sock);
+
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+    silc_server_close_connection(server, sock);
+  }
+}
+  
+/* Parses whole packet, received earlier. */
 
-         SILC_LOG_HEXDUMP(("Incoming packet, len %d", 
-                           packet->packetdata->buffer->len),
-                          packet->packetdata->buffer->data, 
-                          packet->packetdata->buffer->len);
+SILC_TASK_CALLBACK(silc_server_packet_parse_real)
+{
+  SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+  SilcServer server = (SilcServer)parse_ctx->context;
+  SilcSocketConnection sock = parse_ctx->sock;
+  SilcPacketContext *packet = parse_ctx->packet;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  int ret;
 
-         SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", 
-                         srvr->server_type == SILC_SERVER ? 
-                         "server" : "router",
-                         srvr->server_name, paddedlen));
-       
-         /* Parse it real soon as the packet is from server. */
-         silc_task_register(server->timeout_queue, fd, 
-                            silc_server_packet_parse,
-                            (void *)packet, 0, 1, 
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
+  SILC_LOG_DEBUG(("Start"));
 
-         /* Pull the packet from inbuf thus we'll get the next one
-            in the inbuf. */
-         silc_buffer_pull(sock->inbuf, paddedlen);
-         if (srvr->hmac)
-           silc_buffer_pull(sock->inbuf, mac_len);
-       }
-       silc_buffer_clear(sock->inbuf);
-       return;
-      } else {
+  /* Parse the packet */
+  if (parse_ctx->normal)
+    ret = silc_packet_parse(packet, idata ? idata->receive_key : NULL);
+  else
+    ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
 
-       SILC_LOG_HEXDUMP(("An incoming packet, len %d", sock->inbuf->len),
-                        sock->inbuf->data, sock->inbuf->len);
-       
-       SILC_LOG_DEBUG(("Packet from %s %s, packet length %d", 
-                       srvr->server_type == SILC_SERVER ? 
-                       "server" : "router",
-                       srvr->server_name, paddedlen));
-       
-       packet = silc_calloc(1, sizeof(*packet));
-       packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-       packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-       packet->server = server;
-       packet->sock = sock;
-       if (srvr) {
-         packet->cipher = srvr->receive_key;
-         packet->hmac = srvr->hmac;
-       }
-       silc_buffer_clear(sock->inbuf);
-       
-       /* The packet is ready to be parsed now. However, this is a client 
-          connection so we will parse the packet with timeout. */
-       silc_task_register(server->timeout_queue, fd, 
-                          silc_server_packet_parse,
-                          (void *)packet, 0, 1, 
-                          SILC_TASK_TIMEOUT,
-                          SILC_TASK_PRI_NORMAL);
-       return;
+  /* If entry is disabled ignore what we got. */
+  if (ret != SILC_PACKET_RESUME_ROUTER &&
+      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
+    SILC_LOG_DEBUG(("Connection is disabled"));
+    goto out;
+  }
+
+  if (ret == SILC_PACKET_NONE)
+    goto out;
+
+  /* Check that the the current client ID is same as in the client's packet. */
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+    SilcClientEntry client = (SilcClientEntry)sock->user_data;
+    if (client && client->id) {
+      void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                               packet->src_id_type);
+      if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) {
+       silc_free(id);
+       goto out;
       }
+      silc_free(id);
     }
+  }
 
-    /* Decrypt a packet coming from client. */
-    if (sock->type == SILC_SOCKET_TYPE_UNKNOWN) {
-      SilcIDListUnknown *conn_data = (SilcIDListUnknown *)sock->user_data;
-      SilcServerInternalPacket *packet;
+  if (server->server_type == SILC_ROUTER) {
+    /* Route the packet if it is not destined to us. Other ID types but
+       server are handled separately after processing them. */
+    if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+       packet->dst_id_type == SILC_ID_SERVER && 
+       sock->type != SILC_SOCKET_TYPE_CLIENT &&
+       memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
       
-      SILC_LOG_HEXDUMP(("Incoming packet, len %d", sock->inbuf->len),
-                      sock->inbuf->data, sock->inbuf->len);
-
-      SILC_LOG_DEBUG(("Packet from unknown connection, length %d", 
-                     paddedlen));
-
-      packet = silc_calloc(1, sizeof(*packet));
-      packet->packetdata = silc_calloc(1, sizeof(*packet->packetdata));
-      packet->packetdata->buffer = silc_buffer_copy(sock->inbuf);
-      packet->server = server;
-      packet->sock = sock;
-      if (conn_data) {
-       packet->cipher = conn_data->receive_key;
-       packet->hmac = conn_data->hmac;
-      }
+      /* Route the packet to fastest route for the destination ID */
+      void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len, 
+                               packet->dst_id_type);
+      if (!id)
+       goto out;
+      silc_server_packet_route(server,
+                              silc_server_route_get(server, id,
+                                                    packet->dst_id_type),
+                              packet);
+      silc_free(id);
+      goto out;
+    }
+  }
 
-      silc_buffer_clear(sock->inbuf);
+  /* Parse the incoming packet type */
+  silc_server_packet_parse_type(server, sock, packet);
 
-      /* The packet is ready to be parsed now. However, this is unknown 
-        connection so we will parse the packet with timeout. */
-      silc_task_register(server->timeout_queue, fd, 
-                        silc_server_packet_parse,
-                        (void *)packet, 0, 100000, 
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
-      return;
+  if (server->server_type == SILC_ROUTER) {
+    /* Broadcast packet if it is marked as broadcast packet and it is
+       originated from router and we are router. */
+    if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+       packet->flags & SILC_PACKET_FLAG_BROADCAST &&
+       !server->standalone) {
+      /* Broadcast to our primary route */
+      silc_server_packet_broadcast(server, server->router->connection, packet);
+
+      /* If we have backup routers then we need to feed all broadcast
+        data to those servers. */
+      silc_server_backup_broadcast(server, sock, packet);
     }
   }
 
-  SILC_LOG_ERROR(("Weird, nothing happened - ignoring"));
+ out:
+  silc_packet_context_free(packet);
+  silc_free(parse_ctx);
 }
 
-/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
-   after packet has been totally decrypted and parsed. */
+/* Parser callback called by silc_packet_receive_process. This merely
+   registers timeout that will handle the actual parsing when appropriate. */
 
-static int silc_server_packet_check_mac(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       SilcBuffer buffer)
-{
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned int mac_len = 0;
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-      hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-      hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
-      hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
-    }
-  }
-
-  /* Check MAC */
-  if (hmac) {
-    int headlen = buffer->data - buffer->head;
-    unsigned char *packet_mac, mac[32];
-    
-    SILC_LOG_DEBUG(("Verifying MAC"));
-
-    mac_len = hmac->hash->hash->hash_len;
-
-    silc_buffer_push(buffer, headlen);
-    
-    /* Take mac from packet */
-    packet_mac = buffer->tail;
-    
-    /* Make MAC and compare */
-    memset(mac, 0, sizeof(mac));
-    silc_hmac_make_with_key(hmac, 
-                           buffer->data, buffer->len,
-                           hmac_key, hmac_key_len, mac);
-#if 0
-    SILC_LOG_HEXDUMP(("PMAC"), packet_mac, mac_len);
-    SILC_LOG_HEXDUMP(("CMAC"), mac, mac_len);
-#endif
-    if (memcmp(mac, packet_mac, mac_len)) {
-      SILC_LOG_DEBUG(("MAC failed"));
-      return FALSE;
-    }
-    
-    SILC_LOG_DEBUG(("MAC is Ok"));
-    memset(mac, 0, sizeof(mac));
-
-    silc_buffer_pull(buffer, headlen);
-  }
-  
-  return TRUE;
-}
-
-/* Decrypts rest of the packet (after decrypting just the SILC header).
-   After calling this function the packet is ready to be parsed by calling 
-   silc_packet_parse. */
-
-static int silc_server_packet_decrypt_rest(SilcServer server, 
-                                          SilcSocketConnection sock,
-                                          SilcBuffer buffer)
+bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
+                             void *context)
 {
-  SilcCipher session_key = NULL;
-  SilcHmac hmac = NULL;
-  unsigned int mac_len = 0;
-
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      session_key = ((SilcClientList *)sock->user_data)->receive_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      session_key = ((SilcServerList *)sock->user_data)->receive_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-    }
-  }
-  
-  /* Decrypt */
-  if (session_key) {
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      mac_len = hmac->hash->hash->hash_len;
-      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, mac_len);
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-
-    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
-
-    /* Decrypt rest of the packet */
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    silc_packet_decrypt(session_key, buffer, buffer->len);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
-                    buffer->data, buffer->len);
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection sock = parser_context->sock;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+  if (idata)
+    idata->psn_receive = parser_context->packet->sequence + 1;
+
+  /* If protocol for this connection is key exchange or rekey then we'll
+     process all packets synchronously, since there might be packets in
+     queue that we are not able to decrypt without first processing the
+     packets before them. */
+  if ((parser_context->packet->type == SILC_PACKET_REKEY ||
+       parser_context->packet->type == SILC_PACKET_REKEY_DONE) ||
+      (sock->protocol && sock->protocol->protocol && 
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
+    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+                                 parser_context);
+
+    /* Reprocess data since we'll return FALSE here.  This is because
+       the idata->receive_key might have become valid in the last packet
+       and we want to call this processor with valid cipher. */
+    if (idata)
+      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                                 TRUE : FALSE, idata->receive_key, 
+                                 idata->hmac_receive, idata->psn_receive, 
+                                 silc_server_packet_parse, server);
+    else
+      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? 
+                                 TRUE : FALSE, NULL, NULL, 0, 
+                                 silc_server_packet_parse, server);
+    return FALSE;
   }
 
-  return TRUE;
-}
-
-/* Decrypts rest of the SILC Packet header that has been decrypted partly
-   already. This decrypts the padding of the packet also.  After calling 
-   this function the packet is ready to be parsed by calling function 
-   silc_packet_parse. */
-
-static int silc_server_packet_decrypt_rest_special(SilcServer server, 
-                                                  SilcSocketConnection sock,
-                                                  SilcBuffer buffer)
-{
-  SilcCipher session_key = NULL;
-  SilcHmac hmac = NULL;
-  unsigned int mac_len = 0;
-
-  switch(sock->type) {
+  switch (sock->type) {
+  case SILC_SOCKET_TYPE_UNKNOWN:
   case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      session_key = ((SilcClientList *)sock->user_data)->receive_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-    }
+    /* Parse the packet with timeout */
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_packet_parse_real,
+                          (void *)parser_context, 0, 100000,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_NORMAL);
     break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      session_key = ((SilcServerList *)sock->user_data)->receive_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-    }
+    /* Packets from servers are parsed immediately */
+    silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+                                 parser_context);
     break;
   default:
-    if (sock->user_data) {
-      session_key = ((SilcIDListUnknown *)sock->user_data)->receive_key;
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-    }
-  }
-  
-  /* Decrypt rest of the header plus padding */
-  if (session_key) {
-    unsigned short truelen, len1, len2, padlen;
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      mac_len = hmac->hash->hash->hash_len;
-      if ((buffer->len - mac_len) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, mac_len);
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-  
-    SILC_LOG_DEBUG(("Decrypting rest of the header"));
-
-    SILC_GET16_MSB(len1, &buffer->data[4]);
-    SILC_GET16_MSB(len2, &buffer->data[6]);
-
-    truelen = SILC_PACKET_HEADER_LEN + len1 + len2;
-    padlen = SILC_PACKET_PADLEN(truelen);
-    len1 = (truelen + padlen) - (SILC_PACKET_MIN_HEADER_LEN - 2);
-
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
-    silc_packet_decrypt(session_key, buffer, len1);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN - 2);
+    return TRUE;
   }
 
   return TRUE;
 }
 
-/* Parses whole packet, received earlier. This packet is usually received
-   from client. */
-
-SILC_TASK_CALLBACK(silc_server_packet_parse)
-{
-  SilcServerInternalPacket *packet = (SilcServerInternalPacket *)context;
-  SilcServer server = packet->server;
-  SilcSocketConnection sock = packet->sock;
-  SilcBuffer buffer = packet->packetdata->buffer;
-  int ret;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Decrypt start of the packet header */
-  if (packet->cipher)
-    silc_packet_decrypt(packet->cipher, buffer, SILC_PACKET_MIN_HEADER_LEN);
-
-  /* If the packet type is not any special type lets decrypt rest
-     of the packet here. */
-  if (buffer->data[3] != SILC_PACKET_CHANNEL_MESSAGE &&
-      buffer->data[3] != SILC_PACKET_PRIVATE_MESSAGE) {
-  normal:
-    /* Normal packet, decrypt rest of the packet */
-    if (!silc_server_packet_decrypt_rest(server, sock, buffer))
-      goto out;
-
-    /* Parse the packet. Packet type is returned. */
-    ret = silc_packet_parse(packet->packetdata);
-    if (ret == SILC_PACKET_NONE)
-      goto out;
-
-    /* Check MAC */
-    if (!silc_server_packet_check_mac(server, sock, buffer))
-      goto out;
-  } else {
-    /* If private message key is not set for private message it is
-       handled as normal packet. Go back up. */
-    if (buffer->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-       !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
-      goto normal;
-
-    /* Packet requires special handling, decrypt rest of the header.
-       This only decrypts. This does not do any MAC checking, it must
-       be done individually later when doing the special processing. */
-    silc_server_packet_decrypt_rest_special(server, sock, buffer);
-
-    /* Parse the packet header in special way as this is "special"
-       packet type. */
-    ret = silc_packet_parse_special(packet->packetdata);
-    if (ret == SILC_PACKET_NONE)
-      goto out;
-  }
-  
-  /* Parse the incoming packet type */
-  silc_server_packet_parse_type(server, sock, packet->packetdata);
-
- out:
-  silc_buffer_clear(sock->inbuf);
-  //  silc_buffer_free(packetdata->packetdata->buffer);
-  silc_free(packet->packetdata);
-  silc_free(packet);
-}
-
 /* Parses the packet type and calls what ever routines the packet type
    requires. This is done for all incoming packets. */
 
@@ -1570,16 +1776,19 @@ void silc_server_packet_parse_type(SilcServer server,
                                   SilcSocketConnection sock,
                                   SilcPacketContext *packet)
 {
-  SilcBuffer buffer = packet->buffer;
   SilcPacketType type = packet->type;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
 
   SILC_LOG_DEBUG(("Parsing packet type %d", type));
 
   /* Parse the packet type */
-  switch(type) {
+  switch (type) {
   case SILC_PACKET_DISCONNECT:
     SILC_LOG_DEBUG(("Disconnect packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     break;
+
   case SILC_PACKET_SUCCESS:
     /*
      * Success received for something. For now we can have only
@@ -1587,29 +1796,66 @@ void silc_server_packet_parse_type(SilcServer server,
      * success message is for whatever protocol is executing currently.
      */
     SILC_LOG_DEBUG(("Success packet"));
-    if (sock->protocol) {
-      sock->protocol->execute(server->timeout_queue, 0,
-                             sock->protocol, sock->sock, 0, 0);
-    }
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    if (sock->protocol)
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     break;
+
   case SILC_PACKET_FAILURE:
+    /*
+     * Failure received for something. For now we can have only
+     * one protocol for connection executing at once hence this
+     * failure message is for whatever protocol is executing currently.
+     */
     SILC_LOG_DEBUG(("Failure packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    if (sock->protocol) {
+      SilcServerFailureContext f;
+      f = silc_calloc(1, sizeof(*f));
+      f->server = server;
+      f->sock = sock;
+      
+      /* We will wait 5 seconds to process this failure packet */
+      silc_schedule_task_add(server->schedule, sock->sock,
+                        silc_server_failure_callback, (void *)f, 5, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    }
     break;
+
   case SILC_PACKET_REJECT:
     SILC_LOG_DEBUG(("Reject packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     return;
     break;
 
+  case SILC_PACKET_NOTIFY:
+    /*
+     * Received notify packet. Server can receive notify packets from
+     * router. Server then relays the notify messages to clients if needed.
+     */
+    SILC_LOG_DEBUG(("Notify packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_notify_list(server, sock, packet);
+    else
+      silc_server_notify(server, sock, packet);
+    break;
+
     /* 
      * Channel packets
      */
   case SILC_PACKET_CHANNEL_MESSAGE:
     /*
      * Received channel message. Channel messages are special packets
-     * (although probably most common ones) hence they are handled
+     * (although probably most common ones) thus they are handled
      * specially.
      */
     SILC_LOG_DEBUG(("Channel Message packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    idata->last_receive = time(NULL);
     silc_server_channel_message(server, sock, packet);
     break;
 
@@ -1621,6 +1867,8 @@ void silc_server_packet_parse_type(SilcServer server,
      * never receives this channel and thus is ignored.
      */
     SILC_LOG_DEBUG(("Channel Key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_channel_key(server, sock, packet);
     break;
 
@@ -1628,48 +1876,26 @@ void silc_server_packet_parse_type(SilcServer server,
      * Command packets
      */
   case SILC_PACKET_COMMAND:
-    {
-      /*
-       * Recived command. Allocate command context and execute the command.
-       */
-      SilcServerCommandContext ctx;
-
-      SILC_LOG_DEBUG(("Command packet"));
-
-      /* Router cannot send command packet */
-      if (sock->type == SILC_SOCKET_TYPE_ROUTER)
-       break;
-
-      /* Allocate command context. This must be free'd by the
-        command routine receiving it. */
-      ctx = silc_calloc(1, sizeof(*ctx));
-      ctx->server = server;
-      ctx->sock = sock;
-      ctx->packet = packet;    /* Save original packet */
-
-      /* Parse the command payload in the packet */
-      ctx->payload = silc_command_parse_payload(buffer);
-      if (!ctx->payload) {
-       SILC_LOG_ERROR(("Bad command payload, packet dropped"));
-       silc_free(ctx);
-       return;
-      }
-
-      /* Execute command. If this fails the packet is dropped. */
-      SILC_SERVER_COMMAND_EXEC(ctx);
-      silc_buffer_free(buffer);
-    }
+    /*
+     * Recived command. Processes the command request and allocates the
+     * command context and calls the command.
+     */
+    SILC_LOG_DEBUG(("Command packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_command_process(server, sock, packet);
     break;
 
   case SILC_PACKET_COMMAND_REPLY:
     /*
-     * Received command reply packet. Servers never send commands thus
-     * they don't receive command reply packets either, except in cases
-     * where server has forwarded command packet coming from client. 
-     * This must be the case here or we will ignore the packet.
+     * Received command reply packet. Received command reply to command. It
+     * may be reply to command sent by us or reply to command sent by client
+     * that we've routed further.
      */
     SILC_LOG_DEBUG(("Command Reply packet"));
-    silc_server_packet_relay_command_reply(server, sock, packet);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_command_reply(server, sock, packet);
     break;
 
     /*
@@ -1681,10 +1907,19 @@ void silc_server_packet_parse_type(SilcServer server,
      * client or server.
      */
     SILC_LOG_DEBUG(("Private Message packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    idata->last_receive = time(NULL);
     silc_server_private_message(server, sock, packet);
     break;
 
   case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+    /*
+     * Private message key packet.
+     */
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_private_message_key(server, sock, packet);
     break;
 
     /*
@@ -1692,44 +1927,63 @@ void silc_server_packet_parse_type(SilcServer server,
      */
   case SILC_PACKET_KEY_EXCHANGE:
     SILC_LOG_DEBUG(("KE packet"));
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 100000);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
-
-      /* XXX Trigger KE protocol?? Rekey actually, maybe. */
     }
     break;
 
   case SILC_PACKET_KEY_EXCHANGE_1:
     SILC_LOG_DEBUG(("KE 1 packet"));
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       SilcServerRekeyInternalContext *proto_ctx = 
+         (SilcServerRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
 
-      proto_ctx->packet = buffer;
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      } else {
+       SilcServerKEInternalContext *proto_ctx = 
+         (SilcServerKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+       proto_ctx->dest_id_type = packet->src_id_type;
+       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 
                              0, 100000);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1738,23 +1992,42 @@ void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_KEY_EXCHANGE_2:
     SILC_LOG_DEBUG(("KE 2 packet"));
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
 
-      SilcServerKEInternalContext *proto_ctx = 
-       (SilcServerKEInternalContext *)sock->protocol->context;
+    if (sock->protocol && sock->protocol->protocol &&
+       (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
 
-      if (proto_ctx->packet)
-       silc_buffer_free(proto_ctx->packet);
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+       SilcServerRekeyInternalContext *proto_ctx = 
+         (SilcServerRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
 
-      proto_ctx->packet = buffer;
-      proto_ctx->dest_id_type = packet->src_id_type;
-      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      } else {
+       SilcServerKEInternalContext *proto_ctx = 
+         (SilcServerKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+       proto_ctx->dest_id_type = packet->src_id_type;
+       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
 
-      /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock,
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, server->schedule, 
                              0, 100000);
+      }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
                      "protocol active, packet dropped."));
@@ -1762,9 +2035,17 @@ void silc_server_packet_parse_type(SilcServer server,
     break;
 
   case SILC_PACKET_CONNECTION_AUTH_REQUEST:
-    /* If we receive this packet we will send to the other end information
-       about our mandatory authentication method for the connection. 
-       This packet maybe received at any time. */
+    /*
+     * Connection authentication request packet. When we receive this packet
+     * we will send to the other end information about our mandatory
+     * authentication method for the connection. This packet maybe received
+     * at any time. 
+     */
+    SILC_LOG_DEBUG(("Connection authentication request packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_connection_auth_request(server, sock, packet);
+    break;
 
     /*
      * Connection Authentication protocol packets
@@ -1773,17 +2054,19 @@ void silc_server_packet_parse_type(SilcServer server,
     /* Start of the authentication protocol. We receive here the 
        authentication data and will verify it. */
     SILC_LOG_DEBUG(("Connection auth packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
     if (sock->protocol && sock->protocol->protocol->type 
        == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
 
       SilcServerConnAuthInternalContext *proto_ctx = 
        (SilcServerConnAuthInternalContext *)sock->protocol->context;
 
-      proto_ctx->packet = buffer;
+      proto_ctx->packet = silc_packet_context_dup(packet);
 
       /* Let the protocol handle the packet */
-      sock->protocol->execute(server->timeout_queue, 0, 
-                             sock->protocol, sock->sock, 0, 0);
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
                      "protocol active, packet dropped."));
@@ -1798,7 +2081,10 @@ void silc_server_packet_parse_type(SilcServer server,
      * SILC network.
      */
     SILC_LOG_DEBUG(("New ID packet"));
-    silc_server_new_id(server, sock, packet);
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_new_id_list(server, sock, packet);
+    else
+      silc_server_new_id(server, sock, packet);
     break;
 
   case SILC_PACKET_NEW_CLIENT:
@@ -1808,1919 +2094,1969 @@ void silc_server_packet_parse_type(SilcServer server,
      * ID we will send it to the client.
      */
     SILC_LOG_DEBUG(("New Client packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_new_client(server, sock, packet);
     break;
 
   case SILC_PACKET_NEW_SERVER:
     /*
      * Received new server packet. This includes Server ID and some other
-     * information that we may save. This is after server as connected to us.
+     * information that we may save. This is received after server has 
+     * connected to us.
      */
     SILC_LOG_DEBUG(("New Server packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
     silc_server_new_server(server, sock, packet);
     break;
 
-  default:
-    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+  case SILC_PACKET_NEW_CHANNEL:
+    /*
+     * Received new channel packet. Information about new channel in the
+     * network are distributed using this packet.
+     */
+    SILC_LOG_DEBUG(("New Channel packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      silc_server_new_channel_list(server, sock, packet);
+    else
+      silc_server_new_channel(server, sock, packet);
     break;
-  }
-  
-}
 
-/* Internal routine that sends packet or marks packet to be sent. This
-   is used directly only in special cases. Normal cases should use
-   silc_server_packet_send. Returns < 0 error. */
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat.
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    break;
 
-static int silc_server_packet_send_real(SilcServer server,
-                                       SilcSocketConnection sock,
-                                       int force_send)
-{
-  /* Send now if forced to do so */
-  if (force_send == TRUE) {
-    int ret;
-    SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
-    ret = silc_packet_write(sock->sock, sock->outbuf);
+  case SILC_PACKET_KEY_AGREEMENT:
+    /*
+     * Received heartbeat.
+     */
+    SILC_LOG_DEBUG(("Key agreement packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_key_agreement(server, sock, packet);
+    break;
 
-    silc_buffer_clear(sock->outbuf);
+  case SILC_PACKET_REKEY:
+    /*
+     * Received re-key packet. The sender wants to regenerate the session
+     * keys.
+     */
+    SILC_LOG_DEBUG(("Re-key packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_rekey(server, sock, packet);
+    break;
 
-    if (ret == -1)
-      SILC_LOG_ERROR(("Could not write, packet dropped"));
-    if (ret != -2) {
-      silc_buffer_clear(sock->outbuf);
-      return ret;
+  case SILC_PACKET_REKEY_DONE:
+    /*
+     * The re-key is done.
+     */
+    SILC_LOG_DEBUG(("Re-key done packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+
+      SilcServerRekeyInternalContext *proto_ctx = 
+       (SilcServerRekeyInternalContext *)sock->protocol->context;
+
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
+
+      proto_ctx->packet = silc_packet_context_dup(packet);
+
+      /* Let the protocol handle the packet */
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+    } else {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
     }
+    break;
+
+  case SILC_PACKET_FTP:
+    /* FTP packet */
+    SILC_LOG_DEBUG(("FTP packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_ftp(server, sock, packet);
+    break;
 
-    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
-  }  
+  case SILC_PACKET_RESUME_ROUTER:
+    /* Resume router packet received. This packet is received for backup
+       router resuming protocol. */
+    SILC_LOG_DEBUG(("Resume router packet"));
+    if (packet->flags & SILC_PACKET_FLAG_LIST)
+      break;
+    silc_server_backup_resume_router(server, sock, packet);
+    break;
 
-  SILC_LOG_DEBUG(("Packet in queue"));
+  default:
+    SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+    break;
+  }
+  
+}
 
-  /* Mark that there is some outgoing data available for this connection. 
-     This call sets the connection both for input and output (the input
-     is set always and this call keeps the input setting, actually). 
-     Actual data sending is performed by silc_server_packet_process. */
-  SILC_SET_CONNECTION_FOR_OUTPUT(sock->sock);
+/* Creates connection to a remote router. */
 
-  /* Mark to socket that data is pending in outgoing buffer. This flag
-     is needed if new data is added to the buffer before the earlier
-     put data is sent to the network. */
-  SILC_SET_OUTBUF_PENDING(sock);
+void silc_server_create_connection(SilcServer server,
+                                  const char *remote_host, uint32 port)
+{
+  SilcServerConnection sconn;
+
+  /* Allocate connection object for hold connection specific stuff. */
+  sconn = silc_calloc(1, sizeof(*sconn));
+  sconn->server = server;
+  sconn->remote_host = strdup(remote_host);
+  sconn->remote_port = port;
+  sconn->no_reconnect = TRUE;
+
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_connect_router,
+                        (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
+}
 
-  return 0;
+SILC_TASK_CALLBACK(silc_server_close_connection_final)
+{
+  silc_socket_free((SilcSocketConnection)context);
 }
 
-/* Prepare outgoing data buffer for packet sending. This is internal
-   routine and must always be called before sending any packets out. */
+/* Closes connection to socket connection */
 
-static void silc_server_packet_send_prepare(SilcServer server, 
-                                           SilcSocketConnection sock,
-                                           unsigned int header_len,
-                                           unsigned int padlen,
-                                           unsigned int data_len)
+void silc_server_close_connection(SilcServer server,
+                                 SilcSocketConnection sock)
 {
-  int totlen, oldlen;
+  if (!server->sockets[sock->sock])
+    return;
 
-  totlen = header_len + padlen + data_len;
+  SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
+                  sock->port,
+                  (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                   sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                   sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                   "Router")));
 
-  /* Prepare the outgoing buffer for packet sending. */
-  if (!sock->outbuf) {
-    /* Allocate new buffer. This is done only once per connection. */
-    SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
-    
-    sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
-    silc_buffer_pull_tail(sock->outbuf, totlen);
-    silc_buffer_pull(sock->outbuf, header_len + padlen);
-  } else {
-    if (SILC_IS_OUTBUF_PENDING(sock)) {
-      /* There is some pending data in the buffer. */
+  /* We won't listen for this connection anymore */
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
 
-      if ((sock->outbuf->end - sock->outbuf->tail) < data_len) {
-       SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
-       /* XXX: not done yet */
-      }
-      oldlen = sock->outbuf->len;
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
-    } else {
-      /* Buffer is free for use */
-      silc_buffer_clear(sock->outbuf);
-      silc_buffer_pull_tail(sock->outbuf, totlen);
-      silc_buffer_pull(sock->outbuf, header_len + padlen);
-    }
-  }
-}
+  /* Unregister all tasks */
+  silc_schedule_task_del_by_fd(server->schedule, sock->sock);
 
-/* Assembles a new packet to be sent out to network. This doesn't actually
-   send the packet but creates the packet and fills the outgoing data
-   buffer and marks the packet ready to be sent to network. However, If 
-   argument force_send is TRUE the packet is sent immediately and not put 
-   to queue. Normal case is that the packet is not sent immediately. */
-
-void silc_server_packet_send(SilcServer server,
-                            SilcSocketConnection sock, 
-                            SilcPacketType type, 
-                            SilcPacketFlags flags,
-                            unsigned char *data, 
-                            unsigned int data_len,
-                            int force_send)
-{
-  void *dst_id = NULL;
-  SilcIdType dst_id_type = SILC_ID_NONE;
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+  server->sockets[sock->sock] = NULL;
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (((SilcClientList *)sock->user_data)->id) {
-      dst_id = ((SilcClientList *)sock->user_data)->id;
-      dst_id_type = SILC_ID_CLIENT;
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (((SilcServerList *)sock->user_data)->id) {
-      dst_id = ((SilcServerList *)sock->user_data)->id;
-      dst_id_type = SILC_ID_SERVER;
+  /* If sock->user_data is NULL then we'll check for active protocols
+     here since the silc_server_free_sock_user_data has not been called
+     for this connection. */
+  if (!sock->user_data) {
+    /* If any protocol is active cancel its execution. It will call
+       the final callback which will finalize the disconnection. */
+    if (sock->protocol) {
+      silc_protocol_cancel(sock->protocol, server->schedule);
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, server->schedule);
+      sock->protocol = NULL;
+      return;
     }
-    break;
-  default:
-    break;
   }
 
-  silc_server_packet_send_dest(server, sock, type, flags, dst_id,
-                              dst_id_type, data, data_len, force_send);
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_close_connection_final,
+                        (void *)sock, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
 }
 
-/* Assembles a new packet to be sent out to network. This doesn't actually
-   send the packet but creates the packet and fills the outgoing data
-   buffer and marks the packet ready to be sent to network. However, If 
-   argument force_send is TRUE the packet is sent immediately and not put 
-   to queue. Normal case is that the packet is not sent immediately. 
-   Destination information is sent as argument for this function. */
-
-void silc_server_packet_send_dest(SilcServer server,
-                                 SilcSocketConnection sock, 
-                                 SilcPacketType type, 
-                                 SilcPacketFlags flags,
-                                 void *dst_id,
-                                 SilcIdType dst_id_type,
-                                 unsigned char *data, 
-                                 unsigned int data_len,
-                                 int force_send)
-{
-  SilcPacketContext packetdata;
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  unsigned char *dst_id_data = NULL;
-  unsigned int dst_id_len = 0;
-
-  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+/* Sends disconnect message to remote connection and disconnects the 
+   connection. */
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      cipher = ((SilcClientList *)sock->user_data)->send_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
-      }
-    }
-    break;
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      cipher = ((SilcServerList *)sock->user_data)->send_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
-      }
-    }
-    break;
-  default:
-    if (sock->user_data) {
-      /* We don't know what type of connection this is thus it must
-        be in authentication phase. */
-      cipher = ((SilcIDListUnknown *)sock->user_data)->send_key;
-      hmac = ((SilcIDListUnknown *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcIDListUnknown *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcIDListUnknown *)sock->user_data)->hmac_key_len;
-      }
-    }
-    break;
-  }
+void silc_server_disconnect_remote(SilcServer server,
+                                  SilcSocketConnection sock,
+                                  const char *fmt, ...)
+{
+  va_list ap;
+  unsigned char buf[4096];
 
-  if (dst_id) {
-    dst_id_data = silc_id_id2str(dst_id, dst_id_type);
-    dst_id_len = silc_id_get_len(dst_id_type);
-  }
+  if (!sock)
+    return;
 
-  /* Set the packet context pointers */
-  packetdata.type = type;
-  packetdata.flags = flags;
-  packetdata.src_id = silc_id_id2str(server->id, server->id_type);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = server->id_type;
-  packetdata.dst_id = dst_id_data;
-  packetdata.dst_id_len = dst_id_len;
-  packetdata.dst_id_type = dst_id_type;
-  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-    packetdata.src_id_len + dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
-  packetdata.rng = server->rng;
+  memset(buf, 0, sizeof(buf));
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  va_end(ap);
 
-  /* Prepare outgoing data buffer for packet sending */
-  silc_server_packet_send_prepare(server, sock, 
-                                 SILC_PACKET_HEADER_LEN +
-                                 packetdata.src_id_len + 
-                                 packetdata.dst_id_len,
-                                 packetdata.padlen,
-                                 data_len);
+  SILC_LOG_DEBUG(("Disconnecting remote host"));
 
-  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+  /* Notify remote end that the conversation is over. The notify message
+     is tried to be sent immediately. */
+  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
+                         buf, strlen(buf), TRUE);
 
-  packetdata.buffer = sock->outbuf;
+  /* Mark the connection to be disconnected */
+  SILC_SET_DISCONNECTED(sock);
+  silc_server_close_connection(server, sock);
+}
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+typedef struct {
+  SilcServer server;
+  SilcClientEntry client;
+} *FreeClientInternal;
 
-  /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
+SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
+{
+  FreeClientInternal i = (FreeClientInternal)context;
 
-  /* Compute MAC of the packet */
-  if (hmac) {
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-  }
+  silc_idlist_del_data(i->client);
+  silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
+  silc_free(i);
+}
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+/* Frees client data and notifies about client's signoff. */
 
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
+void silc_server_free_client_data(SilcServer server, 
+                                 SilcSocketConnection sock,
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 const char *signoff)
+{
+  FreeClientInternal i = silc_calloc(1, sizeof(*i));
 
-  SILC_LOG_HEXDUMP(("Outgoing packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  /* If there is pending outgoing data for the client then purge it
+     to the network before removing the client entry. */
+  silc_server_packet_queue_purge(server, sock);
 
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
+  if (!client->id)
+    return;
 
-  if (packetdata.src_id)
-    silc_free(packetdata.src_id);
-  if (packetdata.dst_id)
-    silc_free(packetdata.dst_id);
+  /* Send SIGNOFF notify to routers. */
+  if (notify && !server->standalone && server->router)
+    silc_server_send_notify_signoff(server, server->router->connection,
+                                   server->server_type == SILC_SERVER ?
+                                   FALSE : TRUE, client->id, signoff);
+    
+  /* Remove client from all channels */
+  if (notify)
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    TRUE, (char *)signoff, TRUE);
+  else
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    FALSE, NULL, FALSE);
+    
+  /* Update statistics */
+  server->stat.my_clients--;
+  server->stat.clients--;
+  if (server->server_type == SILC_ROUTER)
+    server->stat.cell_clients--;
+  SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+  SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+  /* We will not delete the client entry right away. We will take it
+     into history (for WHOWAS command) for 5 minutes */
+  i->server = server;
+  i->client = client;
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_free_client_data_timeout,
+                        (void *)i, 300, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+  client->router = NULL;
+  client->connection = NULL;
+  client->mode = 0;
 }
 
-/* Forwards packet. Packets sent with this function will be marked as
-   forwarded (in the SILC header flags) so that the receiver knows that
-   we have forwarded the packet to it. Forwarded packets are handled
-   specially by the receiver as they are not destined to the receiver
-   originally. However, the receiver knows this because the forwarded
-   flag has been set (and the flag is authenticated). */
-
-void silc_server_packet_forward(SilcServer server,
-                               SilcSocketConnection sock,
-                               unsigned char *data, unsigned int data_len,
-                               int force_send)
-{
-  SilcCipher cipher = NULL;
-  SilcHmac hmac = NULL;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
+/* Frees user_data pointer from socket connection object. This also sends
+   appropriate notify packets to the network to inform about leaving
+   entities. */
 
-  SILC_LOG_DEBUG(("Forwarding packet"));
+void silc_server_free_sock_user_data(SilcServer server, 
+                                    SilcSocketConnection sock,
+                                    const char *signoff_message)
+{
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Get data used in the packet sending, keys and stuff */
-  switch(sock->type) {
+  switch (sock->type) {
   case SILC_SOCKET_TYPE_CLIENT:
-    if (sock->user_data) {
-      cipher = ((SilcClientList *)sock->user_data)->send_key;
-      hmac = ((SilcClientList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcClientList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcClientList *)sock->user_data)->hmac_key_len;
-      }
+    {
+      SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
+      silc_server_free_client_data(server, sock, user_data, TRUE, 
+                                  signoff_message);
+      break;
     }
-    break;
   case SILC_SOCKET_TYPE_SERVER:
   case SILC_SOCKET_TYPE_ROUTER:
-    if (sock->user_data) {
-      cipher = ((SilcServerList *)sock->user_data)->send_key;
-      hmac = ((SilcServerList *)sock->user_data)->hmac;
-      if (hmac) {
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = ((SilcServerList *)sock->user_data)->hmac_key;
-       hmac_key_len = ((SilcServerList *)sock->user_data)->hmac_key_len;
+    {
+      SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+      SilcServerEntry backup_router = NULL;
+
+      if (user_data->id)
+       backup_router = silc_server_backup_get(server, user_data->id);
+
+      /* If this was our primary router connection then we're lost to
+        the outside world. */
+      if (server->router == user_data) {
+       /* Check whether we have a backup router connection */
+       if (!backup_router || backup_router == user_data) {
+         silc_schedule_task_add(server->schedule, 0, 
+                                silc_server_connect_to_router, 
+                                server, 1, 0,
+                                SILC_TASK_TIMEOUT,
+                                SILC_TASK_PRI_NORMAL);
+
+         server->id_entry->router = NULL;
+         server->router = NULL;
+         server->standalone = TRUE;
+         backup_router = NULL;
+       } else {
+         SILC_LOG_INFO(("New primary router is backup router %s",
+                        backup_router->server_name));
+         SILC_LOG_DEBUG(("New primary router is backup router %s",
+                         backup_router->server_name));
+         server->id_entry->router = backup_router;
+         server->router = backup_router;
+         server->router_connect = time(0);
+         server->backup_primary = TRUE;
+         if (server->server_type == SILC_BACKUP_ROUTER) {
+           server->server_type = SILC_ROUTER;
+
+           /* We'll need to constantly try to reconnect to the primary
+              router so that we'll see when it comes back online. */
+           silc_server_backup_reconnect(server, sock->ip, sock->port,
+                                        silc_server_backup_connected,
+                                        NULL);
+         }
+
+         /* Mark this connection as replaced */
+         silc_server_backup_replaced_add(server, user_data->id, 
+                                         backup_router);
+       }
+      } else if (backup_router) {
+       SILC_LOG_INFO(("Enabling the use of backup router %s",
+                      backup_router->server_name));
+       SILC_LOG_DEBUG(("Enabling the use of backup router %s",
+                       backup_router->server_name));
+
+       /* Mark this connection as replaced */
+       silc_server_backup_replaced_add(server, user_data->id, 
+                                       backup_router);
+      }
+
+      if (!backup_router) {
+       /* Free all client entries that this server owns as they will
+          become invalid now as well. */
+       if (user_data->id)
+         silc_server_remove_clients_by_server(server, user_data, TRUE);
+       if (server->server_type == SILC_SERVER)
+         silc_server_remove_channels_by_server(server, user_data);
+      } else {
+       /* Update the client entries of this server to the new backup
+          router. This also removes the clients that *really* was owned
+          by the primary router and went down with the router.  */
+       silc_server_update_clients_by_server(server, user_data, backup_router,
+                                            TRUE, TRUE);
+       silc_server_update_servers_by_server(server, user_data, backup_router);
+       if (server->server_type == SILC_SERVER)
+         silc_server_update_channels_by_server(server, user_data, 
+                                               backup_router);
+      }
+
+      /* Free the server entry */
+      silc_server_backup_del(server, user_data);
+      silc_server_backup_replaced_del(server, user_data);
+      silc_idlist_del_data(user_data);
+      if (!silc_idlist_del_server(server->local_list, user_data))
+       silc_idlist_del_server(server->global_list, user_data);
+      server->stat.my_servers--;
+      server->stat.servers--;
+      if (server->server_type == SILC_ROUTER)
+       server->stat.cell_servers--;
+
+      if (backup_router) {
+       /* Announce all of our stuff that was created about 5 minutes ago.
+          The backup router knows all the other stuff already. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, time(0) - 300,
+                                      backup_router->connection);
+
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, time(0) - 300,
+                                    backup_router->connection);
+       silc_server_announce_channels(server, time(0) - 300,
+                                     backup_router->connection);
       }
+      break;
     }
-    break;
   default:
-    /* We won't forward to unknown destination - keys must exist with
-       the destination before forwarding. */
-    return;
+    {
+      SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
+
+      silc_idlist_del_data(user_data);
+      silc_free(user_data);
+      break;
+    }
   }
 
-  /* Prepare outgoing data buffer for packet sending */
-  silc_server_packet_send_prepare(server, sock, 0, 0, data_len);
+  /* If any protocol is active cancel its execution */
+  if (sock->protocol) {
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    sock->protocol = NULL;
+  }
 
-  /* Mungle the packet flags and add the FORWARDED flag */
-  if (data)
-    data[2] |= (unsigned char)SILC_PACKET_FLAG_FORWARDED;
+  sock->user_data = NULL;
+}
 
-  /* Put the data to the buffer */
-  if (data && data_len)
-    silc_buffer_put(sock->outbuf, data, data_len);
+/* Removes client from all channels it has joined. This is used when client
+   connection is disconnected. If the client on a channel is last, the
+   channel is removed as well. This sends the SIGNOFF notify types. */
 
-  /* Compute MAC of the packet */
-  if (hmac) {
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-  }
+void silc_server_remove_from_channels(SilcServer server, 
+                                     SilcSocketConnection sock,
+                                     SilcClientEntry client,
+                                     int notify,
+                                     char *signoff_message,
+                                     int keygen)
+{
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer clidp;
 
-  /* Encrypt the packet */
-  if (cipher)
-    silc_packet_encrypt(cipher, sock->outbuf, sock->outbuf->len);
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Pull MAC into the visible data area */
-  if (hmac)
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
+  if (!client || !client->id)
+    return;
 
-  SILC_LOG_HEXDUMP(("Forwarded packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
-  /* Now actually send the packet */
-  silc_server_packet_send_real(server, sock, force_send);
-}
+  /* Remove the client from all channels. The client is removed from
+     the channels' user list. */
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    channel = chl->channel;
+
+    /* Remove channel from client's channel list */
+    silc_hash_table_del(client->channels, channel);
+
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_hash_table_count(channel->user_list) < 2) {
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
+      continue;
+    }
 
-/* This routine is used by the server to send packets to channel. The 
-   packet sent with this function is distributed to all clients on
-   the channel. Usually this is used to send notify messages to the
-   channel, things like notify about new user joining to the channel. */
+    /* Remove client from channel's client list */
+    silc_hash_table_del(channel->user_list, chl->client);
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. Do not check if the removed client is local client. */
+    if (server->server_type != SILC_ROUTER && channel->global_users && 
+       chl->client->router && !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type != SILC_ROUTER &&
+       !silc_server_channel_has_local(channel)) {
+      /* Notify about leaving client if this channel has global users. */
+      if (notify && channel->global_users)
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                          SILC_NOTIFY_TYPE_SIGNOFF, 
+                                          signoff_message ? 2 : 1,
+                                          clidp->data, clidp->len,
+                                          signoff_message, signoff_message ?
+                                          strlen(signoff_message) : 0);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+       SilcHashTableList htl2;
+
+       channel->disabled = TRUE;
+
+       silc_hash_table_list(channel->user_list, &htl2);
+       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+         silc_hash_table_del(chl2->client->channels, channel);
+         silc_hash_table_del(channel->user_list, chl2->client);
+         silc_free(chl2);
+       }
+       silc_hash_table_list_reset(&htl2);
+       continue;
+      }
 
-void silc_server_packet_send_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       unsigned char *data,
-                                       unsigned int data_len,
-                                       int force_send)
-{
-  int i;
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
-  SilcClientList *client = NULL;
-  SilcServerList **routed = NULL;
-  unsigned int routed_count = 0;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  SilcCipher cipher;
-  SilcHmac hmac;
-  SilcBuffer payload;
-
-  SILC_LOG_DEBUG(("Sending packet to channel"));
-
-  /* Generate IV */
-  for (i = 0; i < 16; i++)
-    channel->iv[i] = silc_rng_get_byte(server->rng);
-
-  /* Encode the channel payload */
-  payload = silc_channel_encode_payload(0, "", data_len, data, 
-                                       16, channel->iv, server->rng);
-  if (!payload)
-    return;
-  
-  /* Encrypt payload of the packet. This is encrypted with the 
-     channel key. */
-  channel->channel_key->cipher->encrypt(channel->channel_key->context,
-                                       payload->data, payload->data,
-                                       payload->len - 16, /* -IV_LEN */
-                                       channel->iv);
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = silc_id_id2str(server->id, SILC_ID_SERVER);
-  packetdata.src_id_len = SILC_ID_SERVER_LEN;
-  packetdata.src_id_type = SILC_ID_SERVER;
-  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* If there are global users in the channel we will send the message
-     first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
-      channel->global_users) {
-    SilcServerList *router;
-
-    /* Get data used in packet header encryption, keys and stuff. */
-    router = server->id_entry->router;
-    sock = (SilcSocketConnection)router->connection;
-    cipher = router->send_key;
-    hmac = router->hmac;
-    mac_len = hmac->hash->hash->hash_len;
-    hmac_key = router->hmac_key;
-    hmac_key_len = router->hmac_key_len;
-    
-    SILC_LOG_DEBUG(("Sending packet to router for routing"));
-
-    packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-      packetdata.src_id_len + packetdata.dst_id_len;
-
-    /* Prepare outgoing data buffer for packet sending */
-    silc_server_packet_send_prepare(server, sock, 
-                                   SILC_PACKET_HEADER_LEN +
-                                   packetdata.src_id_len + 
-                                   packetdata.dst_id_len,
-                                   packetdata.padlen,
-                                   payload->len);
-    packetdata.buffer = sock->outbuf;
-
-    /* Put the original packet into the buffer. */
-    silc_buffer_put(sock->outbuf, payload->data, payload->len);
-    
-    /* Create the outgoing packet */
-    silc_packet_assemble(&packetdata);
-    
-    /* Compute MAC of the packet. MAC is computed from the header,
-       padding and the relayed packet. */
-    silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                           hmac_key, hmac_key_len, mac);
-    silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-
-    /* Encrypt the header and padding of the packet. This is encrypted 
-       with normal session key shared with the client. */
-    silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                       packetdata.src_id_len + packetdata.dst_id_len +
-                       packetdata.padlen);
-    
-    /* Pull MAC into the visible data area */
-    silc_buffer_pull_tail(sock->outbuf, mac_len);
-    
-    SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                    sock->outbuf->data, sock->outbuf->len);
+      /* Remove the channel entry */
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
+      continue;
+    }
 
-    /* Now actually send the packet */
-    silc_server_packet_send_real(server, sock, force_send);
+    /* Send notify to channel about client leaving SILC and thus
+       the entire channel. */
+    if (notify)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                        SILC_NOTIFY_TYPE_SIGNOFF, 
+                                        signoff_message ? 2 : 1,
+                                        clidp->data, clidp->len,
+                                        signoff_message, signoff_message ?
+                                        strlen(signoff_message) : 0);
+
+    if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+      /* Re-generate channel key */
+      if (!silc_server_create_channel_key(server, channel, 0))
+       goto out;
+      
+      /* Send the channel key to the channel. The key of course is not sent
+        to the client who was removed from the channel. */
+      silc_server_send_channel_key(server, client->connection, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
   }
 
-  /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+ out:
+  silc_hash_table_list_reset(&htl);
+  silc_buffer_free(clidp);
+}
 
-    /* If client has router set it is not locally connected client and
-       we will route the message to the router set in the client. */
-    if (client && client->router && server->server_type == SILC_ROUTER) {
-      int k;
+/* Removes client from one channel. This is used for example when client
+   calls LEAVE command to remove itself from the channel. Returns TRUE
+   if channel still exists and FALSE if the channel is removed when
+   last client leaves the channel. If `notify' is FALSE notify messages
+   are not sent. */
 
-      /* Check if we have sent the packet to this route already */
-      for (k = 0; k < routed_count; k++)
-       if (routed[k] == client->router)
-         continue;
+int silc_server_remove_from_one_channel(SilcServer server, 
+                                       SilcSocketConnection sock,
+                                       SilcChannelEntry channel,
+                                       SilcClientEntry client,
+                                       int notify)
+{
+  SilcChannelClientEntry chl;
+  SilcBuffer clidp;
 
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->router->connection;
-      cipher = client->router->send_key;
-      hmac = client->router->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = client->router->hmac_key;
-      hmac_key_len = client->router->hmac_key_len;
-      
-      packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     payload->len);
-      packetdata.buffer = sock->outbuf;
-
-      /* Put the encrypted payload data into the buffer. */
-      silc_buffer_put(sock->outbuf, payload->data, payload->len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Get the entry to the channel, if this client is not on the channel
+     then return Ok. */
+  if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
+    return TRUE;
+
+  /* Remove the client from the channel. The client is removed from
+     the channel's user list. */
+
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Remove channel from client's channel list */
+  silc_hash_table_del(client->channels, chl->channel);
+
+  /* Remove channel if there is no users anymore */
+  if (server->server_type == SILC_ROUTER &&
+      silc_hash_table_count(channel->user_list) < 2) {
+    if (channel->rekey)
+      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+    if (silc_idlist_del_channel(server->local_list, channel))
+      server->stat.my_channels--;
+    else 
+      silc_idlist_del_channel(server->global_list, channel);
+    silc_buffer_free(clidp);
+    return FALSE;
+  }
+
+  /* Remove client from channel's client list */
+  silc_hash_table_del(channel->user_list, chl->client);
+  
+  /* If there is no global users on the channel anymore mark the channel
+     as local channel. Do not check if the client is local client. */
+  if (server->server_type != SILC_ROUTER && channel->global_users &&
+      chl->client->router && !silc_server_channel_has_global(channel))
+    channel->global_users = FALSE;
+
+  silc_free(chl);
+  server->stat.my_chanclients--;
+
+  /* If there is not at least one local user on the channel then we don't
+     need the channel entry anymore, we can remove it safely. */
+  if (server->server_type != SILC_ROUTER &&
+      !silc_server_channel_has_local(channel)) {
+    /* Notify about leaving client if this channel has global users. */
+    if (notify && channel->global_users)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                        SILC_NOTIFY_TYPE_LEAVE, 1,
+                                        clidp->data, clidp->len);
+    
+    silc_buffer_free(clidp);
+    
+    if (channel->rekey)
+      silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+    if (channel->founder_key) {
+      /* The founder auth data exists, do not remove the channel entry */
+      SilcChannelClientEntry chl2;
+      SilcHashTableList htl2;
       
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
+      channel->disabled = TRUE;
       
-      SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
+      silc_hash_table_list(channel->user_list, &htl2);
+      while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+       silc_hash_table_del(chl2->client->channels, channel);
+       silc_hash_table_del(channel->user_list, chl2->client);
+       silc_free(chl2);
+      }
+      silc_hash_table_list_reset(&htl2);
+      return FALSE;
+    }
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
+    /* Remove the channel entry */
+    if (silc_idlist_del_channel(server->local_list, channel))
+      server->stat.my_channels--;
+    else 
+      silc_idlist_del_channel(server->global_list, channel);
+    return FALSE;
+  }
 
-      /* We want to make sure that the packet is routed to same router
-        only once. Mark this route as sent route. */
-      k = routed_count;
-      routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-      routed[k] = client->router;
-      routed_count++;
+  /* Send notify to channel about client leaving the channel */
+  if (notify)
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                      SILC_NOTIFY_TYPE_LEAVE, 1,
+                                      clidp->data, clidp->len);
 
-      continue;
-    }
+  silc_buffer_free(clidp);
+  return TRUE;
+}
 
-    /* Send to locally connected client */
-    if (client) {
+/* Timeout callback. This is called if connection is idle or for some
+   other reason is not responding within some period of time. This 
+   disconnects the remote end. */
 
-      /* XXX Check client's mode on the channel. */
+SILC_TASK_CALLBACK(silc_server_timeout_remote)
+{
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection sock = server->sockets[fd];
 
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->connection;
-      cipher = client->send_key;
-      hmac = client->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = client->hmac_key;
-      hmac_key_len = client->hmac_key_len;
-      
-      packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     payload->len);
-      packetdata.buffer = sock->outbuf;
-
-      /* Put the encrypted payload data into the buffer. */
-      silc_buffer_put(sock->outbuf, payload->data, payload->len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_DEBUG(("Start"));
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
-    }
+  if (!sock)
+    return;
+
+  /* If we have protocol active we must assure that we call the protocol's
+     final callback so that all the memory is freed. */
+  if (sock->protocol) {
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute_final(sock->protocol, server->schedule);
+    sock->protocol = NULL;
+    return;
   }
 
-  if (routed_count)
-    silc_free(routed);
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
-  silc_buffer_free(payload);
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
+
+  silc_server_disconnect_remote(server, sock, "Server closed connection: "
+                               "Connection timeout");
 }
 
-/* This routine is explicitly used to relay messages to some channel.
-   Packets sent with this function we have received earlier and are
-   totally encrypted. This just sends the packet to all clients on
-   the channel. If the sender of the packet is someone on the channel 
-   the message will not be sent to that client. The SILC Packet header
-   is encrypted with the session key shared between us and the client.
-   MAC is also computed before encrypting the header. Rest of the
-   packet will be untouched. */
-
-void silc_server_packet_relay_to_channel(SilcServer server,
-                                        SilcSocketConnection sender_sock,
-                                        SilcChannelList *channel,
-                                        void *sender, 
-                                        SilcIdType sender_type,
-                                        unsigned char *data,
-                                        unsigned int data_len,
-                                        int force_send)
+/* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
+   function may be used only by router. In real SILC network all channels
+   are created by routers thus this function is never used by normal
+   server. */
+
+SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
+                                               SilcServerID *router_id,
+                                               char *cipher, 
+                                               char *hmac,
+                                               char *channel_name,
+                                               int broadcast)
 {
-  int i, found = FALSE;
-  SilcSocketConnection sock = NULL;
-  SilcPacketContext packetdata;
-  SilcClientList *client = NULL;
-  SilcServerList **routed = NULL;
-  unsigned int routed_count = 0;
-  unsigned char *hmac_key = NULL;
-  unsigned int hmac_key_len = 0;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
-  SilcCipher cipher;
-  SilcHmac hmac;
-
-  SILC_LOG_DEBUG(("Relaying packet to channel"));
-
-  SILC_LOG_HEXDUMP(("XXX %d", data_len), data, data_len);
-
-  /* Set the packet context pointers. */
-  packetdata.flags = 0;
-  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
-  packetdata.src_id = silc_id_id2str(sender, sender_type);
-  packetdata.src_id_len = silc_id_get_len(sender_type);
-  packetdata.src_id_type = sender_type;
-  packetdata.dst_id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
-  packetdata.dst_id_type = SILC_ID_CHANNEL;
-  packetdata.rng = server->rng;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* If there are global users in the channel we will send the message
-     first to our router for further routing. */
-  if (server->server_type == SILC_SERVER && !server->standalone &&
-      channel->global_users) {
-    SilcServerList *router;
-
-    router = server->id_entry->router;
-
-    /* Check that the sender is not our router. */
-    if (sender_sock != (SilcSocketConnection)router->connection) {
-
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)router->connection;
-      cipher = router->send_key;
-      hmac = router->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = router->hmac_key;
-      hmac_key_len = router->hmac_key_len;
-      
-      SILC_LOG_DEBUG(("Sending packet to router for routing"));
-      
-      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-      
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     data_len);
-      packetdata.buffer = sock->outbuf;
-      
-      /* Put the original packet into the buffer. */
-      silc_buffer_put(sock->outbuf, data, data_len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-      
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
-      
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
-    }
-  }
+  SilcChannelID *channel_id;
+  SilcChannelEntry entry;
+  SilcCipher key;
+  SilcHmac newhmac;
 
-  /* Send the message to clients on the channel's client list. */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+  SILC_LOG_DEBUG(("Creating new channel"));
 
-    if (client) {
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
 
-      /* If sender is one on the channel do not send it the packet. */
-      if (!found && !SILC_ID_CLIENT_COMPARE(client->id, sender)) {
-       found = TRUE;
-       continue;
-      }
+  /* Allocate cipher */
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
-      /* If the client has set router it means that it is not locally
-        connected client and we won't send message to those in this
-        function (they will be routed separately by the caller). */
-      if (server->server_type == SILC_ROUTER && client->router) {
-       int k;
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
+  }
 
-       /* Sender maybe server as well so we want to make sure that
-          we won't send the message to the server it came from. */
-       if (!found && !SILC_ID_SERVER_COMPARE(client->router->id, sender)) {
-         found = TRUE;
-         continue;
-       }
+  channel_name = strdup(channel_name);
 
-       /* Check if we have sent the packet to this route already */
-       for (k = 0; k < routed_count; k++)
-         if (routed[k] == client->router)
-           continue;
-       
-       /* Get data used in packet header encryption, keys and stuff. */
-       sock = (SilcSocketConnection)client->router->connection;
-       cipher = client->router->send_key;
-       hmac = client->router->hmac;
-       mac_len = hmac->hash->hash->hash_len;
-       hmac_key = client->router->hmac_key;
-       hmac_key_len = client->router->hmac_key_len;
-       
-       packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-         packetdata.src_id_len + packetdata.dst_id_len;
-       
-       /* Prepare outgoing data buffer for packet sending */
-       silc_server_packet_send_prepare(server, sock, 
-                                       SILC_PACKET_HEADER_LEN +
-                                       packetdata.src_id_len + 
-                                       packetdata.dst_id_len,
-                                       packetdata.padlen,
-                                       data_len);
-       packetdata.buffer = sock->outbuf;
-       
-       /* Put the original packet into the buffer. */
-       silc_buffer_put(sock->outbuf, data, data_len);
-       
-       /* Create the outgoing packet */
-       silc_packet_assemble(&packetdata);
-       
-       /* Compute MAC of the packet. MAC is computed from the header,
-          padding and the relayed packet. */
-       silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                               hmac_key, hmac_key_len, mac);
-       silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-       memset(mac, 0, sizeof(mac));
-       
-       /* Encrypt the header and padding of the packet. This is encrypted 
-          with normal session key shared with the client. */
-       silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                           packetdata.src_id_len + packetdata.dst_id_len +
-                           packetdata.padlen);
-       
-       /* Pull MAC into the visible data area */
-       silc_buffer_pull_tail(sock->outbuf, mac_len);
-       
-       SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
-                        sock->outbuf->data, sock->outbuf->len);
-       
-       /* Now actually send the packet */
-       silc_server_packet_send_real(server, sock, force_send);
-       
-       /* We want to make sure that the packet is routed to same router
-          only once. Mark this route as sent route. */
-       k = routed_count;
-       routed = silc_realloc(routed, sizeof(*routed) * (k + 1));
-       routed[k] = client->router;
-       routed_count++;
-       
-       continue;
-      }
-      
-      /* XXX Check client's mode on the channel. */
+  /* Create the channel ID */
+  if (!silc_id_create_channel_id(server, router_id, server->rng, 
+                                &channel_id)) {
+    silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
+    return NULL;
+  }
 
+  /* Create the channel */
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key, newhmac, 0);
+  if (!entry) {
+    silc_free(channel_name);
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
+    silc_free(channel_id);
+    return NULL;
+  }
 
-      /* Get data used in packet header encryption, keys and stuff. */
-      sock = (SilcSocketConnection)client->connection;
-      cipher = client->send_key;
-      hmac = client->hmac;
-      mac_len = hmac->hash->hash->hash_len;
-      hmac_key = client->hmac_key;
-      hmac_key_len = client->hmac_key_len;
-      
-      SILC_LOG_DEBUG(("Sending packet to client %s", 
-                     sock->hostname ? sock->hostname : sock->ip));
-
-      packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
-       packetdata.src_id_len + packetdata.dst_id_len;
-
-      /* Prepare outgoing data buffer for packet sending */
-      silc_server_packet_send_prepare(server, sock, 
-                                     SILC_PACKET_HEADER_LEN +
-                                     packetdata.src_id_len + 
-                                     packetdata.dst_id_len,
-                                     packetdata.padlen,
-                                     data_len);
-      packetdata.buffer = sock->outbuf;
-
-      /* Put the original packet into the buffer. */
-      silc_buffer_put(sock->outbuf, data, data_len);
-      
-      /* Create the outgoing packet */
-      silc_packet_assemble(&packetdata);
-      
-      /* Compute MAC of the packet. MAC is computed from the header,
-        padding and the relayed packet. */
-      silc_hmac_make_with_key(hmac, sock->outbuf->data, sock->outbuf->len,
-                             hmac_key, hmac_key_len, mac);
-      silc_buffer_put_tail(sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-
-      /* Encrypt the header and padding of the packet. This is encrypted 
-        with normal session key shared with the client. */
-      silc_packet_encrypt(cipher, sock->outbuf, SILC_PACKET_HEADER_LEN + 
-                         packetdata.src_id_len + packetdata.dst_id_len +
-                         packetdata.padlen);
-      
-      /* Pull MAC into the visible data area */
-      silc_buffer_pull_tail(sock->outbuf, mac_len);
-      
-      SILC_LOG_HEXDUMP(("Channel packet, len %d", sock->outbuf->len),
-                      sock->outbuf->data, sock->outbuf->len);
+  entry->cipher = strdup(cipher);
+  entry->hmac_name = strdup(hmac);
 
-      /* Now actually send the packet */
-      silc_server_packet_send_real(server, sock, force_send);
-    }
+  /* Now create the actual key material */
+  if (!silc_server_create_channel_key(server, entry, 
+                                     silc_cipher_get_key_len(key) / 8)) {
+    silc_idlist_del_channel(server->local_list, entry);
+    return NULL;
   }
 
-  silc_free(packetdata.src_id);
-  silc_free(packetdata.dst_id);
-}
+  /* Notify other routers about the new channel. We send the packet
+     to our primary route. */
+  if (broadcast && server->standalone == FALSE)
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, 
+                                silc_id_get_len(entry->id, SILC_ID_CHANNEL),
+                                entry->mode);
 
-/* Relays received command reply packet to the correct destination. The
-   destination must be one of our locally connected client or the packet
-   will be ignored. This is called when server has forwarded one of
-   client's command request to router and router has now replied to the 
-   command. */
+  server->stat.my_channels++;
 
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
-  SilcClientList *client;
-  SilcClientID *id;
-  SilcSocketConnection dst_sock;
-  unsigned char mac[32];
-  unsigned int mac_len = 0;
+  return entry;
+}
 
-  SILC_LOG_DEBUG(("Start"));
+/* Same as above but creates the channel with Channel ID `channel_id. */
 
-  /* Source must be server or router */
-  /* XXX: actually it must be only router */
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      (sock->type != SILC_SOCKET_TYPE_SERVER ||
-       sock->type != SILC_SOCKET_TYPE_ROUTER))
-    goto out;
+SilcChannelEntry 
+silc_server_create_new_channel_with_id(SilcServer server, 
+                                      char *cipher, 
+                                      char *hmac,
+                                      char *channel_name,
+                                      SilcChannelID *channel_id,
+                                      int broadcast)
+{
+  SilcChannelEntry entry;
+  SilcCipher key;
+  SilcHmac newhmac;
 
-  /* Destination must be client */
-  if (packet->dst_id_type != SILC_ID_CLIENT)
-    goto out;
+  SILC_LOG_DEBUG(("Creating new channel"));
 
-  /* Execute command reply locally for the command */
-  silc_server_command_reply_process(server, sock, buffer);
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
 
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
+  /* Allocate cipher */
+  if (!silc_cipher_alloc(cipher, &key))
+    return NULL;
 
-  /* Destination must be one of ours */
-  client = silc_idlist_find_client_by_id(server->local_list->clients, id);
-  if (!client) {
-    silc_free(id);
-    goto out;
+  /* Allocate hmac */
+  if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+    silc_cipher_free(key);
+    return NULL;
   }
 
-  /* Relay the packet to the client */
-  if (client->hmac)
-    mac_len = client->hmac->hash->hash->hash_len;
+  channel_name = strdup(channel_name);
 
-  dst_sock = (SilcSocketConnection)client->connection;
+  /* Create the channel */
+  entry = silc_idlist_add_channel(server->local_list, channel_name, 
+                                 SILC_CHANNEL_MODE_NONE, channel_id, 
+                                 NULL, key, newhmac, 0);
+  if (!entry) {
+    silc_cipher_free(key);
+    silc_hmac_free(newhmac);
+    silc_free(channel_name);
+    return NULL;
+  }
 
-  silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                  + packet->dst_id_len + packet->padlen);
-  silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-  silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-  
-  /* Compute new HMAC */
-  if (client->hmac) {
-    memset(mac, 0, sizeof(mac));
-    silc_hmac_make_with_key(client->hmac, 
-                           dst_sock->outbuf->data, 
-                           dst_sock->outbuf->len,
-                           client->hmac_key, 
-                           client->hmac_key_len, 
-                           mac);
-    silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
+  /* Now create the actual key material */
+  if (!silc_server_create_channel_key(server, entry, 
+                                     silc_cipher_get_key_len(key) / 8)) {
+    silc_idlist_del_channel(server->local_list, entry);
+    return NULL;
   }
-    
-  /* Encrypt */
-  if (client && client->send_key)
-    silc_packet_encrypt(client->send_key, dst_sock->outbuf, buffer->len);
-    
-  if (client->hmac)
-    silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
-    
-  /* Send the packet */
-  silc_server_packet_send_real(server, dst_sock, FALSE);
 
-  silc_free(id);
+  /* Notify other routers about the new channel. We send the packet
+     to our primary route. */
+  if (broadcast && server->standalone == FALSE)
+    silc_server_send_new_channel(server, server->router->connection, TRUE, 
+                                channel_name, entry->id, 
+                                silc_id_get_len(entry->id, SILC_ID_CHANNEL),
+                                entry->mode);
 
- out:
-  silc_buffer_free(buffer);
+  server->stat.my_channels++;
+
+  return entry;
 }
 
-/* Closes connection to socket connection */
+/* Channel's key re-key timeout callback. */
 
-void silc_server_close_connection(SilcServer server,
-                                 SilcSocketConnection sock)
+SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
 {
+  SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
+  SilcServer server = (SilcServer)rekey->context;
 
-  SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
-
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(sock->sock);
+  rekey->task = NULL;
 
-  /* Unregister all tasks */
-  silc_task_unregister_by_fd(server->io_queue, sock->sock);
-  silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
+  if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
+    return;
 
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
-  server->sockets[sock->sock] = NULL;
-  silc_socket_free(sock);
+  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
 }
 
-/* Sends disconnect message to remote connection and disconnects the 
-   connection. */
+/* Generates new channel key. This is used to create the initial channel key
+   but also to re-generate new key for channel. If `key_len' is provided
+   it is the bytes of the key length. */
 
-void silc_server_disconnect_remote(SilcServer server,
-                                  SilcSocketConnection sock,
-                                  const char *fmt, ...)
+bool silc_server_create_channel_key(SilcServer server, 
+                                   SilcChannelEntry channel,
+                                   uint32 key_len)
 {
-  va_list ap;
-  unsigned char buf[4096];
+  int i;
+  unsigned char channel_key[32], hash[32];
+  uint32 len;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  SILC_LOG_DEBUG(("Generating channel key"));
 
-  SILC_LOG_DEBUG(("Disconnecting remote host"));
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
+    return TRUE;
+  }
 
-  /* Notify remote end that the conversation is over. The notify message
-     is tried to be sent immediately. */
-  silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,  
-                         buf, strlen(buf), TRUE);
+  if (!channel->channel_key)
+    if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) {
+      channel->channel_key = NULL;
+      return FALSE;
+    }
 
-  /* Mark the connection to be disconnected */
-  SILC_SET_DISCONNECTED(sock);
-  silc_server_close_connection(server, sock);
+  if (key_len)
+    len = key_len;
+  else if (channel->key_len)
+    len = channel->key_len / 8;
+  else
+    len = silc_cipher_get_key_len(channel->channel_key) / 8;
+
+  /* Create channel key */
+  for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
+  
+  /* Set the key */
+  silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
+
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+  }
+
+  /* Save the key */
+  channel->key_len = len * 8;
+  channel->key = silc_calloc(len, sizeof(*channel->key));
+  memcpy(channel->key, channel_key, len);
+  memset(channel_key, 0, sizeof(channel_key));
+
+  /* Generate HMAC key from the channel key data and set it */
+  if (!channel->hmac)
+    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+  silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
+  silc_hmac_set_key(channel->hmac, hash, 
+                   silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+  memset(hash, 0, sizeof(hash));
+
+  if (server->server_type == SILC_ROUTER) {
+    if (!channel->rekey)
+      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    channel->rekey->context = (void *)server;
+    channel->rekey->channel = channel;
+    channel->rekey->key_len = key_len;
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task = 
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_channel_key_rekey,
+                            (void *)channel->rekey, 3600, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+  }
+
+  return TRUE;
 }
 
-/* Free's user_data pointer from socket connection object. As this 
-   pointer maybe anything we wil switch here to find the corrent
-   data type and free it the way it needs to be free'd. */
+/* Saves the channel key found in the encoded `key_payload' buffer. This 
+   function is used when we receive Channel Key Payload and also when we're
+   processing JOIN command reply. Returns entry to the channel. */
 
-void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock)
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel)
 {
-  SILC_LOG_DEBUG(("Start"));
-
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+  SilcChannelKeyPayload payload = NULL;
+  SilcChannelID *id = NULL;
+  unsigned char *tmp, hash[32];
+  uint32 tmp_len;
+  char *cipher;
 
-  switch(sock->type) {
-  case SILC_SOCKET_TYPE_CLIENT:
-    {
-      SilcClientList *user_data = (SilcClientList *)sock->user_data;
+  SILC_LOG_DEBUG(("Start"));
 
-      /* Remove client from all channels */
-      silc_server_remove_from_channels(server, sock, user_data);
+  /* Decode channel key payload */
+  payload = silc_channel_key_payload_parse(key_payload->data, 
+                                          key_payload->len);
+  if (!payload) {
+    SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
+    channel = NULL;
+    goto out;
+  }
 
-      /* Clear ID cache */
-      if (user_data->nickname && user_data->id)
-       silc_idcache_del_by_id(LCC(user_data->nickname[0]),
-                              LCCC(user_data->nickname[0]),
-                              SILC_ID_CLIENT, user_data->id);
+  /* Get the channel entry */
+  if (!channel) {
 
-      /* Free the client entry and everything in it */
-      /* XXX must take some info to history before freeing */
-      silc_idlist_del_client(&server->local_list->clients, user_data);
-      break;
+    /* Get channel ID */
+    tmp = silc_channel_key_get_id(payload, &tmp_len);
+    id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
+    if (!id) {
+      channel = NULL;
+      goto out;
     }
-  case SILC_SOCKET_TYPE_SERVER:
-  case SILC_SOCKET_TYPE_ROUTER:
-    {
 
-      break;
-    }
-    break;
-  default:
-    {
-      SilcIDListUnknown *user_data = (SilcIDListUnknown *)sock->user_data;
-
-      if (user_data->send_key)
-       silc_cipher_free(user_data->send_key);
-      if (user_data->receive_key)
-       silc_cipher_free(user_data->receive_key);
-      if (user_data->pkcs)
-       silc_pkcs_free(user_data->pkcs);
-      if (user_data->hmac) {
-       silc_hmac_free(user_data->hmac);
-       memset(user_data->hmac_key, 0, user_data->hmac_key_len);
-       silc_free(user_data->hmac_key);
+    channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+    if (!channel) {
+      channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+      if (!channel) {
+       SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                       silc_id_render(id, SILC_ID_CHANNEL)));
+       goto out;
       }
-      silc_free(user_data);
-      break;
     }
   }
 
-  sock->user_data = NULL;
-#undef LCC
-#undef LCCC
+  tmp = silc_channel_key_get_key(payload, &tmp_len);
+  if (!tmp) {
+    channel = NULL;
+    goto out;
+  }
+
+  cipher = silc_channel_key_get_cipher(payload, NULL);
+  if (!cipher) {
+    channel = NULL;
+    goto out;
+  }
+
+  /* Remove old key if exists */
+  if (channel->key) {
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    silc_cipher_free(channel->channel_key);
+  }
+
+  /* Create new cipher */
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    channel->channel_key = NULL;
+    channel = NULL;
+    goto out;
+  }
+
+  if (channel->cipher)
+    silc_free(channel->cipher);
+  channel->cipher = strdup(cipher);
+
+  /* Save the key */
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
+  memcpy(channel->key, tmp, tmp_len);
+  silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
+
+  /* Generate HMAC key from the channel key data and set it */
+  if (!channel->hmac)
+    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+  silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
+  silc_hmac_set_key(channel->hmac, hash, 
+                   silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+
+  memset(hash, 0, sizeof(hash));
+  memset(tmp, 0, tmp_len);
+
+  if (server->server_type == SILC_ROUTER) {
+    if (!channel->rekey)
+      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    channel->rekey->context = (void *)server;
+    channel->rekey->channel = channel;
+    if (channel->rekey->task)
+      silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+    channel->rekey->task = 
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_channel_key_rekey,
+                            (void *)channel->rekey, 3600, 0,
+                            SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+  }
+
+ out:
+  silc_free(id);
+  if (payload)
+    silc_channel_key_payload_free(payload);
+
+  return channel;
 }
 
-/* Removes client from all channels it has joined. This is used when
-   client connection is disconnected. If the client on a channel
-   is last, the channel is removed as well. */
+/* Heartbeat callback. This function is set as argument for the
+   silc_socket_set_heartbeat function. The library will call this function
+   at the set time interval. */
 
-void silc_server_remove_from_channels(SilcServer server, 
-                                     SilcSocketConnection sock,
-                                     SilcClientList *client)
+void silc_server_perform_heartbeat(SilcSocketConnection sock,
+                                  void *hb_context)
 {
-  int i, k;
-  SilcChannelList *channel;
+  SilcServerHBContext hb = (SilcServerHBContext)hb_context;
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
 
-  /* Remove the client from all channels. The client is removed from
-     the channels' user list. */
-  for (i = 0; i < client->channel_count; i++) {
-    channel = client->channel[i];
-    if (!channel)
-      continue;
+  /* Send the heartbeat */
+  silc_server_send_heartbeat(hb->server, sock);
+}
 
-    /* Remove from channel */
-    for (k = 0; k < channel->user_list_count; k++) {
-      if (channel->user_list[k].client == client) {
-
-       /* If this client is last one on the channel the channel
-          is removed all together. */
-       if (channel->user_list_count == 1) {
-         silc_idcache_del_by_id(LCC(channel->channel_name[0]),
-                                LCCC(channel->channel_name[0]),
-                                SILC_ID_CHANNEL, channel->id);
-         silc_idlist_del_channel(&server->local_list->channels, channel);
-         break;
+/* Returns assembled of all servers in the given ID list. The packet's
+   form is dictated by the New ID payload. */
+
+static void silc_server_announce_get_servers(SilcServer server,
+                                            SilcServerEntry remote,
+                                            SilcIDList id_list,
+                                            SilcBuffer *servers,
+                                            unsigned long creation_time)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+  SilcBuffer idp;
+
+  /* Go through all clients in the list */
+  if (silc_idcache_get_all(id_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       entry = (SilcServerEntry)id_cache->context;
+
+       /* Do not announce the one we've sending our announcements and
+          do not announce ourself. Also check the creation time if it's
+          provided. */
+       if ((entry == remote) || (entry == server->id_entry) ||
+           (creation_time && entry->data.created < creation_time)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
        }
 
-       channel->user_list[k].client = NULL;
-       channel->user_list[k].mode = SILC_CHANNEL_UMODE_NONE;
+       idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
 
-       /* XXX */
-       /* Send notify to channel about client leaving SILC and thus
-          the entire channel. */
-       silc_server_send_notify_to_channel(server, channel,
-                                          "%s has left channel %s",
-                                          client->nickname,
-                                          channel->channel_name);
+       *servers = silc_buffer_realloc(*servers, 
+                                      (*servers ? 
+                                       (*servers)->truelen + idp->len : 
+                                       idp->len));
+       silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
+       silc_buffer_put(*servers, idp->data, idp->len);
+       silc_buffer_pull(*servers, idp->len);
+       silc_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
       }
     }
-  }
 
-  if (client->channel_count)
-    silc_free(client->channel);
-  client->channel = NULL;
-#undef LCC
-#undef LCCC
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Timeout callback. This is called if connection is idle or for some
-   other reason is not responding within some period of time. This 
-   disconnects the remote end. */
-
-SILC_TASK_CALLBACK(silc_server_timeout_remote)
+static SilcBuffer 
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
 {
-  SilcServer server = (SilcServer)context;
-  SilcSocketConnection sock = server->sockets[fd];
+  va_list ap;
+  SilcBuffer p;
 
-  silc_server_disconnect_remote(server, sock, 
-                               "Server closed connection: "
-                               "Connection timeout");
+  va_start(ap, argc);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+  return p;
 }
 
-/* Internal routine used to send (relay, route) private messages to some
-   destination. This is used to by normal server to send the message to
-   its primary route and router uses this to send it to any route it
-   wants. If the private message key does not exist then the message
-   is re-encrypted, otherwise we just pass it along. */
-static void 
-silc_server_private_message_send_internal(SilcServer server,
-                                         SilcSocketConnection dst_sock,
-                                         SilcServerList *router,
-                                         SilcPacketContext *packet)
+/* This function is used by router to announce existing servers to our
+   primary router when we've connected to it. If `creation_time' is non-zero
+   then only the servers that has been created after the `creation_time'
+   will be announced. */
+
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote)
 {
-  SilcBuffer buffer = packet->buffer;
+  SilcBuffer servers = NULL;
+
+  SILC_LOG_DEBUG(("Announcing servers"));
+
+  /* Get servers in local list */
+  silc_server_announce_get_servers(server, remote->user_data,
+                                  server->local_list, &servers,
+                                  creation_time);
+
+  if (global)
+    /* Get servers in global list */
+    silc_server_announce_get_servers(server, remote->user_data,
+                                    server->global_list, &servers,
+                                    creation_time);
+
+  if (servers) {
+    silc_buffer_push(servers, servers->data - servers->head);
+    SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
 
-  /* Send and re-encrypt if private messge key does not exist */
-  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
-    unsigned char mac[32];
-    unsigned int mac_len = 0;
-    
-    if (router->hmac)
-      mac_len = router->hmac->hash->hash->hash_len;
-    
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
-    /* Compute new HMAC */
-    if (router->hmac) {
-      mac_len = router->hmac->hash->hash->hash_len;
-      memset(mac, 0, sizeof(mac));
-      silc_hmac_make_with_key(router->hmac, 
-                             dst_sock->outbuf->data, 
-                             dst_sock->outbuf->len,
-                             router->hmac_key, 
-                             router->hmac_key_len, 
-                             mac);
-      silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
-    }
-    
-    silc_packet_encrypt(router->send_key, dst_sock->outbuf, buffer->len);
-    
-    if (router->hmac)
-      silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
-    
     /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, FALSE);
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
+                           servers->data, servers->len, TRUE);
 
-  } else {
-    /* Key exist so just send it */
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    silc_server_packet_send_real(server, dst_sock, FALSE);
+    silc_buffer_free(servers);
   }
 }
 
-/* Internal routine to send the received private message packet to
-   our locally connected client. */
-static void
-silc_server_private_message_send_local(SilcServer server,
-                                      SilcSocketConnection dst_sock,
-                                      SilcClientList *client,
-                                      SilcPacketContext *packet)
-{
-  SilcBuffer buffer = packet->buffer;
+/* Returns assembled packet of all clients in the given ID list. The
+   packet's form is dictated by the New ID Payload. */
 
-  /* Re-encrypt packet if needed */
-  if ((packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY) == FALSE) {
-    unsigned char mac[32];
-    unsigned int mac_len = 0;
+static void silc_server_announce_get_clients(SilcServer server,
+                                            SilcIDList id_list,
+                                            SilcBuffer *clients,
+                                            SilcBuffer *umodes,
+                                            unsigned long creation_time)
+{
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcClientEntry client;
+  SilcBuffer idp;
+  SilcBuffer tmp;
+  unsigned char mode[4];
+
+  /* Go through all clients in the list */
+  if (silc_idcache_get_all(id_list->clients, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+
+       if (creation_time && client->data.created < creation_time) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
 
-    if (client->hmac)
-      mac_len = client->hmac->hash->hash->hash_len;
-    
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    
-    /* Compute new HMAC */
-    if (client->hmac) {
-      memset(mac, 0, sizeof(mac));
-      silc_hmac_make_with_key(client->hmac, 
-                             dst_sock->outbuf->data, 
-                             dst_sock->outbuf->len,
-                             client->hmac_key, 
-                             client->hmac_key_len, 
-                             mac);
-      silc_buffer_put_tail(dst_sock->outbuf, mac, mac_len);
-      memset(mac, 0, sizeof(mac));
+       idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+       *clients = silc_buffer_realloc(*clients, 
+                                      (*clients ? 
+                                       (*clients)->truelen + idp->len : 
+                                       idp->len));
+       silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
+       silc_buffer_put(*clients, idp->data, idp->len);
+       silc_buffer_pull(*clients, idp->len);
+
+       SILC_PUT32_MSB(client->mode, mode);
+       tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+                                                2, idp->data, idp->len,
+                                                mode, 4);
+       *umodes = silc_buffer_realloc(*umodes, 
+                                     (*umodes ? 
+                                      (*umodes)->truelen + tmp->len :  
+                                      tmp->len));
+       silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+       silc_buffer_put(*umodes, tmp->data, tmp->len);
+       silc_buffer_pull(*umodes, tmp->len);
+       silc_buffer_free(tmp);
+
+       silc_buffer_free(idp);
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
     }
-    
-    /* Encrypt */
-    if (client && client->send_key)
-      silc_packet_encrypt(client->send_key, dst_sock->outbuf, 
-                         buffer->len);
-    
-    if (client->hmac)
-      silc_buffer_pull_tail(dst_sock->outbuf, mac_len);
-    
-    /* Send the packet */
-    silc_server_packet_send_real(server, dst_sock, FALSE);
-  } else {
-    /* Key exist so just send it */
-    silc_buffer_push(buffer, SILC_PACKET_HEADER_LEN + packet->src_id_len 
-                    + packet->dst_id_len + packet->padlen);
-    silc_server_packet_send_prepare(server, dst_sock, 0, 0, buffer->len);
-    silc_buffer_put(dst_sock->outbuf, buffer->data, buffer->len);
-    silc_server_packet_send_real(server, dst_sock, FALSE);
+
+    silc_idcache_list_free(list);
   }
 }
 
-/* Received private message. This resolves the destination of the message 
-   and sends the packet. This is used by both server and router.  If the
-   destination is our locally connected client this sends the packet to
-   the client. This may also send the message for further routing if
-   the destination is not in our server (or router). */
+/* This function is used to announce our existing clients to our router
+   when we've connected to it. If `creation_time' is non-zero then only
+   the clients that has been created after the `creation_time' will be
+   announced. */
 
-void silc_server_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientID *id;
-  SilcServerList *router;
-  SilcSocketConnection dst_sock;
-  SilcClientList *client;
+  SilcBuffer clients = NULL;
+  SilcBuffer umodes = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Announcing clients"));
 
-  if (!packet->dst_id) {
-    SILC_LOG_DEBUG(("Bad Client ID in private message packet"));
-    goto err;
-  }
+  /* Get clients in local list */
+  silc_server_announce_get_clients(server, server->local_list,
+                                  &clients, &umodes, creation_time);
 
-  /* Decode destination Client ID */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CLIENT);
-  if (!id) {
-    SILC_LOG_DEBUG(("Could not decode destination Client ID"));
-    goto err;
-  }
+  /* As router we announce our global list as well */
+  if (server->server_type == SILC_ROUTER)
+    silc_server_announce_get_clients(server, server->global_list,
+                                    &clients, &umodes, creation_time);
 
-  /* If the destination belongs to our server we don't have to route
-     the message anywhere but to send it to the local destination. */
-  /* XXX: Should use local cache to search but the current idcache system
-     is so sucky that it cannot be used... it MUST be rewritten! Using
-     this search is probably faster than if we'd use here the current
-     idcache system. */
-  client =  silc_idlist_find_client_by_id(server->local_list->clients, id);
-  if (client) {
-    /* It exists, now deliver the message to the destination */
-    dst_sock = (SilcSocketConnection)client->connection;
+  if (clients) {
+    silc_buffer_push(clients, clients->data - clients->head);
+    SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
 
-    /* If we are router and the client has router then the client is in
-       our cell but not directly connected to us. */
-    if (server->server_type == SILC_ROUTER && client->router) {
-      silc_server_private_message_send_internal(server, dst_sock,
-                                               client->router, packet);
-      goto out;
-    }
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
+                           clients->data, clients->len, TRUE);
 
-    /* Seems that client really is directly connected to us */
-    silc_server_private_message_send_local(server, dst_sock, client, packet);
-    goto out;
+    silc_buffer_free(clients);
   }
 
-  /* Destination belongs to someone not in this server. If we are normal
-     server our action is to send the packet to our router. */
-  if (server->server_type == SILC_SERVER && !server->standalone) {
-    router = server->id_entry->router;
-    dst_sock = (SilcSocketConnection)router->connection;
+  if (umodes) {
+    silc_buffer_push(umodes, umodes->data - umodes->head);
+    SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
 
-    /* Send to primary route */
-    silc_server_private_message_send_internal(server, dst_sock, router,
-                                             packet);
-    goto out;
-  }
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           umodes->data, umodes->len, TRUE);
 
-  /* We are router and we will perform route lookup for the destination 
-     and send the message to the correct route. */
-  if (server->server_type == SILC_ROUTER && !server->standalone) {
+    silc_buffer_free(umodes);
+  }
+}
 
-    /* If we don't have specific route for the destination we will send
-       it to our primary route (default route). */
-    router = silc_server_route_check(id->ip.s_addr, server->id->port);
-    if (router) {
-      dst_sock = (SilcSocketConnection)router->connection;
-    } else {
-      router = server->id_entry->router;
-      dst_sock = (SilcSocketConnection)router->connection;
-    }
+/* Returns channel's topic for announcing it */
 
-    /* Send packet */
-    silc_server_private_message_send_internal(server, dst_sock, 
-                                             router, packet);
-    goto out;
+void silc_server_announce_get_channel_topic(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *topic)
+{
+  SilcBuffer chidp;
+
+  if (channel->topic) {
+    chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+    *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+                                               chidp->data, chidp->len,
+                                               channel->topic, 
+                                               strlen(channel->topic));
+    silc_buffer_free(chidp);
   }
-
- err:
-  silc_server_send_error(server, sock, 
-                        "No such nickname: Private message not sent");
- out:
-  silc_buffer_free(buffer);
 }
 
-SilcChannelList *silc_find_channel(SilcServer server, SilcChannelID *id)
+/* Returns assembled packets for channel users of the `channel'. */
+
+void silc_server_announce_get_channel_users(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *channel_users,
+                                           SilcBuffer *channel_users_modes)
 {
-  int i;
-  SilcIDCache *id_cache;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer chidp, clidp;
+  SilcBuffer tmp;
+  int len;
+  unsigned char mode[4];
 
-#define LCC(x) server->local_list->channel_cache[(x)]
-#define LCCC(x) server->local_list->channel_cache_count[(x)]
+  SILC_LOG_DEBUG(("Start"));
 
-  for (i = 0; i < 96; i++) {
-    if (LCC(i) == NULL)
-      continue;
-    if (silc_idcache_find_by_id(LCC(i), LCCC(i), (void *)id, 
-                               SILC_ID_CHANNEL, &id_cache))
-      return (SilcChannelList *)id_cache->context;
+  /* Now find all users on the channel */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+
+    /* JOIN Notify */
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2, 
+                                            clidp->data, clidp->len,
+                                            chidp->data, chidp->len);
+    len = tmp->len;
+    *channel_users = 
+      silc_buffer_realloc(*channel_users, 
+                         (*channel_users ? 
+                          (*channel_users)->truelen + len : len));
+    silc_buffer_pull_tail(*channel_users, 
+                         ((*channel_users)->end - 
+                          (*channel_users)->data));
+    
+    silc_buffer_put(*channel_users, tmp->data, tmp->len);
+    silc_buffer_pull(*channel_users, len);
+    silc_buffer_free(tmp);
+
+    /* CUMODE notify for mode change on the channel */
+    SILC_PUT32_MSB(chl->mode, mode);
+    tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE, 
+                                            3, clidp->data, clidp->len,
+                                            mode, 4,
+                                            clidp->data, clidp->len);
+    len = tmp->len;
+    *channel_users_modes = 
+      silc_buffer_realloc(*channel_users_modes, 
+                         (*channel_users_modes ? 
+                          (*channel_users_modes)->truelen + len : len));
+    silc_buffer_pull_tail(*channel_users_modes, 
+                         ((*channel_users_modes)->end - 
+                          (*channel_users_modes)->data));
+    
+    silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
+    silc_buffer_pull(*channel_users_modes, len);
+    silc_buffer_free(tmp);
+
+    silc_buffer_free(clidp);
   }
-  
-  return NULL;
-#undef LCC
-#undef LCCC
+  silc_hash_table_list_reset(&htl);
+  silc_buffer_free(chidp);
 }
 
-/* Process received channel message. */
-
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet)
+/* Returns assembled packets for all channels and users on those channels
+   from the given ID List. The packets are in the form dictated by the
+   New Channel and New Channel User payloads. */
+
+void silc_server_announce_get_channels(SilcServer server,
+                                      SilcIDList id_list,
+                                      SilcBuffer *channels,
+                                      SilcBuffer *channel_users,
+                                      SilcBuffer **channel_users_modes,
+                                      uint32 *channel_users_modes_c,
+                                      SilcBuffer **channel_topics,
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time)
 {
-  SilcChannelList *channel = NULL;
-  SilcChannelID *id = NULL;
-  SilcClientID *sender;
-  SilcBuffer buffer = packet->buffer;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry channel;
+  unsigned char *cid;
+  uint32 id_len;
+  uint16 name_len;
+  int len;
+  int i = *channel_users_modes_c;
+  bool announce;
 
-  SILC_LOG_DEBUG(("Processing channel message"));
-  
-  /* Check MAC */
-  if (!silc_server_packet_check_mac(server, sock, buffer))
-    goto out;
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Sanity checks */
-  if (packet->dst_id_type != SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Received bad message for channel, dropped"));
-    SILC_LOG_DEBUG(("Received bad message for channel, dropped"));
-    goto out;
-  }
+  /* Go through all channels in the list */
+  if (silc_idcache_get_all(id_list->channels, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+
+       if (creation_time && channel->created < creation_time)
+         announce = FALSE;
+       else
+         announce = TRUE;
+
+       cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+       id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+       name_len = strlen(channel->channel_name);
+
+       if (announce) {
+         len = 4 + name_len + id_len + 4;
+         *channels = 
+           silc_buffer_realloc(*channels, 
+                               (*channels ? (*channels)->truelen + 
+                                len : len));
+         silc_buffer_pull_tail(*channels, 
+                               ((*channels)->end - (*channels)->data));
+         silc_buffer_format(*channels,
+                            SILC_STR_UI_SHORT(name_len),
+                            SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                                 name_len),
+                            SILC_STR_UI_SHORT(id_len),
+                            SILC_STR_UI_XNSTRING(cid, id_len),
+                            SILC_STR_UI_INT(channel->mode),
+                            SILC_STR_END);
+         silc_buffer_pull(*channels, len);
+       }
 
-  /* Send to local clients */
-  id = silc_id_str2id(packet->dst_id, SILC_ID_CHANNEL);
-  channel = silc_find_channel(server, id);
-  if (!channel) {
-    SILC_LOG_DEBUG(("Could not find channel"));
-    goto out;
-  }
+       /* Channel user modes */
+       *channel_users_modes = silc_realloc(*channel_users_modes,
+                                           sizeof(**channel_users_modes) * 
+                                           (i + 1));
+       (*channel_users_modes)[i] = NULL;
+       *channel_ids = silc_realloc(*channel_ids, 
+                                   sizeof(**channel_ids) * (i + 1));
+       (*channel_ids)[i] = NULL;
+       silc_server_announce_get_channel_users(server, channel,
+                                              channel_users,
+                                              &(*channel_users_modes)[i]);
+       (*channel_ids)[i] = channel->id;
+
+       /* Channel's topic */
+       *channel_topics = silc_realloc(*channel_topics,
+                                      sizeof(**channel_topics) * (i + 1));
+       (*channel_topics)[i] = NULL;
+       silc_server_announce_get_channel_topic(server, channel,
+                                              &(*channel_topics)[i]);
+       i++;
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
 
-  /* Distribute the packet to our local clients. This will send the
-     packet for further routing as well, if needed. */
-  sender = silc_id_str2id(packet->src_id, packet->src_id_type);
-  silc_server_packet_relay_to_channel(server, sock, channel, sender,
-                                     packet->src_id_type,
-                                     packet->buffer->data,
-                                     packet->buffer->len, FALSE);
+      *channel_users_modes_c += i;
+    }
 
- out:
-  silc_buffer_free(buffer);
+    silc_idcache_list_free(list);
+  }
 }
 
-/* Received channel key packet. We distribute the key to all of our locally
-   connected clients on the channel. Router ignores the packet. */
+/* This function is used to announce our existing channels to our router
+   when we've connected to it. This also announces the users on the
+   channels to the router. If the `creation_time' is non-zero only the
+   channels that was created after the `creation_time' are announced.
+   Note that the channel users are still announced even if the `creation_time'
+   was provided. */
 
-void silc_server_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet)
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time,
+                                  SilcSocketConnection remote)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcChannelKeyPayload payload = NULL;
-  SilcChannelID *id = NULL;
-  SilcChannelList *channel;
-  SilcClientList *client;
-  unsigned char *key;
-  unsigned int key_len;
-  char *cipher;
-  int i;
-
-  if (server->server_type == SILC_ROUTER)
-    goto out;
+  SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer *channel_users_modes = NULL;
+  SilcBuffer *channel_topics = NULL;
+  uint32 channel_users_modes_c = 0;
+  SilcChannelID **channel_ids = NULL;
+
+  SILC_LOG_DEBUG(("Announcing channels and channel users"));
+
+  /* Get channels and channel users in local list */
+  silc_server_announce_get_channels(server, server->local_list,
+                                   &channels, &channel_users,
+                                   &channel_users_modes,
+                                   &channel_users_modes_c,
+                                   &channel_topics,
+                                   &channel_ids, creation_time);
+
+  /* Get channels and channel users in global list */
+  if (server->server_type != SILC_SERVER)
+    silc_server_announce_get_channels(server, server->global_list,
+                                     &channels, &channel_users,
+                                     &channel_users_modes,
+                                     &channel_users_modes_c,
+                                     &channel_topics,
+                                     &channel_ids, creation_time);
+
+  if (channels) {
+    silc_buffer_push(channels, channels->data - channels->head);
+    SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
 
-  if (packet->src_id_type != SILC_ID_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
-    goto out;
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
+                           channels->data, channels->len,
+                           FALSE);
 
-  /* Decode channel key payload */
-  payload = silc_channel_key_parse_payload(buffer);
-  if (!payload) {
-    SILC_LOG_ERROR(("Bad channel key payload, dropped"));
-    SILC_LOG_DEBUG(("Bad channel key payload, dropped"));
+    silc_buffer_free(channels);
   }
 
-  /* Get channel ID */
-  id = silc_id_str2id(silc_channel_key_get_id(payload, NULL), SILC_ID_CHANNEL);
-  if (!id)
-    goto out;
+  if (channel_users) {
+    silc_buffer_push(channel_users, channel_users->data - channel_users->head);
+    SILC_LOG_HEXDUMP(("channel users"), channel_users->data, 
+                    channel_users->len);
 
-  /* Get the channel entry */
-  channel = silc_idlist_find_channel_by_id(server->local_list->channels, id);
-  if (!channel) {
-    SILC_LOG_ERROR(("Received key for non-existent channel"));
-    SILC_LOG_DEBUG(("Received key for non-existent channel"));
-    goto out;
+    /* Send the packet */
+    silc_server_packet_send(server, remote,
+                           SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                           channel_users->data, channel_users->len,
+                           FALSE);
+
+    silc_buffer_free(channel_users);
   }
 
-  /* Save the key for us as well */
-  key = silc_channel_key_get_key(payload, &key_len);
-  if (!key)
-    goto out;
-  cipher = silc_channel_key_get_cipher(payload, NULL);;
-  if (!cipher)
-    goto out;
-  channel->key_len = key_len;
-  channel->key = silc_calloc(key_len, sizeof(unsigned char));
-  memcpy(channel->key, key, key_len);
-  silc_cipher_alloc(cipher, &channel->channel_key);
-  channel->channel_key->cipher->set_key(channel->channel_key->context, 
-                                       key, key_len);
+  if (channel_users_modes) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_users_modes[i])
+        continue;
+      silc_buffer_push(channel_users_modes[i], 
+                      channel_users_modes[i]->data - 
+                      channel_users_modes[i]->head);
+      SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data, 
+                      channel_users_modes[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_users_modes[i]->data, 
+                                  channel_users_modes[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_users_modes[i]);
+    }
+    silc_free(channel_users_modes);
+  }
+
+  if (channel_topics) {
+    int i;
 
-  /* Distribute the key to all clients on the channel */
-  for (i = 0; i < channel->user_list_count; i++) {
-    client = channel->user_list[i].client;
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_topics[i])
+       continue;
 
-    if (client)
-      silc_server_packet_send_dest(server, client->connection,
-                                  SILC_PACKET_CHANNEL_KEY, 0,
-                                  client->id, SILC_ID_CLIENT,
-                                  buffer->data, buffer->len, FALSE);
+      silc_buffer_push(channel_topics[i], 
+                      channel_topics[i]->data - 
+                      channel_topics[i]->head);
+      SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data, 
+                      channel_topics[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_topics[i]->data, 
+                                  channel_topics[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_topics[i]);
+    }
+    silc_free(channel_topics);
   }
 
- out:
-  if (id)
-    silc_free(id);
-  if (payload)
-    silc_channel_key_free_payload(payload);
-  silc_buffer_free(buffer);
+  silc_free(channel_ids);
 }
 
-/* Sends error message. Error messages may or may not have any 
-   implications. */
+/* Failure timeout callback. If this is called then we will immediately
+   process the received failure. We always process the failure with timeout
+   since we do not want to blindly trust to received failure packets. 
+   This won't be called (the timeout is cancelled) if the failure was
+   bogus (it is bogus if remote does not close the connection after sending
+   the failure). */
 
-void silc_server_send_error(SilcServer server,
-                           SilcSocketConnection sock,
-                           const char *fmt, ...)
+SILC_TASK_CALLBACK(silc_server_failure_callback)
 {
-  va_list ap;
-  unsigned char buf[4096];
+  SilcServerFailureContext f = (SilcServerFailureContext)context;
 
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
+  if (f->sock->protocol) {
+    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+    silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
+  }
 
-  silc_server_packet_send(server, sock, SILC_PACKET_ERROR, 0, 
-                         buf, strlen(buf), FALSE);
+  silc_free(f);
 }
 
-/* Sends notify message */
+/* Assembles user list and users mode list from the `channel'. */
 
-void silc_server_send_notify(SilcServer server,
-                            SilcSocketConnection sock,
-                            const char *fmt, ...)
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     uint32 *user_count)
 {
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  silc_server_packet_send(server, sock, SILC_PACKET_NOTIFY, 0, 
-                         buf, strlen(buf), FALSE);
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer client_id_list;
+  SilcBuffer client_mode_list;
+  SilcBuffer idp;
+  uint32 list_count = 0, len = 0;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+    len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
+  silc_hash_table_list_reset(&htl);
+
+  client_id_list = silc_buffer_alloc(len);
+  client_mode_list = 
+    silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
+  silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
+  silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    /* Client ID */
+    idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
+    silc_buffer_put(client_id_list, idp->data, idp->len);
+    silc_buffer_pull(client_id_list, idp->len);
+    silc_buffer_free(idp);
+
+    /* Client's mode on channel */
+    SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+    silc_buffer_pull(client_mode_list, 4);
+
+    list_count++;
+  }
+  silc_hash_table_list_reset(&htl);
+  silc_buffer_push(client_id_list, 
+                  client_id_list->data - client_id_list->head);
+  silc_buffer_push(client_mode_list, 
+                  client_mode_list->data - client_mode_list->head);
+
+  *user_list = client_id_list;
+  *mode_list = client_mode_list;
+  *user_count = list_count;
 }
 
-/* Sends notify message to a channel. The notify message sent is 
-   distributed to all clients on the channel. */
+/* Saves users and their modes to the `channel'. */
 
-void silc_server_send_notify_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       const char *fmt, ...)
+void silc_server_save_users_on_channel(SilcServer server,
+                                      SilcSocketConnection sock,
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      uint32 user_count)
 {
-  va_list ap;
-  unsigned char buf[4096];
-
-  memset(buf, 0, sizeof(buf));
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  va_end(ap);
-
-  silc_server_packet_send_to_channel(server, channel, buf, 
-                                    strlen(buf), FALSE);
-}
+  int i;
+  uint16 idp_len;
+  uint32 mode;
+  SilcClientID *client_id;
+  SilcClientEntry client;
+  SilcIDCacheEntry cache;
+  bool global;
 
-/* Sends New ID Payload to remote end. The packet is used to distribute
-   information about new registered clients, servers, channel etc. usually
-   to routers so that they can keep these information up to date. 
-   If the argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-
-void silc_server_send_new_id(SilcServer server,
-                            SilcSocketConnection sock,
-                            int broadcast,
-                            void *id, SilcIdType id_type, 
-                            unsigned int id_len)
-{
-  SilcBuffer packet;
-  unsigned char *id_string;
+  SILC_LOG_DEBUG(("Start"));
 
-  id_string = silc_id_id2str(id, id_type);
-  if (!id_string)
-    return;
+  for (i = 0; i < user_count; i++) {
+    /* Client ID */
+    SILC_GET16_MSB(idp_len, user_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(user_list->data, idp_len);
+    silc_buffer_pull(user_list, idp_len);
+    if (!client_id)
+      continue;
 
-  packet = silc_buffer_alloc(2 + 2 + id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(id_type),
-                    SILC_STR_UI_SHORT(id_len),
-                    SILC_STR_UI_XNSTRING(id_string, id_len),
-                    SILC_STR_END);
+    /* Mode */
+    SILC_GET32_MSB(mode, mode_list->data);
+    silc_buffer_pull(mode_list, 4);
 
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(packet);
-}
+    if (noadd && SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
+      silc_free(client_id);
+      continue;
+    }
 
-/* Sends Replace ID payload to remote end. This is used to replace old
-   ID with new ID sent in the packet.  This is called for example when
-   user changes nickname and we create new ID for the user.  If the 
-   argument `broadcast' is TRUE then the packet is sent as
-   broadcast packet. */
-/* XXX It would be expected that the new id is same type as the old
-   ID. :) */
-
-void silc_server_send_replace_id(SilcServer server,
-                                SilcSocketConnection sock,
-                                int broadcast,
-                                void *old_id, SilcIdType old_id_type,
-                                unsigned int old_id_len,
-                                void *new_id, SilcIdType new_id_type,
-                                unsigned int new_id_len)
-{
-  SilcBuffer packet;
-  unsigned char *oid;
-  unsigned char *nid;
+    global = FALSE;
+    
+    /* Check if we have this client cached already. */
+    client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                          server->server_type, &cache);
+    if (!client) {
+      client = silc_idlist_find_client_by_id(server->global_list, 
+                                            client_id, server->server_type,
+                                            &cache);
+      global = TRUE;
+    }
+    if (!client) {
+      /* If router did not find such Client ID in its lists then this must
+        be bogus client or some router in the net is buggy. */
+      if (server->server_type == SILC_ROUTER) {
+       silc_free(client_id);
+       continue;
+      }
 
-  oid = silc_id_id2str(old_id, old_id_type);
-  if (!oid)
-    return;
+      /* We don't have that client anywhere, add it. The client is added
+        to global list since server didn't have it in the lists so it must be 
+        global. */
+      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
+                                     sock->user_data, NULL, 0);
+      if (!client) {
+       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
+       silc_free(client_id);
+       continue;
+      }
 
-  nid = silc_id_id2str(new_id, new_id_type);
-  if (!nid)
-    return;
+      client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+    } else {
+      /* Found, if it is from global list we'll assure that we won't
+        expire it now that the entry is on channel. */
+      if (global)
+       cache->expire = 0;
+    }
 
-  packet = silc_buffer_alloc(2 + 2 + 2 + 2 + old_id_len + new_id_len);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-  silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(old_id_type),
-                    SILC_STR_UI_SHORT(old_id_len),
-                    SILC_STR_UI_XNSTRING(oid, old_id_len),
-                    SILC_STR_UI_SHORT(new_id_type),
-                    SILC_STR_UI_SHORT(new_id_len),
-                    SILC_STR_UI_XNSTRING(nid, new_id_len),
-                    SILC_STR_END);
+    silc_free(client_id);
 
-  silc_server_packet_send(server, sock, SILC_PACKET_REPLACE_ID, 
-                         broadcast ? SILC_PACKET_FLAG_BROADCAST : 0, 
-                         packet->data, packet->len, FALSE);
-  silc_free(oid);
-  silc_free(nid);
-  silc_buffer_free(packet);
+    if (!silc_server_client_on_channel(client, channel)) {
+      /* Client was not on the channel, add it. */
+      SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+      chl->client = client;
+      chl->mode = mode;
+      chl->channel = channel;
+      silc_hash_table_add(channel->user_list, chl->client, chl);
+      silc_hash_table_add(client->channels, chl->channel, chl);
+    }
+  }
 }
 
-/* Creates new channel. */
+/* Lookups route to the client indicated by the `id_data'. The connection
+   object and internal data object is returned. Returns NULL if route
+   could not be found to the client. If the `client_id' is specified then
+   it is used and the `id_data' is ignored. */
 
-SilcChannelList *silc_server_new_channel(SilcServer server, 
-                                        SilcServerID *router_id,
-                                        char *cipher, char *channel_name)
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 uint32 id_len,
+                                                 SilcClientID *client_id,
+                                                 SilcIDListData *idata)
 {
-  int i, channel_len;
-  SilcChannelID *channel_id;
-  SilcChannelList *entry;
-  SilcCipher key;
-  unsigned char channel_key[32], *id_string;
-  SilcBuffer packet;
+  SilcClientID *id;
+  SilcClientEntry client;
 
-  SILC_LOG_DEBUG(("Creating new channel"));
+  SILC_LOG_DEBUG(("Start"));
 
-#define LCC(x) server->local_list->channel_cache[(x) - 32]
-#define LCCC(x) server->local_list->channel_cache_count[(x) - 32]
+  /* Decode destination Client ID */
+  if (!client_id) {
+    id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
+    if (!id) {
+      SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+      return NULL;
+    }
+  } else {
+    id = silc_id_dup(client_id, SILC_ID_CLIENT);
+  }
 
-  /* Create channel key */
-  for (i = 0; i < 32; i++)
-    channel_key[i] = silc_rng_get_byte(server->rng);
+  /* If the destination belongs to our server we don't have to route
+     the packet anywhere but to send it to the local destination. */
+  client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
+  if (client) {
+    silc_free(id);
 
-  if (!cipher)
-    cipher = "twofish";
+    /* If we are router and the client has router then the client is in
+       our cell but not directly connected to us. */
+    if (server->server_type == SILC_ROUTER && client->router) {
+      /* We are of course in this case the client's router thus the route
+        to the client is the server who owns the client. So, we will send
+        the packet to that server. */
+      if (idata)
+       *idata = (SilcIDListData)client->router;
+      return client->router->connection;
+    }
 
-  /* Allocate keys */
-  silc_cipher_alloc(cipher, &key);
-  key->cipher->set_key(key->context, channel_key, 16);
+    /* Seems that client really is directly connected to us */
+    if (idata)
+      *idata = (SilcIDListData)client;
+    return client->connection;
+  }
 
-  /* Create the channel */
-  silc_id_create_channel_id(router_id, server->rng, &channel_id);
-  silc_idlist_add_channel(&server->local_list->channels, channel_name, 
-                         SILC_CHANNEL_MODE_NONE, channel_id, NULL, /*XXX*/
-                         key, &entry);
-  LCCC(channel_name[0]) = silc_idcache_add(&LCC(channel_name[0]), 
-                                          LCCC(channel_name[0]),
-                                          channel_name, SILC_ID_CHANNEL, 
-                                          channel_id, (void *)entry);
-  entry->key = silc_calloc(16, sizeof(*entry->key));
-  entry->key_len = 16;
-  memcpy(entry->key, channel_key, 16);
-  memset(channel_key, 0, sizeof(channel_key));
+  /* Destination belongs to someone not in this server. If we are normal
+     server our action is to send the packet to our router. */
+  if (server->server_type != SILC_ROUTER && !server->standalone) {
+    silc_free(id);
+    if (idata)
+      *idata = (SilcIDListData)server->router;
+    return server->router->connection;
+  }
 
-  /* Notify other routers about the new channel. We send the packet
-     to our primary route. */
-  if (server->standalone == FALSE) {
-    channel_len = strlen(channel_name);
-    id_string = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
-    packet = silc_buffer_alloc(2 + SILC_ID_CHANNEL_LEN);
-
-    silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
-    silc_buffer_format(packet,
-                      SILC_STR_UI_SHORT(channel_len),
-                      SILC_STR_UI_XNSTRING(channel_name, channel_len),
-                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
-                      SILC_STR_UI_XNSTRING(id_string, SILC_ID_CHANNEL_LEN),
-                      SILC_STR_END);
+  /* We are router and we will perform route lookup for the destination 
+     and send the packet to fastest route. */
+  if (server->server_type == SILC_ROUTER && !server->standalone) {
+    /* Check first that the ID is valid */
+    client = silc_idlist_find_client_by_id(server->global_list, id, 
+                                          TRUE, NULL);
+    if (client) {
+      SilcSocketConnection dst_sock;
 
-    /* Send the packet to our router. */
-    silc_server_packet_send(server, (SilcSocketConnection) 
-                           server->id_entry->router->connection,
-                           SILC_PACKET_NEW_CHANNEL_USER, 0, 
-                           packet->data, packet->len, TRUE);
-    
-    silc_free(id_string);
-    silc_buffer_free(packet);
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+
+      silc_free(id);
+      if (idata)
+       *idata = (SilcIDListData)dst_sock->user_data;
+      return dst_sock;
+    }
   }
 
-#undef LCC
-#undef LCCC
-  return entry;
+  silc_free(id);
+  return NULL;
 }
 
-/* Create new client. This processes incoming NEW_CLIENT packet and creates
-   Client ID for the client and adds it to lists and cache. */
+/* Encodes and returns channel list of channels the `client' has joined.
+   Secret channels are not put to the list. */
 
-SilcClientList *silc_server_new_client(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+                                              SilcClientEntry client)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcClientList *id_entry;
-  char *username = NULL, *realname = NULL, *id_string;
-  SilcBuffer reply;
-
-  SILC_LOG_DEBUG(("Creating new client"));
-
-  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
-    return NULL;
-
-#define LCC(x) server->local_list->client_cache[(x) - 32]
-#define LCCC(x) server->local_list->client_cache_count[(x) - 32]
+  SilcBuffer buffer = NULL;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  unsigned char *cid;
+  uint32 id_len;
+  uint16 name_len;
+  int len;
+
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    channel = chl->channel;
+
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
+       channel->mode & SILC_CHANNEL_MODE_PRIVATE)
+      continue;
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&username),
-                      SILC_STR_UI16_STRING_ALLOC(&realname),
+    cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+    name_len = strlen(channel->channel_name);
+    
+    len = 4 + name_len + id_len + 4;
+    buffer = silc_buffer_realloc(buffer, 
+                                (buffer ? (buffer)->truelen + len : len));
+    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_SHORT(name_len),
+                      SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                           name_len),
+                      SILC_STR_UI_SHORT(id_len),
+                      SILC_STR_UI_XNSTRING(cid, id_len),
+                      SILC_STR_UI_INT(chl->mode), /* Client's mode */
                       SILC_STR_END);
+    silc_buffer_pull(buffer, len);
+    silc_free(cid);
+  }
+  silc_hash_table_list_reset(&htl);
 
-  /* Set the pointers to the client list and create new client ID */
-  id_entry = (SilcClientList *)sock->user_data;
-  id_entry->nickname = strdup(username);
-  id_entry->username = username;
-  id_entry->userinfo = realname;
-  silc_id_create_client_id(server->id, server->rng, server->md5hash,
-                          username, &id_entry->id);
-
-  /* Add to client cache */
-  LCCC(username[0]) = silc_idcache_add(&LCC(username[0]), 
-                                      LCCC(username[0]),
-                                      username, SILC_ID_CLIENT, 
-                                      id_entry->id, (void *)id_entry);
-
-  /* Notify our router about new client on the SILC network */
-  if (!server->standalone)
-    silc_server_send_new_id(server, (SilcSocketConnection) 
-                           server->id_entry->router->connection, 
-                           server->server_type == SILC_SERVER ? TRUE : FALSE,
-                           id_entry->id, SILC_ID_CLIENT, SILC_ID_CLIENT_LEN);
-  
-  /* Send the new client ID to the client. */
-  id_string = silc_id_id2str(id_entry->id, SILC_ID_CLIENT);
-  reply = silc_buffer_alloc(2 + 2 + SILC_ID_CLIENT_LEN);
-  silc_buffer_pull_tail(reply, SILC_BUFFER_END(reply));
-  silc_buffer_format(reply,
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT),
-                    SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
-                    SILC_STR_UI_XNSTRING(id_string, SILC_ID_CLIENT_LEN),
-                    SILC_STR_END);
-  silc_server_packet_send(server, sock, SILC_PACKET_NEW_ID, 0, 
-                         reply->data, reply->len, FALSE);
-  silc_free(id_string);
-  silc_buffer_free(reply);
-  
-  /* Send some nice info to the client */
-  silc_server_send_notify(server, sock, 
-                         "Welcome to the SILC Network %s@%s",
-                         username, 
-                         sock->hostname ? sock->hostname : sock->ip);
-  silc_server_send_notify(server, sock,
-                         "Your host is %s, running version %s",
-                         server->config->server_info->server_name,
-                         server_version);
-  silc_server_send_notify(server, sock, 
-                         "Your connection is secured with %s cipher, "
-                         "key length %d bits",
-                         id_entry->send_key->cipher->name,
-                         id_entry->send_key->cipher->key_len);
-  silc_server_send_notify(server, sock, 
-                         "Your current nickname is %s",
-                         id_entry->nickname);
-
-  /* XXX Send motd */
-
-#undef LCC
-#undef LCCC
-  return id_entry;
+  if (buffer)
+    silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
 }
 
-/* Create new server. This processes incoming NEW_SERVER packet and
-   saves the received Server ID. The server is our locally connected
-   server thus we save all the information and save it to local list. 
-   This funtion can be used by both normal server and router server.
-   If normal server uses this it means that its router has connected
-   to the server.  If router uses this it means that one of the cell's
-   servers is connected to the router. */
+/* Finds client entry by Client ID and if it is not found then resolves
+   it using WHOIS command. */
 
-SilcServerList *silc_server_new_server(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet)
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+                                              SilcClientID *client_id,
+                                              bool *resolved)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcServerList *id_entry;
-  unsigned char *server_name, *id_string;
+  SilcClientEntry client;
 
-  SILC_LOG_DEBUG(("Creating new server"));
+  if (resolved)
+    *resolved = FALSE;
 
-  if (sock->type != SILC_SOCKET_TYPE_SERVER &&
-      sock->type != SILC_SOCKET_TYPE_ROUTER)
+  client = silc_idlist_find_client_by_id(server->local_list, client_id,
+                                        TRUE, NULL);
+  if (!client) {
+    client = silc_idlist_find_client_by_id(server->global_list, 
+                                          client_id, TRUE, NULL);
+    if (!client && server->server_type == SILC_ROUTER)
+      return NULL;
+  }
+
+  if (!client && server->standalone)
     return NULL;
 
-#define LSC(x) server->local_list->server_cache[(x) - 32]
-#define LSCC(x) server->local_list->server_cache_count[(x) - 32]
+  if (!client || !client->nickname || !client->username) {
+    SilcBuffer buffer, idp;
 
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_UI16_STRING_ALLOC(&server_name),
-                      SILC_STR_END);
+    client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+    client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+    client->resolve_cmd_ident = ++server->cmd_ident;
 
-  /* Save ID and name */
-  id_entry = (SilcServerList *)sock->user_data;
-  id_entry->id = silc_id_str2id(id_string, SILC_ID_SERVER);
-  id_entry->server_name = server_name;
-  
-  /* Add to server cache */
-  LSCC(server_name[0]) = 
-    silc_idcache_add(&LSC(server_name[0]), 
-                    LSCC(server_name[0]),
-                    server_name, SILC_ID_SERVER, 
-                    id_entry->id, (void *)id_entry);
-
-  /* Distribute the information about new server in the SILC network
-     to our router. If we are normal server we won't send anything
-     since this connection must be our router connection. */
-  if (server->server_type == SILC_ROUTER && !server->standalone)
-    silc_server_send_new_id(server, server->id_entry->router->connection,
-                           TRUE, id_entry->id, SILC_ID_SERVER, 
-                           SILC_ID_SERVER_LEN);
+    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
+                                           server->cmd_ident, 1,
+                                           3, idp->data, idp->len);
+    silc_server_packet_send(server, client ? client->router->connection :
+                           server->router->connection,
+                           SILC_PACKET_COMMAND, 0,
+                           buffer->data, buffer->len, FALSE);
+    silc_buffer_free(idp);
+    silc_buffer_free(buffer);
 
-  silc_free(id_string);
+    if (resolved)
+      *resolved = TRUE;
+
+    return NULL;
+  }
 
-#undef LSC
-#undef LSCC
-  return id_entry;
+  return client;
 }
 
-/* Processes incoming New ID Payload. New ID Payload is used to distribute
-   information about newly registered clients, servers and created 
-   channels. */
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
 
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet)
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
 {
-  SilcBuffer buffer = packet->buffer;
-  SilcIdType id_type;
-  unsigned char *id_string;
-  void *id;
-
-  SILC_LOG_DEBUG(("Processing new ID"));
-
-  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      server->server_type == SILC_SERVER)
-    return;
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&id_type),
-                      SILC_STR_UI16_STRING_ALLOC(&id_string),
-                      SILC_STR_END);
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcIDListData idata = (SilcIDListData)sock->user_data;
+  SilcServer server = (SilcServer)idata->rekey->context;
+  SilcProtocol protocol;
+  SilcServerRekeyInternalContext *proto_ctx;
 
-  /* Normal server cannot have other normal server connections */
-  if (id_type == SILC_ID_SERVER && sock->type == SILC_SOCKET_TYPE_SERVER)
-    goto out;
+  SILC_LOG_DEBUG(("Start"));
 
-  id = silc_id_str2id(id_string, id_type);
-  if (!id)
-    goto out;
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = (void *)server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = FALSE;
+  proto_ctx->pfs = idata->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY, 
+                     &protocol, proto_ctx, silc_server_rekey_final);
+  sock->protocol = protocol;
+      
+  /* Run the protocol */
+  silc_protocol_execute(protocol, server->schedule, 0, 0);
 
-  /* XXX Do check whether the packet is coming outside the cell or
-     from someone inside the cell.  If outside use global lists otherwise
-     local lists. */
-  /* XXX If using local list set the idlist->connection to the sender's
-     socket connection as it is used in packet sending */
+  /* Re-register re-key timeout */
+  silc_schedule_task_add(server->schedule, sock->sock, 
+                        silc_server_rekey_callback,
+                        context, idata->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
 
-  switch(id_type) {
-  case SILC_ID_CLIENT:
-    {
-      SilcClientList *idlist;
-
-      /* Add the client to our local list. We are router and we keep
-        cell specific local database of all clients in the cell. */
-      silc_idlist_add_client(&server->local_list->clients, NULL, NULL, NULL,
-                            id, sock->user_data, NULL, NULL, 
-                            NULL, NULL, &idlist);
-      idlist->connection = sock;
-    }
-    break;
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
 
-  case SILC_ID_SERVER:
-    {
-      SilcServerList *idlist;
-
-      /* Add the server to our local list. We are router and we keep
-        cell specific local database of all servers in the cell. */
-      silc_idlist_add_server(&server->local_list->servers, NULL, 0,
-                            id, server->id_entry, NULL, NULL, 
-                          NULL, NULL, &idlist);
-      idlist->connection = sock;
-    }
-    break;
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerRekeyInternalContext *ctx =
+    (SilcServerRekeyInternalContext *)protocol->context;
+  SilcServer server = (SilcServer)ctx->server;
+  SilcSocketConnection sock = ctx->sock;
 
-  case SILC_ID_CHANNEL:
-    /* Add the channel to our local list. We are router and we keep
-       cell specific local database of all channels in the cell. */
-    silc_idlist_add_channel(&server->local_list->channels, NULL, 0,
-                           id, server->id_entry, NULL, NULL);
-    break;
+  SILC_LOG_DEBUG(("Start"));
 
-  default:
-    goto out;
-    break;
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    SILC_LOG_ERROR(("Error occurred during rekey protocol"));
+    silc_protocol_cancel(protocol, server->schedule);
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_free(ctx);
+    return;
   }
 
- out:
-  silc_free(id_string);
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_server_packet_queue_purge(server, sock);
+
+  /* Cleanup */
+  silc_protocol_free(protocol);
+  sock->protocol = NULL;
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_free(ctx);
 }
index ebe661c92ed7c860ba7bfe4dea44d90ab370516e..65ab966e51115a25809f265c020d6ecedb4d428c 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 /* Forward declaration for SILC Server object. The actual object is
    defined in internal header file for server routines. I want to keep
    the object private hence this declaration. */
-typedef struct SilcServerObjectStruct *SilcServer;
+typedef struct SilcServerStruct *SilcServer;
 
-#define SILC_SERVER_MAX_CONNECTIONS 10000
+/* Forward declaration of backup server context */
+typedef struct SilcServerBackupStruct *SilcServerBackup;
+
+#define SILC_SERVER_MAX_CONNECTIONS 1000
 
 /* General definitions */
 
+/* SILC port */
+#define SILC_PORT 768;
+
+/* Server and router. Used internally by the code. */
 #define SILC_SERVER 0
 #define SILC_ROUTER 1
+#define SILC_BACKUP_ROUTER 2
+
+/* Connection retry timeout. We implement exponential backoff algorithm
+   in connection retry. The interval of timeout grows when retry count
+   grows. */
+#define SILC_SERVER_RETRY_COUNT        7        /* Max retry count */
+#define SILC_SERVER_RETRY_MULTIPLIER   2        /* Interval growth */
+#define SILC_SERVER_RETRY_RANDOMIZER   2        /* timeout += rnd % 2 */
+#define SILC_SERVER_RETRY_INTERVAL_MIN 10       /* Min retry timeout */
+#define SILC_SERVER_RETRY_INTERVAL_MAX 600      /* Max generated timeout */
+
+/* 
+   Silc Server Params.
+
+   Structure to hold various default parameters for server that can be
+   given before running the server. 
+
+*/
+typedef struct {
+  uint32 retry_count;
+  uint32 retry_interval_min;
+  uint32 retry_interval_min_usec;
+  uint32 retry_interval_max;
+  char retry_keep_trying;
+
+  uint32 protocol_timeout;
+  uint32 protocol_timeout_usec;
+
+  char require_reverse_mapping;
+} *SilcServerParams;
+
+/* Callback function that is called after the key exchange and connection
+   authentication protocols has been completed with a remote router. The
+   `server_entry' is the remote router entry. */
+typedef void (*SilcServerConnectRouterCallback)(SilcServer server,
+                                               SilcServerEntry server_entry,
+                                               void *context);
+
+typedef struct {
+  SilcSocketConnection sock;
+
+  /* Remote host name and port */
+  char *remote_host;
+  int remote_port;
+  bool backup;
+  char *backup_replace_ip;
+  int backup_replace_port;
+  bool no_reconnect;
+  
+  /* Current connection retry info */
+  uint32 retry_count;
+  uint32 retry_timeout;
+
+  /* Back pointer to server */
+  SilcServer server;
+
+  SilcServerConnectRouterCallback callback;
+  void *callback_context;
+} *SilcServerConnection;
+
+/* Macros */
+
+/* This macro is used to send notify messages with formatted string. The
+   string is formatted with arguments and the formatted string is sent as
+   argument. */
+#define SILC_SERVER_SEND_NOTIFY(server, sock, type, fmt)       \
+do {                                                           \
+  char *__fmt__ = silc_format fmt;                             \
+  silc_server_send_notify(server, sock, FALSE,                         \
+                         type, 1, __fmt__, strlen(__fmt__));   \
+  silc_free(__fmt__);                                          \
+} while(0);
+
+/* Check whether rekey protocol is active */
+#define SILC_SERVER_IS_REKEY(sock)                                     \
+  (sock->protocol && sock->protocol->protocol &&                       \
+   sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)
 
 /* Prototypes */
 int silc_server_alloc(SilcServer *new_server);
 void silc_server_free(SilcServer server);
 int silc_server_init(SilcServer server);
+void silc_server_daemonise(SilcServer server);
+void silc_server_drop(SilcServer server);
 void silc_server_run(SilcServer server);
 void silc_server_stop(SilcServer server);
-void silc_server_packet_send(SilcServer server,
-                            SilcSocketConnection sock, 
-                            SilcPacketType type, 
-                            SilcPacketFlags flags,
-                            unsigned char *data, 
-                            unsigned int data_len,
-                            int force_send);
-void silc_server_packet_send_dest(SilcServer server,
-                                 SilcSocketConnection sock, 
-                                 SilcPacketType type, 
-                                 SilcPacketFlags flags,
-                                 void *dst_id,
-                                 SilcIdType dst_id_type,
-                                 unsigned char *data, 
-                                 unsigned int data_len,
-                                 int force_send);
-void silc_server_packet_forward(SilcServer server,
-                               SilcSocketConnection sock,
-                               unsigned char *data, unsigned int data_len,
-                               int force_send);
-void silc_server_packet_send_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       unsigned char *data,
-                                       unsigned int data_len,
-                                       int force_send);
-void silc_server_packet_relay_to_channel(SilcServer server,
-                                        SilcSocketConnection sender_sock,
-                                        SilcChannelList *channel,
-                                        void *sender, 
-                                        SilcIdType sender_type,
-                                        unsigned char *data,
-                                        unsigned int data_len,
-                                        int force_send);
-void silc_server_packet_relay_command_reply(SilcServer server,
-                                           SilcSocketConnection sock,
-                                           SilcPacketContext *packet);
+void silc_server_start_key_exchange(SilcServer server,
+                                   SilcServerConnection sconn,
+                                   int sock);
+bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
+                             void *context);
+void silc_server_packet_parse_type(SilcServer server, 
+                                  SilcSocketConnection sock,
+                                  SilcPacketContext *packet);
+void silc_server_create_connection(SilcServer server,
+                                  const char *remote_host, uint32 port);
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock);
+void silc_server_free_client_data(SilcServer server, 
+                                 SilcSocketConnection sock,
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 const char *signoff);
 void silc_server_free_sock_user_data(SilcServer server, 
-                                    SilcSocketConnection sock);
+                                    SilcSocketConnection sock,
+                                    const char *signoff_message);
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
-                                     SilcClientList *client);
+                                     SilcClientEntry client,
+                                     int notify,
+                                     char *signoff_message,
+                                     int keygen);
+int silc_server_remove_from_one_channel(SilcServer server, 
+                                       SilcSocketConnection sock,
+                                       SilcChannelEntry channel,
+                                       SilcClientEntry client,
+                                       int notify);
 void silc_server_disconnect_remote(SilcServer server,
                                   SilcSocketConnection sock,
                                   const char *fmt, ...);
-void silc_server_private_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet);
-void silc_server_channel_message(SilcServer server,
-                                SilcSocketConnection sock,
-                                SilcPacketContext *packet);
-void silc_server_channel_key(SilcServer server,
-                            SilcSocketConnection sock,
-                            SilcPacketContext *packet);
-void silc_server_send_error(SilcServer server,
-                           SilcSocketConnection sock,
-                           const char *fmt, ...);
-void silc_server_send_notify(SilcServer server,
-                            SilcSocketConnection sock,
-                            const char *fmt, ...);
-void silc_server_send_notify_to_channel(SilcServer server,
-                                       SilcChannelList *channel,
-                                       const char *fmt, ...);
-void silc_server_send_new_id(SilcServer server,
-                            SilcSocketConnection sock,
-                            int broadcast,
-                            void *id, SilcIdType id_type, 
-                            unsigned int id_len);
-void silc_server_send_replace_id(SilcServer server,
-                                SilcSocketConnection sock,
-                                int broadcast,
-                                void *old_id, SilcIdType old_id_type,
-                                unsigned int old_id_len,
-                                void *new_id, SilcIdType new_id_type,
-                                unsigned int new_id_len);
-SilcChannelList *silc_server_new_channel(SilcServer server, 
-                                        SilcServerID *router_id,
-                                        char *cipher, char *channel_name);
-SilcClientList *silc_server_new_client(SilcServer server,
-                                      SilcSocketConnection sock,
-                                      SilcPacketContext *packet);
-SilcServerList *silc_server_new_server(SilcServer server,
+SilcChannelEntry silc_server_create_new_channel(SilcServer server, 
+                                               SilcServerID *router_id,
+                                               char *cipher, 
+                                               char *hmac,
+                                               char *channel_name,
+                                               int broadcast);
+SilcChannelEntry 
+silc_server_create_new_channel_with_id(SilcServer server, 
+                                      char *cipher, 
+                                      char *hmac,
+                                      char *channel_name,
+                                      SilcChannelID *channel_id,
+                                      int broadcast);
+bool silc_server_create_channel_key(SilcServer server, 
+                                   SilcChannelEntry channel,
+                                   uint32 key_len);
+SilcChannelEntry silc_server_save_channel_key(SilcServer server,
+                                             SilcBuffer key_payload,
+                                             SilcChannelEntry channel);
+void silc_server_perform_heartbeat(SilcSocketConnection sock,
+                                  void *hb_context);
+void silc_server_announce_get_channel_topic(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *topic);
+void silc_server_announce_get_channel_users(SilcServer server,
+                                           SilcChannelEntry channel,
+                                           SilcBuffer *channel_users,
+                                           SilcBuffer *channel_users_modes);
+void silc_server_announce_get_channels(SilcServer server,
+                                      SilcIDList id_list,
+                                      SilcBuffer *channels,
+                                      SilcBuffer *channel_users,
+                                      SilcBuffer **channel_users_modes,
+                                      uint32 *channel_users_modes_c,
+                                      SilcBuffer **channel_topics,
+                                      SilcChannelID ***channel_ids,
+                                      unsigned long creation_time);
+void silc_server_announce_servers(SilcServer server, bool global,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote);
+void silc_server_announce_clients(SilcServer server,
+                                 unsigned long creation_time,
+                                 SilcSocketConnection remote);
+void silc_server_announce_channels(SilcServer server,
+                                  unsigned long creation_time,
+                                  SilcSocketConnection remote);
+void silc_server_get_users_on_channel(SilcServer server,
+                                     SilcChannelEntry channel,
+                                     SilcBuffer *user_list,
+                                     SilcBuffer *mode_list,
+                                     uint32 *user_count);
+void silc_server_save_users_on_channel(SilcServer server,
                                       SilcSocketConnection sock,
-                                      SilcPacketContext *packet);
-void silc_server_new_id(SilcServer server, SilcSocketConnection sock,
-                       SilcPacketContext *packet);
+                                      SilcChannelEntry channel,
+                                      SilcClientID *noadd,
+                                      SilcBuffer user_list,
+                                      SilcBuffer mode_list,
+                                      uint32 user_count);
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 uint32 id_len,
+                                                 SilcClientID *client_id,
+                                                 SilcIDListData *idata);
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+                                              SilcClientEntry client);
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+                                              SilcClientID *client_id,
+                                              bool *resolved);
 
 #endif
diff --git a/apps/silcd/server_backup.c b/apps/silcd/server_backup.c
new file mode 100644 (file)
index 0000000..6b3d876
--- /dev/null
@@ -0,0 +1,1240 @@
+/*
+
+  server_backup.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+SILC_TASK_CALLBACK(silc_server_protocol_backup_done);
+
+/* Backup router */
+typedef struct {
+  SilcServerEntry server;
+  SilcIDIP ip;
+  uint16 port;
+  bool local;
+} SilcServerBackupEntry;
+
+/* Holds IP address and port of the primary router that was replaced
+   by backup router. */
+typedef struct {
+  SilcIDIP ip;
+  uint16 port;
+  SilcServerEntry server;      /* Backup router that replaced the primary */
+} SilcServerBackupReplaced;
+
+/* Backup context */
+struct SilcServerBackupStruct {
+  SilcServerBackupEntry *servers;
+  uint32 servers_count;
+  SilcServerBackupReplaced **replaced;
+  uint32 replaced_count;
+};
+
+typedef struct {
+  uint8 session;
+  bool connected;
+  SilcServerEntry server_entry;
+} SilcServerBackupProtocolSession;
+
+/* Backup resuming protocol context  */
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  bool responder;
+  uint8 type;
+  uint8 session;
+  SilcServerBackupProtocolSession *sessions;
+  uint32 sessions_count;
+  long start;
+} *SilcServerBackupProtocolContext;
+
+/* Adds the `backup_server' to be one of our backup router. This can be
+   called multiple times to set multiple backup routers. The `ip' and `port'
+   is the IP and port that the `backup_router' will replace if the `ip'
+   will become unresponsive. If `local' is TRUE then the `backup_server' is
+   in the local cell, if FALSE it is in some other cell. */
+
+void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
+                           const char *ip, int port, bool local)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!ip)
+    return;
+
+  if (!server->backup)
+    server->backup = silc_calloc(1, sizeof(*server->backup));
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (!server->backup->servers[i].server) {
+      server->backup->servers[i].server = backup_server;
+      server->backup->servers[i].local = local;
+      memset(server->backup->servers[i].ip.data, 0,
+            sizeof(server->backup->servers[i].ip.data));
+      silc_net_addr2bin(ip, server->backup->servers[i].ip.data,
+                       sizeof(server->backup->servers[i].ip.data));
+      //server->backup->servers[i].port = port;
+      return;
+    }
+  }
+
+  i = server->backup->servers_count;
+  server->backup->servers = silc_realloc(server->backup->servers,
+                                        sizeof(*server->backup->servers) *
+                                        (i + 1));
+  server->backup->servers[i].server = backup_server;
+  server->backup->servers[i].local = local;
+  memset(server->backup->servers[i].ip.data, 0,
+        sizeof(server->backup->servers[i].ip.data));
+  silc_net_addr2bin(ip, server->backup->servers[i].ip.data,
+                   sizeof(server->backup->servers[i].ip.data));
+  //server->backup->servers[i].port = server_id->port;
+  server->backup->servers_count++;
+}
+
+/* Returns backup router for IP and port in `replacing' or NULL if there
+   does not exist backup router. */
+
+SilcServerEntry silc_server_backup_get(SilcServer server, 
+                                      SilcServerID *server_id)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!server->backup)
+    return NULL;
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    SILC_LOG_HEXDUMP(("IP"), server_id->ip.data, 16);
+    SILC_LOG_HEXDUMP(("IP"), server->backup->servers[i].ip.data, 16);
+    if (server->backup->servers[i].server &&
+       !memcmp(&server->backup->servers[i].ip, &server_id->ip.data,
+               sizeof(server_id->ip.data)))
+      return server->backup->servers[i].server;
+  }
+
+  return NULL;
+}
+
+/* Deletes the backup server `server_entry'. */
+void silc_server_backup_del(SilcServer server, SilcServerEntry server_entry)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!server->backup)
+    return ;
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    if (server->backup->servers[i].server == server_entry) {
+      server->backup->servers[i].server = NULL;
+      return;
+    }
+  }
+}
+
+/* Marks the IP address and port from the `server_id' as  being replaced
+   by backup router indicated by the `server'. If the router connects at
+   a later time we can check whether it has been replaced by an backup
+   router. */
+
+void silc_server_backup_replaced_add(SilcServer server, 
+                                    SilcServerID *server_id,
+                                    SilcServerEntry server_entry)
+{
+  int i;
+  SilcServerBackupReplaced *r = silc_calloc(1, sizeof(*r));;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!server->backup)
+    server->backup = silc_calloc(1, sizeof(*server->backup));
+  if (!server->backup->replaced) {
+    server->backup->replaced = 
+      silc_calloc(1, sizeof(*server->backup->replaced));
+    server->backup->replaced_count = 1;
+  }
+
+  SILC_LOG_DEBUG(("********************************"));
+  SILC_LOG_DEBUG(("Replaced added"));
+
+  memcpy(&r->ip, &server_id->ip, sizeof(server_id->ip));
+  //r->port = server_id->port;
+  r->server = server_entry;
+
+  for (i = 0; i < server->backup->replaced_count; i++) {
+    if (!server->backup->replaced[i]) {
+      server->backup->replaced[i] = r;
+      return;
+    }
+  }
+
+  i = server->backup->replaced_count;
+  server->backup->replaced = silc_realloc(server->backup->replaced,
+                                         sizeof(*server->backup->replaced) *
+                                         (i + 1));
+  server->backup->replaced[i] = r;
+  server->backup->replaced_count++;
+}
+
+/* Checks whether the IP address and port from the `server_id' has been
+   replaced by an backup router. If it has been then this returns TRUE
+   and the bacup router entry to the `server' pointer if non-NULL. Returns
+   FALSE if the router is not replaced by backup router. */
+
+bool silc_server_backup_replaced_get(SilcServer server,
+                                    SilcServerID *server_id,
+                                    SilcServerEntry *server_entry)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_LOG_DEBUG(("*************************************"));
+
+  if (!server->backup || !server->backup->replaced)
+    return FALSE;
+
+  for (i = 0; i < server->backup->replaced_count; i++) {
+    if (!server->backup->replaced[i])
+      continue;
+    SILC_LOG_HEXDUMP(("IP"), server_id->ip.data, server_id->ip.data_len);
+    SILC_LOG_HEXDUMP(("IP"), server->backup->replaced[i]->ip.data, 
+                    server->backup->replaced[i]->ip.data_len);
+    if (!memcmp(&server->backup->replaced[i]->ip, &server_id->ip.data,
+               sizeof(server_id->ip.data))) {
+      if (server_entry)
+       *server_entry = server->backup->replaced[i]->server;
+      SILC_LOG_DEBUG(("REPLACED"));
+      return TRUE;
+    }
+  }
+
+  SILC_LOG_DEBUG(("NOT REPLACED"));
+  return FALSE;
+}
+
+/* Deletes a replaced host by the set `server_entry. */
+
+void silc_server_backup_replaced_del(SilcServer server,
+                                    SilcServerEntry server_entry)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!server->backup || !server->backup->replaced)
+    return;
+
+  for (i = 0; i < server->backup->replaced_count; i++) {
+    if (!server->backup->replaced[i])
+      continue;
+    if (server->backup->replaced[i]->server == server_entry) {
+      silc_free(server->backup->replaced[i]);
+      server->backup->replaced[i] = NULL;
+      return;
+    }
+  }
+}
+
+/* Broadcast the received packet indicated by `packet' to all of our backup 
+   routers. All router wide information is passed using broadcast packets. 
+   That is why all backup routers need to get this data too. It is expected
+   that the caller already knows that the `packet' is broadcast packet. */
+
+void silc_server_backup_broadcast(SilcServer server, 
+                                 SilcSocketConnection sender,
+                                 SilcPacketContext *packet)
+{
+  SilcServerEntry backup;
+  SilcSocketConnection sock;
+  SilcBuffer buffer;
+  SilcIDListData idata;
+  int i;
+
+  if (!server->backup || server->server_type != SILC_ROUTER)
+    return;
+
+  SILC_LOG_DEBUG(("Broadcasting received packet to backup routers"));
+
+  buffer = packet->buffer;
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    backup = server->backup->servers[i].server;
+
+    if (!backup || backup->connection == sender || 
+       server->backup->servers[i].local == FALSE)
+      continue;
+
+    idata = (SilcIDListData)backup;
+    sock = backup->connection;
+
+    silc_packet_send_prepare(sock, 0, 0, buffer->len); 
+    silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+    silc_packet_encrypt(idata->send_key, idata->hmac_send, idata->psn_send++, 
+                       sock->outbuf, sock->outbuf->len);
+
+    SILC_LOG_HEXDUMP(("Broadcasted packet, len %d", sock->outbuf->len),
+                    sock->outbuf->data, sock->outbuf->len);
+
+    /* Now actually send the packet */
+    silc_server_packet_send_real(server, sock, FALSE);
+  }
+}
+
+/* A generic routine to send data to all backup routers. If the `sender'
+   is provided it will indicate the original sender of the packet and the
+   packet won't be resent to that entity. The `data' is the data that will
+   be assembled to packet context before sending. The packet will be
+   encrypted this function. If the `force_send' is TRUE the data is sent
+   immediately and not put to queue. If `local' is TRUE then the packet
+   will be sent only to local backup routers inside the cell. If false the
+   packet can go from one cell to the other. This function has no effect
+   if there are no any backup routers. */
+
+void silc_server_backup_send(SilcServer server,
+                            SilcServerEntry sender,
+                            SilcPacketType type,
+                            SilcPacketFlags flags,
+                            unsigned char *data,
+                            uint32 data_len,
+                            bool force_send,
+                            bool local)
+{
+  SilcServerEntry backup;
+  SilcSocketConnection sock;
+  int i;
+
+  if (!server->backup || server->server_type != SILC_ROUTER)
+    return;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    backup = server->backup->servers[i].server;
+    if (!backup)
+      continue;
+
+    if (sender == backup)
+      continue;
+
+    if (local && server->backup->servers[i].local == FALSE)
+      continue;
+
+    sock = backup->connection;
+    silc_server_packet_send(server, backup->connection, type, flags, 
+                           data, data_len, force_send);
+  }
+}
+
+/* Same as silc_server_backup_send but sets a specific Destination ID to
+   the packet. The Destination ID is indicated by the `dst_id' and the
+   ID type `dst_id_type'. For example, packets destined to channels must
+   be sent using this function. */
+
+void silc_server_backup_send_dest(SilcServer server,
+                                 SilcServerEntry sender,
+                                 SilcPacketType type,
+                                 SilcPacketFlags flags,
+                                 void *dst_id,
+                                 SilcIdType dst_id_type,
+                                 unsigned char *data,
+                                 uint32 data_len,
+                                 bool force_send,
+                                 bool local)
+{
+  SilcServerEntry backup;
+  SilcSocketConnection sock;
+  int i;
+
+  if (!server->backup || server->server_type != SILC_ROUTER)
+    return;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  for (i = 0; i < server->backup->servers_count; i++) {
+    backup = server->backup->servers[i].server;
+    if (!backup)
+      continue;
+
+    if (sender == backup)
+      continue;
+
+    if (local && server->backup->servers[i].local == FALSE)
+      continue;
+
+    sock = backup->connection;
+    silc_server_packet_send_dest(server, backup->connection, type, flags, 
+                                dst_id, dst_id_type, data, data_len, 
+                                force_send);
+  }
+}
+
+/* Processes incoming RESUME_ROUTER packet. This can give the packet
+   for processing to the protocol handler or allocate new protocol if
+   start command is received. */
+
+void silc_server_backup_resume_router(SilcServer server, 
+                                     SilcSocketConnection sock, 
+                                     SilcPacketContext *packet)
+{
+  uint8 type, session;
+  SilcServerBackupProtocolContext ctx;
+  int i, ret;
+
+  if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
+      sock->type == SILC_SOCKET_TYPE_UNKNOWN)
+    return;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_UI_CHAR(&session),
+                            SILC_STR_END);
+  if (ret < 0)
+    return;
+  
+  /* Activate the protocol for this socket if necessary */
+  if ((type == SILC_SERVER_BACKUP_RESUMED || 
+      type == SILC_SERVER_BACKUP_RESUMED_GLOBAL) &&
+      sock->type == SILC_SOCKET_TYPE_ROUTER && !sock->protocol && 
+      ((SilcIDListData)sock->user_data)->status & 
+      SILC_IDLIST_STATUS_DISABLED) {
+    SilcServerEntry backup_router;
+
+    if (silc_server_backup_replaced_get(server, 
+                                       ((SilcServerEntry)sock->
+                                        user_data)->id, 
+                                       &backup_router)) {
+      SilcSocketConnection bsock = 
+       (SilcSocketConnection)backup_router->connection;
+      if (bsock->protocol && bsock->protocol->protocol &&
+         bsock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
+       sock->protocol = bsock->protocol;
+       ctx = sock->protocol->context;
+       ctx->sock = sock;
+      }
+    }
+  }
+
+  /* If the backup resuming protocol is active then process the packet
+     in the protocol. */
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_BACKUP) {
+    ctx = sock->protocol->context;
+    ctx->type = type;
+
+    SILC_LOG_DEBUG(("********************************"));
+    SILC_LOG_DEBUG(("Continuing protocol, type %d", type));
+
+    if (type != SILC_SERVER_BACKUP_RESUMED &&
+       type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
+      for (i = 0; i < ctx->sessions_count; i++) {
+       if (session == ctx->sessions[i].session) {
+         ctx->session = session;
+         silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+         return;
+       }
+      }
+    } else {
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      return;
+    }
+
+    SILC_LOG_DEBUG(("Bad resume router packet"));
+    return;
+  }
+
+  /* We don't have protocol active. If we are router and the packet is 
+     coming from our primary router then lets check whether it means we've
+     been replaced by an backup router in my cell. This is usually received
+     immediately after we've connected to our primary router. */
+
+  if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
+      server->router == sock->user_data &&
+      type == SILC_SERVER_BACKUP_REPLACED) {
+    /* We have been replaced by an backup router in our cell. We must
+       mark our primary router connection disabled since we are not allowed
+       to use it at this moment. */
+    SilcIDListData idata = (SilcIDListData)sock->user_data;
+
+    SILC_LOG_INFO(("We are replaced by an backup router in this cell, will "
+                  "wait until backup resuming protocol is executed"));
+
+    SILC_LOG_DEBUG(("We are replaced by an backup router in this cell"));
+    idata->status |= SILC_IDLIST_STATUS_DISABLED;
+    return;
+  }
+
+  if (type == SILC_SERVER_BACKUP_START ||
+      type == SILC_SERVER_BACKUP_START_GLOBAL) {
+    /* We have received a start for resuming protocol. */
+    SilcServerBackupProtocolContext proto_ctx;
+
+    proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+    proto_ctx->server = server;
+    proto_ctx->sock = sock;
+    proto_ctx->responder = TRUE;
+    proto_ctx->type = type;
+    proto_ctx->session = session;
+    proto_ctx->start = time(0);
+
+    SILC_LOG_DEBUG(("Starting backup resuming protocol as responder"));
+
+    /* Run the backup resuming protocol */
+    silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
+                       &sock->protocol, proto_ctx, 
+                       silc_server_protocol_backup_done);
+    silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+  }
+}
+
+/* Timeout task callback to connect to remote router */
+
+SILC_TASK_CALLBACK(silc_server_backup_connect_to_router)
+{
+  SilcServerConnection sconn = (SilcServerConnection)context;
+  SilcServer server = sconn->server;
+  int sock;
+
+  SILC_LOG_DEBUG(("Connecting to router %s:%d", sconn->remote_host,
+                 sconn->remote_port));
+
+  /* Connect to remote host */
+  sock = silc_net_create_connection(server->config->server_info->server_ip,
+                                   sconn->remote_port,
+                                   sconn->remote_host);
+  if (sock < 0) {
+    silc_schedule_task_add(server->schedule, 0,
+                          silc_server_backup_connect_to_router,
+                          context, 5, 0, SILC_TASK_TIMEOUT, 
+                          SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Continue with key exchange protocol */
+  silc_server_start_key_exchange(server, sconn, sock);
+}
+
+/* Constantly tries to reconnect to a primary router indicated by the
+   `ip' and `port'. The `connected' callback will be called when the
+   connection is created. */
+
+void silc_server_backup_reconnect(SilcServer server,
+                                 const char *ip, uint16 port,
+                                 SilcServerConnectRouterCallback callback,
+                                 void *context)
+{
+  SilcServerConnection sconn;
+
+  sconn = silc_calloc(1, sizeof(*sconn));
+  sconn->server = server;
+  sconn->remote_host = strdup(ip);
+  sconn->remote_port = port;
+  sconn->callback = callback;
+  sconn->callback_context = context;
+  silc_schedule_task_add(server->schedule, 0, 
+                        silc_server_backup_connect_to_router,
+                        sconn, 1, 0, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+SILC_TASK_CALLBACK(silc_server_backup_connected_later)
+{
+  SilcServerBackupProtocolContext proto_ctx = 
+    (SilcServerBackupProtocolContext)context;
+  SilcServer server = proto_ctx->server;
+  SilcSocketConnection sock = proto_ctx->sock;
+
+  SILC_LOG_DEBUG(("Starting backup resuming protocol as initiator"));
+
+  /* Run the backup resuming protocol */
+  silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
+                     &sock->protocol, proto_ctx, 
+                     silc_server_protocol_backup_done);
+  silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+}
+
+/* Called when we've established connection back to our primary router
+   when we've acting as backup router and have replaced the primary router
+   in the cell. This function will start the backup resuming protocol. */
+
+void silc_server_backup_connected(SilcServer server,
+                                 SilcServerEntry server_entry,
+                                 void *context)
+{
+  SilcServerBackupProtocolContext proto_ctx;
+  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
+
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = server;
+  proto_ctx->sock = sock;
+  proto_ctx->responder = FALSE;
+  proto_ctx->type = SILC_SERVER_BACKUP_START;
+  proto_ctx->start = time(0);
+
+  /* Start through scheduler */
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_backup_connected_later,
+                        proto_ctx, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+/* Called when normal server has connected to its primary router after
+   backup router has sent the START packet in reusming protocol. We will
+   move the protocol context from the backup router connection to the
+   primary router. */
+
+static void silc_server_backup_connect_primary(SilcServer server,
+                                              SilcServerEntry server_entry,
+                                              void *context)
+{
+  SilcSocketConnection backup_router = (SilcSocketConnection)context;
+  SilcSocketConnection sock = (SilcSocketConnection)server_entry->connection;
+  SilcIDListData idata = (SilcIDListData)server_entry;
+  SilcServerBackupProtocolContext ctx = 
+    (SilcServerBackupProtocolContext)backup_router->protocol->context;
+  SilcBuffer buffer;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_LOG_DEBUG(("********************************"));
+  SILC_LOG_DEBUG(("Sending CONNECTED packet, session %d", ctx->session));
+
+  /* Send the CONNECTED packet back to the backup router. */
+  buffer = silc_buffer_alloc(2);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_CONNECTED),
+                    SILC_STR_UI_CHAR(ctx->session),
+                    SILC_STR_END);
+  silc_server_packet_send(server, backup_router, 
+                         SILC_PACKET_RESUME_ROUTER, 0, 
+                         buffer->data, buffer->len, FALSE);
+  silc_buffer_free(buffer);
+
+  /* The primary connection is disabled until it sends the RESUMED packet
+     to us. */
+  idata->status |= SILC_IDLIST_STATUS_DISABLED;
+
+  /* Move this protocol context from this backup router connection to
+     the primary router connection since it will send the subsequent
+     packets in this protocol. We don't talk with backup router 
+     anymore. */
+  sock->protocol = backup_router->protocol;
+  ctx->sock = (SilcSocketConnection)server_entry->connection;
+  backup_router->protocol = NULL;
+}
+
+SILC_TASK_CALLBACK(silc_server_backup_send_resumed)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServer server = ctx->server;
+  SilcBuffer packet;
+  int i;
+
+  for (i = 0; i < ctx->sessions_count; i++)
+    if (ctx->sessions[i].server_entry == ctx->sock->user_data)
+      ctx->session = ctx->sessions[i].session;
+  
+  /* We've received all the CONNECTED packets and now we'll send the
+     ENDING packet to the new primary router. */
+  packet = silc_buffer_alloc(2);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_ENDING),
+                    SILC_STR_UI_CHAR(ctx->session),
+                    SILC_STR_END);
+  silc_server_packet_send(server, ctx->sock, 
+                         SILC_PACKET_RESUME_ROUTER, 0, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+  
+  protocol->state = SILC_PROTOCOL_STATE_END;
+}
+
+/* Resume protocol with RESUME_ROUTER packet: 
+
+   SILC_PACKET_RESUME_ROUTER:
+
+   <uint8 type> <uint8 Session ID>
+
+   <type>          = the protocol opcode
+   <Session ID>    = Identifier for this packet and any subsequent reply
+                     packets must include this identifier.
+
+   Types:
+
+     1    = To router: Comensing backup resuming protocol. This will
+            indicate that the sender is backup router acting as primary
+            and the receiver is primary router that has been replaced by
+           the backup router.
+
+           To server. Comensing backup resuming protocol. This will
+           indicate that the sender is backup router and the receiver
+           must reconnect to the real primary router of the cell.
+
+     2    = To Router: Comesning backup resuming protocol in another
+            cell.  The receiver will connect to its primary router 
+           (the router that is now online again) but will not use
+           the link.  If the receiver is not configured to connect
+           to any router it does as locally configured.  The sender
+           is always backup router.
+
+           To server: this is never sent to server.
+
+     3    = To backup router: Sender is normal server or router and it
+            tells to backup router that they have connected to the
+           primary router.  Backup router never sends this type.
+
+     4    = To router: Ending backup resuming protocol. This is sent
+            to the real primary router to tell that it can take over
+           the task as being primary router.
+
+           To server: same as sending for router.
+
+           Backup router sends this also to the primary route but only
+           after it has sent them to normal servers and has purged all
+           traffic coming from normal servers.
+
+     5    = To router: Sender is the real primary router after it has
+            received type 4 from backup router. To tell that it is again
+           primary router of the cell.
+
+     20   = To router: This is sent only when router is connecting to
+            another router and has been replaced by an backup router.
+           The sender knows that the connectee has been replaced.
+
+ */
+
+/* Backup resuming protocol. This protocol is executed when the primary
+   router wants to resume its position as being primary router. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServer server = ctx->server;
+  SilcBuffer packet;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry server_entry;
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  SILC_LOG_DEBUG(("State=%d", protocol->state));
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    if (ctx->responder == FALSE) {
+      /* Initiator of the protocol. We are backup router */
+
+      packet = silc_buffer_alloc(2);
+      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+
+      SILC_LOG_DEBUG(("********************************"));
+      SILC_LOG_DEBUG(("Sending START packets"));
+
+      /* Send the START packet to primary router and normal servers. */
+      if (silc_idcache_get_all(server->local_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key ||
+               (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           ctx->sessions = silc_realloc(ctx->sessions,
+                                        sizeof(*ctx->sessions) *
+                                        (ctx->sessions_count + 1));
+           ctx->sessions[ctx->sessions_count].session = ctx->sessions_count;
+           ctx->sessions[ctx->sessions_count].connected = FALSE;
+           ctx->sessions[ctx->sessions_count].server_entry = server_entry;
+
+           SILC_LOG_DEBUG(("********************************"));
+           SILC_LOG_DEBUG(("START (local) for session %d", 
+                           ctx->sessions_count));
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_START;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_START_GLOBAL;
+           packet->data[1] = ctx->sessions_count;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+           ctx->sessions_count++;
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      if (silc_idcache_get_all(server->global_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key ||
+               (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           ctx->sessions = silc_realloc(ctx->sessions,
+                                        sizeof(*ctx->sessions) *
+                                        (ctx->sessions_count + 1));
+           ctx->sessions[ctx->sessions_count].session = ctx->sessions_count;
+           ctx->sessions[ctx->sessions_count].connected = FALSE;
+           ctx->sessions[ctx->sessions_count].server_entry = server_entry;
+
+           SILC_LOG_DEBUG(("********************************"));
+           SILC_LOG_DEBUG(("START (global) for session %d", 
+                           ctx->sessions_count));
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_START;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_START_GLOBAL;
+           packet->data[1] = ctx->sessions_count;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+           ctx->sessions_count++;
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      silc_buffer_free(packet);
+
+      /* Announce all of our information */
+      silc_server_announce_servers(server, TRUE, 0, ctx->sock);
+      silc_server_announce_clients(server, 0, ctx->sock);
+      silc_server_announce_channels(server, 0, ctx->sock);
+
+      protocol->state++;
+    } else {
+      /* Responder of the protocol. */
+      SilcServerConfigSectionRouter *primary;
+
+      /* We should have received START or START_GLOBAL packet */
+      if (ctx->type != SILC_SERVER_BACKUP_START &&
+         ctx->type != SILC_SERVER_BACKUP_START_GLOBAL) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      SILC_LOG_DEBUG(("********************************"));
+      SILC_LOG_DEBUG(("Received START packet, reconnecting to router"));
+
+      /* Connect to the primary router that was down that is now supposed
+        to be back online. We send the CONNECTED packet after we've
+        established the connection to the primary router. */
+      primary = silc_server_config_get_primary_router(server->config);
+      if (primary && server->backup_primary) {
+       silc_server_backup_reconnect(server,
+                                    primary->host, primary->port,
+                                    silc_server_backup_connect_primary,
+                                    ctx->sock);
+      } else {
+       /* Nowhere to connect just return the CONNECTED packet */
+
+       SILC_LOG_DEBUG(("********************************"));
+       SILC_LOG_DEBUG(("Sending CONNECTED packet, session %d", ctx->session));
+       
+       /* Send the CONNECTED packet back to the backup router. */
+       packet = silc_buffer_alloc(2);
+       silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+       silc_buffer_format(packet,
+                          SILC_STR_UI_CHAR(SILC_SERVER_BACKUP_CONNECTED),
+                          SILC_STR_UI_CHAR(ctx->session),
+                          SILC_STR_END);
+       silc_server_packet_send(server, ctx->sock, 
+                               SILC_PACKET_RESUME_ROUTER, 0, 
+                               packet->data, packet->len, FALSE);
+       silc_buffer_free(packet);
+      }
+
+      if (server->server_type == SILC_ROUTER &&
+         (!server->router || 
+          server->router->data.status & SILC_IDLIST_STATUS_DISABLED))
+       protocol->state++;
+      else
+       protocol->state = SILC_PROTOCOL_STATE_END;
+
+      ctx->sessions = silc_realloc(ctx->sessions,
+                                  sizeof(*ctx->sessions) *
+                                  (ctx->sessions_count + 1));
+      ctx->sessions[ctx->sessions_count].session = ctx->session;
+      ctx->sessions_count++;
+    }
+    break;
+
+  case 2:
+    if (ctx->responder == FALSE) {
+      /* Initiator */
+
+      /* We should have received CONNECTED packet */
+      if (ctx->type != SILC_SERVER_BACKUP_CONNECTED) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      SILC_LOG_DEBUG(("********************************"));
+      SILC_LOG_DEBUG(("Received CONNECTED packet, session %d", ctx->session));
+
+      for (i = 0; i < ctx->sessions_count; i++) {
+       if (ctx->sessions[i].session == ctx->session) {
+         ctx->sessions[i].connected = TRUE;
+         break;
+       }
+      }
+
+      for (i = 0; i < ctx->sessions_count; i++) {
+       if (!ctx->sessions[i].connected)
+         return;
+      }
+
+      SILC_LOG_DEBUG(("********************************"));
+      SILC_LOG_DEBUG(("Sending ENDING packet to primary"));
+
+      /* Send with a timeout */
+      silc_schedule_task_add(server->schedule, 0, 
+                            silc_server_backup_send_resumed,
+                            protocol, 1, 0, SILC_TASK_TIMEOUT,
+                            SILC_TASK_PRI_NORMAL);
+      return;
+    } else {
+      /* Responder */
+
+      /* We should have been received ENDING packet */
+      if (ctx->type != SILC_SERVER_BACKUP_ENDING) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      SILC_LOG_DEBUG(("********************************"));
+      SILC_LOG_DEBUG(("Received ENDING packet, sending RESUMED packets"));
+
+      /* This state is received by the primary router but also servers
+        and perhaps other routers so check that if we are the primary
+        router of the cell then start sending RESUMED packets.  If we
+        are normal server or one of those other routers then procede
+        to next state. */
+      if (server->router &&
+         !(server->router->data.status & SILC_IDLIST_STATUS_DISABLED) &&
+         silc_server_config_is_primary_route(server->config)) {
+       /* We'll wait for RESUMED packet */
+       protocol->state = SILC_PROTOCOL_STATE_END;
+       break;
+      }
+
+      /* Switch announced informations to our primary router of using the
+        backup router. */
+      silc_server_update_servers_by_server(server, ctx->sock->user_data, 
+                                          server->router);
+      silc_server_update_clients_by_server(server, ctx->sock->user_data,
+                                          server->router, TRUE, FALSE);
+      if (server->server_type == SILC_SERVER)
+       silc_server_update_channels_by_server(server, ctx->sock->user_data, 
+                                             server->router);
+
+      packet = silc_buffer_alloc(2);
+      silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+
+      /* We are the primary router, start sending RESUMED packets. */
+      if (silc_idcache_get_all(server->local_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           SILC_LOG_DEBUG(("********************************"));
+           SILC_LOG_DEBUG(("RESUMED packet (local)"));
+
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      if (silc_idcache_get_all(server->global_list->servers, &list)) {
+       if (silc_idcache_list_first(list, &id_cache)) {
+         while (id_cache) {
+           server_entry = (SilcServerEntry)id_cache->context;
+           if (!server_entry || (server_entry == server->id_entry) || 
+               !server_entry->connection || !server_entry->data.send_key) {
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           SILC_LOG_DEBUG(("********************************"));
+           SILC_LOG_DEBUG(("RESUMED packet (global)"));
+
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+           /* This connection is performing this protocol too now */
+           ((SilcSocketConnection)server_entry->connection)->protocol =
+             protocol;
+
+           if (server_entry->server_type == SILC_ROUTER)
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED;
+           else
+             packet->data[0] = SILC_SERVER_BACKUP_RESUMED_GLOBAL;
+           silc_server_packet_send(server, server_entry->connection,
+                                   SILC_PACKET_RESUME_ROUTER, 0, 
+                                   packet->data, packet->len, FALSE);
+
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+         }
+       }
+
+       silc_idcache_list_free(list);
+      }
+
+      silc_buffer_free(packet);
+
+      SILC_LOG_INFO(("We are now the primary router of our cell again"));
+
+      /* For us this is the end of this protocol. */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, server->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    {
+      SilcIDListData idata;
+      SilcServerEntry router, backup_router;
+
+      /* We should have been received RESUMED packet from our primary
+        router. */
+      if (ctx->type != SILC_SERVER_BACKUP_RESUMED &&
+         ctx->type != SILC_SERVER_BACKUP_RESUMED_GLOBAL) {
+       SILC_LOG_DEBUG(("Bad resume router packet"));
+       break;
+      }
+
+      SILC_LOG_DEBUG(("********************************"));
+      SILC_LOG_DEBUG(("Received RESUMED packet"));
+
+      /* We have now new primary router. All traffic goes there from now on. */
+      if (server->backup_router)
+       server->server_type = SILC_BACKUP_ROUTER;
+
+      router = (SilcServerEntry)ctx->sock->user_data;
+      if (silc_server_backup_replaced_get(server, router->id, 
+                                         &backup_router)) {
+
+       if (backup_router == server->router) {
+         server->id_entry->router = router;
+         server->router = router;
+         SILC_LOG_INFO(("Switching back to primary router %s",
+                        server->router->server_name));
+         SILC_LOG_DEBUG(("Switching back to primary router %s",
+                         server->router->server_name));
+         idata = (SilcIDListData)server->router;
+         idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
+       } else {
+         SILC_LOG_INFO(("Resuming the use of router %s",
+                        router->server_name));
+         SILC_LOG_DEBUG(("Resuming the use of router %s",
+                         router->server_name));
+         idata = (SilcIDListData)router;
+         idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
+       }
+
+       /* Update the client entries of the backup router to the new 
+          router */
+       silc_server_update_servers_by_server(server, backup_router, router);
+       silc_server_update_clients_by_server(server, backup_router,
+                                            router, TRUE, FALSE);
+       if (server->server_type == SILC_SERVER)
+         silc_server_update_channels_by_server(server, backup_router, router);
+       silc_server_backup_replaced_del(server, backup_router);
+       silc_server_backup_add(server, backup_router, 
+                              ctx->sock->ip, ctx->sock->port,
+                              backup_router->server_type != SILC_ROUTER ?
+                              TRUE : FALSE);
+
+       /* Announce all of our information to the router. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server, FALSE, 0, router->connection);
+
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, 0, router->connection);
+       silc_server_announce_channels(server, 0, router->connection);
+      }
+
+      /* Protocol has ended, call the final callback */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, server->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    /* Protocol has ended, call the final callback */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /* Protocol has ended, call the final callback */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, server->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+}
+
+SILC_TASK_CALLBACK(silc_server_protocol_backup_done)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcServerBackupProtocolContext ctx = protocol->context;
+  SilcServer server = ctx->server;
+  SilcServerEntry server_entry;
+  SilcSocketConnection sock;
+  SilcIDCacheList list;
+  SilcIDCacheEntry id_cache;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    SILC_LOG_ERROR(("Error occurred during backup router resuming protcool"));
+  }
+
+  /* Remove this protocol from all server entries that has it */
+  if (silc_idcache_get_all(server->local_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       sock = (SilcSocketConnection)server_entry->connection;
+
+       if (sock->protocol == protocol) {
+         sock->protocol = NULL;
+
+         if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+       }
+       
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (silc_idcache_get_all(server->global_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       sock = (SilcSocketConnection)server_entry->connection;
+
+       if (sock->protocol == protocol) {
+         sock->protocol = NULL;
+
+         if (server_entry->data.status & SILC_IDLIST_STATUS_DISABLED)
+           server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+       }
+       
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (ctx->sock->protocol)
+    ctx->sock->protocol = NULL;
+  silc_protocol_free(protocol);
+  silc_free(ctx->sessions);
+  silc_free(ctx);
+}
diff --git a/apps/silcd/server_backup.h b/apps/silcd/server_backup.h
new file mode 100644 (file)
index 0000000..57f6a05
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+
+  server_backup.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SERVER_BACKUP_H
+#define SERVER_BACKUP_H
+
+/* Backup resuming protocol types */
+#define SILC_SERVER_BACKUP_START            1
+#define SILC_SERVER_BACKUP_START_GLOBAL     2
+#define SILC_SERVER_BACKUP_CONNECTED        3
+#define SILC_SERVER_BACKUP_ENDING           4
+#define SILC_SERVER_BACKUP_RESUMED          5
+#define SILC_SERVER_BACKUP_RESUMED_GLOBAL   6
+#define SILC_SERVER_BACKUP_REPLACED         20
+
+/* Adds the `backup_server' to be one of our backup router. This can be
+   called multiple times to set multiple backup routers. The `replacing' is
+   the IP and port that the `backup_router' will replace if the `replacing'
+   will become unresponsive. If `local' is TRUE then the `backup_server' is
+   in the local cell, if FALSE it is in some other cell. */
+void silc_server_backup_add(SilcServer server, SilcServerEntry backup_server,
+                           const char *ip, int port, bool local);
+
+/* Returns backup router for IP and port in `replacing' or NULL if there
+   does not exist backup router. */
+SilcServerEntry silc_server_backup_get(SilcServer server, 
+                                      SilcServerID *server_id);
+
+/* Deletes the backup server `server_entry'. */
+void silc_server_backup_del(SilcServer server, SilcServerEntry server_entry);
+
+/* Marks the IP address and port from the `server_id' as  being replaced
+   by backup router indicated by the `server'. If the router connects at
+   a later time we can check whether it has been replaced by an backup
+   router. */
+void silc_server_backup_replaced_add(SilcServer server, 
+                                    SilcServerID *server_id,
+                                    SilcServerEntry server_entry);
+
+/* Checks whether the IP address and port from the `server_id' has been
+   replaced by an backup router. If it has been then this returns TRUE
+   and the bacup router entry to the `server' pointer if non-NULL. Returns
+   FALSE if the router is not replaced by backup router. */
+bool silc_server_backup_replaced_get(SilcServer server,
+                                    SilcServerID *server_id,
+                                    SilcServerEntry *server_entry);
+
+/* Deletes a replaced host by the set `server_entry. */
+void silc_server_backup_replaced_del(SilcServer server,
+                                    SilcServerEntry server_entry);
+
+/* Broadcast the received packet indicated by `packet' to all of our backup 
+   routers. All router wide information is passed using broadcast packets. 
+   That is why all backup routers need to get this data too. It is expected
+   that the caller already knows that the `packet' is broadcast packet. */
+void silc_server_backup_broadcast(SilcServer server, 
+                                 SilcSocketConnection sender,
+                                 SilcPacketContext *packet);
+
+/* A generic routine to send data to all backup routers. If the `sender'
+   is provided it will indicate the original sender of the packet and the
+   packet won't be resent to that entity. The `data' is the data that will
+   be assembled to packet context before sending. The packet will be
+   encrypted this function. If the `force_send' is TRUE the data is sent
+   immediately and not put to queue. If `local' is TRUE then the packet
+   will be sent only to local backup routers inside the cell. If false the
+   packet can go from one cell to the other. This function has no effect
+   if there are no any backup routers. */
+void silc_server_backup_send(SilcServer server,
+                            SilcServerEntry sender,
+                            SilcPacketType type,
+                            SilcPacketFlags flags,
+                            unsigned char *data,
+                            uint32 data_len,
+                            bool force_send,
+                            bool local);
+
+/* Same as silc_server_backup_send but sets a specific Destination ID to
+   the packet. The Destination ID is indicated by the `dst_id' and the
+   ID type `dst_id_type'. For example, packets destined to channels must
+   be sent using this function. */
+void silc_server_backup_send_dest(SilcServer server,
+                                 SilcServerEntry sender,
+                                 SilcPacketType type,
+                                 SilcPacketFlags flags,
+                                 void *dst_id,
+                                 SilcIdType dst_id_type,
+                                 unsigned char *data,
+                                 uint32 data_len,
+                                 bool force_send,
+                                 bool local);
+
+/* Processes incoming RESUME_ROUTER packet. This can give the packet
+   for processing to the protocol handler or allocate new protocol if
+   start command is received. */
+void silc_server_backup_resume_router(SilcServer server, 
+                                     SilcSocketConnection sock, 
+                                     SilcPacketContext *packet);
+
+/* Constantly tries to reconnect to a primary router indicated by the
+   `ip' and `port'. The `connected' callback will be called when the
+   connection is created. */
+void silc_server_backup_reconnect(SilcServer server,
+                                 const char *ip, uint16 port,
+                                 SilcServerConnectRouterCallback callback,
+                                 void *context);
+
+/* Called when we've established connection back to our primary router
+   when we've acting as backup router and have replaced the primary router
+   in the cell. This function will start the backup resuming protocol. */
+void silc_server_backup_connected(SilcServer server,
+                                 SilcServerEntry server_entry,
+                                 void *context);
+
+/* Backup resuming protocol. This protocol is executed when the primary
+   router wants to resume its position as being primary router. */
+SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup);
+
+#endif /* SERVER_BACKUP_H */
index 3ed7cbebefe272157760d62fa616fade08795389..41df9ab805195c78fd85d781c76f7235afd29b42 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 /* Server statistics structure. This holds various statistics about
    various things. */
-/* XXX TODO */
 typedef struct {
-
+  /* Local stats (server and router) */
+  uint32 my_clients;             /* Locally connected clients */
+  uint32 my_servers;             /* Locally connected servers */
+  uint32 my_routers;             /* Locally connected routers */
+  uint32 my_channels;            /* Locally created channels */
+  uint32 my_chanclients;         /* Local clients on local channels */
+  uint32 my_aways;               /* Local clients away (XXX) */
+  uint32 my_server_ops;                  /* Local server operators */
+  uint32 my_router_ops;                  /* Local router operators */
+
+  /* Global stats (mainly for router) */
+  uint32 cell_clients;           /* All clients in cell */
+  uint32 cell_servers;           /* All servers in cell */
+  uint32 cell_channels;                  /* All channels in cell */
+  uint32 cell_chanclients;       /* All clients on cell's channels */
+  uint32 clients;                /* All clients */
+  uint32 servers;                /* All servers */
+  uint32 routers;                /* All routers */
+  uint32 channels;               /* All channels */
+  uint32 chanclients;            /* All clients on channels */
+  uint32 server_ops;             /* All server operators */
+  uint32 router_ops;             /* All router operators */
+
+  /* General */
+  uint32 conn_attempts;                  /* Connection attempts */
+  uint32 conn_failures;                  /* Connection failure */
+  uint32 auth_attempts;                  /* Authentication attempts */
+  uint32 auth_failures;                  /* Authentication failures */
+  uint32 packets_sent;           /* Sent packets */
+  uint32 packets_received;       /* Received packets */
 } SilcServerStatistics;
 
 /* 
    SILC Server Object.
 
 */
-typedef struct SilcServerObjectStruct {
+struct SilcServerStruct {
   char *server_name;
   int server_type;
   int sock;
-  int standalone;
-  int listenning;
   SilcServerID *id;
+  unsigned char *id_string;
+  uint32 id_string_len;
   SilcIdType id_type;
-  SilcServerList *id_entry;
 
-  /* SILC server task queues */
-  SilcTaskQueue io_queue;
-  SilcTaskQueue timeout_queue;
-  SilcTaskQueue generic_queue;
+  bool standalone;                  /* TRUE if server is standalone, and
+                                       does not have connection to network. */
+  bool listenning;                  /* TRUE if server is listenning for
+                                       incoming connections. */
+  SilcServerEntry id_entry;         /* Server's own ID entry */
+  SilcServerEntry router;           /* Pointer to the primary router */
+  unsigned long router_connect;             /* Time when router was connected */
+  SilcServerBackup backup;          /* Backup routers */
+  bool backup_router;               /* TRUE if this is backup router */
+  bool backup_primary;              /* TRUE if we've switched our primary
+                                       router to a backup router. */
+  SilcServerConnection router_conn; /* non-NULL when connecting to the
+                                      primary router, and NULL otherwise. */
+
+  /* Current command identifier, 0 not used */
+  uint16 cmd_ident;
+
+  /* SILC server scheduler */
+  SilcSchedule schedule;
 
   /* ID lists. */
   SilcIDList local_list;
@@ -54,13 +96,10 @@ typedef struct SilcServerObjectStruct {
   /* Table of connected sockets */
   SilcSocketConnection *sockets;
 
-  /* Server keys */
-  SilcCipher send_key;
-  SilcCipher receive_key;
-  SilcCipher none_cipher;
-
   /* Server public key */
-  SilcPKCS public_key;
+  SilcPKCS pkcs;
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
 
   /* Hash objects for general hashing */
   SilcHash md5hash;
@@ -71,47 +110,75 @@ typedef struct SilcServerObjectStruct {
   SilcHmac sha1hmac;
 
   /* Configuration object */
-  SilcConfigServer config;
+  SilcServerConfig config;
 
   /* Random pool */
   SilcRng rng;
 
   /* Server statistics */
-  SilcServerStatistics stats;
+  SilcServerStatistics stat;
+
+  /* Pending command queue */
+  SilcDList pending_commands;
+
+  /* Default parameteres for server */
+  SilcServerParams params;
 
 #ifdef SILC_SIM
-  /* SIM (SILC Module) table */
-  SilcSimContext **sim;
-  unsigned int sim_count;
+  /* SIM (SILC Module) list */
+  SilcDList sim;
 #endif
-} SilcServerObject;
+};
+
+/* Server's heartbeat context */
+typedef struct {
+  SilcServer server;
+} *SilcServerHBContext;
+
+/* Failure context. This is allocated when failure packet is received.
+   Failure packets are processed with timeout and data is saved in this
+   structure. */
+typedef struct {
+  SilcServer server;
+  SilcSocketConnection sock;
+  uint32 failure;
+} *SilcServerFailureContext;
 
 /* Macros */
 
 /* Registers generic task for file descriptor for reading from network and
    writing to network. As being generic task the actual task is allocated 
    only once and after that the same task applies to all registered fd's. */
-#define SILC_REGISTER_CONNECTION_FOR_IO(fd)                            \
-do {                                                                   \
-  SilcTask tmptask = silc_task_register(server->generic_queue, (fd),   \
-                                       silc_server_packet_process,     \
-                                       context, 0, 0,                  \
-                                       SILC_TASK_GENERIC,              \
-                                       SILC_TASK_PRI_NORMAL);          \
-  silc_task_set_iotype(tmptask, SILC_TASK_WRITE);                      \
+#define SILC_REGISTER_CONNECTION_FOR_IO(fd)            \
+do {                                                   \
+  silc_schedule_task_add(server->schedule, (fd),       \
+                        silc_server_packet_process,    \
+                        context, 0, 0,                 \
+                        SILC_TASK_GENERIC,             \
+                        SILC_TASK_PRI_NORMAL);         \
 } while(0)
 
-#define SILC_SET_CONNECTION_FOR_INPUT(fd)                              \
-do {                                                                   \
-  silc_schedule_set_listen_fd((fd), (1L << SILC_TASK_READ));            \
+#define SILC_SET_CONNECTION_FOR_INPUT(s, fd)                   \
+do {                                                           \
+  silc_schedule_set_listen_fd((s), (fd), SILC_TASK_READ);      \
 } while(0)
      
-#define SILC_SET_CONNECTION_FOR_OUTPUT(fd)                             \
-do {                                                                   \
-  silc_schedule_set_listen_fd((fd), ((1L << SILC_TASK_READ) |           \
-                                    (1L << SILC_TASK_WRITE)));         \
+#define SILC_SET_CONNECTION_FOR_OUTPUT(s, fd)                                \
+do {                                                                         \
+  silc_schedule_set_listen_fd((s), (fd), (SILC_TASK_READ | SILC_TASK_WRITE)); \
+} while(0)
+
+#define SILC_OPER_STATS_UPDATE(c, type, mod)   \
+do {                                           \
+  if ((c)->mode & (mod)) {                     \
+    if ((c)->connection)                       \
+      server->stat.my_ ## type ## _ops--;      \
+    if (server->server_type == SILC_ROUTER)    \
+      server->stat. type ## _ops--;            \
+  }                                            \
 } while(0)
 
 /* Prototypes */
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final);
 
 #endif
diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c
new file mode 100644 (file)
index 0000000..5c06bcd
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+
+  server_util.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "serverincludes.h"
+#include "server_internal.h"
+
+/* Removes the client from channels and possibly removes the channels
+   as well.  After removing those channels that exist, their channel
+   keys are regnerated. This is called only by the function
+   silc_server_remove_clients_by_server. */
+
+static void silc_server_remove_clients_channels(SilcServer server, 
+                                               SilcSocketConnection sock,
+                                               SilcClientEntry client,
+                                               SilcHashTable channels)
+{
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+  SilcBuffer clidp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client || !client->id)
+    return;
+
+  clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+  /* Remove the client from all channels. The client is removed from
+     the channels' user list. */
+  silc_hash_table_list(client->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    channel = chl->channel;
+
+    /* Remove channel from client's channel list */
+    silc_hash_table_del(client->channels, channel);
+
+    /* Remove channel if there is no users anymore */
+    if (server->server_type == SILC_ROUTER &&
+       silc_hash_table_count(channel->user_list) < 2) {
+
+      if (silc_hash_table_find(channels, channel, NULL, NULL))
+       silc_hash_table_del(channels, channel);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
+      continue;
+    }
+
+    /* Remove client from channel's client list */
+    silc_hash_table_del(channel->user_list, chl->client);
+
+    /* If there is no global users on the channel anymore mark the channel
+       as local channel. Do not check if the removed client is local client. */
+    if (server->server_type != SILC_ROUTER && channel->global_users && 
+       chl->client->router && !silc_server_channel_has_global(channel))
+      channel->global_users = FALSE;
+
+    silc_free(chl);
+    server->stat.my_chanclients--;
+
+    /* If there is not at least one local user on the channel then we don't
+       need the channel entry anymore, we can remove it safely. */
+    if (server->server_type != SILC_ROUTER &&
+       !silc_server_channel_has_local(channel)) {
+
+      if (silc_hash_table_find(channels, channel, NULL, NULL))
+       silc_hash_table_del(channels, channel);
+
+      if (channel->rekey)
+       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+       SilcHashTableList htl2;
+
+       channel->disabled = TRUE;
+
+       silc_hash_table_list(channel->user_list, &htl2);
+       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+         silc_hash_table_del(chl2->client->channels, channel);
+         silc_hash_table_del(channel->user_list, chl2->client);
+         silc_free(chl2);
+       }
+       silc_hash_table_list_reset(&htl2);
+       continue;
+      }
+
+      /* Remove the channel entry */
+      if (silc_idlist_del_channel(server->local_list, channel))
+       server->stat.my_channels--;
+      else 
+        silc_idlist_del_channel(server->global_list, channel);
+      continue;
+    }
+
+    /* Add the channel to the the channels list to regenerate the 
+       channel key */
+    if (!silc_hash_table_find(channels, channel, NULL, NULL))
+      silc_hash_table_add(channels, channel, channel);
+  }
+  silc_hash_table_list_reset(&htl);
+
+  silc_buffer_free(clidp);
+}
+
+/* This function is used to remove all client entries by the server `entry'.
+   This is called when the connection is lost to the server. In this case
+   we must invalidate all the client entries owned by the server `entry'. 
+   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
+   distributed to our local clients. */
+
+bool silc_server_remove_clients_by_server(SilcServer server, 
+                                         SilcServerEntry entry,
+                                         bool server_signoff)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+  SilcBuffer idp;
+  SilcClientEntry *clients = NULL;
+  uint32 clients_c = 0;
+  unsigned char **argv = NULL;
+  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  SilcHashTableList htl;
+  SilcChannelEntry channel;
+  SilcHashTable channels;
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate the hash table that holds the channels that require
+     channel key re-generation after we've removed this server's clients
+     from the channels. */
+  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
+                                  NULL, NULL, TRUE);
+
+  if (server_signoff) {
+    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
+    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
+    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+    memcpy(argv[argc], idp->data, idp->len);
+    argv_lens[argc] = idp->len;
+    argv_types[argc] = argc + 1;
+    argc++;
+    silc_buffer_free(idp);
+  }
+
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (client->router != entry) {
+         if (server_signoff) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
+       /* Update statistics */
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
+       SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       if (!server_signoff) {
+         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       } else {
+         silc_idlist_del_client(server->local_list, client);
+       }
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+  
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
+
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+       
+       if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
+       /* Update statistics */
+       server->stat.clients--;
+       if (server->server_type == SILC_ROUTER)
+         server->stat.cell_clients--;
+       SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       if (!server_signoff) {
+         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       } else {
+         silc_idlist_del_client(server->global_list, client);
+       }
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  /* Send the SERVER_SIGNOFF notify */
+  if (server_signoff) {
+    SilcBuffer args, not;
+
+    /* Send SERVER_SIGNOFF notify to our primary router */
+    if (!server->standalone && server->router &&
+       server->router != entry) {
+      args = silc_argument_payload_encode(1, argv, argv_lens,
+                                         argv_types);
+      silc_server_send_notify_args(server, 
+                                  server->router->connection,
+                                  server->server_type == SILC_SERVER ? 
+                                  FALSE : TRUE, 
+                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                                  argc, args);
+      silc_buffer_free(args);
+    }
+
+    /* Send to local clients. We also send the list of client ID's that
+       is to be removed for those servers that would like to use that list. */
+    args = silc_argument_payload_encode(argc, argv, argv_lens,
+                                       argv_types);
+    not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF, 
+                                         argc, args);
+    silc_server_packet_send_clients(server, clients, clients_c,
+                                   SILC_PACKET_NOTIFY, 0, FALSE,
+                                   not->data, not->len, FALSE);
+
+    silc_free(clients);
+    silc_buffer_free(args);
+    silc_buffer_free(not);
+    for (i = 0; i < argc; i++)
+      silc_free(argv[i]);
+    silc_free(argv);
+    silc_free(argv_lens);
+    silc_free(argv_types);
+  }
+
+  /* We must now re-generate the channel key for all channels that had
+     this server's client(s) on the channel. As they left the channel we
+     must re-generate the channel key. */
+  silc_hash_table_list(channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
+    if (!silc_server_create_channel_key(server, channel, 0)) {
+      silc_hash_table_list_reset(&htl);
+      silc_hash_table_free(channels);
+      return FALSE;
+    }
+
+    /* Do not send the channel key if private channel key mode is set */
+    if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
+      continue;
+
+    silc_server_send_channel_key(server, NULL, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
+  }
+  silc_hash_table_list_reset(&htl);
+  silc_hash_table_free(channels);
+
+  return TRUE;
+}
+
+static SilcServerEntry
+silc_server_update_clients_by_real_server(SilcServer server,
+                                         SilcServerEntry from,
+                                         SilcClientEntry client,
+                                         bool local,
+                                         SilcIDCacheEntry client_cache)
+{
+  SilcServerEntry server_entry;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcIDCacheList list;
+
+  if (!silc_idcache_get_all(server->local_list->servers, &list))
+    return NULL;
+
+  if (silc_idcache_list_first(list, &id_cache)) {
+    while (id_cache) {
+      server_entry = (SilcServerEntry)id_cache->context;
+      if (server_entry != from &&
+         SILC_ID_COMPARE(server_entry->id, client->id, 
+                         client->id->ip.data_len)) {
+       SILC_LOG_DEBUG(("Found (local) %s",
+                       silc_id_render(server_entry->id, SILC_ID_SERVER)));
+
+       if (!server_entry->data.send_key && server_entry->router) {
+         SILC_LOG_DEBUG(("Server not locally connected, use its router"));
+         /* If the client is not marked as local then move it to local list
+            since the server is local. */
+         if (!local) {
+           SILC_LOG_DEBUG(("Moving client to local list"));
+           silc_idcache_add(server->local_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire, NULL);
+           silc_idcache_del_by_context(server->global_list->clients, client);
+         }
+         server_entry = server_entry->router;
+       } else {
+         /* If the client is not marked as local then move it to local list
+            since the server is local. */
+         if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) {
+           SILC_LOG_DEBUG(("Moving client to local list"));
+           silc_idcache_add(server->local_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire, NULL);
+           silc_idcache_del_by_context(server->global_list->clients, client);
+         }
+       }
+
+       silc_idcache_list_free(list);
+       return server_entry;
+      }
+
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  }
+
+  silc_idcache_list_free(list);
+
+  if (!silc_idcache_get_all(server->global_list->servers, &list))
+    return NULL;
+
+  if (silc_idcache_list_first(list, &id_cache)) {
+    while (id_cache) {
+      server_entry = (SilcServerEntry)id_cache->context;
+      if (server_entry != from &&
+         SILC_ID_COMPARE(server_entry->id, client->id, 
+                         client->id->ip.data_len)) {
+       SILC_LOG_DEBUG(("Found (global) %s",
+                       silc_id_render(server_entry->id, SILC_ID_SERVER)));
+
+       if (!server_entry->data.send_key && server_entry->router) {
+         SILC_LOG_DEBUG(("Server not locally connected, use its router"));
+         /* If the client is marked as local then move it to global list
+            since the server is global. */
+         if (local) {
+           SILC_LOG_DEBUG(("Moving client to global list"));
+           silc_idcache_add(server->global_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire, NULL);
+           silc_idcache_del_by_context(server->local_list->clients, client);
+         }
+         server_entry = server_entry->router;
+       } else {
+         /* If the client is marked as local then move it to global list
+            since the server is global. */
+         if (server_entry->server_type != SILC_BACKUP_ROUTER && local) {
+           SILC_LOG_DEBUG(("Moving client to global list"));
+           silc_idcache_add(server->global_list->clients, client_cache->name,
+                            client_cache->id, client_cache->context,
+                            client_cache->expire, NULL);
+           silc_idcache_del_by_context(server->local_list->clients, client);
+         }
+       }
+
+       silc_idcache_list_free(list);
+       return server_entry;
+      }
+
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  }
+
+  silc_idcache_list_free(list);
+
+  return NULL;
+}
+
+/* Updates the clients that are originated from the `from' to be originated
+   from the `to'. If the `resolve_real_server' is TRUE then this will
+   attempt to figure out which clients really are originated from the
+   `from' and which are originated from a server that we have connection
+   to, when we've acting as backup router. If it is FALSE the `to' will
+   be the new source. This function also removes the clients that are
+   *really* originated from `from' if `remove_from' is TRUE. These are
+   clients that the `from' owns, and not just clients that are behind
+   the `from'. */
+
+void silc_server_update_clients_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to,
+                                         bool resolve_real_server,
+                                         bool remove_from)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+  bool local;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_LOG_DEBUG(("Updating %s", silc_id_render(from->id,
+                                               SILC_ID_SERVER)));
+  SILC_LOG_DEBUG(("to %s", silc_id_render(to->id,
+                                         SILC_ID_SERVER)));
+
+
+  local = FALSE;
+  if (silc_idcache_get_all(server->global_list->clients, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       SILC_LOG_DEBUG(("Client (global) %s", 
+                       silc_id_render(client->id, SILC_ID_CLIENT)));
+       if (client->router)
+         SILC_LOG_DEBUG(("Client->router (global) %s", 
+                         silc_id_render(client->router->id, SILC_ID_SERVER)));
+
+       if (client->router == from) {
+         /* Skip clients that are *really* owned by the `from' */
+         if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
+                                            client->id->ip.data_len)) {
+           SILC_LOG_DEBUG(("Found really owned client, skip it"));
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+           else
+             continue;
+         }
+
+         if (resolve_real_server) {
+           client->router = 
+             silc_server_update_clients_by_real_server(server, from, client,
+                                                       local, id_cache);
+           if (!client->router)
+             client->router = to;
+         } else {
+           client->router = to;
+         }
+       }
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  local = TRUE;
+  if (silc_idcache_get_all(server->local_list->clients, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       client = (SilcClientEntry)id_cache->context;
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
+       SILC_LOG_DEBUG(("Client (local) %s", 
+                       silc_id_render(client->id, SILC_ID_CLIENT)));
+       if (client->router)
+         SILC_LOG_DEBUG(("Client->router (local) %s", 
+                         silc_id_render(client->router->id, SILC_ID_SERVER)));
+
+       if (client->router == from) {
+         /* Skip clients that are *really* owned by the `from' */
+         if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
+                                            client->id->ip.data_len)) {
+           SILC_LOG_DEBUG(("Found really owned client, skip it"));
+           if (!silc_idcache_list_next(list, &id_cache))
+             break;
+           else
+             continue;
+         }
+
+         if (resolve_real_server) {
+           client->router = 
+             silc_server_update_clients_by_real_server(server, from, client,
+                                                       local, id_cache);
+           if (!client->router)
+             client->router = from; /* on local list put old from */
+         } else {
+           client->router = to;
+         }
+       }
+
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (remove_from)
+    /* Now remove the clients that are still marked as orignated from the
+       `from'. These are the clients that really was owned by the `from' and
+       not just exist behind the `from'. */
+    silc_server_remove_clients_by_server(server, from, TRUE);
+}
+
+/* Updates servers that are from `from' to be originated from `to'.  This
+   will also update the server's connection to `to's connection. */
+
+void silc_server_update_servers_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcServerEntry server_entry = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->local_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       if (server_entry->router == from) {
+         server_entry->router = to;
+         server_entry->connection = to->connection;
+       }
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+
+  if (silc_idcache_get_all(server->global_list->servers, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       server_entry = (SilcServerEntry)id_cache->context;
+       if (server_entry->router == from) {
+         server_entry->router = to;
+         server_entry->connection = to->connection;
+       }
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+}
+
+/* Removes channels that are from `from. */
+
+void silc_server_remove_channels_by_server(SilcServer server, 
+                                          SilcServerEntry from)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->global_list->channels, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+       if (channel->router == from)
+         silc_idlist_del_channel(server->global_list, channel);
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+}
+
+/* Updates channels that are from `from' to be originated from `to'.  */
+
+void silc_server_update_channels_by_server(SilcServer server, 
+                                          SilcServerEntry from,
+                                          SilcServerEntry to)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (silc_idcache_get_all(server->global_list->channels, &list)) {
+    if (silc_idcache_list_first(list, &id_cache)) {
+      while (id_cache) {
+       channel = (SilcChannelEntry)id_cache->context;
+       if (channel->router == from)
+         channel->router = to;
+       if (!silc_idcache_list_next(list, &id_cache))
+         break;
+      }
+    }
+    silc_idcache_list_free(list);
+  }
+}
+
+/* Checks whether given channel has global users.  If it does this returns
+   TRUE and FALSE if there is only locally connected clients on the channel. */
+
+bool silc_server_channel_has_global(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    if (chl->client->router) {
+      silc_hash_table_list_reset(&htl);
+      return TRUE;
+    }
+  }
+  silc_hash_table_list_reset(&htl);
+
+  return FALSE;
+}
+
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
+
+bool silc_server_channel_has_local(SilcChannelEntry channel)
+{
+  SilcChannelClientEntry chl;
+  SilcHashTableList htl;
+
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+    if (!chl->client->router) {
+      silc_hash_table_list_reset(&htl);
+      return TRUE;
+    }
+  }
+  silc_hash_table_list_reset(&htl);
+
+  return FALSE;
+}
+
+/* Returns TRUE if the given client is on the channel.  FALSE if not. 
+   This works because we assure that the user list on the channel is
+   always in up to date thus we can only check the channel list from 
+   `client' which is faster than checking the user list from `channel'. */
+
+bool silc_server_client_on_channel(SilcClientEntry client,
+                                  SilcChannelEntry channel)
+{
+  if (!client || !channel)
+    return FALSE;
+
+  return silc_hash_table_find(client->channels, channel, NULL, NULL);
+}
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+
+bool silc_server_name_bad_chars(const char *name, uint32 name_len)
+{
+  int i;
+
+  for (i = 0; i < name_len; i++) {
+    if (!isascii(name[i]))
+      return TRUE;
+    if (name[i] <= 32) return TRUE;
+    if (name[i] == ' ') return TRUE;
+    if (name[i] == '*') return TRUE;
+    if (name[i] == '?') return TRUE;
+    if (name[i] == ',') return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Modifies the `name' if it includes bad characters and returns new
+   allocated name that does not include bad characters. */
+
+char *silc_server_name_modify_bad(const char *name, uint32 name_len)
+{
+  int i;
+  char *newname = strdup(name);
+
+  for (i = 0; i < name_len; i++) {
+    if (!isascii(newname[i])) newname[i] = '_';
+    if (newname[i] <= 32) newname[i] = '_';
+    if (newname[i] == ' ') newname[i] = '_';
+    if (newname[i] == '*') newname[i] = '_';
+    if (newname[i] == '?') newname[i] = '_';
+    if (newname[i] == ',') newname[i] = '_';
+  }
+
+  return newname;
+}
diff --git a/apps/silcd/server_util.h b/apps/silcd/server_util.h
new file mode 100644 (file)
index 0000000..9cfe160
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+
+  server_util.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SERVER_UTIL_H
+#define SERVER_UTIL_H
+
+/* This function is used to remove all client entries by the server `entry'.
+   This is called when the connection is lost to the server. In this case
+   we must invalidate all the client entries owned by the server `entry'. 
+   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
+   distributed to our local clients. */
+bool silc_server_remove_clients_by_server(SilcServer server, 
+                                         SilcServerEntry entry,
+                                         bool server_signoff);
+
+/* Updates the clients that are originated from the `from' to be originated
+   from the `to'. If the `resolve_real_server' is TRUE then this will
+   attempt to figure out which clients really are originated from the
+   `from' and which are originated from a server that we have connection
+   to, when we've acting as backup router. If it is FALSE the `to' will
+   be the new source. This function also removes the clients that are
+   *really* originated from `from' if `remove_from' is TRUE. These are
+   clients that the `from' owns, and not just clients that are behind
+   the `from'. */
+void silc_server_update_clients_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to,
+                                         bool resolve_real_server,
+                                         bool remove_from);
+
+/* Updates servers that are from `from' to be originated from `to'.  This
+   will also update the server's connection to `to's connection. */
+void silc_server_update_servers_by_server(SilcServer server, 
+                                         SilcServerEntry from,
+                                         SilcServerEntry to);
+
+/* Removes channels that are from `from. */
+void silc_server_remove_channels_by_server(SilcServer server, 
+                                          SilcServerEntry from);
+
+/* Updates channels that are from `from' to be originated from `to'.  */
+void silc_server_update_channels_by_server(SilcServer server, 
+                                          SilcServerEntry from,
+                                          SilcServerEntry to);
+
+/* Checks whether given channel has global users.  If it does this returns
+   TRUE and FALSE if there is only locally connected clients on the channel. */
+bool silc_server_channel_has_global(SilcChannelEntry channel);
+
+/* Checks whether given channel has locally connected users.  If it does this
+   returns TRUE and FALSE if there is not one locally connected client. */
+bool silc_server_channel_has_local(SilcChannelEntry channel);
+
+/* Returns TRUE if the given client is on the channel.  FALSE if not. 
+   This works because we assure that the user list on the channel is
+   always in up to date thus we can only check the channel list from 
+   `client' which is faster than checking the user list from `channel'. */
+bool silc_server_client_on_channel(SilcClientEntry client,
+                                  SilcChannelEntry channel);
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+bool silc_server_name_bad_chars(const char *name, uint32 name_len);
+
+/* Modifies the `nick' if it includes bad characters and returns new
+   allocated nickname that does not include bad characters. */
+char *silc_server_name_modify_bad(const char *name, uint32 name_len);
+
+#endif /* SERVER_UTIL_H */
index 22984a51326514a927311be184ef7a070ff4d694..1c64390ada5d70d521ed9960f6db826236189370 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #include "serverincludes.h"
+#include "version_internal.h"
 
-const char server_version[] = "27062000";
+const char *server_version = SILC_DIST_VERSION_STRING;
index 2f617c2621c8676c31b8513a41c081c924461391..e51039d619484e970556ec1c4765f1d4c85226fe 100644 (file)
 
   serverconfig.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Johnny Mnemonic <johnny@themnemonic.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2002 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 
-/*  XXX
-   All possible configuration sections for SILC server. 
-
-   <Cipher>
-
-       Format:
-
-       +<Cipher name>:<SIM path>
-
-   <PKCS>
-
-       Format:
-
-       +<PKCS name>:<key length>
-
-   <HashFunction>
-
-       Format:
-
-       +<Hash function name>:<SIM path>
-
-   <ServerInfo>
-
-       This section is used to set the server informations.
-
-       Format:
-
-       +<Server DNS name>:<Server IP>:<Geographic location>:<Port>
-
-   <AdminInfo>
-
-       This section is used to set the server's administrative information.
-
-       Format:
-
-       +<Location>:<Server type>:<Admin's name>:<Admin's email address>
-
-   <ListenPort>
-
-       This section is used to set ports the server is listenning.
-
-       Format:
-
-       +<Local IP/UNIX socket path>:<Remote IP>:<Port>
-
-   <Logging>
-
-       This section is used to set various logging files, their paths
-       and maximum sizes. All the other directives except those defined
-       below are ignored in this section. Log files are purged after they
-       reach the maximum set byte size.
-
-       Format:
-
-       +infologfile:<path>:<max byte size>
-       +errorlogfile:<path>:<max byte size>
-
-   <ConnectionClass>
-
-       This section is used to define connection classes. These can be
-       used to optimize the server and the connections.
-
-       Format:
-
-       +<Class number>:<Ping freq>:<Connect freq>:<Max links>
-
-   <ClientAuth>
-
-       This section is used to define client authentications.
-
-       Format:
-
-       +<Remote address or name>:<auth method>:<password/cert/key/???>:<Port>:<Class>
-
-   <AdminAuth>
-
-       This section is used to define the server's administration 
-       authentications.
-
-       Format:
-
-       +<Hostname>:<auth method>:<password/cert/key/???>:<Nickname hash>:<Class>
-
-   <ServerConnection>
+#define SILC_CONFIG_SERVER_AUTH_METH_PASSWD "passwd"
+#define SILC_CONFIG_SERVER_AUTH_METH_PUBKEY "pubkey"
 
-       This section is used to define the server connections to this
-       server/router. Only routers can have normal server connections.
-       Normal servers leave this section epmty. The remote server cannot be
-       older than specified Version ID.
-
-       Format:
-
-       +<Remote address or name>:<auth method>:<password/key/???>:<Port>:<Version ID>:<Class>
-
-   <RouterConnection>
-
-       This section is used to define the router connections to this
-       server/router. Both normal server and router can have router
-       connections. Normal server usually has only one connection while
-       a router can have multiple. The remote server cannot be older than
-       specified Version ID.
-
-       Format:
-
-       +<Remote address or name>:<auth method>:<password/key/???>:<Port>:<Version ID>:<Class>
-
-   <DenyConnection>
-
-       This section is used to deny specific connections to your server. This
-       can be used to deny both clients and servers.
-
-       Format:
-
-       +<Remote address or name or nickname>:<Time interval>:<Comment>:<Port>
-
-   <RedirectClient>
-
-       This section is used to set the alternate servers that clients will be
-       redirected to when our server is full.
-
-       Format:
-
-       +<Remote address or name>:<Port>
+#if 0
+#define SERVER_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SERVER_CONFIG_DEBUG(fmt)
+#endif
 
-*/
-SilcConfigServerSection silc_config_server_sections[] = {
-  { "[Cipher]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
-  { "[PKCS]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 2 },
-  { "[HashFunction]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
-  { "[ServerInfo]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
-  { "[AdminInfo]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO, 4 },
-  { "[ListenPort]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT, 3 },
-  { "[Logging]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING, 3 },
-  { "[ConnectionClass]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS, 4 },
-  { "[ClientConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION, 5 },
-  { "[ServerConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION, 6 },
-  { "[RouterConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 6 },
-  { "[AdminConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION, 5 },
-  { "[DenyConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION, 4 },
-  { "[RedirectClient]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT, 2 },
-  
-  { NULL, SILC_CONFIG_SERVER_SECTION_TYPE_NONE, 0 }
-};
+/* auto-declare needed variables for the common list parsing */
+#define SILC_SERVER_CONFIG_SECTION_INIT(__type__)                      \
+  SilcServerConfig config = (SilcServerConfig) context;                        \
+  __type__ *findtmp, *tmp = (__type__ *) config->tmp;                  \
+  int got_errno = 0
+
+/* append the tmp field to the specified list */
+#define SILC_SERVER_CONFIG_LIST_APPENDTMP(__list__)                    \
+  if (!__list__)                                                       \
+    __list__ = tmp;                                                    \
+  else {                                                               \
+    for (findtmp = __list__; findtmp->next; findtmp = findtmp->next);  \
+    findtmp->next = tmp;                                               \
+  }
 
-/* Allocates a new configuration object, opens configuration file and
-   parses the file. The parsed data is returned to the newly allocated
-   configuration object. */
+/* loops all elements in a list and provides a di struct pointer of the
+ * specified type containing the current element */
+#define SILC_SERVER_CONFIG_LIST_DESTROY(__type__, __list__)            \
+  for (tmp = (void *) __list__; tmp;) {                                        \
+    __type__ *di = (__type__ *) tmp;                                   \
+    tmp = (void *) di->next;
 
-SilcConfigServer silc_config_server_alloc(char *filename)
+/* free an authdata according to its auth method */
+static void my_free_authdata(SilcAuthMethod auth_meth, void *auth_data)
 {
-  SilcConfigServer new;
-  SilcBuffer buffer;
-  SilcConfigServerParse config_parse;
-
-  SILC_LOG_DEBUG(("Allocating new configuration object"));
-
-  new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    fprintf(stderr, "Could not allocate new configuration object");
-    return NULL;
+  if (auth_meth == SILC_AUTH_PASSWORD) {
+    silc_free(auth_data);
+  } else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
+    silc_pkcs_public_key_free((SilcPublicKey) auth_data);
   }
-
-  new->filename = filename;
-
-  /* Open configuration file and parse it */
-  config_parse = NULL;
-  buffer = NULL;
-  silc_config_open(filename, &buffer);
-  if (!buffer)
-    goto fail;
-  if ((silc_config_server_parse(new, buffer, &config_parse)) == FALSE)
-    goto fail;
-  if ((silc_config_server_parse_lines(new, config_parse)) == FALSE)
-    goto fail;
-
-  silc_free(buffer);
-
-  return new;
-
- fail:
-  silc_free(new);
-  return NULL;
 }
 
-/* Free's a configuration object. */
-
-void silc_config_server_free(SilcConfigServer config)
+/* parse an authdata according to its auth method */
+static bool my_parse_authdata(SilcAuthMethod auth_meth, char *p, uint32 line,
+                             void **auth_data, uint32 *auth_data_len)
 {
-  if (config) {
-    silc_free(config->filename);
-    silc_free(config->server_info);
-    silc_free(config->admin_info);
-    silc_free(config->listen_port);
-    silc_free(config->conn_class);
-    silc_free(config->clients);
-    silc_free(config->admins);
-    silc_free(config->servers);
-    silc_free(config->routers);
-    silc_free(config->denied);
-    silc_free(config->redirect);
-    silc_free(config);
+  if (auth_meth == SILC_AUTH_PASSWORD) {
+    /* p is a plain text password */
+    *auth_data = (void *) strdup(p);
+    *auth_data_len = (uint32) strlen(p);
+  } else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
+    /* p is a public key */
+    SilcPublicKey public_key;
+
+    if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_BIN)) {
+       fprintf(stderr, "\nError while parsing config file at line %lu: "
+               "Could not load public key file!\n", line);
+       return FALSE;
+      }
+    *auth_data = (void *) public_key;
+    *auth_data_len = 0;
+  } else {
+    fprintf(stderr, "\nError while parsing config file at line %lu: Specify "
+               "the AuthMethod before specifying the AuthData.\n", line);
+    return FALSE;
   }
+  return TRUE;
 }
 
-/* Parses the the buffer and returns the parsed lines into return_config
-   argument. The return_config argument doesn't have to be initialized 
-   before calling this. It will be initialized during the parsing. The
-   buffer sent as argument can be safely free'd after this function has
-   succesfully returned. */
+/* Callbacks */
 
-int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer, 
-                            SilcConfigServerParse *return_config)
+SILC_CONFIG_CALLBACK(fetch_generic)
 {
-  int i, begin;
-  unsigned int linenum;
-  char line[1024], *cp;
-  SilcConfigServerSection *cptr = NULL;
-  SilcConfigServerParse parse = *return_config, first = NULL;
-
-  SILC_LOG_DEBUG(("Parsing configuration file"));
-
-  begin = 0;
-  linenum = 0;
-  while((begin = silc_gets(line, sizeof(line), 
-                          buffer->data, buffer->len, begin)) != EOF) {
-    cp = line;
-    linenum++;
-
-    /* Check for bad line */
-    if (silc_check_line(cp))
-      continue;
-
-    /* Remove tabs and whitespaces from the line */
-    if (strchr(cp, '\t')) {
-      i = 0;
-      while(strchr(cp + i, '\t')) {
-       *strchr(cp + i, '\t') = ' ';
-       i++;
-      }
-    }
-    for (i = 0; i < strlen(cp); i++) {
-      if (cp[i] != ' ') {
-       if (i)
-         cp++;
-       break;
-      }
-      cp++;
-    }
-
-    /* Parse line */
-    switch(cp[0]) {
-    case '[':
-      /*
-       * Start of a section
-       */
-
-      /* Remove new line sign */
-      if (strchr(cp, '\n'))
-       *strchr(cp, '\n') = '\0';
-      
-      /* Check for matching sections */
-      for (cptr = silc_config_server_sections; cptr->section; cptr++)
-       if (!strcmp(cp, cptr->section))
-         break;
+  SilcServerConfig config = (SilcServerConfig) context;
 
-      if (!cptr->section) {
-       fprintf(stderr, "%s:%d: Unknown section `%s'\n", 
-                       config->filename, linenum, cp);
-       return FALSE;
-      }
+  if (!strcmp(name, "modulepath")) {
+    if (config->module_path) return SILC_CONFIG_EDOUBLE;
+    /* dup it only if non-empty, otherwise point it to NULL */
+    config->module_path = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+}
 
-      break;
-    default:
-      /*
-       * Start of a configuration line
-       */
-
-      if (cptr->type != SILC_CONFIG_SERVER_SECTION_TYPE_NONE) {
-       
-       if (strchr(cp, '\n'))
-           *strchr(cp, '\n') = ':';
-
-       if (parse == NULL) {
-         parse = silc_calloc(1, sizeof(*parse));
-         parse->line = NULL;
-         parse->section = NULL;
-         parse->next = NULL;
-         parse->prev = NULL;
-       } else {
-         if (parse->next == NULL) {
-           parse->next = silc_calloc(1, sizeof(*parse->next));
-           parse->next->line = NULL;
-           parse->next->section = NULL;
-           parse->next->next = NULL;
-           parse->next->prev = parse;
-           parse = parse->next;
-         }
-       }
-       
-       if (first == NULL)
-         first = parse;
-
-       /* Add the line to parsing structure for further parsing. */
-       if (parse) {
-         parse->section = cptr;
-         parse->line = silc_buffer_alloc(strlen(cp) + 1);
-         parse->linenum = linenum;
-         silc_buffer_pull_tail(parse->line, strlen(cp));
-         silc_buffer_put(parse->line, cp, strlen(cp));
-       }
-      }
-      break;
+SILC_CONFIG_CALLBACK(fetch_cipher)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionCipher);
+
+  SERVER_CONFIG_DEBUG(("Received CIPHER type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->name) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
     }
+    /* the temporary struct is ok, append it to the list */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->cipher);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionCipher *) config->tmp;
   }
-  
-  /* Set the return_config argument to its first value so that further
-     parsing can be started from the first line. */
-  *return_config = first;
 
-  return TRUE;
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "module")) { /* can be empty */
+    if (tmp->module) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    /* dup it only if non-empty, otherwise point it to NULL */
+    tmp->module = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "key_length"))
+    tmp->key_length = *(uint32 *)val;
+  else if (!strcmp(name, "block_length"))
+    tmp->block_length = *(uint32 *)val;
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp->module);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
 }
 
-/* Parses the lines earlier read from configuration file. The config object
-   must not be initialized, it will be initialized in this function. The
-   parse_config argument is uninitialized automatically during this
-   function. */
-
-int silc_config_server_parse_lines(SilcConfigServer config, 
-                                  SilcConfigServerParse parse_config)
+SILC_CONFIG_CALLBACK(fetch_hash)
 {
-  int ret, check = FALSE;
-  unsigned int checkmask;
-  char *tmp;
-  SilcConfigServerParse pc = parse_config;
-  SilcBuffer line;
-
-  SILC_LOG_DEBUG(("Parsing configuration lines"));
-  
-  if (!config)
-    return FALSE;
-  
-  checkmask = 0;
-  while(pc) {
-    check = FALSE;
-    line = pc->line;
-
-    /* Get number of tokens in line */
-    ret = silc_config_check_num_token(line);
-    if (ret != pc->section->maxfields) {
-      /* Bad line */
-      fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
-             config->filename, pc->linenum, ret, 
-             pc->section->maxfields);
-      break;
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionHash);
+
+  SERVER_CONFIG_DEBUG(("Received HASH type=%d name=%s (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->name || (tmp->block_length == 0) || (tmp->digest_length == 0)) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
     }
+    /* the temporary struct in tmp is ok */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hash);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionHash *) config->tmp;
+  }
 
-    /* Parse the line */
-    switch(pc->section->type) {
-    case SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->cipher);
-
-      /* Get cipher name */
-      ret = silc_config_get_token(line, &config->cipher->alg_name);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-
-      /* Get module name */
-      config->cipher->sim_name = NULL;
-      ret = silc_config_get_token(line, &config->cipher->sim_name);
-      if (ret < 0)
-       break;
-
-      /* Get block length */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher block length not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      config->cipher->block_len = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get key length */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Cipher key length not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      config->cipher->key_len = atoi(tmp);
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_PKCS:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->pkcs);
-
-      /* Get PKCS name */
-      ret = silc_config_get_token(line, &config->pkcs->alg_name);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: PKCS name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-
-      /* Get key length */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: PKCS key length not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      config->pkcs->key_len = atoi(tmp);
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->hash_func);
-
-      /* Get Hash function name */
-      ret = silc_config_get_token(line, &config->hash_func->alg_name);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      
-      /* Get Hash function module name */
-      config->hash_func->sim_name = NULL;
-      ret = silc_config_get_token(line, &config->hash_func->sim_name);
-      if (ret < 0)
-       break;
-
-      /* Get block length */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function block length not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      config->hash_func->block_len = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get hash length */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      config->hash_func->key_len = atoi(tmp);
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
-
-      if (!config->server_info)
-       config->server_info = silc_calloc(1, sizeof(*config->server_info));
-
-      /* Get server name */
-      ret = silc_config_get_token(line, &config->server_info->server_name);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Server name not defined */
-
-      }
-      
-      /* Get server IP */
-      ret = silc_config_get_token(line, &config->server_info->server_ip);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Server IP not defined */
-
-      }
-
-      /* Get server location */
-      ret = silc_config_get_token(line, &config->server_info->location);
-      if (ret < 0)
-       break;
-
-      /* Get server port */
-      /* XXX: Need port here??? */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Port not defined */
-
-      }
-      config->server_info->port = atoi(tmp);
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO:
-
-      if (!config->admin_info)
-       config->admin_info = silc_calloc(1, sizeof(*config->admin_info));
-
-      /* Get server type */
-      ret = silc_config_get_token(line, &config->admin_info->server_type);
-      if (ret < 0)
-       break;
-
-      /* Get admins name */
-      ret = silc_config_get_token(line, &config->admin_info->admin_name);
-      if (ret < 0)
-       break;
-
-      /* Get admins email address */
-      ret = silc_config_get_token(line, &config->admin_info->admin_email);
-      if (ret < 0)
-       break;
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->listen_port);
-
-      /* Get host */
-      ret = silc_config_get_token(line, &config->listen_port->host);
-      if (ret < 0)
-       break;
-
-      /* Get remote IP */
-      ret = silc_config_get_token(line, &config->listen_port->remote_ip);
-      if (ret < 0)
-       break;
-
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Any port */
-       config->listen_port->port = 0;
-      } else {
-       config->listen_port->port = atoi(tmp);
-       silc_free(tmp);
-      }
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->conn_class);
-
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Class number not defined */
-
-      }
-      config->conn_class->class = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get ping frequency */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      config->conn_class->ping_freq = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get connect frequency */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      config->conn_class->connect_freq = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get max links */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      config->conn_class->max_links = atoi(tmp);
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->logging);
-
-      /* Get log section type and check it */
-      ret = silc_config_get_token(line, &config->logging->logtype);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Log file section not defined\n", 
-               config->filename, pc->linenum);
-       break;
-      }
-      if (strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_INFO)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_WARNING)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_ERROR)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
-       fprintf(stderr, "%s:%d: Unknown log file section '%s'\n",
-               config->filename, pc->linenum, config->logging->logtype);
-       break;
-      }
-
-      /* Get log filename */
-      ret = silc_config_get_token(line, &config->logging->filename);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Log file name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-
-      /* Get max byte size */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->logging->maxsize = atoi(tmp);
-       silc_free(tmp);
-      }
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->clients);
-
-      /* Get host */
-      ret = silc_config_get_token(line, &config->clients->host);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->clients->host = strdup("*");
-
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
-
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->clients->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
-
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->clients->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
-
-       silc_free(tmp);
-      }
-
-      /* Get authentication data */
-      ret = silc_config_get_token(line, &config->clients->auth_data);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->clients->host = strdup("*");
-
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       config->clients->port = atoi(tmp);
-       silc_free(tmp);
-      }
-
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->clients->class = atoi(tmp);
-       silc_free(tmp);
-      }
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->servers);
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "module")) { /* can be empty */
+    if (tmp->module) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    /* dup it only if non-empty, otherwise point it to NULL */
+    tmp->module = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "block_length"))
+    tmp->block_length = *(int *)val;
+  else if (!strcmp(name, "digest_length"))
+    tmp->digest_length = *(int *)val;
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp->module);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->servers->host);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->servers->host = strdup("*");
+SILC_CONFIG_CALLBACK(fetch_hmac)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionHmac);
+
+  SERVER_CONFIG_DEBUG(("Received HMAC type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->name || !tmp->hash || (tmp->mac_length == 0)) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    /* the temporary struct is ok, append it to the list */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hmac);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionHmac *) config->tmp;
+  }
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "hash")) {
+    if (tmp->hash) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->hash = strdup((char *) val);
+  }
+  else if (!strcmp(name, "mac_length"))
+    tmp->mac_length = *(int *)val;
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp->hash);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->servers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+SILC_CONFIG_CALLBACK(fetch_pkcs)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionPkcs);
+
+  SERVER_CONFIG_DEBUG(("Received PKCS type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->name) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    /* the temporary struct is ok, append it to the list */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->pkcs);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionPkcs *) config->tmp;
+  }
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->servers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    if (tmp->name) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->name = strdup((char *) val);
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_serverinfo)
+{
+  SilcServerConfig config = (SilcServerConfig) context;
+  SilcServerConfigSectionServerInfo *server_info = config->server_info;
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, &config->servers->auth_data);
-      if (ret < 0)
-       break;
+  /* if there isn't the struct alloc it */
+  if (!server_info) {
+    config->server_info = server_info = (SilcServerConfigSectionServerInfo *)
+               silc_calloc(1, sizeof(*server_info));
+  }
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->servers->port = atoi(tmp);
-       silc_free(tmp);
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check for mandatory inputs */
+    return SILC_CONFIG_OK;
+  }
+  if (!strcmp(name, "hostname")) {
+    if (server_info->server_name) return SILC_CONFIG_EDOUBLE;
+    server_info->server_name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "ip")) {
+    if (server_info->server_ip) return SILC_CONFIG_EDOUBLE;
+    server_info->server_ip = strdup((char *) val);
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      fprintf(stderr, "Invalid port number!\n");
+      return SILC_CONFIG_ESILENT;
+    }
+    server_info->port = (uint16) port;
+  }
+  else if (!strcmp(name, "servertype")) {
+    if (server_info->server_type) return SILC_CONFIG_EDOUBLE;
+    server_info->server_type = strdup((char *) val);
+  }
+  else if (!strcmp(name, "admin")) {
+    if (server_info->admin) return SILC_CONFIG_EDOUBLE;
+    server_info->admin = strdup((char *) val);
+  }
+  else if (!strcmp(name, "email")) {
+    if (server_info->email) return SILC_CONFIG_EDOUBLE;
+    server_info->email = strdup((char *) val);
+  }
+  else if (!strcmp(name, "location")) {
+    if (server_info->location) return SILC_CONFIG_EDOUBLE;
+    server_info->location = strdup((char *) val);
+  }
+  else if (!strcmp(name, "user")) {
+    if (server_info->user) return SILC_CONFIG_EDOUBLE;
+    server_info->user = strdup((char *) val);
+  }
+  else if (!strcmp(name, "group")) {
+    if (server_info->group) return SILC_CONFIG_EDOUBLE;
+    server_info->group = strdup((char *) val);
+  }
+  else if (!strcmp(name, "motdfile")) {
+    if (server_info->motd_file) return SILC_CONFIG_EDOUBLE;
+    server_info->motd_file = strdup((char *) val);
+  }
+  else if (!strcmp(name, "pidfile")) {
+    if (server_info->pid_file) return SILC_CONFIG_EDOUBLE;
+    server_info->pid_file = strdup((char *) val);
+  }
+  else if (!strcmp(name, "publickey")) {
+    char *tmp = (char *) val;
+
+    /* try to load specified file, if fail stop config parsing */
+    if (!silc_pkcs_load_public_key(tmp, &server_info->public_key,
+                                  SILC_PKCS_FILE_PEM))
+      if (!silc_pkcs_load_public_key(tmp, &server_info->public_key,
+                                    SILC_PKCS_FILE_BIN)) {
+       fprintf(stderr, "\nError: Could not load public key file.");
+       fprintf(stderr, "\n  line %lu: file \"%s\"\n", line, tmp);
+       return SILC_CONFIG_ESILENT;
       }
-
-      /* Get version */
-      ret = silc_config_get_token(line, &config->servers->version);
-      if (ret < 0)
-       break;
-
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->servers->class = atoi(tmp);
-       silc_free(tmp);
+  }
+  else if (!strcmp(name, "privatekey")) {
+    char *tmp = (char *) val;
+
+    /* try to load specified file, if fail stop config parsing */
+    if (!silc_pkcs_load_private_key(tmp, &server_info->private_key,
+                                   SILC_PKCS_FILE_BIN))
+      if (!silc_pkcs_load_private_key(tmp, &server_info->private_key,
+                                     SILC_PKCS_FILE_PEM)) {
+       fprintf(stderr, "\nError: Could not load private key file.");
+       fprintf(stderr, "\n  line %lu: file \"%s\"\n", line, tmp);
+       return SILC_CONFIG_ESILENT;
       }
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+}
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION:
+SILC_CONFIG_CALLBACK(fetch_logging)
+{
+  SilcServerConfig config = (SilcServerConfig) context;
+  SilcServerConfigSectionLogging *tmp =
+       (SilcServerConfigSectionLogging *) config->tmp;
+  int got_errno;
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->routers);
+  if (!strcmp(name, "quicklogs")) {
+    silc_log_quick = *(bool *)val;
+  }
+  else if (!strcmp(name, "flushdelay")) {
+    int flushdelay = *(int *)val;
+    if (flushdelay < 2) { /* this value was taken from silclog.h (min delay) */
+      fprintf(stderr, "Error: line %lu: invalid flushdelay value, use "
+               "quicklogs if you want real-time logging.\n", line);
+      return SILC_CONFIG_ESILENT;
+    }
+    silc_log_flushdelay = (long) flushdelay;
+  }
+#define FETCH_LOGGING_CHAN(__chan__, __member__)               \
+  else if (!strcmp(name, __chan__)) {                          \
+    if (!tmp) return SILC_CONFIG_OK;                           \
+    if (!tmp->file) {                                          \
+      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;       \
+    }                                                          \
+    config->__member__ = tmp;                                  \
+    config->tmp = NULL;                                                \
+  }
+  FETCH_LOGGING_CHAN("info", logging_info)
+  FETCH_LOGGING_CHAN("warnings", logging_warnings)
+  FETCH_LOGGING_CHAN("errors", logging_errors)
+  FETCH_LOGGING_CHAN("fatals", logging_fatals)
+#undef FETCH_LOGGING_CHAN
+  else if (!strcmp(name, "file")) {
+    if (!tmp) { /* FIXME: what the fuck is this? */
+      config->tmp = silc_calloc(1, sizeof(*tmp));
+      tmp = (SilcServerConfigSectionLogging *) config->tmp;
+    }
+    if (tmp->file) {
+      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;
+    }
+    tmp->file = strdup((char *) val);
+  }
+  else if (!strcmp(name, "size")) {
+    if (!tmp) {
+      config->tmp = silc_calloc(1, sizeof(*tmp));
+      tmp = (SilcServerConfigSectionLogging *) config->tmp;
+    }
+    tmp->maxsize = *(uint32 *) val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->file);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->routers->host);
-      if (ret < 0)
-       break;
-      //      if (ret == 0)
-      ///* Any host */
-      //       config->routers->host = strdup("*");
+SILC_CONFIG_CALLBACK(fetch_client)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionClient);
+
+  SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (tmp->auth_meth && !tmp->auth_data) {
+      fprintf(stderr, "\nError: line %lu: If you specify \"AuthMethod\" field "
+               "then you must also specify the \"AuthData\" field.\n", line);
+      got_errno = SILC_CONFIG_ESILENT;
+      goto got_err;
+    }
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->clients);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionClient *) config->tmp;
+  }
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) { /* any host (*) accepted */
+    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->host = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  /* get authentication method */
+  else if (!strcmp(name, "authmethod")) {
+    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
+      tmp->auth_meth = SILC_AUTH_PASSWORD;
+    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
+      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
+    else {
+      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
+    }
+  }
+  else if (!strcmp(name, "authdata")) {
+    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
+                          &tmp->auth_data, &tmp->auth_data_len)) {
+      got_errno = SILC_CONFIG_ESILENT;
+      goto got_err; /* error outputted in my_parse_authdata */
+    }
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      fprintf(stderr, "Invalid port number!\n");
+      got_errno = SILC_CONFIG_ESILENT; goto got_err;
+    }
+    tmp->port = (uint16) port;
+  }
+  /* FIXME: Improvement: use a direct class struct pointer instead of num */
+  else if (!strcmp(name, "class")) {
+    /* XXX do nothing */
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  my_free_authdata(tmp->auth_meth, tmp->auth_data);
+  silc_free(tmp);
+  return got_errno;
+}
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->routers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+SILC_CONFIG_CALLBACK(fetch_admin)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionAdmin);
+
+  SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->auth_meth) {
+      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;
+    }
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->admins);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionAdmin *) config->tmp;
+  }
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->routers->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) { /* any host (*) accepted */
+    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->host = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "user")) {
+    if (tmp->user) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->user = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "nick")) {
+    if (tmp->nick) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->nick = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  /* get authentication method */
+  else if (!strcmp(name, "authmethod")) {
+    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
+      tmp->auth_meth = SILC_AUTH_PASSWORD;
+    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
+      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
+    else {
+      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
+    }
+  }
+  else if (!strcmp(name, "authdata")) {
+    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
+                          &tmp->auth_data, &tmp->auth_data_len)) {
+      got_errno = SILC_CONFIG_ESILENT;
+      goto got_err; /* error outputted in my_parse_authdata */
+    }
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->user);
+  silc_free(tmp->nick);
+  my_free_authdata(tmp->auth_meth, tmp->auth_data);
+  silc_free(tmp);
+  return got_errno;
+}
 
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_deny)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionDeny);
+
+  SERVER_CONFIG_DEBUG(("Received DENY type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->reason) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->denied);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionDeny *) config->tmp;
+  }
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, &config->routers->auth_data);
-      if (ret < 0)
-       break;
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) { /* any host (*) accepted */
+    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->host = (*(char *)val ? strdup((char *) val) : strdup("*"));
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      fprintf(stderr, "Invalid port number!\n");
+      got_errno = SILC_CONFIG_ESILENT; goto got_err;
+    }
+    tmp->port = (uint16) port;
+  }
+  else if (!strcmp(name, "reason")) {
+    if (tmp->reason) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->reason = strdup((char *) val);
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->reason);
+  silc_free(tmp);
+  return got_errno;
+}
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->routers->port = atoi(tmp);
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_server)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionServer);
+
+  SERVER_CONFIG_DEBUG(("Received SERVER type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->auth_meth || !tmp->version) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    /* the temporary struct is ok, append it to the list */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->servers);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionServer *) config->tmp;
+  }
 
-      /* Get version */
-      ret = silc_config_get_token(line, &config->routers->version);
-      if (ret < 0)
-       break;
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) { /* any host (*) accepted */
+    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->host = (*(char *)val ? strdup((char *) val) : strdup("*"));
+  }
+  /* get authentication method */
+  else if (!strcmp(name, "authmethod")) {
+    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
+      tmp->auth_meth = SILC_AUTH_PASSWORD;
+    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
+      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
+    else {
+      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
+    }
+  }
+  else if (!strcmp(name, "authdata")) {
+    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
+                          &tmp->auth_data, &tmp->auth_data_len)) {
+      got_errno = SILC_CONFIG_ESILENT;
+      goto got_err; /* error outputted in my_parse_authdata */
+    }
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      fprintf(stderr, "Invalid port number!\n");
+      got_errno = SILC_CONFIG_ESILENT; goto got_err;
+    }
+    tmp->port = (uint16) port;
+  }
+  else if (!strcmp(name, "versionid")) {
+    if (tmp->version) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->version = strdup((char *) val);
+  }
+  /* FIXME: Improvement: use a direct class struct pointer instead of num */
+  else if (!strcmp(name, "class")) {
+    /* XXX do nothing */
+  }
+  else if (!strcmp(name, "backup")) {
+    tmp->backup_router = *(bool *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->version);
+  my_free_authdata(tmp->auth_meth, tmp->auth_data);
+  silc_free(tmp);
+  return got_errno;
+}
 
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->routers->class = atoi(tmp);
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_router)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigSectionRouter);
+
+  SERVER_CONFIG_DEBUG(("Received ROUTER type=%d name=\"%s\" (val=%x)", type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!tmp) /* empty sub-block? */
+      return SILC_CONFIG_OK;
+    if (!tmp->auth_meth || !tmp->version) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    /* the temporary struct is ok, append it to the list */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->routers);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  /* if there isn't a temporary struct alloc one */
+  if (!tmp) {
+    config->tmp = silc_calloc(1, sizeof(*findtmp));
+    tmp = (SilcServerConfigSectionRouter *) config->tmp;
+  }
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) {
+    if (tmp->host) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->host = strdup((char *) val);
+  }
+  else if (!strcmp(name, "authmethod")) {
+    if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
+      tmp->auth_meth = SILC_AUTH_PASSWORD;
+    else if (!strcmp((char *) val, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
+      tmp->auth_meth = SILC_AUTH_PUBLIC_KEY;
+    else {
+      got_errno = SILC_CONFIG_EINVALIDTEXT; goto got_err;
+    }
+  }
+  else if (!strcmp(name, "authdata")) {
+    if (!my_parse_authdata(tmp->auth_meth, (char *) val, line,
+                          &tmp->auth_data, &tmp->auth_data_len)) {
+      got_errno = SILC_CONFIG_ESILENT;
+      goto got_err; /* error outputted in my_parse_authdata */
+    }
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      fprintf(stderr, "Invalid port number!\n");
+      got_errno = SILC_CONFIG_ESILENT; goto got_err;
+    }
+    tmp->port = (uint16) port;
+  }
+  else if (!strcmp(name, "versionid")) {
+    if (tmp->version) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->version = strdup((char *) val);
+  }
+  /* FIXME: Improvement: use a direct class struct pointer instead of num */
+  else if (!strcmp(name, "class")) {
+    /* XXX do nothing */
+  }
+  else if (!strcmp(name, "initiator"))
+    tmp->initiator = *(bool *)val;
+  else if (!strcmp(name, "backuphost")) {
+    if (tmp->backup_replace_ip) { got_errno = SILC_CONFIG_EDOUBLE; goto got_err; }
+    tmp->backup_replace_ip = (*(char *)val ? strdup((char *) val) : strdup("*"));
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->version);
+  silc_free(tmp->backup_replace_ip);
+  my_free_authdata(tmp->auth_meth, tmp->auth_data);
+  silc_free(tmp);
+  return got_errno;
+}
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION:
+/* known config options tables */
+static const SilcConfigTable table_general[] = {
+  { "modulepath",      SILC_CONFIG_ARG_STRE,   fetch_generic,  NULL },
+  { 0, 0, 0, 0 }
+};
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->admins);
+static const SilcConfigTable table_cipher[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_cipher,   NULL },
+  { "module",          SILC_CONFIG_ARG_STRE,   fetch_cipher,   NULL },
+  { "key_length",      SILC_CONFIG_ARG_INT,    fetch_cipher,   NULL },
+  { "block_length",    SILC_CONFIG_ARG_INT,    fetch_cipher,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->admins->host);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->admins->host = strdup("*");
+static const SilcConfigTable table_hash[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_hash,     NULL },
+  { "module",          SILC_CONFIG_ARG_STRE,   fetch_hash,     NULL },
+  { "block_length",    SILC_CONFIG_ARG_INT,    fetch_hash,     NULL },
+  { "digest_length",   SILC_CONFIG_ARG_INT,    fetch_hash,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+static const SilcConfigTable table_hmac[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_hmac,     NULL },
+  { "hash",            SILC_CONFIG_ARG_STR,    fetch_hmac,     NULL },
+  { "mac_length",      SILC_CONFIG_ARG_INT,    fetch_hmac,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->admins->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+static const SilcConfigTable table_pkcs[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_pkcs,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->admins->auth_meth = SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY;
+static const SilcConfigTable table_serverinfo[] = {
+  { "hostname",                SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "ip",              SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_serverinfo, NULL},
+  { "servertype",      SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "location",                SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "admin",           SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "email",           SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "user",            SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "group",           SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "publickey",       SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "privatekey",      SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "motdfile",                SILC_CONFIG_ARG_STRE,   fetch_serverinfo, NULL},
+  { "pidfile",         SILC_CONFIG_ARG_STRE,   fetch_serverinfo, NULL},
+  { 0, 0, 0, 0 }
+};
 
-       silc_free(tmp);
-      }
+static const SilcConfigTable table_logging_c[] = {
+  { "file",            SILC_CONFIG_ARG_STR,    fetch_logging,  NULL },
+  { "size",            SILC_CONFIG_ARG_SIZE,   fetch_logging,  NULL },
+/*{ "quicklog",                SILC_CONFIG_ARG_NONE,   fetch_logging,  NULL }, */
+  { 0, 0, 0, 0 }
+};
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, &config->admins->auth_data);
-      if (ret < 0)
-       break;
+static const SilcConfigTable table_logging[] = {
+  { "quicklogs",       SILC_CONFIG_ARG_TOGGLE, fetch_logging,  NULL },
+  { "flushdelay",      SILC_CONFIG_ARG_INT,    fetch_logging,  NULL },
+  { "info",            SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { "warnings",                SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { "errors",          SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { "fatals",          SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get nickname */
-      ret = silc_config_get_token(line, &config->admins->nickname);
-      if (ret < 0)
-       break;
+/* still unsupported
+static const SilcConfigTable table_class[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_class,    NULL },
+  { "ping",            SILC_CONFIG_ARG_INT,    fetch_class,    NULL },
+  { "connect",         SILC_CONFIG_ARG_INT,    fetch_class,    NULL },
+  { "links",           SILC_CONFIG_ARG_INT,    fetch_class,    NULL },
+  { 0, 0, 0, 0 }
+}; */
+
+static const SilcConfigTable table_client[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_client,   NULL },
+  { "authmethod",      SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { "authdata",                SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_client,   NULL },
+  { "class",           SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->admins->class = atoi(tmp);
-       silc_free(tmp);
-      }
+static const SilcConfigTable table_admin[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
+  { "user",            SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
+  { "nick",            SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
+  { "authmethod",      SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
+  { "authdata",                SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_admin,    NULL },
+  { "class",           SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
+  { 0, 0, 0, 0 }
+};
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+static const SilcConfigTable table_deny[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_deny,     NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_deny,     NULL },
+  { "reason",          SILC_CONFIG_ARG_STR,    fetch_deny,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:
-      /* Not implemented yet */
-      check = TRUE;
-      break;
+static const SilcConfigTable table_serverconn[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_server,   NULL },
+  { "authmethod",      SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "authdata",                SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_server,   NULL },
+  { "versionid",       SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "class",           SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "backup",          SILC_CONFIG_ARG_TOGGLE, fetch_server,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT:
-      /* Not implemented yet */
-      check = TRUE;
-      break;
+static const SilcConfigTable table_routerconn[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
+  { "authmethod",      SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "authdata",                SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
+  { "versionid",       SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "class",           SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "initiator",       SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
+  { "backuphost",      SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
+  { "backupport",      SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
+  { "localbackup",     SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_NONE:
-    default:
-      /* Error */
-      break;
-    }
+static const SilcConfigTable table_main[] = {
+  { "general",         SILC_CONFIG_ARG_BLOCK,  NULL,           table_general },
+  { "cipher",          SILC_CONFIG_ARG_BLOCK,  fetch_cipher,   table_cipher },
+  { "hash",            SILC_CONFIG_ARG_BLOCK,  fetch_hash,     table_hash },
+  { "hmac",            SILC_CONFIG_ARG_BLOCK,  fetch_hmac,     table_hmac },
+  { "pkcs",            SILC_CONFIG_ARG_BLOCK,  fetch_pkcs,     table_pkcs },
+  { "serverinfo",      SILC_CONFIG_ARG_BLOCK,  fetch_serverinfo, table_serverinfo },
+  { "logging",         SILC_CONFIG_ARG_BLOCK,  NULL,           table_logging },
+/*{ "class",           SILC_CONFIG_ARG_BLOCK,  fetch_class,    table_class }, */
+  { "client",          SILC_CONFIG_ARG_BLOCK,  fetch_client,   table_client },
+  { "admin",           SILC_CONFIG_ARG_BLOCK,  fetch_admin,    table_admin },
+  { "deny",            SILC_CONFIG_ARG_BLOCK,  fetch_deny,     table_deny },
+  { "serverconnection",        SILC_CONFIG_ARG_BLOCK,  fetch_server,   table_serverconn },
+  { "routerconnection",        SILC_CONFIG_ARG_BLOCK,  fetch_router,   table_routerconn },
+  { 0, 0, 0, 0 }
+};
 
-    /* Check for error */
-    if (check == FALSE) {
-      /* Line could not be parsed */
-      fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
-      break;
+/* Allocates a new configuration object, opens configuration file and
+ * parses it. The parsed data is returned to the newly allocated
+ * configuration object. */
+
+SilcServerConfig silc_server_config_alloc(char *filename)
+{
+  SilcServerConfig config;
+  SilcConfigEntity ent;
+  SilcConfigFile *file;
+  int ret;
+  SILC_LOG_DEBUG(("Loading config data from `%s'", filename));
+
+  /* alloc a config object */
+  config = (SilcServerConfig) silc_calloc(1, sizeof(*config));
+  /* obtain a config file object */
+  file = silc_config_open(filename);
+  if (!file) {
+    fprintf(stderr, "\nError: can't open config file `%s'\n", filename);
+    return NULL;
+  }
+  /* obtain a SilcConfig entity, we can use it to start the parsing */
+  ent = silc_config_init(file);
+  /* load the known configuration options, give our empty object as context */
+  silc_config_register_table(ent, table_main, (void *) config);
+  /* enter the main parsing loop.  When this returns, we have the parsing
+   * result and the object filled (or partially, in case of errors). */
+  ret = silc_config_main(ent);
+  SILC_LOG_DEBUG(("Parser returned [ret=%d]: %s", ret, silc_config_strerror(ret)));
+
+  /* Check if the parser returned errors */
+  if (ret) {
+    /* handle this special error return which asks to quietly return */
+    if (ret != SILC_CONFIG_ESILENT) {
+      char *linebuf, *filename = silc_config_get_filename(file);
+      uint32 line = silc_config_get_line(file);
+      fprintf(stderr, "\nError while parsing config file: %s.\n",
+               silc_config_strerror(ret));
+      linebuf = silc_config_read_line(file, line);
+      fprintf(stderr, "  file %s line %lu:  %s\n\n", filename, line, linebuf);
+      silc_free(linebuf);
     }
+    return NULL;
+  }
+  /* close (destroy) the file object */
+  silc_config_close(file);
 
-    pc = pc->next;
-    /* XXXX */
-    //    silc_free(pc->prev);
-    //    pc->prev = NULL;
-  }
-
-  if (check == FALSE)
-    return FALSE;;
-
-  /* Check that all mandatory sections really were found. If not, the server
-     cannot function and we return error. */
-  ret = silc_config_server_check_sections(checkmask);
-  if (ret == FALSE) {
-    /* XXX */
-
-  }
-  
-  /* Before returning all the lists in the config object must be set
-     to their first values (the last value is first here). */
-  while (config->cipher && config->cipher->prev)
-    config->cipher = config->cipher->prev;
-  while (config->pkcs && config->pkcs->prev)
-    config->pkcs = config->pkcs->prev;
-  while (config->hash_func && config->hash_func->prev)
-    config->hash_func = config->hash_func->prev;
-  while (config->listen_port && config->listen_port->prev)
-    config->listen_port = config->listen_port->prev;
-  while (config->logging && config->logging->prev)
-    config->logging = config->logging->prev;
-  while (config->conn_class && config->conn_class->prev)
-    config->conn_class = config->conn_class->prev;
-  while (config->clients && config->clients->prev)
-    config->clients = config->clients->prev;
-  while (config->servers && config->servers->prev)
-    config->servers = config->servers->prev;
-  while (config->routers && config->routers->prev)
-    config->routers = config->routers->prev;
-  
-  SILC_LOG_DEBUG(("Done"));
-  
-  return TRUE;
+  /* XXX FIXME: check for missing mandatory fields */
+  if (!config->server_info) {
+    fprintf(stderr, "\nError: Missing mandatory block `server_info'\n");
+    return NULL;
+  }
+  return config;
 }
 
-/* This function checks that the mask sent as argument includes all the 
-   sections that are mandatory in SILC server. */
+/* ... */
 
-int silc_config_server_check_sections(unsigned int checkmask)
+void silc_server_config_destroy(SilcServerConfig config)
 {
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO))) {
-    
-    return FALSE;
+  void *tmp;
+  silc_free(config->module_path);
+
+  /* Destroy Logging channels */
+  if (config->logging_info)
+    silc_free(config->logging_info->file);
+  if (config->logging_warnings)
+    silc_free(config->logging_warnings->file);
+  if (config->logging_errors)
+    silc_free(config->logging_errors->file);
+  if (config->logging_fatals)
+    silc_free(config->logging_fatals->file);
+
+  /* Destroy the ServerInfo struct */
+  if (config->server_info) {
+    register SilcServerConfigSectionServerInfo *si = config->server_info;
+    silc_free(si->server_name);
+    silc_free(si->server_ip);
+    silc_free(si->server_type);
+    silc_free(si->location);
+    silc_free(si->admin);
+    silc_free(si->email);
+    silc_free(si->user);
+    silc_free(si->group);
+    silc_free(si->motd_file);
+    silc_free(si->pid_file);
   }
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO))) {
-    
-    return FALSE;
+
+  /* Now let's destroy the lists */
+
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionCipher,
+                                 config->cipher)
+    silc_free(di->name);
+    silc_free(di->module);
+    silc_free(di);
   }
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionHash, config->hash)
+    silc_free(di->name);
+    silc_free(di->module);
+    silc_free(di);
   }
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionHmac, config->hmac)
+    silc_free(di->name);
+    silc_free(di->hash);
+    silc_free(di);
   }
-  if (!(checkmask 
-       & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionPkcs, config->pkcs)
+    silc_free(di->name);
+    silc_free(di);
   }
-  if (!(checkmask 
-       & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionClient,
+                                 config->clients)
+    silc_free(di->host);
+    my_free_authdata(di->auth_meth, di->auth_data);
+    silc_free(di);
   }
-
-  return TRUE;
-}
-
-/* Sets log files where log messages is saved by the server. */
-
-void silc_config_server_setlogfiles(SilcConfigServer config)
-{
-  SilcConfigServerSectionLogging *log;
-  char *info, *warning, *error, *fatal;
-  unsigned int info_size, warning_size, error_size, fatal_size;
-
-  SILC_LOG_DEBUG(("Setting configured log file names"));
-
-  /* Set default files before checking configuration */
-  info = SILC_LOG_FILE_INFO;
-  warning = SILC_LOG_FILE_WARNING;
-  error = SILC_LOG_FILE_ERROR;
-  fatal = SILC_LOG_FILE_FATAL;
-  info_size = 0;
-  warning_size = 0;
-  error_size = 0;
-  fatal_size = 0;
-
-  log = config->logging;
-  while(log) {
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_INFO)) {
-      info = log->filename;
-      info_size = log->maxsize;
-    }
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_WARNING)) {
-      warning = log->filename;
-      warning_size = log->maxsize;
-    }
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_ERROR)) {
-      error = log->filename;
-      error_size = log->maxsize;
-    }
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
-      fatal = log->filename;
-      fatal_size = log->maxsize;
-    }
-
-    log = log->next;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionAdmin, config->admins)
+    silc_free(di->host);
+    silc_free(di->user);
+    silc_free(di->nick);
+    my_free_authdata(di->auth_meth, di->auth_data);
+    silc_free(di);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionDeny, config->denied)
+    silc_free(di->host);
+    silc_free(di->reason);
+    silc_free(di);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionServer,
+                                 config->servers)
+    silc_free(di->host);
+    silc_free(di->version);
+    my_free_authdata(di->auth_meth, di->auth_data);
+    silc_free(di);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigSectionRouter,
+                                 config->routers)
+    silc_free(di->host);
+    silc_free(di->version);
+    silc_free(di->backup_replace_ip);
+    my_free_authdata(di->auth_meth, di->auth_data);
+    silc_free(di);
   }
-
-  silc_log_set_files(info, info_size, warning, warning_size,
-                    error, error_size, fatal, fatal_size);
 }
 
 /* Registers configured ciphers. These can then be allocated by the
    server when needed. */
 
-void silc_config_server_register_ciphers(SilcConfigServer config)
+bool silc_server_config_register_ciphers(SilcServer server)
 {
-  SilcConfigServerSectionAlg *alg;
-  SilcServer server = (SilcServer)config->server;
+  SilcServerConfig config = server->config;
+  SilcServerConfigSectionCipher *cipher = config->cipher;
+  char *module_path = config->module_path;
 
   SILC_LOG_DEBUG(("Registering configured ciphers"));
 
-  alg = config->cipher;
-  while(alg) {
-
-    if (!alg->sim_name) {
-      /* Crypto module is supposed to be built in. Nothing to be done
-        here except to test that the cipher really is built in. */
-      SilcCipher tmp = NULL;
+  if (!cipher) /* any cipher in the config file? */
+    return FALSE;
 
-      if (silc_cipher_alloc(alg->alg_name, &tmp) == FALSE) {
-       SILC_LOG_ERROR(("Unsupported cipher `%s'", alg->alg_name));
+  while (cipher) {
+    /* if there isn't a module_path OR there isn't a module sim name try to
+     * use buil-in functions */
+    if (!module_path || !cipher->module) {
+      int i;
+      for (i = 0; silc_default_ciphers[i].name; i++)
+       if (!strcmp(silc_default_ciphers[i].name, cipher->name)) {
+         silc_cipher_register(&silc_default_ciphers[i]);
+         break;
+       }
+      if (!silc_cipher_is_supported(cipher->name)) {
+       SILC_LOG_ERROR(("Unknown cipher `%s'", cipher->name));
        silc_server_stop(server);
        exit(1);
       }
-      silc_cipher_free(tmp);
-
-#ifdef SILC_SIM
     } else {
+#ifdef SILC_SIM
       /* Load (try at least) the crypto SIM module */
-      SilcCipherObject cipher;
+      char buf[1023], *alg_name;
+      SilcCipherObject cipher_obj;
       SilcSimContext *sim;
 
-      memset(&cipher, 0, sizeof(cipher));
-      cipher.name = alg->alg_name;
-      cipher.block_len = alg->block_len;
-      cipher.key_len = alg->key_len * 8;
+      memset(&cipher_obj, 0, sizeof(cipher_obj));
+      cipher_obj.name = cipher->name;
+      cipher_obj.block_len = cipher->block_length;
+      cipher_obj.key_len = cipher->key_length * 8;
 
+      /* build the libname */
+      snprintf(buf, sizeof(buf), "%s/%s", config->module_path,
+               cipher->module);
       sim = silc_sim_alloc();
       sim->type = SILC_SIM_CIPHER;
-      sim->libname = alg->sim_name;
+      sim->libname = buf;
 
-      if ((silc_sim_load(sim))) {
-       cipher.set_key = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
+      alg_name = strdup(cipher->name);
+      if (strchr(alg_name, '-'))
+       *strchr(alg_name, '-') = '\0';
+
+      if (silc_sim_load(sim)) {
+       cipher_obj.set_key =
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_SET_KEY));
-       SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
-       cipher.set_key_with_string = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
+       SILC_LOG_DEBUG(("set_key=%p", cipher_obj.set_key));
+       cipher_obj.set_key_with_string =
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
-       SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
-       cipher.encrypt = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+       SILC_LOG_DEBUG(("set_key_with_string=%p", cipher_obj.set_key_with_string));
+       cipher_obj.encrypt =
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_ENCRYPT_CBC));
-       SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
-        cipher.decrypt = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+       SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher_obj.encrypt));
+        cipher_obj.decrypt =
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_DECRYPT_CBC));
-       SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
-        cipher.context_len = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+       SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher_obj.decrypt));
+        cipher_obj.context_len =
+         silc_sim_getsym(sim, silc_sim_symname(alg_name,
                                                SILC_CIPHER_SIM_CONTEXT_LEN));
-       SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
+       SILC_LOG_DEBUG(("context_len=%p", cipher_obj.context_len));
 
-       /* Put the SIM to the table of all SIM's in server */
-       server->sim = silc_realloc(server->sim,
-                                  sizeof(*server->sim) * 
-                                  (server->sim_count + 1));
-       server->sim[server->sim_count] = sim;
-       server->sim_count++;
+       /* Put the SIM to the list of all SIM's in server */
+       silc_dlist_add(server->sim, sim);
+
+       silc_free(alg_name);
       } else {
        SILC_LOG_ERROR(("Error configuring ciphers"));
        silc_server_stop(server);
@@ -1181,159 +1164,283 @@ void silc_config_server_register_ciphers(SilcConfigServer config)
       }
 
       /* Register the cipher */
-      silc_cipher_register(&cipher);
-#endif
-    }
-
-    alg = alg->next;
-  }
-}
-
-/* Registers configured PKCS's. */
-/* XXX: This really doesn't do anything now since we have statically
-   registered our PKCS's. This should be implemented when PKCS works
-   as SIM's. This checks now only that the PKCS user requested is 
-   really out there. */
-
-void silc_config_server_register_pkcs(SilcConfigServer config)
-{
-  SilcConfigServerSectionAlg *alg = config->pkcs;
-  SilcServer server = (SilcServer)config->server;
-  SilcPKCS tmp = NULL;
-
-  SILC_LOG_DEBUG(("Registering configured PKCS"));
-
-  while(alg) {
-
-    if (silc_pkcs_alloc(alg->alg_name, &tmp) == FALSE) {
-      SILC_LOG_ERROR(("Unsupported PKCS `%s'", alg->alg_name));
+      silc_cipher_register(&cipher_obj);
+#else
+      SILC_LOG_ERROR(("Dynamic module support not compiled, "
+                       "can't load modules!"));
       silc_server_stop(server);
       exit(1);
+#endif
     }
-    silc_free(tmp);
+    cipher = cipher->next;
+  } /* while */
 
-    alg = alg->next;
-  }
+  return TRUE;
 }
 
 /* Registers configured hash functions. These can then be allocated by the
    server when needed. */
 
-void silc_config_server_register_hashfuncs(SilcConfigServer config)
+bool silc_server_config_register_hashfuncs(SilcServer server)
 {
-  SilcConfigServerSectionAlg *alg;
-  SilcServer server = (SilcServer)config->server;
+  SilcServerConfig config = server->config;
+  SilcServerConfigSectionHash *hash = config->hash;
+  char *module_path = config->module_path;
 
   SILC_LOG_DEBUG(("Registering configured hash functions"));
 
-  alg = config->hash_func;
-  while(alg) {
-
-    if (!alg->sim_name) {
-      /* Hash module is supposed to be built in. Nothing to be done
-        here except to test that the hash function really is built in. */
-      SilcHash tmp = NULL;
+  if (!hash) /* any hash func in the config file? */
+    return FALSE;
 
-      if (silc_hash_alloc(alg->alg_name, &tmp) == FALSE) {
-       SILC_LOG_ERROR(("Unsupported hash function `%s'", alg->alg_name));
+  while (hash) {
+    /* if there isn't a module_path OR there isn't a module sim name try to
+     * use buil-in functions */
+    if (!module_path || !hash->module) {
+      int i;
+      for (i = 0; silc_default_hash[i].name; i++)
+       if (!strcmp(silc_default_hash[i].name, hash->name)) {
+         silc_hash_register(&silc_default_hash[i]);
+         break;
+       }
+      if (!silc_hash_is_supported(hash->name)) {
+       SILC_LOG_ERROR(("Unknown hash funtion `%s'", hash->name));
        silc_server_stop(server);
        exit(1);
       }
-      silc_free(tmp);
-
-#ifdef SILC_SIM
     } else {
+#ifdef SILC_SIM
       /* Load (try at least) the hash SIM module */
-      SilcHashObject hash;
+      SilcHashObject hash_obj;
       SilcSimContext *sim;
 
-      memset(&hash, 0, sizeof(hash));
-      hash.name = alg->alg_name;
-      hash.block_len = alg->block_len;
-      hash.hash_len = alg->key_len;
+      memset(&hash_obj, 0, sizeof(hash_obj));
+      hash_obj.name = hash->name;
+      hash_obj.block_len = hash->block_length;
+      hash_obj.hash_len = hash->digest_length;
 
       sim = silc_sim_alloc();
       sim->type = SILC_SIM_HASH;
-      sim->libname = alg->sim_name;
+      sim->libname = hash->module;
 
       if ((silc_sim_load(sim))) {
-       hash.init = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name, 
+       hash_obj.init =
+         silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_INIT));
-       SILC_LOG_DEBUG(("init=%p", hash.init));
-       hash.update = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+       SILC_LOG_DEBUG(("init=%p", hash_obj.init));
+       hash_obj.update =
+         silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_UPDATE));
-       SILC_LOG_DEBUG(("update=%p", hash.update));
-        hash.final = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+       SILC_LOG_DEBUG(("update=%p", hash_obj.update));
+        hash_obj.final =
+         silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_FINAL));
-       SILC_LOG_DEBUG(("final=%p", hash.final));
-        hash.context_len = 
-         silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+       SILC_LOG_DEBUG(("final=%p", hash_obj.final));
+        hash_obj.context_len =
+         silc_sim_getsym(sim, silc_sim_symname(hash->name,
                                                SILC_HASH_SIM_CONTEXT_LEN));
-       SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
+       SILC_LOG_DEBUG(("context_len=%p", hash_obj.context_len));
 
        /* Put the SIM to the table of all SIM's in server */
-       server->sim = silc_realloc(server->sim,
-                                  sizeof(*server->sim) * 
-                                  (server->sim_count + 1));
-       server->sim[server->sim_count] = sim;
-       server->sim_count++;
+       silc_dlist_add(server->sim, sim);
       } else {
        SILC_LOG_ERROR(("Error configuring hash functions"));
        silc_server_stop(server);
        exit(1);
       }
 
-      /* Register the cipher */
-      silc_hash_register(&hash);
+      /* Register the hash function */
+      silc_hash_register(&hash_obj);
+#else
+      SILC_LOG_ERROR(("Dynamic module support not compiled, "
+                       "can't load modules!"));
+      silc_server_stop(server);
+      exit(1);
 #endif
     }
+    hash = hash->next;
+  } /* while */
 
-    alg = alg->next;
-  }
+  return TRUE;
 }
 
-/* Returns client authentication information from server configuration
-   by host (name or ip). */
+/* Registers configure HMACs. These can then be allocated by the server
+   when needed. */
 
-SilcConfigServerSectionClientConnection *
-silc_config_server_find_client_conn(SilcConfigServer config, 
-                                   char *host, int port)
+bool silc_server_config_register_hmacs(SilcServer server)
 {
-  int i;
-  SilcConfigServerSectionClientConnection *client = NULL;
+  SilcServerConfig config = server->config;
+  SilcServerConfigSectionHmac *hmac = config->hmac;
+
+  SILC_LOG_DEBUG(("Registering configured HMACs"));
+
+  if (!hmac)
+    return FALSE;
+
+  while (hmac) {
+    SilcHmacObject hmac_obj;
+    if (!silc_hash_is_supported(hmac->hash)) {
+      SILC_LOG_ERROR(("Unknown hash function `%s'", hmac->hash));
+      silc_server_stop(server);
+      exit(1);
+    }
+    /* Register the HMAC */
+    memset(&hmac_obj, 0, sizeof(hmac_obj));
+    hmac_obj.name = hmac->name;
+    hmac_obj.len = hmac->mac_length;
+    silc_hmac_register(&hmac_obj);
+
+    hmac = hmac->next;
+  } /* while */
+
+  return TRUE;
+}
+
+/* Registers configured PKCS's. */
+
+bool silc_server_config_register_pkcs(SilcServer server)
+{
+  SilcServerConfig config = server->config;
+  SilcServerConfigSectionPkcs *pkcs = config->pkcs;
+
+  SILC_LOG_DEBUG(("Registering configured PKCS"));
+
+  if (!pkcs)
+    return FALSE;
+
+  while (pkcs) {
+    int i;
+    for (i = 0; silc_default_pkcs[i].name; i++)
+      if (!strcmp(silc_default_pkcs[i].name, pkcs->name)) {
+       silc_pkcs_register(&silc_default_pkcs[i]);
+       break;
+      }
+    if (!silc_pkcs_is_supported(pkcs->name)) {
+      SILC_LOG_ERROR(("Unknown PKCS `%s'", pkcs->name));
+      silc_server_stop(server);
+      exit(1);
+    }
+    pkcs = pkcs->next;
+  } /* while */
+
+  return TRUE;
+}
+
+/* Sets log files where log messages are saved by the server logger. */
+
+void silc_server_config_setlogfiles(SilcServerConfig config,
+                               SilcSchedule sked)
+{
+  SilcServerConfigSectionLogging *this;
+
+  SILC_LOG_DEBUG(("Setting configured log file names"));
+
+  if ((this = config->logging_info))
+    silc_log_set_file(SILC_LOG_INFO, this->file, this->maxsize, sked);
+  if ((this = config->logging_warnings))
+    silc_log_set_file(SILC_LOG_WARNING, this->file, this->maxsize, sked);
+  if ((this = config->logging_errors))
+    silc_log_set_file(SILC_LOG_ERROR, this->file, this->maxsize, sked);
+  if ((this = config->logging_fatals))
+    silc_log_set_file(SILC_LOG_FATAL, this->file, this->maxsize, sked);
+}
+
+/* Returns client authentication information from configuration file by host
+   (name or ip) */
+
+SilcServerConfigSectionClient *
+silc_server_config_find_client(SilcServerConfig config, char *host, int port)
+{
+  SilcServerConfigSectionClient *client;
 
+  if (!config || !port) {
+    SILC_LOG_WARNING(("Bogus: config_find_client(config=0x%08x, "
+                     "host=0x%08x \"%s\", port=%hu)",
+                     (uint32) config, (uint32) host, host, port));
+    return NULL;
+  }
   if (!host)
     return NULL;
 
-  if (!config->clients)
-    return NULL;
+  for (client = config->clients; client; client = client->next) {
+    if (client->host && !silc_string_compare(client->host, host))
+      continue;
+    if (client->port && (client->port != port))
+      continue;
+    break;
+  }
+  /* if none matched, then client is already NULL */
+  return client;
+}
 
-  client = config->clients;
+/* Returns admin connection configuration by host, username and/or
+   nickname. */
 
-  for (i = 0; client; i++) {
-    if (silc_string_compare(client->host, host))
-      break;
-    client = client->next;
+SilcServerConfigSectionAdmin *
+silc_server_config_find_admin(SilcServerConfig config,
+                             char *host, char *user, char *nick)
+{
+  SilcServerConfigSectionAdmin *admin;
+
+  /* make sure we have a value for the matching parameters */
+  if (!host)
+    host = "*";
+  if (!user)
+    user = "*";
+  if (!nick)
+    nick = "*";
+
+  for (admin = config->admins; admin; admin = admin->next) {
+    if (admin->host && !silc_string_compare(admin->host, host))
+      continue;
+    if (admin->user && !silc_string_compare(admin->user, user))
+      continue;
+    if (admin->nick && !silc_string_compare(admin->nick, nick))
+      continue;
+    /* no checks failed -> this entry matches */
+    break;
   }
+  /* if none matched, then admin is already NULL */
+  return admin;
+}
 
-  if (!client)
+/* Returns the denied connection configuration entry by host and port. */
+
+SilcServerConfigSectionDeny *
+silc_server_config_find_denied(SilcServerConfig config,
+                              char *host, uint16 port)
+{
+  SilcServerConfigSectionDeny *deny;
+
+  /* make sure we have a value for the matching parameters */
+  if (!config || !port) {
+    SILC_LOG_WARNING(("Bogus: config_find_denied(config=0x%08x, "
+                     "host=0x%08x \"%s\", port=%hu)",
+                     (uint32) config, (uint32) host, host, port));
+    return NULL;
+  }
+  if (!host)
     return NULL;
 
-  return client;
+  for (deny = config->denied; deny; deny = deny->next) {
+    if (deny->host && !silc_string_compare(deny->host, host))
+      continue;
+    break;
+  }
+  /* if none matched, then deny is already NULL */
+  return deny;
 }
 
-/* Returns server connection info from server configuartion by host 
-   (name or ip). */
+/* Returns server connection info from server configuartion by host
+   (name or ip). If `port' is non-null then both name or IP and the port
+   must match. */
 
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_server_conn(SilcConfigServer config, 
+SilcServerConfigSectionServer *
+silc_server_config_find_server_conn(SilcServerConfig config,
                                    char *host, int port)
 {
   int i;
-  SilcConfigServerSectionServerConnection *serv = NULL;
+  SilcServerConfigSectionServer *serv = NULL;
+  bool match = FALSE;
 
   if (!host)
     return NULL;
@@ -1344,7 +1451,14 @@ silc_config_server_find_server_conn(SilcConfigServer config,
   serv = config->servers;
   for (i = 0; serv; i++) {
     if (silc_string_compare(serv->host, host))
+      match = TRUE;
+
+    if (port && serv->port && serv->port != port)
+      match = FALSE;
+
+    if (match)
       break;
+
     serv = serv->next;
   }
 
@@ -1354,15 +1468,16 @@ silc_config_server_find_server_conn(SilcConfigServer config,
   return serv;
 }
 
-/* Returns router connection info from server configuartion by
+/* Returns router connection info from server configuration by
    host (name or ip). */
 
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_router_conn(SilcConfigServer config, 
+SilcServerConfigSectionRouter *
+silc_server_config_find_router_conn(SilcServerConfig config,
                                    char *host, int port)
 {
   int i;
-  SilcConfigServerSectionServerConnection *serv = NULL;
+  SilcServerConfigSectionRouter *serv = NULL;
+  bool match = FALSE;
 
   if (!host)
     return NULL;
@@ -1373,7 +1488,14 @@ silc_config_server_find_router_conn(SilcConfigServer config,
   serv = config->routers;
   for (i = 0; serv; i++) {
     if (silc_string_compare(serv->host, host))
+      match = TRUE;
+
+    if (port && serv->port && serv->port != port)
+      match = FALSE;
+
+    if (match)
       break;
+
     serv = serv->next;
   }
 
@@ -1383,64 +1505,43 @@ silc_config_server_find_router_conn(SilcConfigServer config,
   return serv;
 }
 
-/* Prints out example configuration file with default built in
-   configuration values. */
+/* Returns TRUE if configuration for a router connection that we are
+   initiating exists. */
 
-void silc_config_server_print()
+bool silc_server_config_is_primary_route(SilcServerConfig config)
 {
-  char *buf;
-
-  buf = "\
-#\n\
-# Automatically generated example SILCd configuration file with default\n\
-# built in values. Use this as a guide to configure your SILCd configuration\n\
-# file for your system. For detailed description of different configuration\n\
-# sections refer to silcd(8) manual page.\n\
-#\n";
-  /*
-#<Cipher>
-#+blowfish
-#+twofish
-#+rc5
-#+rc6
-#+3des
-
-#<HashFunction>
-#+md5
-#+sha1
-
-<ServerInfo>
-+lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1333
-
-<AdminInfo>
-+Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-<ListenPort>
-+10.2.1.6:10.2.1.6:1333
-
-<Logging>
-+infologfile:silcd.log:10000
-#+warninglogfile:/var/log/silcd_warning.log:10000
-#+errorlogfile:ERROR.log:10000
-#+fatallogfile:/var/log/silcd_error.log:
+  int i;
+  SilcServerConfigSectionRouter *serv = NULL;
+  bool found = FALSE;
 
-<ConnectionClass>
-               +1:100:100:100
-                       +2:200:300:400
+  serv = config->routers;
+  for (i = 0; serv; i++) {
+    if (serv->initiator == TRUE && serv->backup_router == FALSE) {
+      found = TRUE;
+      break;
+    }
 
-<ClientAuth>
-+10.2.1.199:priikone:333:1
+    serv = serv->next;
+  }
 
-<AdminAuth>
-+10.2.1.199:priikone:priikone:1
+  return found;
+}
 
-<ServerConnection>
+/* Returns our primary connection configuration or NULL if we do not
+   have primary router configured. */
 
-<RouterConnection>
+SilcServerConfigSectionRouter *
+silc_server_config_get_primary_router(SilcServerConfig config)
+{
+  int i;
+  SilcServerConfigSectionRouter *serv = NULL;
 
-<DenyConnection>
-<RedirectClient>
-  */
+  serv = config->routers;
+  for (i = 0; serv; i++) {
+    if (serv->initiator == TRUE && serv->backup_router == FALSE)
+      return serv;
+    serv = serv->next;
+  }
 
-  fprintf(stdout, "%s\n", buf);
+  return NULL;
 }
index 56ed10eb129e107081e2aad1f7fa0e4863798832..8be6d7cb7ccec9fec883c62321ae4fd72e781a51 100644 (file)
@@ -2,15 +2,15 @@
 
   serverconfig.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Johnny Mnemonic <johnny@themnemonic.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2002 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #ifndef SERVERCONFIG_H
 #define SERVERCONFIG_H
 
-/* Holds information of configured algorithms */
-typedef struct SilcConfigServerSectionAlgStruct {
-  char *alg_name;
-  char *sim_name;
-  unsigned int block_len;
-  unsigned int key_len;
-  struct SilcConfigServerSectionAlgStruct *next;
-  struct SilcConfigServerSectionAlgStruct *prev;
-#define SILC_CONFIG_SERVER_MODNAME "builtin"
-} SilcConfigServerSectionAlg;
-
-/* Holds server information from config file */
-typedef struct {
+typedef struct SilcServerConfigSectionCipherStruct {
+  char *name;
+  char *module;
+  uint32 key_length;
+  uint32 block_length;
+  struct SilcServerConfigSectionCipherStruct *next;
+} SilcServerConfigSectionCipher;
+
+typedef struct SilcServerConfigSectionHashStruct {
+  char *name;
+  char *module;
+  uint32 block_length;
+  uint32 digest_length;
+  struct SilcServerConfigSectionHashStruct *next;
+} SilcServerConfigSectionHash;
+
+typedef struct SilcServerConfigSectionHmacStruct {
+  char *name;
+  char *hash;
+  uint32 mac_length;
+  struct SilcServerConfigSectionHmacStruct *next;
+} SilcServerConfigSectionHmac;
+
+typedef struct SilcServerConfigSectionPkcsStruct {
+  char *name;
+  struct SilcServerConfigSectionPkcsStruct *next;
+} SilcServerConfigSectionPkcs;
+
+typedef struct SilcServerConfigSectionServerInfoStruct {
   char *server_name;
   char *server_ip;
-  char *location;
-  unsigned short port;
-} SilcConfigServerSectionServerInfo;
-
-/* Holds server's administrative information from config file */
-typedef struct {
-  char *server_type;
-  char *admin_name;
-  char *admin_email;
-} SilcConfigServerSectionAdminInfo;
-
-/* Holds all the ports the server is listenning on */
-typedef struct SilcConfigServerSectionListenPortStruct {
-  char *host;
-  char *remote_ip;
-  unsigned short port;
-  struct SilcConfigServerSectionListenPortStruct *next;
-  struct SilcConfigServerSectionListenPortStruct *prev;
-} SilcConfigServerSectionListenPort;
-
-/* Holds all the configured log files. */
-typedef struct SilcConfigServerSectionLoggingStruct {
-  char *logtype;
-  char *filename;
-  unsigned int maxsize;
-  struct SilcConfigServerSectionLoggingStruct *next;
-  struct SilcConfigServerSectionLoggingStruct *prev;
-
-/* Allowed <Logging> section types */
-#define SILC_CONFIG_SERVER_LF_INFO "infologfile"
-#define SILC_CONFIG_SERVER_LF_WARNING "warninglogfile"
-#define SILC_CONFIG_SERVER_LF_ERROR "errorlogfile"
-#define SILC_CONFIG_SERVER_LF_FATAL "fatalogfile"
-} SilcConfigServerSectionLogging;
+  uint16 port;
+  char *server_type;   /* E.g. "Test Server" */
+  char *location;      /* geographic location */
+  char *admin;         /* admin full name */
+  char *email;         /* admin's email address */
+  char *user;          /* userid the server should be runned at */
+  char *group;         /* ditto, but about groupid */
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
+  char *motd_file;     /* path to text motd file (reading only) */
+  char *pid_file;      /* path to the pid file (for reading and writing) */
+} SilcServerConfigSectionServerInfo;
+
+typedef struct SilcServerConfigSectionLoggingStruct {
+  char *file;
+  uint32 maxsize;
+} SilcServerConfigSectionLogging;
 
 /* Holds all configured connection classes */
-typedef struct SilcConfigServerSectionConnectionClassStruct {
-  unsigned int class;
-  unsigned int ping_freq;
-  unsigned int connect_freq;
-  unsigned int max_links;
-  struct SilcConfigServerSectionConnectionClassStruct *next;
-  struct SilcConfigServerSectionConnectionClassStruct *prev;
-} SilcConfigServerSectionConnectionClass;
-
-#define SILC_CONFIG_SERVER_AUTH_METH_PASSWD "passwd"
-#define SILC_CONFIG_SERVER_AUTH_METH_PUBKEY "pubkey"
+/* typedef struct SilcServerConfigSectionClassStruct {
+  uint32 class;
+  uint32 ping_freq;
+  uint32 connect_freq;
+  uint32 max_links;
+  struct SilcServerConfigSectionClassStruct *next;
+} SilcServerConfigSectionClass; */
 
 /* Holds all client authentication data from config file */
-typedef struct SilcConfigServerSectionClientConnectionStruct {
+typedef struct SilcServerConfigSectionClientStruct {
   char *host;
-  int auth_meth;
-  char *auth_data;
-  unsigned short port;
-  unsigned int class;
-  struct SilcConfigServerSectionClientConnectionStruct *next;
-  struct SilcConfigServerSectionClientConnectionStruct *prev;
-} SilcConfigServerSectionClientConnection;
-
-/* Hols all server's administrators authentication data from config file */
-typedef struct SilcConfigServerSectionAdminConnectionStruct {
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  uint16 port;
+  uint32 class;
+  struct SilcServerConfigSectionClientStruct *next;
+} SilcServerConfigSectionClient;
+
+/* Holds all server's administrators authentication data from config file */
+typedef struct SilcServerConfigSectionAdminStruct {
   char *host;
-  int auth_meth;
-  char *auth_data;
-  char *nickname;
-  unsigned int class;
-  struct SilcConfigServerSectionAdminConnectionStruct *next;
-  struct SilcConfigServerSectionAdminConnectionStruct *prev;
-} SilcConfigServerSectionAdminConnection;
-
-/* Holds all configured server/router connections from config file */
-typedef struct SilcConfigServerSectionServerConnectionStruct {
-  char *host;
-  int auth_meth;
-  char *auth_data;
-  unsigned short port;
-  char *version;
-  unsigned int class;
-  struct SilcConfigServerSectionServerConnectionStruct *next;
-  struct SilcConfigServerSectionServerConnectionStruct *prev;
-} SilcConfigServerSectionServerConnection;
+  char *user;
+  char *nick;
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  struct SilcServerConfigSectionAdminStruct *next;
+} SilcServerConfigSectionAdmin;
 
 /* Holds all configured denied connections from config file */
-typedef struct {
+typedef struct SilcServerConfigSectionDenyStruct {
   char *host;
-  char *time;
-  char *comment;
-  unsigned short port;
-} SilcConfigServerSectionDenyConnection;
+  uint16 port;
+  char *reason;
+  struct SilcServerConfigSectionDenyStruct *next;
+} SilcServerConfigSectionDeny;
 
-/* Holds all client redirections from config file */
-typedef struct {
+/* Holds all configured server connections from config file */
+typedef struct SilcServerConfigSectionServerStruct {
   char *host;
-  unsigned short port;
-} SilcConfigServerSectionRedirectClient;
-
-/* 
-   SILC Server Config object. 
-
-   This object holds all the data parsed from the SILC server configuration
-   file. This is mainly used at the initialization of the server.
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  uint16 port;
+  char *version;
+  uint32 class;
+  bool backup_router;
+  struct SilcServerConfigSectionServerStruct *next;
+} SilcServerConfigSectionServer;
 
-*/
-typedef struct {
-  /* Pointer back to the server */
-  void *server;
-
-  /* Filename of the configuration file */
-  char *filename;
-
-  /* Configuration sections */
-  SilcConfigServerSectionAlg *cipher;
-  SilcConfigServerSectionAlg *pkcs;
-  SilcConfigServerSectionAlg *hash_func;
-  SilcConfigServerSectionServerInfo *server_info;
-  SilcConfigServerSectionAdminInfo *admin_info;
-  SilcConfigServerSectionListenPort *listen_port;
-  SilcConfigServerSectionLogging *logging;
-  SilcConfigServerSectionConnectionClass *conn_class;
-  SilcConfigServerSectionClientConnection *clients;
-  SilcConfigServerSectionServerConnection *servers;
-  SilcConfigServerSectionServerConnection *routers;
-  SilcConfigServerSectionAdminConnection *admins;
-  SilcConfigServerSectionDenyConnection *denied;
-  SilcConfigServerSectionRedirectClient *redirect;
-} SilcConfigServerObject;
-
-typedef SilcConfigServerObject *SilcConfigServer;
-
-/* Configuration section type enumerations. */
-typedef enum {
-  SILC_CONFIG_SERVER_SECTION_TYPE_NONE = 0,
-  SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER,
-  SILC_CONFIG_SERVER_SECTION_TYPE_PKCS,
-  SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION,
-  SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO,
-  SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO,
-  SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT,
-  SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING,
-  SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS,
-  SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION,
-  SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION,
-  SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION,
-  SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION,
-  SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION,
-  SILC_CONFIG_SERVER_SECTION_TYPE_REDIRECT_CLIENT,
-} SilcConfigServerSectionType;
-
-/* SILC Configuration Section structure. */
+/* Holds all configured router connections from config file */
+typedef struct SilcServerConfigSectionRouterStruct {
+  char *host;
+  SilcAuthMethod auth_meth;
+  void *auth_data;
+  uint32 auth_data_len;
+  uint16 port;
+  char *version;
+  uint32 class;
+  bool initiator;
+  bool backup_router;
+  char *backup_replace_ip;
+  uint16 backup_replace_port;
+  bool backup_local;
+  struct SilcServerConfigSectionRouterStruct *next;
+} SilcServerConfigSectionRouter;
+
+/* define the SilcServerConfig object */
 typedef struct {
-  const char *section;
-  SilcConfigServerSectionType type;
-  unsigned int maxfields;
-} SilcConfigServerSection;
-
-/* LIst of all possible config sections in SILC server. */
-extern SilcConfigServerSection silc_config_server_sections[];
-
-/* Structure used in parsing the configuration lines. The line is read
-   from a file to this structure before parsing it further. */
-typedef struct SilcConfigServerParseStruct {
-  SilcBuffer line;
-  unsigned int linenum;
-  SilcConfigServerSection *section;
-  struct SilcConfigServerParseStruct *next;
-  struct SilcConfigServerParseStruct *prev;
-} *SilcConfigServerParse;
-
-/* Macros */
-
-/* Allocates list entries for configuration sections. Used by all
-   config sections as this is common. */
-#define SILC_SERVER_CONFIG_LIST_ALLOC(x)               \
-do {                                                   \
-  if (!(x)) {                                          \
-    (x) = silc_calloc(1, sizeof(*(x)));                        \
-    (x)->next = NULL;                                  \
-    (x)->prev = NULL;                                  \
-  } else {                                             \
-    if (!(x)->next) {                                  \
-      (x)->next = silc_calloc(1, sizeof(*(x)->next));  \
-      (x)->next->next = NULL;                          \
-      (x)->next->prev = (x);                           \
-      (x) = (x)->next;                                 \
-    }                                                  \
-  }                                                    \
-} while(0)
+  void *tmp;
+  char *module_path;
+
+  SilcServerConfigSectionCipher *cipher;
+  SilcServerConfigSectionHash *hash;
+  SilcServerConfigSectionHmac *hmac;
+  SilcServerConfigSectionPkcs *pkcs;
+  SilcServerConfigSectionLogging *logging_info;
+  SilcServerConfigSectionLogging *logging_warnings;
+  SilcServerConfigSectionLogging *logging_errors;
+  SilcServerConfigSectionLogging *logging_fatals;
+  SilcServerConfigSectionServerInfo *server_info;
+/*SilcServerConfigSectionClass *conn_class; */
+  SilcServerConfigSectionClient *clients;
+  SilcServerConfigSectionAdmin *admins;
+  SilcServerConfigSectionDeny *denied;
+  SilcServerConfigSectionServer *servers;
+  SilcServerConfigSectionRouter *routers;
+} *SilcServerConfig;
 
 /* Prototypes */
-SilcConfigServer silc_config_server_alloc(char *filename);
-void silc_config_server_free(SilcConfigServer config);
-int silc_config_server_parse(SilcConfigServer config, SilcBuffer buffer,
-                            SilcConfigServerParse *return_config);
-int silc_config_server_parse_lines(SilcConfigServer config, 
-                                  SilcConfigServerParse parse_config);
-int silc_config_server_check_sections(unsigned int checkmask);
-void silc_config_server_setlogfiles(SilcConfigServer config);
-void silc_config_server_register_ciphers(SilcConfigServer config);
-void silc_config_server_register_pkcs(SilcConfigServer config);
-void silc_config_server_register_hashfuncs(SilcConfigServer config);
-SilcConfigServerSectionClientConnection *
-silc_config_server_find_client_conn(SilcConfigServer config, 
-                                   char *host, int port);
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_server_conn(SilcConfigServer config, 
+
+/* basic config operations */
+SilcServerConfig silc_server_config_alloc(char *filename);
+void silc_server_config_destroy(SilcServerConfig config);
+
+/* algorithm registering and reset functions */
+bool silc_server_config_register_ciphers(SilcServer server);
+bool silc_server_config_register_hashfuncs(SilcServer server);
+bool silc_server_config_register_hmacs(SilcServer server);
+bool silc_server_config_register_pkcs(SilcServer server);
+void silc_server_config_setlogfiles(SilcServerConfig config, SilcSchedule sked);
+
+/* run-time config access functions */
+SilcServerConfigSectionClient *
+silc_server_config_find_client(SilcServerConfig config, char *host, int port);
+
+SilcServerConfigSectionAdmin *
+silc_server_config_find_admin(SilcServerConfig config,
+                             char *host, char *user, char *nick);
+
+SilcServerConfigSectionDeny *
+silc_server_config_find_denied(SilcServerConfig config,
+                              char *host, uint16 port);
+
+/* Prototypes - OLD */
+SilcServerConfigSectionServer *
+silc_server_config_find_server_conn(SilcServerConfig config,
                                    char *host, int port);
-SilcConfigServerSectionServerConnection *
-silc_config_server_find_router_conn(SilcConfigServer config, 
+SilcServerConfigSectionRouter *
+silc_server_config_find_router_conn(SilcServerConfig config,
                                    char *host, int port);
-void silc_config_server_print();
+bool silc_server_config_is_primary_route(SilcServerConfig config);
+SilcServerConfigSectionRouter *
+silc_server_config_get_primary_router(SilcServerConfig config);
 
-#endif
+#endif /* !SERVERCONFIG_H */
index 767faace3c1b002e3d17e2257919847aba6619bc..05cf1b35757d845041819ac146d5a2e0b7b29628 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
+#include "server_internal.h"
 
 /* Creates a Server ID. Newly created Server ID is returned to the
    new_id argument. */
 
-void silc_id_create_server_id(int sock, SilcRng rng, SilcServerID **new_id)
+void silc_id_create_server_id(const char *ip, uint16 port, SilcRng rng, 
+                             SilcServerID **new_id)
 {
-  struct sockaddr_in server;
-  int rval, len;
-
   SILC_LOG_DEBUG(("Creating new Server ID"));
 
   *new_id = silc_calloc(1, sizeof(**new_id));
-  if (*new_id == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new Server ID"));
-    return;
-  }
 
-  /* Get IP address */
-  len = sizeof(server);
-  rval = getsockname(sock, (struct sockaddr *)&server, &len);
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Could not get IP address: %s", strerror(errno)));
+  /* Create the ID */
+
+  if (!silc_net_addr2bin(ip, (*new_id)->ip.data, 
+                        sizeof((*new_id)->ip.data))) {
     silc_free(*new_id);
     *new_id = NULL;
     return;
   }
 
-  /* Create the ID */
-  (*new_id)->ip = server.sin_addr;
-  (*new_id)->port = server.sin_port;
+  (*new_id)->ip.data_len = silc_net_is_ip4(ip) ? 4 : 16;
+  (*new_id)->port = htons(port);
   (*new_id)->rnd = silc_rng_get_rn16(rng);
+
+  SILC_LOG_DEBUG(("New ID (%s)", silc_id_render(*new_id, SILC_ID_SERVER)));
 }
 
-/* Creates Client ID */
+/* Creates Client ID. This assures that there are no collisions in the
+   created Client IDs.  If the collision would occur (meaning that there
+   are 2^8 occurences of the `nickname' this will return FALSE, and the
+   caller must recall the function with different nickname. If this returns
+   TRUE the new ID was created successfully. */
 
-void silc_id_create_client_id(SilcServerID *server_id, SilcRng rng,
+bool silc_id_create_client_id(SilcServer server,
+                             SilcServerID *server_id, SilcRng rng,
                              SilcHash md5hash, char *nickname, 
                              SilcClientID **new_id)
 {
   unsigned char hash[16];
+  bool finding = FALSE;
 
   SILC_LOG_DEBUG(("Creating new Client ID"));
 
   *new_id = silc_calloc(1, sizeof(**new_id));
-  if (*new_id == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new Client ID"));
-    return;
-  }
 
   /* Create hash of the nickanem */
   silc_hash_make(md5hash, nickname, strlen(nickname), hash);
 
   /* Create the ID */
-  (*new_id)->ip.s_addr = server_id->ip.s_addr;
+  memcpy((*new_id)->ip.data, server_id->ip.data, server_id->ip.data_len);
+  (*new_id)->ip.data_len = server_id->ip.data_len;
   (*new_id)->rnd = silc_rng_get_byte(rng);
   memcpy((*new_id)->hash, hash, CLIENTID_HASH_LEN);
+
+  /* Assure that the ID does not exist already */
+  while (1) {
+    if (!silc_idlist_find_client_by_id(server->local_list, 
+                                      *new_id, FALSE, NULL))
+      if (!silc_idlist_find_client_by_id(server->global_list, 
+                                        *new_id, FALSE, NULL))
+       break;
+
+    /* The ID exists, start increasing the rnd from 0 until we find a
+       ID that does not exist. If we wrap and it still exists then we
+       will return FALSE and the caller must send some other nickname
+       since this cannot be used anymore. */
+    (*new_id)->rnd++;
+
+    if (finding && (*new_id)->rnd == 0)
+      return FALSE;
+
+    if (!finding) {
+      (*new_id)->rnd = 0;
+      finding = TRUE;
+    }
+  }
+
+  SILC_LOG_DEBUG(("New ID (%s)", silc_id_render(*new_id, SILC_ID_CLIENT)));
+
+  return TRUE;
 }
 
 /* Creates Channel ID */
 
-void silc_id_create_channel_id(SilcServerID *router_id, SilcRng rng,
+bool silc_id_create_channel_id(SilcServer server,
+                              SilcServerID *router_id, SilcRng rng,
                               SilcChannelID **new_id)
 {
+  bool finding = TRUE;
+
   SILC_LOG_DEBUG(("Creating new Channel ID"));
 
   *new_id = silc_calloc(1, sizeof(**new_id));
-  if (*new_id == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new Channel ID"));
-    return;
-  }
 
   /* Create the ID */
-  (*new_id)->ip.s_addr = router_id->ip.s_addr;
+  memcpy((*new_id)->ip.data, router_id->ip.data, router_id->ip.data_len);
+  (*new_id)->ip.data_len = router_id->ip.data_len;
   (*new_id)->port = router_id->port;
   (*new_id)->rnd = silc_rng_get_rn16(rng);
+
+  /* Assure that the ID does not exist already */
+  while (1) {
+    if (!silc_idlist_find_channel_by_id(server->local_list, 
+                                       *new_id, NULL))
+      break;
+
+    (*new_id)->rnd++;
+    
+    if (finding && (*new_id)->rnd == 0)
+      return FALSE;
+    
+    if (!finding) {
+      (*new_id)->rnd = 0;
+      finding = TRUE;
+    }
+  }
+
+  SILC_LOG_DEBUG(("New ID (%s)", silc_id_render(*new_id, SILC_ID_CHANNEL)));
+
+  return TRUE;
 }
index 2f5f858f5f1457ec7130e351e152c88888f7bf3a..9873ec2d67fcec8bde8d70e2ffdd0ca4110efc65 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #define SERVERID_H
 
 /* Prototypes */
-void silc_id_create_server_id(int sock, SilcRng rng, SilcServerID **new_id);
-void silc_id_create_client_id(SilcServerID *server_id, SilcRng rng,
+void silc_id_create_server_id(const char *ip, uint16 port, SilcRng rng, 
+                             SilcServerID **new_id);
+bool silc_id_create_client_id(SilcServer server,
+                             SilcServerID *server_id, SilcRng rng,
                              SilcHash md5hash, char *nickname, 
                              SilcClientID **new_id);
-void silc_id_create_channel_id(SilcServerID *router_id, SilcRng rng,
+bool silc_id_create_channel_id(SilcServer server,
+                              SilcServerID *router_id, SilcRng rng,
                               SilcChannelID **new_id);
 
 #endif
similarity index 90%
rename from includes/serverincludes.h
rename to apps/silcd/serverincludes.h
index 3c8eedf3b39f6d1c4bbb2c004ca4472e17dd9c07..1b2fa8021002b64e271dc5cdf0d2a9c3cc3136c4 100644 (file)
 
 /* SILC Server includes */
 #include "idlist.h"
-#include "route.h"
-#include "serverid.h"
-#include "serverconfig.h"
 #include "server.h"
+#include "serverconfig.h"
+#include "serverid.h"
+#include "server_util.h"
+#include "packet_send.h"
+#include "packet_receive.h"
+#include "route.h"
 #include "protocol.h"
 #include "command.h"
 #include "command_reply.h"
 #include "silcd.h"
+#include "server_backup.h"
 
 #endif
diff --git a/apps/silcd/silc.conf b/apps/silcd/silc.conf
deleted file mode 100644 (file)
index d7d4447..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-[Cipher]
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[HashFunction]
-md5::64:16
-sha1::64:20
-
-#[PKCS]
-#rsa::1024
-#dss::1024
-
-[AdminInfo]
-Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-[ServerInfo]
-silc.pspt.fi:193.166.51.47:Kuopio, Finland:1333
-
-[ListenPort]
-193.166.51.47:193.166.51.47:1333
-
-[Logging]
-infologfile:silcd.log:10000
-#warninglogfile:/var/log/silcd_warning.log:10000
-#errorlogfile:ERROR.log:10000
-#fatallogfile:/var/log/silcd_error.log:
-
-[ConnectionClass]
-1:100:100:100
-2:200:300:400
-
-[ClientConnection]
-:::1333:1
-
-[AdminConnection]
-
-[ServerConnection]
-
-[RouterConnection]
-
-[DenyConnection]
-[RedirectClient]
index 0de8874a5d8fe8353af8c4b64958b6ebb7162cca..cf3315a2cd126100a2611548ee6e1c271d797dda 100644 (file)
 /*
 
   silcd.c
-  
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
 */
-/* 
+/*
  * Created: Wed Mar 19 00:17:12 1997
  *
  * This is the main program for the SILC daemon. This parses command
  * line arguments and creates the server object.
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
 #include "version.h"
 
+/* For now, we'll have this one server context global for this module. */
+static SilcServer silcd;
+
+static void silc_usage();
+static char *silc_server_create_identifier();
+static int
+silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
+                           char *identifier,
+                           SilcPublicKey *ret_pub_key,
+                           SilcPrivateKey *ret_prv_key);
+
 /* Long command line options */
-static struct option long_opts[] = 
+static struct option long_opts[] =
 {
   { "config-file", 1, NULL, 'f' },
-  { "generate-config-file", 0, NULL, 'c' },
+  { "debug", 1, NULL, 'd' },
   { "help", 0, NULL, 'h' },
+  { "foreground", 0, NULL, 'F' },
   { "version", 0, NULL,'V' },
+
+  /* Key management options */
+  { "create-key-pair", 1, NULL, 'C' },
+  { "pkcs", 1, NULL, 10 },
+  { "bits", 1, NULL, 11 },
+  { "identifier", 1, NULL, 12 },
+
   { NULL, 0, NULL, 0 }
 };
 
+/* Command line option variables */
+static bool opt_create_keypair = FALSE;
+static char *opt_keypath = NULL;
+static char *opt_pkcs = "rsa";
+static char *opt_identifier = NULL;
+static int opt_bits = 1024;
+
 /* Prints out the usage of silc client */
 
-void silc_usage()
+static void silc_usage()
 {
-  printf("Usage: silcd [options]\n");
-  printf("Options:\n");
-  printf("  -f  --config-file=FILE        Alternate configuration file\n");
-  printf("  -c  --generate-config-file    Generate example configuration "
-        "file\n");
-  printf("  -h  --help                    Display this message\n");
-  printf("  -V  --version                 Display version\n");
+  printf("\
+Usage: silcd [options]\n\
+\n\
+  Generic Options:\n\
+  -f  --config-file=FILE        Alternate configuration file\n\
+  -d  --debug=string            Enable debugging (Implies --foreground)\n\
+  -h  --help                    Display this message\n\
+  -F  --foreground              Dont fork\n\
+  -V  --version                 Display version\n\
+\n\
+  Key Management Options:\n\
+  -C, --create-key-pair=PATH    Create new public key pair\n\
+      --pkcs=PKCS               Set the PKCS of the public key pair\n\
+      --bits=VALUE              Set length of the public key pair\n\
+      --identifier=IDENTIFIER   Public key identifier\n\
+\n\
+      The public key identifier may be of the following format:\n\
+\n\
+      UN=<username>, HN=<hostname or IP>, RN=<real name>, E=<email>,\n\
+      O=<organization>, C=<country>\n\
+\n\
+      The UN and HN must be provided, the others are optional.  If the\n\
+      --identifier option is not used an identifier will be created for\n\
+      the public key automatically.\n\
+\n\
+      Example identifier: \"UN=foobar, HN=foo.bar.com, RN=Foo T. Bar, \n\
+                           E=foo@bar.com, C=FI\"\n\
+\n");
   exit(0);
 }
 
+/* Dies if a *valid* pid file exists already */
+
+static void silc_server_checkpid(SilcServer silcd)
+{
+  if (silcd->config->server_info->pid_file) {
+    int oldpid;
+    char *buf;
+    uint32 buf_len;
+
+    SILC_LOG_DEBUG(("Checking for another silcd running"));
+    buf = silc_file_readfile(silcd->config->server_info->pid_file, &buf_len);
+    if (!buf)
+      return;
+    oldpid = atoi(buf);
+    silc_free(buf);
+    if (oldpid <= 0)
+      return;
+    kill(oldpid, SIGCHLD); /* this signal does nothing, check if alive */
+    if (errno != ESRCH) {
+      fprintf(stderr, "\nI detected another daemon running with the same pid file.\n");
+      fprintf(stderr, "Please change the config file, or erase the %s\n",
+       silcd->config->server_info->pid_file);
+      exit(1);
+    }
+  }
+}
+
+static void got_hup(int z)
+{
+  /* First, reset all log files (they might have been deleted) */
+  silc_log_reset_all();
+  silc_log_flush_all();
+}
+
+static void stop_server(int z)
+{
+  /* Stop scheduler, the program will stop eventually after noticing
+     that the scheduler is down. */
+  silc_schedule_stop(silcd->schedule); 
+}
+
 int main(int argc, char **argv)
 {
-  int ret;
-  int opt, option_index;
+  int ret, opt, option_index;
   char *config_file = NULL;
-  SilcServer silcd;
+  bool foreground = FALSE;
+  struct sigaction sa;
 
   /* Parse command line arguments */
   if (argc > 1) {
-    while ((opt = getopt_long(argc, argv, "cf:hV",
+    while ((opt = getopt_long(argc, argv, "cf:d:hFVC:",
                              long_opts, &option_index)) != EOF) {
-      switch(opt) 
+      switch(opt)
        {
        case 'h':
          silc_usage();
          break;
        case 'V':
          printf("SILCd Secure Internet Live Conferencing daemon, "
-                "version %s\n", silc_version);
-         printf("(c) 1997 - 2000 Pekka Riikonen "
-                "<priikone@poseidon.pspt.fi>\n");
+                "version %s (base: SILC Toolkit %s)\n",
+                 silc_dist_version, silc_version);
+         printf("(c) 1997 - 2001 Pekka Riikonen "
+                "<priikone@silcnet.org>\n");
          exit(0);
          break;
-       case 'c':
-         /* Print out example configuration file */
-         silc_config_server_print();
-         exit(0);
+       case 'd':
+#ifdef SILC_DEBUG
+         silc_debug = TRUE;
+         silc_debug_hexdump = TRUE;
+         silc_log_set_debug_string(optarg);
+         foreground = TRUE;
+         silc_log_quick = TRUE;
+#else
+         fprintf(stdout,
+                 "Run-time debugging is not enabled. To enable it recompile\n"
+                 "the server with --enable-debug configuration option.\n");
+#endif
          break;
        case 'f':
          config_file = strdup(optarg);
          break;
+       case 'F':
+         foreground = TRUE;
+         break;
+
+         /*
+          * Key management options
+          */
+       case 'C':
+         opt_create_keypair = TRUE;
+         if (optarg)
+           opt_keypath = strdup(optarg);
+         break;
+       case 10:
+         if (optarg)
+           opt_pkcs = strdup(optarg);
+         break;
+       case 11:
+         if (optarg)
+           opt_bits = atoi(optarg);
+         break;
+       case 12:
+         if (optarg)
+           opt_identifier = strdup(optarg);
+         break;
+
        default:
          silc_usage();
          break;
@@ -98,6 +213,17 @@ int main(int argc, char **argv)
     }
   }
 
+  if (opt_create_keypair == TRUE) {
+    /* Create new key pair and exit */
+    silc_cipher_register_default();
+    silc_pkcs_register_default();
+    silc_hash_register_default();
+    silc_hmac_register_default();
+    silc_server_create_key_pair(opt_pkcs, opt_bits, opt_keypath,
+                               opt_identifier, NULL, NULL);
+    exit(0);
+  }
+
   /* Default configuration file */
   if (!config_file)
     config_file = strdup(SILC_SERVER_CONFIG_FILE);
@@ -108,25 +234,161 @@ int main(int argc, char **argv)
     goto fail;
 
   /* Read configuration files */
-  silcd->config = silc_config_server_alloc(config_file);
+  silcd->config = silc_server_config_alloc(config_file);
   if (silcd->config == NULL)
     goto fail;
 
+  /* Check for another silcd running */
+  silc_server_checkpid(silcd);
+
   /* Initialize the server */
   ret = silc_server_init(silcd);
   if (ret == FALSE)
     goto fail;
-  
+
+  /* Ignore SIGPIPE */
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = 0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(SIGPIPE, &sa, NULL);
+  sa.sa_handler = got_hup;
+  sigaction(SIGHUP, &sa, NULL);
+  sa.sa_handler = stop_server;
+  sigaction(SIGTERM, &sa, NULL);
+  sa.sa_handler = stop_server;
+  sigaction(SIGINT, &sa, NULL);
+
+  /* Before running the server, fork to background. */
+  if (!foreground)
+    silc_server_daemonise(silcd);
+
+  /* If set, write pid to file */
+  if (silcd->config->server_info->pid_file) {
+    char buf[10], *pidfile = silcd->config->server_info->pid_file;
+    unlink(pidfile);
+    snprintf(buf, sizeof(buf) - 1, "%d\n", getpid());
+    silc_file_writefile(pidfile, buf, strlen(buf));
+  }
+
+  /* Drop root. */
+  silc_server_drop(silcd);
+
   /* Run the server. When this returns the server has been stopped
      and we will exit. */
   silc_server_run(silcd);
   
-  /* Stop the server. This probably has been done already but it
-     doesn't hurt to do it here again. */
+  /* Stop the server and free it. */
   silc_server_stop(silcd);
   silc_server_free(silcd);
-  
+
+  /* Flush the logging system */
+  silc_log_flush_all();
+
   exit(0);
  fail:
   exit(1);
 }
+
+/* Returns identifier string for public key generation. */
+
+static char *silc_server_create_identifier()
+{
+  char *username = NULL, *realname = NULL;
+  char hostname[256], email[256];
+  
+  /* Get realname */
+  realname = silc_get_real_name();
+
+  /* Get hostname */
+  memset(hostname, 0, sizeof(hostname));
+  gethostname(hostname, sizeof(hostname));
+
+  /* Get username (mandatory) */
+  username = silc_get_username();
+  if (!username)
+    return NULL;
+
+  /* Create default email address, whether it is right or not */
+  snprintf(email, sizeof(email), "%s@%s", username, hostname);
+
+  return silc_pkcs_encode_identifier(username, hostname, realname, email,
+                                    NULL, NULL);
+}
+
+/* Creates new public key and private key pair. This is used only
+   when user wants to create new key pair from command line. */
+
+static int 
+silc_server_create_key_pair(char *pkcs_name, int bits, char *path,
+                           char *identifier, 
+                           SilcPublicKey *ret_pub_key,
+                           SilcPrivateKey *ret_prv_key)
+{
+  SilcPKCS pkcs;
+  SilcPublicKey pub_key;
+  SilcPrivateKey prv_key;
+  SilcRng rng;
+  unsigned char *key;
+  uint32 key_len;
+  char pkfile[256], prvfile[256];
+
+  if (!pkcs_name || !path)
+    return FALSE;
+
+  if (!silc_pkcs_is_supported(pkcs_name)) {
+    fprintf(stderr, "Unsupported PKCS `%s'", pkcs_name);
+    return FALSE;
+  }
+
+  if (!bits)
+    bits = 1024;
+
+  if (!identifier)
+    identifier = silc_server_create_identifier();
+
+  rng = silc_rng_alloc();
+  silc_rng_init(rng);
+  silc_rng_global_init(rng);
+
+  snprintf(pkfile, sizeof(pkfile) - 1, "%s%s", path,
+          SILC_SERVER_PUBLIC_KEY_NAME);
+  snprintf(prvfile, sizeof(prvfile) - 1, "%s%s", path,
+          SILC_SERVER_PRIVATE_KEY_NAME);
+
+  /* Generate keys */
+  silc_pkcs_alloc(pkcs_name, &pkcs);
+  pkcs->pkcs->init(pkcs->context, bits, rng);
+
+  /* Save public key into file */
+  key = silc_pkcs_get_public_key(pkcs, &key_len);
+  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
+                                      key, key_len);
+  silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
+  if (ret_pub_key)
+    *ret_pub_key = pub_key;
+  else
+    silc_pkcs_public_key_free(pub_key);
+
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  /* Save private key into file */
+  key = silc_pkcs_get_private_key(pkcs, &key_len);
+  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
+  silc_pkcs_save_private_key(prvfile, prv_key, NULL, SILC_PKCS_FILE_BIN);
+  if (ret_prv_key)
+    *ret_prv_key = prv_key;
+  else
+    silc_pkcs_private_key_free(prv_key);
+
+  printf("Public key has been saved into `%s'\n", pkfile);
+  printf("Private key has been saved into `%s'\n", prvfile);
+
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  silc_rng_free(rng);
+  silc_pkcs_free(pkcs);
+
+  return TRUE;
+}
index 1f33d12c7c5ea99123045327e508bf10a3e97b2f..512ce94a3e44e807bf7eabefbbe211bc5a21a5ff 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
    compilation time. Otherwise, use default. This can be overridden on
    command line as well. */
 #ifndef SILC_SERVER_CONFIG_FILE
-#define SILC_SERVER_CONFIG_FILE "/etc/silc/silcd.conf\0"
+#define SILC_SERVER_CONFIG_FILE "/etc/silc/silcd.conf"
 #endif
 
-void usage();
-
-char file_public_key[100];       /* server public key */
-char file_private_key[100];      /* server private key */
-
-#define PUBLICKEY_NAME "/silc_host_public"
-#define PRIVATEKEY_NAME "/silc_host_private"
+#define SILC_SERVER_PUBLIC_KEY_NAME "/silcd.pub"
+#define SILC_SERVER_PRIVATE_KEY_NAME "/silcd.prv"
 
 #define SERVER_KEY_EXPIRATION_DAYS 180
-int key_expires;
 
 #endif
diff --git a/apps/silcd/testi.conf b/apps/silcd/testi.conf
deleted file mode 100644 (file)
index e5417e2..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-[Cipher]
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[HashFunction]
-md5::64:16
-sha1::64:20
-
-#[PKCS]
-#rsa::1024
-#dss::1024
-
-[AdminInfo]
-Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-[ServerInfo]
-lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1333
-
-[ListenPort]
-10.2.1.6:10.2.1.6:1333
-
-[Logging]
-infologfile:silcd.log:10000
-#warninglogfile:/var/log/silcd_warning.log:10000
-errorlogfile:silcd2_error.log:10000
-#fatallogfile:/var/log/silcd_error.log:
-
-[ConnectionClass]
-1:100:100:100
-2:200:300:400
-
-[ClientConnection]
-10.2.1.199:passwd:priikone:333:1
-:::1333:1
-
-[AdminConnection]
-
-[ServerConnection]
-
-[RouterConnection]
-
-[DenyConnection]
-[RedirectClient]
diff --git a/apps/silcd/testi2.conf b/apps/silcd/testi2.conf
deleted file mode 100644 (file)
index 8d44456..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-[Cipher]
-rc6:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-twofish:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-mars:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-[HashFunction]
-md5::64:16
-sha1::64:20
-
-#[PKCS]
-#rsa::1024
-#dss::1024
-
-[AdminInfo]
-Mun huone:Mun servo:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-[ServerInfo]
-lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1334
-
-[ListenPort]
-10.2.1.6:10.2.1.6:1334
-
-[Logging]
-infologfile:silcd.log:10000
-#warninglogfile:/var/log/silcd_warning.log:10000
-errorlogfile:silcd2_error.log:10000
-#fatallogfile:/var/log/silcd_error.log:
-
-[ConnectionClass]
-1:100:100:100
-2:200:300:400
-
-[ClientConnection]
-10.2.1.199:passwd:priikone:333:1
-:::1333:1
-
-[AdminConnection]
-10.2.1.199:passwd:priikone:priikone:1
-
-[ServerConnection]
-
-[RouterConnection]
-10.2.1.6:passwd:priikone:1333:1:1
-
-[DenyConnection]
-[RedirectClient]
diff --git a/apps/silcer/ABOUT-NLS b/apps/silcer/ABOUT-NLS
new file mode 100644 (file)
index 0000000..a4fb870
--- /dev/null
@@ -0,0 +1,319 @@
+Notes on the Free Translation Project
+*************************************
+
+   Free software is going international!  The Free Translation Project
+is a way to get maintainers of free software, translators, and users all
+together, so that will gradually become able to speak many languages.
+A few packages already provide translations for their messages.
+
+   If you found this `ABOUT-NLS' file inside a distribution, you may
+assume that the distributed package does use GNU `gettext' internally,
+itself available at your nearest GNU archive site.  But you do _not_
+need to install GNU `gettext' prior to configuring, installing or using
+this package with messages translated.
+
+   Installers will find here some useful hints.  These notes also
+explain how users should proceed for getting the programs to use the
+available translations.  They tell how people wanting to contribute and
+work at translations should contact the appropriate team.
+
+   When reporting bugs in the `intl/' directory or bugs which may be
+related to internationalization, you should tell about the version of
+`gettext' which is used.  The information can be found in the
+`intl/VERSION' file, in internationalized packages.
+
+Quick configuration advice
+==========================
+
+   If you want to exploit the full power of internationalization, you
+should configure it using
+
+     ./configure --with-included-gettext
+
+to force usage of internationalizing routines provided within this
+package, despite the existence of internationalizing capabilities in the
+operating system where this package is being installed.  So far, only
+the `gettext' implementation in the GNU C library version 2 provides as
+many features (such as locale alias, message inheritance, automatic
+charset conversion or plural form handling) as the implementation here.
+It is also not possible to offer this additional functionality on top
+of a `catgets' implementation.  Future versions of GNU `gettext' will
+very likely convey even more functionality.  So it might be a good idea
+to change to GNU `gettext' as soon as possible.
+
+   So you need _not_ provide this option if you are using GNU libc 2 or
+you have installed a recent copy of the GNU gettext package with the
+included `libintl'.
+
+INSTALL Matters
+===============
+
+   Some packages are "localizable" when properly installed; the
+programs they contain can be made to speak your own native language.
+Most such packages use GNU `gettext'.  Other packages have their own
+ways to internationalization, predating GNU `gettext'.
+
+   By default, this package will be installed to allow translation of
+messages.  It will automatically detect whether the system already
+provides the GNU `gettext' functions.  If not, the GNU `gettext' own
+library will be used.  This library is wholly contained within this
+package, usually in the `intl/' subdirectory, so prior installation of
+the GNU `gettext' package is _not_ required.  Installers may use
+special options at configuration time for changing the default
+behaviour.  The commands:
+
+     ./configure --with-included-gettext
+     ./configure --disable-nls
+
+will respectively bypass any pre-existing `gettext' to use the
+internationalizing routines provided within this package, or else,
+_totally_ disable translation of messages.
+
+   When you already have GNU `gettext' installed on your system and run
+configure without an option for your new package, `configure' will
+probably detect the previously built and installed `libintl.a' file and
+will decide to use this.  This might be not what is desirable.  You
+should use the more recent version of the GNU `gettext' library.  I.e.
+if the file `intl/VERSION' shows that the library which comes with this
+package is more recent, you should use
+
+     ./configure --with-included-gettext
+
+to prevent auto-detection.
+
+   The configuration process will not test for the `catgets' function
+and therefore it will not be used.  The reason is that even an
+emulation of `gettext' on top of `catgets' could not provide all the
+extensions of the GNU `gettext' library.
+
+   Internationalized packages have usually many `po/LL.po' files, where
+LL gives an ISO 639 two-letter code identifying the language.  Unless
+translations have been forbidden at `configure' time by using the
+`--disable-nls' switch, all available translations are installed
+together with the package.  However, the environment variable `LINGUAS'
+may be set, prior to configuration, to limit the installed set.
+`LINGUAS' should then contain a space separated list of two-letter
+codes, stating which languages are allowed.
+
+Using This Package
+==================
+
+   As a user, if your language has been installed for this package, you
+only have to set the `LANG' environment variable to the appropriate
+`LL_CC' combination.  Here `LL' is an ISO 639 two-letter language code,
+and `CC' is an ISO 3166 two-letter country code.  For example, let's
+suppose that you speak German and live in Germany.  At the shell
+prompt, merely execute `setenv LANG de_DE' (in `csh'),
+`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash').
+This can be done from your `.login' or `.profile' file, once and for
+all.
+
+   You might think that the country code specification is redundant.
+But in fact, some languages have dialects in different countries.  For
+example, `de_AT' is used for Austria, and `pt_BR' for Brazil.  The
+country code serves to distinguish the dialects.
+
+   Not all programs have translations for all languages.  By default, an
+English message is shown in place of a nonexistent translation.  If you
+understand other languages, you can set up a priority list of languages.
+This is done through a different environment variable, called
+`LANGUAGE'.  GNU `gettext' gives preference to `LANGUAGE' over `LANG'
+for the purpose of message handling, but you still need to have `LANG'
+set to the primary language; this is required by other parts of the
+system libraries.  For example, some Swedish users who would rather
+read translations in German than English for when Swedish is not
+available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'.
+
+   In the `LANGUAGE' environment variable, but not in the `LANG'
+environment variable, `LL_CC' combinations can be abbreviated as `LL'
+to denote the language's main dialect.  For example, `de' is equivalent
+to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT'
+(Portuguese as spoken in Portugal) in this context.
+
+Translating Teams
+=================
+
+   For the Free Translation Project to be a success, we need interested
+people who like their own language and write it well, and who are also
+able to synergize with other translators speaking the same language.
+Each translation team has its own mailing list.  The up-to-date list of
+teams can be found at the Free Translation Project's homepage,
+`http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams"
+area.
+
+   If you'd like to volunteer to _work_ at translating messages, you
+should become a member of the translating team for your own language.
+The subscribing address is _not_ the same as the list itself, it has
+`-request' appended.  For example, speakers of Swedish can send a
+message to `sv-request@li.org', having this message body:
+
+     subscribe
+
+   Keep in mind that team members are expected to participate
+_actively_ in translations, or at solving translational difficulties,
+rather than merely lurking around.  If your team does not exist yet and
+you want to start one, or if you are unsure about what to do or how to
+get started, please write to `translation@iro.umontreal.ca' to reach the
+coordinator for all translator teams.
+
+   The English team is special.  It works at improving and uniformizing
+the terminology in use.  Proven linguistic skill are praised more than
+programming skill, here.
+
+Available Packages
+==================
+
+   Languages are not equally supported in all packages.  The following
+matrix shows the current state of internationalization, as of July
+2001.  The matrix shows, in regard of each package, for which languages
+PO files have been submitted to translation coordination, with a
+translation percentage of at least 50%.
+
+     Ready PO files    bg cs da de el en eo es et fi fr gl he hr id it
+                     +-------------------------------------------------+
+     a2ps            |          []             []                      |
+     bash            |          []       [] []       []                |
+     bfd             |                                                 |
+     binutils        |                                                 |
+     bison           |          []          [] []    []                |
+     clisp           |          []    []    []       []                |
+     cpio            |       [] []          []       [] []             |
+     diffutils       |       [] []       [] []       [] []       []    |
+     enscript        |          []                   []                |
+     error           |                      []       []                |
+     fetchmail       |                                                 |
+     fileutils       |    [] [] [] []       []       [] []             |
+     findutils       |       [] []          [] []    [] []       [] [] |
+     flex            |       []             []       []                |
+     freetype        |                                                 |
+     gas             |                                                 |
+     gawk            |                                     []          |
+     gcal            |                                                 |
+     gcc             |                                                 |
+     gettext         |    [] [] [] []       []       [] []       [] [] |
+     gnupg           |          []       []    []    [] []          [] |
+     gprof           |                                                 |
+     grep            |          []       [] [] []    [] []          [] |
+     hello           |       [] [] []    [] [] [] [] [] []          [] |
+     id-utils        |       [] []                   []                |
+     indent          |       [] []             []    [] []             |
+     jpilot          |                               []                |
+     kbd             |                                                 |
+     ld              |                                                 |
+     libc            |    [] [] [] []       []       [] []          [] |
+     lilypond        |                                                 |
+     lynx            |    [] [] []                                     |
+     m4              |    [] [] [] []                [] []       []    |
+     make            |       [] []          []       [] []             |
+     nano            |                                  []       []    |
+     opcodes         |                                                 |
+     parted          |          []                      []             |
+     ptx             |       [] []          [] []    [] []       []    |
+     python          |                                                 |
+     recode          |       [] [] []    [] []       [] [] []       [] |
+     sed             |    [] [] [] []    []    []    [] []       [] [] |
+     sh-utils        |    [] [] [] []       [] []    [] []          [] |
+     sharutils       |    [] [] [] []       []       [] []             |
+     soundtracker    |                                  []             |
+     sp              |                                                 |
+     tar             |    [] [] []          [] []    []          [] [] |
+     texinfo         |    [] [] []       []          []                |
+     textutils       |    [] [] [] []       []       [] []             |
+     util-linux      |    [] []                                        |
+     wdiff           |          []             []                      |
+     wget            |    [] [] [] []       [] []    [] [] []          |
+                     +-------------------------------------------------+
+                       bg cs da de el en eo es et fi fr gl he hr id it
+                        0 13 23 30 11  1  8 21 13  1 29 22  3  0  8 10
+     
+                       ja ko lv nl no pl pt pt_BR ru sk sl sv tr uk zh
+                     +-------------------------------------------------+
+     a2ps            |          []                []    []             |  5
+     bash            |                                                 |  4
+     bfd             |                                                 |  0
+     binutils        |                                                 |  0
+     bison           | []       []                []                   |  7
+     clisp           |          []                                     |  5
+     cpio            |    []    []    []     []   []                   | 10
+     diffutils       |                []          []       []          | 10
+     enscript        |          []           []   []                   |  5
+     error           |                                        []       |  3
+     fetchmail       |                                                 |  0
+     fileutils       | [] []    []    []     []   [] [] [] [] []       | 17
+     findutils       |    []    []    []     []   []    [] [] []       | 16
+     flex            |    []                      []       []          |  6
+     freetype        |                                                 |  0
+     gas             |                                                 |  0
+     gawk            |                                        []       |  2
+     gcal            |                                                 |  0
+     gcc             |                                                 |  0
+     gettext         | [] []          []     []   []    [] [] []    [] | 18
+     gnupg           | []             []                   [] []       | 10
+     gprof           |                                                 |  0
+     grep            |                []                []    []       | 10
+     hello           | [] [] [] [] [] []          [] []    [] [] []    | 21
+     id-utils        |          []                []       []          |  6
+     indent          |    []    []    []          [] []    [] []       | 12
+     jpilot          |                                                 |  1
+     kbd             |                                        []       |  1
+     ld              |                                                 |  0
+     libc            | [] []    [] [] []     []      []    [] []       | 17
+     lilypond        | []       []                                     |  2
+     lynx            | []       []           []   []       []          |  8
+     m4              | []       []    []          []       []          | 12
+     make            | [] []    []    []     []   []          []       | 12
+     nano            |                                     []          |  3
+     opcodes         |                                                 |  0
+     parted          | []       []                []                   |  5
+     ptx             |          [] [] [] []       []       [] []       | 14
+     python          |                                                 |  0
+     recode          |                []          []    [] []          | 13
+     sed             | []       []           []   [] [] [] [] []       | 18
+     sh-utils        | []       [] [] []     []   [] [] [] [] []    [] | 20
+     sharutils       | []       []                []       []          | 11
+     soundtracker    |                                                 |  1
+     sp              |                                                 |  0
+     tar             | []       [] [] []     []   []    [] [] []       | 17
+     texinfo         | []                         []                   |  7
+     textutils       | []       [] [] []     []   [] [] []             | 15
+     util-linux      |                       []               []       |  4
+     wdiff           |                            [] []       []       |  5
+     wget            |          []                [] [] [] [] [] []    | 16
+                     +-------------------------------------------------+
+       31 teams        ja ko lv nl no pl pt pt_BR ru sk sl sv tr uk zh
+       51 domains      17  9  1 23  6 17  1  13   26  9 11 20 19  2  2  369
+
+   Some counters in the preceding matrix are higher than the number of
+visible blocks let us expect.  This is because a few extra PO files are
+used for implementing regional variants of languages, or language
+dialects.
+
+   For a PO file in the matrix above to be effective, the package to
+which it applies should also have been internationalized and
+distributed as such by its maintainer.  There might be an observable
+lag between the mere existence a PO file and its wide availability in a
+distribution.
+
+   If July 2001 seems to be old, you may fetch a more recent copy of
+this `ABOUT-NLS' file on most GNU archive sites.  The most up-to-date
+matrix with full percentage details can be found at
+`http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'.
+
+Using `gettext' in new packages
+===============================
+
+   If you are writing a freely available program and want to
+internationalize it you are welcome to use GNU `gettext' in your
+package.  Of course the GNU General Public License applies to your
+sources from then on if you include `gettext' directly in your
+distribution but since you are writing free software anyway this is no
+restriction.
+
+   Once the sources are changed appropriately and the setup can handle
+to use of `gettext' the only thing missing are the translations.  The
+Free Translation Project is also available for packages which are not
+developed inside the GNU project.  Therefore the information given above
+applies also for every other Free Software Project.  Contact
+`translation@iro.umontreal.ca' to make the `.pot' files available to
+the translation teams.
+
diff --git a/apps/silcer/AUTHORS b/apps/silcer/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/silcer/COPYING b/apps/silcer/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/apps/silcer/ChangeLog b/apps/silcer/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/silcer/INSTALL b/apps/silcer/INSTALL
new file mode 100644 (file)
index 0000000..b42a17a
--- /dev/null
@@ -0,0 +1,182 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
diff --git a/apps/silcer/Makefile.am b/apps/silcer/Makefile.am
new file mode 100644 (file)
index 0000000..8475507
--- /dev/null
@@ -0,0 +1,28 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = intl po macros src
+
+EXTRA_DIST = \
+       autogen.sh ui
+
+install-data-local:
+       @$(NORMAL_INSTALL)
+       if test -d $(srcdir)/pixmaps; then \
+         $(mkinstalldirs) $(DESTDIR)$(datadir)/pixmaps/$(PACKAGE); \
+         for pixmap in $(srcdir)/pixmaps/*; do \
+           if test -f $$pixmap; then \
+             $(INSTALL_DATA) $$pixmap $(DESTDIR)$(datadir)/pixmaps/$(PACKAGE); \
+           fi \
+         done \
+       fi
+
+dist-hook:
+       if test -d pixmaps; then \
+         mkdir $(distdir)/pixmaps; \
+         for pixmap in pixmaps/*; do \
+           if test -f $$pixmap; then \
+             cp -p $$pixmap $(distdir)/pixmaps; \
+           fi \
+         done \
+       fi
+
diff --git a/apps/silcer/NEWS b/apps/silcer/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/silcer/README b/apps/silcer/README
new file mode 100644 (file)
index 0000000..06e6ec5
--- /dev/null
@@ -0,0 +1,37 @@
+Silcer Gnome SILC Client
+========================
+
+This is Silcer, the Gnome SILC Client.  It is only provided with SILC 
+Toolkit package as an example GUI application, to show how to program with 
+SILC Toolkit.  It is a good example for all GUI programmers to figure out 
+how to hook up SILC Toolkit, and in the end the SILC Client Library to a 
+GUI application.
+
+The silcer.png file found in this package is a screenshot of the 
+application in action.  You can compile it by running first the 
+./autogen.sh script and then giving command `make' or `gmake'.
+
+When you run the silcer you must run it by giving command src/silcer 
+because it will attempt to look for GUI files in the ui/ directory found 
+in this package.  So, do not enter src/ directory, but run it directly 
+from the root where you gave the make command.
+
+When the program starts it will attempt to connect directly to 
+silc.silcnet.org router on port 706.  After it has connected you can 
+freely use it as any SILC client.  In the input box on the screen you can 
+give commands.  Commands, such as /nick, /join, etc.  The application is 
+only an example so all features has not been implemented.  For example 
+various command notifications and other notifications are not implemented 
+on the screen.  This means that when you give command /nick you won't 
+actually see whether your nickname changed or not.  But it did. :)  Same 
+is when you join to a channel by giving command /join you won't get list 
+of users or other familiar things.  This is because those notifications 
+has not been implemented.  They are left as an exerise for Toolkit 
+programmers.  You can still converse on a channel.  You will see other 
+users' messages on the channel and you can talk to the channel, so you can 
+test the application if you like.
+
+The sources resides in the src/ directory and are C++.  The GUI files 
+resides in ui/ directory and was done with glade.
+
+Happy hacking!
diff --git a/apps/silcer/acconfig.h b/apps/silcer/acconfig.h
new file mode 100644 (file)
index 0000000..894805b
--- /dev/null
@@ -0,0 +1,12 @@
+#undef ENABLE_NLS
+#undef HAVE_CATGETS
+#undef HAVE_GETTEXT
+#undef HAVE_LC_MESSAGES
+#undef HAVE_STPCPY
+#undef HAVE_LIBSM
+#undef PACKAGE_LOCALE_DIR
+#undef PACKAGE_DATA_DIR
+#undef PACKAGE_SOURCE_DIR
+
+#undef EXTRA_GNOME_LIBS
+#undef EXTRA_GNOME_CFLAGS
diff --git a/apps/silcer/aclocal.m4 b/apps/silcer/aclocal.m4
new file mode 100644 (file)
index 0000000..aa0c77c
--- /dev/null
@@ -0,0 +1,1949 @@
+# aclocal.m4 generated automatically by aclocal 1.5
+
+# Copyright 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+# Do all the work for Automake.  This macro actually does too much --
+# some checks are only needed if your package does certain things.
+# But this isn't really a big deal.
+
+# serial 5
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# We require 2.13 because we rely on SHELL being computed by configure.
+AC_PREREQ([2.13])
+
+# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
+# -----------------------------------------------------------
+# If MACRO-NAME is provided do IF-PROVIDED, else IF-NOT-PROVIDED.
+# The purpose of this macro is to provide the user with a means to
+# check macros which are provided without letting her know how the
+# information is coded.
+# If this macro is not defined by Autoconf, define it here.
+ifdef([AC_PROVIDE_IFELSE],
+      [],
+      [define([AC_PROVIDE_IFELSE],
+              [ifdef([AC_PROVIDE_$1],
+                     [$2], [$3])])])
+
+
+# AM_INIT_AUTOMAKE(PACKAGE,VERSION, [NO-DEFINE])
+# ----------------------------------------------
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_REQUIRE([AC_PROG_INSTALL])dnl
+# test to see if srcdir already configured
+if test "`CDPATH=:; cd $srcdir && pwd`" != "`pwd`" &&
+   test -f $srcdir/config.status; then
+  AC_MSG_ERROR([source directory already configured; run \"make distclean\" there first])
+fi
+
+# Define the identity of the package.
+PACKAGE=$1
+AC_SUBST(PACKAGE)dnl
+VERSION=$2
+AC_SUBST(VERSION)dnl
+ifelse([$3],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])
+
+# Autoconf 2.50 wants to disallow AM_ names.  We explicitly allow
+# the ones we care about.
+ifdef([m4_pattern_allow],
+      [m4_pattern_allow([^AM_[A-Z]+FLAGS])])dnl
+
+# Autoconf 2.50 always computes EXEEXT.  However we need to be
+# compatible with 2.13, for now.  So we always define EXEEXT, but we
+# don't compute it.
+AC_SUBST(EXEEXT)
+# Similar for OBJEXT -- only we only use OBJEXT if the user actually
+# requests that it be used.  This is a bit dumb.
+: ${OBJEXT=o}
+AC_SUBST(OBJEXT)
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal)
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake)
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_MISSING_PROG(AMTAR, tar)
+AM_PROG_INSTALL_SH
+AM_PROG_INSTALL_STRIP
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_PROVIDE_IFELSE([AC_PROG_][CC],
+                  [_AM_DEPENDENCIES(CC)],
+                  [define([AC_PROG_][CC],
+                          defn([AC_PROG_][CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_][CXX],
+                  [_AM_DEPENDENCIES(CXX)],
+                  [define([AC_PROG_][CXX],
+                          defn([AC_PROG_][CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+])
+
+#
+# Check to make sure that the build environment is sane.
+#
+
+# serial 3
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+   if test "$[*]" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$[*]" != "X $srcdir/configure conftest.file" \
+      && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+
+# serial 2
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  am_backtick='`'
+  AC_MSG_WARN([${am_backtick}missing' script is too old or missing])
+fi
+])
+
+# AM_AUX_DIR_EXPAND
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND], [
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`CDPATH=:; cd $ac_aux_dir && pwd`
+])
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+install_sh=${install_sh-"$am_aux_dir/install-sh"}
+AC_SUBST(install_sh)])
+
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# serial 4                                             -*- Autoconf -*-
+
+
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+
+# _AM_DEPENDENCIES(NAME)
+# ---------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX" or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
+       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
+       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc']
+       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                   [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    echo '#include "conftest.h"' > conftest.c
+    echo 'int i;' > conftest.h
+    echo "${am__include} ${am__quote}conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=conftest.c object=conftest.o \
+       depfile=conftest.Po tmpdepfile=conftest.TPo \
+       $SHELL ./depcomp $depcc -c conftest.c -o conftest.o >/dev/null 2>&1 &&
+       grep conftest.h conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      am_cv_$1_dependencies_compiler_type=$depmode
+      break
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+$1DEPMODE="depmode=$am_cv_$1_dependencies_compiler_type"
+AC_SUBST([$1DEPMODE])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[rm -f .deps 2>/dev/null
+mkdir .deps 2>/dev/null
+if test -d .deps; then
+  DEPDIR=.deps
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  DEPDIR=_deps
+fi
+rmdir .deps 2>/dev/null
+AC_SUBST(DEPDIR)
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[  --disable-dependency-tracking Speeds up one-time builds
+  --enable-dependency-tracking  Do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+pushdef([subst], defn([AC_SUBST]))
+subst(AMDEPBACKSLASH)
+popdef([subst])
+])
+
+# Generate code to set up dependency tracking.
+# This macro should only be invoked once -- use via AC_REQUIRE.
+# Usage:
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],[
+AC_OUTPUT_COMMANDS([
+test x"$AMDEP_TRUE" != x"" ||
+for mf in $CONFIG_FILES; do
+  case "$mf" in
+  Makefile) dirpart=.;;
+  */Makefile) dirpart=`echo "$mf" | sed -e 's|/[^/]*$||'`;;
+  *) continue;;
+  esac
+  grep '^DEP_FILES *= *[^ #]' < "$mf" > /dev/null || continue
+  # Extract the definition of DEP_FILES from the Makefile without
+  # running `make'.
+  DEPDIR=`sed -n -e '/^DEPDIR = / s///p' < "$mf"`
+  test -z "$DEPDIR" && continue
+  # When using ansi2knr, U may be empty or an underscore; expand it
+  U=`sed -n -e '/^U = / s///p' < "$mf"`
+  test -d "$dirpart/$DEPDIR" || mkdir "$dirpart/$DEPDIR"
+  # We invoke sed twice because it is the simplest approach to
+  # changing $(DEPDIR) to its actual value in the expansion.
+  for file in `sed -n -e '
+    /^DEP_FILES = .*\\\\$/ {
+      s/^DEP_FILES = //
+      :loop
+       s/\\\\$//
+       p
+       n
+       /\\\\$/ b loop
+      p
+    }
+    /^DEP_FILES = / s/^DEP_FILES = //p' < "$mf" | \
+       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+    # Make sure the directory exists.
+    test -f "$dirpart/$file" && continue
+    fdir=`echo "$file" | sed -e 's|/[^/]*$||'`
+    $ac_aux_dir/mkinstalldirs "$dirpart/$fdir" > /dev/null 2>&1
+    # echo "creating $dirpart/$file"
+    echo '# dummy' > "$dirpart/$file"
+  done
+done
+], [AMDEP_TRUE="$AMDEP_TRUE"
+ac_aux_dir="$ac_aux_dir"])])
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+doit:
+       @echo done
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include='#'
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | fgrep -v 'ing directory'`" = "done"; then
+   am__include=include
+   am__quote=
+   _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+      am__include=.include
+      am__quote='"'
+      _am_result=BSD
+   fi
+fi
+AC_SUBST(am__include)
+AC_SUBST(am__quote)
+AC_MSG_RESULT($_am_result)
+rm -f confinc confmf
+])
+
+# serial 3
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+#
+# FIXME: Once using 2.50, use this:
+# m4_match([$1], [^TRUE\|FALSE$], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_DEFUN([AM_CONDITIONAL],
+[ifelse([$1], [TRUE],
+        [errprint(__file__:__line__: [$0: invalid condition: $1
+])dnl
+m4exit(1)])dnl
+ifelse([$1], [FALSE],
+       [errprint(__file__:__line__: [$0: invalid condition: $1
+])dnl
+m4exit(1)])dnl
+AC_SUBST([$1_TRUE])
+AC_SUBST([$1_FALSE])
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi])
+
+# Like AC_CONFIG_HEADER, but automatically create stamp file.
+
+# serial 3
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  We must strip everything past the first ":",
+# and everything past the last "/".
+
+AC_PREREQ([2.12])
+
+AC_DEFUN([AM_CONFIG_HEADER],
+[ifdef([AC_FOREACH],dnl
+        [dnl init our file count if it isn't already
+        m4_ifndef([_AM_Config_Header_Index], m4_define([_AM_Config_Header_Index], [0]))
+        dnl prepare to store our destination file list for use in config.status
+        AC_FOREACH([_AM_File], [$1],
+                   [m4_pushdef([_AM_Dest], m4_patsubst(_AM_File, [:.*]))
+                   m4_define([_AM_Config_Header_Index], m4_incr(_AM_Config_Header_Index))
+                   dnl and add it to the list of files AC keeps track of, along
+                   dnl with our hook
+                   AC_CONFIG_HEADERS(_AM_File,
+dnl COMMANDS, [, INIT-CMDS]
+[# update the timestamp
+echo timestamp >"AS_ESCAPE(_AM_DIRNAME(]_AM_Dest[))/stamp-h]_AM_Config_Header_Index["
+][$2]m4_ifval([$3], [, [$3]]))dnl AC_CONFIG_HEADERS
+                   m4_popdef([_AM_Dest])])],dnl
+[AC_CONFIG_HEADER([$1])
+  AC_OUTPUT_COMMANDS(
+   ifelse(patsubst([$1], [[^ ]], []),
+         [],
+         [test -z "$CONFIG_HEADERS" || echo timestamp >dnl
+          patsubst([$1], [^\([^:]*/\)?.*], [\1])stamp-h]),dnl
+[am_indx=1
+for am_file in $1; do
+  case " \$CONFIG_HEADERS " in
+  *" \$am_file "*)
+    am_dir=\`echo \$am_file |sed 's%:.*%%;s%[^/]*\$%%'\`
+    if test -n "\$am_dir"; then
+      am_tmpdir=\`echo \$am_dir |sed 's%^\(/*\).*\$%\1%'\`
+      for am_subdir in \`echo \$am_dir |sed 's%/% %'\`; do
+        am_tmpdir=\$am_tmpdir\$am_subdir/
+        if test ! -d \$am_tmpdir; then
+          mkdir \$am_tmpdir
+        fi
+      done
+    fi
+    echo timestamp > "\$am_dir"stamp-h\$am_indx
+    ;;
+  esac
+  am_indx=\`expr \$am_indx + 1\`
+done])
+])]) # AM_CONFIG_HEADER
+
+# _AM_DIRNAME(PATH)
+# -----------------
+# Like AS_DIRNAME, only do it during macro expansion
+AC_DEFUN([_AM_DIRNAME],
+       [m4_if(m4_regexp([$1], [^.*[^/]//*[^/][^/]*/*$]), -1,
+             m4_if(m4_regexp([$1], [^//\([^/]\|$\)]), -1,
+                   m4_if(m4_regexp([$1], [^/.*]), -1,
+                         [.],
+                         m4_patsubst([$1], [^\(/\).*], [\1])),
+                   m4_patsubst([$1], [^\(//\)\([^/].*\|$\)], [\1])),
+             m4_patsubst([$1], [^\(.*[^/]\)//*[^/][^/]*/*$], [\1]))[]dnl
+]) # _AM_DIRNAME
+
+# aclocal-include.m4
+# 
+# This macro adds the name macrodir to the set of directories
+# that `aclocal' searches for macros.  
+
+# serial 1
+
+dnl AM_ACLOCAL_INCLUDE(macrodir)
+AC_DEFUN([AM_ACLOCAL_INCLUDE],
+[
+       AM_CONDITIONAL(INSIDE_GNOME_COMMON, test x = y)
+
+       test -n "$ACLOCAL_FLAGS" && ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+       for k in $1 ; do ACLOCAL="$ACLOCAL -I $k" ; done
+])
+
+
+define([HACK_SUBST], defn([AC_SUBST]))
+
+# serial 1 AC_PROG_XML_I18N_TOOLS
+AC_DEFUN(AC_PROG_XML_I18N_TOOLS,
+[
+
+dnl This is a hack - we use the expansion of AC_SUBST instead of
+dnl AC_SUBST itself to avoid automake putting 
+dnl XML_I18N_MERGE_OAF_RULE = @XML_I18N_MERGE_OAF_RULE@
+dnl in all the Makefile.in's, because that will blow up when substituted.
+XML_I18N_MERGE_OAF_RULE='\%.oaf : \%.oaf.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -o $(top_srcdir)/po $< [$]*.oaf'
+HACK_SUBST(XML_I18N_MERGE_OAF_RULE)
+
+XML_I18N_MERGE_SERVER_RULE='\%.server : \%.server.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -o $(top_srcdir)/po $< [$]*.server'
+HACK_SUBST(XML_I18N_MERGE_SERVER_RULE)
+
+dnl same deal
+XML_I18N_MERGE_KEYS_RULE='\%.keys : \%.keys.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -k $(top_srcdir)/po $< [$]*.keys'
+HACK_SUBST(XML_I18N_MERGE_KEYS_RULE)
+
+dnl same deal
+XML_I18N_MERGE_DESKTOP_RULE='\%.desktop : \%.desktop.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -d $(top_srcdir)/po $< [$]*.desktop'
+HACK_SUBST(XML_I18N_MERGE_DESKTOP_RULE)
+
+dnl same deal
+XML_I18N_MERGE_DIRECTORY_RULE='\%.directory : \%.directory.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -d $(top_srcdir)/po $< [$]*.directory'
+HACK_SUBST(XML_I18N_MERGE_DIRECTORY_RULE)
+
+dnl same deal
+XML_I18N_MERGE_SOUNDLIST_RULE='\%.soundlist : \%.soundlist.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -d $(top_srcdir)/po $< [$]*.soundlist'
+HACK_SUBST(XML_I18N_MERGE_SOUNDLIST_RULE)
+
+dnl same deal
+XML_I18N_MERGE_PONG_RULE='\%.pong : \%.pong.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -x $(top_srcdir)/po $< [$]*.pong'
+HACK_SUBST(XML_I18N_MERGE_PONG_RULE)
+
+dnl same deal
+XML_I18N_MERGE_XML_RULE='\%.xml : \%.xml.in $(top_builddir)/xml-i18n-merge $(top_srcdir)/po/*.po\
+       $(top_builddir)/xml-i18n-merge -x $(top_srcdir)/po $< [$]*.xml'
+HACK_SUBST(XML_I18N_MERGE_XML_RULE)
+
+# Always use our own xml-i18n-tools.
+XML_I18N_EXTRACT='$(top_builddir)/xml-i18n-extract'
+AC_SUBST(XML_I18N_EXTRACT)dnl
+
+XML_I18N_MERGE='$(top_builddir)/xml-i18n-merge'
+AC_SUBST(XML_I18N_MERGE)dnl
+
+XML_I18N_UPDATE='$(top_builddir)/xml-i18n-update'
+AC_SUBST(XML_I18N_UPDATE)dnl
+
+AC_PATH_PROG(XML_I18N_TOOLS_PERL, perl)
+if test -z "$XML_I18N_TOOLS_PERL"; then
+   AC_MSG_ERROR([perl not found; required for xml-i18n-tools])
+fi
+if test -z "`$XML_I18N_TOOLS_PERL -v | fgrep '5.' 2> /dev/null`"; then
+   AC_MSG_ERROR([perl 5.x required for xml-i18n-tools])
+fi
+
+dnl  manually sed perl in so people don't have to put the xml-i18n-tools scripts in their 
+dnl  AC_OUTPUT
+AC_OUTPUT_COMMANDS([
+sed -e "s:@XML_I18N_TOOLS_PERL@:${XML_I18N_TOOLS_PERL}:;" < ${srcdir}/xml-i18n-extract.in > xml-i18n-extract;
+chmod ugo+x xml-i18n-extract;
+chmod u+w xml-i18n-extract;
+
+sed -e "s:@XML_I18N_TOOLS_PERL@:${XML_I18N_TOOLS_PERL}:;" < ${srcdir}/xml-i18n-merge.in > xml-i18n-merge;
+chmod ugo+x xml-i18n-merge;
+chmod u+w xml-i18n-merge;
+
+sed -e "s:@XML_I18N_TOOLS_PERL@:${XML_I18N_TOOLS_PERL}:;" < ${srcdir}/xml-i18n-update.in > xml-i18n-update;
+chmod ugo+x xml-i18n-update;
+chmod u+w xml-i18n-update;
+], XML_I18N_TOOLS_PERL=${XML_I18N_TOOLS_PERL})
+
+# Redirect the config.log output again, so that the ltconfig log is not
+# clobbered by the next message.
+exec 5>>./config.log
+])
+
+dnl old names
+AC_DEFUN(AM_PROG_XML_I18N_TOOLS, [indir([AC_PROG_XML_I18N_TOOLS])])dnl
+
+# gnome-common.m4
+# 
+# This only for packages that are not in the GNOME CVS tree.
+
+dnl GNOME_COMMON_INIT
+
+AC_DEFUN([GNOME_COMMON_INIT],
+[
+       GNOME_ACLOCAL_DIR="$GNOME_COMMON_MACROS_DIR"
+       AC_SUBST(GNOME_ACLOCAL_DIR)
+
+       ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+])
+
+
+dnl
+dnl GNOME_INIT_HOOK (script-if-gnome-enabled, [failflag], [additional-inits])
+dnl
+dnl if failflag is "fail" then GNOME_INIT_HOOK will abort if gnomeConf.sh
+dnl is not found. 
+dnl
+
+AC_DEFUN([GNOME_INIT_HOOK],[
+       AC_SUBST(GNOME_LIBS)
+       AC_SUBST(GNOMEUI_LIBS)
+       AC_SUBST(GNOMEGNORBA_LIBS)
+       AC_SUBST(GTKXMHTML_LIBS)
+       AC_SUBST(ZVT_LIBS)
+       AC_SUBST(GNOME_LIBDIR)
+       AC_SUBST(GNOME_INCLUDEDIR)
+
+       AC_ARG_WITH(gnome-includes,
+       [  --with-gnome-includes   Specify location of GNOME headers],[
+       CFLAGS="$CFLAGS -I$withval"
+       ])
+       
+       AC_ARG_WITH(gnome-libs,
+       [  --with-gnome-libs       Specify location of GNOME libs],[
+       LDFLAGS="$LDFLAGS -L$withval"
+       gnome_prefix=$withval
+       ])
+
+       AC_ARG_WITH(gnome,
+       [  --with-gnome            Specify prefix for GNOME files],
+               if test x$withval = xyes; then
+                       want_gnome=yes
+                       dnl Note that an empty true branch is not
+                       dnl valid sh syntax.
+                       ifelse([$1], [], :, [$1])
+               else
+                       if test "x$withval" = xno; then
+                               want_gnome=no
+                       else
+                               want_gnome=yes
+                               LDFLAGS="$LDFLAGS -L$withval/lib"
+                               CFLAGS="$CFLAGS -I$withval/include"
+                               gnome_prefix=$withval/lib
+                       fi
+               fi,
+               want_gnome=yes)
+
+       if test "x$want_gnome" = xyes; then
+
+           AC_PATH_PROG(GNOME_CONFIG,gnome-config,no)
+           if test "$GNOME_CONFIG" = "no"; then
+             no_gnome_config="yes"
+           else
+             AC_MSG_CHECKING(if $GNOME_CONFIG works)
+             if $GNOME_CONFIG --libs-only-l gnome >/dev/null 2>&1; then
+               AC_MSG_RESULT(yes)
+               GNOME_GNORBA_HOOK([],$2)
+               GNOME_LIBS="`$GNOME_CONFIG --libs-only-l gnome`"
+               GNOMEUI_LIBS="`$GNOME_CONFIG --libs-only-l gnomeui`"
+               GNOMEGNORBA_LIBS="`$GNOME_CONFIG --libs-only-l gnorba gnomeui`"
+               GTKXMHTML_LIBS="`$GNOME_CONFIG --libs-only-l gtkxmhtml`"
+               ZVT_LIBS="`$GNOME_CONFIG --libs-only-l zvt`"
+               GNOME_LIBDIR="`$GNOME_CONFIG --libs-only-L gnorba gnomeui`"
+               GNOME_INCLUDEDIR="`$GNOME_CONFIG --cflags gnorba gnomeui`"
+                $1
+             else
+               AC_MSG_RESULT(no)
+               no_gnome_config="yes"
+              fi
+            fi
+
+           if test x$exec_prefix = xNONE; then
+               if test x$prefix = xNONE; then
+                   gnome_prefix=$ac_default_prefix/lib
+               else
+                   gnome_prefix=$prefix/lib
+               fi
+           else
+               gnome_prefix=`eval echo \`echo $libdir\``
+           fi
+       
+           if test "$no_gnome_config" = "yes"; then
+              AC_MSG_CHECKING(for gnomeConf.sh file in $gnome_prefix)
+             if test -f $gnome_prefix/gnomeConf.sh; then
+               AC_MSG_RESULT(found)
+               echo "loading gnome configuration from" \
+                    "$gnome_prefix/gnomeConf.sh"
+               . $gnome_prefix/gnomeConf.sh
+               $1
+             else
+               AC_MSG_RESULT(not found)
+               if test x$2 = xfail; then
+                 AC_MSG_ERROR(Could not find the gnomeConf.sh file that is generated by gnome-libs install)
+               fi
+             fi
+            fi
+       fi
+
+       if test -n "$3"; then
+         n="$3"
+         for i in $n; do
+           AC_MSG_CHECKING(extra library \"$i\")
+           case $i in 
+             applets)
+               AC_SUBST(GNOME_APPLETS_LIBS)
+               GNOME_APPLETS_LIBS=`$GNOME_CONFIG --libs-only-l applets`
+               AC_MSG_RESULT($GNOME_APPLETS_LIBS);;
+             docklets)
+               AC_SUBST(GNOME_DOCKLETS_LIBS)
+               GNOME_DOCKLETS_LIBS=`$GNOME_CONFIG --libs-only-l docklets`
+               AC_MSG_RESULT($GNOME_DOCKLETS_LIBS);;
+             capplet)
+               AC_SUBST(GNOME_CAPPLET_LIBS)
+               GNOME_CAPPLET_LIBS=`$GNOME_CONFIG --libs-only-l capplet`
+               AC_MSG_RESULT($GNOME_CAPPLET_LIBS);;
+             *)
+               AC_MSG_RESULT(unknown library)
+           esac
+         done
+       fi
+])
+
+dnl
+dnl GNOME_INIT ([additional-inits])
+dnl
+
+AC_DEFUN([GNOME_INIT],[
+       GNOME_INIT_HOOK([],fail,$1)
+])
+
+dnl
+dnl GNOME_GNORBA_HOOK (script-if-gnorba-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if gnorba is not found.
+dnl
+
+AC_DEFUN([GNOME_GNORBA_HOOK],[
+       GNOME_ORBIT_HOOK([],$2)
+       AC_CACHE_CHECK([for gnorba libraries],gnome_cv_gnorba_found,[
+               gnome_cv_gnorba_found=no
+               if test x$gnome_cv_orbit_found = xyes; then
+                       GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+                       GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+                       if test -n "$GNORBA_LIBS"; then
+                               gnome_cv_gnorba_found=yes
+                       fi
+               fi
+       ])
+       AM_CONDITIONAL(HAVE_GNORBA, test x$gnome_cv_gnorba_found = xyes)
+       if test x$gnome_cv_orbit_found = xyes; then
+               $1
+               GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+               GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+               AC_SUBST(GNORBA_CFLAGS)
+               AC_SUBST(GNORBA_LIBS)
+       else
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(gnorba library not installed or installation problem)
+               fi
+       fi
+])
+
+AC_DEFUN([GNOME_GNORBA_CHECK], [
+       GNOME_GNORBA_HOOK([],failure)
+])
+
+dnl
+dnl GNOME_ORBIT_HOOK (script-if-orbit-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if orbit is not found.
+dnl
+
+AC_DEFUN([GNOME_ORBIT_HOOK],[
+       AC_PATH_PROG(ORBIT_CONFIG,orbit-config,no)
+       AC_PATH_PROG(ORBIT_IDL,orbit-idl,no)
+       AC_CACHE_CHECK([for working ORBit environment],gnome_cv_orbit_found,[
+               if test x$ORBIT_CONFIG = xno -o x$ORBIT_IDL = xno; then
+                       gnome_cv_orbit_found=no
+               else
+                       gnome_cv_orbit_found=yes
+               fi
+       ])
+       AM_CONDITIONAL(HAVE_ORBIT, test x$gnome_cv_orbit_found = xyes)
+       if test x$gnome_cv_orbit_found = xyes; then
+               $1
+               ORBIT_CFLAGS=`orbit-config --cflags client server`
+               ORBIT_LIBS=`orbit-config --use-service=name --libs client server`
+               AC_SUBST(ORBIT_CFLAGS)
+               AC_SUBST(ORBIT_LIBS)
+       else
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(ORBit not installed or installation problem)
+               fi
+       fi
+])
+
+AC_DEFUN([GNOME_ORBIT_CHECK], [
+       GNOME_ORBIT_HOOK([],failure)
+])
+
+#serial 1
+# This test replaces the one in autoconf.
+# Currently this macro should have the same name as the autoconf macro
+# because gettext's gettext.m4 (distributed in the automake package)
+# still uses it.  Otherwise, the use in gettext.m4 makes autoheader
+# give these diagnostics:
+#   configure.in:556: AC_TRY_COMPILE was called before AC_ISC_POSIX
+#   configure.in:556: AC_TRY_RUN was called before AC_ISC_POSIX
+
+undefine([AC_ISC_POSIX])
+
+AC_DEFUN([AC_ISC_POSIX],
+  [
+    dnl This test replaces the obsolescent AC_ISC_POSIX kludge.
+    AC_CHECK_LIB(cposix, strerror, [LIBS="$LIBS -lcposix"])
+  ]
+)
+
+
+# serial 1
+
+# @defmac AC_PROG_CC_STDC
+# @maindex PROG_CC_STDC
+# @ovindex CC
+# If the C compiler in not in ANSI C mode by default, try to add an option
+# to output variable @code{CC} to make it so.  This macro tries various
+# options that select ANSI C on some system or another.  It considers the
+# compiler to be in ANSI C mode if it handles function prototypes correctly.
+#
+# If you use this macro, you should check after calling it whether the C
+# compiler has been set to accept ANSI C; if not, the shell variable
+# @code{am_cv_prog_cc_stdc} is set to @samp{no}.  If you wrote your source
+# code in ANSI C, you can make an un-ANSIfied copy of it by using the
+# program @code{ansi2knr}, which comes with Ghostscript.
+# @end defmac
+
+AC_DEFUN([AM_PROG_CC_STDC],
+[AC_REQUIRE([AC_PROG_CC])
+AC_BEFORE([$0], [AC_C_INLINE])
+AC_BEFORE([$0], [AC_C_CONST])
+dnl Force this before AC_PROG_CPP.  Some cpp's, eg on HPUX, require
+dnl a magic option to avoid problems with ANSI preprocessor commands
+dnl like #elif.
+dnl FIXME: can't do this because then AC_AIX won't work due to a
+dnl circular dependency.
+dnl AC_BEFORE([$0], [AC_PROG_CPP])
+AC_MSG_CHECKING([for ${CC-cc} option to accept ANSI C])
+AC_CACHE_VAL(am_cv_prog_cc_stdc,
+[am_cv_prog_cc_stdc=no
+ac_save_CC="$CC"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX                  -qlanglvl=ansi
+# Ultrix and OSF/1     -std1
+# HP-UX 10.20 and later        -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4                 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  AC_TRY_COMPILE(
+[#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+], [
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+],
+[am_cv_prog_cc_stdc="$ac_arg"; break])
+done
+CC="$ac_save_CC"
+])
+if test -z "$am_cv_prog_cc_stdc"; then
+  AC_MSG_RESULT([none needed])
+else
+  AC_MSG_RESULT([$am_cv_prog_cc_stdc])
+fi
+case "x$am_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $am_cv_prog_cc_stdc" ;;
+esac
+])
+
+dnl GNOME_COMPILE_WARNINGS
+dnl Turn on many useful compiler warnings
+dnl For now, only works on GCC
+AC_DEFUN([GNOME_COMPILE_WARNINGS],[
+  AC_ARG_ENABLE(compile-warnings, 
+    [  --enable-compile-warnings=[no/minimum/yes]      Turn on compiler warnings.],,enable_compile_warnings=minimum)
+
+  AC_MSG_CHECKING(what warning flags to pass to the C compiler)
+  warnCFLAGS=
+  if test "x$GCC" != xyes; then
+    enable_compile_warnings=no
+  fi
+
+  if test "x$enable_compile_warnings" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CFLAGS " in
+      *[\ \    ]-Wall[\ \      ]*) ;;
+      *) warnCFLAGS="-Wall -Wunused" ;;
+      esac
+
+      ## -W is not all that useful.  And it cannot be controlled
+      ## with individual -Wno-xxx flags, unlike -Wall
+      if test "x$enable_compile_warnings" = "xyes"; then
+       warnCFLAGS="$warnCFLAGS -Wmissing-prototypes -Wmissing-declarations"
+      fi
+    fi
+  fi
+  AC_MSG_RESULT($warnCFLAGS)
+
+  AC_ARG_ENABLE(iso-c,
+    [  --enable-iso-c          Try to warn if code is not ISO C ],,
+    enable_iso_c=no)
+
+  AC_MSG_CHECKING(what language compliance flags to pass to the C compiler)
+  complCFLAGS=
+  if test "x$enable_iso_c" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CFLAGS " in
+      *[\ \    ]-ansi[\ \      ]*) ;;
+      *) complCFLAGS="$complCFLAGS -ansi" ;;
+      esac
+
+      case " $CFLAGS " in
+      *[\ \    ]-pedantic[\ \  ]*) ;;
+      *) complCFLAGS="$complCFLAGS -pedantic" ;;
+      esac
+    fi
+  fi
+  AC_MSG_RESULT($complCFLAGS)
+  if test "x$cflags_set" != "xyes"; then
+    CFLAGS="$CFLAGS $warnCFLAGS $complCFLAGS"
+    cflags_set=yes
+    AC_SUBST(cflags_set)
+  fi
+])
+
+dnl For C++, do basically the same thing.
+
+AC_DEFUN([GNOME_CXX_WARNINGS],[
+  AC_ARG_ENABLE(cxx-warnings, 
+    [  --enable-cxx-warnings=[no/minimum/yes]  Turn on compiler warnings.],,enable_cxx_warnings=minimum)
+
+  AC_MSG_CHECKING(what warning flags to pass to the C++ compiler)
+  warnCXXFLAGS=
+  if test "x$GCC" != xyes; then
+    enable_compile_warnings=no
+  fi
+  if test "x$enable_cxx_warnings" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CXXFLAGS " in
+      *[\ \    ]-Wall[\ \      ]*) ;;
+      *) warnCXXFLAGS="-Wall -Wno-unused" ;;
+      esac
+
+      ## -W is not all that useful.  And it cannot be controlled
+      ## with individual -Wno-xxx flags, unlike -Wall
+      if test "x$enable_cxx_warnings" = "xyes"; then
+       warnCXXFLAGS="$warnCXXFLAGS -Wmissing-prototypes -Wmissing-declarations -Wshadow -Woverloaded-virtual"
+      fi
+    fi
+  fi
+  AC_MSG_RESULT($warnCXXFLAGS)
+
+   AC_ARG_ENABLE(iso-cxx,
+     [  --enable-iso-cxx          Try to warn if code is not ISO C++ ],,
+     enable_iso_cxx=no)
+
+   AC_MSG_CHECKING(what language compliance flags to pass to the C++ compiler)
+   complCXXFLAGS=
+   if test "x$enable_iso_cxx" != "xno"; then
+     if test "x$GCC" = "xyes"; then
+      case " $CXXFLAGS " in
+      *[\ \    ]-ansi[\ \      ]*) ;;
+      *) complCXXFLAGS="$complCXXFLAGS -ansi" ;;
+      esac
+
+      case " $CXXFLAGS " in
+      *[\ \    ]-pedantic[\ \  ]*) ;;
+      *) complCXXFLAGS="$complCXXFLAGS -pedantic" ;;
+      esac
+     fi
+   fi
+  AC_MSG_RESULT($complCXXFLAGS)
+  if test "x$cxxflags_set" != "xyes"; then
+    CXXFLAGS="$CXXFLAGS $warnCXXFLAGS $complCXXFLAGS"
+    cxxflags_set=yes
+    AC_SUBST(cxxflags_set)
+  fi
+])
+
+dnl GNOME_X_CHECKS
+dnl
+dnl Basic X11 related checks for X11.  At the end, the following will be
+dnl defined/changed:
+dnl   GTK_{CFLAGS,LIBS}      From AM_PATH_GTK
+dnl   CPPFLAGS              Will include $X_CFLAGS
+dnl   GNOME_HAVE_SM         `true' or `false' depending on whether session
+dnl                          management is available.  It is available if
+dnl                          both -lSM and X11/SM/SMlib.h exist.  (Some
+dnl                          Solaris boxes have the library but not the header)
+dnl   XPM_LIBS               -lXpm if Xpm library is present, otherwise ""
+dnl
+dnl The following configure cache variables are defined (but not used):
+dnl   gnome_cv_passdown_{x_libs,X_LIBS,X_CFLAGS}
+dnl
+AC_DEFUN([GNOME_X_CHECKS],
+[
+       AM_PATH_GTK(1.2.0,,AC_MSG_ERROR(GTK not installed, or gtk-config not in path))
+       dnl Hope that GTK_CFLAGS have only -I and -D.  Otherwise, we could
+       dnl   test -z "$x_includes" || CPPFLAGS="$CPPFLAGS -I$x_includes"
+       dnl
+       dnl Use CPPFLAGS instead of CFLAGS because AC_CHECK_HEADERS uses
+       dnl CPPFLAGS, not CFLAGS
+        CPPFLAGS="$CPPFLAGS $GTK_CFLAGS"
+
+        saved_ldflags="$LDFLAGS"
+        LDFLAGS="$LDFLAGS $GTK_LIBS"
+
+       gnome_cv_passdown_x_libs="$GTK_LIBS"
+       gnome_cv_passdown_X_LIBS="$GTK_LIBS"
+       gnome_cv_passdown_X_CFLAGS="$GTK_CFLAGS"
+       gnome_cv_passdown_GTK_LIBS="$GTK_LIBS"
+
+        LDFLAGS="$saved_ldflags $GTK_LIBS"
+
+dnl We are requiring GTK >= 1.1.1, which means this will be fine anyhow.
+       USE_DEVGTK=true
+
+dnl    AC_MSG_CHECKING([whether to use features from (unstable) GTK+ 1.1.x])
+dnl    AC_EGREP_CPP(answer_affirmatively,
+dnl    [#include <gtk/gtkfeatures.h>
+dnl    #ifdef GTK_HAVE_FEATURES_1_1_0
+dnl       answer_affirmatively
+dnl    #endif
+dnl    ], dev_gtk=yes, dev_gtk=no)
+dnl    if test "$dev_gtk" = "yes"; then
+dnl       USE_DEVGTK=true
+dnl    fi
+dnl    AC_MSG_RESULT("$dev_gtk")
+
+       GNOME_HAVE_SM=true
+       case "$GTK_LIBS" in
+        *-lSM*)
+           dnl Already found it.
+           ;;
+        *)
+           dnl Assume that if we have -lSM then we also have -lICE.
+           AC_CHECK_LIB(SM, SmcSaveYourselfDone,
+               [GTK_LIBS="-lSM -lICE $GTK_LIBS"],GNOME_HAVE_SM=false,
+               $x_libs -lICE)
+           ;;
+       esac
+
+       if test "$GNOME_HAVE_SM" = true; then
+          AC_CHECK_HEADERS(X11/SM/SMlib.h,,GNOME_HAVE_SM=false)
+       fi
+
+       if test "$GNOME_HAVE_SM" = true; then
+          AC_DEFINE(HAVE_LIBSM)
+       fi
+
+       XPM_LIBS=""
+       AC_CHECK_LIB(Xpm, XpmFreeXpmImage, [XPM_LIBS="-lXpm"], , $x_libs)
+       AC_SUBST(XPM_LIBS)
+
+       AC_REQUIRE([GNOME_PTHREAD_CHECK])
+        LDFLAGS="$saved_ldflags"
+
+       AC_PROVIDE([GNOME_X_CHECKS])
+])
+
+# Configure paths for GTK+
+# Owen Taylor     97-11-3
+
+dnl AM_PATH_GTK([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
+dnl Test for GTK, and define GTK_CFLAGS and GTK_LIBS
+dnl
+AC_DEFUN(AM_PATH_GTK,
+[dnl 
+dnl Get the cflags and libraries from the gtk-config script
+dnl
+AC_ARG_WITH(gtk-prefix,[  --with-gtk-prefix=PFX   Prefix where GTK is installed (optional)],
+            gtk_config_prefix="$withval", gtk_config_prefix="")
+AC_ARG_WITH(gtk-exec-prefix,[  --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)],
+            gtk_config_exec_prefix="$withval", gtk_config_exec_prefix="")
+AC_ARG_ENABLE(gtktest, [  --disable-gtktest       Do not try to compile and run a test GTK program],
+                   , enable_gtktest=yes)
+
+  for module in . $4
+  do
+      case "$module" in
+         gthread) 
+             gtk_config_args="$gtk_config_args gthread"
+         ;;
+      esac
+  done
+
+  if test x$gtk_config_exec_prefix != x ; then
+     gtk_config_args="$gtk_config_args --exec-prefix=$gtk_config_exec_prefix"
+     if test x${GTK_CONFIG+set} != xset ; then
+        GTK_CONFIG=$gtk_config_exec_prefix/bin/gtk-config
+     fi
+  fi
+  if test x$gtk_config_prefix != x ; then
+     gtk_config_args="$gtk_config_args --prefix=$gtk_config_prefix"
+     if test x${GTK_CONFIG+set} != xset ; then
+        GTK_CONFIG=$gtk_config_prefix/bin/gtk-config
+     fi
+  fi
+
+  AC_PATH_PROG(GTK_CONFIG, gtk-config, no)
+  min_gtk_version=ifelse([$1], ,0.99.7,$1)
+  AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
+  no_gtk=""
+  if test "$GTK_CONFIG" = "no" ; then
+    no_gtk=yes
+  else
+    GTK_CFLAGS=`$GTK_CONFIG $gtk_config_args --cflags`
+    GTK_LIBS=`$GTK_CONFIG $gtk_config_args --libs`
+    gtk_config_major_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    gtk_config_minor_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    gtk_config_micro_version=`$GTK_CONFIG $gtk_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_gtktest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GTK_CFLAGS"
+      LIBS="$GTK_LIBS $LIBS"
+dnl
+dnl Now check if the installed GTK is sufficiently new. (Also sanity
+dnl checks the results of gtk-config to some extent
+dnl
+      rm -f conf.gtktest
+      AC_TRY_RUN([
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int 
+main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.gtktest");
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = g_strdup("$min_gtk_version");
+  if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+     printf("%s, bad version string\n", "$min_gtk_version");
+     exit(1);
+   }
+
+  if ((gtk_major_version != $gtk_config_major_version) ||
+      (gtk_minor_version != $gtk_config_minor_version) ||
+      (gtk_micro_version != $gtk_config_micro_version))
+    {
+      printf("\n*** 'gtk-config --version' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n", 
+             $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version,
+             gtk_major_version, gtk_minor_version, gtk_micro_version);
+      printf ("*** was found! If gtk-config was correct, then it is best\n");
+      printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n");
+      printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n");
+      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+      printf("*** required on your system.\n");
+      printf("*** If gtk-config was wrong, set the environment variable GTK_CONFIG\n");
+      printf("*** to point to the correct copy of gtk-config, and remove the file config.cache\n");
+      printf("*** before re-running configure\n");
+    } 
+#if defined (GTK_MAJOR_VERSION) && defined (GTK_MINOR_VERSION) && defined (GTK_MICRO_VERSION)
+  else if ((gtk_major_version != GTK_MAJOR_VERSION) ||
+          (gtk_minor_version != GTK_MINOR_VERSION) ||
+           (gtk_micro_version != GTK_MICRO_VERSION))
+    {
+      printf("*** GTK+ header files (version %d.%d.%d) do not match\n",
+            GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
+      printf("*** library (version %d.%d.%d)\n",
+            gtk_major_version, gtk_minor_version, gtk_micro_version);
+    }
+#endif /* defined (GTK_MAJOR_VERSION) ... */
+  else
+    {
+      if ((gtk_major_version > major) ||
+        ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
+        ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro)))
+      {
+        return 0;
+       }
+     else
+      {
+        printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n",
+               gtk_major_version, gtk_minor_version, gtk_micro_version);
+        printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n",
+              major, minor, micro);
+        printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n");
+        printf("***\n");
+        printf("*** If you have already installed a sufficiently new version, this error\n");
+        printf("*** probably means that the wrong copy of the gtk-config shell script is\n");
+        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+        printf("*** of GTK+, but you can also set the GTK_CONFIG environment to point to the\n");
+        printf("*** correct copy of gtk-config. (In this case, you will have to\n");
+        printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n");
+        printf("*** so that the correct libraries are found at run-time))\n");
+      }
+    }
+  return 1;
+}
+],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_gtk" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$GTK_CONFIG" = "no" ; then
+       echo "*** The gtk-config script installed by GTK could not be found"
+       echo "*** If GTK was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the GTK_CONFIG environment variable to the"
+       echo "*** full path to gtk-config."
+     else
+       if test -f conf.gtktest ; then
+        :
+       else
+          echo "*** Could not run GTK test program, checking why..."
+          CFLAGS="$CFLAGS $GTK_CFLAGS"
+          LIBS="$LIBS $GTK_LIBS"
+          AC_TRY_LINK([
+#include <gtk/gtk.h>
+#include <stdio.h>
+],      [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding GTK or finding the wrong"
+          echo "*** version of GTK. If it is not finding GTK, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+         echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"
+          echo "***"
+          echo "*** If you have a RedHat 5.0 system, you should remove the GTK package that"
+          echo "*** came with the system with the command"
+          echo "***"
+          echo "***    rpm --erase --nodeps gtk gtk-devel" ],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means GTK was incorrectly installed"
+          echo "*** or that you have moved GTK since it was installed. In the latter case, you"
+          echo "*** may want to edit the gtk-config script: $GTK_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     GTK_CFLAGS=""
+     GTK_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(GTK_CFLAGS)
+  AC_SUBST(GTK_LIBS)
+  rm -f conf.gtktest
+])
+
+dnl
+dnl And better, use gthreads instead...
+dnl
+
+AC_DEFUN([GNOME_PTHREAD_CHECK],[
+       PTHREAD_LIB=""
+       AC_CHECK_LIB(pthread, pthread_create, PTHREAD_LIB="-lpthread",
+               [AC_CHECK_LIB(pthreads, pthread_create, PTHREAD_LIB="-lpthreads",
+                   [AC_CHECK_LIB(c_r, pthread_create, PTHREAD_LIB="-lc_r",
+                       [AC_CHECK_FUNC(pthread_create)]
+                   )]
+               )]
+       )
+       AC_SUBST(PTHREAD_LIB)
+       AC_PROVIDE([GNOME_PTHREAD_CHECK])
+])
+
+# Macro to add for using GNU gettext.
+# Ulrich Drepper <drepper@cygnus.com>, 1995.
+#
+# This file can be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU General Public
+# License but which still want to provide support for the GNU gettext
+# functionality.
+# Please note that the actual code of GNU gettext is covered by the GNU
+# General Public License and is *not* in the public domain.
+
+# serial 10
+
+dnl Usage: AM_WITH_NLS([TOOLSYMBOL], [NEEDSYMBOL], [LIBDIR]).
+dnl If TOOLSYMBOL is specified and is 'use-libtool', then a libtool library
+dnl    $(top_builddir)/intl/libintl.la will be created (shared and/or static,
+dnl    depending on --{enable,disable}-{shared,static} and on the presence of
+dnl    AM-DISABLE-SHARED). Otherwise, a static library
+dnl    $(top_builddir)/intl/libintl.a will be created.
+dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext
+dnl    implementations (in libc or libintl) without the ngettext() function
+dnl    will be ignored.
+dnl LIBDIR is used to find the intl libraries.  If empty,
+dnl    the value `$(top_builddir)/intl/' is used.
+dnl
+dnl The result of the configuration is one of three cases:
+dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled
+dnl    and used.
+dnl    Catalog format: GNU --> install in $(datadir)
+dnl    Catalog extension: .mo after installation, .gmo in source tree
+dnl 2) GNU gettext has been found in the system's C library.
+dnl    Catalog format: GNU --> install in $(datadir)
+dnl    Catalog extension: .mo after installation, .gmo in source tree
+dnl 3) No internationalization, always use English msgid.
+dnl    Catalog format: none
+dnl    Catalog extension: none
+dnl The use of .gmo is historical (it was needed to avoid overwriting the
+dnl GNU format catalogs when building on a platform with an X/Open gettext),
+dnl but we keep it in order not to force irrelevant filename changes on the
+dnl maintainers.
+dnl
+AC_DEFUN([AM_WITH_NLS],
+  [AC_MSG_CHECKING([whether NLS is requested])
+    dnl Default is enabled NLS
+    AC_ARG_ENABLE(nls,
+      [  --disable-nls           do not use Native Language Support],
+      USE_NLS=$enableval, USE_NLS=yes)
+    AC_MSG_RESULT($USE_NLS)
+    AC_SUBST(USE_NLS)
+
+    BUILD_INCLUDED_LIBINTL=no
+    USE_INCLUDED_LIBINTL=no
+    INTLLIBS=
+
+    dnl If we use NLS figure out what method
+    if test "$USE_NLS" = "yes"; then
+      AC_DEFINE(ENABLE_NLS, 1,
+        [Define to 1 if translation of program messages to the user's native language
+   is requested.])
+      AC_MSG_CHECKING([whether included gettext is requested])
+      AC_ARG_WITH(included-gettext,
+        [  --with-included-gettext use the GNU gettext library included here],
+        nls_cv_force_use_gnu_gettext=$withval,
+        nls_cv_force_use_gnu_gettext=no)
+      AC_MSG_RESULT($nls_cv_force_use_gnu_gettext)
+
+      nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext"
+      if test "$nls_cv_force_use_gnu_gettext" != "yes"; then
+        dnl User does not insist on using GNU NLS library.  Figure out what
+        dnl to use.  If GNU gettext is available we use this.  Else we have
+        dnl to fall back to GNU NLS library.
+       CATOBJEXT=NONE
+
+        dnl Add a version number to the cache macros.
+        define(gt_cv_func_gnugettext_libc, [gt_cv_func_gnugettext]ifelse([$2], need-ngettext, 2, 1)[_libc])
+        define(gt_cv_func_gnugettext_libintl, [gt_cv_func_gnugettext]ifelse([$2], need-ngettext, 2, 1)[_libintl])
+
+       AC_CHECK_HEADER(libintl.h,
+         [AC_CACHE_CHECK([for GNU gettext in libc], gt_cv_func_gnugettext_libc,
+           [AC_TRY_LINK([#include <libintl.h>
+extern int _nl_msg_cat_cntr;],
+              [bindtextdomain ("", "");
+return (int) gettext ("")]ifelse([$2], need-ngettext, [ + (int) ngettext ("", "", 0)], [])[ + _nl_msg_cat_cntr],
+              gt_cv_func_gnugettext_libc=yes,
+              gt_cv_func_gnugettext_libc=no)])
+
+          if test "$gt_cv_func_gnugettext_libc" != "yes"; then
+            AC_CACHE_CHECK([for GNU gettext in libintl],
+              gt_cv_func_gnugettext_libintl,
+              [gt_save_LIBS="$LIBS"
+               LIBS="$LIBS -lintl $LIBICONV"
+               AC_TRY_LINK([#include <libintl.h>
+extern int _nl_msg_cat_cntr;],
+                 [bindtextdomain ("", "");
+return (int) gettext ("")]ifelse([$2], need-ngettext, [ + (int) ngettext ("", "", 0)], [])[ + _nl_msg_cat_cntr],
+                 gt_cv_func_gnugettext_libintl=yes,
+                 gt_cv_func_gnugettext_libintl=no)
+               LIBS="$gt_save_LIBS"])
+          fi
+
+          dnl If an already present or preinstalled GNU gettext() is found,
+          dnl use it.  But if this macro is used in GNU gettext, and GNU
+          dnl gettext is already preinstalled in libintl, we update this
+          dnl libintl.  (Cf. the install rule in intl/Makefile.in.)
+          if test "$gt_cv_func_gnugettext_libc" = "yes" \
+             || { test "$gt_cv_func_gnugettext_libintl" = "yes" \
+                  && test "$PACKAGE" != gettext; }; then
+            AC_DEFINE(HAVE_GETTEXT, 1,
+               [Define if the GNU gettext() function is already present or preinstalled.])
+
+            if test "$gt_cv_func_gnugettext_libintl" = "yes"; then
+              dnl If iconv() is in a separate libiconv library, then anyone
+              dnl linking with libintl{.a,.so} also needs to link with
+              dnl libiconv.
+              INTLLIBS="-lintl $LIBICONV"
+            fi
+
+            gt_save_LIBS="$LIBS"
+            LIBS="$LIBS $INTLLIBS"
+            AC_CHECK_FUNCS(dcgettext)
+            LIBS="$gt_save_LIBS"
+
+            dnl Search for GNU msgfmt in the PATH.
+            AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+              [$ac_dir/$ac_word --statistics /dev/null >/dev/null 2>&1], :)
+            AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
+
+            dnl Search for GNU xgettext in the PATH.
+            AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+              [$ac_dir/$ac_word --omit-header /dev/null >/dev/null 2>&1], :)
+
+            CATOBJEXT=.gmo
+          fi
+       ])
+
+        if test "$CATOBJEXT" = "NONE"; then
+         dnl GNU gettext is not found in the C library.
+         dnl Fall back on GNU gettext library.
+         nls_cv_use_gnu_gettext=yes
+        fi
+      fi
+
+      if test "$nls_cv_use_gnu_gettext" = "yes"; then
+        dnl Mark actions used to generate GNU NLS library.
+        INTLOBJS="\$(GETTOBJS)"
+        AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+         [$ac_dir/$ac_word --statistics /dev/null >/dev/null 2>&1], :)
+        AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
+        AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+         [$ac_dir/$ac_word --omit-header /dev/null >/dev/null 2>&1], :)
+        AC_SUBST(MSGFMT)
+       BUILD_INCLUDED_LIBINTL=yes
+       USE_INCLUDED_LIBINTL=yes
+        CATOBJEXT=.gmo
+       INTLLIBS="ifelse([$3],[],\$(top_builddir)/intl,[$3])/libintl.ifelse([$1], use-libtool, [l], [])a $LIBICONV"
+       LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'`
+      fi
+
+      dnl This could go away some day; the PATH_PROG_WITH_TEST already does it.
+      dnl Test whether we really found GNU msgfmt.
+      if test "$GMSGFMT" != ":"; then
+       dnl If it is no GNU msgfmt we define it as : so that the
+       dnl Makefiles still can work.
+       if $GMSGFMT --statistics /dev/null >/dev/null 2>&1; then
+         : ;
+       else
+         AC_MSG_RESULT(
+           [found msgfmt program is not GNU msgfmt; ignore it])
+         GMSGFMT=":"
+       fi
+      fi
+
+      dnl This could go away some day; the PATH_PROG_WITH_TEST already does it.
+      dnl Test whether we really found GNU xgettext.
+      if test "$XGETTEXT" != ":"; then
+       dnl If it is no GNU xgettext we define it as : so that the
+       dnl Makefiles still can work.
+       if $XGETTEXT --omit-header /dev/null >/dev/null 2>&1; then
+         : ;
+       else
+         AC_MSG_RESULT(
+           [found xgettext program is not GNU xgettext; ignore it])
+         XGETTEXT=":"
+       fi
+      fi
+
+      dnl We need to process the po/ directory.
+      POSUB=po
+    fi
+    AC_OUTPUT_COMMANDS(
+     [for ac_file in $CONFIG_FILES; do
+        # Support "outfile[:infile[:infile...]]"
+        case "$ac_file" in
+          *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+        esac
+        # PO directories have a Makefile.in generated from Makefile.in.in.
+        case "$ac_file" in */Makefile.in)
+          # Adjust a relative srcdir.
+          ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+          ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+          ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+          # In autoconf-2.13 it is called $ac_given_srcdir.
+          # In autoconf-2.50 it is called $srcdir.
+          test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+          case "$ac_given_srcdir" in
+            .)  top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+            /*) top_srcdir="$ac_given_srcdir" ;;
+            *)  top_srcdir="$ac_dots$ac_given_srcdir" ;;
+          esac
+          if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+            rm -f "$ac_dir/POTFILES"
+            test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+            sed -e "/^#/d" -e "/^[     ]*\$/d" -e "s,.*,     $top_srcdir/& \\\\," -e "\$s/\(.*\) \\\\/\1/" < "$ac_given_srcdir/$ac_dir/POTFILES.in" > "$ac_dir/POTFILES"
+            test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+            sed -e "/POTFILES =/r $ac_dir/POTFILES" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+          fi
+          ;;
+        esac
+      done])
+
+
+    dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL
+    dnl to 'yes' because some of the testsuite requires it.
+    if test "$PACKAGE" = gettext; then
+      BUILD_INCLUDED_LIBINTL=yes
+    fi
+
+    dnl intl/plural.c is generated from intl/plural.y. It requires bison,
+    dnl because plural.y uses bison specific features. It requires at least
+    dnl bison-1.26 because earlier versions generate a plural.c that doesn't
+    dnl compile.
+    dnl bison is only needed for the maintainer (who touches plural.y). But in
+    dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put
+    dnl the rule in general Makefile. Now, some people carelessly touch the
+    dnl files or have a broken "make" program, hence the plural.c rule will
+    dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not
+    dnl present or too old.
+    AC_CHECK_PROGS([INTLBISON], [bison])
+    if test -z "$INTLBISON"; then
+      ac_verc_fail=yes
+    else
+      dnl Found it, now check the version.
+      AC_MSG_CHECKING([version of bison])
+changequote(<<,>>)dnl
+      ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison .* \([0-9]*\.[0-9.]*\).*$/\1/p'`
+      case $ac_prog_version in
+        '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;;
+        1.2[6-9]* | 1.[3-9][0-9]* | [2-9].*)
+changequote([,])dnl
+           ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;;
+        *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;;
+      esac
+      AC_MSG_RESULT([$ac_prog_version])
+    fi
+    if test $ac_verc_fail = yes; then
+      INTLBISON=:
+    fi
+
+    dnl These rules are solely for the distribution goal.  While doing this
+    dnl we only have to keep exactly one list of the available catalogs
+    dnl in configure.in.
+    for lang in $ALL_LINGUAS; do
+      GMOFILES="$GMOFILES $lang.gmo"
+      POFILES="$POFILES $lang.po"
+    done
+
+    dnl Make all variables we use known to autoconf.
+    AC_SUBST(BUILD_INCLUDED_LIBINTL)
+    AC_SUBST(USE_INCLUDED_LIBINTL)
+    AC_SUBST(CATALOGS)
+    AC_SUBST(CATOBJEXT)
+    AC_SUBST(GMOFILES)
+    AC_SUBST(INTLLIBS)
+    AC_SUBST(INTLOBJS)
+    AC_SUBST(POFILES)
+    AC_SUBST(POSUB)
+
+    dnl For backward compatibility. Some configure.ins may be using this.
+    nls_cv_header_intl=
+    nls_cv_header_libgt=
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    DATADIRNAME=share
+    AC_SUBST(DATADIRNAME)
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    INSTOBJEXT=.mo
+    AC_SUBST(INSTOBJEXT)
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    GENCAT=gencat
+    AC_SUBST(GENCAT)
+  ])
+
+dnl Usage: Just like AM_WITH_NLS, which see.
+AC_DEFUN([AM_GNU_GETTEXT],
+  [AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+   AC_REQUIRE([AC_PROG_CC])dnl
+   AC_REQUIRE([AC_CANONICAL_HOST])dnl
+   AC_REQUIRE([AC_PROG_RANLIB])dnl
+   AC_REQUIRE([AC_ISC_POSIX])dnl
+   AC_REQUIRE([AC_HEADER_STDC])dnl
+   AC_REQUIRE([AC_C_CONST])dnl
+   AC_REQUIRE([AC_C_INLINE])dnl
+   AC_REQUIRE([AC_TYPE_OFF_T])dnl
+   AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+   AC_REQUIRE([AC_FUNC_ALLOCA])dnl
+   AC_REQUIRE([AC_FUNC_MMAP])dnl
+   AC_REQUIRE([jm_GLIBC21])dnl
+
+   AC_CHECK_HEADERS([argz.h limits.h locale.h nl_types.h malloc.h stddef.h \
+stdlib.h string.h unistd.h sys/param.h])
+   AC_CHECK_FUNCS([feof_unlocked fgets_unlocked getcwd getegid geteuid \
+getgid getuid mempcpy munmap putenv setenv setlocale stpcpy strchr strcasecmp \
+strdup strtoul tsearch __argz_count __argz_stringify __argz_next])
+
+   AM_ICONV
+   AM_LANGINFO_CODESET
+   AM_LC_MESSAGES
+   AM_WITH_NLS([$1],[$2],[$3])
+
+   if test "x$CATOBJEXT" != "x"; then
+     if test "x$ALL_LINGUAS" = "x"; then
+       LINGUAS=
+     else
+       AC_MSG_CHECKING(for catalogs to be installed)
+       NEW_LINGUAS=
+       for presentlang in $ALL_LINGUAS; do
+         useit=no
+         for desiredlang in ${LINGUAS-$ALL_LINGUAS}; do
+           # Use the presentlang catalog if desiredlang is
+           #   a. equal to presentlang, or
+           #   b. a variant of presentlang (because in this case,
+           #      presentlang can be used as a fallback for messages
+           #      which are not translated in the desiredlang catalog).
+           case "$desiredlang" in
+             "$presentlang"*) useit=yes;;
+           esac
+         done
+         if test $useit = yes; then
+           NEW_LINGUAS="$NEW_LINGUAS $presentlang"
+         fi
+       done
+       LINGUAS=$NEW_LINGUAS
+       AC_MSG_RESULT($LINGUAS)
+     fi
+
+     dnl Construct list of names of catalog files to be constructed.
+     if test -n "$LINGUAS"; then
+       for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done
+     fi
+   fi
+
+   dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly
+   dnl find the mkinstalldirs script in another subdir but $(top_srcdir).
+   dnl Try to locate is.
+   MKINSTALLDIRS=
+   if test -n "$ac_aux_dir"; then
+     MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs"
+   fi
+   if test -z "$MKINSTALLDIRS"; then
+     MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs"
+   fi
+   AC_SUBST(MKINSTALLDIRS)
+
+   dnl Enable libtool support if the surrounding package wishes it.
+   INTL_LIBTOOL_SUFFIX_PREFIX=ifelse([$1], use-libtool, [l], [])
+   AC_SUBST(INTL_LIBTOOL_SUFFIX_PREFIX)
+  ])
+
+# Search path for a program which passes the given test.
+# Ulrich Drepper <drepper@cygnus.com>, 1996.
+#
+# This file can be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU General Public
+# License but which still want to provide support for the GNU gettext
+# functionality.
+# Please note that the actual code of GNU gettext is covered by the GNU
+# General Public License and is *not* in the public domain.
+
+# serial 2
+
+dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR,
+dnl   TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]])
+AC_DEFUN([AM_PATH_PROG_WITH_TEST],
+[# Extract the first word of "$2", so it can be a program name with args.
+set dummy $2; ac_word=[$]2
+AC_MSG_CHECKING([for $ac_word])
+AC_CACHE_VAL(ac_cv_path_$1,
+[case "[$]$1" in
+  /*)
+  ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+  for ac_dir in ifelse([$5], , $PATH, [$5]); do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if [$3]; then
+       ac_cv_path_$1="$ac_dir/$ac_word"
+       break
+      fi
+    fi
+  done
+  IFS="$ac_save_ifs"
+dnl If no 4th arg is given, leave the cache variable unset,
+dnl so AC_PATH_PROGS will keep looking.
+ifelse([$4], , , [  test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+])dnl
+  ;;
+esac])dnl
+$1="$ac_cv_path_$1"
+if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+  AC_MSG_RESULT([$]$1)
+else
+  AC_MSG_RESULT(no)
+fi
+AC_SUBST($1)dnl
+])
+
+#serial 2
+
+# Test for the GNU C Library, version 2.1 or newer.
+# From Bruno Haible.
+
+AC_DEFUN([jm_GLIBC21],
+  [
+    AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer,
+      ac_cv_gnu_library_2_1,
+      [AC_EGREP_CPP([Lucky GNU user],
+       [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2)
+  Lucky GNU user
+ #endif
+#endif
+       ],
+       ac_cv_gnu_library_2_1=yes,
+       ac_cv_gnu_library_2_1=no)
+      ]
+    )
+    AC_SUBST(GLIBC21)
+    GLIBC21="$ac_cv_gnu_library_2_1"
+  ]
+)
+
+#serial AM2
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_ICONV],
+[
+  dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+  dnl those with the standalone portable GNU libiconv installed).
+
+  AC_ARG_WITH([libiconv-prefix],
+[  --with-libiconv-prefix=DIR  search for libiconv in DIR/include and DIR/lib], [
+    for dir in `echo "$withval" | tr : ' '`; do
+      if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi
+      if test -d $dir/lib; then LDFLAGS="$LDFLAGS -L$dir/lib"; fi
+    done
+   ])
+
+  AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
+    am_cv_func_iconv="no, consider installing GNU libiconv"
+    am_cv_lib_iconv=no
+    AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+      [iconv_t cd = iconv_open("","");
+       iconv(cd,NULL,NULL,NULL,NULL);
+       iconv_close(cd);],
+      am_cv_func_iconv=yes)
+    if test "$am_cv_func_iconv" != yes; then
+      am_save_LIBS="$LIBS"
+      LIBS="$LIBS -liconv"
+      AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+        [iconv_t cd = iconv_open("","");
+         iconv(cd,NULL,NULL,NULL,NULL);
+         iconv_close(cd);],
+        am_cv_lib_iconv=yes
+        am_cv_func_iconv=yes)
+      LIBS="$am_save_LIBS"
+    fi
+  ])
+  if test "$am_cv_func_iconv" = yes; then
+    AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
+    AC_MSG_CHECKING([for iconv declaration])
+    AC_CACHE_VAL(am_cv_proto_iconv, [
+      AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
+      am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+    am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+    AC_MSG_RESULT([$]{ac_t:-
+         }[$]am_cv_proto_iconv)
+    AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+      [Define as const if the declaration of iconv() needs const.])
+  fi
+  LIBICONV=
+  if test "$am_cv_lib_iconv" = yes; then
+    LIBICONV="-liconv"
+  fi
+  AC_SUBST(LIBICONV)
+])
+
+#serial AM1
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_LANGINFO_CODESET],
+[
+  AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset,
+    [AC_TRY_LINK([#include <langinfo.h>],
+      [char* cs = nl_langinfo(CODESET);],
+      am_cv_langinfo_codeset=yes,
+      am_cv_langinfo_codeset=no)
+    ])
+  if test $am_cv_langinfo_codeset = yes; then
+    AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
+      [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
+  fi
+])
+
+# Check whether LC_MESSAGES is available in <locale.h>.
+# Ulrich Drepper <drepper@cygnus.com>, 1995.
+#
+# This file can be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU General Public
+# License but which still want to provide support for the GNU gettext
+# functionality.
+# Please note that the actual code of GNU gettext is covered by the GNU
+# General Public License and is *not* in the public domain.
+
+# serial 2
+
+AC_DEFUN([AM_LC_MESSAGES],
+  [if test $ac_cv_header_locale_h = yes; then
+    AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES,
+      [AC_TRY_LINK([#include <locale.h>], [return LC_MESSAGES],
+       am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)])
+    if test $am_cv_val_LC_MESSAGES = yes; then
+      AC_DEFINE(HAVE_LC_MESSAGES, 1,
+        [Define if your <locale.h> file defines LC_MESSAGES.])
+    fi
+  fi])
+
diff --git a/apps/silcer/autogen.sh b/apps/silcer/autogen.sh
new file mode 100755 (executable)
index 0000000..8cda498
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="the package."
+
+(test -f $srcdir/configure.in) || {
+    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+    echo " top-level directory"
+    exit 1
+}
+
+. $srcdir/macros/autogen.sh
diff --git a/apps/silcer/config.guess b/apps/silcer/config.guess
new file mode 100755 (executable)
index 0000000..cd430f6
--- /dev/null
@@ -0,0 +1,1314 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-08-21'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int dummy(){}" > $dummy.c ;
+       for c in cc gcc c89 ; do
+         ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ;
+         if test $? = 0 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       rm -f $dummy.c $dummy.o $dummy.rel ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # Netbsd (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       # Determine the machine/vendor (is the vendor relevant).
+       case "${UNAME_MACHINE}" in
+           amiga) machine=m68k-unknown ;;
+           arm32) machine=arm-unknown ;;
+           atari*) machine=m68k-atari ;;
+           sun3*) machine=m68k-sun ;;
+           mac68k) machine=m68k-apple ;;
+           macppc) machine=powerpc-apple ;;
+           hp3[0-9][05]) machine=m68k-hp ;;
+           ibmrt|romp-ibm) machine=romp-ibm ;;
+           *) machine=${UNAME_MACHINE}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE}" in
+           i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep __ELF__ >/dev/null
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit 0 ;;
+    alpha:OSF1:*:*)
+       if test $UNAME_RELEASE = "V4.0"; then
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+       fi
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       cat <<EOF >$dummy.s
+       .data
+\$Lformat:
+       .byte 37,100,45,37,120,10,0     # "%d-%x\n"
+
+       .text
+       .globl main
+       .align 4
+       .ent main
+main:
+       .frame \$30,16,\$26,0
+       ldgp \$29,0(\$27)
+       .prologue 1
+       .long 0x47e03d80 # implver \$0
+       lda \$2,-1
+       .long 0x47e20c21 # amask \$2,\$1
+       lda \$16,\$Lformat
+       mov \$0,\$17
+       not \$1,\$18
+       jsr \$26,printf
+       ldgp \$29,0(\$26)
+       mov 0,\$16
+       jsr \$26,exit
+       .end main
+EOF
+       eval $set_cc_for_build
+       $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+       if test "$?" = 0 ; then
+               case `./$dummy` in
+                       0-0)
+                               UNAME_MACHINE="alpha"
+                               ;;
+                       1-0)
+                               UNAME_MACHINE="alphaev5"
+                               ;;
+                       1-1)
+                               UNAME_MACHINE="alphaev56"
+                               ;;
+                       1-101)
+                               UNAME_MACHINE="alphapca56"
+                               ;;
+                       2-303)
+                               UNAME_MACHINE="alphaev6"
+                               ;;
+                       2-307)
+                               UNAME_MACHINE="alphaev67"
+                               ;;
+                       2-1307)
+                               UNAME_MACHINE="alphaev68"
+                               ;;
+               esac
+       fi
+       rm -f $dummy.s $dummy
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit 0 ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit 0;;
+    amiga:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit 0 ;;
+    arc64:OpenBSD:*:*)
+       echo mips64el-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    arc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hkmips:OpenBSD:*:*)
+       echo mips-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    pmax:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sgi:OpenBSD:*:*)
+       echo mips-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+       echo mipsel-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit 0 ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit 0 ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit 0 ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit 0 ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    sparc*:NetBSD:*)
+       echo `uname -p`-unknown-netbsd${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    sun3*:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+       echo m88k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit 0 ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit 0 ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       eval $set_cc_for_build
+       $CC_FOR_BUILD $dummy.c -o $dummy \
+         && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+         && rm -f $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit 0 ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit 0 ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit 0 ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit 0 ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit 0 ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit 0 ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+       exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit 0 ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               eval $set_cc_for_build
+               $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+               rm -f $dummy.c $dummy
+               echo rs6000-ibm-aix3.2.5
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit 0 ;;
+    *:AIX:*:[45])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+              case "${HPUX_REV}" in
+                11.[0-9][0-9])
+                  if [ -x /usr/bin/getconf ]; then
+                    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                        esac ;;
+                    esac
+                  fi ;;
+              esac
+              if [ "${HP_ARCH}" = "" ]; then
+              sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+       eval $set_cc_for_build
+       (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+       if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+       rm -f $dummy.c $dummy
+       fi ;;
+       esac
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    3050*:HI-UX:*:*)
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       eval $set_cc_for_build
+       $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       echo unknown-hitachi-hiuxwe2
+       exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit 0 ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit 0 ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit 0 ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit 0 ;;
+    hppa*:OpenBSD:*:*)
+       echo hppa-unknown-openbsd
+       exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+       echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*T3D:*:*:*)
+       echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY-2:*:*:*)
+       echo cray2-cray-unicos
+        exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    hp300:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:FreeBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit 0 ;;
+    *:OpenBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       exit 0 ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit 0 ;;
+    i*:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit 0 ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i386-pc-interix
+       exit 0 ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit 0 ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit 0 ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    *:GNU:*:*)
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit 0 ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit 0 ;;
+    arm*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux
+       exit 0 ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    mips:Linux:*:*)
+       case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in
+         big)    echo mips-unknown-linux-gnu && exit 0 ;;
+         little) echo mipsel-unknown-linux-gnu && exit 0 ;;
+       esac
+       ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit 0 ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit 0 ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit 0 ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-gnu
+       exit 0 ;;
+    i*86:Linux:*:*)
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       ld_supported_targets=`cd /; ld --help 2>&1 \
+                        | sed -ne '/supported targets:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported targets: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_targets" in
+         elf32-i386)
+               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+               ;;
+         a.out-i386-linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit 0 ;;               
+         coff-i386)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit 0 ;;
+         "")
+               # Either a pre-BFD a.out linker (linux-gnuoldld) or
+               # one that does not give us useful --help.
+               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+               exit 0 ;;
+       esac
+       # Determine whether the default compiler is a.out or elf
+       cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-pc-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-pc-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+       eval $set_cc_for_build
+       $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+       rm -f $dummy.c $dummy
+       test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit 0 ;;
+    i*86:*:5:[78]*)
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit 0 ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit 0 ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit 0 ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit 0 ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit 0 ;;
+    M68*:*:R3V[567]*:*)
+       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit 0 ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit 0 ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit 0 ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit 0 ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit 0 ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit 0 ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit 0 ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Darwin:*:*)
+       echo `uname -p`-apple-darwin${UNAME_RELEASE}
+       exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       if test "${UNAME_MACHINE}" = "x86pc"; then
+               UNAME_MACHINE=pc
+       fi
+       echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+       exit 0 ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit 0 ;;
+    NSR-[KW]:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit 0 ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit 0 ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit 0 ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit 0 ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit 0 ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit 0 ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit 0 ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit 0 ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit 0 ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+eval $set_cc_for_build
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit 0 ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit 0 ;;
+    c34*)
+       echo c34-convex-bsd
+       exit 0 ;;
+    c38*)
+       echo c38-convex-bsd
+       exit 0 ;;
+    c4*)
+       echo c4-convex-bsd
+       exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/apps/silcer/config.h.in b/apps/silcer/config.h.in
new file mode 100644 (file)
index 0000000..22d7de6
--- /dev/null
@@ -0,0 +1,199 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+#undef ENABLE_NLS
+#undef HAVE_CATGETS
+#undef HAVE_GETTEXT
+#undef HAVE_LC_MESSAGES
+#undef HAVE_STPCPY
+#undef HAVE_LIBSM
+#undef PACKAGE_LOCALE_DIR
+#undef PACKAGE_DATA_DIR
+#undef PACKAGE_SOURCE_DIR
+
+#undef EXTRA_GNOME_LIBS
+#undef EXTRA_GNOME_CFLAGS
+
+/* Define if using `alloca.c'. */
+#undef C_ALLOCA
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+#undef CRAY_STACKSEG_END
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#undef ENABLE_NLS
+
+/* Define if you have `alloca', as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define if you have the `__argz_count' function. */
+#undef HAVE___ARGZ_COUNT
+
+/* Define if you have the <argz.h> header file. */
+#undef HAVE_ARGZ_H
+
+/* Define if you have the `__argz_next' function. */
+#undef HAVE___ARGZ_NEXT
+
+/* Define if you have the `__argz_stringify' function. */
+#undef HAVE___ARGZ_STRINGIFY
+
+/* Define if you have the `dcgettext' function. */
+#undef HAVE_DCGETTEXT
+
+/* Define if you have the `feof_unlocked' function. */
+#undef HAVE_FEOF_UNLOCKED
+
+/* Define if you have the `fgets_unlocked' function. */
+#undef HAVE_FGETS_UNLOCKED
+
+/* Define if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define if you have the `getegid' function. */
+#undef HAVE_GETEGID
+
+/* Define if you have the `geteuid' function. */
+#undef HAVE_GETEUID
+
+/* Define if you have the `getgid' function. */
+#undef HAVE_GETGID
+
+/* Define if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#undef HAVE_GETTEXT
+
+/* Define if you have the `getuid' function. */
+#undef HAVE_GETUID
+
+/* Define if you have the iconv() function. */
+#undef HAVE_ICONV
+
+/* Define if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#undef HAVE_LANGINFO_CODESET
+
+/* Define if your <locale.h> file defines LC_MESSAGES. */
+#undef HAVE_LC_MESSAGES
+
+/* Define if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the `mempcpy' function. */
+#undef HAVE_MEMPCPY
+
+/* Define if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define if you have the <nl_types.h> header file. */
+#undef HAVE_NL_TYPES_H
+
+/* Define if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define if you have the `stpcpy' function. */
+#undef HAVE_STPCPY
+
+/* Define if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define if you have the `tsearch' function. */
+#undef HAVE_TSEARCH
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the <X11/SM/SMlib.h> header file. */
+#undef HAVE_X11_SM_SMLIB_H
+
+/* Define as const if the declaration of iconv() needs const. */
+#undef ICONV_CONST
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
+#undef inline
+
+/* Define to `long' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+        STACK_DIRECTION > 0 => grows toward higher addresses
+        STACK_DIRECTION < 0 => grows toward lower addresses
+        STACK_DIRECTION = 0 => direction of growth unknown */
+#undef STACK_DIRECTION
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
diff --git a/apps/silcer/config.sub b/apps/silcer/config.sub
new file mode 100755 (executable)
index 0000000..12ebc78
--- /dev/null
@@ -0,0 +1,1410 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-08-13'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+       | c4x | clipper \
+       | d10v | d30v | dsp16xx \
+       | fr30 \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | m32r | m68000 | m68k | m88k | mcore \
+       | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+       | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+       | mipsbe | mipsel | mipsle | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | ns16k | ns32k \
+       | openrisc \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | s390 | s390x \
+       | sh | sh[34] | sh[34]eb | shbe | shle \
+       | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+       | strongarm \
+       | tahoe | thumb | tic80 | tron \
+       | v850 \
+       | we32k \
+       | x86 | xscale \
+       | z8k)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alphapca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armv*-* \
+       | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c54x-* \
+       | clipper-* | cray2-* | cydra-* \
+       | d10v-* | d30v-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fr30-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | m32r-* \
+       | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | mcore-* \
+       | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+       | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipsel-* \
+       | mipsle-* | mipstx39-* | mipstx39el-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | s390-* | s390x-* \
+       | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+       | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+       | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \
+       | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+       | v850-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \
+       | ymp-* \
+       | z8k-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       cray2)
+               basic_machine=cray2-cray
+               os=-unicos
+               ;;
+       [cjt]90)
+               basic_machine=${basic_machine}-cray
+               os=-unicos
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mipsel*-linux*)
+               basic_machine=mipsel-unknown
+               os=-linux-gnu
+               ;;
+       mips*-linux*)
+               basic_machine=mips-unknown
+               os=-linux-gnu
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       mmix*)
+               basic_machine=mmix-knuth
+               os=-mmixware
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+        pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | k5 | k6 | nexgen)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2)
+               basic_machine=i686-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sparclite-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=t3e-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       windows32)
+               basic_machine=i386-pc
+               os=-windows32-msvcrt
+               ;;
+       xmp)
+               basic_machine=xmp-cray
+               os=-unicos
+               ;;
+        xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       mips)
+               if [ x$os = x-linux-gnu ]; then
+                       basic_machine=mips-unknown
+               else
+                       basic_machine=mips-mips
+               fi
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh3 | sh4 | sh3eb | sh4eb)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv9 | sparcv9b)
+               basic_machine=sparc-sun
+               ;;
+        cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       c4x*)
+               basic_machine=c4x-none
+               os=-coff
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto*)
+               os=-nto-qnx
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+       pdp10-*)
+               os=-tops20
+               ;;
+        pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+        *-gould)
+               os=-sysv
+               ;;
+        *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+        *-sgi)
+               os=-irix
+               ;;
+        *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -vxsim* | -vxworks*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/apps/silcer/configure.in b/apps/silcer/configure.in
new file mode 100644 (file)
index 0000000..aef8e2c
--- /dev/null
@@ -0,0 +1,46 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(configure.in)
+AM_INIT_AUTOMAKE(silcer, 0.1)
+AM_CONFIG_HEADER(config.h)
+AC_PREFIX_DEFAULT(/usr/local)
+
+AM_ACLOCAL_INCLUDE(macros)
+AM_PROG_XML_I18N_TOOLS
+GNOME_COMMON_INIT
+GNOME_INIT
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_PROG_CXX
+AM_PROG_CC_STDC
+AC_HEADER_STDC
+
+GNOME_CXX_WARNINGS
+GNOME_COMPILE_WARNINGS
+GNOME_X_CHECKS
+
+EXTRA_GNOME_LIBS="`$GNOME_CONFIG --libs libglade gnomeui gnomemm gal`"
+EXTRA_GNOME_CFLAGS="`$GNOME_CONFIG --cflags libglade gnomeui gnomemm gal`"
+AC_SUBST(EXTRA_GNOME_LIBS)  
+AC_SUBST(EXTRA_GNOME_CFLAGS)
+
+dnl Add the languages which your application supports here.
+ALL_LINGUAS=""
+AM_GNU_GETTEXT
+
+dnl Set PACKAGE_LOCALE_DIR in config.h.
+if test "x${prefix}" = "xNONE"; then
+  AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, "${ac_default_prefix}/${DATADIRNAME}/locale")
+else
+  AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, "${prefix}/${DATADIRNAME}/locale")
+fi
+
+AC_OUTPUT([
+Makefile
+macros/Makefile
+src/Makefile
+intl/Makefile
+po/Makefile.in
+])
+
diff --git a/apps/silcer/depcomp b/apps/silcer/depcomp
new file mode 100755 (executable)
index 0000000..6589965
--- /dev/null
@@ -0,0 +1,411 @@
+#! /bin/sh
+
+# depcomp - compile a program generating dependencies as side-effects
+# Copyright 1999, 2000 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+# `libtool' can also be set to `yes' or `no'.
+
+depfile=${depfile-`echo "$object" | sed 's,\([^/]*\)$,.deps/\1,;s/\.\([^.]*\)$/.P\1/'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+   # This is just like dashmstdout with a different argument.
+   dashmflag=-xM
+   depmode=dashmstdout
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+  "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+  tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'.  On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like `#:fec' to the end of the
+    # dependency line.
+    tr ' ' '
+' < "$tmpdepfile" \
+    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+    tr '
+' ' ' >> $depfile
+    echo >> $depfile
+
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' '
+' < "$tmpdepfile" \
+   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+   >> $depfile
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  This file always lives in the current directory.
+  # Also, the AIX compiler puts `$object:' at the start of each line;
+  # $object doesn't have directory information.
+  stripped=`echo "$object" | sed -e 's,^.*/,,' -e 's/\(.*\)\..*$/\1/'`
+  tmpdepfile="$stripped.u"
+  outname="$stripped.o"
+  if test "$libtool" = yes; then
+    "$@" -Wc,-M
+  else
+    "$@" -M
+  fi
+
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form `foo.o: dependent.h'.
+    # Do two passes, one to just change these to
+    # `$object: dependent.h' and one to simply `dependent.h:'.
+    sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
+    sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+tru64)
+   # The Tru64 AIX compiler uses -MD to generate dependencies as a side
+   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put 
+   # dependencies in `foo.d' instead, so we check for that too.
+   # Subdirectories are respected.
+
+   tmpdepfile1="$object.d"
+   tmpdepfile2=`echo "$object" | sed -e 's/.o$/.d/'` 
+   if test "$libtool" = yes; then
+      "$@" -Wc,-MD
+   else
+      "$@" -MD
+   fi
+
+   stat=$?
+   if test $stat -eq 0; then :
+   else
+      rm -f "$tmpdepfile1" "$tmpdepfile2"
+      exit $stat
+   fi
+
+   if test -f "$tmpdepfile1"; then
+      tmpdepfile="$tmpdepfile1"
+   else
+      tmpdepfile="$tmpdepfile2"
+   fi
+   if test -f "$tmpdepfile"; then
+      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+      # That's a space and a tab in the [].
+      sed -e 's,^.*\.[a-z]*:[  ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+   else
+      echo "#dummy" > "$depfile"
+   fi
+   rm -f "$tmpdepfile"
+   ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the proprocessed file to stdout, regardless of -o,
+  # because we must use -o when running libtool.
+  test -z "$dashmflag" && dashmflag=-M
+  ( IFS=" "
+    case " $* " in
+    *" --mode=compile "*) # this is libtool, let us make it quiet
+      for arg
+      do # cycle over the arguments
+        case "$arg" in
+       "--mode=compile")
+         # insert --quiet before "--mode=compile"
+         set fnord "$@" --quiet
+         shift # fnord
+         ;;
+       esac
+       set fnord "$@" "$arg"
+       shift # fnord
+       shift # "$arg"
+      done
+      ;;
+    esac
+    "$@" $dashmflag | sed 's:^[^:]*\:[         ]*:'"$object"'\: :' > "$tmpdepfile"
+  ) &
+  proc=$!
+  "$@"
+  stat=$?
+  wait "$proc"
+  if test "$stat" != 0; then exit $stat; fi
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  # X makedepend
+  (
+    shift
+    cleared=no
+    for arg in "$@"; do
+      case $cleared in no)
+        set ""; shift
+       cleared=yes
+      esac
+      case "$arg" in
+        -D*|-I*)
+         set fnord "$@" "$arg"; shift;;
+       -*)
+         ;;
+       *)
+         set fnord "$@" "$arg"; shift;;
+      esac
+    done
+    obj_suffix="`echo $object | sed 's/^.*\././'`"
+    touch "$tmpdepfile"
+    ${MAKEDEPEND-makedepend} 2>/dev/null -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  ) &
+  proc=$!
+  "$@"
+  stat=$?
+  wait "$proc"
+  if test "$stat" != 0; then exit $stat; fi
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tail +3 "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the proprocessed file to stdout, regardless of -o,
+  # because we must use -o when running libtool.
+  ( IFS=" "
+    case " $* " in
+    *" --mode=compile "*)
+      for arg
+      do # cycle over the arguments
+        case $arg in
+       "--mode=compile")
+         # insert --quiet before "--mode=compile"
+         set fnord "$@" --quiet
+         shift # fnord
+         ;;
+       esac
+       set fnord "$@" "$arg"
+       shift # fnord
+       shift # "$arg"
+      done
+      ;;
+    esac
+    "$@" -E |
+    sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+    sed '$ s: \\$::' > "$tmpdepfile"
+  ) &
+  proc=$!
+  "$@"
+  stat=$?
+  wait "$proc"
+  if test "$stat" != 0; then exit $stat; fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the proprocessed file to stdout, regardless of -o,
+  # because we must use -o when running libtool.
+  ( IFS=" "
+    case " $* " in
+    *" --mode=compile "*)
+      for arg
+      do # cycle over the arguments
+        case $arg in
+       "--mode=compile")
+         # insert --quiet before "--mode=compile"
+         set fnord "$@" --quiet
+         shift # fnord
+         ;;
+       esac
+       set fnord "$@" "$arg"
+       shift # fnord
+       shift # "$arg"
+      done
+      ;;
+    esac
+    "$@" -E |
+    sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+  ) &
+  proc=$!
+  "$@"
+  stat=$?
+  wait "$proc"
+  if test "$stat" != 0; then exit $stat; fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::   \1 \\:p' >> "$depfile"
+  echo "       " >> "$depfile"
+  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
diff --git a/apps/silcer/install-sh b/apps/silcer/install-sh
new file mode 100755 (executable)
index 0000000..e9de238
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/apps/silcer/intl/ChangeLog b/apps/silcer/intl/ChangeLog
new file mode 100644 (file)
index 0000000..e62afd4
--- /dev/null
@@ -0,0 +1,4 @@
+2001-07-24  GNU  <bug-gnu-utils@gnu.org>
+
+       * Version 0.10.39 released.
+
diff --git a/apps/silcer/intl/Makefile.in.in b/apps/silcer/intl/Makefile.in.in
new file mode 100644 (file)
index 0000000..32b7376
--- /dev/null
@@ -0,0 +1,196 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995-1997, 2000, 2001 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file file be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU Public License
+# but which still want to provide support for the GNU gettext functionality.
+# Please note that the actual code is *not* freely available.
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+SHELL = /bin/sh
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datadir = @datadir@
+localedir = $(datadir)/locale
+gettextsrcdir = $(datadir)/gettext/po
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+mkinstalldirs = $(SHELL) `case "$(MKINSTALLDIRS)" in /*) echo "$(MKINSTALLDIRS)" ;; *) echo "$(top_builddir)/$(MKINSTALLDIRS)" ;; esac`
+
+CC = @CC@
+GMSGFMT = @GMSGFMT@
+MSGFMT = @MSGFMT@
+XGETTEXT = @XGETTEXT@
+MSGMERGE = msgmerge
+
+DEFS = @DEFS@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+INCLUDES = -I.. -I$(top_srcdir)/intl
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(PACKAGE).pot \
+$(POFILES) $(GMOFILES)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .pox .gmo .mo
+
+.c.o:
+       $(COMPILE) $<
+
+.po.pox:
+       $(MAKE) $(PACKAGE).pot
+       $(MSGMERGE) $< $(srcdir)/$(PACKAGE).pot -o $*.pox
+
+.po.mo:
+       $(MSGFMT) -o $@ $<
+
+.po.gmo:
+       file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \
+         && rm -f $$file && $(GMSGFMT) --statistics -o $$file $<
+
+
+all: all-@USE_NLS@
+
+all-yes: $(CATALOGS)
+all-no:
+
+# Note: Target 'all' must not depend on target '$(srcdir)/$(PACKAGE).pot',
+# otherwise packages like GCC can not be built if only parts of the source
+# have been downloaded.
+
+$(srcdir)/$(PACKAGE).pot: $(POTFILES) $(srcdir)/POTFILES.in
+       $(XGETTEXT) --default-domain=$(PACKAGE) --directory=$(top_srcdir) \
+         --add-comments --keyword=_ --keyword=N_ \
+         --files-from=$(srcdir)/POTFILES.in \
+       && test ! -f $(PACKAGE).po \
+          || ( rm -f $(srcdir)/$(PACKAGE).pot \
+               && mv $(PACKAGE).po $(srcdir)/$(PACKAGE).pot )
+
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-@USE_NLS@
+       if test "$(PACKAGE)" = "gettext"; then \
+         $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
+         $(INSTALL_DATA) $(srcdir)/Makefile.in.in \
+                         $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+       else \
+         : ; \
+       fi
+install-data-no: all
+install-data-yes: all
+       $(mkinstalldirs) $(DESTDIR)$(datadir)
+       @catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+         dir=$(localedir)/$$lang/LC_MESSAGES; \
+         $(mkinstalldirs) $(DESTDIR)$$dir; \
+         if test -r $$cat; then \
+           $(INSTALL_DATA) $$cat $(DESTDIR)$$dir/$(PACKAGE).mo; \
+           echo "installing $$cat as $(DESTDIR)$$dir/$(PACKAGE).mo"; \
+         else \
+           $(INSTALL_DATA) $(srcdir)/$$cat $(DESTDIR)$$dir/$(PACKAGE).mo; \
+           echo "installing $(srcdir)/$$cat as" \
+                "$(DESTDIR)$$dir/$(PACKAGE).mo"; \
+         fi; \
+       done
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+       catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+         rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(PACKAGE).mo; \
+       done
+       if test "$(PACKAGE)" = "gettext"; then \
+         rm -f $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+       else \
+         : ; \
+       fi
+
+check: all
+
+dvi info tags TAGS ID:
+
+mostlyclean:
+       rm -f core core.* *.pox $(PACKAGE).po *.new.po
+       rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile Makefile.in POTFILES *.mo
+
+maintainer-clean: distclean
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+       rm -f $(GMOFILES)
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir:
+       $(MAKE) update-po
+       @$(MAKE) dist2
+# This is a separate target because 'update-po' must be executed before.
+dist2: $(DISTFILES)
+       dists="$(DISTFILES)"; \
+       for file in $$dists; do \
+         if test -f $$file; then dir=.; else dir=$(srcdir); fi; \
+         cp -p $$dir/$$file $(distdir); \
+       done
+
+update-po: Makefile
+       $(MAKE) $(PACKAGE).pot
+       if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; fi; \
+       cd $(srcdir); \
+       catalogs='$(GMOFILES)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+         echo "$$lang:"; \
+         if $(MSGMERGE) $$lang.po $(PACKAGE).pot -o $$lang.new.po; then \
+           mv -f $$lang.new.po $$lang.po; \
+         else \
+           echo "msgmerge for $$cat failed!"; \
+           rm -f $$lang.new.po; \
+         fi; \
+       done
+       $(MAKE) update-gmo
+
+update-gmo: Makefile $(GMOFILES)
+       @:
+
+Makefile: Makefile.in.in $(top_builddir)/config.status POTFILES.in
+       cd $(top_builddir) \
+         && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+              $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/apps/silcer/intl/VERSION b/apps/silcer/intl/VERSION
new file mode 100644 (file)
index 0000000..be17e27
--- /dev/null
@@ -0,0 +1 @@
+GNU gettext library from gettext-0.10.39
diff --git a/apps/silcer/intl/bindtextdom.c b/apps/silcer/intl/bindtextdom.c
new file mode 100644 (file)
index 0000000..7e5a74a
--- /dev/null
@@ -0,0 +1,368 @@
+/* Implementation of the bindtextdomain(3) function
+   Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+#include "gettextP.h"
+
+#ifdef _LIBC
+/* We have to handle multi-threaded applications.  */
+# include <bits/libc-lock.h>
+#else
+/* Provide dummy implementation if this is outside glibc.  */
+# define __libc_rwlock_define(CLASS, NAME)
+# define __libc_rwlock_wrlock(NAME)
+# define __libc_rwlock_unlock(NAME)
+#endif
+
+/* The internal variables in the standalone libintl.a must have different
+   names than the internal variables in GNU libc, otherwise programs
+   using libintl.a cannot be linked statically.  */
+#if !defined _LIBC
+# define _nl_default_dirname _nl_default_dirname__
+# define _nl_domain_bindings _nl_domain_bindings__
+#endif
+
+/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
+#ifndef offsetof
+# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Contains the default location of the message catalogs.  */
+extern const char _nl_default_dirname[];
+
+/* List with bindings of specific domains.  */
+extern struct binding *_nl_domain_bindings;
+
+/* Lock variable to protect the global data in the gettext implementation.  */
+__libc_rwlock_define (extern, _nl_state_lock)
+
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define BINDTEXTDOMAIN __bindtextdomain
+# define BIND_TEXTDOMAIN_CODESET __bind_textdomain_codeset
+# ifndef strdup
+#  define strdup(str) __strdup (str)
+# endif
+#else
+# define BINDTEXTDOMAIN bindtextdomain__
+# define BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset__
+#endif
+
+/* Prototypes for local functions.  */
+static void set_binding_values PARAMS ((const char *domainname,
+                                       const char **dirnamep,
+                                       const char **codesetp));
+     
+/* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP
+   to be used for the DOMAINNAME message catalog.
+   If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not
+   modified, only the current value is returned.
+   If DIRNAMEP or CODESETP is NULL, the corresponding attribute is neither
+   modified nor returned.  */
+static void
+set_binding_values (domainname, dirnamep, codesetp)
+     const char *domainname;
+     const char **dirnamep;
+     const char **codesetp;
+{
+  struct binding *binding;
+  int modified;
+
+  /* Some sanity checks.  */
+  if (domainname == NULL || domainname[0] == '\0')
+    {
+      if (dirnamep)
+       *dirnamep = NULL;
+      if (codesetp)
+       *codesetp = NULL;
+      return;
+    }
+
+  __libc_rwlock_wrlock (_nl_state_lock);
+
+  modified = 0;
+
+  for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
+    {
+      int compare = strcmp (domainname, binding->domainname);
+      if (compare == 0)
+       /* We found it!  */
+       break;
+      if (compare < 0)
+       {
+         /* It is not in the list.  */
+         binding = NULL;
+         break;
+       }
+    }
+
+  if (binding != NULL)
+    {
+      if (dirnamep)
+       {
+         const char *dirname = *dirnamep;
+
+         if (dirname == NULL)
+           /* The current binding has be to returned.  */
+           *dirnamep = binding->dirname;
+         else
+           {
+             /* The domain is already bound.  If the new value and the old
+                one are equal we simply do nothing.  Otherwise replace the
+                old binding.  */
+             char *result = binding->dirname;
+             if (strcmp (dirname, result) != 0)
+               {
+                 if (strcmp (dirname, _nl_default_dirname) == 0)
+                   result = (char *) _nl_default_dirname;
+                 else
+                   {
+#if defined _LIBC || defined HAVE_STRDUP
+                     result = strdup (dirname);
+#else
+                     size_t len = strlen (dirname) + 1;
+                     result = (char *) malloc (len);
+                     if (__builtin_expect (result != NULL, 1))
+                       memcpy (result, dirname, len);
+#endif
+                   }
+
+                 if (__builtin_expect (result != NULL, 1))
+                   {
+                     if (binding->dirname != _nl_default_dirname)
+                       free (binding->dirname);
+
+                     binding->dirname = result;
+                     modified = 1;
+                   }
+               }
+             *dirnamep = result;
+           }
+       }
+
+      if (codesetp)
+       {
+         const char *codeset = *codesetp;
+
+         if (codeset == NULL)
+           /* The current binding has be to returned.  */
+           *codesetp = binding->codeset;
+         else
+           {
+             /* The domain is already bound.  If the new value and the old
+                one are equal we simply do nothing.  Otherwise replace the
+                old binding.  */
+             char *result = binding->codeset;
+             if (result == NULL || strcmp (codeset, result) != 0)
+               {
+#if defined _LIBC || defined HAVE_STRDUP
+                 result = strdup (codeset);
+#else
+                 size_t len = strlen (codeset) + 1;
+                 result = (char *) malloc (len);
+                 if (__builtin_expect (result != NULL, 1))
+                   memcpy (result, codeset, len);
+#endif
+
+                 if (__builtin_expect (result != NULL, 1))
+                   {
+                     if (binding->codeset != NULL)
+                       free (binding->codeset);
+
+                     binding->codeset = result;
+                     binding->codeset_cntr++;
+                     modified = 1;
+                   }
+               }
+             *codesetp = result;
+           }
+       }
+    }
+  else if ((dirnamep == NULL || *dirnamep == NULL)
+          && (codesetp == NULL || *codesetp == NULL))
+    {
+      /* Simply return the default values.  */
+      if (dirnamep)
+       *dirnamep = _nl_default_dirname;
+      if (codesetp)
+       *codesetp = NULL;
+    }
+  else
+    {
+      /* We have to create a new binding.  */
+      size_t len = strlen (domainname) + 1;
+      struct binding *new_binding =
+       (struct binding *) malloc (offsetof (struct binding, domainname) + len);
+
+      if (__builtin_expect (new_binding == NULL, 0))
+       goto failed;
+
+      memcpy (new_binding->domainname, domainname, len);
+
+      if (dirnamep)
+       {
+         const char *dirname = *dirnamep;
+
+         if (dirname == NULL)
+           /* The default value.  */
+           dirname = _nl_default_dirname;
+         else
+           {
+             if (strcmp (dirname, _nl_default_dirname) == 0)
+               dirname = _nl_default_dirname;
+             else
+               {
+                 char *result;
+#if defined _LIBC || defined HAVE_STRDUP
+                 result = strdup (dirname);
+                 if (__builtin_expect (result == NULL, 0))
+                   goto failed_dirname;
+#else
+                 size_t len = strlen (dirname) + 1;
+                 result = (char *) malloc (len);
+                 if (__builtin_expect (result == NULL, 0))
+                   goto failed_dirname;
+                 memcpy (result, dirname, len);
+#endif
+                 dirname = result;
+               }
+           }
+         *dirnamep = dirname;
+         new_binding->dirname = (char *) dirname;
+       }
+      else
+       /* The default value.  */
+       new_binding->dirname = (char *) _nl_default_dirname;
+
+      new_binding->codeset_cntr = 0;
+
+      if (codesetp)
+       {
+         const char *codeset = *codesetp;
+
+         if (codeset != NULL)
+           {
+             char *result;
+
+#if defined _LIBC || defined HAVE_STRDUP
+             result = strdup (codeset);
+             if (__builtin_expect (result == NULL, 0))
+               goto failed_codeset;
+#else
+             size_t len = strlen (codeset) + 1;
+             result = (char *) malloc (len);
+             if (__builtin_expect (result == NULL, 0))
+               goto failed_codeset;
+             memcpy (result, codeset, len);
+#endif
+             codeset = result;
+             new_binding->codeset_cntr++;
+           }
+         *codesetp = codeset;
+         new_binding->codeset = (char *) codeset;
+       }
+      else
+       new_binding->codeset = NULL;
+
+      /* Now enqueue it.  */
+      if (_nl_domain_bindings == NULL
+         || strcmp (domainname, _nl_domain_bindings->domainname) < 0)
+       {
+         new_binding->next = _nl_domain_bindings;
+         _nl_domain_bindings = new_binding;
+       }
+      else
+       {
+         binding = _nl_domain_bindings;
+         while (binding->next != NULL
+                && strcmp (domainname, binding->next->domainname) > 0)
+           binding = binding->next;
+
+         new_binding->next = binding->next;
+         binding->next = new_binding;
+       }
+
+      modified = 1;
+
+      /* Here we deal with memory allocation failures.  */
+      if (0)
+       {
+       failed_codeset:
+         if (new_binding->dirname != _nl_default_dirname)
+           free (new_binding->dirname);
+       failed_dirname:
+         free (new_binding);
+       failed:
+         if (dirnamep)
+           *dirnamep = NULL;
+         if (codesetp)
+           *codesetp = NULL;
+       }
+    }
+
+  /* If we modified any binding, we flush the caches.  */
+  if (modified)
+    ++_nl_msg_cat_cntr;
+
+  __libc_rwlock_unlock (_nl_state_lock);
+}
+
+/* Specify that the DOMAINNAME message catalog will be found
+   in DIRNAME rather than in the system locale data base.  */
+char *
+BINDTEXTDOMAIN (domainname, dirname)
+     const char *domainname;
+     const char *dirname;
+{
+  set_binding_values (domainname, &dirname, NULL);
+  return (char *) dirname;
+}
+
+/* Specify the character encoding in which the messages from the
+   DOMAINNAME message catalog will be returned.  */
+char *
+BIND_TEXTDOMAIN_CODESET (domainname, codeset)
+     const char *domainname;
+     const char *codeset;
+{
+  set_binding_values (domainname, NULL, &codeset);
+  return (char *) codeset;
+}
+
+#ifdef _LIBC
+/* Aliases for function names in GNU C Library.  */
+weak_alias (__bindtextdomain, bindtextdomain);
+weak_alias (__bind_textdomain_codeset, bind_textdomain_codeset);
+#endif
diff --git a/apps/silcer/intl/config.charset b/apps/silcer/intl/config.charset
new file mode 100755 (executable)
index 0000000..f4f2611
--- /dev/null
@@ -0,0 +1,438 @@
+#! /bin/sh
+# Output a system dependent table of character encoding aliases.
+#
+#   Copyright (C) 2000-2001 Free Software Foundation, Inc.
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU Library General Public License as published
+#   by the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   Library General Public License for more details.
+#
+#   You should have received a copy of the GNU Library General Public
+#   License along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+#   USA.
+#
+# The table consists of lines of the form
+#    ALIAS  CANONICAL
+#
+# ALIAS is the (system dependent) result of "nl_langinfo (CODESET)".
+# ALIAS is compared in a case sensitive way.
+#
+# CANONICAL is the GNU canonical name for this character encoding.
+# It must be an encoding supported by libiconv. Support by GNU libc is
+# also desirable. CANONICAL is case insensitive. Usually an upper case
+# MIME charset name is preferred.
+# The current list of GNU canonical charset names is as follows.
+#
+#       name                         used by which systems         a MIME name?
+#   ASCII, ANSI_X3.4-1968     glibc solaris freebsd
+#   ISO-8859-1                glibc aix hpux irix osf solaris freebsd   yes
+#   ISO-8859-2                glibc aix hpux irix osf solaris freebsd   yes
+#   ISO-8859-3                glibc                                     yes
+#   ISO-8859-4                osf solaris freebsd                       yes
+#   ISO-8859-5                glibc aix hpux irix osf solaris freebsd   yes
+#   ISO-8859-6                glibc aix hpux solaris                    yes
+#   ISO-8859-7                glibc aix hpux irix osf solaris           yes
+#   ISO-8859-8                glibc aix hpux osf solaris                yes
+#   ISO-8859-9                glibc aix hpux irix osf solaris           yes
+#   ISO-8859-13               glibc
+#   ISO-8859-15               glibc aix osf solaris freebsd
+#   KOI8-R                    glibc solaris freebsd                     yes
+#   KOI8-U                    glibc freebsd                             yes
+#   CP437                     dos
+#   CP775                     dos
+#   CP850                     aix osf dos
+#   CP852                     dos
+#   CP855                     dos
+#   CP856                     aix
+#   CP857                     dos
+#   CP861                     dos
+#   CP862                     dos
+#   CP864                     dos
+#   CP865                     dos
+#   CP866                     freebsd dos
+#   CP869                     dos
+#   CP874                     win32 dos
+#   CP922                     aix
+#   CP932                     aix win32 dos
+#   CP943                     aix
+#   CP949                     osf win32 dos
+#   CP950                     win32 dos
+#   CP1046                    aix
+#   CP1124                    aix
+#   CP1129                    aix
+#   CP1250                    win32
+#   CP1251                    glibc win32
+#   CP1252                    aix win32
+#   CP1253                    win32
+#   CP1254                    win32
+#   CP1255                    win32
+#   CP1256                    win32
+#   CP1257                    win32
+#   GB2312                    glibc aix hpux irix solaris freebsd       yes
+#   EUC-JP                    glibc aix hpux irix osf solaris freebsd   yes
+#   EUC-KR                    glibc aix hpux irix osf solaris freebsd   yes
+#   EUC-TW                    glibc aix hpux irix osf solaris
+#   BIG5                      glibc aix hpux osf solaris freebsd        yes
+#   BIG5-HKSCS                glibc
+#   GBK                       aix osf win32 dos
+#   GB18030                   glibc
+#   SHIFT_JIS                 hpux osf solaris freebsd                  yes
+#   JOHAB                     glibc win32
+#   TIS-620                   glibc aix hpux osf solaris
+#   VISCII                    glibc                                     yes
+#   HP-ROMAN8                 hpux
+#   HP-ARABIC8                hpux
+#   HP-GREEK8                 hpux
+#   HP-HEBREW8                hpux
+#   HP-TURKISH8               hpux
+#   HP-KANA8                  hpux
+#   DEC-KANJI                 osf
+#   DEC-HANYU                 osf
+#   UTF-8                     glibc aix hpux osf solaris                yes
+#
+# Note: Names which are not marked as being a MIME name should not be used in
+# Internet protocols for information interchange (mail, news, etc.).
+#
+# Note: ASCII and ANSI_X3.4-1968 are synonymous canonical names. Applications
+# must understand both names and treat them as equivalent.
+#
+# The first argument passed to this file is the canonical host specification,
+#    CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or
+#    CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+
+host="$1"
+os=`echo "$host" | sed -e 's/^[^-]*-[^-]*-\(.*\)$/\1/'`
+echo "# This file contains a table of character encoding aliases,"
+echo "# suitable for operating system '${os}'."
+echo "# It was automatically generated from config.charset."
+# List of references, updated during installation:
+echo "# Packages using this file: "
+case "$os" in
+    linux* | *-gnu*)
+       # With glibc-2.1 or newer, we don't need any canonicalization,
+       # because glibc has iconv and both glibc and libiconv support all
+       # GNU canonical names directly. Therefore, the Makefile does not
+       # need to install the alias file at all.
+       # The following applies only to glibc-2.0.x and older libcs.
+       echo "ISO_646.IRV:1983 ASCII"
+       ;;
+    aix*)
+       echo "ISO8859-1 ISO-8859-1"
+       echo "ISO8859-2 ISO-8859-2"
+       echo "ISO8859-5 ISO-8859-5"
+       echo "ISO8859-6 ISO-8859-6"
+       echo "ISO8859-7 ISO-8859-7"
+       echo "ISO8859-8 ISO-8859-8"
+       echo "ISO8859-9 ISO-8859-9"
+       echo "ISO8859-15 ISO-8859-15"
+       echo "IBM-850 CP850"
+       echo "IBM-856 CP856"
+       echo "IBM-921 ISO-8859-13"
+       echo "IBM-922 CP922"
+       echo "IBM-932 CP932"
+       echo "IBM-943 CP943"
+       echo "IBM-1046 CP1046"
+       echo "IBM-1124 CP1124"
+       echo "IBM-1129 CP1129"
+       echo "IBM-1252 CP1252"
+       echo "IBM-eucCN GB2312"
+       echo "IBM-eucJP EUC-JP"
+       echo "IBM-eucKR EUC-KR"
+       echo "IBM-eucTW EUC-TW"
+       echo "big5 BIG5"
+       echo "GBK GBK"
+       echo "TIS-620 TIS-620"
+       echo "UTF-8 UTF-8"
+       ;;
+    hpux*)
+       echo "iso88591 ISO-8859-1"
+       echo "iso88592 ISO-8859-2"
+       echo "iso88595 ISO-8859-5"
+       echo "iso88596 ISO-8859-6"
+       echo "iso88597 ISO-8859-7"
+       echo "iso88598 ISO-8859-8"
+       echo "iso88599 ISO-8859-9"
+       echo "iso885915 ISO-8859-15"
+       echo "roman8 HP-ROMAN8"
+       echo "arabic8 HP-ARABIC8"
+       echo "greek8 HP-GREEK8"
+       echo "hebrew8 HP-HEBREW8"
+       echo "turkish8 HP-TURKISH8"
+       echo "kana8 HP-KANA8"
+       echo "tis620 TIS-620"
+       echo "big5 BIG5"
+       echo "eucJP EUC-JP"
+       echo "eucKR EUC-KR"
+       echo "eucTW EUC-TW"
+       echo "hp15CN GB2312"
+       #echo "ccdc ?" # what is this?
+       echo "SJIS SHIFT_JIS"
+       echo "utf8 UTF-8"
+       ;;
+    irix*)
+       echo "ISO8859-1 ISO-8859-1"
+       echo "ISO8859-2 ISO-8859-2"
+       echo "ISO8859-5 ISO-8859-5"
+       echo "ISO8859-7 ISO-8859-7"
+       echo "ISO8859-9 ISO-8859-9"
+       echo "eucCN GB2312"
+       echo "eucJP EUC-JP"
+       echo "eucKR EUC-KR"
+       echo "eucTW EUC-TW"
+       ;;
+    osf*)
+       echo "ISO8859-1 ISO-8859-1"
+       echo "ISO8859-2 ISO-8859-2"
+       echo "ISO8859-4 ISO-8859-4"
+       echo "ISO8859-5 ISO-8859-5"
+       echo "ISO8859-7 ISO-8859-7"
+       echo "ISO8859-8 ISO-8859-8"
+       echo "ISO8859-9 ISO-8859-9"
+       echo "ISO8859-15 ISO-8859-15"
+       echo "cp850 CP850"
+       echo "big5 BIG5"
+       echo "dechanyu DEC-HANYU"
+       echo "dechanzi GB2312"
+       echo "deckanji DEC-KANJI"
+       echo "deckorean EUC-KR"
+       echo "eucJP EUC-JP"
+       echo "eucKR EUC-KR"
+       echo "eucTW EUC-TW"
+       echo "GBK GBK"
+       echo "KSC5601 CP949"
+       echo "sdeckanji EUC-JP"
+       echo "SJIS SHIFT_JIS"
+       echo "TACTIS TIS-620"
+       echo "UTF-8 UTF-8"
+       ;;
+    solaris*)
+       echo "646 ASCII"
+       echo "ISO8859-1 ISO-8859-1"
+       echo "ISO8859-2 ISO-8859-2"
+       echo "ISO8859-4 ISO-8859-4"
+       echo "ISO8859-5 ISO-8859-5"
+       echo "ISO8859-6 ISO-8859-6"
+       echo "ISO8859-7 ISO-8859-7"
+       echo "ISO8859-8 ISO-8859-8"
+       echo "ISO8859-9 ISO-8859-9"
+       echo "ISO8859-15 ISO-8859-15"
+       echo "koi8-r KOI8-R"
+       echo "BIG5 BIG5"
+       echo "gb2312 GB2312"
+       echo "cns11643 EUC-TW"
+       echo "5601 EUC-KR"
+       echo "eucJP EUC-JP"
+       echo "PCK SHIFT_JIS"
+       echo "TIS620.2533 TIS-620"
+       #echo "sun_eu_greek ?" # what is this?
+       echo "UTF-8 UTF-8"
+       ;;
+    freebsd*)
+       # FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore
+       # localcharset.c falls back to using the full locale name
+       # from the environment variables.
+       echo "C ASCII"
+       echo "US-ASCII ASCII"
+       for l in la_LN lt_LN; do
+         echo "$l.ASCII ASCII"
+       done
+       for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \
+                fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT la_LN \
+                lt_LN nl_BE nl_NL no_NO pt_PT sv_SE; do
+         echo "$l.ISO_8859-1 ISO-8859-1"
+         echo "$l.DIS_8859-15 ISO-8859-15"
+       done
+       for l in cs_CZ hr_HR hu_HU la_LN lt_LN pl_PL sl_SI; do
+         echo "$l.ISO_8859-2 ISO-8859-2"
+       done
+       for l in la_LN lt_LT; do
+         echo "$l.ISO_8859-4 ISO-8859-4"
+       done
+       for l in ru_RU ru_SU; do
+         echo "$l.KOI8-R KOI8-R"
+         echo "$l.ISO_8859-5 ISO-8859-5"
+         echo "$l.CP866 CP866"
+       done
+       echo "uk_UA.KOI8-U KOI8-U"
+       echo "zh_TW.BIG5 BIG5"
+       echo "zh_TW.Big5 BIG5"
+       echo "zh_CN.EUC GB2312"
+       echo "ja_JP.EUC EUC-JP"
+       echo "ja_JP.SJIS SHIFT_JIS"
+       echo "ja_JP.Shift_JIS SHIFT_JIS"
+       echo "ko_KR.EUC EUC-KR"
+       ;;
+    beos*)
+       # BeOS has a single locale, and it has UTF-8 encoding.
+       echo "* UTF-8"
+       ;;
+    msdosdjgpp*)
+       # DJGPP 2.03 doesn't have nl_langinfo(CODESET); therefore
+       # localcharset.c falls back to using the full locale name
+       # from the environment variables.
+       echo "#"
+       echo "# The encodings given here may not all be correct."
+       echo "# If you find that the encoding given for your language and"
+       echo "# country is not the one your DOS machine actually uses, just"
+       echo "# correct it in this file, and send a mail to"
+       echo "# Juan Manuel Guerrero <st001906@hrz1.hrz.tu-darmstadt.de>"
+       echo "# and Bruno Haible <haible@clisp.cons.org>."
+       echo "#"
+       echo "C ASCII"
+       # ISO-8859-1 languages
+       echo "ca CP850"
+       echo "ca_ES CP850"
+       echo "da CP865"    # not CP850 ??
+       echo "da_DK CP865" # not CP850 ??
+       echo "de CP850"
+       echo "de_AT CP850"
+       echo "de_CH CP850"
+       echo "de_DE CP850"
+       echo "en CP850"
+       echo "en_AU CP850" # not CP437 ??
+       echo "en_CA CP850"
+       echo "en_GB CP850"
+       echo "en_NZ CP437"
+       echo "en_US CP437"
+       echo "en_ZA CP850" # not CP437 ??
+       echo "es CP850"
+       echo "es_AR CP850"
+       echo "es_BO CP850"
+       echo "es_CL CP850"
+       echo "es_CO CP850"
+       echo "es_CR CP850"
+       echo "es_CU CP850"
+       echo "es_DO CP850"
+       echo "es_EC CP850"
+       echo "es_ES CP850"
+       echo "es_GT CP850"
+       echo "es_HN CP850"
+       echo "es_MX CP850"
+       echo "es_NI CP850"
+       echo "es_PA CP850"
+       echo "es_PY CP850"
+       echo "es_PE CP850"
+       echo "es_SV CP850"
+       echo "es_UY CP850"
+       echo "es_VE CP850"
+       echo "et CP850"
+       echo "et_EE CP850"
+       echo "eu CP850"
+       echo "eu_ES CP850"
+       echo "fi CP850"
+       echo "fi_FI CP850"
+       echo "fr CP850"
+       echo "fr_BE CP850"
+       echo "fr_CA CP850"
+       echo "fr_CH CP850"
+       echo "fr_FR CP850"
+       echo "ga CP850"
+       echo "ga_IE CP850"
+       echo "gd CP850"
+       echo "gd_GB CP850"
+       echo "gl CP850"
+       echo "gl_ES CP850"
+       echo "id CP850"    # not CP437 ??
+       echo "id_ID CP850" # not CP437 ??
+       echo "is CP861"    # not CP850 ??
+       echo "is_IS CP861" # not CP850 ??
+       echo "it CP850"
+       echo "it_CH CP850"
+       echo "it_IT CP850"
+       echo "lt CP775"
+       echo "lt_LT CP775"
+       echo "lv CP775"
+       echo "lv_LV CP775"
+       echo "nb CP865"    # not CP850 ??
+       echo "nb_NO CP865" # not CP850 ??
+       echo "nl CP850"
+       echo "nl_BE CP850"
+       echo "nl_NL CP850"
+       echo "nn CP865"    # not CP850 ??
+       echo "nn_NO CP865" # not CP850 ??
+       echo "no CP865"    # not CP850 ??
+       echo "no_NO CP865" # not CP850 ??
+       echo "pt CP850"
+       echo "pt_BR CP850"
+       echo "pt_PT CP850"
+       echo "sv CP850"
+       echo "sv_SE CP850"
+       # ISO-8859-2 languages
+       echo "cs CP852"
+       echo "cs_CZ CP852"
+       echo "hr CP852"
+       echo "hr_HR CP852"
+       echo "hu CP852"
+       echo "hu_HU CP852"
+       echo "pl CP852"
+       echo "pl_PL CP852"
+       echo "ro CP852"
+       echo "ro_RO CP852"
+       echo "sk CP852"
+       echo "sk_SK CP852"
+       echo "sl CP852"
+       echo "sl_SI CP852"
+       echo "sq CP852"
+       echo "sq_AL CP852"
+       echo "sr CP852"    # CP852 or CP866 or CP855 ??
+       echo "sr_YU CP852" # CP852 or CP866 or CP855 ??
+       # ISO-8859-3 languages
+       echo "mt CP850"
+       echo "mt_MT CP850"
+       # ISO-8859-5 languages
+       echo "be CP866"
+       echo "be_BE CP866"
+       echo "bg CP866"    # not CP855 ??
+       echo "bg_BG CP866" # not CP855 ??
+       echo "mk CP866"    # not CP855 ??
+       echo "mk_MK CP866" # not CP855 ??
+       echo "ru KOI8-R"    # not CP866 ??
+       echo "ru_RU KOI8-R" # not CP866 ??
+       # ISO-8859-6 languages
+       echo "ar CP864"
+       echo "ar_AE CP864"
+       echo "ar_DZ CP864"
+       echo "ar_EG CP864"
+       echo "ar_IQ CP864"
+       echo "ar_IR CP864"
+       echo "ar_JO CP864"
+       echo "ar_KW CP864"
+       echo "ar_MA CP864"
+       echo "ar_OM CP864"
+       echo "ar_QA CP864"
+       echo "ar_SA CP864"
+       echo "ar_SY CP864"
+       # ISO-8859-7 languages
+       echo "el CP869"
+       echo "el_GR CP869"
+       # ISO-8859-8 languages
+       echo "he CP862"
+       echo "he_IL CP862"
+       # ISO-8859-9 languages
+       echo "tr CP857"
+       echo "tr_TR CP857"
+       # Japanese
+       echo "ja CP932"
+       echo "ja_JP CP932"
+       # Chinese
+       echo "zh_CN GBK"
+       echo "zh_TW CP950" # not CP938 ??
+       # Korean
+       echo "kr CP949"    # not CP934 ??
+       echo "kr_KR CP949" # not CP934 ??
+       # Thai
+       echo "th CP874"
+       echo "th_TH CP874"
+       # Other
+       echo "eo CP850"
+       echo "eo_EO CP850"
+       ;;
+esac
diff --git a/apps/silcer/intl/dcgettext.c b/apps/silcer/intl/dcgettext.c
new file mode 100644 (file)
index 0000000..469e78d
--- /dev/null
@@ -0,0 +1,57 @@
+/* Implementation of the dcgettext(3) function.
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define DCGETTEXT __dcgettext
+# define DCIGETTEXT __dcigettext
+#else
+# define DCGETTEXT dcgettext__
+# define DCIGETTEXT dcigettext__
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
+   locale.  */
+char *
+DCGETTEXT (domainname, msgid, category)
+     const char *domainname;
+     const char *msgid;
+     int category;
+{
+  return DCIGETTEXT (domainname, msgid, NULL, 0, 0, category);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__dcgettext, dcgettext);
+#endif
diff --git a/apps/silcer/intl/dcigettext.c b/apps/silcer/intl/dcigettext.c
new file mode 100644 (file)
index 0000000..b7627bf
--- /dev/null
@@ -0,0 +1,1258 @@
+/* Implementation of the internal dcigettext function.
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Tell glibc's <string.h> to provide a prototype for mempcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE   1
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+# define HAVE_ALLOCA 1
+#else
+# if defined HAVE_ALLOCA_H || defined _LIBC
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+#ifndef __set_errno
+# define __set_errno(val) errno = (val)
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <string.h>
+#if !HAVE_STRCHR && !defined _LIBC
+# ifndef strchr
+#  define strchr index
+# endif
+#endif
+
+#if defined HAVE_UNISTD_H || defined _LIBC
+# include <unistd.h>
+#endif
+
+#include <locale.h>
+
+#if defined HAVE_SYS_PARAM_H || defined _LIBC
+# include <sys/param.h>
+#endif
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+#include "hash-string.h"
+
+/* Thread safetyness.  */
+#ifdef _LIBC
+# include <bits/libc-lock.h>
+#else
+/* Provide dummy implementation if this is outside glibc.  */
+# define __libc_lock_define_initialized(CLASS, NAME)
+# define __libc_lock_lock(NAME)
+# define __libc_lock_unlock(NAME)
+# define __libc_rwlock_define_initialized(CLASS, NAME)
+# define __libc_rwlock_rdlock(NAME)
+# define __libc_rwlock_unlock(NAME)
+#endif
+
+/* Alignment of types.  */
+#if defined __GNUC__ && __GNUC__ >= 2
+# define alignof(TYPE) __alignof__ (TYPE)
+#else
+# define alignof(TYPE) \
+    ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
+#endif
+
+/* The internal variables in the standalone libintl.a must have different
+   names than the internal variables in GNU libc, otherwise programs
+   using libintl.a cannot be linked statically.  */
+#if !defined _LIBC
+# define _nl_default_default_domain _nl_default_default_domain__
+# define _nl_current_default_domain _nl_current_default_domain__
+# define _nl_default_dirname _nl_default_dirname__
+# define _nl_domain_bindings _nl_domain_bindings__
+#endif
+
+/* Some compilers, like SunOS4 cc, don't have offsetof in <stddef.h>.  */
+#ifndef offsetof
+# define offsetof(type,ident) ((size_t)&(((type*)0)->ident))
+#endif
+
+/* @@ end of prolog @@ */
+
+#ifdef _LIBC
+/* Rename the non ANSI C functions.  This is required by the standard
+   because some ANSI C functions will require linking with this object
+   file and the name space must not be polluted.  */
+# define getcwd __getcwd
+# ifndef stpcpy
+#  define stpcpy __stpcpy
+# endif
+# define tfind __tfind
+#else
+# if !defined HAVE_GETCWD
+char *getwd ();
+#  define getcwd(buf, max) getwd (buf)
+# else
+char *getcwd ();
+# endif
+# ifndef HAVE_STPCPY
+static char *stpcpy PARAMS ((char *dest, const char *src));
+# endif
+# ifndef HAVE_MEMPCPY
+static void *mempcpy PARAMS ((void *dest, const void *src, size_t n));
+# endif
+#endif
+
+/* Amount to increase buffer size by in each try.  */
+#define PATH_INCR 32
+
+/* The following is from pathmax.h.  */
+/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
+   PATH_MAX but might cause redefinition warnings when sys/param.h is
+   later included (as on MORE/BSD 4.3).  */
+#if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
+# include <limits.h>
+#endif
+
+#ifndef _POSIX_PATH_MAX
+# define _POSIX_PATH_MAX 255
+#endif
+
+#if !defined PATH_MAX && defined _PC_PATH_MAX
+# define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf ("/", _PC_PATH_MAX))
+#endif
+
+/* Don't include sys/param.h if it already has been.  */
+#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
+# include <sys/param.h>
+#endif
+
+#if !defined PATH_MAX && defined MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+/* Pathname support.
+   ISSLASH(C)           tests whether C is a directory separator character.
+   IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
+                        it may be concatenated to a directory pathname.
+   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
+ */
+#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
+  /* Win32, OS/2, DOS */
+# define ISSLASH(C) ((C) == '/' || (C) == '\\')
+# define HAS_DEVICE(P) \
+    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
+     && (P)[1] == ':')
+# define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
+# define IS_PATH_WITH_DIR(P) \
+    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
+#else
+  /* Unix */
+# define ISSLASH(C) ((C) == '/')
+# define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
+# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
+#endif
+
+/* XPG3 defines the result of `setlocale (category, NULL)' as:
+   ``Directs `setlocale()' to query `category' and return the current
+     setting of `local'.''
+   However it does not specify the exact format.  Neither do SUSV2 and
+   ISO C 99.  So we can use this feature only on selected systems (e.g.
+   those using GNU C Library).  */
+#if defined _LIBC || (defined __GNU_LIBRARY__ && __GNU_LIBRARY__ >= 2)
+# define HAVE_LOCALE_NULL
+#endif
+
+/* This is the type used for the search tree where known translations
+   are stored.  */
+struct known_translation_t
+{
+  /* Domain in which to search.  */
+  char *domainname;
+
+  /* The category.  */
+  int category;
+
+  /* State of the catalog counter at the point the string was found.  */
+  int counter;
+
+  /* Catalog where the string was found.  */
+  struct loaded_l10nfile *domain;
+
+  /* And finally the translation.  */
+  const char *translation;
+  size_t translation_length;
+
+  /* Pointer to the string in question.  */
+  char msgid[ZERO];
+};
+
+/* Root of the search tree with known translations.  We can use this
+   only if the system provides the `tsearch' function family.  */
+#if defined HAVE_TSEARCH || defined _LIBC
+# include <search.h>
+
+static void *root;
+
+# ifdef _LIBC
+#  define tsearch __tsearch
+# endif
+
+/* Function to compare two entries in the table of known translations.  */
+static int transcmp PARAMS ((const void *p1, const void *p2));
+static int
+transcmp (p1, p2)
+     const void *p1;
+     const void *p2;
+{
+  const struct known_translation_t *s1;
+  const struct known_translation_t *s2;
+  int result;
+
+  s1 = (const struct known_translation_t *) p1;
+  s2 = (const struct known_translation_t *) p2;
+
+  result = strcmp (s1->msgid, s2->msgid);
+  if (result == 0)
+    {
+      result = strcmp (s1->domainname, s2->domainname);
+      if (result == 0)
+       /* We compare the category last (though this is the cheapest
+          operation) since it is hopefully always the same (namely
+          LC_MESSAGES).  */
+       result = s1->category - s2->category;
+    }
+
+  return result;
+}
+#endif
+
+/* Name of the default domain used for gettext(3) prior any call to
+   textdomain(3).  The default value for this is "messages".  */
+const char _nl_default_default_domain[] = "messages";
+
+/* Value used as the default domain for gettext(3).  */
+const char *_nl_current_default_domain = _nl_default_default_domain;
+
+/* Contains the default location of the message catalogs.  */
+const char _nl_default_dirname[] = LOCALEDIR;
+
+/* List with bindings of specific domains created by bindtextdomain()
+   calls.  */
+struct binding *_nl_domain_bindings;
+
+/* Prototypes for local functions.  */
+static char *plural_lookup PARAMS ((struct loaded_l10nfile *domain,
+                                   unsigned long int n,
+                                   const char *translation,
+                                   size_t translation_len))
+     internal_function;
+static unsigned long int plural_eval PARAMS ((struct expression *pexp,
+                                             unsigned long int n))
+     internal_function;
+static const char *category_to_name PARAMS ((int category)) internal_function;
+static const char *guess_category_value PARAMS ((int category,
+                                                const char *categoryname))
+     internal_function;
+
+
+/* For those loosing systems which don't have `alloca' we have to add
+   some additional code emulating it.  */
+#ifdef HAVE_ALLOCA
+/* Nothing has to be done.  */
+# define ADD_BLOCK(list, address) /* nothing */
+# define FREE_BLOCKS(list) /* nothing */
+#else
+struct block_list
+{
+  void *address;
+  struct block_list *next;
+};
+# define ADD_BLOCK(list, addr)                                               \
+  do {                                                                       \
+    struct block_list *newp = (struct block_list *) malloc (sizeof (*newp));  \
+    /* If we cannot get a free block we cannot add the new element to        \
+       the list.  */                                                         \
+    if (newp != NULL) {                                                              \
+      newp->address = (addr);                                                \
+      newp->next = (list);                                                   \
+      (list) = newp;                                                         \
+    }                                                                        \
+  } while (0)
+# define FREE_BLOCKS(list)                                                   \
+  do {                                                                       \
+    while (list != NULL) {                                                   \
+      struct block_list *old = list;                                         \
+      list = list->next;                                                     \
+      free (old);                                                            \
+    }                                                                        \
+  } while (0)
+# undef alloca
+# define alloca(size) (malloc (size))
+#endif /* have alloca */
+
+
+#ifdef _LIBC
+/* List of blocks allocated for translations.  */
+typedef struct transmem_list
+{
+  struct transmem_list *next;
+  char data[ZERO];
+} transmem_block_t;
+static struct transmem_list *transmem_list;
+#else
+typedef unsigned char transmem_block_t;
+#endif
+
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define DCIGETTEXT __dcigettext
+#else
+# define DCIGETTEXT dcigettext__
+#endif
+
+/* Lock variable to protect the global data in the gettext implementation.  */
+#ifdef _LIBC
+__libc_rwlock_define_initialized (, _nl_state_lock)
+#endif
+
+/* Checking whether the binaries runs SUID must be done and glibc provides
+   easier methods therefore we make a difference here.  */
+#ifdef _LIBC
+# define ENABLE_SECURE __libc_enable_secure
+# define DETERMINE_SECURE
+#else
+# ifndef HAVE_GETUID
+#  define getuid() 0
+# endif
+# ifndef HAVE_GETGID
+#  define getgid() 0
+# endif
+# ifndef HAVE_GETEUID
+#  define geteuid() getuid()
+# endif
+# ifndef HAVE_GETEGID
+#  define getegid() getgid()
+# endif
+static int enable_secure;
+# define ENABLE_SECURE (enable_secure == 1)
+# define DETERMINE_SECURE \
+  if (enable_secure == 0)                                                    \
+    {                                                                        \
+      if (getuid () != geteuid () || getgid () != getegid ())                \
+       enable_secure = 1;                                                    \
+      else                                                                   \
+       enable_secure = -1;                                                   \
+    }
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current
+   CATEGORY locale and, if PLURAL is nonzero, search over string
+   depending on the plural form determined by N.  */
+char *
+DCIGETTEXT (domainname, msgid1, msgid2, plural, n, category)
+     const char *domainname;
+     const char *msgid1;
+     const char *msgid2;
+     int plural;
+     unsigned long int n;
+     int category;
+{
+#ifndef HAVE_ALLOCA
+  struct block_list *block_list = NULL;
+#endif
+  struct loaded_l10nfile *domain;
+  struct binding *binding;
+  const char *categoryname;
+  const char *categoryvalue;
+  char *dirname, *xdomainname;
+  char *single_locale;
+  char *retval;
+  size_t retlen;
+  int saved_errno;
+#if defined HAVE_TSEARCH || defined _LIBC
+  struct known_translation_t *search;
+  struct known_translation_t **foundp = NULL;
+  size_t msgid_len;
+#endif
+  size_t domainname_len;
+
+  /* If no real MSGID is given return NULL.  */
+  if (msgid1 == NULL)
+    return NULL;
+
+  __libc_rwlock_rdlock (_nl_state_lock);
+
+  /* If DOMAINNAME is NULL, we are interested in the default domain.  If
+     CATEGORY is not LC_MESSAGES this might not make much sense but the
+     definition left this undefined.  */
+  if (domainname == NULL)
+    domainname = _nl_current_default_domain;
+
+#if defined HAVE_TSEARCH || defined _LIBC
+  msgid_len = strlen (msgid1) + 1;
+
+  /* Try to find the translation among those which we found at
+     some time.  */
+  search = (struct known_translation_t *)
+          alloca (offsetof (struct known_translation_t, msgid) + msgid_len);
+  memcpy (search->msgid, msgid1, msgid_len);
+  search->domainname = (char *) domainname;
+  search->category = category;
+
+  foundp = (struct known_translation_t **) tfind (search, &root, transcmp);
+  if (foundp != NULL && (*foundp)->counter == _nl_msg_cat_cntr)
+    {
+      /* Now deal with plural.  */
+      if (plural)
+       retval = plural_lookup ((*foundp)->domain, n, (*foundp)->translation,
+                               (*foundp)->translation_length);
+      else
+       retval = (char *) (*foundp)->translation;
+
+      __libc_rwlock_unlock (_nl_state_lock);
+      return retval;
+    }
+#endif
+
+  /* Preserve the `errno' value.  */
+  saved_errno = errno;
+
+  /* See whether this is a SUID binary or not.  */
+  DETERMINE_SECURE;
+
+  /* First find matching binding.  */
+  for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
+    {
+      int compare = strcmp (domainname, binding->domainname);
+      if (compare == 0)
+       /* We found it!  */
+       break;
+      if (compare < 0)
+       {
+         /* It is not in the list.  */
+         binding = NULL;
+         break;
+       }
+    }
+
+  if (binding == NULL)
+    dirname = (char *) _nl_default_dirname;
+  else if (IS_ABSOLUTE_PATH (binding->dirname))
+    dirname = binding->dirname;
+  else
+    {
+      /* We have a relative path.  Make it absolute now.  */
+      size_t dirname_len = strlen (binding->dirname) + 1;
+      size_t path_max;
+      char *ret;
+
+      path_max = (unsigned int) PATH_MAX;
+      path_max += 2;           /* The getcwd docs say to do this.  */
+
+      for (;;)
+       {
+         dirname = (char *) alloca (path_max + dirname_len);
+         ADD_BLOCK (block_list, dirname);
+
+         __set_errno (0);
+         ret = getcwd (dirname, path_max);
+         if (ret != NULL || errno != ERANGE)
+           break;
+
+         path_max += path_max / 2;
+         path_max += PATH_INCR;
+       }
+
+      if (ret == NULL)
+       {
+         /* We cannot get the current working directory.  Don't signal an
+            error but simply return the default string.  */
+         FREE_BLOCKS (block_list);
+         __libc_rwlock_unlock (_nl_state_lock);
+         __set_errno (saved_errno);
+         return (plural == 0
+                 ? (char *) msgid1
+                 /* Use the Germanic plural rule.  */
+                 : n == 1 ? (char *) msgid1 : (char *) msgid2);
+       }
+
+      stpcpy (stpcpy (strchr (dirname, '\0'), "/"), binding->dirname);
+    }
+
+  /* Now determine the symbolic name of CATEGORY and its value.  */
+  categoryname = category_to_name (category);
+  categoryvalue = guess_category_value (category, categoryname);
+
+  domainname_len = strlen (domainname);
+  xdomainname = (char *) alloca (strlen (categoryname)
+                                + domainname_len + 5);
+  ADD_BLOCK (block_list, xdomainname);
+
+  stpcpy (mempcpy (stpcpy (stpcpy (xdomainname, categoryname), "/"),
+                 domainname, domainname_len),
+         ".mo");
+
+  /* Creating working area.  */
+  single_locale = (char *) alloca (strlen (categoryvalue) + 1);
+  ADD_BLOCK (block_list, single_locale);
+
+
+  /* Search for the given string.  This is a loop because we perhaps
+     got an ordered list of languages to consider for the translation.  */
+  while (1)
+    {
+      /* Make CATEGORYVALUE point to the next element of the list.  */
+      while (categoryvalue[0] != '\0' && categoryvalue[0] == ':')
+       ++categoryvalue;
+      if (categoryvalue[0] == '\0')
+       {
+         /* The whole contents of CATEGORYVALUE has been searched but
+            no valid entry has been found.  We solve this situation
+            by implicitly appending a "C" entry, i.e. no translation
+            will take place.  */
+         single_locale[0] = 'C';
+         single_locale[1] = '\0';
+       }
+      else
+       {
+         char *cp = single_locale;
+         while (categoryvalue[0] != '\0' && categoryvalue[0] != ':')
+           *cp++ = *categoryvalue++;
+         *cp = '\0';
+
+         /* When this is a SUID binary we must not allow accessing files
+            outside the dedicated directories.  */
+         if (ENABLE_SECURE && IS_PATH_WITH_DIR (single_locale))
+           /* Ingore this entry.  */
+           continue;
+       }
+
+      /* If the current locale value is C (or POSIX) we don't load a
+        domain.  Return the MSGID.  */
+      if (strcmp (single_locale, "C") == 0
+         || strcmp (single_locale, "POSIX") == 0)
+       {
+         FREE_BLOCKS (block_list);
+         __libc_rwlock_unlock (_nl_state_lock);
+         __set_errno (saved_errno);
+         return (plural == 0
+                 ? (char *) msgid1
+                 /* Use the Germanic plural rule.  */
+                 : n == 1 ? (char *) msgid1 : (char *) msgid2);
+       }
+
+
+      /* Find structure describing the message catalog matching the
+        DOMAINNAME and CATEGORY.  */
+      domain = _nl_find_domain (dirname, single_locale, xdomainname, binding);
+
+      if (domain != NULL)
+       {
+         retval = _nl_find_msg (domain, binding, msgid1, &retlen);
+
+         if (retval == NULL)
+           {
+             int cnt;
+
+             for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
+               {
+                 retval = _nl_find_msg (domain->successor[cnt], binding,
+                                        msgid1, &retlen);
+
+                 if (retval != NULL)
+                   {
+                     domain = domain->successor[cnt];
+                     break;
+                   }
+               }
+           }
+
+         if (retval != NULL)
+           {
+             /* Found the translation of MSGID1 in domain DOMAIN:
+                starting at RETVAL, RETLEN bytes.  */
+             FREE_BLOCKS (block_list);
+             __set_errno (saved_errno);
+#if defined HAVE_TSEARCH || defined _LIBC
+             if (foundp == NULL)
+               {
+                 /* Create a new entry and add it to the search tree.  */
+                 struct known_translation_t *newp;
+
+                 newp = (struct known_translation_t *)
+                   malloc (offsetof (struct known_translation_t, msgid)
+                           + msgid_len + domainname_len + 1);
+                 if (newp != NULL)
+                   {
+                     newp->domainname =
+                       mempcpy (newp->msgid, msgid1, msgid_len);
+                     memcpy (newp->domainname, domainname, domainname_len + 1);
+                     newp->category = category;
+                     newp->counter = _nl_msg_cat_cntr;
+                     newp->domain = domain;
+                     newp->translation = retval;
+                     newp->translation_length = retlen;
+
+                     /* Insert the entry in the search tree.  */
+                     foundp = (struct known_translation_t **)
+                       tsearch (newp, &root, transcmp);
+                     if (foundp == NULL
+                         || __builtin_expect (*foundp != newp, 0))
+                       /* The insert failed.  */
+                       free (newp);
+                   }
+               }
+             else
+               {
+                 /* We can update the existing entry.  */
+                 (*foundp)->counter = _nl_msg_cat_cntr;
+                 (*foundp)->domain = domain;
+                 (*foundp)->translation = retval;
+                 (*foundp)->translation_length = retlen;
+               }
+#endif
+             /* Now deal with plural.  */
+             if (plural)
+               retval = plural_lookup (domain, n, retval, retlen);
+
+             __libc_rwlock_unlock (_nl_state_lock);
+             return retval;
+           }
+       }
+    }
+  /* NOTREACHED */
+}
+
+
+char *
+internal_function
+_nl_find_msg (domain_file, domainbinding, msgid, lengthp)
+     struct loaded_l10nfile *domain_file;
+     struct binding *domainbinding;
+     const char *msgid;
+     size_t *lengthp;
+{
+  struct loaded_domain *domain;
+  size_t act;
+  char *result;
+  size_t resultlen;
+
+  if (domain_file->decided == 0)
+    _nl_load_domain (domain_file, domainbinding);
+
+  if (domain_file->data == NULL)
+    return NULL;
+
+  domain = (struct loaded_domain *) domain_file->data;
+
+  /* Locate the MSGID and its translation.  */
+  if (domain->hash_size > 2 && domain->hash_tab != NULL)
+    {
+      /* Use the hashing table.  */
+      nls_uint32 len = strlen (msgid);
+      nls_uint32 hash_val = hash_string (msgid);
+      nls_uint32 idx = hash_val % domain->hash_size;
+      nls_uint32 incr = 1 + (hash_val % (domain->hash_size - 2));
+
+      while (1)
+       {
+         nls_uint32 nstr = W (domain->must_swap, domain->hash_tab[idx]);
+
+         if (nstr == 0)
+           /* Hash table entry is empty.  */
+           return NULL;
+
+         /* Compare msgid with the original string at index nstr-1.
+            We compare the lengths with >=, not ==, because plural entries
+            are represented by strings with an embedded NUL.  */
+         if (W (domain->must_swap, domain->orig_tab[nstr - 1].length) >= len
+             && (strcmp (msgid,
+                         domain->data + W (domain->must_swap,
+                                           domain->orig_tab[nstr - 1].offset))
+                 == 0))
+           {
+             act = nstr - 1;
+             goto found;
+           }
+
+         if (idx >= domain->hash_size - incr)
+           idx -= domain->hash_size - incr;
+         else
+           idx += incr;
+       }
+      /* NOTREACHED */
+    }
+  else
+    {
+      /* Try the default method:  binary search in the sorted array of
+        messages.  */
+      size_t top, bottom;
+
+      bottom = 0;
+      top = domain->nstrings;
+      while (bottom < top)
+       {
+         int cmp_val;
+
+         act = (bottom + top) / 2;
+         cmp_val = strcmp (msgid, (domain->data
+                                   + W (domain->must_swap,
+                                        domain->orig_tab[act].offset)));
+         if (cmp_val < 0)
+           top = act;
+         else if (cmp_val > 0)
+           bottom = act + 1;
+         else
+           goto found;
+       }
+      /* No translation was found.  */
+      return NULL;
+    }
+
+ found:
+  /* The translation was found at index ACT.  If we have to convert the
+     string to use a different character set, this is the time.  */
+  result = ((char *) domain->data
+           + W (domain->must_swap, domain->trans_tab[act].offset));
+  resultlen = W (domain->must_swap, domain->trans_tab[act].length) + 1;
+
+#if defined _LIBC || HAVE_ICONV
+  if (domain->codeset_cntr
+      != (domainbinding != NULL ? domainbinding->codeset_cntr : 0))
+    {
+      /* The domain's codeset has changed through bind_textdomain_codeset()
+        since the message catalog was initialized or last accessed.  We
+        have to reinitialize the converter.  */
+      _nl_free_domain_conv (domain);
+      _nl_init_domain_conv (domain_file, domain, domainbinding);
+    }
+
+  if (
+# ifdef _LIBC
+      domain->conv != (__gconv_t) -1
+# else
+#  if HAVE_ICONV
+      domain->conv != (iconv_t) -1
+#  endif
+# endif
+      )
+    {
+      /* We are supposed to do a conversion.  First allocate an
+        appropriate table with the same structure as the table
+        of translations in the file, where we can put the pointers
+        to the converted strings in.
+        There is a slight complication with plural entries.  They
+        are represented by consecutive NUL terminated strings.  We
+        handle this case by converting RESULTLEN bytes, including
+        NULs.  */
+
+      if (domain->conv_tab == NULL
+         && ((domain->conv_tab = (char **) calloc (domain->nstrings,
+                                                   sizeof (char *)))
+             == NULL))
+       /* Mark that we didn't succeed allocating a table.  */
+       domain->conv_tab = (char **) -1;
+
+      if (__builtin_expect (domain->conv_tab == (char **) -1, 0))
+       /* Nothing we can do, no more memory.  */
+       goto converted;
+
+      if (domain->conv_tab[act] == NULL)
+       {
+         /* We haven't used this string so far, so it is not
+            translated yet.  Do this now.  */
+         /* We use a bit more efficient memory handling.
+            We allocate always larger blocks which get used over
+            time.  This is faster than many small allocations.   */
+         __libc_lock_define_initialized (static, lock)
+# define INITIAL_BLOCK_SIZE    4080
+         static unsigned char *freemem;
+         static size_t freemem_size;
+
+         const unsigned char *inbuf;
+         unsigned char *outbuf;
+         int malloc_count;
+# ifndef _LIBC
+         transmem_block_t *transmem_list = NULL;
+# endif
+
+         __libc_lock_lock (lock);
+
+         inbuf = (const unsigned char *) result;
+         outbuf = freemem + sizeof (size_t);
+
+         malloc_count = 0;
+         while (1)
+           {
+             transmem_block_t *newmem;
+# ifdef _LIBC
+             size_t non_reversible;
+             int res;
+
+             if (freemem_size < sizeof (size_t))
+               goto resize_freemem;
+
+             res = __gconv (domain->conv,
+                            &inbuf, inbuf + resultlen,
+                            &outbuf,
+                            outbuf + freemem_size - sizeof (size_t),
+                            &non_reversible);
+
+             if (res == __GCONV_OK || res == __GCONV_EMPTY_INPUT)
+               break;
+
+             if (res != __GCONV_FULL_OUTPUT)
+               {
+                 __libc_lock_unlock (lock);
+                 goto converted;
+               }
+
+             inbuf = result;
+# else
+#  if HAVE_ICONV
+             const char *inptr = (const char *) inbuf;
+             size_t inleft = resultlen;
+             char *outptr = (char *) outbuf;
+             size_t outleft;
+
+             if (freemem_size < sizeof (size_t))
+               goto resize_freemem;
+
+             outleft = freemem_size - sizeof (size_t);
+             if (iconv (domain->conv,
+                        (ICONV_CONST char **) &inptr, &inleft,
+                        &outptr, &outleft)
+                 != (size_t) (-1))
+               {
+                 outbuf = (unsigned char *) outptr;
+                 break;
+               }
+             if (errno != E2BIG)
+               {
+                 __libc_lock_unlock (lock);
+                 goto converted;
+               }
+#  endif
+# endif
+
+           resize_freemem:
+             /* We must allocate a new buffer or resize the old one.  */
+             if (malloc_count > 0)
+               {
+                 ++malloc_count;
+                 freemem_size = malloc_count * INITIAL_BLOCK_SIZE;
+                 newmem = (transmem_block_t *) realloc (transmem_list,
+                                                        freemem_size);
+# ifdef _LIBC
+                 if (newmem != NULL)
+                   transmem_list = transmem_list->next;
+                 else
+                   {
+                     struct transmem_list *old = transmem_list;
+
+                     transmem_list = transmem_list->next;
+                     free (old);
+                   }
+# endif
+               }
+             else
+               {
+                 malloc_count = 1;
+                 freemem_size = INITIAL_BLOCK_SIZE;
+                 newmem = (transmem_block_t *) malloc (freemem_size);
+               }
+             if (__builtin_expect (newmem == NULL, 0))
+               {
+                 freemem = NULL;
+                 freemem_size = 0;
+                 __libc_lock_unlock (lock);
+                 goto converted;
+               }
+
+# ifdef _LIBC
+             /* Add the block to the list of blocks we have to free
+                 at some point.  */
+             newmem->next = transmem_list;
+             transmem_list = newmem;
+
+             freemem = newmem->data;
+             freemem_size -= offsetof (struct transmem_list, data);
+# else
+             transmem_list = newmem;
+             freemem = newmem;
+# endif
+
+             outbuf = freemem + sizeof (size_t);
+           }
+
+         /* We have now in our buffer a converted string.  Put this
+            into the table of conversions.  */
+         *(size_t *) freemem = outbuf - freemem - sizeof (size_t);
+         domain->conv_tab[act] = (char *) freemem;
+         /* Shrink freemem, but keep it aligned.  */
+         freemem_size -= outbuf - freemem;
+         freemem = outbuf;
+         freemem += freemem_size & (alignof (size_t) - 1);
+         freemem_size = freemem_size & ~ (alignof (size_t) - 1);
+
+         __libc_lock_unlock (lock);
+       }
+
+      /* Now domain->conv_tab[act] contains the translation of all
+        the plural variants.  */
+      result = domain->conv_tab[act] + sizeof (size_t);
+      resultlen = *(size_t *) domain->conv_tab[act];
+    }
+
+ converted:
+  /* The result string is converted.  */
+
+#endif /* _LIBC || HAVE_ICONV */
+
+  *lengthp = resultlen;
+  return result;
+}
+
+
+/* Look up a plural variant.  */
+static char *
+internal_function
+plural_lookup (domain, n, translation, translation_len)
+     struct loaded_l10nfile *domain;
+     unsigned long int n;
+     const char *translation;
+     size_t translation_len;
+{
+  struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
+  unsigned long int index;
+  const char *p;
+
+  index = plural_eval (domaindata->plural, n);
+  if (index >= domaindata->nplurals)
+    /* This should never happen.  It means the plural expression and the
+       given maximum value do not match.  */
+    index = 0;
+
+  /* Skip INDEX strings at TRANSLATION.  */
+  p = translation;
+  while (index-- > 0)
+    {
+#ifdef _LIBC
+      p = __rawmemchr (p, '\0');
+#else
+      p = strchr (p, '\0');
+#endif
+      /* And skip over the NUL byte.  */
+      p++;
+
+      if (p >= translation + translation_len)
+       /* This should never happen.  It means the plural expression
+          evaluated to a value larger than the number of variants
+          available for MSGID1.  */
+       return (char *) translation;
+    }
+  return (char *) p;
+}
+
+
+/* Function to evaluate the plural expression and return an index value.  */
+static unsigned long int
+internal_function
+plural_eval (pexp, n)
+     struct expression *pexp;
+     unsigned long int n;
+{
+  switch (pexp->nargs)
+    {
+    case 0:
+      switch (pexp->operation)
+       {
+       case var:
+         return n;
+       case num:
+         return pexp->val.num;
+       default:
+         break;
+       }
+      /* NOTREACHED */
+      break;
+    case 1:
+      {
+       /* pexp->operation must be lnot.  */
+       unsigned long int arg = plural_eval (pexp->val.args[0], n);
+       return ! arg;
+      }
+    case 2:
+      {
+       unsigned long int leftarg = plural_eval (pexp->val.args[0], n);
+       if (pexp->operation == lor)
+         return leftarg || plural_eval (pexp->val.args[1], n);
+       else if (pexp->operation == land)
+         return leftarg && plural_eval (pexp->val.args[1], n);
+       else
+         {
+           unsigned long int rightarg = plural_eval (pexp->val.args[1], n);
+
+           switch (pexp->operation)
+             {
+             case mult:
+               return leftarg * rightarg;
+             case divide:
+               return leftarg / rightarg;
+             case module:
+               return leftarg % rightarg;
+             case plus:
+               return leftarg + rightarg;
+             case minus:
+               return leftarg - rightarg;
+             case less_than:
+               return leftarg < rightarg;
+             case greater_than:
+               return leftarg > rightarg;
+             case less_or_equal:
+               return leftarg <= rightarg;
+             case greater_or_equal:
+               return leftarg >= rightarg;
+             case equal:
+               return leftarg == rightarg;
+             case not_equal:
+               return leftarg != rightarg;
+             default:
+               break;
+             }
+         }
+       /* NOTREACHED */
+       break;
+      }
+    case 3:
+      {
+       /* pexp->operation must be qmop.  */
+       unsigned long int boolarg = plural_eval (pexp->val.args[0], n);
+       return plural_eval (pexp->val.args[boolarg ? 1 : 2], n);
+      }
+    }
+  /* NOTREACHED */
+  return 0;
+}
+
+
+/* Return string representation of locale CATEGORY.  */
+static const char *
+internal_function
+category_to_name (category)
+     int category;
+{
+  const char *retval;
+
+  switch (category)
+  {
+#ifdef LC_COLLATE
+  case LC_COLLATE:
+    retval = "LC_COLLATE";
+    break;
+#endif
+#ifdef LC_CTYPE
+  case LC_CTYPE:
+    retval = "LC_CTYPE";
+    break;
+#endif
+#ifdef LC_MONETARY
+  case LC_MONETARY:
+    retval = "LC_MONETARY";
+    break;
+#endif
+#ifdef LC_NUMERIC
+  case LC_NUMERIC:
+    retval = "LC_NUMERIC";
+    break;
+#endif
+#ifdef LC_TIME
+  case LC_TIME:
+    retval = "LC_TIME";
+    break;
+#endif
+#ifdef LC_MESSAGES
+  case LC_MESSAGES:
+    retval = "LC_MESSAGES";
+    break;
+#endif
+#ifdef LC_RESPONSE
+  case LC_RESPONSE:
+    retval = "LC_RESPONSE";
+    break;
+#endif
+#ifdef LC_ALL
+  case LC_ALL:
+    /* This might not make sense but is perhaps better than any other
+       value.  */
+    retval = "LC_ALL";
+    break;
+#endif
+  default:
+    /* If you have a better idea for a default value let me know.  */
+    retval = "LC_XXX";
+  }
+
+  return retval;
+}
+
+/* Guess value of current locale from value of the environment variables.  */
+static const char *
+internal_function
+guess_category_value (category, categoryname)
+     int category;
+     const char *categoryname;
+{
+  const char *language;
+  const char *retval;
+
+  /* The highest priority value is the `LANGUAGE' environment
+     variable.  But we don't use the value if the currently selected
+     locale is the C locale.  This is a GNU extension.  */
+  language = getenv ("LANGUAGE");
+  if (language != NULL && language[0] == '\0')
+    language = NULL;
+
+  /* We have to proceed with the POSIX methods of looking to `LC_ALL',
+     `LC_xxx', and `LANG'.  On some systems this can be done by the
+     `setlocale' function itself.  */
+#if defined _LIBC || (defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL)
+  retval = setlocale (category, NULL);
+#else
+  /* Setting of LC_ALL overwrites all other.  */
+  retval = getenv ("LC_ALL");
+  if (retval == NULL || retval[0] == '\0')
+    {
+      /* Next comes the name of the desired category.  */
+      retval = getenv (categoryname);
+      if (retval == NULL || retval[0] == '\0')
+       {
+         /* Last possibility is the LANG environment variable.  */
+         retval = getenv ("LANG");
+         if (retval == NULL || retval[0] == '\0')
+           /* We use C as the default domain.  POSIX says this is
+              implementation defined.  */
+           return "C";
+       }
+    }
+#endif
+
+  return language != NULL && strcmp (retval, "C") != 0 ? language : retval;
+}
+
+/* @@ begin of epilog @@ */
+
+/* We don't want libintl.a to depend on any other library.  So we
+   avoid the non-standard function stpcpy.  In GNU C Library this
+   function is available, though.  Also allow the symbol HAVE_STPCPY
+   to be defined.  */
+#if !_LIBC && !HAVE_STPCPY
+static char *
+stpcpy (dest, src)
+     char *dest;
+     const char *src;
+{
+  while ((*dest++ = *src++) != '\0')
+    /* Do nothing. */ ;
+  return dest - 1;
+}
+#endif
+
+#if !_LIBC && !HAVE_MEMPCPY
+static void *
+mempcpy (dest, src, n)
+     void *dest;
+     const void *src;
+     size_t n;
+{
+  return (void *) ((char *) memcpy (dest, src, n) + n);
+}
+#endif
+
+
+#ifdef _LIBC
+/* If we want to free all resources we have to do some work at
+   program's end.  */
+static void __attribute__ ((unused))
+free_mem (void)
+{
+  void *old;
+
+  while (_nl_domain_bindings != NULL)
+    {
+      struct binding *oldp = _nl_domain_bindings;
+      _nl_domain_bindings = _nl_domain_bindings->next;
+      if (oldp->dirname != _nl_default_dirname)
+       /* Yes, this is a pointer comparison.  */
+       free (oldp->dirname);
+      free (oldp->codeset);
+      free (oldp);
+    }
+
+  if (_nl_current_default_domain != _nl_default_default_domain)
+    /* Yes, again a pointer comparison.  */
+    free ((char *) _nl_current_default_domain);
+
+  /* Remove the search tree with the known translations.  */
+  __tdestroy (root, free);
+  root = NULL;
+
+  while (transmem_list != NULL)
+    {
+      old = transmem_list;
+      transmem_list = transmem_list->next;
+      free (old);
+    }
+}
+
+text_set_element (__libc_subfreeres, free_mem);
+#endif
diff --git a/apps/silcer/intl/dcngettext.c b/apps/silcer/intl/dcngettext.c
new file mode 100644 (file)
index 0000000..e5da257
--- /dev/null
@@ -0,0 +1,59 @@
+/* Implementation of the dcngettext(3) function.
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define DCNGETTEXT __dcngettext
+# define DCIGETTEXT __dcigettext
+#else
+# define DCNGETTEXT dcngettext__
+# define DCIGETTEXT dcigettext__
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
+   locale.  */
+char *
+DCNGETTEXT (domainname, msgid1, msgid2, n, category)
+     const char *domainname;
+     const char *msgid1;
+     const char *msgid2;
+     unsigned long int n;
+     int category;
+{
+  return DCIGETTEXT (domainname, msgid1, msgid2, 1, n, category);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__dcngettext, dcngettext);
+#endif
diff --git a/apps/silcer/intl/dgettext.c b/apps/silcer/intl/dgettext.c
new file mode 100644 (file)
index 0000000..c513041
--- /dev/null
@@ -0,0 +1,58 @@
+/* Implementation of the dgettext(3) function.
+   Copyright (C) 1995-1997, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <locale.h>
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define DGETTEXT __dgettext
+# define DCGETTEXT __dcgettext
+#else
+# define DGETTEXT dgettext__
+# define DCGETTEXT dcgettext__
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog of the current
+   LC_MESSAGES locale.  */
+char *
+DGETTEXT (domainname, msgid)
+     const char *domainname;
+     const char *msgid;
+{
+  return DCGETTEXT (domainname, msgid, LC_MESSAGES);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__dgettext, dgettext);
+#endif
diff --git a/apps/silcer/intl/dngettext.c b/apps/silcer/intl/dngettext.c
new file mode 100644 (file)
index 0000000..79aaa9a
--- /dev/null
@@ -0,0 +1,60 @@
+/* Implementation of the dngettext(3) function.
+   Copyright (C) 1995-1997, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <locale.h>
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define DNGETTEXT __dngettext
+# define DCNGETTEXT __dcngettext
+#else
+# define DNGETTEXT dngettext__
+# define DCNGETTEXT dcngettext__
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog of the current
+   LC_MESSAGES locale and skip message according to the plural form.  */
+char *
+DNGETTEXT (domainname, msgid1, msgid2, n)
+     const char *domainname;
+     const char *msgid1;
+     const char *msgid2;
+     unsigned long int n;
+{
+  return DCNGETTEXT (domainname, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__dngettext, dngettext);
+#endif
diff --git a/apps/silcer/intl/explodename.c b/apps/silcer/intl/explodename.c
new file mode 100644 (file)
index 0000000..c4ddcc4
--- /dev/null
@@ -0,0 +1,191 @@
+/* Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
+   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "loadinfo.h"
+
+/* On some strange systems still no definition of NULL is found.  Sigh!  */
+#ifndef NULL
+# if defined __STDC__ && __STDC__
+#  define NULL ((void *) 0)
+# else
+#  define NULL 0
+# endif
+#endif
+
+/* @@ end of prolog @@ */
+
+char *
+_nl_find_language (name)
+     const char *name;
+{
+  while (name[0] != '\0' && name[0] != '_' && name[0] != '@'
+        && name[0] != '+' && name[0] != ',')
+    ++name;
+
+  return (char *) name;
+}
+
+
+int
+_nl_explode_name (name, language, modifier, territory, codeset,
+                 normalized_codeset, special, sponsor, revision)
+     char *name;
+     const char **language;
+     const char **modifier;
+     const char **territory;
+     const char **codeset;
+     const char **normalized_codeset;
+     const char **special;
+     const char **sponsor;
+     const char **revision;
+{
+  enum { undecided, xpg, cen } syntax;
+  char *cp;
+  int mask;
+
+  *modifier = NULL;
+  *territory = NULL;
+  *codeset = NULL;
+  *normalized_codeset = NULL;
+  *special = NULL;
+  *sponsor = NULL;
+  *revision = NULL;
+
+  /* Now we determine the single parts of the locale name.  First
+     look for the language.  Termination symbols are `_' and `@' if
+     we use XPG4 style, and `_', `+', and `,' if we use CEN syntax.  */
+  mask = 0;
+  syntax = undecided;
+  *language = cp = name;
+  cp = _nl_find_language (*language);
+
+  if (*language == cp)
+    /* This does not make sense: language has to be specified.  Use
+       this entry as it is without exploding.  Perhaps it is an alias.  */
+    cp = strchr (*language, '\0');
+  else if (cp[0] == '_')
+    {
+      /* Next is the territory.  */
+      cp[0] = '\0';
+      *territory = ++cp;
+
+      while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@'
+            && cp[0] != '+' && cp[0] != ',' && cp[0] != '_')
+       ++cp;
+
+      mask |= TERRITORY;
+
+      if (cp[0] == '.')
+       {
+         /* Next is the codeset.  */
+         syntax = xpg;
+         cp[0] = '\0';
+         *codeset = ++cp;
+
+         while (cp[0] != '\0' && cp[0] != '@')
+           ++cp;
+
+         mask |= XPG_CODESET;
+
+         if (*codeset != cp && (*codeset)[0] != '\0')
+           {
+             *normalized_codeset = _nl_normalize_codeset (*codeset,
+                                                          cp - *codeset);
+             if (strcmp (*codeset, *normalized_codeset) == 0)
+               free ((char *) *normalized_codeset);
+             else
+               mask |= XPG_NORM_CODESET;
+           }
+       }
+    }
+
+  if (cp[0] == '@' || (syntax != xpg && cp[0] == '+'))
+    {
+      /* Next is the modifier.  */
+      syntax = cp[0] == '@' ? xpg : cen;
+      cp[0] = '\0';
+      *modifier = ++cp;
+
+      while (syntax == cen && cp[0] != '\0' && cp[0] != '+'
+            && cp[0] != ',' && cp[0] != '_')
+       ++cp;
+
+      mask |= XPG_MODIFIER | CEN_AUDIENCE;
+    }
+
+  if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_'))
+    {
+      syntax = cen;
+
+      if (cp[0] == '+')
+       {
+         /* Next is special application (CEN syntax).  */
+         cp[0] = '\0';
+         *special = ++cp;
+
+         while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_')
+           ++cp;
+
+         mask |= CEN_SPECIAL;
+       }
+
+      if (cp[0] == ',')
+       {
+         /* Next is sponsor (CEN syntax).  */
+         cp[0] = '\0';
+         *sponsor = ++cp;
+
+         while (cp[0] != '\0' && cp[0] != '_')
+           ++cp;
+
+         mask |= CEN_SPONSOR;
+       }
+
+      if (cp[0] == '_')
+       {
+         /* Next is revision (CEN syntax).  */
+         cp[0] = '\0';
+         *revision = ++cp;
+
+         mask |= CEN_REVISION;
+       }
+    }
+
+  /* For CEN syntax values it might be important to have the
+     separator character in the file name, not for XPG syntax.  */
+  if (syntax == xpg)
+    {
+      if (*territory != NULL && (*territory)[0] == '\0')
+       mask &= ~TERRITORY;
+
+      if (*codeset != NULL && (*codeset)[0] == '\0')
+       mask &= ~XPG_CODESET;
+
+      if (*modifier != NULL && (*modifier)[0] == '\0')
+       mask &= ~XPG_MODIFIER;
+    }
+
+  return mask;
+}
diff --git a/apps/silcer/intl/finddomain.c b/apps/silcer/intl/finddomain.c
new file mode 100644 (file)
index 0000000..4882554
--- /dev/null
@@ -0,0 +1,197 @@
+/* Handle list of needed message catalogs
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+   Written by Ulrich Drepper <drepper@gnu.org>, 1995.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_UNISTD_H || defined _LIBC
+# include <unistd.h>
+#endif
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+/* @@ end of prolog @@ */
+/* List of already loaded domains.  */
+static struct loaded_l10nfile *_nl_loaded_domains;
+
+
+/* Return a data structure describing the message catalog described by
+   the DOMAINNAME and CATEGORY parameters with respect to the currently
+   established bindings.  */
+struct loaded_l10nfile *
+internal_function
+_nl_find_domain (dirname, locale, domainname, domainbinding)
+     const char *dirname;
+     char *locale;
+     const char *domainname;
+     struct binding *domainbinding;
+{
+  struct loaded_l10nfile *retval;
+  const char *language;
+  const char *modifier;
+  const char *territory;
+  const char *codeset;
+  const char *normalized_codeset;
+  const char *special;
+  const char *sponsor;
+  const char *revision;
+  const char *alias_value;
+  int mask;
+
+  /* LOCALE can consist of up to four recognized parts for the XPG syntax:
+
+               language[_territory[.codeset]][@modifier]
+
+     and six parts for the CEN syntax:
+
+       language[_territory][+audience][+special][,[sponsor][_revision]]
+
+     Beside the first part all of them are allowed to be missing.  If
+     the full specified locale is not found, the less specific one are
+     looked for.  The various parts will be stripped off according to
+     the following order:
+               (1) revision
+               (2) sponsor
+               (3) special
+               (4) codeset
+               (5) normalized codeset
+               (6) territory
+               (7) audience/modifier
+   */
+
+  /* If we have already tested for this locale entry there has to
+     be one data set in the list of loaded domains.  */
+  retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
+                              strlen (dirname) + 1, 0, locale, NULL, NULL,
+                              NULL, NULL, NULL, NULL, NULL, domainname, 0);
+  if (retval != NULL)
+    {
+      /* We know something about this locale.  */
+      int cnt;
+
+      if (retval->decided == 0)
+       _nl_load_domain (retval, domainbinding);
+
+      if (retval->data != NULL)
+       return retval;
+
+      for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
+       {
+         if (retval->successor[cnt]->decided == 0)
+           _nl_load_domain (retval->successor[cnt], domainbinding);
+
+         if (retval->successor[cnt]->data != NULL)
+           break;
+       }
+      return cnt >= 0 ? retval : NULL;
+      /* NOTREACHED */
+    }
+
+  /* See whether the locale value is an alias.  If yes its value
+     *overwrites* the alias name.  No test for the original value is
+     done.  */
+  alias_value = _nl_expand_alias (locale);
+  if (alias_value != NULL)
+    {
+#if defined _LIBC || defined HAVE_STRDUP
+      locale = strdup (alias_value);
+      if (locale == NULL)
+       return NULL;
+#else
+      size_t len = strlen (alias_value) + 1;
+      locale = (char *) malloc (len);
+      if (locale == NULL)
+       return NULL;
+
+      memcpy (locale, alias_value, len);
+#endif
+    }
+
+  /* Now we determine the single parts of the locale name.  First
+     look for the language.  Termination symbols are `_' and `@' if
+     we use XPG4 style, and `_', `+', and `,' if we use CEN syntax.  */
+  mask = _nl_explode_name (locale, &language, &modifier, &territory,
+                          &codeset, &normalized_codeset, &special,
+                          &sponsor, &revision);
+
+  /* Create all possible locale entries which might be interested in
+     generalization.  */
+  retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
+                              strlen (dirname) + 1, mask, language, territory,
+                              codeset, normalized_codeset, modifier, special,
+                              sponsor, revision, domainname, 1);
+  if (retval == NULL)
+    /* This means we are out of core.  */
+    return NULL;
+
+  if (retval->decided == 0)
+    _nl_load_domain (retval, domainbinding);
+  if (retval->data == NULL)
+    {
+      int cnt;
+      for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
+       {
+         if (retval->successor[cnt]->decided == 0)
+           _nl_load_domain (retval->successor[cnt], domainbinding);
+         if (retval->successor[cnt]->data != NULL)
+           break;
+       }
+    }
+
+  /* The room for an alias was dynamically allocated.  Free it now.  */
+  if (alias_value != NULL)
+    free (locale);
+
+  /* The space for normalized_codeset is dynamically allocated.  Free it.  */
+  if (mask & XPG_NORM_CODESET)
+    free ((void *) normalized_codeset);
+
+  return retval;
+}
+
+
+#ifdef _LIBC
+static void __attribute__ ((unused))
+free_mem (void)
+{
+  struct loaded_l10nfile *runp = _nl_loaded_domains;
+
+  while (runp != NULL)
+    {
+      struct loaded_l10nfile *here = runp;
+      if (runp->data != NULL)
+       _nl_unload_domain ((struct loaded_domain *) runp->data);
+      runp = runp->next;
+      free ((char *) here->filename);
+      free (here);
+    }
+}
+
+text_set_element (__libc_subfreeres, free_mem);
+#endif
diff --git a/apps/silcer/intl/gettext.c b/apps/silcer/intl/gettext.c
new file mode 100644 (file)
index 0000000..a640205
--- /dev/null
@@ -0,0 +1,63 @@
+/* Implementation of gettext(3) function.
+   Copyright (C) 1995, 1997, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef _LIBC
+# define __need_NULL
+# include <stddef.h>
+#else
+# include <stdlib.h>           /* Just for NULL.  */
+#endif
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define GETTEXT __gettext
+# define DCGETTEXT __dcgettext
+#else
+# define GETTEXT gettext__
+# define DCGETTEXT dcgettext__
+#endif
+
+/* Look up MSGID in the current default message catalog for the current
+   LC_MESSAGES locale.  If not found, returns MSGID itself (the default
+   text).  */
+char *
+GETTEXT (msgid)
+     const char *msgid;
+{
+  return DCGETTEXT (NULL, msgid, LC_MESSAGES);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__gettext, gettext);
+#endif
diff --git a/apps/silcer/intl/gettext.h b/apps/silcer/intl/gettext.h
new file mode 100644 (file)
index 0000000..eb58890
--- /dev/null
@@ -0,0 +1,101 @@
+/* Description of GNU message catalog format: general file layout.
+   Copyright (C) 1995, 1997, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETTEXT_H
+#define _GETTEXT_H 1
+
+#if HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* @@ end of prolog @@ */
+
+/* The magic number of the GNU message catalog format.  */
+#define _MAGIC 0x950412de
+#define _MAGIC_SWAPPED 0xde120495
+
+/* Revision number of the currently used .mo (binary) file format.  */
+#define MO_REVISION_NUMBER 0
+
+/* The following contortions are an attempt to use the C preprocessor
+   to determine an unsigned integral type that is 32 bits wide.  An
+   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+   as of version autoconf-2.13, the AC_CHECK_SIZEOF macro doesn't work
+   when cross-compiling.  */
+
+#if __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+#else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+#endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+   This should be valid for all systems GNU cares about because
+   that doesn't include 16-bit systems, and only modern systems
+   (that certainly have <limits.h>) have 64+-bit integral types.  */
+
+#ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+#endif
+
+#if UINT_MAX == UINT_MAX_32_BITS
+typedef unsigned nls_uint32;
+#else
+# if USHRT_MAX == UINT_MAX_32_BITS
+typedef unsigned short nls_uint32;
+# else
+#  if ULONG_MAX == UINT_MAX_32_BITS
+typedef unsigned long nls_uint32;
+#  else
+  /* The following line is intended to throw an error.  Using #error is
+     not portable enough.  */
+  "Cannot determine unsigned 32-bit data type."
+#  endif
+# endif
+#endif
+
+
+/* Header for binary .mo file format.  */
+struct mo_file_header
+{
+  /* The magic number.  */
+  nls_uint32 magic;
+  /* The revision number of the file format.  */
+  nls_uint32 revision;
+  /* The number of strings pairs.  */
+  nls_uint32 nstrings;
+  /* Offset of table with start offsets of original strings.  */
+  nls_uint32 orig_tab_offset;
+  /* Offset of table with start offsets of translation strings.  */
+  nls_uint32 trans_tab_offset;
+  /* Size of hashing table.  */
+  nls_uint32 hash_tab_size;
+  /* Offset of first hashing entry.  */
+  nls_uint32 hash_tab_offset;
+};
+
+struct string_desc
+{
+  /* Length of addressed string.  */
+  nls_uint32 length;
+  /* Offset of string in file.  */
+  nls_uint32 offset;
+};
+
+/* @@ begin of epilog @@ */
+
+#endif /* gettext.h  */
diff --git a/apps/silcer/intl/gettextP.h b/apps/silcer/intl/gettextP.h
new file mode 100644 (file)
index 0000000..ee8ca48
--- /dev/null
@@ -0,0 +1,251 @@
+/* Header describing internals of libintl library.
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+   Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _GETTEXTP_H
+#define _GETTEXTP_H
+
+#include <stddef.h>            /* Get size_t.  */
+
+#ifdef _LIBC
+# include "../iconv/gconv_int.h"
+#else
+# if HAVE_ICONV
+#  include <iconv.h>
+# endif
+#endif
+
+#include "loadinfo.h"
+
+#include "gettext.h"           /* Get nls_uint32.  */
+
+/* @@ end of prolog @@ */
+
+#ifndef PARAMS
+# if __STDC__
+#  define PARAMS(args) args
+# else
+#  define PARAMS(args) ()
+# endif
+#endif
+
+#ifndef internal_function
+# define internal_function
+#endif
+
+/* Tell the compiler when a conditional or integer expression is
+   almost always true or almost always false.  */
+#ifndef HAVE_BUILTIN_EXPECT
+# define __builtin_expect(expr, val) (expr)
+#endif
+
+#ifndef W
+# define W(flag, data) ((flag) ? SWAP (data) : (data))
+#endif
+
+
+#ifdef _LIBC
+# include <byteswap.h>
+# define SWAP(i) bswap_32 (i)
+#else
+static inline nls_uint32
+SWAP (i)
+     nls_uint32 i;
+{
+  return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
+}
+#endif
+
+
+/* This is the representation of the expressions to determine the
+   plural form.  */
+struct expression
+{
+  int nargs;                   /* Number of arguments.  */
+  enum operator
+  {
+    /* Without arguments:  */
+    var,                       /* The variable "n".  */
+    num,                       /* Decimal number.  */
+    /* Unary operators:  */
+    lnot,                      /* Logical NOT.  */
+    /* Binary operators:  */
+    mult,                      /* Multiplication.  */
+    divide,                    /* Division.  */
+    module,                    /* Module operation.  */
+    plus,                      /* Addition.  */
+    minus,                     /* Subtraction.  */
+    less_than,                 /* Comparison.  */
+    greater_than,              /* Comparison.  */
+    less_or_equal,             /* Comparison.  */
+    greater_or_equal,          /* Comparison.  */
+    equal,                     /* Comparision for equality.  */
+    not_equal,                 /* Comparision for inequality.  */
+    land,                      /* Logical AND.  */
+    lor,                       /* Logical OR.  */
+    /* Ternary operators:  */
+    qmop                       /* Question mark operator.  */
+  } operation;
+  union
+  {
+    unsigned long int num;     /* Number value for `num'.  */
+    struct expression *args[3];        /* Up to three arguments.  */
+  } val;
+};
+
+/* This is the data structure to pass information to the parser and get
+   the result in a thread-safe way.  */
+struct parse_args
+{
+  const char *cp;
+  struct expression *res;
+};
+
+
+/* The representation of an opened message catalog.  */
+struct loaded_domain
+{
+  const char *data;
+  int use_mmap;
+  size_t mmap_size;
+  int must_swap;
+  nls_uint32 nstrings;
+  struct string_desc *orig_tab;
+  struct string_desc *trans_tab;
+  nls_uint32 hash_size;
+  nls_uint32 *hash_tab;
+  int codeset_cntr;
+#ifdef _LIBC
+  __gconv_t conv;
+#else
+# if HAVE_ICONV
+  iconv_t conv;
+# endif
+#endif
+  char **conv_tab;
+
+  struct expression *plural;
+  unsigned long int nplurals;
+};
+
+/* We want to allocate a string at the end of the struct.  But ISO C
+   doesn't allow zero sized arrays.  */
+#ifdef __GNUC__
+# define ZERO 0
+#else
+# define ZERO 1
+#endif
+
+/* A set of settings bound to a message domain.  Used to store settings
+   from bindtextdomain() and bind_textdomain_codeset().  */
+struct binding
+{
+  struct binding *next;
+  char *dirname;
+  int codeset_cntr;    /* Incremented each time codeset changes.  */
+  char *codeset;
+  char domainname[ZERO];
+};
+
+/* A counter which is incremented each time some previous translations
+   become invalid.
+   This variable is part of the external ABI of the GNU libintl.  */
+extern int _nl_msg_cat_cntr;
+
+struct loaded_l10nfile *_nl_find_domain PARAMS ((const char *__dirname,
+                                                char *__locale,
+                                                const char *__domainname,
+                                             struct binding *__domainbinding))
+     internal_function;
+void _nl_load_domain PARAMS ((struct loaded_l10nfile *__domain,
+                             struct binding *__domainbinding))
+     internal_function;
+void _nl_unload_domain PARAMS ((struct loaded_domain *__domain))
+     internal_function;
+const char *_nl_init_domain_conv PARAMS ((struct loaded_l10nfile *__domain_file,
+                                         struct loaded_domain *__domain,
+                                         struct binding *__domainbinding))
+     internal_function;
+void _nl_free_domain_conv PARAMS ((struct loaded_domain *__domain))
+     internal_function;
+
+char *_nl_find_msg PARAMS ((struct loaded_l10nfile *domain_file,
+                           struct binding *domainbinding,
+                           const char *msgid, size_t *lengthp))
+     internal_function;
+
+#ifdef _LIBC
+extern char *__gettext PARAMS ((const char *__msgid));
+extern char *__dgettext PARAMS ((const char *__domainname,
+                                const char *__msgid));
+extern char *__dcgettext PARAMS ((const char *__domainname,
+                                 const char *__msgid, int __category));
+extern char *__ngettext PARAMS ((const char *__msgid1, const char *__msgid2,
+                                unsigned long int __n));
+extern char *__dngettext PARAMS ((const char *__domainname,
+                                 const char *__msgid1, const char *__msgid2,
+                                 unsigned long int n));
+extern char *__dcngettext PARAMS ((const char *__domainname,
+                                  const char *__msgid1, const char *__msgid2,
+                                  unsigned long int __n, int __category));
+extern char *__dcigettext PARAMS ((const char *__domainname,
+                                  const char *__msgid1, const char *__msgid2,
+                                  int __plural, unsigned long int __n,
+                                  int __category));
+extern char *__textdomain PARAMS ((const char *__domainname));
+extern char *__bindtextdomain PARAMS ((const char *__domainname,
+                                      const char *__dirname));
+extern char *__bind_textdomain_codeset PARAMS ((const char *__domainname,
+                                               const char *__codeset));
+#else
+extern char *gettext__ PARAMS ((const char *__msgid));
+extern char *dgettext__ PARAMS ((const char *__domainname,
+                                const char *__msgid));
+extern char *dcgettext__ PARAMS ((const char *__domainname,
+                                 const char *__msgid, int __category));
+extern char *ngettext__ PARAMS ((const char *__msgid1, const char *__msgid2,
+                                unsigned long int __n));
+extern char *dngettext__ PARAMS ((const char *__domainname,
+                                 const char *__msgid1, const char *__msgid2,
+                                 unsigned long int __n));
+extern char *dcngettext__ PARAMS ((const char *__domainname,
+                                  const char *__msgid1, const char *__msgid2,
+                                  unsigned long int __n, int __category));
+extern char *dcigettext__ PARAMS ((const char *__domainname,
+                                  const char *__msgid1, const char *__msgid2,
+                                  int __plural, unsigned long int __n,
+                                  int __category));
+extern char *textdomain__ PARAMS ((const char *__domainname));
+extern char *bindtextdomain__ PARAMS ((const char *__domainname,
+                                      const char *__dirname));
+extern char *bind_textdomain_codeset__ PARAMS ((const char *__domainname,
+                                               const char *__codeset));
+#endif
+
+#ifdef _LIBC
+extern void __gettext_free_exp PARAMS ((struct expression *exp))
+     internal_function;
+extern int __gettextparse PARAMS ((void *arg));
+#else
+extern void gettext_free_exp__ PARAMS ((struct expression *exp))
+     internal_function;
+extern int gettextparse__ PARAMS ((void *arg));
+#endif
+
+/* @@ begin of epilog @@ */
+
+#endif /* gettextP.h  */
diff --git a/apps/silcer/intl/hash-string.h b/apps/silcer/intl/hash-string.h
new file mode 100644 (file)
index 0000000..37d4ce1
--- /dev/null
@@ -0,0 +1,58 @@
+/* Description of GNU message catalog format: string hashing function.
+   Copyright (C) 1995, 1997, 1998, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* @@ end of prolog @@ */
+
+#ifndef PARAMS
+# if __STDC__
+#  define PARAMS(Args) Args
+# else
+#  define PARAMS(Args) ()
+# endif
+#endif
+
+/* We assume to have `unsigned long int' value with at least 32 bits.  */
+#define HASHWORDBITS 32
+
+
+/* Defines the so called `hashpjw' function by P.J. Weinberger
+   [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
+   1986, 1987 Bell Telephone Laboratories, Inc.]  */
+static unsigned long int hash_string PARAMS ((const char *__str_param));
+
+static inline unsigned long int
+hash_string (str_param)
+     const char *str_param;
+{
+  unsigned long int hval, g;
+  const char *str = str_param;
+
+  /* Compute the hash value for the given string.  */
+  hval = 0;
+  while (*str != '\0')
+    {
+      hval <<= 4;
+      hval += (unsigned long int) *str++;
+      g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
+      if (g != 0)
+       {
+         hval ^= g >> (HASHWORDBITS - 8);
+         hval ^= g;
+       }
+    }
+  return hval;
+}
diff --git a/apps/silcer/intl/intl-compat.c b/apps/silcer/intl/intl-compat.c
new file mode 100644 (file)
index 0000000..b8edaa1
--- /dev/null
@@ -0,0 +1,165 @@
+/* intl-compat.c - Stub functions to call gettext functions from GNU gettext
+   Library.
+   Copyright (C) 1995, 2000, 2001 Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libgnuintl.h"
+#include "gettextP.h"
+
+/* @@ end of prolog @@ */
+
+/* This file redirects the gettext functions (without prefix or suffix) to
+   those defined in the included GNU gettext library (with "__" suffix).
+   It is compiled into libintl when the included GNU gettext library is
+   configured --with-included-gettext.
+
+   This redirection works also in the case that the system C library or
+   the system libintl library contain gettext/textdomain/... functions.
+   If it didn't, we would need to add preprocessor level redirections to
+   libgnuintl.h of the following form:
+
+#    define gettext gettext__
+#    define dgettext dgettext__
+#    define dcgettext dcgettext__
+#    define ngettext ngettext__
+#    define dngettext dngettext__
+#    define dcngettext dcngettext__
+#    define textdomain textdomain__
+#    define bindtextdomain bindtextdomain__
+#    define bind_textdomain_codeset bind_textdomain_codeset__
+
+   How does this redirection work? There are two cases.
+   A. When libintl.a is linked into an executable, it works because
+      functions defined in the executable always override functions in
+      the shared libraries.
+   B. When libintl.so is used, it works because
+      1. those systems defining gettext/textdomain/... in the C library
+         (namely, Solaris 2.4 and newer, and GNU libc 2.0 and newer) are
+         ELF systems and define these symbols as weak, thus explicitly
+         letting other shared libraries override it.
+      2. those systems defining gettext/textdomain/... in a standalone
+         libintl.so library (namely, Solaris 2.3 and newer) have this
+         shared library in /usr/lib, and the linker will search /usr/lib
+         *after* the directory where the GNU gettext library is installed.
+
+   A third case, namely when libintl.a is linked into a shared library
+   whose name is not libintl.so, is not supported. In this case, on
+   Solaris, when -lintl precedes the linker option for the shared library
+   containing GNU gettext, the system's gettext would indeed override
+   the GNU gettext. Anyone doing this kind of stuff must be clever enough
+   to 1. compile libintl.a with -fPIC, 2. remove -lintl from his linker
+   command line.  */
+
+
+#undef gettext
+#undef dgettext
+#undef dcgettext
+#undef ngettext
+#undef dngettext
+#undef dcngettext
+#undef textdomain
+#undef bindtextdomain
+#undef bind_textdomain_codeset
+
+
+char *
+gettext (msgid)
+     const char *msgid;
+{
+  return gettext__ (msgid);
+}
+
+
+char *
+dgettext (domainname, msgid)
+     const char *domainname;
+     const char *msgid;
+{
+  return dgettext__ (domainname, msgid);
+}
+
+
+char *
+dcgettext (domainname, msgid, category)
+     const char *domainname;
+     const char *msgid;
+     int category;
+{
+  return dcgettext__ (domainname, msgid, category);
+}
+
+
+char *
+ngettext (msgid1, msgid2, n)
+     const char *msgid1;
+     const char *msgid2;
+     unsigned long int n;
+{
+  return ngettext__ (msgid1, msgid2, n);
+}
+
+
+char *
+dngettext (domainname, msgid1, msgid2, n)
+     const char *domainname;
+     const char *msgid1;
+     const char *msgid2;
+     unsigned long int n;
+{
+  return dngettext__ (domainname, msgid1, msgid2, n);
+}
+
+
+char *
+dcngettext (domainname, msgid1, msgid2, n, category)
+     const char *domainname;
+     const char *msgid1;
+     const char *msgid2;
+     unsigned long int n;
+     int category;
+{
+  return dcngettext__ (domainname, msgid1, msgid2, n, category);
+}
+
+
+char *
+textdomain (domainname)
+     const char *domainname;
+{
+  return textdomain__ (domainname);
+}
+
+
+char *
+bindtextdomain (domainname, dirname)
+     const char *domainname;
+     const char *dirname;
+{
+  return bindtextdomain__ (domainname, dirname);
+}
+
+
+char *
+bind_textdomain_codeset (domainname, codeset)
+     const char *domainname;
+     const char *codeset;
+{
+  return bind_textdomain_codeset__ (domainname, codeset);
+}
diff --git a/apps/silcer/intl/l10nflist.c b/apps/silcer/intl/l10nflist.c
new file mode 100644 (file)
index 0000000..557253e
--- /dev/null
@@ -0,0 +1,404 @@
+/* Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+   Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Tell glibc's <string.h> to provide a prototype for stpcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE   1
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#if !HAVE_STRCHR && !defined _LIBC
+# ifndef strchr
+#  define strchr index
+# endif
+#endif
+
+#if defined _LIBC || defined HAVE_ARGZ_H
+# include <argz.h>
+#endif
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "loadinfo.h"
+
+/* On some strange systems still no definition of NULL is found.  Sigh!  */
+#ifndef NULL
+# if defined __STDC__ && __STDC__
+#  define NULL ((void *) 0)
+# else
+#  define NULL 0
+# endif
+#endif
+
+/* @@ end of prolog @@ */
+
+#ifdef _LIBC
+/* Rename the non ANSI C functions.  This is required by the standard
+   because some ANSI C functions will require linking with this object
+   file and the name space must not be polluted.  */
+# ifndef stpcpy
+#  define stpcpy(dest, src) __stpcpy(dest, src)
+# endif
+#else
+# ifndef HAVE_STPCPY
+static char *stpcpy PARAMS ((char *dest, const char *src));
+# endif
+#endif
+
+/* Define function which are usually not available.  */
+
+#if !defined _LIBC && !defined HAVE___ARGZ_COUNT
+/* Returns the number of strings in ARGZ.  */
+static size_t argz_count__ PARAMS ((const char *argz, size_t len));
+
+static size_t
+argz_count__ (argz, len)
+     const char *argz;
+     size_t len;
+{
+  size_t count = 0;
+  while (len > 0)
+    {
+      size_t part_len = strlen (argz);
+      argz += part_len + 1;
+      len -= part_len + 1;
+      count++;
+    }
+  return count;
+}
+# undef __argz_count
+# define __argz_count(argz, len) argz_count__ (argz, len)
+#endif /* !_LIBC && !HAVE___ARGZ_COUNT */
+
+#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
+/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
+   except the last into the character SEP.  */
+static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
+
+static void
+argz_stringify__ (argz, len, sep)
+     char *argz;
+     size_t len;
+     int sep;
+{
+  while (len > 0)
+    {
+      size_t part_len = strlen (argz);
+      argz += part_len;
+      len -= part_len + 1;
+      if (len > 0)
+       *argz++ = sep;
+    }
+}
+# undef __argz_stringify
+# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
+#endif /* !_LIBC && !HAVE___ARGZ_STRINGIFY */
+
+#if !defined _LIBC && !defined HAVE___ARGZ_NEXT
+static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
+                                 const char *entry));
+
+static char *
+argz_next__ (argz, argz_len, entry)
+     char *argz;
+     size_t argz_len;
+     const char *entry;
+{
+  if (entry)
+    {
+      if (entry < argz + argz_len)
+        entry = strchr (entry, '\0') + 1;
+
+      return entry >= argz + argz_len ? NULL : (char *) entry;
+    }
+  else
+    if (argz_len > 0)
+      return argz;
+    else
+      return 0;
+}
+# undef __argz_next
+# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
+#endif /* !_LIBC && !HAVE___ARGZ_NEXT */
+
+
+/* Return number of bits set in X.  */
+static int pop PARAMS ((int x));
+
+static inline int
+pop (x)
+     int x;
+{
+  /* We assume that no more than 16 bits are used.  */
+  x = ((x & ~0x5555) >> 1) + (x & 0x5555);
+  x = ((x & ~0x3333) >> 2) + (x & 0x3333);
+  x = ((x >> 4) + x) & 0x0f0f;
+  x = ((x >> 8) + x) & 0xff;
+
+  return x;
+}
+
+\f
+struct loaded_l10nfile *
+_nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
+                   territory, codeset, normalized_codeset, modifier, special,
+                   sponsor, revision, filename, do_allocate)
+     struct loaded_l10nfile **l10nfile_list;
+     const char *dirlist;
+     size_t dirlist_len;
+     int mask;
+     const char *language;
+     const char *territory;
+     const char *codeset;
+     const char *normalized_codeset;
+     const char *modifier;
+     const char *special;
+     const char *sponsor;
+     const char *revision;
+     const char *filename;
+     int do_allocate;
+{
+  char *abs_filename;
+  struct loaded_l10nfile *last = NULL;
+  struct loaded_l10nfile *retval;
+  char *cp;
+  size_t entries;
+  int cnt;
+
+  /* Allocate room for the full file name.  */
+  abs_filename = (char *) malloc (dirlist_len
+                                 + strlen (language)
+                                 + ((mask & TERRITORY) != 0
+                                    ? strlen (territory) + 1 : 0)
+                                 + ((mask & XPG_CODESET) != 0
+                                    ? strlen (codeset) + 1 : 0)
+                                 + ((mask & XPG_NORM_CODESET) != 0
+                                    ? strlen (normalized_codeset) + 1 : 0)
+                                 + (((mask & XPG_MODIFIER) != 0
+                                     || (mask & CEN_AUDIENCE) != 0)
+                                    ? strlen (modifier) + 1 : 0)
+                                 + ((mask & CEN_SPECIAL) != 0
+                                    ? strlen (special) + 1 : 0)
+                                 + (((mask & CEN_SPONSOR) != 0
+                                     || (mask & CEN_REVISION) != 0)
+                                    ? (1 + ((mask & CEN_SPONSOR) != 0
+                                            ? strlen (sponsor) + 1 : 0)
+                                       + ((mask & CEN_REVISION) != 0
+                                          ? strlen (revision) + 1 : 0)) : 0)
+                                 + 1 + strlen (filename) + 1);
+
+  if (abs_filename == NULL)
+    return NULL;
+
+  retval = NULL;
+  last = NULL;
+
+  /* Construct file name.  */
+  memcpy (abs_filename, dirlist, dirlist_len);
+  __argz_stringify (abs_filename, dirlist_len, PATH_SEPARATOR);
+  cp = abs_filename + (dirlist_len - 1);
+  *cp++ = '/';
+  cp = stpcpy (cp, language);
+
+  if ((mask & TERRITORY) != 0)
+    {
+      *cp++ = '_';
+      cp = stpcpy (cp, territory);
+    }
+  if ((mask & XPG_CODESET) != 0)
+    {
+      *cp++ = '.';
+      cp = stpcpy (cp, codeset);
+    }
+  if ((mask & XPG_NORM_CODESET) != 0)
+    {
+      *cp++ = '.';
+      cp = stpcpy (cp, normalized_codeset);
+    }
+  if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
+    {
+      /* This component can be part of both syntaces but has different
+        leading characters.  For CEN we use `+', else `@'.  */
+      *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
+      cp = stpcpy (cp, modifier);
+    }
+  if ((mask & CEN_SPECIAL) != 0)
+    {
+      *cp++ = '+';
+      cp = stpcpy (cp, special);
+    }
+  if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
+    {
+      *cp++ = ',';
+      if ((mask & CEN_SPONSOR) != 0)
+       cp = stpcpy (cp, sponsor);
+      if ((mask & CEN_REVISION) != 0)
+       {
+         *cp++ = '_';
+         cp = stpcpy (cp, revision);
+       }
+    }
+
+  *cp++ = '/';
+  stpcpy (cp, filename);
+
+  /* Look in list of already loaded domains whether it is already
+     available.  */
+  last = NULL;
+  for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
+    if (retval->filename != NULL)
+      {
+       int compare = strcmp (retval->filename, abs_filename);
+       if (compare == 0)
+         /* We found it!  */
+         break;
+       if (compare < 0)
+         {
+           /* It's not in the list.  */
+           retval = NULL;
+           break;
+         }
+
+       last = retval;
+      }
+
+  if (retval != NULL || do_allocate == 0)
+    {
+      free (abs_filename);
+      return retval;
+    }
+
+  retval = (struct loaded_l10nfile *)
+    malloc (sizeof (*retval) + (__argz_count (dirlist, dirlist_len)
+                               * (1 << pop (mask))
+                               * sizeof (struct loaded_l10nfile *)));
+  if (retval == NULL)
+    return NULL;
+
+  retval->filename = abs_filename;
+  retval->decided = (__argz_count (dirlist, dirlist_len) != 1
+                    || ((mask & XPG_CODESET) != 0
+                        && (mask & XPG_NORM_CODESET) != 0));
+  retval->data = NULL;
+
+  if (last == NULL)
+    {
+      retval->next = *l10nfile_list;
+      *l10nfile_list = retval;
+    }
+  else
+    {
+      retval->next = last->next;
+      last->next = retval;
+    }
+
+  entries = 0;
+  /* If the DIRLIST is a real list the RETVAL entry corresponds not to
+     a real file.  So we have to use the DIRLIST separation mechanism
+     of the inner loop.  */
+  cnt = __argz_count (dirlist, dirlist_len) == 1 ? mask - 1 : mask;
+  for (; cnt >= 0; --cnt)
+    if ((cnt & ~mask) == 0
+       && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
+       && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
+      {
+       /* Iterate over all elements of the DIRLIST.  */
+       char *dir = NULL;
+
+       while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
+              != NULL)
+         retval->successor[entries++]
+           = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1, cnt,
+                                 language, territory, codeset,
+                                 normalized_codeset, modifier, special,
+                                 sponsor, revision, filename, 1);
+      }
+  retval->successor[entries] = NULL;
+
+  return retval;
+}
+\f
+/* Normalize codeset name.  There is no standard for the codeset
+   names.  Normalization allows the user to use any of the common
+   names.  The return value is dynamically allocated and has to be
+   freed by the caller.  */
+const char *
+_nl_normalize_codeset (codeset, name_len)
+     const char *codeset;
+     size_t name_len;
+{
+  int len = 0;
+  int only_digit = 1;
+  char *retval;
+  char *wp;
+  size_t cnt;
+
+  for (cnt = 0; cnt < name_len; ++cnt)
+    if (isalnum (codeset[cnt]))
+      {
+       ++len;
+
+       if (isalpha (codeset[cnt]))
+         only_digit = 0;
+      }
+
+  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
+
+  if (retval != NULL)
+    {
+      if (only_digit)
+       wp = stpcpy (retval, "iso");
+      else
+       wp = retval;
+
+      for (cnt = 0; cnt < name_len; ++cnt)
+       if (isalpha (codeset[cnt]))
+         *wp++ = tolower (codeset[cnt]);
+       else if (isdigit (codeset[cnt]))
+         *wp++ = codeset[cnt];
+
+      *wp = '\0';
+    }
+
+  return (const char *) retval;
+}
+
+
+/* @@ begin of epilog @@ */
+
+/* We don't want libintl.a to depend on any other library.  So we
+   avoid the non-standard function stpcpy.  In GNU C Library this
+   function is available, though.  Also allow the symbol HAVE_STPCPY
+   to be defined.  */
+#if !_LIBC && !HAVE_STPCPY
+static char *
+stpcpy (dest, src)
+     char *dest;
+     const char *src;
+{
+  while ((*dest++ = *src++) != '\0')
+    /* Do nothing. */ ;
+  return dest - 1;
+}
+#endif
diff --git a/apps/silcer/intl/libgettext.h b/apps/silcer/intl/libgettext.h
new file mode 100644 (file)
index 0000000..553382c
--- /dev/null
@@ -0,0 +1,48 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+   Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option.  */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions.  */
+# include <libintl.h>
+
+#else
+
+# define gettext(Msgid) (Msgid)
+# define dgettext(Domainname, Msgid) (Msgid)
+# define dcgettext(Domainname, Msgid, Category) (Msgid)
+# define ngettext(Msgid1, Msgid2, N) \
+    ((N) == 1 ? (char *) (Msgid1) : (char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+    ((N) == 1 ? (char *) (Msgid1) : (char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+    ((N) == 1 ? (char *) (Msgid1) : (char *) (Msgid2))
+# define textdomain(Domainname) ((char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((char *) (Codeset))
+
+#endif
+
+/* For automatical extraction of messages sometimes no real
+   translation is needed.  Instead the string itself is the result.  */
+#define gettext_noop(Str) (Str)
+
+#endif /* _LIBGETTEXT_H */
diff --git a/apps/silcer/intl/libgnuintl.h b/apps/silcer/intl/libgnuintl.h
new file mode 100644 (file)
index 0000000..577001a
--- /dev/null
@@ -0,0 +1,127 @@
+/* Message catalogs for internationalization.
+   Copyright (C) 1995-1997, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _LIBINTL_H
+#define _LIBINTL_H     1
+
+#include <locale.h>
+
+/* The LC_MESSAGES locale category is the category used by the functions
+   gettext() and dgettext().  It is specified in POSIX, but not in ANSI C.
+   On systems that don't define it, use an arbitrary value instead.
+   On Solaris, <locale.h> defines __LOCALE_H then includes <libintl.h> (i.e.
+   this file!) and then only defines LC_MESSAGES.  To avoid a redefinition
+   warning, don't define LC_MESSAGES in this case.  */
+#if !defined LC_MESSAGES && !defined __LOCALE_H
+# define LC_MESSAGES 1729
+#endif
+
+/* We define an additional symbol to signal that we use the GNU
+   implementation of gettext.  */
+#define __USE_GNU_GETTEXT 1
+
+/* Resolve a platform specific conflict on DJGPP.  GNU gettext takes
+   precedence over _conio_gettext.  */
+#ifdef __DJGPP__
+# undef gettext
+# define gettext gettext
+#endif
+
+#ifndef PARAMS
+# if __STDC__ || defined __cplusplus
+#  define PARAMS(args) args
+# else
+#  define PARAMS(args) ()
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Look up MSGID in the current default message catalog for the current
+   LC_MESSAGES locale.  If not found, returns MSGID itself (the default
+   text).  */
+extern char *gettext PARAMS ((const char *__msgid));
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current
+   LC_MESSAGES locale.  */
+extern char *dgettext PARAMS ((const char *__domainname, const char *__msgid));
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
+   locale.  */
+extern char *dcgettext PARAMS ((const char *__domainname, const char *__msgid,
+                               int __category));
+
+
+/* Similar to `gettext' but select the plural form corresponding to the
+   number N.  */
+extern char *ngettext PARAMS ((const char *__msgid1, const char *__msgid2,
+                              unsigned long int __n));
+
+/* Similar to `dgettext' but select the plural form corresponding to the
+   number N.  */
+extern char *dngettext PARAMS ((const char *__domainname, const char *__msgid1,
+                               const char *__msgid2, unsigned long int __n));
+
+/* Similar to `dcgettext' but select the plural form corresponding to the
+   number N.  */
+extern char *dcngettext PARAMS ((const char *__domainname, const char *__msgid1,
+                                const char *__msgid2, unsigned long int __n,
+                                int __category));
+
+
+/* Set the current default message catalog to DOMAINNAME.
+   If DOMAINNAME is null, return the current default.
+   If DOMAINNAME is "", reset to the default of "messages".  */
+extern char *textdomain PARAMS ((const char *__domainname));
+
+/* Specify that the DOMAINNAME message catalog will be found
+   in DIRNAME rather than in the system locale data base.  */
+extern char *bindtextdomain PARAMS ((const char *__domainname,
+                                    const char *__dirname));
+
+/* Specify the character encoding in which the messages from the
+   DOMAINNAME message catalog will be returned.  */
+extern char *bind_textdomain_codeset PARAMS ((const char *__domainname,
+                                             const char *__codeset));
+
+
+/* Optimized version of the functions above.  */
+#if defined __OPTIMIZED
+/* These are macros, but could also be inline functions.  */
+
+# define gettext(msgid)                                                              \
+  dgettext (NULL, msgid)
+
+# define dgettext(domainname, msgid)                                         \
+  dcgettext (domainname, msgid, LC_MESSAGES)
+
+# define ngettext(msgid1, msgid2, n)                                         \
+  dngettext (NULL, msgid1, msgid2, n)
+
+# define dngettext(domainname, msgid1, msgid2, n)                            \
+  dcngettext (domainname, msgid1, msgid2, n, LC_MESSAGES)
+
+#endif /* Optimizing. */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* libintl.h */
diff --git a/apps/silcer/intl/loadinfo.h b/apps/silcer/intl/loadinfo.h
new file mode 100644 (file)
index 0000000..5171a8f
--- /dev/null
@@ -0,0 +1,108 @@
+/* Copyright (C) 1996-1999, 2000, 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _LOADINFO_H
+#define _LOADINFO_H    1
+
+#ifndef PARAMS
+# if __STDC__
+#  define PARAMS(args) args
+# else
+#  define PARAMS(args) ()
+# endif
+#endif
+
+#ifndef internal_function
+# define internal_function
+#endif
+
+/* Tell the compiler when a conditional or integer expression is
+   almost always true or almost always false.  */
+#ifndef HAVE_BUILTIN_EXPECT
+# define __builtin_expect(expr, val) (expr)
+#endif
+
+/* Separator in PATH like lists of pathnames.  */
+#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
+  /* Win32, OS/2, DOS */
+# define PATH_SEPARATOR ';'
+#else
+  /* Unix */
+# define PATH_SEPARATOR ':'
+#endif
+
+/* Encoding of locale name parts.  */
+#define CEN_REVISION           1
+#define CEN_SPONSOR            2
+#define CEN_SPECIAL            4
+#define XPG_NORM_CODESET       8
+#define XPG_CODESET            16
+#define TERRITORY              32
+#define CEN_AUDIENCE           64
+#define XPG_MODIFIER           128
+
+#define CEN_SPECIFIC   (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE)
+#define XPG_SPECIFIC   (XPG_CODESET|XPG_NORM_CODESET|XPG_MODIFIER)
+
+
+struct loaded_l10nfile
+{
+  const char *filename;
+  int decided;
+
+  const void *data;
+
+  struct loaded_l10nfile *next;
+  struct loaded_l10nfile *successor[1];
+};
+
+
+/* Normalize codeset name.  There is no standard for the codeset
+   names.  Normalization allows the user to use any of the common
+   names.  The return value is dynamically allocated and has to be
+   freed by the caller.  */
+extern const char *_nl_normalize_codeset PARAMS ((const char *codeset,
+                                                 size_t name_len));
+
+extern struct loaded_l10nfile *
+_nl_make_l10nflist PARAMS ((struct loaded_l10nfile **l10nfile_list,
+                           const char *dirlist, size_t dirlist_len, int mask,
+                           const char *language, const char *territory,
+                           const char *codeset,
+                           const char *normalized_codeset,
+                           const char *modifier, const char *special,
+                           const char *sponsor, const char *revision,
+                           const char *filename, int do_allocate));
+
+
+extern const char *_nl_expand_alias PARAMS ((const char *name));
+
+/* normalized_codeset is dynamically allocated and has to be freed by
+   the caller.  */
+extern int _nl_explode_name PARAMS ((char *name, const char **language,
+                                    const char **modifier,
+                                    const char **territory,
+                                    const char **codeset,
+                                    const char **normalized_codeset,
+                                    const char **special,
+                                    const char **sponsor,
+                                    const char **revision));
+
+extern char *_nl_find_language PARAMS ((const char *name));
+
+#endif /* loadinfo.h */
diff --git a/apps/silcer/intl/loadmsgcat.c b/apps/silcer/intl/loadmsgcat.c
new file mode 100644 (file)
index 0000000..d589243
--- /dev/null
@@ -0,0 +1,566 @@
+/* Load needed message catalogs.
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Tell glibc's <string.h> to provide a prototype for mempcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE    1
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+# define HAVE_ALLOCA 1
+#else
+# if defined HAVE_ALLOCA_H || defined _LIBC
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_UNISTD_H || defined _LIBC
+# include <unistd.h>
+#endif
+
+#ifdef _LIBC
+# include <langinfo.h>
+# include <locale.h>
+#endif
+
+#if (defined HAVE_MMAP && defined HAVE_MUNMAP && !defined DISALLOW_MMAP) \
+    || (defined _LIBC && defined _POSIX_MAPPED_FILES)
+# include <sys/mman.h>
+# undef HAVE_MMAP
+# define HAVE_MMAP     1
+#else
+# undef HAVE_MMAP
+#endif
+
+#include "gettext.h"
+#include "gettextP.h"
+
+#ifdef _LIBC
+# include "../locale/localeinfo.h"
+#endif
+
+/* @@ end of prolog @@ */
+
+#ifdef _LIBC
+/* Rename the non ISO C functions.  This is required by the standard
+   because some ISO C functions will require linking with this object
+   file and the name space must not be polluted.  */
+# define open   __open
+# define close  __close
+# define read   __read
+# define mmap   __mmap
+# define munmap __munmap
+#endif
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define PLURAL_PARSE __gettextparse
+#else
+# define PLURAL_PARSE gettextparse__
+#endif
+
+/* For those losing systems which don't have `alloca' we have to add
+   some additional code emulating it.  */
+#ifdef HAVE_ALLOCA
+# define freea(p) /* nothing */
+#else
+# define alloca(n) malloc (n)
+# define freea(p) free (p)
+#endif
+
+/* For systems that distinguish between text and binary I/O.
+   O_BINARY is usually declared in <fcntl.h>. */
+#if !defined O_BINARY && defined _O_BINARY
+  /* For MSC-compatible compilers.  */
+# define O_BINARY _O_BINARY
+# define O_TEXT _O_TEXT
+#endif
+#ifdef __BEOS__
+  /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect.  */
+# undef O_BINARY
+# undef O_TEXT
+#endif
+/* On reasonable systems, binary I/O is the default.  */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+/* We need a sign, whether a new catalog was loaded, which can be associated
+   with all translations.  This is important if the translations are
+   cached by one of GCC's features.  */
+int _nl_msg_cat_cntr;
+
+#if (defined __GNUC__ && !defined __APPLE_CC__) \
+    || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+
+/* These structs are the constant expression for the germanic plural
+   form determination.  It represents the expression  "n != 1".  */
+static const struct expression plvar =
+{
+  .nargs = 0,
+  .operation = var,
+};
+static const struct expression plone =
+{
+  .nargs = 0,
+  .operation = num,
+  .val =
+  {
+    .num = 1
+  }
+};
+static struct expression germanic_plural =
+{
+  .nargs = 2,
+  .operation = not_equal,
+  .val =
+  {
+    .args =
+    {
+      [0] = (struct expression *) &plvar,
+      [1] = (struct expression *) &plone
+    }
+  }
+};
+
+# define INIT_GERMANIC_PLURAL()
+
+#else
+
+/* For compilers without support for ISO C 99 struct/union initializers:
+   Initialization at run-time.  */
+
+static struct expression plvar;
+static struct expression plone;
+static struct expression germanic_plural;
+
+static void
+init_germanic_plural ()
+{
+  if (plone.val.num == 0)
+    {
+      plvar.nargs = 0;
+      plvar.operation = var;
+
+      plone.nargs = 0;
+      plone.operation = num;
+      plone.val.num = 1;
+
+      germanic_plural.nargs = 2;
+      germanic_plural.operation = not_equal;
+      germanic_plural.val.args[0] = &plvar;
+      germanic_plural.val.args[1] = &plone;
+    }
+}
+
+# define INIT_GERMANIC_PLURAL() init_germanic_plural ()
+
+#endif
+
+
+/* Initialize the codeset dependent parts of an opened message catalog.
+   Return the header entry.  */
+const char *
+internal_function
+_nl_init_domain_conv (domain_file, domain, domainbinding)
+     struct loaded_l10nfile *domain_file;
+     struct loaded_domain *domain;
+     struct binding *domainbinding;
+{
+  /* Find out about the character set the file is encoded with.
+     This can be found (in textual form) in the entry "".  If this
+     entry does not exist or if this does not contain the `charset='
+     information, we will assume the charset matches the one the
+     current locale and we don't have to perform any conversion.  */
+  char *nullentry;
+  size_t nullentrylen;
+
+  /* Preinitialize fields, to avoid recursion during _nl_find_msg.  */
+  domain->codeset_cntr =
+    (domainbinding != NULL ? domainbinding->codeset_cntr : 0);
+#ifdef _LIBC
+  domain->conv = (__gconv_t) -1;
+#else
+# if HAVE_ICONV
+  domain->conv = (iconv_t) -1;
+# endif
+#endif
+  domain->conv_tab = NULL;
+
+  /* Get the header entry.  */
+  nullentry = _nl_find_msg (domain_file, domainbinding, "", &nullentrylen);
+
+  if (nullentry != NULL)
+    {
+#if defined _LIBC || HAVE_ICONV
+      const char *charsetstr;
+
+      charsetstr = strstr (nullentry, "charset=");
+      if (charsetstr != NULL)
+       {
+         size_t len;
+         char *charset;
+         const char *outcharset;
+
+         charsetstr += strlen ("charset=");
+         len = strcspn (charsetstr, " \t\n");
+
+         charset = (char *) alloca (len + 1);
+# if defined _LIBC || HAVE_MEMPCPY
+         *((char *) mempcpy (charset, charsetstr, len)) = '\0';
+# else
+         memcpy (charset, charsetstr, len);
+         charset[len] = '\0';
+# endif
+
+         /* The output charset should normally be determined by the
+            locale.  But sometimes the locale is not used or not correctly
+            set up, so we provide a possibility for the user to override
+            this.  Moreover, the value specified through
+            bind_textdomain_codeset overrides both.  */
+         if (domainbinding != NULL && domainbinding->codeset != NULL)
+           outcharset = domainbinding->codeset;
+         else
+           {
+             outcharset = getenv ("OUTPUT_CHARSET");
+             if (outcharset == NULL || outcharset[0] == '\0')
+               {
+# ifdef _LIBC
+                 outcharset = (*_nl_current[LC_CTYPE])->values[_NL_ITEM_INDEX (CODESET)].string;
+# else
+#  if HAVE_ICONV
+                 extern const char *locale_charset (void);
+                 outcharset = locale_charset ();
+#  endif
+# endif
+               }
+           }
+
+# ifdef _LIBC
+         /* We always want to use transliteration.  */
+         outcharset = norm_add_slashes (outcharset, "TRANSLIT");
+         charset = norm_add_slashes (charset, NULL);
+         if (__gconv_open (outcharset, charset, &domain->conv,
+                           GCONV_AVOID_NOCONV)
+             != __GCONV_OK)
+           domain->conv = (__gconv_t) -1;
+# else
+#  if HAVE_ICONV
+         /* When using GNU libiconv, we want to use transliteration.  */
+#   if _LIBICONV_VERSION >= 0x0105
+         len = strlen (outcharset);
+         {
+           char *tmp = (char *) alloca (len + 10 + 1);
+           memcpy (tmp, outcharset, len);
+           memcpy (tmp + len, "//TRANSLIT", 10 + 1);
+           outcharset = tmp;
+         }
+#   endif
+         domain->conv = iconv_open (outcharset, charset);
+#   if _LIBICONV_VERSION >= 0x0105
+         freea (outcharset);
+#   endif
+#  endif
+# endif
+
+         freea (charset);
+       }
+#endif /* _LIBC || HAVE_ICONV */
+    }
+
+  return nullentry;
+}
+
+/* Frees the codeset dependent parts of an opened message catalog.  */
+void
+internal_function
+_nl_free_domain_conv (domain)
+     struct loaded_domain *domain;
+{
+  if (domain->conv_tab != NULL && domain->conv_tab != (char **) -1)
+    free (domain->conv_tab);
+
+#ifdef _LIBC
+  if (domain->conv != (__gconv_t) -1)
+    __gconv_close (domain->conv);
+#else
+# if HAVE_ICONV
+  if (domain->conv != (iconv_t) -1)
+    iconv_close (domain->conv);
+# endif
+#endif
+}
+
+/* Load the message catalogs specified by FILENAME.  If it is no valid
+   message catalog do nothing.  */
+void
+internal_function
+_nl_load_domain (domain_file, domainbinding)
+     struct loaded_l10nfile *domain_file;
+     struct binding *domainbinding;
+{
+  int fd;
+  size_t size;
+#ifdef _LIBC
+  struct stat64 st;
+#else
+  struct stat st;
+#endif
+  struct mo_file_header *data = (struct mo_file_header *) -1;
+  int use_mmap = 0;
+  struct loaded_domain *domain;
+  const char *nullentry;
+
+  domain_file->decided = 1;
+  domain_file->data = NULL;
+
+  /* Note that it would be useless to store domainbinding in domain_file
+     because domainbinding might be == NULL now but != NULL later (after
+     a call to bind_textdomain_codeset).  */
+
+  /* If the record does not represent a valid locale the FILENAME
+     might be NULL.  This can happen when according to the given
+     specification the locale file name is different for XPG and CEN
+     syntax.  */
+  if (domain_file->filename == NULL)
+    return;
+
+  /* Try to open the addressed file.  */
+  fd = open (domain_file->filename, O_RDONLY | O_BINARY);
+  if (fd == -1)
+    return;
+
+  /* We must know about the size of the file.  */
+  if (
+#ifdef _LIBC
+      __builtin_expect (fstat64 (fd, &st) != 0, 0)
+#else
+      __builtin_expect (fstat (fd, &st) != 0, 0)
+#endif
+      || __builtin_expect ((size = (size_t) st.st_size) != st.st_size, 0)
+      || __builtin_expect (size < sizeof (struct mo_file_header), 0))
+    {
+      /* Something went wrong.  */
+      close (fd);
+      return;
+    }
+
+#ifdef HAVE_MMAP
+  /* Now we are ready to load the file.  If mmap() is available we try
+     this first.  If not available or it failed we try to load it.  */
+  data = (struct mo_file_header *) mmap (NULL, size, PROT_READ,
+                                        MAP_PRIVATE, fd, 0);
+
+  if (__builtin_expect (data != (struct mo_file_header *) -1, 1))
+    {
+      /* mmap() call was successful.  */
+      close (fd);
+      use_mmap = 1;
+    }
+#endif
+
+  /* If the data is not yet available (i.e. mmap'ed) we try to load
+     it manually.  */
+  if (data == (struct mo_file_header *) -1)
+    {
+      size_t to_read;
+      char *read_ptr;
+
+      data = (struct mo_file_header *) malloc (size);
+      if (data == NULL)
+       return;
+
+      to_read = size;
+      read_ptr = (char *) data;
+      do
+       {
+         long int nb = (long int) read (fd, read_ptr, to_read);
+         if (nb <= 0)
+           {
+#ifdef EINTR
+             if (nb == -1 && errno == EINTR)
+               continue;
+#endif
+             close (fd);
+             return;
+           }
+         read_ptr += nb;
+         to_read -= nb;
+       }
+      while (to_read > 0);
+
+      close (fd);
+    }
+
+  /* Using the magic number we can test whether it really is a message
+     catalog file.  */
+  if (__builtin_expect (data->magic != _MAGIC && data->magic != _MAGIC_SWAPPED,
+                       0))
+    {
+      /* The magic number is wrong: not a message catalog file.  */
+#ifdef HAVE_MMAP
+      if (use_mmap)
+       munmap ((caddr_t) data, size);
+      else
+#endif
+       free (data);
+      return;
+    }
+
+  domain = (struct loaded_domain *) malloc (sizeof (struct loaded_domain));
+  if (domain == NULL)
+    return;
+  domain_file->data = domain;
+
+  domain->data = (char *) data;
+  domain->use_mmap = use_mmap;
+  domain->mmap_size = size;
+  domain->must_swap = data->magic != _MAGIC;
+
+  /* Fill in the information about the available tables.  */
+  switch (W (domain->must_swap, data->revision))
+    {
+    case 0:
+      domain->nstrings = W (domain->must_swap, data->nstrings);
+      domain->orig_tab = (struct string_desc *)
+       ((char *) data + W (domain->must_swap, data->orig_tab_offset));
+      domain->trans_tab = (struct string_desc *)
+       ((char *) data + W (domain->must_swap, data->trans_tab_offset));
+      domain->hash_size = W (domain->must_swap, data->hash_tab_size);
+      domain->hash_tab = (nls_uint32 *)
+       ((char *) data + W (domain->must_swap, data->hash_tab_offset));
+      break;
+    default:
+      /* This is an invalid revision.  */
+#ifdef HAVE_MMAP
+      if (use_mmap)
+       munmap ((caddr_t) data, size);
+      else
+#endif
+       free (data);
+      free (domain);
+      domain_file->data = NULL;
+      return;
+    }
+
+  /* Now initialize the character set converter from the character set
+     the file is encoded with (found in the header entry) to the domain's
+     specified character set or the locale's character set.  */
+  nullentry = _nl_init_domain_conv (domain_file, domain, domainbinding);
+
+  /* Also look for a plural specification.  */
+  if (nullentry != NULL)
+    {
+      const char *plural;
+      const char *nplurals;
+
+      plural = strstr (nullentry, "plural=");
+      nplurals = strstr (nullentry, "nplurals=");
+      if (plural == NULL || nplurals == NULL)
+       goto no_plural;
+      else
+       {
+         /* First get the number.  */
+         char *endp;
+         unsigned long int n;
+         struct parse_args args;
+
+         nplurals += 9;
+         while (*nplurals != '\0' && isspace (*nplurals))
+           ++nplurals;
+#if defined HAVE_STRTOUL || defined _LIBC
+         n = strtoul (nplurals, &endp, 10);
+#else
+         for (endp = nplurals, n = 0; *endp >= '0' && *endp <= '9'; endp++)
+           n = n * 10 + (*endp - '0');
+#endif
+         domain->nplurals = n;
+         if (nplurals == endp)
+           goto no_plural;
+
+         /* Due to the restrictions bison imposes onto the interface of the
+            scanner function we have to put the input string and the result
+            passed up from the parser into the same structure which address
+            is passed down to the parser.  */
+         plural += 7;
+         args.cp = plural;
+         if (PLURAL_PARSE (&args) != 0)
+           goto no_plural;
+         domain->plural = args.res;
+       }
+    }
+  else
+    {
+      /* By default we are using the Germanic form: singular form only
+         for `one', the plural form otherwise.  Yes, this is also what
+         English is using since English is a Germanic language.  */
+    no_plural:
+      INIT_GERMANIC_PLURAL ();
+      domain->plural = &germanic_plural;
+      domain->nplurals = 2;
+    }
+}
+
+
+#ifdef _LIBC
+void
+internal_function
+_nl_unload_domain (domain)
+     struct loaded_domain *domain;
+{
+  if (domain->plural != &germanic_plural)
+    __gettext_free_exp (domain->plural);
+
+  _nl_free_domain_conv (domain);
+
+# ifdef _POSIX_MAPPED_FILES
+  if (domain->use_mmap)
+    munmap ((caddr_t) domain->data, domain->mmap_size);
+  else
+# endif        /* _POSIX_MAPPED_FILES */
+    free ((void *) domain->data);
+
+  free (domain);
+}
+#endif
diff --git a/apps/silcer/intl/localcharset.c b/apps/silcer/intl/localcharset.c
new file mode 100644 (file)
index 0000000..61f8f3e
--- /dev/null
@@ -0,0 +1,271 @@
+/* Determine a canonical name for the current locale's character encoding.
+
+   Copyright (C) 2000-2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published
+   by the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+/* Written by Bruno Haible <haible@clisp.cons.org>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+
+#include <stdio.h>
+#if HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#if defined _WIN32 || defined __WIN32__
+# undef WIN32   /* avoid warning on mingw32 */
+# define WIN32
+#endif
+
+#ifndef WIN32
+# if HAVE_LANGINFO_CODESET
+#  include <langinfo.h>
+# else
+#  if HAVE_SETLOCALE
+#   include <locale.h>
+#  endif
+# endif
+#else /* WIN32 */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+#ifndef DIRECTORY_SEPARATOR
+# define DIRECTORY_SEPARATOR '/'
+#endif
+
+#ifndef ISSLASH
+# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
+#endif
+
+/* The following static variable is declared 'volatile' to avoid a
+   possible multithread problem in the function get_charset_aliases. If we
+   are running in a threaded environment, and if two threads initialize
+   'charset_aliases' simultaneously, both will produce the same value,
+   and everything will be ok if the two assignments to 'charset_aliases'
+   are atomic. But I don't know what will happen if the two assignments mix.  */
+#if __STDC__ != 1
+# define volatile /* empty */
+#endif
+/* Pointer to the contents of the charset.alias file, if it has already been
+   read, else NULL.  Its format is:
+   ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0'  */
+static const char * volatile charset_aliases;
+
+/* Return a pointer to the contents of the charset.alias file.  */
+static const char *
+get_charset_aliases ()
+{
+  const char *cp;
+
+  cp = charset_aliases;
+  if (cp == NULL)
+    {
+#ifndef WIN32
+      FILE *fp;
+      const char *dir = LIBDIR;
+      const char *base = "charset.alias";
+      char *file_name;
+
+      /* Concatenate dir and base into freshly allocated file_name.  */
+      {
+       size_t dir_len = strlen (dir);
+       size_t base_len = strlen (base);
+       int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1]));
+       file_name = (char *) malloc (dir_len + add_slash + base_len + 1);
+       if (file_name != NULL)
+         {
+           memcpy (file_name, dir, dir_len);
+           if (add_slash)
+             file_name[dir_len] = DIRECTORY_SEPARATOR;
+           memcpy (file_name + dir_len + add_slash, base, base_len + 1);
+         }
+      }
+
+      if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL)
+       /* Out of memory or file not found, treat it as empty.  */
+       cp = "";
+      else
+       {
+         /* Parse the file's contents.  */
+         int c;
+         char buf1[50+1];
+         char buf2[50+1];
+         char *res_ptr = NULL;
+         size_t res_size = 0;
+         size_t l1, l2;
+
+         for (;;)
+           {
+             c = getc (fp);
+             if (c == EOF)
+               break;
+             if (c == '\n' || c == ' ' || c == '\t')
+               continue;
+             if (c == '#')
+               {
+                 /* Skip comment, to end of line.  */
+                 do
+                   c = getc (fp);
+                 while (!(c == EOF || c == '\n'));
+                 if (c == EOF)
+                   break;
+                 continue;
+               }
+             ungetc (c, fp);
+             if (fscanf(fp, "%50s %50s", buf1, buf2) < 2)
+               break;
+             l1 = strlen (buf1);
+             l2 = strlen (buf2);
+             if (res_size == 0)
+               {
+                 res_size = l1 + 1 + l2 + 1;
+                 res_ptr = malloc (res_size + 1);
+               }
+             else
+               {
+                 res_size += l1 + 1 + l2 + 1;
+                 res_ptr = realloc (res_ptr, res_size + 1);
+               }
+             if (res_ptr == NULL)
+               {
+                 /* Out of memory. */
+                 res_size = 0;
+                 break;
+               }
+             strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
+             strcpy (res_ptr + res_size - (l2 + 1), buf2);
+           }
+         fclose (fp);
+         if (res_size == 0)
+           cp = "";
+         else
+           {
+             *(res_ptr + res_size) = '\0';
+             cp = res_ptr;
+           }
+       }
+
+      if (file_name != NULL)
+       free (file_name);
+
+#else /* WIN32 */
+
+      /* To avoid the troubles of installing a separate file in the same
+        directory as the DLL and of retrieving the DLL's directory at
+        runtime, simply inline the aliases here.  */
+
+      cp = "CP936" "\0" "GBK" "\0"
+          "CP1361" "\0" "JOHAB" "\0";
+#endif
+
+      charset_aliases = cp;
+    }
+
+  return cp;
+}
+
+/* Determine the current locale's character encoding, and canonicalize it
+   into one of the canonical names listed in config.charset.
+   The result must not be freed; it is statically allocated.
+   If the canonical name cannot be determined, the result is a non-canonical
+   name.  */
+
+#ifdef STATIC
+STATIC
+#endif
+const char *
+locale_charset ()
+{
+  const char *codeset;
+  const char *aliases;
+
+#ifndef WIN32
+
+# if HAVE_LANGINFO_CODESET
+
+  /* Most systems support nl_langinfo (CODESET) nowadays.  */
+  codeset = nl_langinfo (CODESET);
+
+# else
+
+  /* On old systems which lack it, use setlocale or getenv.  */
+  const char *locale = NULL;
+
+  /* But most old systems don't have a complete set of locales.  Some
+     (like SunOS 4 or DJGPP) have only the C locale.  Therefore we don't
+     use setlocale here; it would return "C" when it doesn't support the
+     locale name the user has set.  */
+#  if HAVE_SETLOCALE && 0
+  locale = setlocale (LC_CTYPE, NULL);
+#  endif
+  if (locale == NULL || locale[0] == '\0')
+    {
+      locale = getenv ("LC_ALL");
+      if (locale == NULL || locale[0] == '\0')
+       {
+         locale = getenv ("LC_CTYPE");
+         if (locale == NULL || locale[0] == '\0')
+           locale = getenv ("LANG");
+       }
+    }
+
+  /* On some old systems, one used to set locale = "iso8859_1". On others,
+     you set it to "language_COUNTRY.charset". In any case, we resolve it
+     through the charset.alias file.  */
+  codeset = locale;
+
+# endif
+
+#else /* WIN32 */
+
+  static char buf[2 + 10 + 1];
+
+  /* Win32 has a function returning the locale's codepage as a number.  */
+  sprintf (buf, "CP%u", GetACP ());
+  codeset = buf;
+
+#endif
+
+  if (codeset == NULL)
+    /* The canonical name cannot be determined.  */
+    codeset = "";
+
+  /* Resolve alias. */
+  for (aliases = get_charset_aliases ();
+       *aliases != '\0';
+       aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
+    if (strcmp (codeset, aliases) == 0
+       || (aliases[0] == '*' && aliases[1] == '\0'))
+      {
+       codeset = aliases + strlen (aliases) + 1;
+       break;
+      }
+
+  return codeset;
+}
diff --git a/apps/silcer/intl/locale.alias b/apps/silcer/intl/locale.alias
new file mode 100644 (file)
index 0000000..48940f7
--- /dev/null
@@ -0,0 +1,77 @@
+# Locale name alias data base.
+# Copyright (C) 1996,1997,1998,1999,2000,2001 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# The format of this file is the same as for the corresponding file of
+# the X Window System, which normally can be found in
+#      /usr/lib/X11/locale/locale.alias
+# A single line contains two fields: an alias and a substitution value.
+# All entries are case independent.
+
+# Note: This file is far from being complete.  If you have a value for
+# your own site which you think might be useful for others too, share
+# it with the rest of us.  Send it using the `glibcbug' script to
+# bugs@gnu.org.
+
+# Packages using this file: 
+
+bokmal         no_NO.ISO-8859-1
+bokmÃ¥l         no_NO.ISO-8859-1
+catalan                ca_ES.ISO-8859-1
+croatian       hr_HR.ISO-8859-2
+czech          cs_CZ.ISO-8859-2
+danish          da_DK.ISO-8859-1
+dansk          da_DK.ISO-8859-1
+deutsch                de_DE.ISO-8859-1
+dutch          nl_NL.ISO-8859-1
+eesti          et_EE.ISO-8859-1
+estonian       et_EE.ISO-8859-1
+finnish         fi_FI.ISO-8859-1
+français       fr_FR.ISO-8859-1
+french         fr_FR.ISO-8859-1
+galego         gl_ES.ISO-8859-1
+galician       gl_ES.ISO-8859-1
+german         de_DE.ISO-8859-1
+greek           el_GR.ISO-8859-7
+hebrew          iw_IL.ISO-8859-8
+hrvatski       hr_HR.ISO-8859-2
+hungarian       hu_HU.ISO-8859-2
+icelandic       is_IS.ISO-8859-1
+italian         it_IT.ISO-8859-1
+japanese       ja_JP.eucJP
+japanese.euc   ja_JP.eucJP
+ja_JP          ja_JP.eucJP
+ja_JP.ujis     ja_JP.eucJP
+japanese.sjis  ja_JP.SJIS
+korean         ko_KR.eucKR
+korean.euc     ko_KR.eucKR
+ko_KR          ko_KR.eucKR
+lithuanian      lt_LT.ISO-8859-13
+nb_NO          no_NO.ISO-8859-1
+nb_NO.ISO-8859-1 no_NO.ISO-8859-1
+norwegian       no_NO.ISO-8859-1
+nynorsk                nn_NO.ISO-8859-1
+polish          pl_PL.ISO-8859-2
+portuguese      pt_PT.ISO-8859-1
+romanian        ro_RO.ISO-8859-2
+russian         ru_RU.ISO-8859-5
+slovak          sk_SK.ISO-8859-2
+slovene         sl_SI.ISO-8859-2
+slovenian       sl_SI.ISO-8859-2
+spanish         es_ES.ISO-8859-1
+swedish         sv_SE.ISO-8859-1
+thai           th_TH.TIS-620
+turkish         tr_TR.ISO-8859-9
diff --git a/apps/silcer/intl/localealias.c b/apps/silcer/intl/localealias.c
new file mode 100644 (file)
index 0000000..76f19a9
--- /dev/null
@@ -0,0 +1,403 @@
+/* Handle aliases for locale names.
+   Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Tell glibc's <string.h> to provide a prototype for mempcpy().
+   This must come before <config.h> because <config.h> may include
+   <features.h>, and once <features.h> has been included, it's too late.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE    1
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+# define HAVE_ALLOCA 1
+#else
+# if defined HAVE_ALLOCA_H || defined _LIBC
+#  include <alloca.h>
+# else
+#  ifdef _AIX
+ #pragma alloca
+#  else
+#   ifndef alloca
+char *alloca ();
+#   endif
+#  endif
+# endif
+#endif
+
+#include <stdlib.h>
+
+#include <string.h>
+#if !HAVE_STRCHR && !defined _LIBC
+# ifndef strchr
+#  define strchr index
+# endif
+#endif
+
+#include "gettextP.h"
+
+/* @@ end of prolog @@ */
+
+#ifdef _LIBC
+/* Rename the non ANSI C functions.  This is required by the standard
+   because some ANSI C functions will require linking with this object
+   file and the name space must not be polluted.  */
+# define strcasecmp __strcasecmp
+
+# ifndef mempcpy
+#  define mempcpy __mempcpy
+# endif
+# define HAVE_MEMPCPY  1
+
+/* We need locking here since we can be called from different places.  */
+# include <bits/libc-lock.h>
+
+__libc_lock_define_initialized (static, lock);
+#endif
+
+#ifndef internal_function
+# define internal_function
+#endif
+
+/* For those losing systems which don't have `alloca' we have to add
+   some additional code emulating it.  */
+#ifdef HAVE_ALLOCA
+# define freea(p) /* nothing */
+#else
+# define alloca(n) malloc (n)
+# define freea(p) free (p)
+#endif
+
+#if defined _LIBC_REENTRANT || defined HAVE_FGETS_UNLOCKED
+# undef fgets
+# define fgets(buf, len, s) fgets_unlocked (buf, len, s)
+#endif
+#if defined _LIBC_REENTRANT || defined HAVE_FEOF_UNLOCKED
+# undef feof
+# define feof(s) feof_unlocked (s)
+#endif
+
+
+struct alias_map
+{
+  const char *alias;
+  const char *value;
+};
+
+
+static char *string_space;
+static size_t string_space_act;
+static size_t string_space_max;
+static struct alias_map *map;
+static size_t nmap;
+static size_t maxmap;
+
+
+/* Prototypes for local functions.  */
+static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
+     internal_function;
+static int extend_alias_table PARAMS ((void));
+static int alias_compare PARAMS ((const struct alias_map *map1,
+                                 const struct alias_map *map2));
+
+
+const char *
+_nl_expand_alias (name)
+    const char *name;
+{
+  static const char *locale_alias_path = LOCALE_ALIAS_PATH;
+  struct alias_map *retval;
+  const char *result = NULL;
+  size_t added;
+
+#ifdef _LIBC
+  __libc_lock_lock (lock);
+#endif
+
+  do
+    {
+      struct alias_map item;
+
+      item.alias = name;
+
+      if (nmap > 0)
+       retval = (struct alias_map *) bsearch (&item, map, nmap,
+                                              sizeof (struct alias_map),
+                                              (int (*) PARAMS ((const void *,
+                                                                const void *))
+                                               ) alias_compare);
+      else
+       retval = NULL;
+
+      /* We really found an alias.  Return the value.  */
+      if (retval != NULL)
+       {
+         result = retval->value;
+         break;
+       }
+
+      /* Perhaps we can find another alias file.  */
+      added = 0;
+      while (added == 0 && locale_alias_path[0] != '\0')
+       {
+         const char *start;
+
+         while (locale_alias_path[0] == PATH_SEPARATOR)
+           ++locale_alias_path;
+         start = locale_alias_path;
+
+         while (locale_alias_path[0] != '\0'
+                && locale_alias_path[0] != PATH_SEPARATOR)
+           ++locale_alias_path;
+
+         if (start < locale_alias_path)
+           added = read_alias_file (start, locale_alias_path - start);
+       }
+    }
+  while (added != 0);
+
+#ifdef _LIBC
+  __libc_lock_unlock (lock);
+#endif
+
+  return result;
+}
+
+
+static size_t
+internal_function
+read_alias_file (fname, fname_len)
+     const char *fname;
+     int fname_len;
+{
+  FILE *fp;
+  char *full_fname;
+  size_t added;
+  static const char aliasfile[] = "/locale.alias";
+
+  full_fname = (char *) alloca (fname_len + sizeof aliasfile);
+#ifdef HAVE_MEMPCPY
+  mempcpy (mempcpy (full_fname, fname, fname_len),
+          aliasfile, sizeof aliasfile);
+#else
+  memcpy (full_fname, fname, fname_len);
+  memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
+#endif
+
+  fp = fopen (full_fname, "r");
+  freea (full_fname);
+  if (fp == NULL)
+    return 0;
+
+  added = 0;
+  while (!feof (fp))
+    {
+      /* It is a reasonable approach to use a fix buffer here because
+        a) we are only interested in the first two fields
+        b) these fields must be usable as file names and so must not
+           be that long
+       */
+      char buf[BUFSIZ];
+      char *alias;
+      char *value;
+      char *cp;
+
+      if (fgets (buf, sizeof buf, fp) == NULL)
+       /* EOF reached.  */
+       break;
+
+      /* Possibly not the whole line fits into the buffer.  Ignore
+        the rest of the line.  */
+      if (strchr (buf, '\n') == NULL)
+       {
+         char altbuf[BUFSIZ];
+         do
+           if (fgets (altbuf, sizeof altbuf, fp) == NULL)
+             /* Make sure the inner loop will be left.  The outer loop
+                will exit at the `feof' test.  */
+             break;
+         while (strchr (altbuf, '\n') == NULL);
+       }
+
+      cp = buf;
+      /* Ignore leading white space.  */
+      while (isspace (cp[0]))
+       ++cp;
+
+      /* A leading '#' signals a comment line.  */
+      if (cp[0] != '\0' && cp[0] != '#')
+       {
+         alias = cp++;
+         while (cp[0] != '\0' && !isspace (cp[0]))
+           ++cp;
+         /* Terminate alias name.  */
+         if (cp[0] != '\0')
+           *cp++ = '\0';
+
+         /* Now look for the beginning of the value.  */
+         while (isspace (cp[0]))
+           ++cp;
+
+         if (cp[0] != '\0')
+           {
+             size_t alias_len;
+             size_t value_len;
+
+             value = cp++;
+             while (cp[0] != '\0' && !isspace (cp[0]))
+               ++cp;
+             /* Terminate value.  */
+             if (cp[0] == '\n')
+               {
+                 /* This has to be done to make the following test
+                    for the end of line possible.  We are looking for
+                    the terminating '\n' which do not overwrite here.  */
+                 *cp++ = '\0';
+                 *cp = '\n';
+               }
+             else if (cp[0] != '\0')
+               *cp++ = '\0';
+
+             if (nmap >= maxmap)
+               if (__builtin_expect (extend_alias_table (), 0))
+                 return added;
+
+             alias_len = strlen (alias) + 1;
+             value_len = strlen (value) + 1;
+
+             if (string_space_act + alias_len + value_len > string_space_max)
+               {
+                 /* Increase size of memory pool.  */
+                 size_t new_size = (string_space_max
+                                    + (alias_len + value_len > 1024
+                                       ? alias_len + value_len : 1024));
+                 char *new_pool = (char *) realloc (string_space, new_size);
+                 if (new_pool == NULL)
+                   return added;
+
+                 if (__builtin_expect (string_space != new_pool, 0))
+                   {
+                     size_t i;
+
+                     for (i = 0; i < nmap; i++)
+                       {
+                         map[i].alias += new_pool - string_space;
+                         map[i].value += new_pool - string_space;
+                       }
+                   }
+
+                 string_space = new_pool;
+                 string_space_max = new_size;
+               }
+
+             map[nmap].alias = memcpy (&string_space[string_space_act],
+                                       alias, alias_len);
+             string_space_act += alias_len;
+
+             map[nmap].value = memcpy (&string_space[string_space_act],
+                                       value, value_len);
+             string_space_act += value_len;
+
+             ++nmap;
+             ++added;
+           }
+       }
+    }
+
+  /* Should we test for ferror()?  I think we have to silently ignore
+     errors.  --drepper  */
+  fclose (fp);
+
+  if (added > 0)
+    qsort (map, nmap, sizeof (struct alias_map),
+          (int (*) PARAMS ((const void *, const void *))) alias_compare);
+
+  return added;
+}
+
+
+static int
+extend_alias_table ()
+{
+  size_t new_size;
+  struct alias_map *new_map;
+
+  new_size = maxmap == 0 ? 100 : 2 * maxmap;
+  new_map = (struct alias_map *) realloc (map, (new_size
+                                               * sizeof (struct alias_map)));
+  if (new_map == NULL)
+    /* Simply don't extend: we don't have any more core.  */
+    return -1;
+
+  map = new_map;
+  maxmap = new_size;
+  return 0;
+}
+
+
+#ifdef _LIBC
+static void __attribute__ ((unused))
+free_mem (void)
+{
+  if (string_space != NULL)
+    free (string_space);
+  if (map != NULL)
+    free (map);
+}
+text_set_element (__libc_subfreeres, free_mem);
+#endif
+
+
+static int
+alias_compare (map1, map2)
+     const struct alias_map *map1;
+     const struct alias_map *map2;
+{
+#if defined _LIBC || defined HAVE_STRCASECMP
+  return strcasecmp (map1->alias, map2->alias);
+#else
+  const unsigned char *p1 = (const unsigned char *) map1->alias;
+  const unsigned char *p2 = (const unsigned char *) map2->alias;
+  unsigned char c1, c2;
+
+  if (p1 == p2)
+    return 0;
+
+  do
+    {
+      /* I know this seems to be odd but the tolower() function in
+        some systems libc cannot handle nonalpha characters.  */
+      c1 = isupper (*p1) ? tolower (*p1) : *p1;
+      c2 = isupper (*p2) ? tolower (*p2) : *p2;
+      if (c1 == '\0')
+       break;
+      ++p1;
+      ++p2;
+    }
+  while (c1 == c2);
+
+  return c1 - c2;
+#endif
+}
diff --git a/apps/silcer/intl/ngettext.c b/apps/silcer/intl/ngettext.c
new file mode 100644 (file)
index 0000000..8b1fa02
--- /dev/null
@@ -0,0 +1,67 @@
+/* Implementation of ngettext(3) function.
+   Copyright (C) 1995, 1997, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef _LIBC
+# define __need_NULL
+# include <stddef.h>
+#else
+# include <stdlib.h>           /* Just for NULL.  */
+#endif
+
+#include "gettextP.h"
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+
+#include <locale.h>
+
+/* @@ end of prolog @@ */
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define NGETTEXT __ngettext
+# define DCNGETTEXT __dcngettext
+#else
+# define NGETTEXT ngettext__
+# define DCNGETTEXT dcngettext__
+#endif
+
+/* Look up MSGID in the current default message catalog for the current
+   LC_MESSAGES locale.  If not found, returns MSGID itself (the default
+   text).  */
+char *
+NGETTEXT (msgid1, msgid2, n)
+     const char *msgid1;
+     const char *msgid2;
+     unsigned long int n;
+{
+  return DCNGETTEXT (NULL, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__ngettext, ngettext);
+#endif
diff --git a/apps/silcer/intl/plural.c b/apps/silcer/intl/plural.c
new file mode 100644 (file)
index 0000000..8191335
--- /dev/null
@@ -0,0 +1,1325 @@
+
+/*  A Bison parser, made from plural.y
+    by GNU Bison version 1.28  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+#define yyparse __gettextparse
+#define yylex __gettextlex
+#define yyerror __gettexterror
+#define yylval __gettextlval
+#define yychar __gettextchar
+#define yydebug __gettextdebug
+#define yynerrs __gettextnerrs
+#define        EQUOP2  257
+#define        CMPOP2  258
+#define        ADDOP2  259
+#define        MULOP2  260
+#define        NUMBER  261
+
+#line 1 "plural.y"
+
+/* Expression parsing for plural form selection.
+   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+   Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* The bison generated parser uses alloca.  AIX 3 forces us to put this
+   declaration at the beginning of the file.  The declaration in bison's
+   skeleton file comes too late.  This must come before <config.h>
+   because <config.h> may include arbitrary system headers.  */
+#if defined _AIX && !defined __GNUC__
+ #pragma alloca
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include "gettextP.h"
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define FREE_EXPRESSION __gettext_free_exp
+#else
+# define FREE_EXPRESSION gettext_free_exp__
+# define __gettextparse gettextparse__
+#endif
+
+#define YYLEX_PARAM    &((struct parse_args *) arg)->cp
+#define YYPARSE_PARAM  arg
+
+#line 52 "plural.y"
+typedef union {
+  unsigned long int num;
+  enum operator op;
+  struct expression *exp;
+} YYSTYPE;
+#line 58 "plural.y"
+
+/* Prototypes for local functions.  */
+static struct expression *new_exp PARAMS ((int nargs, enum operator op,
+                                          struct expression * const *args));
+static inline struct expression *new_exp_0 PARAMS ((enum operator op));
+static inline struct expression *new_exp_1 PARAMS ((enum operator op,
+                                                  struct expression *right));
+static struct expression *new_exp_2 PARAMS ((enum operator op,
+                                            struct expression *left,
+                                            struct expression *right));
+static inline struct expression *new_exp_3 PARAMS ((enum operator op,
+                                                  struct expression *bexp,
+                                                  struct expression *tbranch,
+                                                  struct expression *fbranch));
+static int yylex PARAMS ((YYSTYPE *lval, const char **pexp));
+static void yyerror PARAMS ((const char *str));
+
+/* Allocation of expressions.  */
+
+static struct expression *
+new_exp (nargs, op, args)
+     int nargs;
+     enum operator op;
+     struct expression * const *args;
+{
+  int i;
+  struct expression *newp;
+
+  /* If any of the argument could not be malloc'ed, just return NULL.  */
+  for (i = nargs - 1; i >= 0; i--)
+    if (args[i] == NULL)
+      goto fail;
+
+  /* Allocate a new expression.  */
+  newp = (struct expression *) malloc (sizeof (*newp));
+  if (newp != NULL)
+    {
+      newp->nargs = nargs;
+      newp->operation = op;
+      for (i = nargs - 1; i >= 0; i--)
+       newp->val.args[i] = args[i];
+      return newp;
+    }
+
+ fail:
+  for (i = nargs - 1; i >= 0; i--)
+    FREE_EXPRESSION (args[i]);
+
+  return NULL;
+}
+
+static inline struct expression *
+new_exp_0 (op)
+     enum operator op;
+{
+  return new_exp (0, op, NULL);
+}
+
+static inline struct expression *
+new_exp_1 (op, right)
+     enum operator op;
+     struct expression *right;
+{
+  struct expression *args[1];
+
+  args[0] = right;
+  return new_exp (1, op, args);
+}
+
+static struct expression *
+new_exp_2 (op, left, right)
+     enum operator op;
+     struct expression *left;
+     struct expression *right;
+{
+  struct expression *args[2];
+
+  args[0] = left;
+  args[1] = right;
+  return new_exp (2, op, args);
+}
+
+static inline struct expression *
+new_exp_3 (op, bexp, tbranch, fbranch)
+     enum operator op;
+     struct expression *bexp;
+     struct expression *tbranch;
+     struct expression *fbranch;
+{
+  struct expression *args[3];
+
+  args[0] = bexp;
+  args[1] = tbranch;
+  args[2] = fbranch;
+  return new_exp (3, op, args);
+}
+
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define        YYFINAL         27
+#define        YYFLAG          -32768
+#define        YYNTBASE        16
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 261 ? yytranslate[x] : 18)
+
+static const char yytranslate[] = {     0,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,    10,     2,     2,     2,     2,     5,     2,    14,
+    15,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,    12,     2,     2,
+     2,     2,     3,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,    13,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     4,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     1,     6,     7,     8,     9,
+    11
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = {     0,
+     0,     2,     8,    12,    16,    20,    24,    28,    32,    35,
+    37,    39
+};
+
+static const short yyrhs[] = {    17,
+     0,    17,     3,    17,    12,    17,     0,    17,     4,    17,
+     0,    17,     5,    17,     0,    17,     6,    17,     0,    17,
+     7,    17,     0,    17,     8,    17,     0,    17,     9,    17,
+     0,    10,    17,     0,    13,     0,    11,     0,    14,    17,
+    15,     0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+   177,   185,   189,   193,   197,   201,   205,   209,   213,   217,
+   221,   226
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = {   "$","error","$undefined.","'?'","'|'",
+"'&'","EQUOP2","CMPOP2","ADDOP2","MULOP2","'!'","NUMBER","':'","'n'","'('","')'",
+"start","exp", NULL
+};
+#endif
+
+static const short yyr1[] = {     0,
+    16,    17,    17,    17,    17,    17,    17,    17,    17,    17,
+    17,    17
+};
+
+static const short yyr2[] = {     0,
+     1,     5,     3,     3,     3,     3,     3,     3,     2,     1,
+     1,     3
+};
+
+static const short yydefact[] = {     0,
+     0,    11,    10,     0,     1,     9,     0,     0,     0,     0,
+     0,     0,     0,     0,    12,     0,     3,     4,     5,     6,
+     7,     8,     0,     2,     0,     0,     0
+};
+
+static const short yydefgoto[] = {    25,
+     5
+};
+
+static const short yypact[] = {    -9,
+    -9,-32768,-32768,    -9,    34,-32768,    11,    -9,    -9,    -9,
+    -9,    -9,    -9,    -9,-32768,    24,    39,    43,    16,    26,
+    -3,-32768,    -9,    34,    21,    53,-32768
+};
+
+static const short yypgoto[] = {-32768,
+    -1
+};
+
+
+#define        YYLAST          53
+
+
+static const short yytable[] = {     6,
+     1,     2,     7,     3,     4,    14,    16,    17,    18,    19,
+    20,    21,    22,     8,     9,    10,    11,    12,    13,    14,
+    26,    24,    12,    13,    14,    15,     8,     9,    10,    11,
+    12,    13,    14,    13,    14,    23,     8,     9,    10,    11,
+    12,    13,    14,    10,    11,    12,    13,    14,    11,    12,
+    13,    14,    27
+};
+
+static const short yycheck[] = {     1,
+    10,    11,     4,    13,    14,     9,     8,     9,    10,    11,
+    12,    13,    14,     3,     4,     5,     6,     7,     8,     9,
+     0,    23,     7,     8,     9,    15,     3,     4,     5,     6,
+     7,     8,     9,     8,     9,    12,     3,     4,     5,     6,
+     7,     8,     9,     5,     6,     7,     8,     9,     6,     7,
+     8,     9,     0
+};
+#define YYPURE 1
+
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+#line 3 "/home/haible/gnu/arch/linuxlibc6/share/bison.simple"
+/* This file comes from bison-1.28.  */
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C.  */
+/* This used to test MSDOS, but that is a bad idea
+   since that symbol is in the user namespace.  */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+        instead, just don't use alloca.  */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+   So I turned it off.   rms, 2 May 1997.  */
+/* #include <malloc.h>  */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up,
+                and on HPUX 10.  Eventually we can turn this on.  */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                -2
+#define YYEOF          0
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT        goto yyabortlab
+#define YYERROR                goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL         goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    { yychar = (token), yylval = (value);                      \
+      yychar1 = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK;                                              \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    { yyerror ("syntax error: cannot back up"); YYERROR; }     \
+while (0)
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+#ifndef YYPURE
+#define YYLEX          yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX          yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX          yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX          yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX          yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int    yychar;                 /*  the lookahead symbol                */
+YYSTYPE        yylval;                 /*  the semantic value of the           */
+                               /*  lookahead symbol                    */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;                        /*  location data for the lookahead     */
+                               /*  symbol                              */
+#endif
+
+int yynerrs;                   /*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;                   /*  nonzero means print parse trace     */
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks      */
+
+#ifndef        YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+\f
+/* Define __yy_memcpy.  Note that the size argument
+   should be passed with type unsigned int, because that is what the non-GCC
+   definitions require.  With GCC, __builtin_memcpy takes an arg
+   of type size_t, but it can handle unsigned int.  */
+
+#if __GNUC__ > 1               /* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(TO,FROM,COUNT)     __builtin_memcpy(TO,FROM,COUNT)
+#else                          /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (to, from, count)
+     char *to;
+     char *from;
+     unsigned int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+  register char *t = to;
+  register char *f = from;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+\f
+#line 217 "/home/haible/gnu/arch/linuxlibc6/share/bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;     /*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;             /*  lookahead token as an internal (translated) token number */
+
+  short        yyssa[YYINITDEPTH];     /*  the state stack                     */
+  YYSTYPE yyvsa[YYINITDEPTH];  /*  the semantic value stack            */
+
+  short *yyss = yyssa;         /*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;       /*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];  /*  the location stack                  */
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+  int yyfree_stacks = 0;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;               /*  the variable used to return         */
+                               /*  semantic values from the action     */
+                               /*  routines                            */
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+        the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+        but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+                &yyss1, size * sizeof (*yyssp),
+                &yyvs1, size * sizeof (*yyvsp),
+                &yyls1, size * sizeof (*yylsp),
+                &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+                &yyss1, size * sizeof (*yyssp),
+                &yyvs1, size * sizeof (*yyvsp),
+                &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+       {
+         yyerror("parser stack overflow");
+         if (yyfree_stacks)
+           {
+             free (yyss);
+             free (yyvs);
+#ifdef YYLSP_NEEDED
+             free (yyls);
+#endif
+           }
+         return 2;
+       }
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+       yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+      yyfree_stacks = 1;
+#endif
+      yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss, (char *)yyss1,
+                  size * (unsigned int) sizeof (*yyssp));
+      yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+                  size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls, (char *)yyls1,
+                  size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+       YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)             /* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;          /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+       {
+         fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+         /* Give the individual parser a way to print the precise meaning
+            of a token, for further debugging info.  */
+#ifdef YYPRINT
+         YYPRINT (stderr, yychar, yylval);
+#endif
+         fprintf (stderr, ")\n");
+       }
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+              yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+       fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+
+  switch (yyn) {
+
+case 1:
+#line 178 "plural.y"
+{
+           if (yyvsp[0].exp == NULL)
+             YYABORT;
+           ((struct parse_args *) arg)->res = yyvsp[0].exp;
+         ;
+    break;}
+case 2:
+#line 186 "plural.y"
+{
+           yyval.exp = new_exp_3 (qmop, yyvsp[-4].exp, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 3:
+#line 190 "plural.y"
+{
+           yyval.exp = new_exp_2 (lor, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 4:
+#line 194 "plural.y"
+{
+           yyval.exp = new_exp_2 (land, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 5:
+#line 198 "plural.y"
+{
+           yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 6:
+#line 202 "plural.y"
+{
+           yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 7:
+#line 206 "plural.y"
+{
+           yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 8:
+#line 210 "plural.y"
+{
+           yyval.exp = new_exp_2 (yyvsp[-1].op, yyvsp[-2].exp, yyvsp[0].exp);
+         ;
+    break;}
+case 9:
+#line 214 "plural.y"
+{
+           yyval.exp = new_exp_1 (lnot, yyvsp[0].exp);
+         ;
+    break;}
+case 10:
+#line 218 "plural.y"
+{
+           yyval.exp = new_exp_0 (var);
+         ;
+    break;}
+case 11:
+#line 222 "plural.y"
+{
+           if ((yyval.exp = new_exp_0 (num)) != NULL)
+             yyval.exp->val.num = yyvsp[0].num;
+         ;
+    break;}
+case 12:
+#line 227 "plural.y"
+{
+           yyval.exp = yyvsp[-1].exp;
+         ;
+    break;}
+}
+   /* the action file gets copied in in place of this dollarsign */
+#line 543 "/home/haible/gnu/arch/linuxlibc6/share/bison.simple"
+\f
+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+       fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+       {
+         int size = 0;
+         char *msg;
+         int x, count;
+
+         count = 0;
+         /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+         for (x = (yyn < 0 ? -yyn : 0);
+              x < (sizeof(yytname) / sizeof(char *)); x++)
+           if (yycheck[x + yyn] == x)
+             size += strlen(yytname[x]) + 15, count++;
+         msg = (char *) malloc(size + 15);
+         if (msg != 0)
+           {
+             strcpy(msg, "parse error");
+
+             if (count < 5)
+               {
+                 count = 0;
+                 for (x = (yyn < 0 ? -yyn : 0);
+                      x < (sizeof(yytname) / sizeof(char *)); x++)
+                   if (yycheck[x + yyn] == x)
+                     {
+                       strcat(msg, count == 0 ? ", expecting `" : " or `");
+                       strcat(msg, yytname[x]);
+                       strcat(msg, "'");
+                       count++;
+                     }
+               }
+             yyerror(msg);
+             free(msg);
+           }
+         else
+           yyerror ("parse error; also virtual memory exceeded");
+       }
+      else
+#endif /* YYERROR_VERBOSE */
+       yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+       YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+       fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;             /* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+       fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+       goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+ yyacceptlab:
+  /* YYACCEPT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 0;
+
+ yyabortlab:
+  /* YYABORT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 1;
+}
+#line 232 "plural.y"
+
+
+void
+internal_function
+FREE_EXPRESSION (exp)
+     struct expression *exp;
+{
+  if (exp == NULL)
+    return;
+
+  /* Handle the recursive case.  */
+  switch (exp->nargs)
+    {
+    case 3:
+      FREE_EXPRESSION (exp->val.args[2]);
+      /* FALLTHROUGH */
+    case 2:
+      FREE_EXPRESSION (exp->val.args[1]);
+      /* FALLTHROUGH */
+    case 1:
+      FREE_EXPRESSION (exp->val.args[0]);
+      /* FALLTHROUGH */
+    default:
+      break;
+    }
+
+  free (exp);
+}
+
+
+static int
+yylex (lval, pexp)
+     YYSTYPE *lval;
+     const char **pexp;
+{
+  const char *exp = *pexp;
+  int result;
+
+  while (1)
+    {
+      if (exp[0] == '\0')
+       {
+         *pexp = exp;
+         return YYEOF;
+       }
+
+      if (exp[0] != ' ' && exp[0] != '\t')
+       break;
+
+      ++exp;
+    }
+
+  result = *exp++;
+  switch (result)
+    {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      {
+       unsigned long int n = result - '0';
+       while (exp[0] >= '0' && exp[0] <= '9')
+         {
+           n *= 10;
+           n += exp[0] - '0';
+           ++exp;
+         }
+       lval->num = n;
+       result = NUMBER;
+      }
+      break;
+
+    case '=':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = equal;
+         result = EQUOP2;
+       }
+      else
+       result = YYERRCODE;
+      break;
+
+    case '!':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = not_equal;
+         result = EQUOP2;
+       }
+      break;
+
+    case '&':
+    case '|':
+      if (exp[0] == result)
+       ++exp;
+      else
+       result = YYERRCODE;
+      break;
+
+    case '<':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = less_or_equal;
+       }
+      else
+       lval->op = less_than;
+      result = CMPOP2;
+      break;
+
+    case '>':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = greater_or_equal;
+       }
+      else
+       lval->op = greater_than;
+      result = CMPOP2;
+      break;
+
+    case '*':
+      lval->op = mult;
+      result = MULOP2;
+      break;
+
+    case '/':
+      lval->op = divide;
+      result = MULOP2;
+      break;
+
+    case '%':
+      lval->op = module;
+      result = MULOP2;
+      break;
+
+    case '+':
+      lval->op = plus;
+      result = ADDOP2;
+      break;
+
+    case '-':
+      lval->op = minus;
+      result = ADDOP2;
+      break;
+
+    case 'n':
+    case '?':
+    case ':':
+    case '(':
+    case ')':
+      /* Nothing, just return the character.  */
+      break;
+
+    case ';':
+    case '\n':
+    case '\0':
+      /* Be safe and let the user call this function again.  */
+      --exp;
+      result = YYEOF;
+      break;
+
+    default:
+      result = YYERRCODE;
+#if YYDEBUG != 0
+      --exp;
+#endif
+      break;
+    }
+
+  *pexp = exp;
+
+  return result;
+}
+
+
+static void
+yyerror (str)
+     const char *str;
+{
+  /* Do nothing.  We don't print error messages here.  */
+}
diff --git a/apps/silcer/intl/plural.y b/apps/silcer/intl/plural.y
new file mode 100644 (file)
index 0000000..42ffa0e
--- /dev/null
@@ -0,0 +1,412 @@
+%{
+/* Expression parsing for plural form selection.
+   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+   Written by Ulrich Drepper <drepper@cygnus.com>, 2000.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* The bison generated parser uses alloca.  AIX 3 forces us to put this
+   declaration at the beginning of the file.  The declaration in bison's
+   skeleton file comes too late.  This must come before <config.h>
+   because <config.h> may include arbitrary system headers.  */
+#if defined _AIX && !defined __GNUC__
+ #pragma alloca
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include "gettextP.h"
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define FREE_EXPRESSION __gettext_free_exp
+#else
+# define FREE_EXPRESSION gettext_free_exp__
+# define __gettextparse gettextparse__
+#endif
+
+#define YYLEX_PARAM    &((struct parse_args *) arg)->cp
+#define YYPARSE_PARAM  arg
+%}
+%pure_parser
+%expect 10
+
+%union {
+  unsigned long int num;
+  enum operator op;
+  struct expression *exp;
+}
+
+%{
+/* Prototypes for local functions.  */
+static struct expression *new_exp PARAMS ((int nargs, enum operator op,
+                                          struct expression * const *args));
+static inline struct expression *new_exp_0 PARAMS ((enum operator op));
+static inline struct expression *new_exp_1 PARAMS ((enum operator op,
+                                                  struct expression *right));
+static struct expression *new_exp_2 PARAMS ((enum operator op,
+                                            struct expression *left,
+                                            struct expression *right));
+static inline struct expression *new_exp_3 PARAMS ((enum operator op,
+                                                  struct expression *bexp,
+                                                  struct expression *tbranch,
+                                                  struct expression *fbranch));
+static int yylex PARAMS ((YYSTYPE *lval, const char **pexp));
+static void yyerror PARAMS ((const char *str));
+
+/* Allocation of expressions.  */
+
+static struct expression *
+new_exp (nargs, op, args)
+     int nargs;
+     enum operator op;
+     struct expression * const *args;
+{
+  int i;
+  struct expression *newp;
+
+  /* If any of the argument could not be malloc'ed, just return NULL.  */
+  for (i = nargs - 1; i >= 0; i--)
+    if (args[i] == NULL)
+      goto fail;
+
+  /* Allocate a new expression.  */
+  newp = (struct expression *) malloc (sizeof (*newp));
+  if (newp != NULL)
+    {
+      newp->nargs = nargs;
+      newp->operation = op;
+      for (i = nargs - 1; i >= 0; i--)
+       newp->val.args[i] = args[i];
+      return newp;
+    }
+
+ fail:
+  for (i = nargs - 1; i >= 0; i--)
+    FREE_EXPRESSION (args[i]);
+
+  return NULL;
+}
+
+static inline struct expression *
+new_exp_0 (op)
+     enum operator op;
+{
+  return new_exp (0, op, NULL);
+}
+
+static inline struct expression *
+new_exp_1 (op, right)
+     enum operator op;
+     struct expression *right;
+{
+  struct expression *args[1];
+
+  args[0] = right;
+  return new_exp (1, op, args);
+}
+
+static struct expression *
+new_exp_2 (op, left, right)
+     enum operator op;
+     struct expression *left;
+     struct expression *right;
+{
+  struct expression *args[2];
+
+  args[0] = left;
+  args[1] = right;
+  return new_exp (2, op, args);
+}
+
+static inline struct expression *
+new_exp_3 (op, bexp, tbranch, fbranch)
+     enum operator op;
+     struct expression *bexp;
+     struct expression *tbranch;
+     struct expression *fbranch;
+{
+  struct expression *args[3];
+
+  args[0] = bexp;
+  args[1] = tbranch;
+  args[2] = fbranch;
+  return new_exp (3, op, args);
+}
+
+%}
+
+/* This declares that all operators have the same associativity and the
+   precedence order as in C.  See [Harbison, Steele: C, A Reference Manual].
+   There is no unary minus and no bitwise operators.
+   Operators with the same syntactic behaviour have been merged into a single
+   token, to save space in the array generated by bison.  */
+%right '?'             /*   ?          */
+%left '|'              /*   ||         */
+%left '&'              /*   &&         */
+%left EQUOP2           /*   == !=      */
+%left CMPOP2           /*   < > <= >=  */
+%left ADDOP2           /*   + -        */
+%left MULOP2           /*   * / %      */
+%right '!'             /*   !          */
+
+%token <op> EQUOP2 CMPOP2 ADDOP2 MULOP2
+%token <num> NUMBER
+%type <exp> exp
+
+%%
+
+start:   exp
+         {
+           if ($1 == NULL)
+             YYABORT;
+           ((struct parse_args *) arg)->res = $1;
+         }
+       ;
+
+exp:     exp '?' exp ':' exp
+         {
+           $$ = new_exp_3 (qmop, $1, $3, $5);
+         }
+       | exp '|' exp
+         {
+           $$ = new_exp_2 (lor, $1, $3);
+         }
+       | exp '&' exp
+         {
+           $$ = new_exp_2 (land, $1, $3);
+         }
+       | exp EQUOP2 exp
+         {
+           $$ = new_exp_2 ($2, $1, $3);
+         }
+       | exp CMPOP2 exp
+         {
+           $$ = new_exp_2 ($2, $1, $3);
+         }
+       | exp ADDOP2 exp
+         {
+           $$ = new_exp_2 ($2, $1, $3);
+         }
+       | exp MULOP2 exp
+         {
+           $$ = new_exp_2 ($2, $1, $3);
+         }
+       | '!' exp
+         {
+           $$ = new_exp_1 (lnot, $2);
+         }
+       | 'n'
+         {
+           $$ = new_exp_0 (var);
+         }
+       | NUMBER
+         {
+           if (($$ = new_exp_0 (num)) != NULL)
+             $$->val.num = $1;
+         }
+       | '(' exp ')'
+         {
+           $$ = $2;
+         }
+       ;
+
+%%
+
+void
+internal_function
+FREE_EXPRESSION (exp)
+     struct expression *exp;
+{
+  if (exp == NULL)
+    return;
+
+  /* Handle the recursive case.  */
+  switch (exp->nargs)
+    {
+    case 3:
+      FREE_EXPRESSION (exp->val.args[2]);
+      /* FALLTHROUGH */
+    case 2:
+      FREE_EXPRESSION (exp->val.args[1]);
+      /* FALLTHROUGH */
+    case 1:
+      FREE_EXPRESSION (exp->val.args[0]);
+      /* FALLTHROUGH */
+    default:
+      break;
+    }
+
+  free (exp);
+}
+
+
+static int
+yylex (lval, pexp)
+     YYSTYPE *lval;
+     const char **pexp;
+{
+  const char *exp = *pexp;
+  int result;
+
+  while (1)
+    {
+      if (exp[0] == '\0')
+       {
+         *pexp = exp;
+         return YYEOF;
+       }
+
+      if (exp[0] != ' ' && exp[0] != '\t')
+       break;
+
+      ++exp;
+    }
+
+  result = *exp++;
+  switch (result)
+    {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      {
+       unsigned long int n = result - '0';
+       while (exp[0] >= '0' && exp[0] <= '9')
+         {
+           n *= 10;
+           n += exp[0] - '0';
+           ++exp;
+         }
+       lval->num = n;
+       result = NUMBER;
+      }
+      break;
+
+    case '=':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = equal;
+         result = EQUOP2;
+       }
+      else
+       result = YYERRCODE;
+      break;
+
+    case '!':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = not_equal;
+         result = EQUOP2;
+       }
+      break;
+
+    case '&':
+    case '|':
+      if (exp[0] == result)
+       ++exp;
+      else
+       result = YYERRCODE;
+      break;
+
+    case '<':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = less_or_equal;
+       }
+      else
+       lval->op = less_than;
+      result = CMPOP2;
+      break;
+
+    case '>':
+      if (exp[0] == '=')
+       {
+         ++exp;
+         lval->op = greater_or_equal;
+       }
+      else
+       lval->op = greater_than;
+      result = CMPOP2;
+      break;
+
+    case '*':
+      lval->op = mult;
+      result = MULOP2;
+      break;
+
+    case '/':
+      lval->op = divide;
+      result = MULOP2;
+      break;
+
+    case '%':
+      lval->op = module;
+      result = MULOP2;
+      break;
+
+    case '+':
+      lval->op = plus;
+      result = ADDOP2;
+      break;
+
+    case '-':
+      lval->op = minus;
+      result = ADDOP2;
+      break;
+
+    case 'n':
+    case '?':
+    case ':':
+    case '(':
+    case ')':
+      /* Nothing, just return the character.  */
+      break;
+
+    case ';':
+    case '\n':
+    case '\0':
+      /* Be safe and let the user call this function again.  */
+      --exp;
+      result = YYEOF;
+      break;
+
+    default:
+      result = YYERRCODE;
+#if YYDEBUG != 0
+      --exp;
+#endif
+      break;
+    }
+
+  *pexp = exp;
+
+  return result;
+}
+
+
+static void
+yyerror (str)
+     const char *str;
+{
+  /* Do nothing.  We don't print error messages here.  */
+}
diff --git a/apps/silcer/intl/ref-add.sin b/apps/silcer/intl/ref-add.sin
new file mode 100644 (file)
index 0000000..167374e
--- /dev/null
@@ -0,0 +1,31 @@
+# Add this package to a list of references stored in a text file.
+#
+#   Copyright (C) 2000 Free Software Foundation, Inc.
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU Library General Public License as published
+#   by the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   Library General Public License for more details.
+#
+#   You should have received a copy of the GNU Library General Public
+#   License along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+#   USA.
+#
+# Written by Bruno Haible <haible@clisp.cons.org>.
+#
+/^# Packages using this file: / {
+  s/# Packages using this file://
+  ta
+  :a
+  s/ @PACKAGE@ / @PACKAGE@ /
+  tb
+  s/ $/ @PACKAGE@ /
+  :b
+  s/^/# Packages using this file:/
+}
diff --git a/apps/silcer/intl/ref-del.sin b/apps/silcer/intl/ref-del.sin
new file mode 100644 (file)
index 0000000..613cf37
--- /dev/null
@@ -0,0 +1,26 @@
+# Remove this package from a list of references stored in a text file.
+#
+#   Copyright (C) 2000 Free Software Foundation, Inc.
+#
+#   This program is free software; you can redistribute it and/or modify it
+#   under the terms of the GNU Library General Public License as published
+#   by the Free Software Foundation; either version 2, or (at your option)
+#   any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   Library General Public License for more details.
+#
+#   You should have received a copy of the GNU Library General Public
+#   License along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+#   USA.
+#
+# Written by Bruno Haible <haible@clisp.cons.org>.
+#
+/^# Packages using this file: / {
+  s/# Packages using this file://
+  s/ @PACKAGE@ / /
+  s/^/# Packages using this file:/
+}
diff --git a/apps/silcer/intl/textdomain.c b/apps/silcer/intl/textdomain.c
new file mode 100644 (file)
index 0000000..05c2fd7
--- /dev/null
@@ -0,0 +1,141 @@
+/* Implementation of the textdomain(3) function.
+   Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "libgnuintl.h"
+#endif
+#include "gettextP.h"
+
+#ifdef _LIBC
+/* We have to handle multi-threaded applications.  */
+# include <bits/libc-lock.h>
+#else
+/* Provide dummy implementation if this is outside glibc.  */
+# define __libc_rwlock_define(CLASS, NAME)
+# define __libc_rwlock_wrlock(NAME)
+# define __libc_rwlock_unlock(NAME)
+#endif
+
+/* The internal variables in the standalone libintl.a must have different
+   names than the internal variables in GNU libc, otherwise programs
+   using libintl.a cannot be linked statically.  */
+#if !defined _LIBC
+# define _nl_default_default_domain _nl_default_default_domain__
+# define _nl_current_default_domain _nl_current_default_domain__
+#endif
+
+/* @@ end of prolog @@ */
+
+/* Name of the default text domain.  */
+extern const char _nl_default_default_domain[];
+
+/* Default text domain in which entries for gettext(3) are to be found.  */
+extern const char *_nl_current_default_domain;
+
+
+/* Names for the libintl functions are a problem.  They must not clash
+   with existing names and they should follow ANSI C.  But this source
+   code is also used in GNU C Library where the names have a __
+   prefix.  So we have to make a difference here.  */
+#ifdef _LIBC
+# define TEXTDOMAIN __textdomain
+# ifndef strdup
+#  define strdup(str) __strdup (str)
+# endif
+#else
+# define TEXTDOMAIN textdomain__
+#endif
+
+/* Lock variable to protect the global data in the gettext implementation.  */
+__libc_rwlock_define (extern, _nl_state_lock)
+
+/* Set the current default message catalog to DOMAINNAME.
+   If DOMAINNAME is null, return the current default.
+   If DOMAINNAME is "", reset to the default of "messages".  */
+char *
+TEXTDOMAIN (domainname)
+     const char *domainname;
+{
+  char *new_domain;
+  char *old_domain;
+
+  /* A NULL pointer requests the current setting.  */
+  if (domainname == NULL)
+    return (char *) _nl_current_default_domain;
+
+  __libc_rwlock_wrlock (_nl_state_lock);
+
+  old_domain = (char *) _nl_current_default_domain;
+
+  /* If domain name is the null string set to default domain "messages".  */
+  if (domainname[0] == '\0'
+      || strcmp (domainname, _nl_default_default_domain) == 0)
+    {
+      _nl_current_default_domain = _nl_default_default_domain;
+      new_domain = (char *) _nl_current_default_domain;
+    }
+  else if (strcmp (domainname, old_domain) == 0)
+    /* This can happen and people will use it to signal that some
+       environment variable changed.  */
+    new_domain = old_domain;
+  else
+    {
+      /* If the following malloc fails `_nl_current_default_domain'
+        will be NULL.  This value will be returned and so signals we
+        are out of core.  */
+#if defined _LIBC || defined HAVE_STRDUP
+      new_domain = strdup (domainname);
+#else
+      size_t len = strlen (domainname) + 1;
+      new_domain = (char *) malloc (len);
+      if (new_domain != NULL)
+       memcpy (new_domain, domainname, len);
+#endif
+
+      if (new_domain != NULL)
+       _nl_current_default_domain = new_domain;
+    }
+
+  /* We use this possibility to signal a change of the loaded catalogs
+     since this is most likely the case and there is no other easy we
+     to do it.  Do it only when the call was successful.  */
+  if (new_domain != NULL)
+    {
+      ++_nl_msg_cat_cntr;
+
+      if (old_domain != new_domain && old_domain != _nl_default_default_domain)
+       free (old_domain);
+    }
+
+  __libc_rwlock_unlock (_nl_state_lock);
+
+  return new_domain;
+}
+
+#ifdef _LIBC
+/* Alias for function name in GNU C Library.  */
+weak_alias (__textdomain, textdomain);
+#endif
diff --git a/apps/silcer/macros/Makefile.am b/apps/silcer/macros/Makefile.am
new file mode 100644 (file)
index 0000000..cb7c185
--- /dev/null
@@ -0,0 +1,42 @@
+## Please update this variable if any new macros are created
+
+MACROS=                                                \
+  aclocal-include.m4                           \
+  compiler-flags.m4                            \
+  curses.m4                                    \
+  gnome-bonobo-check.m4                                \
+  gnome-fileutils.m4                           \
+  gnome-ghttp-check.m4                         \
+  gnome-gnorba-check.m4                                \
+  gnome-guile-checks.m4                                \
+  gnome-libgtop-check.m4                       \
+  gnome-objc-checks.m4                         \
+  gnome-orbit-check.m4                         \
+  gnome-print-check.m4                         \
+  gnome-pthread-check.m4                       \
+  gnome-support.m4                             \
+  gnome-undelfs.m4                             \
+  gnome-vfs.m4                                 \
+  gnome-x-checks.m4                            \
+  gnome-xml-check.m4                           \
+  gnome.m4                                     \
+  gperf-check.m4                               \
+  linger.m4                                    \
+  need-declaration.m4
+
+EXTRA_DIST=$(MACROS) gnome-common.m4 gnome-gettext.m4 autogen.sh
+MAINTAINERCLEANFILES=macros.dep
+
+@MAINT@macros.dep: Makefile.am
+@MAINT@        @echo '$$(top_srcdir)/aclocal.m4: $(MACROS:%=macros/%)' > $@
+
+if INSIDE_GNOME_COMMON
+gnome_aclocaldir = $(datadir)/aclocal/gnome-macros
+
+gnome-macros.dep: Makefile.am
+       @echo '$$(top_srcdir)/aclocal.m4: $(MACROS:%=$(gnome_aclocaldir)/%)' > $@
+
+gnome_aclocal_DATA = $(MACROS) gnome-macros.dep gnome-common.m4 \
+       gnome-gettext.m4 autogen.sh
+
+endif
diff --git a/apps/silcer/macros/aclocal-include.m4 b/apps/silcer/macros/aclocal-include.m4
new file mode 100644 (file)
index 0000000..abf6533
--- /dev/null
@@ -0,0 +1,16 @@
+# aclocal-include.m4
+# 
+# This macro adds the name macrodir to the set of directories
+# that `aclocal' searches for macros.  
+
+# serial 1
+
+dnl AM_ACLOCAL_INCLUDE(macrodir)
+AC_DEFUN([AM_ACLOCAL_INCLUDE],
+[
+       AM_CONDITIONAL(INSIDE_GNOME_COMMON, test x = y)
+
+       test -n "$ACLOCAL_FLAGS" && ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+       for k in $1 ; do ACLOCAL="$ACLOCAL -I $k" ; done
+])
diff --git a/apps/silcer/macros/autogen.sh b/apps/silcer/macros/autogen.sh
new file mode 100644 (file)
index 0000000..43d7d3f
--- /dev/null
@@ -0,0 +1,193 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+DIE=0
+
+if [ -n "$GNOME2_PATH" ]; then
+       ACLOCAL_FLAGS="-I $GNOME2_PATH/share/aclocal $ACLOCAL_FLAGS"
+       PATH="$GNOME2_PATH/bin:$PATH"
+       export PATH
+fi
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`autoconf' installed to compile Gnome."
+  echo "Download the appropriate package for your distribution,"
+  echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+  DIE=1
+}
+
+(grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.in >/dev/null) && {
+  (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || {
+    echo 
+    echo "**Error**: You must have \`xml-i18n-toolize' installed to compile Gnome."
+    echo "Get ftp://ftp.gnome.org/pub/GNOME/stable/sources/xml-i18n-tools/xml-i18n-tools-0.6.tar.gz"
+    echo "(or a newer version if it is available)"
+    DIE=1
+  }
+}
+
+(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && {
+  (libtool --version) < /dev/null > /dev/null 2>&1 || {
+    echo
+    echo "**Error**: You must have \`libtool' installed to compile Gnome."
+    echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
+    echo "(or a newer version if it is available)"
+    DIE=1
+  }
+}
+
+#grep "^AM_GNU_GETTEXT" $srcdir/configure.in >/dev/null && {
+#  grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \
+#  (gettext --version) < /dev/null > /dev/null 2>&1 || {
+#    echo
+#    echo "**Error**: You must have \`gettext' installed to compile Gnome."
+#    echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz"
+#    echo "(or a newer version if it is available)"
+#    DIE=1
+#  }
+#}
+
+#grep "^AM_GNOME_GETTEXT" $srcdir/configure.in >/dev/null && {
+#  grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \
+#  (gettext --version) < /dev/null > /dev/null 2>&1 || {
+#    echo
+#    echo "**Error**: You must have \`gettext' installed to compile Gnome."
+#    echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz"
+#    echo "(or a newer version if it is available)"
+#    DIE=1
+#  }
+#}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: You must have \`automake' installed to compile Gnome."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+  NO_AUTOMAKE=yes
+}
+
+
+# if no automake, don't bother testing for aclocal
+test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || {
+  echo
+  echo "**Error**: Missing \`aclocal'.  The version of \`automake'"
+  echo "installed doesn't appear recent enough."
+  echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
+  echo "(or a newer version if it is available)"
+  DIE=1
+}
+
+if test "$DIE" -eq 1; then
+  exit 1
+fi
+
+if test -z "$*"; then
+  echo "**Warning**: I am going to run \`configure' with no arguments."
+  echo "If you wish to pass any to it, please specify them on the"
+  echo \`$0\'" command line."
+  echo
+fi
+
+case $CC in
+xlc )
+  am_opt=--include-deps;;
+esac
+
+for coin in `find $srcdir -name configure.in -print`
+do 
+  dr=`dirname $coin`
+  if test -f $dr/NO-AUTO-GEN; then
+    echo skipping $dr -- flagged as no auto-gen
+  else
+    echo processing $dr
+    macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin`
+    ( cd $dr
+      macrosdir=`find . -name macros -print`
+      for i in $macrodirs; do
+       if test -f $i/gnome-gettext.m4; then
+         DELETEFILES="$DELETEFILES $i/gnome-gettext.m4"
+       fi
+      done
+
+      echo "deletefiles is $DELETEFILES"
+      aclocalinclude="$ACLOCAL_FLAGS"
+      for k in $aclocalinclude; do
+       if test -d $k; then
+         if [ -f $k/gnome.m4 -a "$GNOME_INTERFACE_VERSION" = "1" ]; then
+           rm -f $DELETEFILES
+         fi
+        fi
+      done
+      for k in $macrodirs; do
+       if test -d $k; then
+          aclocalinclude="$aclocalinclude -I $k"
+         if [ -f $k/gnome.m4 -a "$GNOME_INTERFACE_VERSION" = "1" ]; then
+           rm -f $DELETEFILES
+         fi
+        fi
+      done
+      if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then
+       if grep "sed.*POTFILES" configure.in >/dev/null; then
+         : do nothing -- we still have an old unmodified configure.in
+       else
+         echo "Creating $dr/aclocal.m4 ..."
+         test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+         echo "Running gettextize...  Ignore non-fatal messages."
+         echo "no" | gettextize --force --copy
+         echo "Making $dr/aclocal.m4 writable ..."
+         test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+        fi
+      fi
+      if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then
+       echo "Creating $dr/aclocal.m4 ..."
+       test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
+       echo "Running gettextize...  Ignore non-fatal messages."
+       echo "no" | gettextize --force --copy
+       echo "Making $dr/aclocal.m4 writable ..."
+       test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
+      fi
+      if grep "^AM_PROG_XML_I18N_TOOLS" configure.in >/dev/null; then
+        echo "Running xml-i18n-toolize... Ignore non-fatal messages."
+       xml-i18n-toolize --copy --force --automake
+      fi
+      if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
+       if test -z "$NO_LIBTOOLIZE" ; then 
+         echo "Running libtoolize..."
+         libtoolize --force --copy
+       fi
+      fi
+      echo "Running aclocal $aclocalinclude ..."
+      aclocal $aclocalinclude || {
+       echo
+       echo "**Error**: aclocal failed. This may mean that you have not"
+       echo "installed all of the packages you need, or you may need to"
+       echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\""
+       echo "for the prefix where you installed the packages whose"
+       echo "macros were not found"
+       exit 1
+      }
+
+      if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
+       echo "Running autoheader..."
+       autoheader || { echo "**Error**: autoheader failed."; exit 1; }
+      fi
+      echo "Running automake --gnu $am_opt ..."
+      automake --add-missing --gnu $am_opt ||
+       { echo "**Error**: automake failed."; exit 1; }
+      echo "Running autoconf ..."
+      autoconf || { echo "**Error**: autoconf failed."; exit 1; }
+    ) || exit 1
+  fi
+done
+
+conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
+
+if test x$NOCONFIGURE = x; then
+  echo Running $srcdir/configure $conf_flags "$@" ...
+  $srcdir/configure $conf_flags "$@" \
+  && echo Now type \`make\' to compile $PKG_NAME || exit 1
+else
+  echo Skipping configure process.
+fi
diff --git a/apps/silcer/macros/compiler-flags.m4 b/apps/silcer/macros/compiler-flags.m4
new file mode 100644 (file)
index 0000000..63f8e2e
--- /dev/null
@@ -0,0 +1,109 @@
+dnl GNOME_COMPILE_WARNINGS
+dnl Turn on many useful compiler warnings
+dnl For now, only works on GCC
+AC_DEFUN([GNOME_COMPILE_WARNINGS],[
+  AC_ARG_ENABLE(compile-warnings, 
+    [  --enable-compile-warnings=[no/minimum/yes]      Turn on compiler warnings.],,enable_compile_warnings=minimum)
+
+  AC_MSG_CHECKING(what warning flags to pass to the C compiler)
+  warnCFLAGS=
+  if test "x$GCC" != xyes; then
+    enable_compile_warnings=no
+  fi
+
+  if test "x$enable_compile_warnings" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CFLAGS " in
+      *[\ \    ]-Wall[\ \      ]*) ;;
+      *) warnCFLAGS="-Wall -Wunused" ;;
+      esac
+
+      ## -W is not all that useful.  And it cannot be controlled
+      ## with individual -Wno-xxx flags, unlike -Wall
+      if test "x$enable_compile_warnings" = "xyes"; then
+       warnCFLAGS="$warnCFLAGS -Wmissing-prototypes -Wmissing-declarations"
+      fi
+    fi
+  fi
+  AC_MSG_RESULT($warnCFLAGS)
+
+  AC_ARG_ENABLE(iso-c,
+    [  --enable-iso-c          Try to warn if code is not ISO C ],,
+    enable_iso_c=no)
+
+  AC_MSG_CHECKING(what language compliance flags to pass to the C compiler)
+  complCFLAGS=
+  if test "x$enable_iso_c" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CFLAGS " in
+      *[\ \    ]-ansi[\ \      ]*) ;;
+      *) complCFLAGS="$complCFLAGS -ansi" ;;
+      esac
+
+      case " $CFLAGS " in
+      *[\ \    ]-pedantic[\ \  ]*) ;;
+      *) complCFLAGS="$complCFLAGS -pedantic" ;;
+      esac
+    fi
+  fi
+  AC_MSG_RESULT($complCFLAGS)
+  if test "x$cflags_set" != "xyes"; then
+    CFLAGS="$CFLAGS $warnCFLAGS $complCFLAGS"
+    cflags_set=yes
+    AC_SUBST(cflags_set)
+  fi
+])
+
+dnl For C++, do basically the same thing.
+
+AC_DEFUN([GNOME_CXX_WARNINGS],[
+  AC_ARG_ENABLE(cxx-warnings, 
+    [  --enable-cxx-warnings=[no/minimum/yes]  Turn on compiler warnings.],,enable_cxx_warnings=minimum)
+
+  AC_MSG_CHECKING(what warning flags to pass to the C++ compiler)
+  warnCXXFLAGS=
+  if test "x$GCC" != xyes; then
+    enable_compile_warnings=no
+  fi
+  if test "x$enable_cxx_warnings" != "xno"; then
+    if test "x$GCC" = "xyes"; then
+      case " $CXXFLAGS " in
+      *[\ \    ]-Wall[\ \      ]*) ;;
+      *) warnCXXFLAGS="-Wall -Wno-unused" ;;
+      esac
+
+      ## -W is not all that useful.  And it cannot be controlled
+      ## with individual -Wno-xxx flags, unlike -Wall
+      if test "x$enable_cxx_warnings" = "xyes"; then
+       warnCXXFLAGS="$warnCXXFLAGS -Wmissing-prototypes -Wmissing-declarations -Wshadow -Woverloaded-virtual"
+      fi
+    fi
+  fi
+  AC_MSG_RESULT($warnCXXFLAGS)
+
+   AC_ARG_ENABLE(iso-cxx,
+     [  --enable-iso-cxx          Try to warn if code is not ISO C++ ],,
+     enable_iso_cxx=no)
+
+   AC_MSG_CHECKING(what language compliance flags to pass to the C++ compiler)
+   complCXXFLAGS=
+   if test "x$enable_iso_cxx" != "xno"; then
+     if test "x$GCC" = "xyes"; then
+      case " $CXXFLAGS " in
+      *[\ \    ]-ansi[\ \      ]*) ;;
+      *) complCXXFLAGS="$complCXXFLAGS -ansi" ;;
+      esac
+
+      case " $CXXFLAGS " in
+      *[\ \    ]-pedantic[\ \  ]*) ;;
+      *) complCXXFLAGS="$complCXXFLAGS -pedantic" ;;
+      esac
+     fi
+   fi
+  AC_MSG_RESULT($complCXXFLAGS)
+  if test "x$cxxflags_set" != "xyes"; then
+    CXXFLAGS="$CXXFLAGS $warnCXXFLAGS $complCXXFLAGS"
+    cxxflags_set=yes
+    AC_SUBST(cxxflags_set)
+  fi
+])
diff --git a/apps/silcer/macros/curses.m4 b/apps/silcer/macros/curses.m4
new file mode 100644 (file)
index 0000000..5307e13
--- /dev/null
@@ -0,0 +1,318 @@
+dnl Curses detection: Munged from Midnight Commander's configure.in
+dnl
+dnl What it does:
+dnl =============
+dnl
+dnl - Determine which version of curses is installed on your system
+dnl   and set the -I/-L/-l compiler entries and add a few preprocessor
+dnl   symbols 
+dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that
+dnl   @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in
+dnl   Makefile.in's
+dnl - Modify the following configure variables (these are the only
+dnl   curses.m4 variables you can access from within configure.in)
+dnl   CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if
+dnl                       an ncurses.h that's been renamed to curses.h
+dnl                       is found.
+dnl   CURSES_LIBS       - sets -L and -l's appropriately
+dnl   CFLAGS            - if --with-sco, add -D_SVID3 
+dnl   has_curses        - exports result of tests to rest of configure
+dnl
+dnl Usage:
+dnl ======
+dnl 1) Add lines indicated below to acconfig.h
+dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in
+dnl 3) Instead of #include <curses.h> you should use the following to
+dnl    properly locate ncurses or curses header file
+dnl
+dnl    #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+dnl    #include <ncurses.h>
+dnl    #else
+dnl    #include <curses.h>
+dnl    #endif
+dnl
+dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags
+dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS
+dnl
+dnl Notes with automake:
+dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from
+dnl   configure.in
+dnl - your Makefile.am can look something like this
+dnl   -----------------------------------------------
+dnl   INCLUDES= blah blah blah $(CURSES_INCLUDEDIR) 
+dnl   if HAS_CURSES
+dnl   CURSES_TARGETS=name_of_curses_prog
+dnl   endif
+dnl   bin_PROGRAMS = other_programs $(CURSES_TARGETS)
+dnl   other_programs_SOURCES = blah blah blah
+dnl   name_of_curses_prog_SOURCES = blah blah blah
+dnl   other_programs_LDADD = blah
+dnl   name_of_curses_prog_LDADD = blah $(CURSES_LIBS)
+dnl   -----------------------------------------------
+dnl
+dnl
+dnl The following lines should be added to acconfig.h:
+dnl ==================================================
+dnl
+dnl /*=== Curses version detection defines ===*/
+dnl /* Found some version of curses that we're going to use */
+dnl #undef HAS_CURSES
+dnl    
+dnl /* Use SunOS SysV curses? */
+dnl #undef USE_SUNOS_CURSES
+dnl 
+dnl /* Use old BSD curses - not used right now */
+dnl #undef USE_BSD_CURSES
+dnl 
+dnl /* Use SystemV curses? */
+dnl #undef USE_SYSV_CURSES
+dnl 
+dnl /* Use Ncurses? */
+dnl #undef USE_NCURSES
+dnl 
+dnl /* If you Curses does not have color define this one */
+dnl #undef NO_COLOR_CURSES
+dnl 
+dnl /* Define if you want to turn on SCO-specific code */
+dnl #undef SCO_FLAVOR
+dnl 
+dnl /* Set to reflect version of ncurses *
+dnl  *   0 = version 1.*
+dnl  *   1 = version 1.9.9g
+dnl  *   2 = version 4.0/4.1 */
+dnl #undef NCURSES_970530
+dnl
+dnl /*=== End new stuff for acconfig.h ===*/
+dnl 
+
+
+AC_DEFUN([AC_CHECK_CURSES],[
+       search_ncurses=true
+       screen_manager=""
+       has_curses=false
+
+       CFLAGS=${CFLAGS--O}
+
+       AC_SUBST(CURSES_LIBS)
+       AC_SUBST(CURSES_INCLUDEDIR)
+
+       AC_ARG_WITH(sco,
+         [  --with-sco              Use this to turn on SCO-specific code],[
+         if test x$withval = xyes; then
+               AC_DEFINE(SCO_FLAVOR)
+               CFLAGS="$CFLAGS -D_SVID3"
+         fi
+       ])
+
+       AC_ARG_WITH(sunos-curses,
+         [  --with-sunos-curses     Used to force SunOS 4.x curses],[
+         if test x$withval = xyes; then
+               AC_USE_SUNOS_CURSES
+         fi
+       ])
+
+       AC_ARG_WITH(osf1-curses,
+         [  --with-osf1-curses      Used to force OSF/1 curses],[
+         if test x$withval = xyes; then
+               AC_USE_OSF1_CURSES
+         fi
+       ])
+
+       AC_ARG_WITH(vcurses,
+         [  --with-vcurses[=incdir] Used to force SysV curses],
+         if test x$withval != xyes; then
+               CURSES_INCLUDEDIR="-I$withval"
+         fi
+         AC_USE_SYSV_CURSES
+       )
+
+       AC_ARG_WITH(ncurses,
+         [  --with-ncurses[=dir]  Compile with ncurses/locate base dir],
+         if test x$withval = xno ; then
+               search_ncurses=false
+         elif test x$withval != xyes ; then
+               CURSES_LIBS="$LIBS -L$withval/lib -lncurses"
+               CURSES_INCLUDEDIR="-I$withval/include"
+               search_ncurses=false
+               screen_manager="ncurses"
+               AC_DEFINE(USE_NCURSES)
+               AC_DEFINE(HAS_CURSES)
+               has_curses=true
+         fi
+       )
+
+       if $search_ncurses
+       then
+               AC_SEARCH_NCURSES()
+       fi
+
+
+])
+
+
+AC_DEFUN([AC_USE_SUNOS_CURSES], [
+       search_ncurses=false
+       screen_manager="SunOS 4.x /usr/5include curses"
+       AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
+       AC_DEFINE(USE_SUNOS_CURSES)
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(NO_COLOR_CURSES)
+       AC_DEFINE(USE_SYSV_CURSES)
+       CURSES_INCLUDEDIR="-I/usr/5include"
+       CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
+       AC_MSG_RESULT(Please note that some screen refreshs may fail)
+])
+
+AC_DEFUN([AC_USE_OSF1_CURSES], [
+       AC_MSG_RESULT(Using OSF1 curses)
+       search_ncurses=false
+       screen_manager="OSF1 curses"
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(NO_COLOR_CURSES)
+       AC_DEFINE(USE_SYSV_CURSES)
+       CURSES_LIBS="-lcurses"
+])
+
+AC_DEFUN([AC_USE_SYSV_CURSES], [
+       AC_MSG_RESULT(Using SysV curses)
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(USE_SYSV_CURSES)
+       search_ncurses=false
+       screen_manager="SysV/curses"
+       CURSES_LIBS="-lcurses"
+])
+
+dnl AC_ARG_WITH(bsd-curses,
+dnl [--with-bsd-curses         Used to compile with bsd curses, not very fancy],
+dnl    search_ncurses=false
+dnl    screen_manager="Ultrix/cursesX"
+dnl    if test $system = ULTRIX
+dnl    then
+dnl        THIS_CURSES=cursesX
+dnl        else
+dnl        THIS_CURSES=curses
+dnl    fi
+dnl
+dnl    CURSES_LIBS="-l$THIS_CURSES -ltermcap"
+dnl    AC_DEFINE(HAS_CURSES)
+dnl    has_curses=true
+dnl    AC_DEFINE(USE_BSD_CURSES)
+dnl    AC_MSG_RESULT(Please note that some screen refreshs may fail)
+dnl    AC_MSG_WARN(Use of the bsdcurses extension has some)
+dnl    AC_MSG_WARN(display/input problems.)
+dnl    AC_MSG_WARN(Reconsider using xcurses)
+dnl)
+
+       
+dnl
+dnl Parameters: directory filename cureses_LIBS curses_INCLUDEDIR nicename
+dnl
+AC_DEFUN([AC_NCURSES], [
+    if $search_ncurses
+    then
+        if test -f $1/$2
+       then
+           AC_MSG_RESULT(Found ncurses on $1/$2)
+           CURSES_LIBS="$3"
+           CURSES_INCLUDEDIR="$4"
+           search_ncurses=false
+           screen_manager=$5
+            AC_DEFINE(HAS_CURSES)
+            has_curses=true
+           AC_DEFINE(USE_NCURSES)
+       fi
+    fi
+])
+
+AC_DEFUN([AC_SEARCH_NCURSES], [
+    AC_CHECKING("location of ncurses.h file")
+
+    AC_NCURSES(/usr/include, ncurses.h, -lncurses,, "ncurses on /usr/include")
+    AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses, "ncurses on /usr/include/ncurses")
+    AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include, "ncurses on /usr/local")
+    AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -L/usr/local/lib/ncurses -lncurses, -I/usr/local/include/ncurses, "ncurses on /usr/local/include/ncurses")
+
+    AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/local/.../ncurses")
+
+    AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/include/ncurses")
+
+    dnl
+    dnl We couldn't find ncurses, try SysV curses
+    dnl
+    if $search_ncurses 
+    then
+        AC_EGREP_HEADER(init_color, /usr/include/curses.h,
+           AC_USE_SYSV_CURSES)
+       AC_EGREP_CPP(USE_NCURSES,[
+#include <curses.h>
+#ifdef __NCURSES_H
+#undef USE_NCURSES
+USE_NCURSES
+#endif
+],[
+       CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
+        AC_DEFINE(HAS_CURSES)
+       has_curses=true
+        AC_DEFINE(USE_NCURSES)
+        search_ncurses=false
+        screen_manager="ncurses installed as curses"
+])
+    fi
+
+    dnl
+    dnl Try SunOS 4.x /usr/5{lib,include} ncurses
+    dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
+    dnl should be replaced by a more fine grained selection routine
+    dnl
+    if $search_ncurses
+    then
+       if test -f /usr/5include/curses.h
+       then
+           AC_USE_SUNOS_CURSES
+        fi
+    else
+        # check for ncurses version, to properly ifdef mouse-fix
+       AC_MSG_CHECKING(for ncurses version)
+       ncurses_version=unknown
+cat > conftest.$ac_ext <<EOF
+[#]line __oline__ "configure"
+#include "confdefs.h"
+#ifdef RENAMED_NCURSES
+#include <curses.h>
+#else
+#include <ncurses.h>
+#endif
+#undef VERSION
+VERSION:NCURSES_VERSION
+EOF
+        if (eval "$ac_cpp conftest.$ac_ext") 2>&AC_FD_CC |
+  egrep "VERSION:" >conftest.out 2>&1; then
+changequote(,)dnl
+            ncurses_version=`cat conftest.out|sed -e 's/^[^"]*"//' -e 's/".*//'`
+changequote([,])dnl
+       fi
+       rm -rf conftest*
+        AC_MSG_RESULT($ncurses_version)
+       case "$ncurses_version" in
+changequote(,)dnl
+       4.[01])
+changequote([,])dnl
+            AC_DEFINE(NCURSES_970530,2)
+            ;;
+       1.9.9g)
+            AC_DEFINE(NCURSES_970530,1)
+            ;;
+       1*)
+            AC_DEFINE(NCURSES_970530,0)
+            ;;
+       esac
+    fi
+])
+
+
+
+
+
diff --git a/apps/silcer/macros/gnome-bonobo-check.m4 b/apps/silcer/macros/gnome-bonobo-check.m4
new file mode 100644 (file)
index 0000000..daa109c
--- /dev/null
@@ -0,0 +1,166 @@
+# Configure paths for Bonobo
+# Miguel de Icaza, 99-04-12
+# Stolen from Chris Lahey      99-2-5
+# stolen from Manish Singh again
+# stolen back from Frank Belew
+# stolen from Manish Singh
+# Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_BONOBO ([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for Bonobo, and define BONOBO_CFLAGS and BONOBO_LIBS
+dnl
+AC_DEFUN([AM_PATH_BONOBO],
+[
+dnl 
+dnl Get the cflags and libraries from the gnome-config script
+dnl
+AC_ARG_WITH(bonobo-prefix,[  --with-bonobo-prefix=PFX   Prefix where Bonobo is installed (optional)],
+            bonobo_prefix="$withval", bonobo_prefix="")
+AC_ARG_WITH(bonobo-exec-prefix,[  --with-bonobo-exec-prefix=PFX Exec prefix where Bonobo is installed (optional)],
+            bonobo_exec_prefix="$withval", bonobo_exec_prefix="")
+AC_ARG_ENABLE(bonobotest, [  --disable-bonobotest       Do not try to compile and run a test Bonobo program],
+                   , enable_bonobotest=yes)
+
+  if test x$bonobo_exec_prefix != x ; then
+     bonobo_args="$bonobo_args --exec-prefix=$bonobo_exec_prefix"
+     if test x${GNOME_CONFIG+set} != xset ; then
+        GNOME_CONFIG=$bonobo_exec_prefix/bin/gnome-config
+     fi
+  fi
+  if test x$bonobo_prefix != x ; then
+     bonobo_args="$bonobo_args --prefix=$bonobo_prefix"
+     if test x${GNOME_CONFIG+set} != xset ; then
+        GNOME_CONFIG=$bonobo_prefix/bin/gnome-config
+     fi
+  fi
+
+  AC_PATH_PROG(GNOME_CONFIG, gnome-config, no)
+  min_bonobo_version=ifelse([$1], ,0.1.0,$1)
+  AC_MSG_CHECKING(for BONOBO - version >= $min_bonobo_version)
+  no_bonobo=""
+  if test "$GNOME_CONFIG" = "no" ; then
+    no_bonobo=yes
+  else
+    BONOBO_CFLAGS=`$GNOME_CONFIG $bonoboconf_args --cflags bonobo bonobox`
+    BONOBO_LIBS=`$GNOME_CONFIG $bonoboconf_args --libs bonobo bonobox`
+
+    bonobo_major_version=`$GNOME_CONFIG $bonobo_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+    bonobo_minor_version=`$GNOME_CONFIG $bonobo_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+    bonobo_micro_version=`$GNOME_CONFIG $bonobo_config_args --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+    if test "x$enable_bonobotest" = "xyes" ; then
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $BONOBO_CFLAGS"
+      LIBS="$LIBS $BONOBO_LIBS"
+dnl
+dnl Now check if the installed BONOBO is sufficiently new. (Also sanity
+dnl checks the results of gnome-config to some extent
+dnl
+      rm -f conf.bonobotest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bonobo.h>
+
+static char*
+my_strdup (char *str)
+{
+  char *new_str;
+  
+  if (str)
+    {
+      new_str = malloc ((strlen (str) + 1) * sizeof(char));
+      strcpy (new_str, str);
+    }
+  else
+    new_str = NULL;
+  
+  return new_str;
+}
+
+int main ()
+{
+  int major, minor, micro;
+  char *tmp_version;
+
+  system ("touch conf.bonobotest");
+  bonobo_object_get_type ();
+  return 0;
+}
+
+],, no_bonobo=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+  fi
+  if test "x$no_bonobo" = x ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$2], , :, [$2])     
+  else
+     AC_MSG_RESULT(no)
+     if test "$GNOME_CONFIG" = "no" ; then
+       echo "*** The gnome-config script installed by GNOME-LIBS could not be found"
+       echo "*** If BONOBO was installed in PREFIX, make sure PREFIX/bin is in"
+       echo "*** your path, or set the GNOME_CONFIG environment variable to the"
+       echo "*** full path to gnome-config."
+     else
+       if test -f conf.bonobotest ; then
+        :
+       else
+          echo "*** Could not run BONOBO test program, checking why..."
+          CFLAGS="$CFLAGS $BONOBO_CFLAGS"
+          LIBS="$LIBS $BONOBO_LIBS"
+          AC_TRY_LINK([
+#include <stdio.h>
+#include <bonobo/gnome-object.h>
+],      [ return 0; ],
+        [ echo "*** The test program compiled, but did not run. This usually means"
+          echo "*** that the run-time linker is not finding BONOBO or finding the wrong"
+          echo "*** version of BONOBO. If it is not finding BONOBO, you'll need to set your"
+          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+          echo "*** is required on your system"
+         echo "***"
+          echo "*** If you have an old version installed, it is best to remove it, although"
+          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+          echo "*** exact error that occured. This usually means BONOBO was incorrectly installed"
+          echo "*** or that you have moved BONOBO since it was installed. In the latter case, you"
+          echo "*** may want to edit the gnome-config script: $GNOME_CONFIG" ])
+          CFLAGS="$ac_save_CFLAGS"
+          LIBS="$ac_save_LIBS"
+       fi
+     fi
+     BONOBO_CFLAGS=""
+     BONOBO_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+  AC_SUBST(BONOBO_CFLAGS)
+  AC_SUBST(BONOBO_LIBS)
+  rm -f conf.bonobotest
+])
+
+AC_DEFUN([BONOBO_CHECK], [
+       AM_PATH_BONOBO(0.1.0,,[AC_MSG_ERROR(BONOBO not found)])
+])
+
+AC_DEFUN([AM_BONOBO_USES_OAF],
+[
+       AC_REQUIRE([AM_PATH_BONOBO])
+
+       AC_MSG_CHECKING(if Bonobo uses OAF)
+       if ( gnome-config --libs bonobo | grep oaf ) > /dev/null 2>&1 ; then
+         using_oaf="yes"
+         AC_DEFINE(BONOBO_USES_OAF)
+       else
+         using_oaf="no"
+       fi
+
+       AC_MSG_RESULT("$using_oaf")
+
+       AM_CONDITIONAL(BONOBO_USES_OAF, test x"using_oaf" = "xyes")
+])
diff --git a/apps/silcer/macros/gnome-common.m4 b/apps/silcer/macros/gnome-common.m4
new file mode 100644 (file)
index 0000000..83bb00d
--- /dev/null
@@ -0,0 +1,14 @@
+# gnome-common.m4
+# 
+# This only for packages that are not in the GNOME CVS tree.
+
+dnl GNOME_COMMON_INIT
+
+AC_DEFUN([GNOME_COMMON_INIT],
+[
+       GNOME_ACLOCAL_DIR="$GNOME_COMMON_MACROS_DIR"
+       AC_SUBST(GNOME_ACLOCAL_DIR)
+
+       ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+])
+
diff --git a/apps/silcer/macros/gnome-fileutils.m4 b/apps/silcer/macros/gnome-fileutils.m4
new file mode 100644 (file)
index 0000000..7c11a78
--- /dev/null
@@ -0,0 +1,414 @@
+dnl
+dnl GNOME_FILEUTILS_CHECKS
+dnl
+dnl checks that are needed for the diskusage applet.
+dnl
+
+AC_DEFUN([GNOME_FILEUTILS_CHECKS],
+[      
+AC_CHECK_HEADERS(fcntl.h sys/param.h sys/statfs.h sys/fstyp.h \
+mnttab.h mntent.h sys/statvfs.h sys/vfs.h sys/mount.h \
+sys/filsys.h sys/fs_types.h sys/fs/s5param.h)
+
+AC_CHECK_FUNCS(bcopy endgrent endpwent fchdir ftime ftruncate \
+getcwd getmntinfo gettimeofday isascii lchown \
+listmntent memcpy mkfifo strchr strerror strrchr vprintf)
+
+dnl Set some defaults when cross-compiling
+
+if test x$cross_compiling = xyes ; then
+       case "$host_os" in
+       linux*)
+         fu_cv_sys_mounted_getmntent1=yes
+         fu_cv_sys_stat_statfs2_bsize=yes
+         ;;
+       sunos*)
+         fu_cv_sys_stat_statfs4=yes
+         ;;
+       freebsd*)
+         fu_cv_sys_stat_statfs2_bsize=yes
+         ;;
+       osf*)
+         fu_cv_sys_stat_statfs3_osf1=yes
+         ;;
+       esac
+fi
+
+# Determine how to get the list of mounted filesystems.
+list_mounted_fs=
+
+# If the getmntent function is available but not in the standard library,
+# make sure LIBS contains -lsun (on Irix4) or -lseq (on PTX).
+AC_FUNC_GETMNTENT
+
+# This test must precede the ones for getmntent because Unicos-9 is
+# reported to have the getmntent function, but its support is incompatible
+# with other getmntent implementations.
+
+# NOTE: Normally, I wouldn't use a check for system type as I've done for
+# `CRAY' below since that goes against the whole autoconf philosophy.  But
+# I think there is too great a chance that some non-Cray system has a
+# function named listmntent to risk the false positive.
+
+if test -z "$list_mounted_fs"; then
+# Cray UNICOS 9
+AC_MSG_CHECKING([for listmntent of Cray/Unicos-9])
+AC_CACHE_VAL(fu_cv_sys_mounted_cray_listmntent,
+[fu_cv_sys_mounted_cray_listmntent=no
+AC_EGREP_CPP(yes,
+[#ifdef _CRAY
+yes
+#endif
+], [test $ac_cv_func_listmntent = yes \
+&& fu_cv_sys_mounted_cray_listmntent=yes]
+)
+]
+)
+AC_MSG_RESULT($fu_cv_sys_mounted_cray_listmntent)
+if test $fu_cv_sys_mounted_cray_listmntent = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_LISTMNTENT)
+fi
+fi
+
+if test $ac_cv_func_getmntent = yes; then
+
+# This system has the getmntent function.
+# Determine whether it's the one-argument variant or the two-argument one.
+
+if test -z "$list_mounted_fs"; then
+# 4.3BSD, SunOS, HP-UX, Dynix, Irix
+AC_MSG_CHECKING([for one-argument getmntent function])
+AC_CACHE_VAL(fu_cv_sys_mounted_getmntent1,
+[test $ac_cv_header_mntent_h = yes \
+&& fu_cv_sys_mounted_getmntent1=yes \
+|| fu_cv_sys_mounted_getmntent1=no])
+AC_MSG_RESULT($fu_cv_sys_mounted_getmntent1)
+if test $fu_cv_sys_mounted_getmntent1 = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_GETMNTENT1)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+# SVR4
+AC_MSG_CHECKING([for two-argument getmntent function])
+AC_CACHE_VAL(fu_cv_sys_mounted_getmntent2,
+[AC_EGREP_HEADER(getmntent, sys/mnttab.h,
+fu_cv_sys_mounted_getmntent2=yes,
+fu_cv_sys_mounted_getmntent2=no)])
+AC_MSG_RESULT($fu_cv_sys_mounted_getmntent2)
+if test $fu_cv_sys_mounted_getmntent2 = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_GETMNTENT2)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+AC_MSG_ERROR([could not determine how to read list of mounted filesystems])
+fi
+
+fi
+
+if test -z "$list_mounted_fs"; then
+# DEC Alpha running OSF/1.
+AC_MSG_CHECKING([for getfsstat function])
+AC_CACHE_VAL(fu_cv_sys_mounted_getsstat,
+[AC_TRY_LINK([
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/fs_types.h>],
+[struct statfs *stats;
+int numsys = getfsstat ((struct statfs *)0, 0L, MNT_WAIT); ],
+fu_cv_sys_mounted_getsstat=yes,
+fu_cv_sys_mounted_getsstat=no)])
+AC_MSG_RESULT($fu_cv_sys_mounted_getsstat)
+if test $fu_cv_sys_mounted_getsstat = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_GETFSSTAT)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+# AIX.
+AC_MSG_CHECKING([for mntctl function and struct vmount])
+AC_CACHE_VAL(fu_cv_sys_mounted_vmount,
+[AC_TRY_CPP([#include <fshelp.h>],
+fu_cv_sys_mounted_vmount=yes,
+fu_cv_sys_mounted_vmount=no)])
+AC_MSG_RESULT($fu_cv_sys_mounted_vmount)
+if test $fu_cv_sys_mounted_vmount = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_VMOUNT)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+# SVR3
+AC_MSG_CHECKING([for FIXME existence of three headers])
+AC_CACHE_VAL(fu_cv_sys_mounted_fread_fstyp,
+[AC_TRY_CPP([
+#include <sys/statfs.h>
+#include <sys/fstyp.h>
+#include <mnttab.h>],
+fu_cv_sys_mounted_fread_fstyp=yes,
+fu_cv_sys_mounted_fread_fstyp=no)])
+AC_MSG_RESULT($fu_cv_sys_mounted_fread_fstyp)
+if test $fu_cv_sys_mounted_fread_fstyp = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_FREAD_FSTYP)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+# 4.4BSD and DEC OSF/1.
+AC_MSG_CHECKING([for getmntinfo function])
+AC_CACHE_VAL(fu_cv_sys_mounted_getmntinfo,
+[
+ok=
+if test $ac_cv_func_getmntinfo = yes; then
+AC_EGREP_HEADER(f_type;, sys/mount.h,
+ok=yes)
+fi
+test -n "$ok" \
+&& fu_cv_sys_mounted_getmntinfo=yes \
+|| fu_cv_sys_mounted_getmntinfo=no
+])
+AC_MSG_RESULT($fu_cv_sys_mounted_getmntinfo)
+if test $fu_cv_sys_mounted_getmntinfo = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_GETMNTINFO)
+fi
+fi
+
+# FIXME: add a test for netbsd-1.1 here
+
+if test -z "$list_mounted_fs"; then
+# Ultrix
+AC_MSG_CHECKING([for getmnt function])
+AC_CACHE_VAL(fu_cv_sys_mounted_getmnt,
+[AC_TRY_CPP([
+#include <sys/fs_types.h>
+#include <sys/mount.h>],
+fu_cv_sys_mounted_getmnt=yes,
+fu_cv_sys_mounted_getmnt=no)])
+AC_MSG_RESULT($fu_cv_sys_mounted_getmnt)
+if test $fu_cv_sys_mounted_getmnt = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_GETMNT)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+# SVR2
+AC_MSG_CHECKING([whether it is possible to resort to fread on /etc/mnttab])
+AC_CACHE_VAL(fu_cv_sys_mounted_fread,
+[AC_TRY_CPP([#include <mnttab.h>],
+fu_cv_sys_mounted_fread=yes,
+fu_cv_sys_mounted_fread=no)])
+AC_MSG_RESULT($fu_cv_sys_mounted_fread)
+if test $fu_cv_sys_mounted_fread = yes; then
+list_mounted_fs=found
+AC_DEFINE(MOUNTED_FREAD)
+fi
+fi
+
+if test -z "$list_mounted_fs"; then
+AC_MSG_ERROR([could not determine how to read list of mounted filesystems])
+# FIXME -- no need to abort building the whole package
+# Can't build mountlist.c or anything that needs its functions
+fi
+
+AC_CHECKING(how to get filesystem space usage)
+space=no
+
+# Perform only the link test since it seems there are no variants of the
+# statvfs function.  This check is more than just AC_CHECK_FUNCS(statvfs)
+# because that got a false positive on SCO OSR5.  Adding the declaration
+# of a `struct statvfs' causes this test to fail (as it should) on such
+# systems.  That system is reported to work fine with STAT_STATFS4 which
+# is what it gets when this test fails.
+if test $space = no; then
+# SVR4
+AC_CACHE_CHECK([statvfs function (SVR4)], fu_cv_sys_stat_statvfs,
+[AC_TRY_LINK([#include <sys/types.h>
+#include <sys/statvfs.h>],
+[struct statvfs fsd; statvfs (0, &fsd);],
+fu_cv_sys_stat_statvfs=yes,
+fu_cv_sys_stat_statvfs=no)])
+if test $fu_cv_sys_stat_statvfs = yes; then
+space=yes
+AC_DEFINE(STAT_STATVFS)
+fi
+fi
+
+if test $space = no; then
+# DEC Alpha running OSF/1
+AC_MSG_CHECKING([for 3-argument statfs function (DEC OSF/1)])
+AC_CACHE_VAL(fu_cv_sys_stat_statfs3_osf1,
+[AC_TRY_RUN([
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+main ()
+{
+struct statfs fsd;
+fsd.f_fsize = 0;
+exit (statfs (".", &fsd, sizeof (struct statfs)));
+}],
+fu_cv_sys_stat_statfs3_osf1=yes,
+fu_cv_sys_stat_statfs3_osf1=no,
+fu_cv_sys_stat_statfs3_osf1=no)])
+AC_MSG_RESULT($fu_cv_sys_stat_statfs3_osf1)
+if test $fu_cv_sys_stat_statfs3_osf1 = yes; then
+space=yes
+AC_DEFINE(STAT_STATFS3_OSF1)
+fi
+fi
+
+if test $space = no; then
+# AIX
+AC_MSG_CHECKING([for two-argument statfs with statfs.bsize dnl
+member (AIX, 4.3BSD)])
+AC_CACHE_VAL(fu_cv_sys_stat_statfs2_bsize,
+[AC_TRY_RUN([
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#endif
+main ()
+{
+struct statfs fsd;
+fsd.f_bsize = 0;
+exit (statfs (".", &fsd));
+}],
+fu_cv_sys_stat_statfs2_bsize=yes,
+fu_cv_sys_stat_statfs2_bsize=no,
+fu_cv_sys_stat_statfs2_bsize=no)])
+AC_MSG_RESULT($fu_cv_sys_stat_statfs2_bsize)
+if test $fu_cv_sys_stat_statfs2_bsize = yes; then
+space=yes
+AC_DEFINE(STAT_STATFS2_BSIZE)
+fi
+fi
+
+if test $space = no; then
+# SVR3
+AC_MSG_CHECKING([for four-argument statfs (AIX-3.2.5, SVR3)])
+AC_CACHE_VAL(fu_cv_sys_stat_statfs4,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/statfs.h>
+main ()
+{
+struct statfs fsd;
+exit (statfs (".", &fsd, sizeof fsd, 0));
+}],
+fu_cv_sys_stat_statfs4=yes,
+fu_cv_sys_stat_statfs4=no,
+fu_cv_sys_stat_statfs4=no)])
+AC_MSG_RESULT($fu_cv_sys_stat_statfs4)
+if test $fu_cv_sys_stat_statfs4 = yes; then
+space=yes
+AC_DEFINE(STAT_STATFS4)
+fi
+fi
+
+if test $space = no; then
+# 4.4BSD and NetBSD
+AC_MSG_CHECKING([for two-argument statfs with statfs.fsize dnl
+member (4.4BSD and NetBSD)])
+AC_CACHE_VAL(fu_cv_sys_stat_statfs2_fsize,
+[AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+main ()
+{
+struct statfs fsd;
+fsd.f_fsize = 0;
+exit (statfs (".", &fsd));
+}],
+fu_cv_sys_stat_statfs2_fsize=yes,
+fu_cv_sys_stat_statfs2_fsize=no,
+fu_cv_sys_stat_statfs2_fsize=no)])
+AC_MSG_RESULT($fu_cv_sys_stat_statfs2_fsize)
+if test $fu_cv_sys_stat_statfs2_fsize = yes; then
+space=yes
+AC_DEFINE(STAT_STATFS2_FSIZE)
+fi
+fi
+
+if test $space = no; then
+# Ultrix
+AC_MSG_CHECKING([for two-argument statfs with struct fs_data (Ultrix)])
+AC_CACHE_VAL(fu_cv_sys_stat_fs_data,
+[AC_TRY_RUN([#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_FS_TYPES_H
+#include <sys/fs_types.h>
+#endif
+main ()
+{
+struct fs_data fsd;
+/* Ultrix's statfs returns 1 for success,
+0 for not mounted, -1 for failure.  */
+exit (statfs (".", &fsd) != 1);
+}],
+fu_cv_sys_stat_fs_data=yes,
+fu_cv_sys_stat_fs_data=no,
+fu_cv_sys_stat_fs_data=no)])
+AC_MSG_RESULT($fu_cv_sys_stat_fs_data)
+if test $fu_cv_sys_stat_fs_data = yes; then
+space=yes
+AC_DEFINE(STAT_STATFS2_FS_DATA)
+fi
+fi
+
+if test $space = no; then
+# SVR2
+AC_TRY_CPP([#include <sys/filsys.h>],
+AC_DEFINE(STAT_READ_FILSYS) space=yes)
+fi
+
+if test -n "$list_mounted_fs" && test $space != no; then
+DF_PROG="df"
+# LIBOBJS="$LIBOBJS fsusage.o"
+# LIBOBJS="$LIBOBJS mountlist.o"
+fi
+
+# Check for SunOS statfs brokenness wrt partitions 2GB and larger.
+# If <sys/vfs.h> exists and struct statfs has a member named f_spare,
+# enable the work-around code in fsusage.c.
+AC_MSG_CHECKING([for statfs that truncates block counts])
+AC_CACHE_VAL(fu_cv_sys_truncating_statfs,
+[AC_TRY_COMPILE([
+#if !defined(sun) && !defined(__sun)
+choke -- this is a workaround for a Sun-specific problem
+#endif
+#include <sys/types.h>
+#include <sys/vfs.h>],
+[struct statfs t; long c = *(t.f_spare);],
+fu_cv_sys_truncating_statfs=yes,
+fu_cv_sys_truncating_statfs=no,
+)])
+if test $fu_cv_sys_truncating_statfs = yes; then
+AC_DEFINE(STATFS_TRUNCATES_BLOCK_COUNTS)
+fi
+AC_MSG_RESULT($fu_cv_sys_truncating_statfs)
+
+AC_CHECKING(for AFS)
+test -d /afs && AC_DEFINE(AFS)
+])
diff --git a/apps/silcer/macros/gnome-ghttp-check.m4 b/apps/silcer/macros/gnome-ghttp-check.m4
new file mode 100644 (file)
index 0000000..0ecacaa
--- /dev/null
@@ -0,0 +1,14 @@
+AC_DEFUN([GNOME_GHTTP_CHECK],[
+       AC_REQUIRE([GNOME_INIT_HOOK])
+       GHTTP_LIB=
+       AC_CHECK_FUNC(connect,,[
+         AC_CHECK_LIB(socket,connect,
+               GHTTP_LIB="-lsocket $GHTTP_LIB",,$GHTTP_LIB)])
+       AC_CHECK_FUNC(gethostbyname,,[
+         AC_CHECK_LIB(nsl,gethostbyname,
+               GHTTP_LIB="-lnsl $GHTTP_LIB",,$GHTTP_LIB)])
+       AC_CHECK_LIB(ghttp, ghttp_request_new, 
+               GHTTP_LIB="-lghttp $GHTTP_LIB",GHTTP_LIB="",-L$gnome_prefix $GHTTP_LIB)
+       AC_SUBST(GHTTP_LIB)
+       AC_PROVIDE([GNOME_GHTTP_CHECK])
+])
diff --git a/apps/silcer/macros/gnome-gnorba-check.m4 b/apps/silcer/macros/gnome-gnorba-check.m4
new file mode 100644 (file)
index 0000000..dbac0a6
--- /dev/null
@@ -0,0 +1,35 @@
+dnl
+dnl GNOME_GNORBA_HOOK (script-if-gnorba-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if gnorba is not found.
+dnl
+
+AC_DEFUN([GNOME_GNORBA_HOOK],[
+       GNOME_ORBIT_HOOK([],$2)
+       AC_CACHE_CHECK([for gnorba libraries],gnome_cv_gnorba_found,[
+               gnome_cv_gnorba_found=no
+               if test x$gnome_cv_orbit_found = xyes; then
+                       GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+                       GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+                       if test -n "$GNORBA_LIBS"; then
+                               gnome_cv_gnorba_found=yes
+                       fi
+               fi
+       ])
+       AM_CONDITIONAL(HAVE_GNORBA, test x$gnome_cv_gnorba_found = xyes)
+       if test x$gnome_cv_orbit_found = xyes; then
+               $1
+               GNORBA_CFLAGS="`gnome-config --cflags gnorba gnomeui`"
+               GNORBA_LIBS="`gnome-config --libs gnorba gnomeui`"
+               AC_SUBST(GNORBA_CFLAGS)
+               AC_SUBST(GNORBA_LIBS)
+       else
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(gnorba library not installed or installation problem)
+               fi
+       fi
+])
+
+AC_DEFUN([GNOME_GNORBA_CHECK], [
+       GNOME_GNORBA_HOOK([],failure)
+])
diff --git a/apps/silcer/macros/gnome-guile-checks.m4 b/apps/silcer/macros/gnome-guile-checks.m4
new file mode 100644 (file)
index 0000000..1086d30
--- /dev/null
@@ -0,0 +1,119 @@
+dnl
+dnl GNOME_CHECK_GUILE (failflag)
+dnl
+dnl if failflag is "fail" then GNOME_CHECK_GUILE will abort if guile is not found.
+dnl
+
+AC_DEFUN([GNOME_CHECK_GUILE],
+[
+       saved_ldflags="$LDFLAGS"
+       saved_cppflags="$CPPFLAGS"
+       LDFLAGS="$LDFLAGS $GNOME_LIBDIR"
+
+       AC_CHECK_LIB(qthreads,qt_null,[
+               QTTHREADS_LIB="-lqthreads"
+       ],[
+               AC_CHECK_LIB(qt, qt_null, QTTHREADS_LIB="-lqt")
+       ],$LIBS)
+       AC_SUBST(QTTHREADS_LIB)
+
+       AC_CHECK_LIB(termcap,main,TERMCAP_LIB="-ltermcap")
+       AC_CHECK_LIB(readline,main,READLINE_LIB="-lreadline",,$TERMCAP_LIB)
+
+       AC_SUBST(TERMCAP_LIB)
+       AC_SUBST(READLINE_LIB)
+
+       if test "x$cross_compiling" = "xyes" ; then
+         name_build_guile="$target_alias-guile-config"
+       else
+         name_build_guile="guile-config"
+       fi
+
+       AC_CHECK_PROG(BUILD_GUILE, $name_build_guile, yes, no)
+
+       if test "x$BUILD_GUILE" = "xyes"; then
+           AC_MSG_CHECKING(whether $name_build_guile works)
+           if test x`$name_build_guile --version >/dev/null 2>&1 || \
+               echo no` = xno; then
+               BUILD_GUILE=no
+           fi
+           AC_MSG_RESULT($BUILD_GUILE)
+       else
+
+           if test "x$cross_compiling" = "xyes" ; then
+               name_build_guile="$target_alias-build-guile"
+           else        
+               name_build_guile="build-guile"
+           fi
+
+           AC_CHECK_PROG(BUILD_GUILE, $name_build_guile, yes, no)
+
+           if test "x$BUILD_GUILE" = "xyes"; then
+               AC_MSG_CHECKING(whether $name_build_guile works)
+               if test x`$name_build_guile --version >/dev/null 2>&1 || \
+                   echo no` = xno; then
+                   BUILD_GUILE=no
+               fi
+               AC_MSG_RESULT($BUILD_GUILE)
+           fi
+       fi
+
+       AC_CHECK_LIB(m, sin)
+
+       if test "x$BUILD_GUILE" = "xyes"; then
+               AC_MSG_CHECKING(for guile libraries)
+               GUILE_LIBS="`$name_build_guile link`"
+               AC_MSG_RESULT($GUILE_LIBS)
+               AC_MSG_CHECKING(for guile headers)
+               GUILE_INCS="`$name_build_guile compile`"
+               AC_MSG_RESULT($GUILE_INCS)
+       else
+               GUILE_LIBS="$GNOME_LIBDIR"
+               GUILE_INCS="$GNOME_INCLUDEDIR"
+               AC_CHECK_LIB(rx, main, GUILE_LIBS="-lrx $GUILE_LIBS")
+               AC_CHECK_LIB(qt, qt_null, GUILE_LIBS="-lqt $GUILE_LIBS")
+               AC_CHECK_LIB(dl, dlopen, GUILE_LIBS="-ldl $GUILE_LIBS")
+               AC_CHECK_LIB(nsl, t_accept, GUILE_LIBS="$GUILE_LIBS -lnsl")
+               AC_CHECK_LIB(socket, socket, GUILE_LIBS="$GUILE_LIBS -lsocket")
+               GUILE_LIBS="-lguile $GUILE_LIBS $QTTHREADS_LIB $READLINE_LIB $TERMCAP_LIB"
+       fi
+
+       AC_SUBST(GUILE_LIBS)
+       AC_SUBST(GUILE_INCS)
+
+       saved_LIBS="$LIBS"
+       LIBS="$LIBS $GUILE_LIBS"
+       CPPFLAGS="$saved_cppflags $GUILE_INCS"
+
+       AC_MSG_CHECKING(whether guile works)
+       AC_TRY_LINK([
+               #include <libguile.h>
+               #include <guile/gh.h>
+       ],[
+               gh_eval_str("(newline)");
+               scm_boot_guile(0,NULL,NULL,NULL);
+       ],[
+               ac_cv_guile_found=yes
+               AC_DEFINE(HAVE_GUILE)
+       ],[
+               ac_cv_guile_found=no
+       ])
+       AC_MSG_RESULT($ac_cv_guile_found)
+
+       if test x$ac_cv_guile_found = xno ; then
+               if test x$1 = xfail ; then
+                 AC_MSG_ERROR(Can not find Guile on this system)
+               else
+                 AC_MSG_WARN(Can not find Guile on this system)
+               fi
+               ac_cv_guile_found=no
+               GUILE_LIBS= GUILE_INCS=
+       fi
+
+       LIBS="$saved_LIBS"
+       LDFLAGS="$saved_ldflags"
+       CPPFLAGS="$saved_cppflags"
+
+       AC_SUBST(GUILE_LIBS)
+       AM_CONDITIONAL(GUILE, test x$ac_cv_guile_found = xyes)
+])
diff --git a/apps/silcer/macros/gnome-libgtop-check.m4 b/apps/silcer/macros/gnome-libgtop-check.m4
new file mode 100644 (file)
index 0000000..1b4e174
--- /dev/null
@@ -0,0 +1,217 @@
+dnl
+dnl LIBGTOP_CHECK_TYPE
+dnl
+dnl Improved version of AC_CHECK_TYPE which takes into account
+dnl that we need to #include some other header files on some
+dnl systems to get some types.
+
+dnl AC_LIBGTOP_CHECK_TYPE(TYPE, DEFAULT)
+AC_DEFUN([AC_LIBGTOP_CHECK_TYPE],
+[AC_REQUIRE([AC_HEADER_STDC])dnl
+AC_MSG_CHECKING(for $1)
+AC_CACHE_VAL(ac_cv_type_$1,
+[AC_EGREP_CPP(dnl
+changequote(<<,>>)dnl
+<<(^|[^a-zA-Z_0-9])$1[^a-zA-Z_0-9]>>dnl
+changequote([,]), [#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+
+/* For Tru64 */
+#ifdef HAVE_SYS_BITYPES_H
+#include <sys/bitypes.h>
+#endif
+], ac_cv_type_$1=yes, ac_cv_type_$1=no)])dnl
+AC_MSG_RESULT($ac_cv_type_$1)
+if test $ac_cv_type_$1 = no; then
+  AC_DEFINE($1, $2)
+fi
+])
+
+dnl
+dnl GNOME_LIBGTOP_TYPES
+dnl
+dnl some typechecks for libgtop.
+dnl
+
+AC_DEFUN([GNOME_LIBGTOP_TYPES],
+[
+       AC_CHECK_HEADERS(sys/bitypes.h)
+       AC_LIBGTOP_CHECK_TYPE(u_int64_t, unsigned long long int)
+       AC_LIBGTOP_CHECK_TYPE(int64_t, signed long long int)
+])
+
+dnl
+dnl GNOME_LIBGTOP_HOOK (minversion, script-if-libgtop-enabled, failflag)
+dnl
+dnl if failflag is "fail" then GNOME_LIBGTOP_HOOK will abort if LibGTop
+dnl is not found. 
+dnl
+
+AC_DEFUN([GNOME_LIBGTOP_HOOK],
+[      
+       AC_REQUIRE([GNOME_LIBGTOP_TYPES])
+
+       AC_SUBST(LIBGTOP_LIBDIR)
+       AC_SUBST(LIBGTOP_INCLUDEDIR)
+       AC_SUBST(LIBGTOP_EXTRA_LIBS)
+       AC_SUBST(LIBGTOP_LIBS)
+       AC_SUBST(LIBGTOP_INCS)
+       AC_SUBST(LIBGTOP_NAMES_LIBS)
+       AC_SUBST(LIBGTOP_NAMES_INCS)
+       AC_SUBST(LIBGTOP_MAJOR_VERSION)
+       AC_SUBST(LIBGTOP_MINOR_VERSION)
+       AC_SUBST(LIBGTOP_MICRO_VERSION)
+       AC_SUBST(LIBGTOP_VERSION)
+       AC_SUBST(LIBGTOP_VERSION_CODE)
+       AC_SUBST(LIBGTOP_SERVER_VERSION)
+       AC_SUBST(LIBGTOP_INTERFACE_AGE)
+       AC_SUBST(LIBGTOP_BINARY_AGE)
+       AC_SUBST(LIBGTOP_BINDIR)
+       AC_SUBST(LIBGTOP_SERVER)
+
+       dnl Get the cflags and libraries from the libgtop-config script
+       dnl
+       AC_ARG_WITH(libgtop,
+       [  --with-libgtop=PFX      Prefix where LIBGTOP is installed (optional)],
+       libgtop_config_prefix="$withval", libgtop_config_prefix="")
+       AC_ARG_WITH(libgtop-exec,
+       [  --with-libgtop-exec=PFX Exec prefix where LIBGTOP is installed (optional)],
+       libgtop_config_exec_prefix="$withval", libgtop_config_exec_prefix="")
+
+       if test x$libgtop_config_exec_prefix != x ; then
+         libgtop_config_args="$libgtop_config_args --exec-prefix=$libgtop_config_exec_prefix"
+         if test x${LIBGTOP_CONFIG+set} != xset ; then
+           LIBGTOP_CONFIG=$libgtop_config_exec_prefix/bin/libgtop-config
+         fi
+       fi
+       if test x$libgtop_config_prefix != x ; then
+         libgtop_config_args="$libgtop_config_args --prefix=$libgtop_config_prefix"
+         if test x${LIBGTOP_CONFIG+set} != xset ; then
+           LIBGTOP_CONFIG=$libgtop_config_prefix/bin/libgtop-config
+         fi
+       fi
+
+       AC_PATH_PROG(LIBGTOP_CONFIG, libgtop-config, no)
+       dnl IMPORTANT NOTICE:
+       dnl   If you increase this number here, this means that *ALL*
+       dnl   modules will require the new version, even if they explicitly
+       dnl   give a lower number in their `configure.in' !!!
+       real_min_libgtop_version=1.0.0
+       min_libgtop_version=ifelse([$1], ,$real_min_libgtop_version,$1)
+       dnl I know, the following code looks really ugly, but if you want
+       dnl to make changes, please test it with a brain-dead /bin/sh and
+       dnl with a brain-dead /bin/test (not all shells/tests support the
+       dnl `<' operator to compare strings, that's why I convert everything
+       dnl into numbers and test them).
+       min_libgtop_major=`echo $min_libgtop_version | \
+         sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+       min_libgtop_minor=`echo $min_libgtop_version | \
+         sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+       min_libgtop_micro=`echo $min_libgtop_version | \
+         sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+       test x$min_libgtop_micro = x && min_libgtop_micro=0
+       real_min_libgtop_major=`echo $real_min_libgtop_version | \
+         sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+       real_min_libgtop_minor=`echo $real_min_libgtop_version | \
+         sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+       real_min_libgtop_micro=`echo $real_min_libgtop_version | \
+         sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+       test x$real_min_libgtop_micro = x && real_min_libgtop_micro=0
+       dnl You cannot require a version less then $real_min_libgtop_version,
+       dnl so you don't need to update each `configure.in' when it's increased.
+       if test $real_min_libgtop_major -gt $min_libgtop_major ; then
+         min_libgtop_major=$real_min_libgtop_major
+         min_libgtop_minor=$real_min_libgtop_minor
+         min_libgtop_micro=$real_min_libgtop_micro
+       elif test $real_min_libgtop_major = $min_libgtop_major ; then
+         if test $real_min_libgtop_minor -gt $min_libgtop_minor ; then
+           min_libgtop_minor=$real_min_libgtop_minor
+           min_libgtop_micro=$real_min_libgtop_micro
+         elif test $real_min_libgtop_minor = $min_libgtop_minor ; then
+           if test $real_min_libgtop_micro -gt $min_libgtop_micro ; then
+             min_libgtop_micro=$real_min_libgtop_micro
+           fi
+         fi
+       fi
+       min_libgtop_version="$min_libgtop_major.$min_libgtop_minor.$min_libgtop_micro"
+       AC_MSG_CHECKING(for libgtop - version >= $min_libgtop_version)
+       no_libgtop=""
+       if test "$LIBGTOP_CONFIG" = "no" ; then
+         no_libgtop=yes
+       else
+         configfile=`$LIBGTOP_CONFIG --config`
+         libgtop_major_version=`$LIBGTOP_CONFIG --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+         libgtop_minor_version=`$LIBGTOP_CONFIG --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+         libgtop_micro_version=`$LIBGTOP_CONFIG --version | \
+           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+         if test $libgtop_major_version != $min_libgtop_major ; then 
+           no_libgtop=mismatch
+         else 
+           test $libgtop_minor_version -lt $min_libgtop_minor && no_libgtop=yes
+           if test $libgtop_minor_version = $min_libgtop_minor ; then
+             test $libgtop_micro_version -lt $min_libgtop_micro && no_libgtop=yes
+           fi
+         fi
+         . $configfile
+       fi
+       if test x$no_libgtop = x ; then
+         AC_DEFINE(HAVE_LIBGTOP)
+         AC_DEFINE_UNQUOTED(LIBGTOP_VERSION, "$LIBGTOP_VERSION")
+         AC_DEFINE_UNQUOTED(LIBGTOP_VERSION_CODE, $LIBGTOP_VERSION_CODE)
+         AC_DEFINE_UNQUOTED(LIBGTOP_MAJOR_VERSION, $LIBGTOP_MAJOR_VERSION)
+         AC_DEFINE_UNQUOTED(LIBGTOP_MINOR_VERSION, $LIBGTOP_MINOR_VERSION)
+         AC_DEFINE_UNQUOTED(LIBGTOP_MICRO_VERSION, $LIBGTOP_MICRO_VERSION)
+         AC_DEFINE_UNQUOTED(LIBGTOP_SERVER_VERSION, $LIBGTOP_SERVER_VERSION)
+         AC_MSG_RESULT(yes)
+         dnl Note that an empty true branch is not valid sh syntax.
+         ifelse([$2], [], :, [$2])
+       else
+         AC_MSG_RESULT(no)
+         if test "$no_libgtop"x = mismatchx; then
+           AC_MSG_ERROR(LibGTop major version mismatch $libgtop_major_version != $min_libgtop_major)
+         fi
+         if test "x$3" = "xfail"; then
+           AC_MSG_ERROR(LibGTop >= $min_libgtop_version not found)
+         else
+           AC_MSG_WARN(LibGTop >= $min_libgtop_version not found)
+         fi
+       fi
+
+       AM_CONDITIONAL(HAVE_LIBGTOP, test x$no_libgtop != xyes)
+])
+
+AC_DEFUN([GNOME_INIT_LIBGTOP],[
+       GNOME_LIBGTOP_HOOK($1,[ifelse([$3], [], :, [$3])],$2)
+])
+
+dnl
+dnl GNOME_LIBGTOP_DOCU
+dnl
+dnl checks whether the documentation of LibGTop is installed
+dnl
+
+AC_DEFUN([GNOME_LIBGTOP_DOCU],
+[
+       AC_REQUIRE([GNOME_LIBGTOP_HOOK])
+
+       helpdir="$LIBGTOP_DATADIR/gnome/help/libgtop"
+
+       AC_MSG_CHECKING(whether you have the LibGTop Documentation)
+
+       if test -f "$helpdir/C/topic.dat" ; then
+         have_libgtop_docu=yes
+         AC_DEFINE(HAVE_LIBGTOP_DOCU)
+       else
+         have_libgtop_docu=no
+       fi
+
+       AC_MSG_RESULT($have_libgtop_docu)
+
+       AM_CONDITIONAL(HAVE_LIBGTOP_DOCU, test x$have_libgtop_docu = xyes)
+])
+
diff --git a/apps/silcer/macros/gnome-objc-checks.m4 b/apps/silcer/macros/gnome-objc-checks.m4
new file mode 100644 (file)
index 0000000..c69acb0
--- /dev/null
@@ -0,0 +1,83 @@
+AC_DEFUN([GNOME_CHECK_OBJC],
+[
+dnl Look for an ObjC compiler.
+dnl FIXME: extend list of possible names of ObjC compilers.
+  AC_CHECK_PROGS(OBJC, $OBJC egcs, "")
+  if test "x$OBJC" = "x" ; then
+    AC_CHECK_PROGS(OBJC, $OBJC egcc, "")
+    if test "x$OBJC" = "x" ; then
+      AC_CHECK_PROGS(OBJC, $OBJC gcc, "")
+    fi
+  fi
+
+  AC_REQUIRE([GNOME_PTHREAD_CHECK])
+
+  OBJC_LIBS="-lobjc $PTHREAD_LIB"
+  AC_CHECK_FUNC(sched_yield,,[
+    AC_CHECK_LIB(rt,sched_yield,
+      OBJC_LIBS="$OBJC_LIBS -lrt",[
+      AC_CHECK_LIB(posix4,sched_yield,
+        OBJC_LIBS="$OBJC_LIBS -lposix4",, 
+        $OBJC_LIBS)],
+      $OBJC_LIBS)])
+  AC_SUBST(OBJC_LIBS)
+
+  AC_CACHE_CHECK([if Objective C compiler ($OBJC) works],
+                ac_cv_prog_objc_works, [
+    if test -n "$OBJC"; then
+      cat > conftest.m <<EOF
+#include <objc/Object.h>
+@interface myRandomObj : Object
+{
+}
+@end
+@implementation myRandomObj
+@end
+int main () {
+  /* No, you are not seeing double.  Remember that square brackets
+     are the autoconf m4 quotes.  */
+  id myid = [[myRandomObj alloc]];
+  [[myid free]];
+  return 0;
+}
+EOF
+
+      $OBJC $CFLAGS -o conftest $LDFLAGS conftest.m $OBJC_LIBS 1>&AC_FD_CC 2>&1
+      result=$?
+      rm -f conftest*
+
+      if test $result -eq 0; then
+        ac_cv_prog_objc_works=yes
+      fi
+    else
+      ac_cv_prog_objc_works=no
+    fi
+  ])
+
+  AM_CONDITIONAL(OBJECTIVE_C, test x$ac_cv_prog_objc_works = xyes)
+  dnl Also set the shell variable OBJECTIVE_C to "yes" or "no".
+  OBJECTIVE_C=$ac_cv_prog_objc_works
+])
+
+AC_DEFUN([GNOME_INIT_OBJC],
+[
+       AC_MSG_CHECKING(for an obGnomeConf.sh)
+       my_gnome_libdir=`$GNOME_CONFIG --libdir`
+       if test -f $my_gnome_libdir/obGnomeConf.sh; then
+           . $my_gnome_libdir/obGnomeConf.sh
+           AC_MSG_RESULT(found $my_gnome_libdir)
+           ac_cv_have_gnome_objc=yes
+       else
+           AC_MSG_RESULT(not found)
+           AC_MSG_WARN(Could not find the obGnomeConf.sh file that is generated by gnome-objc install)
+           ac_cv_have_gnome_objc=no
+       fi
+       
+       dnl Add a conditional on whether or not we have gnome-objc
+       AM_CONDITIONAL(HAVE_GNOME_OBJC, test x$ac_cv_have_gnome_objc = xyes)
+       HAVE_GNOME_OBJC=$ac_cv_have_gnome_objc
+
+       AC_SUBST(OBGNOME_INCLUDEDIR)
+       AC_SUBST(OBGNOME_LIBS)
+       AC_SUBST(OBGTK_LIBS)
+])
diff --git a/apps/silcer/macros/gnome-orbit-check.m4 b/apps/silcer/macros/gnome-orbit-check.m4
new file mode 100644 (file)
index 0000000..54bf33a
--- /dev/null
@@ -0,0 +1,33 @@
+dnl
+dnl GNOME_ORBIT_HOOK (script-if-orbit-found, failflag)
+dnl
+dnl if failflag is "failure" it aborts if orbit is not found.
+dnl
+
+AC_DEFUN([GNOME_ORBIT_HOOK],[
+       AC_PATH_PROG(ORBIT_CONFIG,orbit-config,no)
+       AC_PATH_PROG(ORBIT_IDL,orbit-idl,no)
+       AC_CACHE_CHECK([for working ORBit environment],gnome_cv_orbit_found,[
+               if test x$ORBIT_CONFIG = xno -o x$ORBIT_IDL = xno; then
+                       gnome_cv_orbit_found=no
+               else
+                       gnome_cv_orbit_found=yes
+               fi
+       ])
+       AM_CONDITIONAL(HAVE_ORBIT, test x$gnome_cv_orbit_found = xyes)
+       if test x$gnome_cv_orbit_found = xyes; then
+               $1
+               ORBIT_CFLAGS=`orbit-config --cflags client server`
+               ORBIT_LIBS=`orbit-config --use-service=name --libs client server`
+               AC_SUBST(ORBIT_CFLAGS)
+               AC_SUBST(ORBIT_LIBS)
+       else
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(ORBit not installed or installation problem)
+               fi
+       fi
+])
+
+AC_DEFUN([GNOME_ORBIT_CHECK], [
+       GNOME_ORBIT_HOOK([],failure)
+])
diff --git a/apps/silcer/macros/gnome-print-check.m4 b/apps/silcer/macros/gnome-print-check.m4
new file mode 100644 (file)
index 0000000..c5f0fe3
--- /dev/null
@@ -0,0 +1,63 @@
+# Configure paths for GNOME-PRINT
+# Chris Lahey  99-2-5
+# stolen from Manish Singh again
+# stolen back from Frank Belew
+# stolen from Manish Singh
+# Shamelessly stolen from Owen Taylor
+
+dnl AM_PATH_GNOME_PRINT([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
+dnl Test for GNOME-PRINT, and define GNOME_PRINT_CFLAGS and GNOME_PRINT_LIBS
+dnl
+AC_DEFUN([AM_PATH_GNOME_PRINT],
+[
+  min_version=ifelse([$1],,0.21,$1)
+
+  gnome_print_ok=""
+
+  AC_PATH_PROG(GNOME_CONFIG, gnome-config, no)
+  if test "$GNOME_CONFIG" = "no" ; then
+    AC_MSG_RESULT(gnome-config is missing, check your gnome installation)
+  else
+    AC_MSG_CHECKING(for GNOME-PRINT - version >= $min_version)
+    if `$GNOME_CONFIG --libs print > /dev/null 2>&1`; then
+      rqmajor=$(echo "$1" | sed -e 's/cvs-//' | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/')
+      rqminor=$(echo "$1" | sed -e 's/cvs-//' | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/')
+      major=$($GNOME_CONFIG --modversion print | sed -e 's/gnome-print-//' | sed -e 's/cvs-//' | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\1/')
+      minor=$($GNOME_CONFIG --modversion print | sed -e 's/gnome-print-//' | sed -e 's/cvs-//' | sed 's/\([[0-9]]*\)\.\([[0-9]]*\).*/\2/')
+      if test "$major" -ge "$rqmajor"; then
+        if test "$major" -gt "$rqmajor"; then
+          AC_MSG_RESULT("found $major.$minor")
+          gnome_print_ok="yes"
+        else
+          if test "$minor" -ge "$rqminor"; then
+            AC_MSG_RESULT("found $major.$minor")
+            gnome_print_ok="yes"
+          else
+            AC_MSG_RESULT("you have $major.$minor")
+          fi
+        fi
+      else
+        AC_MSG_RESULT("you have $major.$minor")
+      fi
+    else
+      AC_MSG_RESULT("did not find any version")
+    fi
+  fi
+
+  if test "x$gnome_print_ok" != "x" ; then
+    GNOME_PRINT_CFLAGS=`$GNOME_CONFIG --cflags print`
+    GNOME_PRINT_LIBS=`$GNOME_CONFIG --libs print`
+    ifelse([$2], , :, [$2])
+  else
+     GNOME_PRINT_CFLAGS=""
+     GNOME_PRINT_LIBS=""
+     ifelse([$3], , :, [$3])
+  fi
+
+  AC_SUBST(GNOME_PRINT_CFLAGS)
+  AC_SUBST(GNOME_PRINT_LIBS)
+])
+
+AC_DEFUN([GNOME_PRINT_CHECK], [
+       AM_PATH_GNOME_PRINT($1,,[AC_MSG_ERROR(GNOME-PRINT not found or wrong version)])
+])
diff --git a/apps/silcer/macros/gnome-pthread-check.m4 b/apps/silcer/macros/gnome-pthread-check.m4
new file mode 100644 (file)
index 0000000..a4eb3b4
--- /dev/null
@@ -0,0 +1,16 @@
+dnl
+dnl And better, use gthreads instead...
+dnl
+
+AC_DEFUN([GNOME_PTHREAD_CHECK],[
+       PTHREAD_LIB=""
+       AC_CHECK_LIB(pthread, pthread_create, PTHREAD_LIB="-lpthread",
+               [AC_CHECK_LIB(pthreads, pthread_create, PTHREAD_LIB="-lpthreads",
+                   [AC_CHECK_LIB(c_r, pthread_create, PTHREAD_LIB="-lc_r",
+                       [AC_CHECK_FUNC(pthread_create)]
+                   )]
+               )]
+       )
+       AC_SUBST(PTHREAD_LIB)
+       AC_PROVIDE([GNOME_PTHREAD_CHECK])
+])
diff --git a/apps/silcer/macros/gnome-support.m4 b/apps/silcer/macros/gnome-support.m4
new file mode 100644 (file)
index 0000000..2c1d049
--- /dev/null
@@ -0,0 +1,68 @@
+dnl GNOME_SUPPORT_CHECKS
+dnl    Check for various support functions needed by the standard
+dnl    Gnome libraries.  Sets LIBOBJS, might define some macros.
+dnl    This should only be used when building the Gnome libs; 
+dnl    Gnome clients should not need this macro.
+AC_DEFUN([GNOME_SUPPORT_CHECKS],[
+  # we need an `awk' to build `gnomesupport.h'
+  AC_REQUIRE([AC_PROG_AWK])
+
+  # this should go away soon
+  need_gnome_support=yes
+
+  save_LIBOBJS="$LIBOBJS"
+  LIBOBJS=
+
+  AC_CHECK_FUNCS(getopt_long,,LIBOBJS="$LIBOBJS getopt.o getopt1.o")
+
+  # for `scandir'
+  AC_HEADER_DIRENT
+
+  # copied from `configure.in' of `libiberty'
+  vars="program_invocation_short_name program_invocation_name sys_errlist"
+  for v in $vars; do
+    AC_MSG_CHECKING([for $v])
+    AC_CACHE_VAL(gnome_cv_var_$v,
+      [AC_TRY_LINK([int *p;], [extern int $v; p = &$v;],
+                  [eval "gnome_cv_var_$v=yes"],
+                  [eval "gnome_cv_var_$v=no"])])
+    if eval "test \"`echo '$gnome_cv_var_'$v`\" = yes"; then
+      AC_MSG_RESULT(yes)
+      n=HAVE_`echo $v | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+      AC_DEFINE_UNQUOTED($n)
+    else
+      AC_MSG_RESULT(no)
+    fi
+  done
+
+  AC_REPLACE_FUNCS(memmove mkstemp scandir strcasecmp strerror strndup strnlen)
+  AC_REPLACE_FUNCS(strtok_r strtod strtol strtoul vasprintf vsnprintf)
+
+  AC_CHECK_FUNCS(realpath,,LIBOBJS="$LIBOBJS canonicalize.o")
+
+  # to include `error.c' error.c has some HAVE_* checks
+  AC_CHECK_FUNCS(vprintf doprnt strerror_r)
+  AM_FUNC_ERROR_AT_LINE
+
+  # This is required if we declare setreuid () and setregid ().
+  AC_TYPE_UID_T
+
+  # see if we need to declare some functions.  Solaris is notorious for
+  # putting functions into the `libc' but not listing them in the headers
+  AC_CHECK_HEADERS(string.h strings.h stdlib.h unistd.h dirent.h)
+  GCC_NEED_DECLARATIONS(gethostname setreuid setregid getpagesize)
+  GCC_NEED_DECLARATION(scandir,[
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+])
+
+  # Turn our LIBOBJS into libtool objects.  This is gross, but it
+  # requires changes to autoconf before it goes away.
+  LTLIBOBJS=`echo "$LIBOBJS" | sed 's/\.o/.lo/g'`
+  AC_SUBST(need_gnome_support)
+  AC_SUBST(LTLIBOBJS)
+
+  LIBOBJS="$save_LIBOBJS"
+  AM_CONDITIONAL(BUILD_GNOME_SUPPORT, test "$need_gnome_support" = yes)
+])
diff --git a/apps/silcer/macros/gnome-undelfs.m4 b/apps/silcer/macros/gnome-undelfs.m4
new file mode 100644 (file)
index 0000000..c8ea6f4
--- /dev/null
@@ -0,0 +1,20 @@
+dnl GNOME_UNDELFS_CHECKS
+dnl    Check for ext2fs undel support.
+dnl    Set shell variable ext2fs_undel to "yes" if we have it,
+dnl    "no" otherwise.  May define USE_EXT2FSLIB for cpp.
+dnl    Will set EXT2FS_UNDEL_LIBS to required libraries.
+
+AC_DEFUN([GNOME_UNDELFS_CHECKS], [
+  AC_CHECK_HEADERS(ext2fs/ext2fs.h linux/ext2_fs.h)
+  ext2fs_undel=no
+  EXT2FS_UNDEL_LIBS=
+  if test x$ac_cv_header_ext2fs_ext2fs_h = xyes
+  then
+    if test x$ac_cv_header_linux_ext2_fs_h = xyes
+    then
+      AC_DEFINE(USE_EXT2FSLIB)
+      ext2fs_undel=yes
+      EXT2FS_UNDEL_LIBS="-lext2fs -lcom_err"
+    fi
+  fi
+])
diff --git a/apps/silcer/macros/gnome-vfs.m4 b/apps/silcer/macros/gnome-vfs.m4
new file mode 100644 (file)
index 0000000..8ca361f
--- /dev/null
@@ -0,0 +1,120 @@
+dnl GNOME_VFS_CHECKS
+dnl   Check for various functions needed by libvfs.
+dnl   This has various effects:
+dnl     Sets GNOME_VFS_LIBS to libraries required
+dnl     Sets termnet  to true or false depending on whether it is required.
+dnl        If yes, defines USE_TERMNET.
+dnl     Sets vfs_flags to "pretty" list of vfs implementations we include.
+dnl     Sets shell variable use_vfs to yes (default, --with-vfs) or
+dnl        "no" (--without-vfs).
+dnl     Calls AC_SUBST(mcserv), which is either empty or "mcserv".
+
+dnl Private define
+AC_DEFUN([GNOME_WITH_VFS],[
+  dnl FIXME: network checks should probably be in their own macro.
+  AC_CHECK_LIB(nsl, t_accept)
+  AC_CHECK_LIB(socket, socket)
+
+  have_socket=no
+  AC_CHECK_FUNCS(socket, have_socket=yes)
+  if test $have_socket = no; then
+    # socket is not in the default libraries.  See if it's in some other.
+    for lib in bsd socket inet; do
+      AC_CHECK_LIB($lib, socket, [
+         LIBS="$LIBS -l$lib"
+         have_socket=yes
+         AC_DEFINE(HAVE_SOCKET)
+         break])
+    done
+  fi
+
+  have_gethostbyname=no
+  AC_CHECK_FUNC(gethostbyname, have_gethostbyname=yes)
+  if test $have_gethostbyname = no; then
+    # gethostbyname is not in the default libraries.  See if it's in some other.
+    for lib in bsd socket inet; do
+      AC_CHECK_LIB($lib, gethostbyname, [LIBS="$LIBS -l$lib"; have_gethostbyname=yes; break])
+    done
+  fi
+
+  vfs_flags="tarfs"
+  use_net_code=false
+  if test $have_socket = yes; then
+      AC_STRUCT_LINGER
+      AC_CHECK_FUNCS(pmap_set, , [
+        AC_CHECK_LIB(rpc, pmap_set, [
+          LIBS="-lrpc $LIBS"
+         AC_DEFINE(HAVE_PMAP_SET)
+         ])])
+      AC_CHECK_FUNCS(pmap_getport pmap_getmaps rresvport)
+      dnl add for source routing support setsockopt
+      AC_CHECK_HEADERS(rpc/pmap_clnt.h)
+      vfs_flags="$vfs_flags, mcfs, ftpfs, fish"
+      use_net_code=true
+  fi
+
+  dnl
+  dnl Samba support
+  dnl
+  smbfs=""
+  SAMBAFILES=""
+  AC_ARG_WITH(samba,
+         [--with-samba             Support smb virtual file system],[
+         if test "x$withval" != "xno"; then
+                 AC_DEFINE(WITH_SMBFS)
+                 vfs_flags="$vfs_flags, smbfs"
+                 smbfs="smbfs.o"
+                 SAMBAFILES="\$(SAMBAFILES)"
+         fi
+  ])
+  AC_SUBST(smbfs)
+  AC_SUBST(SAMBAFILES)
+  
+  dnl
+  dnl The termnet support
+  dnl
+  termnet=false
+  AC_ARG_WITH(termnet,
+         [--with-termnet             If you want a termified net support],[
+         if test x$withval = xyes; then
+                 AC_DEFINE(USE_TERMNET)
+                 termnet=true          
+         fi
+  ])
+
+  TERMNET=""
+  AC_DEFINE(USE_VFS)
+  if $use_net_code; then
+     AC_DEFINE(USE_NETCODE)
+  fi
+  mcserv=
+  if test $have_socket = yes; then
+     mcserv="mcserv"
+     if $termnet; then
+       TERMNET="-ltermnet"
+     fi
+  fi
+
+  AC_SUBST(TERMNET)
+  AC_SUBST(mcserv)
+
+dnl FIXME:
+dnl GNOME_VFS_LIBS=
+
+])
+
+AC_DEFUN([GNOME_VFS_CHECKS],[
+       use_vfs=yes
+       AC_ARG_WITH(vfs,
+               [--with-vfs                Compile with the VFS code],
+               use_vfs=$withval
+       )
+       case $use_vfs in
+               yes)    GNOME_WITH_VFS;;
+               no)     use_vfs=no;;
+               *)      use_vfs=no;;
+                       dnl Should we issue a warning?
+       esac
+])
+
+
diff --git a/apps/silcer/macros/gnome-x-checks.m4 b/apps/silcer/macros/gnome-x-checks.m4
new file mode 100644 (file)
index 0000000..1e397ef
--- /dev/null
@@ -0,0 +1,80 @@
+dnl GNOME_X_CHECKS
+dnl
+dnl Basic X11 related checks for X11.  At the end, the following will be
+dnl defined/changed:
+dnl   GTK_{CFLAGS,LIBS}      From AM_PATH_GTK
+dnl   CPPFLAGS              Will include $X_CFLAGS
+dnl   GNOME_HAVE_SM         `true' or `false' depending on whether session
+dnl                          management is available.  It is available if
+dnl                          both -lSM and X11/SM/SMlib.h exist.  (Some
+dnl                          Solaris boxes have the library but not the header)
+dnl   XPM_LIBS               -lXpm if Xpm library is present, otherwise ""
+dnl
+dnl The following configure cache variables are defined (but not used):
+dnl   gnome_cv_passdown_{x_libs,X_LIBS,X_CFLAGS}
+dnl
+AC_DEFUN([GNOME_X_CHECKS],
+[
+       AM_PATH_GTK(1.2.0,,AC_MSG_ERROR(GTK not installed, or gtk-config not in path))
+       dnl Hope that GTK_CFLAGS have only -I and -D.  Otherwise, we could
+       dnl   test -z "$x_includes" || CPPFLAGS="$CPPFLAGS -I$x_includes"
+       dnl
+       dnl Use CPPFLAGS instead of CFLAGS because AC_CHECK_HEADERS uses
+       dnl CPPFLAGS, not CFLAGS
+        CPPFLAGS="$CPPFLAGS $GTK_CFLAGS"
+
+        saved_ldflags="$LDFLAGS"
+        LDFLAGS="$LDFLAGS $GTK_LIBS"
+
+       gnome_cv_passdown_x_libs="$GTK_LIBS"
+       gnome_cv_passdown_X_LIBS="$GTK_LIBS"
+       gnome_cv_passdown_X_CFLAGS="$GTK_CFLAGS"
+       gnome_cv_passdown_GTK_LIBS="$GTK_LIBS"
+
+        LDFLAGS="$saved_ldflags $GTK_LIBS"
+
+dnl We are requiring GTK >= 1.1.1, which means this will be fine anyhow.
+       USE_DEVGTK=true
+
+dnl    AC_MSG_CHECKING([whether to use features from (unstable) GTK+ 1.1.x])
+dnl    AC_EGREP_CPP(answer_affirmatively,
+dnl    [#include <gtk/gtkfeatures.h>
+dnl    #ifdef GTK_HAVE_FEATURES_1_1_0
+dnl       answer_affirmatively
+dnl    #endif
+dnl    ], dev_gtk=yes, dev_gtk=no)
+dnl    if test "$dev_gtk" = "yes"; then
+dnl       USE_DEVGTK=true
+dnl    fi
+dnl    AC_MSG_RESULT("$dev_gtk")
+
+       GNOME_HAVE_SM=true
+       case "$GTK_LIBS" in
+        *-lSM*)
+           dnl Already found it.
+           ;;
+        *)
+           dnl Assume that if we have -lSM then we also have -lICE.
+           AC_CHECK_LIB(SM, SmcSaveYourselfDone,
+               [GTK_LIBS="-lSM -lICE $GTK_LIBS"],GNOME_HAVE_SM=false,
+               $x_libs -lICE)
+           ;;
+       esac
+
+       if test "$GNOME_HAVE_SM" = true; then
+          AC_CHECK_HEADERS(X11/SM/SMlib.h,,GNOME_HAVE_SM=false)
+       fi
+
+       if test "$GNOME_HAVE_SM" = true; then
+          AC_DEFINE(HAVE_LIBSM)
+       fi
+
+       XPM_LIBS=""
+       AC_CHECK_LIB(Xpm, XpmFreeXpmImage, [XPM_LIBS="-lXpm"], , $x_libs)
+       AC_SUBST(XPM_LIBS)
+
+       AC_REQUIRE([GNOME_PTHREAD_CHECK])
+        LDFLAGS="$saved_ldflags"
+
+       AC_PROVIDE([GNOME_X_CHECKS])
+])
diff --git a/apps/silcer/macros/gnome-xml-check.m4 b/apps/silcer/macros/gnome-xml-check.m4
new file mode 100644 (file)
index 0000000..1caad10
--- /dev/null
@@ -0,0 +1,32 @@
+dnl
+dnl GNOME_XML_HOOK (script-if-xml-found, failflag)
+dnl
+dnl If failflag is "failure", script aborts due to lack of XML
+dnl 
+dnl Check for availability of the libxml library
+dnl the XML parser uses libz if available too
+dnl
+
+AC_DEFUN([GNOME_XML_HOOK],[
+       AC_PATH_PROG(GNOME_CONFIG,gnome-config,no)
+       if test "$GNOME_CONFIG" = no; then
+               if test x$2 = xfailure; then
+                       AC_MSG_ERROR(Could not find gnome-config)
+               fi
+       fi
+       GNOME_XML_CFLAGS=`$GNOME_CONFIG --cflags xml`
+       AC_SUBST(GNOME_XML_CFLAGS)
+       AC_CHECK_LIB(xml, xmlNewDoc, [
+               $1
+               GNOME_XML_LIB=`$GNOME_CONFIG --libs xml`
+       ], [
+               if test x$2 = xfailure; then 
+                       AC_MSG_ERROR(Could not link sample xml program)
+               fi
+       ], `$GNOME_CONFIG --libs xml`)
+       AC_SUBST(GNOME_XML_LIB)
+])
+
+AC_DEFUN([GNOME_XML_CHECK], [
+       GNOME_XML_HOOK([],failure)
+])
diff --git a/apps/silcer/macros/gnome.m4 b/apps/silcer/macros/gnome.m4
new file mode 100644 (file)
index 0000000..659c22c
--- /dev/null
@@ -0,0 +1,128 @@
+dnl
+dnl GNOME_INIT_HOOK (script-if-gnome-enabled, [failflag], [additional-inits])
+dnl
+dnl if failflag is "fail" then GNOME_INIT_HOOK will abort if gnomeConf.sh
+dnl is not found. 
+dnl
+
+AC_DEFUN([GNOME_INIT_HOOK],[
+       AC_SUBST(GNOME_LIBS)
+       AC_SUBST(GNOMEUI_LIBS)
+       AC_SUBST(GNOMEGNORBA_LIBS)
+       AC_SUBST(GTKXMHTML_LIBS)
+       AC_SUBST(ZVT_LIBS)
+       AC_SUBST(GNOME_LIBDIR)
+       AC_SUBST(GNOME_INCLUDEDIR)
+
+       AC_ARG_WITH(gnome-includes,
+       [  --with-gnome-includes   Specify location of GNOME headers],[
+       CFLAGS="$CFLAGS -I$withval"
+       ])
+       
+       AC_ARG_WITH(gnome-libs,
+       [  --with-gnome-libs       Specify location of GNOME libs],[
+       LDFLAGS="$LDFLAGS -L$withval"
+       gnome_prefix=$withval
+       ])
+
+       AC_ARG_WITH(gnome,
+       [  --with-gnome            Specify prefix for GNOME files],
+               if test x$withval = xyes; then
+                       want_gnome=yes
+                       dnl Note that an empty true branch is not
+                       dnl valid sh syntax.
+                       ifelse([$1], [], :, [$1])
+               else
+                       if test "x$withval" = xno; then
+                               want_gnome=no
+                       else
+                               want_gnome=yes
+                               LDFLAGS="$LDFLAGS -L$withval/lib"
+                               CFLAGS="$CFLAGS -I$withval/include"
+                               gnome_prefix=$withval/lib
+                       fi
+               fi,
+               want_gnome=yes)
+
+       if test "x$want_gnome" = xyes; then
+
+           AC_PATH_PROG(GNOME_CONFIG,gnome-config,no)
+           if test "$GNOME_CONFIG" = "no"; then
+             no_gnome_config="yes"
+           else
+             AC_MSG_CHECKING(if $GNOME_CONFIG works)
+             if $GNOME_CONFIG --libs-only-l gnome >/dev/null 2>&1; then
+               AC_MSG_RESULT(yes)
+               GNOME_GNORBA_HOOK([],$2)
+               GNOME_LIBS="`$GNOME_CONFIG --libs-only-l gnome`"
+               GNOMEUI_LIBS="`$GNOME_CONFIG --libs-only-l gnomeui`"
+               GNOMEGNORBA_LIBS="`$GNOME_CONFIG --libs-only-l gnorba gnomeui`"
+               GTKXMHTML_LIBS="`$GNOME_CONFIG --libs-only-l gtkxmhtml`"
+               ZVT_LIBS="`$GNOME_CONFIG --libs-only-l zvt`"
+               GNOME_LIBDIR="`$GNOME_CONFIG --libs-only-L gnorba gnomeui`"
+               GNOME_INCLUDEDIR="`$GNOME_CONFIG --cflags gnorba gnomeui`"
+                $1
+             else
+               AC_MSG_RESULT(no)
+               no_gnome_config="yes"
+              fi
+            fi
+
+           if test x$exec_prefix = xNONE; then
+               if test x$prefix = xNONE; then
+                   gnome_prefix=$ac_default_prefix/lib
+               else
+                   gnome_prefix=$prefix/lib
+               fi
+           else
+               gnome_prefix=`eval echo \`echo $libdir\``
+           fi
+       
+           if test "$no_gnome_config" = "yes"; then
+              AC_MSG_CHECKING(for gnomeConf.sh file in $gnome_prefix)
+             if test -f $gnome_prefix/gnomeConf.sh; then
+               AC_MSG_RESULT(found)
+               echo "loading gnome configuration from" \
+                    "$gnome_prefix/gnomeConf.sh"
+               . $gnome_prefix/gnomeConf.sh
+               $1
+             else
+               AC_MSG_RESULT(not found)
+               if test x$2 = xfail; then
+                 AC_MSG_ERROR(Could not find the gnomeConf.sh file that is generated by gnome-libs install)
+               fi
+             fi
+            fi
+       fi
+
+       if test -n "$3"; then
+         n="$3"
+         for i in $n; do
+           AC_MSG_CHECKING(extra library \"$i\")
+           case $i in 
+             applets)
+               AC_SUBST(GNOME_APPLETS_LIBS)
+               GNOME_APPLETS_LIBS=`$GNOME_CONFIG --libs-only-l applets`
+               AC_MSG_RESULT($GNOME_APPLETS_LIBS);;
+             docklets)
+               AC_SUBST(GNOME_DOCKLETS_LIBS)
+               GNOME_DOCKLETS_LIBS=`$GNOME_CONFIG --libs-only-l docklets`
+               AC_MSG_RESULT($GNOME_DOCKLETS_LIBS);;
+             capplet)
+               AC_SUBST(GNOME_CAPPLET_LIBS)
+               GNOME_CAPPLET_LIBS=`$GNOME_CONFIG --libs-only-l capplet`
+               AC_MSG_RESULT($GNOME_CAPPLET_LIBS);;
+             *)
+               AC_MSG_RESULT(unknown library)
+           esac
+         done
+       fi
+])
+
+dnl
+dnl GNOME_INIT ([additional-inits])
+dnl
+
+AC_DEFUN([GNOME_INIT],[
+       GNOME_INIT_HOOK([],fail,$1)
+])
diff --git a/apps/silcer/macros/gperf-check.m4 b/apps/silcer/macros/gperf-check.m4
new file mode 100644 (file)
index 0000000..1b73d3f
--- /dev/null
@@ -0,0 +1,79 @@
+dnl
+dnl AC_PROG_GPERF (MINIMUM-VERSION)
+dnl
+dnl Check for availability of gperf.
+dnl Abort if not found or if current version is not up to par.
+dnl
+
+AC_DEFUN([AC_PROG_GPERF],[
+       AC_PATH_PROG(GPERF, gperf, no)
+       if test "$GPERF" = no; then
+               AC_MSG_ERROR(Could not find gperf)
+       fi
+       min_gperf_version=ifelse([$1], ,2.7,$1)
+       AC_MSG_CHECKING(for gperf - version >= $min_gperf_version)
+       gperf_major_version=`$GPERF --version | \
+               sed 's/GNU gperf \([[0-9]]*\).\([[0-9]]*\)/\1/'`
+       gperf_minor_version=`$GPERF --version | \
+               sed 's/GNU gperf \([[0-9]]*\).\([[0-9]]*\)/\2/'`
+       no_gperf=""
+dnl
+dnl Now check if the installed gperf is sufficiently new.
+dnl
+       AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char*
+my_strdup (char *str)
+{
+  char *new_str;
+  
+  if (str)
+    {
+      new_str = malloc ((strlen (str) + 1) * sizeof(char));
+      strcpy (new_str, str);
+    }
+  else
+    new_str = NULL;
+  
+  return new_str;
+}
+
+int 
+main ()
+{
+  char  *tmp_version;
+  
+  int    major;
+  int    minor;
+
+  /* HP/UX 9 (%@#!) writes to sscanf strings */
+  tmp_version = my_strdup("$min_gperf_version");
+  if (sscanf(tmp_version, "%d.%d", &major, &minor) != 2) {
+    printf ("%s, bad version string\n", "$min_gperf_version");
+    exit (1);
+  }
+
+  if (($gperf_major_version > major) ||
+      (($gperf_major_version == major) && ($gperf_minor_version >= minor))) {
+    return 0;
+  } else {
+    printf ("\n");
+    printf ("*** An old version of gperf ($gperf_major_version.$gperf_minor_version) was found.\n");
+    printf ("*** You need a version of gperf newer than %d.%d.%d.  The latest version of\n",
+              major, minor);
+    printf ("*** gperf is always available from ftp://ftp.gnu.org.\n");
+    printf ("***\n");
+    return 1;
+  }
+}
+],,no_gperf=yes,[/bin/true])
+       if test "x$no_gperf" = x ; then
+               AC_MSG_RESULT(yes)
+       else
+               AC_MSG_RESULT(no)
+       fi
+
+])
diff --git a/apps/silcer/macros/linger.m4 b/apps/silcer/macros/linger.m4
new file mode 100644 (file)
index 0000000..1dfb89d
--- /dev/null
@@ -0,0 +1,28 @@
+dnl
+dnl Check for struct linger
+dnl
+AC_DEFUN([AC_STRUCT_LINGER], [
+av_struct_linger=no
+AC_MSG_CHECKING(struct linger is available)
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/socket.h>
+
+struct linger li;
+
+main ()
+{
+    li.l_onoff = 1;
+    li.l_linger = 120;
+    exit (0);
+}
+],[
+AC_DEFINE(HAVE_STRUCT_LINGER)
+av_struct_linger=yes
+],[
+av_struct_linger=no
+],[
+av_struct_linger=no
+])
+AC_MSG_RESULT($av_struct_linger)
+])
diff --git a/apps/silcer/macros/need-declaration.m4 b/apps/silcer/macros/need-declaration.m4
new file mode 100644 (file)
index 0000000..8a217b8
--- /dev/null
@@ -0,0 +1,42 @@
+dnl See whether we need a declaration for a function.
+dnl GCC_NEED_DECLARATION(FUNCTION [, EXTRA-HEADER-FILES])
+AC_DEFUN([GCC_NEED_DECLARATION],
+[AC_MSG_CHECKING([whether $1 must be declared])
+AC_CACHE_VAL(gcc_cv_decl_needed_$1,
+[AC_TRY_COMPILE([
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+$2],
+[char *(*pfn) = (char *(*)) $1],
+eval "gcc_cv_decl_needed_$1=no", eval "gcc_cv_decl_needed_$1=yes")])
+if eval "test \"`echo '$gcc_cv_decl_needed_'$1`\" = yes"; then
+  AC_MSG_RESULT(yes)
+  gcc_need_declarations="$gcc_need_declarations $1"
+  gcc_tr_decl=NEED_DECLARATION_`echo $1 | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  AC_DEFINE_UNQUOTED($gcc_tr_decl)
+else
+  AC_MSG_RESULT(no)
+fi
+])dnl
+
+dnl Check multiple functions to see whether each needs a declaration.
+dnl GCC_NEED_DECLARATIONS(FUNCTION... [, EXTRA-HEADER-FILES])
+AC_DEFUN([GCC_NEED_DECLARATIONS],
+[for ac_func in $1
+do
+GCC_NEED_DECLARATION($ac_func, $2)
+done
+]
+)
diff --git a/apps/silcer/missing b/apps/silcer/missing
new file mode 100755 (executable)
index 0000000..0a7fb5a
--- /dev/null
@@ -0,0 +1,283 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+case "$1" in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case "$1" in
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  tar          try tar, gnutar, gtar, then tar without non-portable flags
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]"
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing 0.3 - GNU automake"
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+  aclocal)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`${configure_ac}'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case "$f" in
+      *:*) touch_files="$touch_files "`echo "$f" |
+                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+          sed 's/\.am$/.in/' |
+          while read f; do touch "$f"; done
+    ;;
+
+  bison|yacc)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+       case "$LASTARG" in
+       *.y)
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" y.tab.c
+           fi
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" y.tab.h
+           fi
+         ;;
+       esac
+    fi
+    if [ ! -f y.tab.h ]; then
+       echo >y.tab.h
+    fi
+    if [ ! -f y.tab.c ]; then
+       echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex|flex)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+       case "$LASTARG" in
+       *.l)
+           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" lex.yy.c
+           fi
+         ;;
+       esac
+    fi
+    if [ ! -f lex.yy.c ]; then
+       echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  help2man)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+        you modified a dependency of a manual page.  You may need the
+        \`Help2man' package in order for those modifications to take
+        effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+       file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+    fi
+    if [ -f "$file" ]; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo ".ab help2man is required to generate this page"
+       exit 1
+    fi
+    ;;
+
+  makeinfo)
+    if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
+       # We have makeinfo, but it failed.
+       exit 1
+    fi
+
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+      file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+    fi
+    touch $file
+    ;;
+
+  tar)
+    shift
+    if test -n "$run"; then
+      echo 1>&2 "ERROR: \`tar' requires --run"
+      exit 1
+    fi
+
+    # We have already tried tar in the generic part.
+    # Look for gnutar/gtar before invocation to avoid ugly error
+    # messages.
+    if (gnutar --version > /dev/null 2>&1); then
+       gnutar ${1+"$@"} && exit 0
+    fi
+    if (gtar --version > /dev/null 2>&1); then
+       gtar ${1+"$@"} && exit 0
+    fi
+    firstarg="$1"
+    if shift; then
+       case "$firstarg" in
+       *o*)
+           firstarg=`echo "$firstarg" | sed s/o//`
+           tar "$firstarg" ${1+"$@"} && exit 0
+           ;;
+       esac
+       case "$firstarg" in
+       *h*)
+           firstarg=`echo "$firstarg" | sed s/h//`
+           tar "$firstarg" ${1+"$@"} && exit 0
+           ;;
+       esac
+    fi
+
+    echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+         You may want to install GNU tar or Free paxutils, or check the
+         command line arguments."
+    exit 1
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and you do not seem to have it handy on your
+         system.  You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequirements for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
diff --git a/apps/silcer/mkinstalldirs b/apps/silcer/mkinstalldirs
new file mode 100755 (executable)
index 0000000..6b3b5fc
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp"
+
+        mkdir "$pathcomp" || lasterr=$?
+
+        if test ! -d "$pathcomp"; then
+         errstatus=$lasterr
+        fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/apps/silcer/pixmaps/folder.xpm b/apps/silcer/pixmaps/folder.xpm
new file mode 100644 (file)
index 0000000..6ad12c5
--- /dev/null
@@ -0,0 +1,403 @@
+/* XPM */
+static char * folder_xpm[] = {
+"48 48 352 2",
+"      c None",
+".     c #101110",
+"+     c #000000",
+"@     c #1D1F1C",
+"#     c #D3D9D2",
+"$     c #E7E8E7",
+"%     c #0D0E0D",
+"&     c #202320",
+"*     c #ABB9AA",
+"=     c #C5CFC4",
+"-     c #F4F5F4",
+";     c #BABABA",
+">     c #080908",
+",     c #010101",
+"'     c #212421",
+")     c #B1BEAF",
+"!     c #DEE3DE",
+"~     c #F8F9F8",
+"{     c #7F7B80",
+"]     c #272B27",
+"^     c #060706",
+"/     c #FAFAFA",
+"(     c #B3B3B3",
+"_     c #2B2E2B",
+":     c #B9C4B7",
+"<     c #E9ECE9",
+"[     c #C2C8C1",
+"}     c #1D201D",
+"|     c #E0E5E0",
+"1     c #E7E8E8",
+"2     c #FFFFFF",
+"3     c #FEFEFE",
+"4     c #DADADA",
+"5     c #4F554E",
+"6     c #9AA798",
+"7     c #AAB8A9",
+"8     c #4D5B4C",
+"9     c #353B34",
+"0     c #E9EBE9",
+"a     c #FDFDFD",
+"b     c #F6F6F6",
+"c     c #252625",
+"d     c #3D423D",
+"e     c #7D877C",
+"f     c #99A598",
+"g     c #576655",
+"h     c #2D332C",
+"i     c #A8B6A7",
+"j     c #ADBAAC",
+"k     c #E7EBE6",
+"l     c #F9FAF9",
+"m     c #535653",
+"n     c #1A1C19",
+"o     c #575F56",
+"p     c #818B80",
+"q     c #A3B0A1",
+"r     c #697867",
+"s     c #D1D7D0",
+"t     c #383A37",
+"u     c #5B635A",
+"v     c #C9D2C8",
+"w     c #201E20",
+"x     c #FBFBFB",
+"y     c #FCFDFC",
+"z     c #BABDBA",
+"A     c #050605",
+"B     c #3B3F3A",
+"C     c #757F74",
+"D     c #A0AE9F",
+"E     c #C3CDC2",
+"F     c #E7EAE7",
+"G     c #AFAFAF",
+"H     c #343833",
+"I     c #B0BDAF",
+"J     c #F7F8F7",
+"K     c #B4B4B4",
+"L     c #0A0B0A",
+"M     c #C4C5C4",
+"N     c #FCFCFC",
+"O     c #5E615E",
+"P     c #0E0F0E",
+"Q     c #525851",
+"R     c #909C8F",
+"S     c #F0F2EF",
+"T     c #5C5B5C",
+"U     c #1D1F1D",
+"V     c #D1D7D1",
+"W     c #FDFDFC",
+"X     c #F5F5F6",
+"Y     c #464546",
+"Z     c #474747",
+"`     c #B2B6B1",
+" .    c #242724",
+"..    c #262926",
+"+.    c #757E73",
+"@.    c #A4B1A2",
+"#.    c #BEC8BD",
+"$.    c #ECEFEC",
+"%.    c #0F100F",
+"&.    c #1C1D1B",
+"*.    c #869285",
+"=.    c #ECEEEC",
+"-.    c #BBB5BC",
+";.    c #212221",
+">.    c #ACACAC",
+",.    c #F2F3F2",
+"'.    c #8B8F8A",
+").    c #000100",
+"!.    c #434942",
+"~.    c #95A194",
+"{.    c #AEBBAC",
+"].    c #D4DAD3",
+"^.    c #868586",
+"/.    c #030403",
+"(.    c #080907",
+"_.    c #4F554F",
+":.    c #C8D1C8",
+"<.    c #F7F7F7",
+"[.    c #E4E5E4",
+"}.    c #454645",
+"|.    c #3C3E3B",
+"1.    c #F5F5F5",
+"2.    c #BDC1BC",
+"3.    c #252825",
+"4.    c #232623",
+"5.    c #717A6F",
+"6.    c #B2BEB1",
+"7.    c #DCE2DB",
+"8.    c #CED1CE",
+"9.    c #121312",
+"0.    c #262825",
+"a.    c #B3C0B2",
+"b.    c #EBEDEB",
+"c.    c #777877",
+"d.    c #2F302F",
+"e.    c #ACAEAB",
+"f.    c #666A65",
+"g.    c #4C524B",
+"h.    c #9CA99A",
+"i.    c #C7D0C5",
+"j.    c #D4D8D3",
+"k.    c #585A57",
+"l.    c #181A18",
+"m.    c #ADBBAC",
+"n.    c #D3DAD2",
+"o.    c #F3F4F3",
+"p.    c #C4C3C4",
+"q.    c #505050",
+"r.    c #757875",
+"s.    c #E6E6E6",
+"t.    c #F9F9F9",
+"u.    c #FBFCFB",
+"v.    c #ABB1AA",
+"w.    c #161716",
+"x.    c #454B44",
+"y.    c #A9B6A7",
+"z.    c #B7C3B6",
+"A.    c #141514",
+"B.    c #131513",
+"C.    c #B6C1B5",
+"D.    c #E3E7E2",
+"E.    c #E0E1E0",
+"F.    c #767676",
+"G.    c #595959",
+"H.    c #A9ACA9",
+"I.    c #DFE3DF",
+"J.    c #5D615C",
+"K.    c #9EAB9C",
+"L.    c #BCC4BB",
+"M.    c #545E53",
+"N.    c #0B0C0B",
+"O.    c #141614",
+"P.    c #A6B3A6",
+"Q.    c #B3BFB2",
+"R.    c #CCD4CB",
+"S.    c #E6E9E6",
+"T.    c #A4A4A4",
+"U.    c #5D5C5D",
+"V.    c #5A5D5A",
+"W.    c #DEDEDE",
+"X.    c #0C0D0C",
+"Y.    c #7A8479",
+"Z.    c #747D72",
+"`.    c #8E998C",
+" +    c #8A9988",
+".+    c #364035",
+"++    c #191B18",
+"@+    c #030303",
+"#+    c #7E887D",
+"$+    c #B2BFB1",
+"%+    c #C6CFC5",
+"&+    c #D9E0D9",
+"*+    c #E0E0E0",
+"=+    c #747474",
+"-+    c #282B28",
+";+    c #B6B6B6",
+">+    c #E3E3E3",
+",+    c #CACBCA",
+"'+    c #646C63",
+")+    c #676F66",
+"!+    c #697168",
+"~+    c #9DAA9B",
+"{+    c #728170",
+"]+    c #1C221B",
+"^+    c #090A09",
+"/+    c #4F564E",
+"(+    c #BDC7BC",
+"_+    c #C6D0C6",
+":+    c #D6DCD5",
+"<+    c #464F44",
+"[+    c #404540",
+"}+    c #999999",
+"|+    c #BEBEBE",
+"1+    c #BCBCBC",
+"2+    c #566554",
+"3+    c #121611",
+"4+    c #1C1E1C",
+"5+    c #A8B5A7",
+"6+    c #4C5B4A",
+"7+    c #BFBFBF",
+"8+    c #757675",
+"9+    c #040504",
+"0+    c #374635",
+"a+    c #151914",
+"b+    c #0A0B09",
+"c+    c #869184",
+"d+    c #526150",
+"e+    c #868686",
+"f+    c #C0C0C0",
+"g+    c #5E5E5E",
+"h+    c #7B857A",
+"i+    c #93A291",
+"j+    c #263025",
+"k+    c #191C19",
+"l+    c #202520",
+"m+    c #7B8679",
+"n+    c #677764",
+"o+    c #151815",
+"p+    c #6C6C6C",
+"q+    c #C2C2C2",
+"r+    c #585858",
+"s+    c #929D90",
+"t+    c #6A7968",
+"u+    c #1D231C",
+"v+    c #141714",
+"w+    c #2B332A",
+"x+    c #798578",
+"y+    c #6C7C69",
+"z+    c #454545",
+"A+    c #4A4A4A",
+"B+    c #A6B3A4",
+"C+    c #3E4D3C",
+"D+    c #181D18",
+"E+    c #313B30",
+"F+    c #6D7A6B",
+"G+    c #72826F",
+"H+    c #313930",
+"I+    c #242524",
+"J+    c #292C29",
+"K+    c #717970",
+"L+    c #394637",
+"M+    c #121512",
+"N+    c #212621",
+"O+    c #545F52",
+"P+    c #A6B5A4",
+"Q+    c #748372",
+"R+    c #374235",
+"S+    c #1C1D1C",
+"T+    c #969696",
+"U+    c #161616",
+"V+    c #474D46",
+"W+    c #758473",
+"X+    c #2B352A",
+"Y+    c #242A23",
+"Z+    c #455043",
+"`+    c #9DAC9B",
+" @    c #3B4639",
+".@    c #272827",
+"+@    c #7B7B7B",
+"@@    c #111111",
+"#@    c #596158",
+"$@    c #4A5948",
+"%@    c #131613",
+"&@    c #859483",
+"*@    c #808F7E",
+"=@    c #1F211E",
+"-@    c #7D7D7D",
+";@    c #080808",
+">@    c #666E65",
+",@    c #798677",
+"'@    c #2D392C",
+")@    c #333D32",
+"!@    c #869584",
+"~@    c #394437",
+"{@    c #2D312C",
+"]@    c #687067",
+"^@    c #5D6C5B",
+"/@    c #181E17",
+"(@    c #2C342B",
+"_@    c #566653",
+":@    c #899887",
+"<@    c #8C9B8A",
+"[@    c #404D3E",
+"}@    c #070807",
+"|@    c #687267",
+"1@    c #344132",
+"2@    c #010201",
+"3@    c #232A23",
+"4@    c #4F5E4D",
+"5@    c #738271",
+"6@    c #95A493",
+"7@    c #222821",
+"8@    c #222522",
+"9@    c #4E5A4D",
+"0@    c #080A07",
+"a@    c #20261F",
+"b@    c #546552",
+"c@    c #7A8978",
+"d@    c #A4B2A3",
+"e@    c #98A697",
+"f@    c #526350",
+"g@    c #30382F",
+"h@    c #2A2D2A",
+"i@    c #5C645B",
+"j@    c #192018",
+"k@    c #232823",
+"l@    c #536451",
+"m@    c #5A6B58",
+"n@    c #8F9E8D",
+"o@    c #3D453C",
+"p@    c #353F34",
+"q@    c #272B26",
+"r@    c #2D352C",
+"s@    c #8E9D8C",
+"t@    c #525C51",
+"u@    c #4D554C",
+"v@    c #1A2019",
+"w@    c #3E443D",
+"x@    c #242924",
+"y@    c #596956",
+"z@    c #495847",
+"A@    c #1E211E",
+"B@    c #252D24",
+"C@    c #313730",
+"D@    c #323A31",
+"E@    c #4F5F4C",
+"F@    c #334031",
+"G@    c #272C27",
+"H@    c #141713",
+"I@    c #545D52",
+"J@    c #323831",
+"K@    c #10130F",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                        . +                                                     ",
+"                                      @ # $ % +                                                 ",
+"                                      & * = - ; > ,                                             ",
+"                +           + + +     ' * * ) ! ~ { . +                                         ",
+"              + ] ^         + / ( +   _ * * * * : < [ }                                         ",
+"              + | 1 + +     + 2 3 4 + 5 6 7 * * * * 8 9                                         ",
+"              + 7 0   + +   + a 2 2 b c d e f * * * g h +                                       ",
+"              + i j k     + + 3 2 3 2 l m n o p q * r s t + +                                   ",
+"                u * * v a   + w x 3 3 x y z A B C 6 D E F G ^ + +                               ",
+"                H * * * I J a K L M N a N a F O P Q R * ) ! S T + +                             ",
+"                U * * * * * V W X Y Z b 3 2 / y `  ...+.@.* #.$.$ %.+ +                         ",
+"                &.*.* * * * * I =.N -.;.>.3 2 2 x ,.'.).!.~.* {.].$.^./.+ +                     ",
+"                (._.* * * * * * * :.<.[.}.|.1.3 2 a 2 2.3.4.5.@.* 6.7.8.9.+ +                   ",
+"                  0.* * * * * * * * a.b.b.c.d.e.3 2 2 x $.f.> g.h.* * i.j.k.A +                 ",
+"                  l.* * * * * * * * * m.n.o.p.q.r.s.a 2 t.u.v.w.x.R y.* z.7.` A.+               ",
+"                  B.* * * * * * * * * * * C.D.E.F.G.H.b 2 2 a I.J.' +.K.* ) L.M.N.              ",
+"                  O.P.* * * * * * * * * * * Q.R.S.T.U.V.W.N 2 3 F X.Y.Z.`.7  +.+++              ",
+"                  @+#+* * * * * * * * * * * * $+%+&+*+=+-+;+>+a ,+X.'+)+!+~+{+]+^+              ",
+"                  + /+* * * * * * * * * * * * * * (+_+:+<+[+}+|+1+N.)+)+)+~+2+3+                ",
+"                    4+K.* * * * * * * * * * * * * * $+5+6+^+}+7+8+9+)+)+)+~+0+a+                ",
+"                    b+c+* * * * * * * * * * * * * * * * d+L e+f+g+% )+)+h+i+j+k+                ",
+"                      l+m+* * * * * * * * * * * * * * * n+o+p+q+r+9.)+)+s+t+u+v+                ",
+"                        w+x+* * * * * * * * * * * * * * y+w+z+f+A+n )+)+B+C+D+                  ",
+"                          E+F+* * * * * * * * * * * * * G+H+I+G J+..)+K+i+L+M+                  ",
+"                            N+O+P+* * * * * * * * * * * Q+R+S+T+U+V+)+h+W+X+^+                  ",
+"                              Y+Z+`+* * * * * * * * * * Q+ @.@+@@@#@)+c+$@%@                    ",
+"                                %@E+&@7 * * * * * * * * *@ @=@-@;@>@)+,@'@9+                    ",
+"                                  X.)@G+q * * * * * * * !@~@{@J+> )+]@^@/@+ +                   ",
+"                                    9+(@_@:@* * * * * * <@[@h }@w.)+|@1@2@+ + + +               ",
+"                                      }@3@4@5@`+* * * * 6@6+7@, 8@)+9@0@+ + + + + + +           ",
+"                                          a@$@b@c@d@* * e@f@g@A h@i@j@+ + + + + + + +           ",
+"                                            k@ @l@m@n@* e@l@o@9+5 p@2@+ + + + + + +             ",
+"                                              q@r@4@l@c@s@l@t@% u@v@+ + + + + + +               ",
+"                                                w@x@~@l@y@z@i@A@B@A + + + + +                   ",
+"                                                    C@D@E@F@o@G@H@+ + + + +                     ",
+"                                                      I@J@K@+ + + + + +                         ",
+"                                                            + + + +                             ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                ",
+"                                                                                                "};
diff --git a/apps/silcer/pixmaps/glade-group.xpm b/apps/silcer/pixmaps/glade-group.xpm
new file mode 100644 (file)
index 0000000..44a7a2c
--- /dev/null
@@ -0,0 +1,29 @@
+/* XPM */
+static char * glade_group_xpm[] = {
+"16 16 10 1",
+"      c None",
+".     c #020204",
+"+     c #826E54",
+"@     c #9A8A76",
+"#     c #AD9E89",
+"$     c #BAAE98",
+"%     c #E0D9CF",
+"&     c #D2CAC0",
+"*     c #C6BEB0",
+"=     c #EDE9DF",
+"                ",
+" ....           ",
+".++@#.          ",
+".+##$.......    ",
+".....%&&**$#.   ",
+".==%%&&**$$#.   ",
+".=%%&&*$$$##.   ",
+".%%&&**$$##@.   ",
+".%&&*$$$##@@.   ",
+".&&**$$##@@+.   ",
+"..*$$$##@@++.   ",
+" ...........    ",
+"                ",
+"                ",
+"                ",
+"                "};
diff --git a/apps/silcer/po/Makefile.in.in b/apps/silcer/po/Makefile.in.in
new file mode 100644 (file)
index 0000000..0636f0c
--- /dev/null
@@ -0,0 +1,210 @@
+# Makefile for program source directory in GNU NLS utilities package.
+# Copyright (C) 1995-1997, 2000, 2001 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file file be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU Public License
+# but which still want to provide support for the GNU gettext functionality.
+# Please note that the actual code is *not* freely available.
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+SHELL = /bin/sh
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datadir = @datadir@
+localedir = $(datadir)/locale
+gettextsrcdir = $(datadir)/gettext/po
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+mkinstalldirs = $(SHELL) `case "$(MKINSTALLDIRS)" in /*) echo "$(MKINSTALLDIRS)" ;; *) echo "$(top_builddir)/$(MKINSTALLDIRS)" ;; esac`
+
+CC = @CC@
+GMSGFMT = @GMSGFMT@
+MSGFMT = @MSGFMT@
+XGETTEXT = @XGETTEXT@
+XML_I18N_UPDATE = @XML_I18N_UPDATE@
+XML_I18N_EXTRACT = @XML_I18N_EXTRACT@
+
+MSGMERGE = XML_I18N_EXTRACT=$(XML_I18N_EXTRACT) $(XML_I18N_UPDATE) --dist
+GENPOT   = XML_I18N_EXTRACT=$(XML_I18N_EXTRACT) $(XML_I18N_UPDATE) --pot
+
+
+DEFS = @DEFS@
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+INCLUDES = -I.. -I$(top_srcdir)/intl
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(PACKAGE).pot \
+$(POFILES) $(GMOFILES)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+
+.SUFFIXES:
+.SUFFIXES: .c .o .po .pox .gmo .mo
+
+.c.o:
+       $(COMPILE) $<
+
+.po.pox:
+       $(MAKE) $(PACKAGE).pot
+       $(MSGMERGE) $< $(srcdir)/$(PACKAGE).pot -o $*.pox
+
+.po.mo:
+       $(MSGFMT) -o $@ $<
+
+.po.gmo:
+       file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \
+         && rm -f $$file && $(GMSGFMT) --statistics -o $$file $<
+
+
+all: all-@USE_NLS@
+
+all-yes: $(CATALOGS)
+all-no:
+
+# Note: Target 'all' must not depend on target '$(srcdir)/$(PACKAGE).pot',
+# otherwise packages like GCC can not be built if only parts of the source
+# have been downloaded.
+
+$(srcdir)/$(PACKAGE).pot: $(POTFILES) $(srcdir)/POTFILES.in
+       $(GENPOT)
+
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-@USE_NLS@
+       if test "$(PACKAGE)" = "gettext"; then \
+         $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
+         $(INSTALL_DATA) $(srcdir)/Makefile.in.in \
+                         $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+       else \
+         : ; \
+       fi
+install-data-no: all
+install-data-yes: all
+       $(mkinstalldirs) $(DESTDIR)$(datadir)
+       @catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+         dir=$(localedir)/$$lang/LC_MESSAGES; \
+         $(mkinstalldirs) $(DESTDIR)$$dir; \
+         if test -r $$cat; then \
+           $(INSTALL_DATA) $$cat $(DESTDIR)$$dir/$(PACKAGE).mo; \
+           echo "installing $$cat as $(DESTDIR)$$dir/$(PACKAGE).mo"; \
+         else \
+           $(INSTALL_DATA) $(srcdir)/$$cat $(DESTDIR)$$dir/$(PACKAGE).mo; \
+           echo "installing $(srcdir)/$$cat as" \
+                "$(DESTDIR)$$dir/$(PACKAGE).mo"; \
+         fi; \
+       done
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+       catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+         rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(PACKAGE).mo; \
+       done
+       if test "$(PACKAGE)" = "gettext"; then \
+         rm -f $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \
+       else \
+         : ; \
+       fi
+
+check: all
+
+dvi info tags TAGS ID:
+
+mostlyclean:
+       rm -f core core.* *.pox $(PACKAGE).po *.new.po
+       rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile Makefile.in POTFILES *.mo
+
+maintainer-clean: distclean
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+       rm -f $(GMOFILES)
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir:
+       $(MAKE) update-po
+       @$(MAKE) dist2
+# This is a separate target because 'update-po' must be executed before.
+dist2: $(DISTFILES)
+       dists="$(DISTFILES)"; \
+       for file in $$dists; do \
+         if test -f $$file; then dir=.; else dir=$(srcdir); fi; \
+         cp -p $$dir/$$file $(distdir); \
+       done
+
+update-po: Makefile
+       $(MAKE) $(PACKAGE).pot
+       if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; fi; \
+       cd $(srcdir); \
+       catalogs='$(GMOFILES)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed 's/\.gmo$$//'`; \
+         cp $$lang.po $$lang.old.po; \
+         echo "$$lang:"; \
+         if $(MSGMERGE) $$lang ; then \
+           rm -f $$lang.old.po; \
+         else \
+           echo "msgmerge for $$cat failed!"; \
+           mv $$lang.old.po $$lang.po; \
+         fi; \
+       done
+       $(MAKE) update-gmo
+
+.po: Makefile
+       $(MAKE)  $(PACKAGE).pot;
+       PATH=`pwd`/../src:$$PATH; \
+       echo; printf "$*: "; \
+       if $(MSGMERGE) $*; then \
+         rm -f $*.old.po; \
+         else \
+           echo "msgmerge for * failed!"; \
+           mv $*.old.po $*.po; \
+       fi; \
+       msgfmt --statistics $*.po; echo;
+
+
+update-gmo: Makefile $(GMOFILES)
+       @:
+
+Makefile: Makefile.in.in $(top_builddir)/config.status POTFILES.in
+       cd $(top_builddir) \
+         && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+              $(SHELL) ./config.status
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/apps/silcer/po/POTFILES.in b/apps/silcer/po/POTFILES.in
new file mode 100644 (file)
index 0000000..ab2d29e
--- /dev/null
@@ -0,0 +1,6 @@
+# List of source files containing translatable strings.
+
+src/main.c
+src/interface.c
+src/callbacks.c
+src/support.c
diff --git a/apps/silcer/silcer.png b/apps/silcer/silcer.png
new file mode 100644 (file)
index 0000000..c80c08d
Binary files /dev/null and b/apps/silcer/silcer.png differ
diff --git a/apps/silcer/src/Makefile.am b/apps/silcer/src/Makefile.am
new file mode 100644 (file)
index 0000000..42e5f17
--- /dev/null
@@ -0,0 +1,53 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+silc_top_srcdir = ../../
+
+bin_PROGRAMS = silcer
+silcer_SOURCES = silcer.cc             \
+               silcerapp.cc            \
+               silcerbasewin.cc        \
+               silcerchatview.cc       \
+               xtext.c                 \
+               gtkurl.c                \
+               gtkspell.c              \
+               SilcerMainDlg.cc
+
+silcer_LDADD = @EXTRA_GNOME_LIBS@ -lxml \
+       -L$(silc_top_srcdir)/lib -lsilcclient -lsilc
+silcer_LDFLAGS =
+
+INCLUDES = @EXTRA_GNOME_CFLAGS@ -DENABLE_NLS \
+       -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+       -DDATADIR=\"$(datadir)\" \
+       -I$(top_srcdir)/intl \
+       -I$(silc_top_srcdir)/lib/silccore \
+       -I$(silc_top_srcdir)/lib/silccrypt \
+        -I$(silc_top_srcdir)/lib/silcmath \
+        -I$(silc_top_srcdir)/lib/silcmath/mpi \
+       -I$(silc_top_srcdir)/lib/silcske \
+       -I$(silc_top_srcdir)/lib/silcsim \
+       -I$(silc_top_srcdir)/lib/silcutil \
+       -I$(silc_top_srcdir)/lib/silcsftp \
+       -I$(silc_top_srcdir)/lib/silcclient \
+       -I$(silc_top_srcdir)/lib/contrib \
+        -I$(silc_top_srcdir)/includes \
+        -I$(silc_top_srcdir)/doc \
+       -I$(silc_top_srcdir)/lib/trq
+
+EXTRA_DIST = 
diff --git a/apps/silcer/src/SilcerMainDlg.cc b/apps/silcer/src/SilcerMainDlg.cc
new file mode 100644 (file)
index 0000000..fb3dd93
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+
+  SilcerMainDlg.cc 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcerapp.hh"
+#include "silcerchatview.hh"
+#include "SilcerMainDlg.hh"
+#include "gtkspell.h"
+
+#include <libgnomeui/gnome-window-icon.h>   
+#include <libgnome/gnome-help.h>
+#include <gtk--/style.h>
+#include <gnome--/href.h>
+#include <gtk--/text.h>
+#include <gtk--/toolbar.h>
+#include <gtk--/notebook.h>
+
+static char *parse_command(const char *buffer)
+{
+  char *ret;
+  char *cp = (char *)buffer;
+  int len;
+
+  len = strcspn((const char *)cp, " ");
+  ret = silc_to_upper(cp + 1);
+  ret[len - 1] = 0;
+  return ret;
+}
+
+SilcerMainDlg::SilcerMainDlg(void) : SilcerBaseDialog("SilcerMainDlg")
+{
+  _thisWindow->realize();
+
+  // Get intput box and set it for input
+  _InputBox = getWidget<Gtk::Text>("SilcerMainDlgInputBox");
+  _InputBox->key_press_event.connect(slot(this,
+                                         &SilcerMainDlg::InputBoxKeyPress));
+  _Completer = g_completion_new(NULL);
+
+  // Get output box and create new chat view box
+  _OutputBox = getWidget<Gtk::HBox>("SilcerMainDlgOutputBox");
+  _ChatView = new SilcerChatView(_thisWindow, _OutputBox, false);
+
+  // Show only icons on toolbar
+  getWidget<Gtk::Toolbar>("SilcerMainDlgToolbar")->
+    set_style(GTK_TOOLBAR_ICONS);
+
+  // Hide tabs, since they are not used currently!
+  getWidget<Gtk::Notebook>("SilcerMainDlgTab")->set_show_tabs(false);
+
+  _thisWindow->show();
+}
+
+SilcerMainDlg::~SilcerMainDlg(void)
+{
+
+}
+
+void SilcerMainDlg::print(const string message)
+{
+  _ChatView->render(message, "", "", BLUE);
+}
+
+void SilcerMainDlg::print(const string message, const string nickname)
+{
+  _ChatView->render(message, nickname, "", BLUE2);
+}
+
+gint SilcerMainDlg::InputBoxKeyPress(GdkEventKey *key)
+{
+  string msg;
+
+  switch (key->keyval) {
+  case GDK_space:
+    if (gtkspell_running())
+      gtkspell_check_all(_InputBox->gtkobj());
+    break;
+
+  case GDK_Return:
+  case GDK_KP_Enter:
+    if (key->state & GDK_SHIFT_MASK) {
+      key->state ^= GDK_SHIFT_MASK;
+      return 0;
+    }
+
+    // Parse message to see whether it is command
+    msg = _InputBox->get_chars(0, -1);
+    if (msg.empty()) {
+      _InputBox->delete_text(0, -1);
+      gtk_signal_emit_stop_by_name(GTK_OBJECT(_InputBox->gtkobj()), 
+                                  "key_press_event");
+      break;
+    }
+
+    if (msg.at(0) == '/') {
+      // Command
+      SilcClientCommand cmd;
+      SilcClientCommandContext ctx;
+      char *tmpcmd;
+      uint32 argc = 0;
+      unsigned char **argv;
+      uint32 *argv_lens, *argv_types;
+
+      // Parse arguments
+      tmpcmd = parse_command(msg.c_str());
+      cmd = silc_client_command_find(silc_client, (const char *)tmpcmd);
+      silc_free(tmpcmd);
+      if (cmd == NULL)
+       break;
+
+      silc_parse_command_line((unsigned char *)msg.c_str(), &argv, &argv_lens,
+                             &argv_types, &argc, cmd->max_args);
+
+      ctx = silc_client_command_alloc();
+      ctx->client = silc_client;
+      ctx->conn = silc_client_conn;
+      ctx->command = cmd;
+      ctx->argc = argc;
+      ctx->argv = argv;
+      ctx->argv_lens = argv_lens;
+      ctx->argv_types = argv_types;
+      
+      // Execute the command
+      silc_client_command_call(cmd, ctx);
+    } else {
+      // Channel message
+      if (silc_client_conn->current_channel) {
+       print(msg);
+       silc_client_send_channel_message(silc_client, 
+                                        silc_client_conn,
+                                        silc_client_conn->current_channel, 
+                                        NULL,
+                                        0, (unsigned char *)msg.c_str(), 
+                                        msg.length(), TRUE);
+      }
+    }
+
+    _InputBox->delete_text(0, -1);
+    gtk_signal_emit_stop_by_name(GTK_OBJECT(_InputBox->gtkobj()), 
+                                "key_press_event");
+    break;
+
+  case GDK_Tab:
+    {
+      // Word completion
+      msg = _InputBox->get_chars(0, -1);
+
+      if (!msg.empty()) {
+       string lastword;
+
+       // Search for the last whitespace
+       string::size_type n = msg.find_last_of(" ");
+       if (n != string::npos)
+         lastword = msg.substr(n+1);
+       else
+         lastword = msg;
+       
+       // Try and autocomplete
+       gchar *prefix;
+       g_completion_complete(_Completer, (char*)lastword.c_str(), &prefix);
+       if (prefix != NULL){
+         // Replace the last word in the message and update
+         if (n != string::npos)
+           msg.replace(msg.find_last_of(" ")+1, msg.length(), prefix);
+         else
+           msg = string(prefix);
+         g_free(prefix);
+         
+         // Update text
+         _InputBox->delete_text(0, -1);
+         _InputBox->insert(msg);
+       }
+
+       gtk_signal_emit_stop_by_name(GTK_OBJECT(_InputBox->gtkobj()), 
+                                    "key_press_event");
+       return 1;
+      }
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  return 0;
+}
diff --git a/apps/silcer/src/SilcerMainDlg.hh b/apps/silcer/src/SilcerMainDlg.hh
new file mode 100644 (file)
index 0000000..9005706
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+
+  SilcerMainDlg.hh
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCERMAINDLG_HH
+#define SILCERMAINDLG_HH
+
+#include "silcerbasewin.hh"
+#include "silcerchatview.hh"
+#include <gnome--/color-picker.h>
+#include <gtk--/spinbutton.h>
+#include <gtk--/notebook.h>
+#include <gtk--/text.h>
+
+class SilcerMainDlg : public SilcerBaseDialog
+{
+public:
+  SilcerMainDlg(void);
+  ~SilcerMainDlg(void);
+
+  // Print message to output box
+  void print(const string message);
+  void print(const string message, const string nickname);
+
+protected:
+  // Events
+  gint InputBoxKeyPress(GdkEventKey *key);
+
+private:
+  SilcerChatView *_ChatView;
+  GCompletion *_Completer;
+  Gtk::HBox *_OutputBox;
+  Gtk::Text *_InputBox;
+  Gtk::Notebook *_Tab;
+};
+
+#endif /* SILCERMAINDLG */
diff --git a/apps/silcer/src/gtkspell.c b/apps/silcer/src/gtkspell.c
new file mode 100644 (file)
index 0000000..6555e74
--- /dev/null
@@ -0,0 +1,639 @@
+/* gtkspell - a spell-checking addon for GtkText
+ * Copyright (c) 2000 Evan Martin.
+ * vim: ts=4 sw=4
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include <gtk/gtk.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <unistd.h>   
+#include <stdio.h>    
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define GTKSPELL_USE_GNOME
+
+#ifdef GTKSPELL_USE_GNOME
+#include <gnome.h>
+#endif /* GTKSPELL_USE_GNOME */
+
+#include "gtkspell.h"
+
+/* TODO:
+ * handle dictionary changes
+ * asynchronous lookups
+ */
+
+/* size of the text buffer used in various word-processing routines. */
+#define BUFSIZE 1024
+/* number of suggestions to display on each menu. */
+#define MENUCOUNT 10
+#define BUGEMAIL "gtkspell-devel@lists.sourceforge.net"
+
+/* because we keep only one copy of the spell program running, 
+ * all ispell-related variables can be static.  
+ */
+static pid_t spell_pid = -1;
+static int fd_write[2] = {0}, fd_read[2] = {0};
+static int signal_set_up = 0;
+
+/* FIXME? */
+static GdkColor highlight = { 0, 255*256, 0, 0 };
+
+static void entry_insert_cb(GtkText *gtktext, 
+               gchar *newtext, guint len, guint *ppos, gpointer d);
+static void set_up_signal();
+
+int gtkspell_running() {
+       return (spell_pid > 0);
+}
+
+static void error_print(const char *fmt, ...) {
+       va_list ap;
+       va_start(ap, fmt);
+       fprintf(stderr, "gtkspell: ");
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
+
+/* functions to interface with pipe */
+static void writetext(char *text) {
+       write(fd_write[1], text, strlen(text));
+}
+static int readpipe(char *buf, int bufsize) {
+       int len;
+       len = read(fd_read[0], buf, bufsize-1);
+       if (len < 0) {
+               error_print("read: %s\n", strerror(errno));
+               return -1;
+       } else if (len == 0) {
+               error_print("pipe closed.\n");
+               return -1;
+       } else if (len == bufsize-1) {
+               error_print("buffer overflowed?\n");
+       }
+
+       buf[len] = 0;
+       return len;
+}
+static int readline(char *buf) {
+       return readpipe(buf, BUFSIZE);
+}
+
+static int readresponse(char *buf) {
+       int len;
+       len = readpipe(buf, BUFSIZE);
+
+       /* all ispell responses of any reasonable length should end in \n\n.
+        * depending on the speed of the spell checker, this may require more
+        * reading. */
+       if (len >= 2 && (buf[len-1] != '\n' || buf[len-2] != '\n')) {
+               len += readpipe(buf+len, BUFSIZE-len);
+       }
+
+       /* now we can remove all of the the trailing newlines. */
+       while (len > 0 && buf[len-1] == '\n')
+               buf[--len] = 0;
+
+       return len;
+}
+
+
+void gtkspell_stop() {
+       if (gtkspell_running()) {
+               kill(spell_pid, SIGHUP); 
+               spell_pid = 0;
+               close(fd_read[0]);
+               close(fd_write[1]);
+       }
+}
+
+int gtkspell_start(char *path, char * args[]) {
+       int fd_error[2];
+       char buf[BUFSIZE];
+
+       if (gtkspell_running()) {
+               error_print("gtkspell_start called while already running.\n");
+               gtkspell_stop();
+       }
+
+       if (!signal_set_up) {
+               set_up_signal();
+               signal_set_up = 1;
+       }
+
+       pipe(fd_write);
+       pipe(fd_read);
+       pipe(fd_error);
+
+       spell_pid = fork();
+       if (spell_pid < 0) {
+               error_print("fork: %s\n", strerror(errno));
+               return -1;
+       } else if (spell_pid == 0) {
+               dup2(fd_write[0], 0);
+               close(fd_write[0]);
+               close(fd_write[1]);
+
+               dup2(fd_read[1], 1);
+               close(fd_read[0]);
+               close(fd_read[1]);
+
+               dup2(fd_error[1], 2);
+               close(fd_error[0]);
+               close(fd_error[1]);
+
+               if (path == NULL) {
+                       if (execvp(args[0], args) < 0) 
+                               error_print("execvp('%s'): %s\n", args[0], strerror(errno));
+               } else {
+                       if (execv(path, args) < 0) 
+                               error_print("execv('%s'): %s\n", path, strerror(errno));
+               }
+               /* if we get here, we failed.
+                * send some text on the pipe to indicate status.
+                */
+               write(0, "!", 1); /* stdout _is_ the pipe. */
+
+               _exit(0);
+       } else {
+               /* there are at least two ways to fail:
+                * - the exec() can fail
+                * - the exec() can succeed, but the program can dump the help screen
+                * we must check for both.
+                */
+               fd_set rfds;
+               struct timeval tv;
+               
+               close(fd_write[0]);
+               close(fd_read[1]);
+
+               FD_ZERO(&rfds);
+               FD_SET(fd_error[0], &rfds);
+               FD_SET(fd_read[0], &rfds);
+               tv.tv_sec = 2;
+               tv.tv_usec = 0;
+
+               if (select(MAX(fd_error[0], fd_read[0])+1, 
+                                       &rfds, NULL, NULL, &tv) < 0) {
+                       /* FIXME: is this needed? */
+                       error_print("Timed out waiting for spell command.\n");
+                       gtkspell_stop();
+                       return -1;
+               }
+
+               if (FD_ISSET(fd_error[0], &rfds)) { /* stderr readable? */
+                       error_print("Spell command printed on stderr -- probably failed.\n");
+                       gtkspell_stop();
+                       return -1;
+               }
+
+               /* we're done with stderr, now. */
+               close(fd_error[0]);
+               close(fd_error[1]);
+
+               /* otherwise, fd_read[0] is set. */
+               readline(buf);
+
+               /* ispell should print something like this:
+                * @(#) International Ispell Version 3.1.20 10/10/95
+                * if it doesn't, it's an error. */
+               if (buf[0] != '@') {
+                       gtkspell_stop();
+                       return -1;
+               }
+       }
+
+       /* put ispell into terse mode.  
+        * this makes it not respond on correctly spelled words. */
+       sprintf(buf, "!\n");
+       writetext(buf);
+       return 0;
+}
+
+static GList* misspelled_suggest(char *word) {
+       char buf[BUFSIZE];
+       char *newword;
+       GList *l = NULL;
+       int count;
+
+       sprintf(buf, "^%s\n", word); /* guard against ispell control chars */
+       writetext(buf);
+       readresponse(buf);
+
+       switch (buf[0]) { /* first char is ispell command. */
+               case 0: /* no response: word is ok. */
+                       return NULL;
+               case '&': /* misspelled, with suggestions */
+                       /* & <orig> <count> <ofs>: <miss>, <miss>, <guess>, ... */
+                       strtok(buf, " "); /* & */
+                       newword = strtok(NULL, " "); /* orig */
+                       l = g_list_append(l, g_strdup(newword));
+                       newword = strtok(NULL, " "); /* count */
+                       count = atoi(newword);
+                       strtok(NULL, " "); /* ofs: */
+
+                       while ((newword = strtok(NULL, ",")) != NULL) {
+                               int len = strlen(newword);
+                               if (newword[len-1] == ' ' || newword[len-1] == '\n') 
+                                       newword[len-1] = 0;
+                               if (count == 0) {
+                                       g_list_append(l, NULL); /* signal the "suggestions" */
+                               }
+                               /* add it to the list, skipping the initial space. */
+                               l = g_list_append(l, 
+                                               g_strdup(newword[0] == ' ' ? newword+1 : newword));
+
+                               count--;
+                       }
+                       return l;
+
+               case '#': /* misspelled, no suggestions */
+               case '?': /* ispell is guessing. */
+                       /* # <orig> <ofs> */
+                       strtok(buf, " "); /* & */
+                       newword = strtok(NULL, " "); /* orig */
+                       l = g_list_append(l, g_strdup(newword));
+                       return l;
+               default:
+                       error_print("Unsupported spell command '%c'.\n"
+                                       "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]);
+                       error_print("Input [%s]\nOutput [%s]\n", word, buf);
+
+       }
+       return NULL;
+}
+
+static int misspelled_test(char *word) {
+       char buf[BUFSIZE];
+       sprintf(buf, "^%s\n", word); /* guard against ispell control chars */
+       writetext(buf);
+       readresponse(buf);
+
+       if (buf[0] == 0) {
+               return 0;
+       } else if (buf[0] == '&' || buf[0] == '#' || buf[0] == '?') {
+               return 1;
+       }
+       
+       error_print("Unsupported spell command '%c'.\n"
+                       "This is a bug; mail " BUGEMAIL " about it.\n", buf[0]);
+       error_print("Input [%s]\nOutput [%s]\n", word, buf);
+       return -1;
+}
+
+static gboolean iswordsep(char c) {
+       return !isalpha(c) && c != '\'';
+}
+
+static gboolean get_word_from_pos(GtkText* gtktext, int pos, char* buf, 
+               int *pstart, int *pend) {
+       gint start, end;
+
+       if (iswordsep(GTK_TEXT_INDEX(gtktext, pos))) return FALSE;
+
+       for (start = pos; start >= 0; --start) {
+               if (iswordsep(GTK_TEXT_INDEX(gtktext, start))) break;
+       }
+       start++;
+
+       for (end = pos; end <= gtk_text_get_length(gtktext); end++) {
+               if (iswordsep(GTK_TEXT_INDEX(gtktext, end))) break;
+       }
+
+       if (buf) {
+               for (pos = start; pos < end; pos++) 
+                       buf[pos-start] = GTK_TEXT_INDEX(gtktext, pos);
+               buf[pos-start] = 0;
+       }
+
+       if (pstart) *pstart = start;
+       if (pend) *pend = end;
+
+       return TRUE;
+}
+
+static gboolean get_curword(GtkText* gtktext, char* buf, 
+               int *pstart, int *pend) {
+       int pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+       return get_word_from_pos(gtktext, pos, buf, pstart, pend);
+}
+
+static void change_color(GtkText *gtktext, 
+               int start, int end, GdkColor *color) {
+       char *newtext = gtk_editable_get_chars(GTK_EDITABLE(gtktext), start, end);
+       gtk_text_freeze(gtktext);
+       gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext), 
+                       GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
+       
+       gtk_text_set_point(gtktext, start);
+       gtk_text_forward_delete(gtktext, end-start);
+
+       if (newtext && end-start > 0)
+               gtk_text_insert(gtktext, NULL, color, NULL, newtext, end-start);
+
+       gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), 
+                       GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
+       gtk_text_thaw(gtktext);
+       g_free(newtext);
+}
+
+static gboolean check_at(GtkText *gtktext, int from_pos) {
+       int start, end;
+       char buf[BUFSIZE];
+
+       if (!get_word_from_pos(gtktext, from_pos, buf, &start, &end)) {
+               return FALSE;
+       }
+
+       if (misspelled_test(buf)) {
+               if (highlight.pixel == 0) {
+                       /* add an entry for the highlight in the color map. */
+                       GdkColormap *gc = gtk_widget_get_colormap(GTK_WIDGET(gtktext));
+                       gdk_colormap_alloc_color(gc, &highlight, FALSE, TRUE);;
+               }
+               change_color(gtktext, start, end, &highlight);
+               return TRUE;
+       } else { 
+               change_color(gtktext, start, end, 
+                               &(GTK_WIDGET(gtktext)->style->fg[0]));
+               return FALSE;
+       }
+}
+
+void gtkspell_check_all(GtkText *gtktext) {
+       guint origpos;
+       guint pos = 0;
+       guint len;
+       float adj_value;
+       
+       if (!gtkspell_running()) return;
+       
+       len = gtk_text_get_length(gtktext);
+
+       adj_value = gtktext->vadj->value;
+       gtk_text_freeze(gtktext);
+       origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+       while (pos < len) {
+               while (pos < len && iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
+                       pos++;
+               while (pos < len && !iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
+                       pos++;
+               if (pos > 0)
+                       check_at(gtktext, pos-1);
+       }
+       gtk_text_thaw(gtktext);
+       gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+}
+
+static void entry_insert_cb(GtkText *gtktext, 
+               gchar *newtext, guint len, guint *ppos, gpointer d) {
+       int origpos;
+
+       if (!gtkspell_running()) return;
+
+       gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
+                                                                        GTK_SIGNAL_FUNC(entry_insert_cb),
+                                                                        NULL);
+       gtk_text_insert(GTK_TEXT(gtktext), NULL,
+                       &(GTK_WIDGET(gtktext)->style->fg[0]), NULL, newtext, len);
+       gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
+                                                                        GTK_SIGNAL_FUNC(entry_insert_cb),
+                                                                        NULL);
+       gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "insert-text");
+       *ppos += len;
+
+       origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+
+       if (iswordsep(newtext[0])) {
+               /* did we just end a word? */
+               if (*ppos >= 2) check_at(gtktext, *ppos-2);
+
+               /* did we just split a word? */
+               if (*ppos < gtk_text_get_length(gtktext))
+                       check_at(gtktext, *ppos+1);
+       } else {
+               /* check as they type, *except* if they're typing at the end (the most
+                * common case.
+                */
+               if (*ppos < gtk_text_get_length(gtktext) && 
+                               !iswordsep(GTK_TEXT_INDEX(gtktext, *ppos)))
+                       check_at(gtktext, *ppos-1);
+       }
+
+       gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+       gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
+}
+
+static void entry_delete_cb(GtkText *gtktext,
+               gint start, gint end, gpointer d) {
+       int origpos;
+
+       if (!gtkspell_running()) return;
+
+       origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+       check_at(gtktext, start-1);
+       gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+       gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
+       /* this is to *UNDO* the selection, in case they were holding shift
+        * while hitting backspace. */
+}
+
+static void replace_word(GtkWidget *w, gpointer d) {
+       int start, end;
+       char *newword;
+       char buf[BUFSIZE];
+
+       /* we don't save their position, 
+        * because the cursor is moved by the click. */
+
+       gtk_text_freeze(GTK_TEXT(d));
+
+       gtk_label_get(GTK_LABEL(GTK_BIN(w)->child), &newword);
+       get_curword(GTK_TEXT(d), buf, &start, &end);
+
+       gtk_text_set_point(GTK_TEXT(d), end);
+       gtk_text_backward_delete(GTK_TEXT(d), end-start);
+       gtk_text_insert(GTK_TEXT(d), NULL, NULL, NULL, newword, strlen(newword));
+
+       gtk_text_thaw(GTK_TEXT(d));
+}
+
+static GtkMenu *make_menu(GList *l, GtkText *gtktext) {
+       GtkWidget *menu, *item;
+       char *caption;
+       menu = gtk_menu_new(); {
+               caption = g_strdup_printf("Not in dictionary: %s", (char*)l->data);
+               item = gtk_menu_item_new_with_label(caption);
+               /* I'd like to make it so this item is never selectable, like
+                * the menu titles in the GNOME panel... unfortunately, the GNOME
+                * panel creates their own custom widget to do this! */
+               gtk_widget_show(item);
+               gtk_menu_append(GTK_MENU(menu), item);
+
+               item = gtk_menu_item_new();
+               gtk_widget_show(item);
+               gtk_menu_append(GTK_MENU(menu), item);
+
+               l = l->next;
+               if (l == NULL) {
+                       item = gtk_menu_item_new_with_label("(no suggestions)");
+                       gtk_widget_show(item);
+                       gtk_menu_append(GTK_MENU(menu), item);
+               } else {
+                       GtkWidget *curmenu = menu;
+                       int count = 0;
+                       do {
+                               if (l->data == NULL && l->next != NULL) {
+                                       count = 0;
+                                       curmenu = gtk_menu_new();
+                                       item = gtk_menu_item_new_with_label("Other Possibilities...");
+                                       gtk_widget_show(item);
+                                       gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
+                                       gtk_menu_append(GTK_MENU(curmenu), item);
+                                       l = l->next;
+                               } else if (count > MENUCOUNT) {
+                                       count -= MENUCOUNT;
+                                       item = gtk_menu_item_new_with_label("More...");
+                                       gtk_widget_show(item);
+                                       gtk_menu_append(GTK_MENU(curmenu), item);
+                                       curmenu = gtk_menu_new();
+                                       gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), curmenu);
+                               }
+                               item = gtk_menu_item_new_with_label((char*)l->data);
+                               gtk_signal_connect(GTK_OBJECT(item), "activate",
+                                               GTK_SIGNAL_FUNC(replace_word), gtktext);
+                               gtk_widget_show(item);
+                               gtk_menu_append(GTK_MENU(curmenu), item);
+                               count++;
+                       } while ((l = l->next) != NULL);
+               }
+       }
+       return GTK_MENU(menu);
+}
+
+static void popup_menu(GtkText *gtktext, GdkEventButton *eb) {
+       char buf[BUFSIZE];
+       GList *list, *l;
+
+       get_curword(gtktext, buf, NULL, NULL);
+
+       list = misspelled_suggest(buf);
+       if (list != NULL) {
+               gtk_menu_popup(make_menu(list, gtktext), NULL, NULL, NULL, NULL,
+                               eb->button, eb->time);
+               for (l = list; l != NULL; l = l->next)
+                       g_free(l->data);
+               g_list_free(list);
+       }
+}
+
+/* ok, this is pretty wacky:
+ * we need to let the right-mouse-click go through, so it moves the cursor, 
+ * but we *can't* let it go through, because GtkText interprets rightclicks as
+ * weird selection modifiers.
+ *
+ * so what do we do?  forge rightclicks as leftclicks, then popup the menu. 
+ * HACK HACK HACK. 
+ */
+static gint button_press_intercept_cb(GtkText *gtktext, GdkEvent *e, gpointer d) {
+       GdkEventButton *eb;
+       gboolean retval;
+
+       if (!gtkspell_running()) return FALSE;
+
+       if (e->type != GDK_BUTTON_PRESS) return FALSE;
+       eb = (GdkEventButton*) e;
+
+       if (eb->button != 3) return FALSE;
+
+       /* forge the leftclick */
+       eb->button = 1;
+
+       gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext), 
+                       GTK_SIGNAL_FUNC(button_press_intercept_cb), d);
+       gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event",
+                       e, &retval);
+       gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), 
+                       GTK_SIGNAL_FUNC(button_press_intercept_cb), d);
+       gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event");
+
+       /* now do the menu wackiness */
+       popup_menu(gtktext, eb);
+       return TRUE;
+}
+
+void gtkspell_uncheck_all(GtkText *gtktext) {
+       int origpos;
+       char *text;
+       float adj_value;
+
+       adj_value = gtktext->vadj->value;
+       gtk_text_freeze(gtktext);
+       origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+       text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
+       gtk_text_set_point(gtktext, 0);
+       gtk_text_forward_delete(gtktext, gtk_text_get_length(gtktext));
+       gtk_text_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
+       gtk_text_thaw(gtktext);
+
+       gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+       gtk_adjustment_set_value(gtktext->vadj, adj_value);
+}
+
+void gtkspell_attach(GtkText *gtktext) {
+       gtk_signal_connect(GTK_OBJECT(gtktext), "insert-text",
+               GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
+       gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text",
+               GTK_SIGNAL_FUNC(entry_delete_cb), NULL);
+       gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event",
+                       GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL);
+}
+
+void gtkspell_detach(GtkText *gtktext) {
+       gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
+               GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
+       gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
+               GTK_SIGNAL_FUNC(entry_delete_cb), NULL);
+       gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext), 
+                       GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL);
+
+       gtkspell_uncheck_all(gtktext);
+}
+
+static void sigchld(int param) {
+       if (gtkspell_running() &&
+               (waitpid(spell_pid, NULL, WNOHANG) == spell_pid)) {
+               spell_pid = 0;
+       } else {
+               /* a default SIGCHLD handler.
+                * what else to do here? */
+               waitpid(-1, NULL, WNOHANG);
+       }
+}
+
+static void set_up_signal() {
+       struct sigaction sigact;
+       memset(&sigact, 0, sizeof(struct sigaction));
+
+       sigact.sa_handler = sigchld;
+       sigaction(SIGCHLD, &sigact, NULL);
+}
diff --git a/apps/silcer/src/gtkspell.h b/apps/silcer/src/gtkspell.h
new file mode 100644 (file)
index 0000000..df099a7
--- /dev/null
@@ -0,0 +1,107 @@
+/* gtkspell - a spell-checking addon for GtkText
+ * Copyright (c) 2000 Evan Martin.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __gtkspell_h__
+#define __gtkspell_h__
+
+BEGIN_GNOME_DECLS
+
+/* PLEASE NOTE that this API is unstable and subject to change. */
+#define GTKSPELL_VERSION "0.3.2"
+
+extern int gtkspell_start(char *path, char *args[]);
+/* Spawns the spell checking program.
+ *
+ * Arguments:
+ *  - "path" should be the full path to the spell checking program, or NULL
+ *    if you want to search the PATH for args[0].
+ *  - "args" should be a array of arguments to the spell checking program.
+ *    The first element should be the name of the program.
+ *    You should give the argument to run the spell checking program in the
+ *    "embedded" mode.  for ispell, this is "-a".
+ *    The last element should be NULL.
+ * Return:
+ *  0 on success, and -1 on error.
+ *
+ * Example:
+ *  char *args[] = { "ispell", "-a", NULL };
+ *  if (gtkspell_start(NULL, args) < 0) {
+ *     fprintf(stderr, "Unable to start GtkSpell.\n");
+ *     return -1;
+ *  }
+ *
+ */
+
+
+extern void gtkspell_stop();
+/* Stop the spellchecking program.
+ * This kills the spell checker's process and frees memory.
+ */
+
+extern int gtkspell_running();
+/* Is gtkspell running?
+ *
+ * Return:
+ *     nonzero if it running
+ *     zero if is not running
+ *
+ * Example:
+ *  if (gtkspell_running())
+ *     printf("gtkspell is running.\n");
+ */
+
+extern void gtkspell_attach(GtkText *text);
+/* Attach GtkSpell to a GtkText Widget.
+ * This enables checking as you type and the popup menu.
+ *
+ * Arguments:
+ *  - "text" is the widget to which GtkSpell should attach.
+ *
+ * Example:
+ *  GtkWidget *text;
+ *  text = gtk_text_new(NULL, NULL); 
+ *  gtk_text_set_editable(GTK_TEXT(text), TRUE);
+ *  gtkspell_attach(GTK_TEXT(text));
+ */  
+
+void gtkspell_detach(GtkText *gtktext);
+/* Detach GtkSpell from a GtkText widget.
+ * 
+ * Arguments:
+ *  - "text" is the widget from which GtkSpell should detach.
+ * 
+ */ 
+
+void gtkspell_check_all(GtkText *gtktext);
+/* Check and highlight the misspelled words.
+ * Note that the popup menu will not work unless you gtkspell_attach().
+ *
+ * Arguments:
+ *  - "text" is the widget to check.
+ */
+
+void gtkspell_uncheck_all(GtkText *gtktext);
+/* Remove all of the highlighting from the widget.
+ *
+ * Arguments:
+ *  - "text" is the widget to check.
+ */
+
+END_GNOME_DECLS
+
+#endif /* __gtkspell_h__ */
diff --git a/apps/silcer/src/gtkurl.c b/apps/silcer/src/gtkurl.c
new file mode 100644 (file)
index 0000000..567c379
--- /dev/null
@@ -0,0 +1,587 @@
+/* GtkUrl - A addon for GtkText that enables colored and clickable URLs
+ * Copyright (C) 2001 Benedikt Roth <Benedikt.Roth@bratislav.de>  
+ *   Based on code from 
+ *   gtkspell - a spell-checking addon for GtkText
+ *   Copyright (c) 2000 Evan Martin.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#define GTKURL_USE_GNOME
+
+#include <gtk/gtk.h>
+#ifdef GTKURL_USE_GNOME
+#include <gnome.h>
+#endif /* GTKURL_USE_GNOME */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "gtkurl.h"
+
+/* FIXME? */
+static GdkColor highlight = { 0, 0, 0, 255*256 };
+
+enum {
+  GTKURL_NO_URL,
+  GTKURL_URL,
+  GTKURL_HOST
+};
+
+
+static void entry_insert_cb(GtkText *gtktext, gchar *newtext, guint len, guint *ppos, gpointer d);
+static void entry_delete_cb(GtkText *gtktext, gint start, gint end, gpointer d);
+static gint button_press_intercept_cb(GtkText *gtktext, GdkEvent *e, gpointer d);
+
+static void popup_menu(GtkText *gtktext, GdkEventButton *eb);
+static GtkMenu *make_menu(gchar *url);
+
+static gboolean visit_url_gnome_cb( GtkWidget *widget, gpointer *data);
+static int my_poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr);
+static gboolean visit_url_cmd_cb( GtkWidget *widget, gpointer *data);
+
+static gboolean check_at(GtkText *gtktext, gint from_pos);
+static gchar *get_word_from_pos(GtkText* gtktext, gint pos, gint *pstart, gint *pend);
+static gchar *get_curword(GtkText* gtktext, gint *pstart, gint *pend);
+
+static void change_color(GtkText *gtktext, gint start, gint end, GdkColor *color);
+
+static gboolean iswordsep(gchar c);
+static gint is_url(gchar* word);
+
+
+
+void gtkurl_attach(GtkText *gtktext)
+{
+   gtk_signal_connect(GTK_OBJECT(gtktext), "insert-text",
+                    GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
+   gtk_signal_connect_after(GTK_OBJECT(gtktext), "delete-text",
+                           GTK_SIGNAL_FUNC(entry_delete_cb), NULL);
+   gtk_signal_connect(GTK_OBJECT(gtktext), "button-press-event",
+                     GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL);
+}
+
+
+void gtkurl_detach(GtkText *gtktext)
+{
+  gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
+                               GTK_SIGNAL_FUNC(entry_insert_cb), NULL);
+  gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext),
+                               GTK_SIGNAL_FUNC(entry_delete_cb), NULL);
+  gtk_signal_disconnect_by_func(GTK_OBJECT(gtktext), 
+                               GTK_SIGNAL_FUNC(button_press_intercept_cb), NULL);
+  
+  gtkurl_uncheck_all(gtktext);
+}
+
+
+void gtkurl_check_all(GtkText *gtktext)
+{
+  guint origpos;
+  guint pos = 0;
+  guint len;
+  float adj_value;
+
+  len = gtk_text_get_length(gtktext);
+  
+  adj_value = gtktext->vadj->value;
+  gtk_text_freeze(gtktext);
+  origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+
+  while (pos < len) 
+    { 
+      while (pos < len && iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
+       pos++;
+      while (pos < len && !iswordsep(GTK_TEXT_INDEX(gtktext, pos)))
+       pos++;
+      if (pos > 0)
+       check_at(gtktext, pos-1);
+    }
+
+  gtk_text_thaw(gtktext);
+  gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+}
+
+
+void gtkurl_uncheck_all(GtkText *gtktext)
+{
+  gint origpos;
+  gchar *text;
+  gfloat adj_value;
+
+  adj_value = gtktext->vadj->value;
+  gtk_text_freeze(gtktext);
+  origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+  text = gtk_editable_get_chars(GTK_EDITABLE(gtktext), 0, -1);
+  gtk_text_set_point(gtktext, 0);
+  gtk_text_forward_delete(gtktext, gtk_text_get_length(gtktext));
+  gtk_text_insert(gtktext, NULL, NULL, NULL, text, strlen(text));
+  gtk_text_thaw(gtktext);
+
+  gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+  gtk_adjustment_set_value(gtktext->vadj, adj_value);
+}
+
+
+static void entry_insert_cb(GtkText *gtktext, gchar *newtext, guint len, guint *ppos, gpointer d)
+{
+  gint origpos;
+
+  gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),
+                                  GTK_SIGNAL_FUNC(entry_insert_cb), 
+                                  NULL );
+  
+  gtk_text_insert(GTK_TEXT(gtktext), NULL,
+                 &(GTK_WIDGET(gtktext)->style->fg[0]), NULL, newtext, len);
+
+  gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext),
+                                    GTK_SIGNAL_FUNC(entry_insert_cb),
+                                    NULL);
+  
+  gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "insert-text");
+  *ppos += len;
+
+  origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+
+  if (iswordsep(newtext[0])) 
+    {
+      /* did we just end a word? */
+      if (*ppos >= 2) check_at(gtktext, *ppos-2);
+      
+      /* did we just split a word? */
+      if (*ppos < gtk_text_get_length(gtktext))
+       check_at(gtktext, *ppos+1);
+    } 
+  else 
+    {
+      /* check as they type, *except* if they're typing at the end (the most
+       * common case.
+       */
+      if (*ppos < gtk_text_get_length(gtktext) && !iswordsep(GTK_TEXT_INDEX(gtktext, *ppos)))
+       check_at(gtktext, *ppos-1);
+    }
+
+  gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+  gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
+}
+
+
+static void entry_delete_cb(GtkText *gtktext, gint start, gint end, gpointer d)
+{
+  gint origpos;
+  
+  origpos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+  check_at(gtktext, start-1);
+  gtk_editable_set_position(GTK_EDITABLE(gtktext), origpos);
+  gtk_editable_select_region(GTK_EDITABLE(gtktext), origpos, origpos);
+  /* this is to *UNDO* the selection, in case they were holding shift
+   * while hitting backspace. */
+}
+
+
+/* ok, this is pretty wacky:
+ * we need to let the right-mouse-click go through, so it moves the cursor, 
+ * but we *can't* let it go through, because GtkText interprets rightclicks as
+ * weird selection modifiers.
+ *
+ * so what do we do?  forge rightclicks as leftclicks, then popup the menu. 
+ * HACK HACK HACK. 
+ */
+static gint button_press_intercept_cb(GtkText *gtktext, GdkEvent *e, gpointer d)
+{
+  GdkEventButton *eb;
+  gboolean retval;
+  
+  if (e->type != GDK_BUTTON_PRESS) return FALSE;
+  eb = (GdkEventButton*) e;
+
+  if (eb->button != 3)
+    return FALSE;
+
+  /* forge the leftclick */
+  eb->button = 1;
+
+  gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext), 
+                                  GTK_SIGNAL_FUNC(button_press_intercept_cb), d);
+  gtk_signal_emit_by_name(GTK_OBJECT(gtktext), "button-press-event",
+                         e, &retval);
+  gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), 
+                                    GTK_SIGNAL_FUNC(button_press_intercept_cb), d);
+  gtk_signal_emit_stop_by_name(GTK_OBJECT(gtktext), "button-press-event");
+
+  /* now do the menu wackiness */
+  popup_menu(gtktext, eb);
+  return TRUE;
+}
+
+
+static void popup_menu(GtkText *gtktext, GdkEventButton *eb)
+{
+  gchar *buf;
+  
+  buf = get_curword(gtktext, NULL, NULL);
+  
+  gtk_menu_popup(make_menu(buf), NULL, NULL, NULL, NULL,
+                eb->button, eb->time);
+}
+
+
+static GtkMenu *make_menu(gchar *url)
+{
+  GtkWidget *menu, *item;
+  gchar *caption;
+  gchar *s = "http://";
+  gchar *cmd;
+
+  switch( is_url(url) )
+    {
+    case GTKURL_URL:
+      url = g_strdup_printf("%s", url);
+      break;
+    case GTKURL_HOST: 
+      url = g_strdup_printf("%s%s", s, url);
+      break;
+    }    
+
+  menu = gtk_menu_new(); 
+  
+  caption = g_strdup_printf("%s", url);
+  item = gtk_menu_item_new_with_label(caption);
+  g_free(caption);
+  gtk_widget_set_sensitive( GTK_WIDGET(item), FALSE);
+  /* I'd like to make it so this item is never selectable, like
+   * the menu titles in the GNOME panel... unfortunately, the GNOME
+   * panel creates their own custom widget to do this! */
+  gtk_widget_show(item);
+  gtk_menu_append(GTK_MENU(menu), item);
+  
+  item = gtk_menu_item_new();
+  gtk_widget_show(item);
+  gtk_menu_append(GTK_MENU(menu), item);
+
+#ifdef GTKURL_USE_GNOME
+  item = gtk_menu_item_new_with_label(_("Open with GNOME URL Handler"));
+  gtk_signal_connect(GTK_OBJECT(item), "activate", 
+                    GTK_SIGNAL_FUNC(visit_url_gnome_cb), g_strdup(url) );
+  gtk_menu_append(GTK_MENU(menu), item);
+  gtk_widget_show(item);
+#endif /* GTKURL_USE_GNOME */
+    
+  item = gtk_menu_item_new_with_label(_("Open with Netscape (Existing)"));
+  cmd = g_strdup_printf("netscape -remote 'openURL(%s)'", url);
+  gtk_signal_connect(GTK_OBJECT(item), "activate",
+                    GTK_SIGNAL_FUNC(visit_url_cmd_cb), g_strdup(cmd) );
+  g_free(cmd);
+  gtk_menu_append(GTK_MENU(menu), item);
+  gtk_widget_show(item);
+
+  item = gtk_menu_item_new_with_label(_("Open with Netscape (New Window)"));
+  cmd = g_strdup_printf("netscape -remote 'openURL(%s,new-window)'", url);
+  gtk_signal_connect(GTK_OBJECT(item), "activate",
+                    GTK_SIGNAL_FUNC(visit_url_cmd_cb), g_strdup(cmd) );
+  g_free(cmd);
+  gtk_menu_append(GTK_MENU(menu), item);
+  gtk_widget_show(item);
+
+  item = gtk_menu_item_new_with_label(_("Open with Netscape (Run New)"));
+  cmd = g_strdup_printf("netscape %s", url);
+  gtk_signal_connect(GTK_OBJECT(item), "activate",
+                    GTK_SIGNAL_FUNC(visit_url_cmd_cb), g_strdup(cmd) );
+  g_free(cmd);
+  gtk_menu_append(GTK_MENU(menu), item);
+  gtk_widget_show(item);
+
+  g_free(url);
+  
+  return GTK_MENU(menu);
+}
+
+
+#ifdef GTKURL_USE_GNOME
+static gboolean visit_url_gnome_cb( GtkWidget *widget, gpointer *data)
+{
+  gnome_url_show((gchar *) data);
+  g_free(data);
+  return(TRUE);
+}
+#endif /* GTKURL_USE_GNOME */
+
+
+/* this is taken from gnome-libs 1.2.4 */
+#define POPT_ARGV_ARRAY_GROW_DELTA 5
+
+static int my_poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr)
+{
+    char * buf, * bufStart, * dst;
+    const char * src;
+    char quote = '\0';
+    int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
+    char ** argv = malloc(sizeof(*argv) * argvAlloced);
+    const char ** argv2;
+    int argc = 0;
+    int i, buflen;
+
+    buflen = strlen(s) + 1;
+    bufStart = buf = alloca(buflen);
+    memset(buf, '\0', buflen);
+
+    src = s;
+    argv[argc] = buf;
+
+    while (*src) {
+       if (quote == *src) {
+           quote = '\0';
+       } else if (quote) {
+           if (*src == '\\') {
+               src++;
+               if (!*src) {
+                   free(argv);
+                   return 1;
+               }
+               if (*src != quote) *buf++ = '\\';
+           }
+           *buf++ = *src;
+       } else if (isspace(*src)) {
+           if (*argv[argc]) {
+               buf++, argc++;
+               if (argc == argvAlloced) {
+                   argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
+                   argv = realloc(argv, sizeof(*argv) * argvAlloced);
+               }
+               argv[argc] = buf;
+           }
+       } else switch (*src) {
+         case '"':
+         case '\'':
+           quote = *src;
+           break;
+         case '\\':
+           src++;
+           if (!*src) {
+               free(argv);
+               return 1;
+           }
+           /* fallthrough */
+         default:
+           *buf++ = *src;
+       }
+
+       src++;
+    }
+
+    if (strlen(argv[argc])) {
+       argc++, buf++;
+    }
+
+    dst = malloc((argc + 1) * sizeof(*argv) + (buf - bufStart));
+    argv2 = (void *) dst;
+    dst += (argc + 1) * sizeof(*argv);
+    memcpy(argv2, argv, argc * sizeof(*argv));
+    argv2[argc] = NULL;
+    memcpy(dst, bufStart, buf - bufStart);
+
+    for (i = 0; i < argc; i++) {
+       argv2[i] = dst + (argv[i] - bufStart);
+    }
+
+    free(argv);
+
+    *argvPtr = (char **)argv2; /* XXX don't change the API */
+    *argcPtr = argc;
+
+    return 0;
+}
+
+
+static gboolean visit_url_cmd_cb( GtkWidget *widget, gpointer *data)
+{
+  int pid;
+  char **argv;
+  int argc;
+
+  if (my_poptParseArgvString ( (const char *)data, &argc, &argv) != 0)
+    return -1;
+
+  pid = fork ();
+  if (pid == -1)
+    return -1;
+  if (pid == 0)
+    {
+      execvp (argv[0], argv);
+      _exit (0);
+    } else
+      {
+       free (argv);
+       return pid;
+      }
+  
+  g_free(data);
+
+  return(TRUE);
+}
+
+
+static gboolean check_at(GtkText *gtktext, gint from_pos)
+{
+  gint start, end;
+  gchar *buf;
+  
+  if ( ! (buf = get_word_from_pos(gtktext, from_pos, &start, &end)) ) 
+      return FALSE;
+  
+  if ( is_url(buf) ) 
+    {
+      if (highlight.pixel == 0) 
+       {
+         /* add an entry for the highlight in the color map. */
+         GdkColormap *gc = gtk_widget_get_colormap(GTK_WIDGET(gtktext));
+         gdk_colormap_alloc_color(gc, &highlight, FALSE, TRUE);;
+       }
+      change_color(gtktext, start, end, &highlight);
+      return(TRUE);
+    } 
+  else 
+    { 
+      change_color(gtktext, start, end, &(GTK_WIDGET(gtktext)->style->fg[0]));
+      return(FALSE);
+    }
+}
+
+
+static gchar *get_word_from_pos(GtkText* gtktext, gint pos, gint *pstart, gint *pend)
+{
+  GString *word = g_string_new("");
+  gint start, end;
+  gchar ch;
+  
+  if (iswordsep(GTK_TEXT_INDEX(gtktext, pos))) 
+    return(NULL);
+
+  /* Get start and end position from the word */
+  for (start = pos; start >= 0; --start) 
+    if (iswordsep(GTK_TEXT_INDEX(gtktext, start))) 
+      break;
+  start++;
+  
+  for (end = pos; end < gtk_text_get_length(gtktext); end++) 
+    if (iswordsep(GTK_TEXT_INDEX(gtktext, end)) )
+      break;
+
+  /* Be sure to not include punctation marks etc. */
+  for ( ;end>start; end-- )
+    {
+      ch = GTK_TEXT_INDEX(gtktext, end-1); 
+      if( isalpha(ch) || isdigit(ch) || ch == ':' )
+       break;
+    }
+
+  /* Get the word (everyting between start and end */
+  for (pos = start; pos < end; pos++)
+    g_string_append_c( word, GTK_TEXT_INDEX(gtktext, pos) );
+
+  if (pstart) 
+    *pstart = start;
+  if (pend) 
+    *pend = end;
+  
+  return(word->str);
+}
+
+
+static gchar *get_curword(GtkText* gtktext, gint *pstart, gint *pend)
+{
+  gint pos = gtk_editable_get_position(GTK_EDITABLE(gtktext));
+  return(get_word_from_pos(gtktext, pos, pstart, pend));
+}
+
+
+static void change_color(GtkText *gtktext, gint start, gint end, GdkColor *color)
+{
+  gchar *newtext;
+
+  /* So we don't need spaces at the very end of the text */
+  if ( end == gtk_text_get_length(GTK_TEXT(gtktext))+1 )
+    end--;
+
+  newtext = gtk_editable_get_chars(GTK_EDITABLE(gtktext), start, end);
+    
+  gtk_text_freeze(gtktext);
+  gtk_signal_handler_block_by_func(GTK_OBJECT(gtktext),  
+                                  GTK_SIGNAL_FUNC(entry_insert_cb), NULL); 
+       
+  gtk_text_set_point(gtktext, start);
+  gtk_text_forward_delete(gtktext, end-start);
+
+  if (newtext && end-start > 0)
+    gtk_text_insert(gtktext, NULL, color, NULL, newtext, end-start); 
+
+  gtk_signal_handler_unblock_by_func(GTK_OBJECT(gtktext), 
+                                    GTK_SIGNAL_FUNC(entry_insert_cb), NULL); 
+  gtk_text_thaw(gtktext);
+  g_free(newtext);
+}
+
+
+static gboolean iswordsep(gchar c)
+{
+/*     return !isalpha(c) && c != '\''; */
+  return( isspace(c) );
+}
+
+
+static gint is_url(gchar* word)
+{
+     gint len;
+     if (!word)
+         return GTKURL_NO_URL;
+
+   len = strlen (word);
+
+   if (!strncasecmp (word, "irc://", 6))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word, "irc.", 4))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word, "ftp.", 4))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word, "ftp:", 4))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word, "www.", 4))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word, "http:", 5))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word, "https:", 6))
+      return GTKURL_URL;
+
+   if (!strncasecmp (word + len - 4, ".org", 4))
+      return GTKURL_HOST;
+
+   if (!strncasecmp (word + len - 4, ".net", 4))
+      return GTKURL_HOST;
+
+   if (!strncasecmp (word + len - 4, ".com", 4))
+      return GTKURL_HOST;
+
+   if (!strncasecmp (word + len - 4, ".edu", 4))
+      return GTKURL_HOST;
+
+   return GTKURL_NO_URL;
+}
diff --git a/apps/silcer/src/gtkurl.h b/apps/silcer/src/gtkurl.h
new file mode 100644 (file)
index 0000000..ccf76d7
--- /dev/null
@@ -0,0 +1,69 @@
+/* GtkUrl - A addon for GtkText that enables colored and clickable URLs
+ * Copyright (C) 2001 Benedikt Roth <Benedikt.Roth@bratislav.de>  
+ *   Based on code from 
+ *   gtkspell - a spell-checking addon for GtkText
+ *   Copyright (c) 2000 Evan Martin.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __GTKSPELL_h__
+#define __GTKSPELL_h__
+
+BEGIN_GNOME_DECLS
+
+#define GTKURL_VERSION "0.1"
+
+/* Attach GtkURL to a GtkText Widget.
+ * This enables URL-checking as you type and the popup menu.
+ *
+ * Arguments:
+ *  - "text" is the widget to which GtkURL should attach.
+ *
+ * Example:
+ *  GtkWidget *text;
+ *  text = gtk_text_new(NULL, NULL); 
+ *  gtk_text_set_editable(GTK_TEXT(text), TRUE);
+ *  gtkurl_attach(GTK_TEXT(text));
+ */  
+void gtkurl_attach(GtkText *text);
+
+
+/* Detach GtkUrl from a GtkText widget.
+ * 
+ * Arguments:
+ *  - "text" is the widget from which GtkUrl should detach.
+ */ 
+void gtkurl_detach(GtkText *text);
+
+
+/* Highlight all urls
+ * Note that the popup menu will not work unless you gtkurl_attach().
+ *
+ * Arguments:
+ *  - "text" is the widget to check.
+ */
+void gtkurl_check_all(GtkText *text);
+
+/* Remove all of the highlighting from the widget.
+ *
+ * Arguments:
+ *  - "text" is the widget to check.
+ */
+void gtkurl_uncheck_all(GtkText *gtktext);
+
+END_GNOME_DECLS
+
+#endif /* __GTKURL_H__ */
diff --git a/apps/silcer/src/silcer.cc b/apps/silcer/src/silcer.cc
new file mode 100644 (file)
index 0000000..c8ca8cc
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+
+  silcer.cc 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include <fstream>
+#include <glade/glade-xml.h>
+#include <sigc++/object_slot.h>
+#include <gnome--/client.h>
+#include <gnome--/main.h>
+#include <libgnome/gnome-i18n.h>
+#include "silcerapp.hh"
+
+int main (int argc, char** argv)
+{
+#ifdef ENABLE_LNS
+  // Load translation
+  bindtextdomain(ConfigManager::get_PACKAGE(), GNOMELOCALEDIR);
+  textdomain(ConfigManager::get_PACKAGE());
+#endif
+  
+  new SilcerApp(argc, argv);
+  Silcer_App->run();
+
+  return 0;
+}
diff --git a/apps/silcer/src/silcer_gladehelper.hh b/apps/silcer/src/silcer_gladehelper.hh
new file mode 100644 (file)
index 0000000..4e3e321
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+
+  silcer_gladehelper.hh 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCER_GLADEHELPER_HH
+#define SILCER_GLADEHELPER_HH
+
+#include <glade/glade-xml.h>
+#include <gtk/gtkobject.h>
+#include <gtk--/base.h>
+
+template<class T> T *SilcerGetWidget(GladeXML* obj, const char *name)
+{
+  T *widget = 
+    static_cast<T *>(Gtk::wrap_auto((GtkObject *)
+                                   glade_xml_get_widget(obj, name)));
+  if (!widget)
+    g_error("Could not find widget `%s'", name);
+  return widget;
+}
+
+#endif /* SILCER_GLADEHELPER_HH */
diff --git a/apps/silcer/src/silcerapp.cc b/apps/silcer/src/silcerapp.cc
new file mode 100644 (file)
index 0000000..79e644b
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+
+  silcerapp.cc 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcerapp.hh"
+#include "gtkspell.h"
+
+#include <sys/utsname.h>
+#include <glade/glade.h>
+#include <libgnome/gnome-triggers.h>
+#include <libgnome/gnome-util.h>
+#include <libgnomeui/gnome-window-icon.h>
+#include <gnome--/client.h>
+
+// Pointer to the application
+SilcerApp *Silcer_App;
+string package = "silcer";
+string version = "1.0";
+
+SilcClient silc_client;
+SilcClientConnection silc_client_conn;
+
+static int 
+silc_create_key_pair(char *pkcs_name, int bits, char *path,
+                    char *identifier, 
+                    SilcPublicKey *ret_pub_key,
+                    SilcPrivateKey *ret_prv_key)
+{
+  SilcPKCS pkcs;
+  SilcPublicKey pub_key;
+  SilcPrivateKey prv_key;
+  SilcRng rng;
+  unsigned char *key;
+  uint32 key_len;
+  char pkfile[256], prvfile[256];
+
+  if (!pkcs_name || !path)
+    return FALSE;
+
+  if (!bits)
+    bits = 1024;
+
+  rng = silc_rng_alloc();
+  silc_rng_init(rng);
+  silc_rng_global_init(rng);
+
+  /* Generate keys */
+  silc_pkcs_alloc((const unsigned char *)pkcs_name, &pkcs);
+  pkcs->pkcs->init(pkcs->context, bits, rng);
+
+  /* Save public key into file */
+  key = silc_pkcs_get_public_key(pkcs, &key_len);
+  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,
+                                       key, key_len);
+  *ret_pub_key = pub_key;
+
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  /* Save private key into file */
+  key = silc_pkcs_get_private_key(pkcs, &key_len);
+  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);
+  *ret_prv_key = prv_key;
+
+  memset(key, 0, sizeof(key_len));
+  silc_free(key);
+
+  silc_rng_free(rng);
+  silc_pkcs_free(pkcs);
+
+  return TRUE;
+}
+
+static
+void silc_op_say(SilcClient client, SilcClientConnection conn, 
+                 SilcClientMessageType type, char *msg, ...)
+{
+  va_list va;
+  char *str;
+
+  va_start(va, msg);
+  str = g_strdup_vprintf(msg, va);
+  Silcer_App->_MainDialog->print((string)str);
+  g_free(str);
+  va_end(va);
+}
+
+static
+void silc_channel_message(SilcClient client, SilcClientConnection conn, 
+                         SilcClientEntry sender, SilcChannelEntry channel, 
+                         SilcMessageFlags flags, char *msg)
+{
+  Silcer_App->_MainDialog->print((string)msg, (string)sender->nickname);
+}
+
+static
+void silc_private_message(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcMessageFlags flags,
+                         char *msg)
+{
+  Silcer_App->_MainDialog->print((string)msg);
+}
+
+static
+void silc_notify(SilcClient client, SilcClientConnection conn, 
+                SilcNotifyType type, ...)
+{
+  va_list va;
+  
+  va_start(va, type);
+  Silcer_App->_MainDialog->print((string)va_arg(va, char *));
+  va_end(va);
+}
+
+static
+void silc_connect(SilcClient client, SilcClientConnection conn, int success)
+{
+  silc_client_conn = conn;
+}
+
+static
+void silc_disconnect(SilcClient client, SilcClientConnection conn)
+{
+  silc_client_conn = NULL;
+}
+
+static
+void silc_auth_meth(SilcClient client, 
+                   SilcClientConnection conn,
+                   char *hostname, uint16 port,
+                   SilcGetAuthMeth completion, void *context)
+{
+  completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
+}
+
+static
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context)
+{
+  completion(TRUE, context);
+}
+
+static
+void silc_command(SilcClient client, SilcClientConnection conn, 
+                 SilcClientCommandContext cmd_context, int success,
+                 SilcCommand command)
+{
+
+}
+
+static
+void silc_command_reply(SilcClient client, SilcClientConnection conn,
+                       SilcCommandPayload cmd_payload, int success,
+                       SilcCommand command, SilcCommandStatus status, ...)
+{
+
+}
+
+/* SILC client operations */
+SilcClientOperations ops = {
+  silc_op_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connect,
+  silc_disconnect,
+  silc_auth_meth,
+  silc_verify_public_key,
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+SILC_TASK_CALLBACK(connect_client)
+{
+  SilcClient client = (SilcClient)context;
+  silc_client_connect_to_server(client, 706, "silc.silcnet.org", NULL);
+}
+
+SilcerApp::SilcerApp(int argc, char **argv)
+  : _GnomeApp(package, version, argc, argv),
+  _gclient(Gnome::Client::master_client())
+{
+  // Save application pointer
+  Silcer_App = this;
+
+  // Initialize SILC stuff
+  silc_debug = TRUE;
+  silc_debug_hexdump = TRUE;
+  silc_log_set_debug_string("*client*,*net*,*ske*");
+
+  // Initialize SILC Client Library */
+  silc_client = silc_client_alloc(&ops, NULL, NULL, "SILC-1.0-0.6.2");
+  silc_client->realname = "Foo T. Bar";
+  silc_client->username = "foobar";
+  silc_client->hostname = "foo.bar.foobar.com";
+  silc_cipher_register_default();
+  silc_pkcs_register_default();
+  silc_hash_register_default();
+  silc_hmac_register_default();
+  silc_create_key_pair("rsa", 1024, "kk", "UN=foobar, "
+                      "HN=foo.bar.foobar.com", 
+                      &silc_client->public_key, &silc_client->private_key);
+  silc_client_init(silc_client);
+
+  // Setup SILC scheduler as timeout task
+  Gnome::Main::timeout.connect(slot(this, &SilcerApp::silc_scheduler), 50);
+
+  // XXXXX
+  // This is now used to directly connect to silc.silcnet.org router
+  // XXXXX
+  silc_schedule_task_add(silc_client->schedule, 0, connect_client, 
+                        silc_client, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL); 
+
+   // Initialize glade
+  glade_gnome_init();
+
+  // Locate glade files
+  if (!g_file_exists(string(_SourceDir + "SilcerMainDlg.glade").c_str()))
+    _SourceDir = "./";
+  if (!g_file_exists(string(_SourceDir + "SilcerMainDlg.glade").c_str()))
+    _SourceDir = "./ui/";
+  if (!g_file_exists(string(_SourceDir + "SilcerMainDlg.glade").c_str()))
+    _SourceDir = "./src/";
+  if (!g_file_exists(string(_SourceDir + "SilcerMainDlg.glade").c_str())) {
+    g_error("Could not find SilcerMainDlg.glade");
+    exit(-1);
+  }
+
+  _MainDialog = new SilcerMainDlg();
+}
+
+SilcerApp::~SilcerApp()
+{
+  delete _MainDialog;
+}
+
+void SilcerApp::run()
+{
+  // Let the gnome app start processing messages
+  Gnome::Main::run();
+}
+
+void SilcerApp::quit()
+{
+  // Stop gtk/gnome message loop
+  Gnome::Main::quit();
+  delete Silcer_App;
+}
+
+GladeXML *SilcerApp::load_resource(const char *name)
+{
+  return glade_xml_new(string(_SourceDir + name + ".glade").c_str(), name);
+}
+
+GladeXML *SilcerApp::load_resource(const char *name, const char *filename)
+{
+  return glade_xml_new(string(_SourceDir + filename + ".glade").c_str(), name);
+}
+
+gint SilcerApp::silc_scheduler()
+{
+  // Run the SILC client once, and return immediately.  This function
+  // is called every 50 milliseconds by the Gnome main loop, to process
+  // SILC stuff.  This function will read data, and write data to network,
+  // etc.  Makes the client library tick! :)
+  silc_client_run_one(silc_client);
+  return 1;
+}
diff --git a/apps/silcer/src/silcerapp.hh b/apps/silcer/src/silcerapp.hh
new file mode 100644 (file)
index 0000000..8afc10a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+
+  silcerapp.hh 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCERAPP_HH
+#define SILCERAPP_HH
+
+#include "SilcerMainDlg.hh"
+#include "silcer_gladehelper.hh"
+
+extern "C" {
+#include "silcincludes.h"
+#include "clientlibincludes.h"
+}
+
+#include <fstream>
+#include <glade/glade-xml.h>
+#include <sigc++/object_slot.h>
+#include <gnome--/client.h>
+#include <gnome--/main.h>
+
+// Forward declarations
+class SilcerApp;
+
+// Global pointer for the application
+extern SilcerApp *Silcer_App;
+
+// Global pointer to the SILC Client Library object
+extern SilcClient silc_client;
+extern SilcClientConnection silc_client_conn;
+
+// Silcer class
+class SilcerApp : public SigC::Object
+{
+public:
+  SilcerApp(int argc, char **argv);
+  ~SilcerApp();
+
+  SilcerMainDlg *_MainDialog;
+
+  void run();
+  void quit();
+  GladeXML* load_resource(const char *name);
+  GladeXML* load_resource(const char *name, const char *filename);
+
+protected:
+  gint silc_scheduler();
+
+private:
+  Gnome::Main _GnomeApp;
+  Gnome::Client *_gclient;
+  string _SourceDir;
+  string _pix_path;
+};
+
+#endif /* SILCERAPP_HH */
diff --git a/apps/silcer/src/silcerbasewin.cc b/apps/silcer/src/silcerbasewin.cc
new file mode 100644 (file)
index 0000000..342a45f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+
+  silcerbasewin.cc 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcerbasewin.hh"
+#include "silcerapp.hh"
+
+SilcerBaseWindow::SilcerBaseWindow(const char *widgetname)
+{ 
+  _thisGH = Silcer_App->load_resource(widgetname); 
+  _thisWindow = SilcerGetWidget<Gtk::Window>(_thisGH, widgetname);
+  reference();
+}
+
+void SilcerBaseWindow::set_dynamic()
+{
+  SigC::Object::set_dynamic();
+  set_sink();
+}
+
+void SilcerBaseWindow::close()
+{
+  unreference();
+}
+
+SilcerBaseWindow::~SilcerBaseWindow()
+{
+  evtDestroy();
+  _thisWindow->destroy();
+  gtk_object_unref(GTK_OBJECT(_thisGH));
+}
+
+SilcerBaseDialog::SilcerBaseDialog(const char *widgetname, 
+                                  gboolean close_hides)
+  : SilcerBaseWindow(widgetname)
+{
+  _thisDialog = static_cast<Gnome::Dialog*>(_thisWindow);
+  _thisDialog->close_hides(close_hides);
+  if (!close_hides)
+    _thisDialog->close.connect(slot(this, &SilcerBaseDialog::on_Dialog_close));
+}
+
+gboolean SilcerBaseDialog::on_Dialog_close()
+{
+  _thisWindow->destroy();
+  return true;
+}
+
+SilcerBaseWidget::SilcerBaseWidget(const char *widgetname, 
+                                  const char* filename)
+{ 
+  _thisGH = Silcer_App->load_resource(widgetname, filename); 
+  _thisWidget = SilcerGetWidget<Gtk::Widget>(_thisGH, widgetname);
+  reference();
+}
+
+void SilcerBaseWidget::set_dynamic()
+{
+  SigC::Object::set_dynamic();
+  set_sink();
+}
+
+void SilcerBaseWidget::close()
+{
+  unreference();
+}
+
+SilcerBaseWidget::~SilcerBaseWidget()
+{
+  evtDestroy();
+  _thisWidget->destroy();
+  gtk_object_unref(GTK_OBJECT(_thisGH));
+}
diff --git a/apps/silcer/src/silcerbasewin.hh b/apps/silcer/src/silcerbasewin.hh
new file mode 100644 (file)
index 0000000..0b3270f
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+
+  silcerbasewin.hh 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCERBASEWIN_HH
+#define SILCERBASEWIN_HH
+
+#include "silcer_gladehelper.hh"
+
+extern "C" {
+#include "silcincludes.h"
+#include "clientlibincludes.h"
+}
+
+#include <sigc++/signal_system.h>
+#include <sigc++/object_slot.h>
+#include <sigc++/marshal.h>
+#include <glade/glade-xml.h>
+#include <gtk--/box.h>
+#include <gtk--/button.h>
+#include <gtk--/checkbutton.h>
+#include <gtk--/ctree.h>
+#include <gtk--/entry.h>
+#include <gtk--/eventbox.h>
+#include <gtk--/frame.h>
+#include <gtk--/label.h>
+#include <gtk--/menuitem.h>
+#include <gtk--/optionmenu.h>
+#include <gtk--/text.h>
+#include <gtk--/widget.h>
+#include <gtk--/window.h>
+#include <gnome--/dialog.h>
+#include <gnome--/entry.h>
+#include <gnome--/pixmap.h>
+#include <gnome--/pixmapmenuitem.h>
+
+using namespace SigC;
+
+class SilcerBaseWindow : public SigC::Object
+{
+public:
+  SilcerBaseWindow(const char *widgetname);
+  virtual ~SilcerBaseWindow();
+
+  void show() { _thisWindow->show(); }
+  void hide() { _thisWindow->hide(); }
+  virtual void close(); 
+  // Object extender
+  virtual void set_dynamic();
+  // Destruction signal
+  Signal0<void, Marshal<void> > evtDestroy;
+
+protected:
+  SilcerBaseWindow();
+  // FIXME: Should make this function properly copy
+  SilcerBaseWindow& operator=(const SilcerBaseWindow&) { return *this;}
+  SilcerBaseWindow(const SilcerBaseWindow&) {}
+  
+public:
+  // Helper functions
+
+  Gtk::Button *getButton(const char *name)
+  { return SilcerGetWidget<Gtk::Button>(_thisGH, name); }
+
+  Gtk::CheckButton *getCheckButton(const char *name)
+  { return SilcerGetWidget<Gtk::CheckButton>(_thisGH, name); }
+
+  Gtk::CTree *getCTree(const char *name)
+  { return SilcerGetWidget<Gtk::CTree>(_thisGH, name); }
+
+  Gtk::Entry *getEntry(const char *name)
+  { return SilcerGetWidget<Gtk::Entry>(_thisGH, name); }
+
+  Gtk::EventBox *getEventBox(const char *name)
+  { return SilcerGetWidget<Gtk::EventBox>(_thisGH, name); }
+
+  Gtk::Frame *getFrame(const char *name)
+  { return SilcerGetWidget<Gtk::Frame>(_thisGH, name); }
+
+  Gtk::HBox *getHBox(const char *name)
+  { return SilcerGetWidget<Gtk::HBox>(_thisGH, name); }
+
+  Gnome::Entry *getGEntry(const char *name)
+  { return SilcerGetWidget<Gnome::Entry>(_thisGH, name); }
+
+  Gtk::Label *getLabel(const char *name)
+  { return SilcerGetWidget<Gtk::Label>(_thisGH, name); }
+
+  Gtk::MenuItem *getMenuItem(const char *name)
+  { return SilcerGetWidget<Gtk::MenuItem>(_thisGH, name); }
+
+  Gtk::OptionMenu *getOptionMenu(const char *name)
+  { return SilcerGetWidget<Gtk::OptionMenu>(_thisGH, name); }
+
+  Gnome::Pixmap *getPixmap(const char *name)
+  { return SilcerGetWidget<Gnome::Pixmap>(_thisGH, name); }
+
+  Gtk::PixmapMenuItem *getPixmapMenuItem(const char *name)
+  { return SilcerGetWidget<Gtk::PixmapMenuItem>(_thisGH, name); }
+
+  Gtk::Text *getText(const char *name)
+  { return SilcerGetWidget<Gtk::Text>(_thisGH, name); }
+
+  Gtk::VBox *getVBox(const char *name)
+  { return SilcerGetWidget<Gtk::VBox>(_thisGH, name); }
+
+  template <class T> T *getWidget(const char *name)
+  { return SilcerGetWidget<T>(_thisGH, name); }
+
+protected:
+  Gtk::Window *_thisWindow;
+
+private:
+  GladeXML *_thisGH;
+};
+
+class SilcerBaseDialog : public SilcerBaseWindow
+{
+public:
+  SilcerBaseDialog(const char *widgetname, gboolean close_hides = false);
+  virtual ~SilcerBaseDialog() {}
+
+protected:
+  Gnome::Dialog *_thisDialog;
+  gboolean on_Dialog_close();
+};
+
+class SilcerBaseWidget : public SigC::Object
+{
+public:
+  SilcerBaseWidget(const char* widgetname, const char* filename);
+  virtual ~SilcerBaseWidget();
+  void show() { _thisWidget->show(); }
+  void hide() { _thisWidget->hide(); }
+  Gtk::Widget* get_this_widget() { return _thisWidget; }
+  virtual void close(); 
+  // Object extender
+  virtual void set_dynamic();
+  // Destruction signal
+  Signal0<void, Marshal<void> > evtDestroy;
+protected:
+  SilcerBaseWidget();
+  // FIXME: Should make this function properly copy
+  SilcerBaseWidget& operator=(const SilcerBaseWidget&) { return *this;}
+  SilcerBaseWidget(const SilcerBaseWidget&) {}
+public:
+  // Helper functions
+
+  Gtk::Button *getButton(const char *name)
+  { return SilcerGetWidget<Gtk::Button>(_thisGH, name); }
+
+  Gtk::CheckButton *getCheckButton(const char *name)
+  { return SilcerGetWidget<Gtk::CheckButton>(_thisGH, name); }
+
+  Gtk::Entry *getEntry(const char *name)
+  { return SilcerGetWidget<Gtk::Entry>(_thisGH, name); }
+
+  Gtk::Label *getLabel(const char *name)
+  { return SilcerGetWidget<Gtk::Label>(_thisGH, name); }
+
+  Gtk::MenuItem *getMenuItem(const char *name)
+  { return SilcerGetWidget<Gtk::MenuItem>(_thisGH, name); }
+
+  Gtk::OptionMenu *getOptionMenu(const char *name)
+  { return SilcerGetWidget<Gtk::OptionMenu>(_thisGH, name); }
+
+  Gtk::PixmapMenuItem *getPixmapMenuItem(const char *name)
+  { return SilcerGetWidget<Gtk::PixmapMenuItem>(_thisGH, name); }
+
+  Gtk::Text *getText(const char *name)
+  { return SilcerGetWidget<Gtk::Text>(_thisGH, name); }
+
+  template <class T> T *getWidget(const char *name)
+  { return SilcerGetWidget<T>(_thisGH, name); }
+
+protected:
+  Gtk::Widget *_thisWidget;
+
+private:
+  GladeXML *_thisGH;
+};
+
+#endif /* SILCERBASEWIN_HH */
diff --git a/apps/silcer/src/silcerchatview.cc b/apps/silcer/src/silcerchatview.cc
new file mode 100644 (file)
index 0000000..7692fea
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+
+  silcerchatview.cc 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  Code is mostly ripped from Gabber, thus code also copyrighted by:
+  Copyright (C) 1999-2001 Dave Smith & Julian Missig
+  
+*/
+
+#include "silcerchatview.hh"
+#include <libgnome/gnome-url.h>
+#include <gtk--/text.h>
+#include <gtk/gtkbox.h>
+
+extern "C" {
+#include "xtext.h"
+}
+
+GdkColor colors[] =
+{
+   {0, 0, 0, 0},                /* 0  black */
+   {0, 0xcccc, 0xcccc, 0xcccc}, /* 1  white */
+   {0, 0, 0, 0xcccc},           /* 2  blue */
+   {0, 0, 0xcccc, 0},           /* 3  green */
+   {0, 0xcccc, 0, 0},           /* 4  red */
+   {0, 0xbbbb, 0xbbbb, 0},      /* 5  yellow/brown */
+   {0, 0xbbbb, 0, 0xbbbb},      /* 6  purple */
+   {0, 0xffff, 0xaaaa, 0},      /* 7  orange */
+   {0, 0xffff, 0xffff, 0},      /* 8  yellow */
+   {0, 0, 0xffff, 0},           /* 9  green */
+   {0, 0, 0xcccc, 0xcccc},      /* 10 aqua */
+   {0, 0, 0xffff, 0xffff},      /* 11 light aqua */
+   {0, 0, 0, 0xffff},           /* 12 blue */
+   {0, 0xffff, 0, 0xffff},      /* 13 pink */
+   {0, 0x7777, 0x7777, 0x7777}, /* 14 grey */
+   {0, 0x9999, 0x9999, 0x9999}, /* 15 light grey */
+   {0, 0, 0, 0xcccc},           /* 16 blue markBack */
+   {0, 0xeeee, 0xeeee, 0xeeee}, /* 17 white markFore */
+   {0, 0xcccc, 0xcccc, 0xcccc}, /* 18 foreground (white) */
+   {0, 0, 0, 0},                /* 19 background (black) */
+};
+
+typedef const int CONSTANT;
+const int WORD_URL  = 1;
+const int WORD_HOST = 2;
+
+void palette_load(GtkWidget* w)
+{
+  int i;
+  
+  if (!colors[0].pixel)             /* don't do it again */
+    for (i = 0; i < 20; i++) {
+      colors[i].pixel = (gulong) ((colors[i].red & 0xff00) * 256 +
+                                 (colors[i].green & 0xff00) +
+                                 (colors[i].blue & 0xff00) / 256);
+      if (!gdk_colormap_alloc_color (gtk_widget_get_colormap (w), 
+                                    &colors[i], 0, 1))
+       cerr << "Error allocating color " << i << endl;
+    }
+}
+
+int word_check(GtkXText* t, char* word)
+{
+  if (!word)
+    return 0;
+  int len = strlen (word);
+  
+  if (!strncasecmp (word, "irc://", 6))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "irc.", 4))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "ftp.", 4))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "ftp:", 4))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "www.", 4))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "http:", 5))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "https:", 6))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "silc:", 7))
+    return WORD_URL;
+
+  if (!strncasecmp (word, "sftp:", 7))
+    return WORD_URL;
+
+  if (!strncasecmp (word + len - 4, ".org", 4))
+    return WORD_HOST;
+
+  if (!strncasecmp (word + len - 4, ".net", 4))
+    return WORD_HOST;
+
+  if (!strncasecmp (word + len - 4, ".com", 4))
+    return WORD_HOST;
+
+  if (!strncasecmp (word + len - 4, ".edu", 4))
+    return WORD_HOST;
+
+  return 0;
+}
+
+SilcerChatView::SilcerChatView(Gtk::Widget *owner, Gtk::Container *parent, 
+                              gboolean indent)
+{
+  // Create a Gtk::Text so we can grab the colors, then destroy it
+  Gtk::Text textwidget;
+  parent->add(textwidget);
+  textwidget.realize();
+
+  // Copy the style of the Gtk::Text widget
+  GtkStyle* gs = gtk_widget_get_style(GTK_WIDGET(textwidget.gtkobj()));
+  colors[16] = gs->bg[GTK_STATE_SELECTED];
+  colors[17] = gs->fg[GTK_STATE_SELECTED];
+  colors[18] = gs->fg[GTK_STATE_NORMAL];
+  colors[19] = gs->base[GTK_STATE_NORMAL];
+  parent->remove(textwidget);
+  textwidget.destroy();
+
+  // Initialize the palette
+  palette_load(owner->gtkobj());
+
+  // Create the GtkXText object
+  _xtext = GTK_XTEXT(gtk_xtext_new(75, 0));
+
+  // Internal init
+  _xtext->max_lines = 1024;
+  _xtext->urlcheck_function = word_check;    
+  _xtext->auto_indent = !indent;
+  _xtext->wordwrap = true;
+
+  // Display the widget
+  gtk_xtext_set_palette(_xtext, colors);
+  gtk_xtext_set_font(_xtext, gs->font, "");
+
+  // Create a frame around it
+  _frmChat = GTK_FRAME(gtk_frame_new(NULL));
+  gtk_frame_set_shadow_type(GTK_FRAME(_frmChat), GTK_SHADOW_IN);
+
+  // Add the widget to a container, which happens to be a frame
+  gtk_container_add(parent->gtkobj(), GTK_WIDGET(_frmChat));
+  gtk_container_add(GTK_CONTAINER(_frmChat), GTK_WIDGET(_xtext));
+  gtk_widget_show(GTK_WIDGET(_frmChat));
+  gtk_widget_show(GTK_WIDGET(_xtext));
+  
+  // Create a scrollbar
+  _vsChat = GTK_VSCROLLBAR(gtk_vscrollbar_new(_xtext->adj));
+  gtk_box_pack_start (GTK_BOX (parent->gtkobj()), GTK_WIDGET(_vsChat), 
+                     FALSE, FALSE, 1);
+  GTK_WIDGET_UNSET_FLAGS (_vsChat, GTK_CAN_FOCUS);
+  gtk_widget_show (GTK_WIDGET(_vsChat));
+
+  // Hookup stub callback
+  gtk_signal_connect(GTK_OBJECT(_xtext), "word_click", 
+                    GTK_SIGNAL_FUNC(&SilcerChatView::_on_word_clicked_stub), 
+                    this);
+}
+
+SilcerChatView::~SilcerChatView()
+{
+  gtk_widget_destroy(GTK_WIDGET(_xtext));
+  gtk_widget_destroy(GTK_WIDGET(_frmChat));
+  gtk_widget_destroy(GTK_WIDGET(_vsChat));
+}
+
+void SilcerChatView::render(const string &message, const string &username, 
+                     const string &timestamp, COLOR_T &c)
+{
+  if (username == "")
+    print(timestamp  + c + "***\017", message);
+  else
+    print(timestamp + c + "<\017" + username + c + ">\017" , message);
+}
+
+void SilcerChatView::render_error(const string &message, const string &error, 
+                                 const string &timestamp, COLOR_T &c)
+{
+  // Process error messages
+  print(timestamp + c + error + "\017:", message);
+}
+
+void SilcerChatView::clearbuffer()
+{
+  // Flush the buffer
+  gtk_xtext_remove_lines(_xtext, -1, true);
+}
+
+string SilcerChatView::get_chars()
+{
+  return string(gtk_xtext_get_chars(_xtext));
+}
+
+void SilcerChatView::on_word_clicked(char *word, GdkEventButton *evt)
+{
+  string s_word = word;
+
+  if (evt->button == 3) {
+    switch(word_check(_xtext, word))
+      {
+      case WORD_URL:
+       gnome_url_show(word);
+       break;
+      case WORD_HOST:
+       s_word = "http://" + s_word;
+       gnome_url_show(s_word.c_str());
+       break;
+      }          
+  }
+}
+
+void SilcerChatView::_on_word_clicked_stub(GtkXText *xtext, char *word, 
+                                    GdkEventButton *evt, 
+                                          SilcerChatView *_this)
+{
+  _this->on_word_clicked(word, evt);
+}
+
+inline void SilcerChatView::print(const string &s)
+{
+  char* astr = (char*)s.c_str();
+  char* anewline = strchr(astr, '\n');
+
+  if (anewline) {
+    while(1) {
+      gtk_xtext_append(_xtext, astr, 
+                      (unsigned long)anewline - (unsigned long)astr);
+      astr = anewline + 1;
+      if (*astr == 0)
+       break;
+      anewline = strchr(astr, '\n');
+      if (!anewline) {
+       gtk_xtext_append(_xtext, astr, -1);
+       break;
+      }
+    }
+  } else {
+    gtk_xtext_append(_xtext, astr, s.length());
+  }
+}
+
+inline void SilcerChatView::print(const string& left, const string& right)
+{
+  char* s = (char*)right.c_str();
+  char* newline = strchr(s, '\n');
+
+  if (newline) {
+    // Display the first line w/ the user who said it
+    gtk_xtext_append_indent(_xtext, (char*)left.c_str(), left.length(),
+                           s, (unsigned long)newline - (unsigned long)s);
+    // Now loop through the rest of the string, displaying it
+    while (1) {
+      s = newline + 1;
+      if (*s == 0)
+       break;
+      newline = strchr(s, '\n');
+      // If another newline is found, append it to the display and loop again
+      if (newline) {
+       gtk_xtext_append_indent(_xtext, "", 0, s, 
+                               (unsigned long)newline - (unsigned long)s);
+      } else {
+       // Otherwise append the rest of the string and break out of  the loop
+       gtk_xtext_append_indent(_xtext, "", 0, s, -1);
+       break;
+      }
+    }
+  } else {
+    gtk_xtext_append_indent(_xtext, (char*)left.c_str(), left.length(),
+                           (char*)right.c_str(), right.length());
+  }
+}
diff --git a/apps/silcer/src/silcerchatview.hh b/apps/silcer/src/silcerchatview.hh
new file mode 100644 (file)
index 0000000..5f8d3b6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+
+  silcerchatview.hh 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  Code is mostly ripped from Gabber, thus code also copyrighted by:
+  Copyright (C) 1999-2001 Dave Smith & Julian Missig
+*/
+
+#ifndef SILCERCHATVIEW_HH
+#define SILCERCHATVIEW_HH
+
+extern "C" {
+#include "xtext.h"
+}
+
+#include <gtk/gtkframe.h>
+#include <gtk/gtkvscrollbar.h>
+#include <gtk--/container.h>
+#include <gtk--/paned.h>
+#include <gtk--/widget.h>
+
+typedef const string COLOR_T;
+
+COLOR_T BLACK       = "\0030";
+COLOR_T WHITE       = "\0031";
+COLOR_T BLUE        = "\0032";
+COLOR_T GREEN       = "\0033";
+COLOR_T RED         = "\0034";
+COLOR_T YELLOWBROWN = "\0035";
+COLOR_T PURPLE      = "\0036";
+COLOR_T ORANGE      = "\0037";
+COLOR_T YELLOW      = "\0038";
+COLOR_T GREEN2      = "\0039";
+COLOR_T AQUA        = "\00310";
+COLOR_T LIGHTAQUA   = "\00311";
+COLOR_T BLUE2       = "\00312";
+COLOR_T PINK        = "\00313";
+COLOR_T GREY        = "\00314";
+COLOR_T LIGHTGREY   = "\00315";
+COLOR_T BLUEMARKBACK= "\00316";
+COLOR_T WHITEMARKFORE= "\00317";
+COLOR_T WHITEFORE   = "\00318";
+COLOR_T BLACKBACK   = "\00319";
+
+
+class SilcerChatView
+{
+public:
+  SilcerChatView(Gtk::Widget *owner, Gtk::Container *parent, 
+                gboolean indent = true);
+  SilcerChatView(Gtk::Widget *owner, Gtk::Paned *parent, 
+                gboolean indent = true);
+  ~SilcerChatView();
+  void render(const string &message, const string &username, 
+             const string &timestamp, COLOR_T &delimiter_color);
+  void render_error(const string &message, const string &error, 
+                   const string &timestamp, COLOR_T &delimiter_color);
+  void clearbuffer();
+  string get_chars();
+  GtkXText *_xtext;
+  GtkFrame *_frmChat;
+  GtkVScrollbar *_vsChat;
+
+ protected:
+  void print(const string &s);
+  void print(const string &left, const string &right);
+  void on_word_clicked(char* word, GdkEventButton *evt);
+  static void _on_word_clicked_stub(GtkXText *xtext, char *word, 
+                                   GdkEventButton *evt, 
+                                   SilcerChatView *_this);
+};
+
+#endif /* SILCERCHATVIEW_HH */
diff --git a/apps/silcer/src/xtext.c b/apps/silcer/src/xtext.c
new file mode 100644 (file)
index 0000000..74e8452
--- /dev/null
@@ -0,0 +1,3033 @@
+/* X-Chat
+ * Copyright (C) 1998 Peter Zelezny.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * =========================================================================
+ *
+ * xtext, the text widget used by X-Chat.
+ *
+ * By Peter Zelezny <zed@linux.com>.
+ * Some functions used from Zvt and Eterm (transparency stuff).
+ *
+ */
+
+#define USE_XLIB                                               /* turn this ON for non-xchat use. */
+#undef XCHAT                                                   /* using xchat */
+#define REFRESH_TIMEOUT 20
+#define WORDWRAP_LIMIT 24
+#define TINT_VALUE 195                         /* 195/255 of the brightness. */
+#define MOTION_MONITOR 1                       /* URL hilights. */
+#define MARGIN 2                                               /* dont touch. */
+
+#include <config.h>                    /* can define USE_XLIB here */
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkselection.h>
+
+#ifdef USE_XLIB
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#endif
+
+#include "xtext.h"
+
+#ifdef USE_GDK_PIXBUF
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#endif
+
+#undef GTK_WIDGET
+#define GTK_WIDGET(n) ((GtkWidget*)n)
+#undef GTK_OBJECT
+#define GTK_OBJECT(n) ((GtkObject*)n)
+#undef GTK_OBJECT_CLASS
+#define GTK_OBJECT_CLASS(n) ((GtkObjectClass*)n)
+
+static GtkWidgetClass *parent_class = NULL;
+
+enum
+{
+       WORD_CLICK,
+       LAST_SIGNAL
+};
+static guint xtext_signals[LAST_SIGNAL] = { 0 };
+
+#ifdef XCHAT
+char *nocasestrstr (char *text, char *tofind); /* util.c */
+#endif
+static void gtk_xtext_render_page (GtkXText * xtext);
+static void gtk_xtext_calc_lines (GtkXText * xtext, int);
+#ifdef USE_XLIB
+static void gtk_xtext_load_trans (GtkXText * xtext);
+static void gtk_xtext_free_trans (GtkXText * xtext);
+#endif
+static textentry *gtk_xtext_nth (GtkXText * xtext, textentry * start_ent,
+                                                                                       int line, int width, int *subline);
+static gint gtk_xtext_selection_kill (GtkWidget * widget,
+                                                                                                 GdkEventSelection * event);
+static void gtk_xtext_selection_get (GtkWidget * widget,
+                                                                                                GtkSelectionData * selection_data_ptr,
+                                                                                                guint info, guint time);
+static int gtk_xtext_text_width (GtkXText * xtext, unsigned char *text,
+                                                                                       int len);
+static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
+                                                                                                               GtkXText * xtext);
+static void gtk_xtext_draw_sep (GtkXText * xtext, int height);
+static void gtk_xtext_render_ents (GtkXText * xtext, textentry *, textentry *,
+                                                                                         int);
+static void gtk_xtext_recalc_widths (GtkXText * xtext, int);
+static void gtk_xtext_fix_indent (GtkXText * xtext);
+
+/* some utility functions first */
+
+#ifndef XCHAT  /* xchat has this in util.c */
+
+static char *
+nocasestrstr (char *s, char *wanted)
+{
+   register const size_t len = strlen (wanted);
+
+   if (len == 0)
+     return (char *)s;
+   while (toupper(*s) != toupper(*wanted) || strncasecmp (s, wanted, len))
+     if (*s++ == '\0')
+       return (char *)NULL;
+   return (char *)s;   
+}
+
+#endif
+
+static int
+is_del (char c)
+{
+       switch (c)
+       {
+       case ' ':
+       case 0:
+       case '\n':
+               /*case '[':
+                  case ']': */
+       case ')':
+       case '(':
+       case '>':
+       case '<':
+               return 1;
+       }
+       return 0;
+}
+
+static void
+xtext_set_fg (GdkGC *gc, gulong pixel)
+{
+       GdkColor col;
+
+       col.pixel = pixel;
+       gdk_gc_set_foreground (gc, &col);
+}
+
+static void
+xtext_set_bg (GdkGC *gc, gulong pixel)
+{
+       GdkColor col;
+
+       col.pixel = pixel;
+       gdk_gc_set_background (gc, &col);
+}
+
+static void
+gtk_xtext_init (GtkXText * xtext)
+{
+       xtext->old_value = -1;
+       xtext->pixmap = NULL;
+       xtext->text_first = NULL;
+       xtext->text_last = NULL;
+       xtext->io_tag = -1;
+       xtext->add_io_tag = -1;
+       xtext->scroll_tag = -1;
+/*   xtext->frozen = 0;*/
+       xtext->num_lines = 0;
+       xtext->max_lines = 0;
+       xtext->col_back = 19;
+       xtext->col_fore = 18;
+       xtext->nc = 0;
+       xtext->scrollbar_down = TRUE;
+       xtext->bold = FALSE;
+       xtext->underline = FALSE;
+       xtext->reverse = FALSE;
+       xtext->time_stamp = FALSE;
+       xtext->font = NULL;
+       xtext->error_function = NULL;
+       xtext->urlcheck_function = NULL;
+       xtext->color_paste = FALSE;
+       xtext->skip_fills = FALSE;
+       xtext->skip_border_fills = FALSE;
+       xtext->do_underline_fills_only = FALSE;
+       xtext->tint_red = xtext->tint_green = xtext->tint_blue = TINT_VALUE;
+
+       xtext->adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 0, 1, 0, 0);
+       gtk_object_ref ((GtkObject *) xtext->adj);
+       gtk_object_sink ((GtkObject *) xtext->adj);
+
+       gtk_signal_connect (GTK_OBJECT (xtext->adj), "value_changed",
+                                                         GTK_SIGNAL_FUNC (gtk_xtext_adjustment_changed), xtext);
+       gtk_signal_connect (GTK_OBJECT (xtext), "selection_clear_event",
+                                                         GTK_SIGNAL_FUNC (gtk_xtext_selection_kill), xtext);
+       gtk_selection_add_target (GTK_WIDGET (xtext),
+                                                                         GDK_SELECTION_PRIMARY,
+                                                                         GDK_SELECTION_TYPE_STRING, 1);
+       gtk_signal_connect (GTK_OBJECT (xtext), "selection_get",
+                                                         GTK_SIGNAL_FUNC (gtk_xtext_selection_get), xtext);
+}
+
+static void
+gtk_xtext_adjustment_set (GtkXText * xtext, int fire_signal)
+{
+       GtkAdjustment *adj = xtext->adj;
+
+       adj->lower = 0;
+       adj->upper = xtext->num_lines;
+
+       adj->page_size =
+               (GTK_WIDGET (xtext)->allocation.height -
+                xtext->font->descent) / xtext->fontsize;
+       adj->page_increment = adj->page_size;
+
+       if (adj->value > adj->upper - adj->page_size)
+               adj->value = adj->upper - adj->page_size;
+
+       if (fire_signal)
+               gtk_adjustment_changed (adj);
+}
+
+static gint
+gtk_xtext_adjustment_timeout (GtkXText * xtext)
+{
+       gtk_xtext_render_page (xtext);
+       xtext->io_tag = -1;
+       return 0;
+}
+
+static void
+gtk_xtext_adjustment_changed (GtkAdjustment * adj, GtkXText * xtext)
+{
+/*   if (xtext->frozen)
+      return;*/
+
+       if ((int) xtext->old_value != (int) xtext->adj->value)
+       {
+               if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size)
+                       xtext->scrollbar_down = TRUE;
+               else
+                       xtext->scrollbar_down = FALSE;
+
+               if (xtext->adj->value + 1 == xtext->old_value ||
+                        xtext->adj->value - 1 == xtext->old_value)     /* clicked an arrow? */
+               {
+                       if (xtext->io_tag != -1)
+                       {
+                               gtk_timeout_remove (xtext->io_tag);
+                               xtext->io_tag = -1;
+                       }
+                       gtk_xtext_render_page (xtext);
+               } else
+               {
+                       if (xtext->io_tag == -1)
+                               xtext->io_tag = gtk_timeout_add (REFRESH_TIMEOUT,
+                                                                                                                       (GtkFunction)
+                                                                                                                       gtk_xtext_adjustment_timeout,
+                                                                                                                       xtext);
+               }
+       }
+       xtext->old_value = adj->value;
+}
+
+GtkWidget *
+gtk_xtext_new (int indent, int separator)
+{
+       GtkXText *xtext;
+
+       xtext = gtk_type_new (gtk_xtext_get_type ());
+       xtext->indent = indent;
+       xtext->separator = separator;
+       xtext->wordwrap = FALSE;
+       xtext->double_buffer = FALSE;
+
+       return GTK_WIDGET (xtext);
+}
+
+static void
+gtk_xtext_destroy (GtkObject * object)
+{
+       GtkXText *xtext = GTK_XTEXT (object);
+       textentry *ent, *next;
+
+       if (xtext->add_io_tag != -1)
+       {
+               gtk_timeout_remove (xtext->add_io_tag);
+               xtext->add_io_tag = -1;
+       }
+
+       if (xtext->scroll_tag != -1)
+       {
+               gtk_timeout_remove (xtext->scroll_tag);
+               xtext->scroll_tag = -1;
+       }
+
+       if (xtext->io_tag != -1)
+       {
+               gtk_timeout_remove (xtext->io_tag);
+               xtext->io_tag = -1;
+       }
+
+       if (xtext->pixmap)
+       {
+#ifdef USE_XLIB
+               if (xtext->transparent)
+                       gtk_xtext_free_trans (xtext);
+               else
+#endif
+                       gdk_pixmap_unref (xtext->pixmap);
+               xtext->pixmap = NULL;
+       }
+
+       if (xtext->font)
+       {
+               gdk_font_unref (xtext->font);
+               xtext->font = NULL;
+       }
+
+       if (xtext->adj)
+       {
+               gtk_signal_disconnect_by_data (GTK_OBJECT (xtext->adj), xtext);
+               gtk_object_unref (GTK_OBJECT (xtext->adj));
+               xtext->adj = NULL;
+       }
+
+       if (xtext->bgc)
+       {
+               gdk_gc_destroy (xtext->bgc);
+               xtext->bgc = NULL;
+       }
+
+       if (xtext->fgc)
+       {
+               gdk_gc_destroy (xtext->fgc);
+               xtext->fgc = NULL;
+       }
+
+       if (xtext->light_gc)
+       {
+               gdk_gc_destroy (xtext->light_gc);
+               xtext->light_gc = NULL;
+       }
+
+       if (xtext->dark_gc)
+       {
+               gdk_gc_destroy (xtext->dark_gc);
+               xtext->dark_gc = NULL;
+       }
+
+       if (xtext->hand_cursor)
+       {
+               gdk_cursor_destroy (xtext->hand_cursor);
+               xtext->hand_cursor = NULL;
+       }
+
+       ent = xtext->text_first;
+       while (ent)
+       {
+               next = ent->next;
+               free (ent);
+               ent = next;
+       }
+       xtext->text_first = NULL;
+
+       if (GTK_OBJECT_CLASS (parent_class)->destroy)
+               (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gtk_xtext_realize (GtkWidget * widget)
+{
+       GtkXText *xtext;
+       GdkWindowAttr attributes;
+       GdkGCValues val;
+       GdkColor col;
+       GdkColormap *cmap;
+
+       GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+       xtext = GTK_XTEXT (widget);
+
+       attributes.x = widget->allocation.x;
+       attributes.y = widget->allocation.y;
+       attributes.width = widget->allocation.width;
+       attributes.height = widget->allocation.height;
+       attributes.wclass = GDK_INPUT_OUTPUT;
+       attributes.window_type = GDK_WINDOW_CHILD;
+       attributes.event_mask = gtk_widget_get_events (widget) |
+               GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+#ifdef MOTION_MONITOR
+               | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK;
+#else
+               | GDK_POINTER_MOTION_MASK;
+#endif
+
+       cmap = gtk_widget_get_colormap (widget);
+       attributes.colormap = cmap;
+       attributes.visual = gtk_widget_get_visual (widget);
+
+       widget->window = gdk_window_new (widget->parent->window, &attributes,
+                                                                                               GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
+                                                                                               GDK_WA_COLORMAP);
+
+       gdk_window_set_user_data (widget->window, widget);
+
+       xtext->depth = gdk_window_get_visual (widget->window)->depth;
+
+       val.subwindow_mode = GDK_INCLUDE_INFERIORS;
+       val.graphics_exposures = 0;
+
+       xtext->bgc = gdk_gc_new_with_values (widget->window, &val,
+                                                                                                        GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+       xtext->fgc = gdk_gc_new_with_values (widget->window, &val,
+                                                                                                        GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+       xtext->light_gc = gdk_gc_new_with_values (widget->window, &val,
+                                                                                       GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+       xtext->dark_gc = gdk_gc_new_with_values (widget->window, &val,
+                                                                                       GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+
+       /* for the separator bar (light) */
+       col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff;
+       /* is setting the pixel necessary (or even correct) ?? */
+       col.pixel = (gulong)((col.red & 0xff00) * 256 +
+                                                               (col.green & 0xff00) +
+                                                               (col.blue & 0xff00) / 256);
+       gdk_color_alloc (cmap, &col);
+       gdk_gc_set_foreground (xtext->light_gc, &col);
+
+       /* for the separator bar (dark) */
+       col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38;
+       col.pixel = (gulong)((col.red & 0xff00) * 256 +
+                                                               (col.green & 0xff00) +
+                                                               (col.blue & 0xff00) / 256);
+       gdk_color_alloc (cmap, &col);
+       gdk_gc_set_foreground (xtext->dark_gc, &col);
+
+       if (xtext->fonttype != FONT_SET && xtext->font != NULL)
+               gdk_gc_set_font (xtext->fgc, xtext->font);
+
+       xtext_set_fg (xtext->fgc, xtext->palette[18]);
+       xtext_set_bg (xtext->fgc, xtext->palette[19]);
+       xtext_set_fg (xtext->bgc, xtext->palette[19]);
+
+#ifdef USE_XLIB
+       if (xtext->transparent)
+       {
+               gtk_xtext_load_trans (xtext);
+       } else if (xtext->pixmap)
+       {
+               gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+               gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+               gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+       }
+#else
+       if (xtext->pixmap)
+       {
+               gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+               gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+               gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+       }
+#endif
+
+       xtext->hand_cursor = gdk_cursor_new (GDK_HAND1);
+
+       gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
+
+       /* if not doublebuffer, draw directly to window */
+       if (!xtext->double_buffer)
+               xtext->draw_buf = widget->window;
+
+       if (xtext->auto_indent)
+               xtext->indent = 1;
+}
+
+static void
+gtk_xtext_size_request (GtkWidget * widget, GtkRequisition * requisition)
+{
+       requisition->width = GTK_XTEXT (widget)->fontwidth['Z'] * 20;
+       requisition->height = (GTK_XTEXT (widget)->fontsize * 10) + 3;
+}
+
+static void
+gtk_xtext_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
+{
+       GtkXText *xtext = GTK_XTEXT (widget);
+
+       if (allocation->width == widget->allocation.width &&
+                allocation->height == widget->allocation.height &&
+                allocation->x == widget->allocation.x &&
+                allocation->y == widget->allocation.y)
+               return;
+
+       widget->allocation = *allocation;
+       if (GTK_WIDGET_REALIZED (widget))
+       {
+               gdk_window_move_resize (widget->window,
+                                                                               allocation->x, allocation->y,
+                                                                               allocation->width, allocation->height);
+               gtk_xtext_calc_lines (xtext, FALSE);
+       }
+}
+
+static void
+gtk_xtext_draw (GtkWidget * widget, GdkRectangle * area)
+{
+       int x, y;
+       GtkXText *xtext = GTK_XTEXT (widget);
+
+#ifdef USE_XLIB
+       if (xtext->transparent)
+       {
+               gdk_window_get_origin (widget->window, &x, &y);
+               /* update transparency only if it moved */
+               if (xtext->last_win_x != x || xtext->last_win_y != y)
+               {
+                       xtext->last_win_x = x;
+                       xtext->last_win_y = y;
+                       gtk_xtext_free_trans (xtext);
+                       gtk_xtext_load_trans (xtext);
+               }
+       }
+#endif
+
+       if (xtext->scrollbar_down)
+               gtk_adjustment_set_value (xtext->adj,
+                                                                                       xtext->adj->upper - xtext->adj->page_size);
+       gtk_xtext_render_page (xtext);
+}
+
+static int
+gtk_xtext_selection_clear (GtkXText * xtext)
+{
+       textentry *ent;
+       int ret = 0;
+
+       ent = xtext->last_ent_start;
+       while (ent)
+       {
+               if (ent->mark_start != -1)
+                       ret = 1;
+               ent->mark_start = -1;
+               ent->mark_end = -1;
+               if (ent == xtext->last_ent_end)
+                       break;
+               ent = ent->next;
+       }
+
+       return ret;
+}
+
+static int
+find_x_8bit (GtkXText *xtext, textentry *ent, char *text, int x, int indent)
+{
+       int xx = indent;
+       int i = 0;
+       int col = FALSE;
+       int nc = 0;
+       char *orig = text;
+       int a;
+
+       while (*text)
+       {
+               if ((col && isdigit (*text) && nc < 2) ||
+                        (col && *text == ',' && nc < 3))
+               {
+                       nc++;
+                       if (*text == ',')
+                               nc = 0;
+               } else
+               {
+                       col = FALSE;
+                       switch (*text)
+                       {
+                       case ATTR_COLOR:
+                               col = TRUE;
+                               nc = 0;
+                               break;
+                       case ATTR_BEEP:
+                       case ATTR_RESET:
+                       case ATTR_REVERSE:
+                       case ATTR_BOLD:
+                       case ATTR_UNDERLINE:
+                               break;
+                       default:
+                               a = *((unsigned char *)text);
+                               xx += xtext->fontwidth[a];
+                               if (xx >= x)
+                                       return i + (orig - ent->str);
+                       }
+               }
+               text++;
+               i++;
+               if (text - orig >= ent->str_len)
+                       return ent->str_len;
+       }
+
+       return ent->str_len;
+}
+
+static int
+find_x_general (GtkXText * xtext, textentry * ent, char *str, int x, int indent)
+{
+       int str_width;
+       int len = 1;
+
+       while (1)
+       {
+               str_width = gtk_xtext_text_width (xtext, str, len);
+               if (str_width + indent >= x)
+                       return (str + len) - ent->str;
+               len++;
+               if (len + (str - ent->str) > ent->str_len)
+                       return ent->str_len;
+               if (str_width + indent + 40 < x)
+                       len += 2;
+       }
+}
+
+static int
+find_x (GtkXText * xtext, textentry * ent, char *str, int x, int indent)
+{
+       if (xtext->fonttype == FONT_1BYTE)
+               return find_x_8bit (xtext, ent, str, x, indent);
+
+       return find_x_general (xtext, ent, str, x, indent);
+}
+
+static int
+gtk_xtext_find_x (GtkXText * xtext, int x, textentry * ent, int offset,
+                                               int line, int win_width, int *out_of_bounds)
+{
+       int indent;
+       char *str;
+
+       if (offset < 1)
+               indent = ent->indent;
+       else
+               indent = xtext->indent;
+
+       if (line > xtext->adj->page_size || line < 0)
+               return 0;
+
+       if (xtext->grid_offset[line] > ent->str_len)
+               return 0;
+
+       if (xtext->grid_offset[line] < 0)
+               return 0;
+
+       str = ent->str + xtext->grid_offset[line];
+
+       if (x < indent)
+       {
+               *out_of_bounds = 1;
+               return (str - ent->str);
+       }
+
+       *out_of_bounds = 0;
+
+       return find_x (xtext, ent, str, x, indent);
+}
+
+static textentry *
+gtk_xtext_find_char (GtkXText * xtext, int x, int y, int *off,
+                                                       int *out_of_bounds)
+{
+       textentry *ent;
+       int line;
+       int subline;
+       int win_width;
+
+       gdk_window_get_size (GTK_WIDGET (xtext)->window, &win_width, 0);
+       win_width -= MARGIN;
+
+       line = (y - xtext->font->descent) / xtext->fontsize;
+
+       subline = xtext->pagetop_subline;
+       ent = gtk_xtext_nth (xtext, xtext->pagetop_ent, line, win_width, &subline);
+       if (!ent)
+               return 0;
+
+       if (off)
+               *off = gtk_xtext_find_x (xtext, x, ent, subline, line, win_width,
+                                                               out_of_bounds);
+
+       return ent;
+}
+
+static gint
+gtk_xtext_expose (GtkWidget * widget, GdkEventExpose * event)
+{
+       GtkXText *xtext = GTK_XTEXT (widget);
+       textentry *ent_start, *ent_end;
+
+       if (xtext->double_buffer)
+       {
+               gtk_xtext_render_page (xtext);
+               return FALSE;
+       }
+
+       gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1,
+                                                         event->area.x, event->area.y,
+                                                         event->area.width, event->area.height);
+
+       ent_start = gtk_xtext_find_char (xtext, event->area.x, event->area.y,
+                                                                                               NULL, NULL);
+       ent_end = gtk_xtext_find_char (xtext, event->area.x + event->area.width,
+                                               event->area.y + event->area.height, NULL, NULL);
+
+       xtext->skip_fills = TRUE;
+       xtext->skip_border_fills = TRUE;
+
+       gtk_xtext_render_ents (xtext, ent_start, ent_end, TRUE);
+
+       xtext->skip_fills = FALSE;
+       xtext->skip_border_fills = FALSE;
+
+       return FALSE;
+}
+
+static void
+gtk_xtext_selection_draw (GtkXText * xtext, GdkEventMotion * event)
+{
+       textentry *ent;
+       textentry *ent_end;
+       textentry *ent_start;
+       int offset_start;
+       int offset_end;
+       int low_x;
+       int low_y;
+       int high_x;
+       int high_y;
+       int tmp;
+
+       if (xtext->select_start_y > xtext->select_end_y)
+       {
+               low_x = xtext->select_end_x;
+               low_y = xtext->select_end_y;
+               high_x = xtext->select_start_x;
+               high_y = xtext->select_start_y;
+       } else
+       {
+               low_x = xtext->select_start_x;
+               low_y = xtext->select_start_y;
+               high_x = xtext->select_end_x;
+               high_y = xtext->select_end_y;
+       }
+
+       ent_start = gtk_xtext_find_char (xtext, low_x, low_y, &offset_start, &tmp);
+       ent_end = gtk_xtext_find_char (xtext, high_x, high_y, &offset_end, &tmp);
+
+       if (ent_start && !ent_end)
+       {
+               ent_end = xtext->text_last;
+               offset_end = ent_end->str_len;
+       }
+
+       if (!ent_start || !ent_end)
+       {
+               if (xtext->adj->value != xtext->old_value)
+                       gtk_xtext_render_page (xtext);
+               return;
+       }
+
+       gtk_xtext_selection_clear (xtext);
+
+       /* marking less than a complete line? */
+       if (ent_start == ent_end)
+       {
+               ent_start->mark_start = MIN (offset_start, offset_end);
+               ent_start->mark_end = MAX (offset_end, offset_start);
+               if (offset_start == offset_end)
+                       ent_start->mark_end++;
+       } else
+       {
+               ent_start->mark_start = offset_start;
+               ent_start->mark_end = ent_start->str_len;
+
+               if (offset_end != 0)
+               {
+                       ent_end->mark_start = 0;
+                       ent_end->mark_end = offset_end;
+               }
+       }
+
+       if (ent_start != ent_end)
+       {
+               ent = ent_start->next;
+               while (ent && ent != ent_end)
+               {
+                       ent->mark_start = 0;
+                       ent->mark_end = ent->str_len;
+                       ent = ent->next;
+               }
+       }
+
+       /* has the selection changed? Dont render unless necessary */
+       if (xtext->last_ent_start == ent_start &&
+                xtext->last_ent_end == ent_end &&
+                xtext->last_offset_start == offset_start &&
+                xtext->last_offset_end == offset_end)
+               return;
+
+       gtk_selection_owner_set (GTK_WIDGET (xtext), GDK_SELECTION_PRIMARY,
+                                                                        event->time);
+
+       if (xtext->double_buffer)
+       {
+               if (xtext->io_tag == -1)
+                       xtext->io_tag = gtk_timeout_add (REFRESH_TIMEOUT,
+                                                                                                               (GtkFunction)
+                                                                                                               gtk_xtext_adjustment_timeout,
+                                                                                                               xtext);
+       } else
+       {
+               ent = xtext->last_ent_end;
+               if (ent)
+                       if (ent->next == ent_end)
+                               ent = ent_end;
+               xtext->skip_border_fills = TRUE;
+               gtk_xtext_render_ents (xtext, xtext->last_ent_start, ent, TRUE);
+               xtext->skip_border_fills = FALSE;
+               xtext->old_ent_start = xtext->last_ent_start;
+               xtext->old_ent_end = xtext->last_ent_end;
+       }
+
+       xtext->last_ent_start = ent_start;
+       xtext->last_ent_end = ent_end;
+       xtext->last_offset_start = offset_start;
+       xtext->last_offset_end = offset_end;
+}
+
+static gint
+gtk_xtext_scrolldown_timeout (GtkXText * xtext)
+{
+       int p_y, win_height;
+
+       gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
+       gdk_window_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
+
+       if (p_y > win_height &&
+                xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
+       {
+               xtext->adj->value++;
+               gtk_adjustment_changed (xtext->adj);
+               gtk_xtext_render_page (xtext);
+               return 1;
+       }
+
+       xtext->scroll_tag = -1;
+       return 0;
+}
+
+static gint
+gtk_xtext_scrollup_timeout (GtkXText * xtext)
+{
+       int p_y;
+
+       gdk_window_get_pointer (GTK_WIDGET (xtext)->window, 0, &p_y, 0);
+
+       if (p_y < 0 && xtext->adj->value > 0.0)
+       {
+               xtext->adj->value--;
+               gtk_adjustment_changed (xtext->adj);
+               gtk_xtext_render_page (xtext);
+               return 1;
+       }
+
+       xtext->scroll_tag = -1;
+       return 0;
+}
+
+static void
+gtk_xtext_selection_update (GtkXText * xtext, GdkEventMotion * event, int p_y)
+{
+       int win_height;
+       int moved;
+
+       gdk_window_get_size (GTK_WIDGET (xtext)->window, 0, &win_height);
+
+       /* selecting past top of window, scroll up! */
+       if (p_y < 0 && xtext->adj->value >= 0)
+       {
+               if (xtext->scroll_tag == -1)
+                       xtext->scroll_tag = gtk_timeout_add (100,
+                                                                                                                        (GtkFunction)
+                                                                                                                        gtk_xtext_scrollup_timeout,
+                                                                                                                        xtext);
+               return;
+       }
+
+       /* selecting past bottom of window, scroll down! */
+       if (p_y > win_height &&
+                xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size))
+       {
+               if (xtext->scroll_tag == -1)
+                       xtext->scroll_tag = gtk_timeout_add (100,
+                                                                                                                        (GtkFunction)
+                                                                                                                        gtk_xtext_scrolldown_timeout,
+                                                                                                                        xtext);
+               return;
+       }
+
+       moved = xtext->adj->value - xtext->select_start_adj;
+       xtext->select_start_y -= (moved * xtext->fontsize);
+       xtext->select_start_adj = xtext->adj->value;
+       gtk_xtext_selection_draw (xtext, event);
+}
+
+static char *
+gtk_xtext_get_word (GtkXText * xtext, int x, int y, textentry ** ret_ent,
+                                                 int *ret_off, int *ret_len)
+{
+       textentry *ent;
+       int offset;
+       char *str;
+       char *word;
+       int len;
+       int out_of_bounds;
+
+       ent = gtk_xtext_find_char (xtext, x, y, &offset, &out_of_bounds);
+       if (!ent)
+               return 0;
+
+       if (out_of_bounds)
+               return 0;
+
+       if (offset == ent->str_len)
+               return 0;
+
+       if (offset < 1)
+               return 0;
+
+       offset--;
+
+       str = ent->str + offset;
+
+       while (!is_del (*str) && str != ent->str)
+               str--;
+       word = str + 1;
+
+       len = 0;
+       str = word;
+       while (!is_del (*str) && len != ent->str_len)
+       {
+               str++;
+               len++;
+       }
+
+       if (ret_ent)
+               *ret_ent = ent;
+       if (ret_off)
+               *ret_off = word - ent->str;
+       if (ret_len)
+               *ret_len = str - word;
+
+       word = gtk_xtext_strip_color (word, len, NULL, NULL);
+
+       return word;
+}
+
+static gint
+gtk_xtext_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
+{
+#ifdef MOTION_MONITOR
+       GtkXText *xtext = GTK_XTEXT (widget);
+
+       if (xtext->cursor_hand)
+       {
+               xtext->hilight_start = -1;
+               xtext->hilight_end = -1;
+               xtext->cursor_hand = FALSE;
+               gdk_window_set_cursor (widget->window, 0);
+               xtext->skip_border_fills = TRUE;
+               xtext->do_underline_fills_only = TRUE;
+               gtk_xtext_render_ents (xtext, xtext->hilight_ent, NULL, FALSE);
+               xtext->skip_border_fills = FALSE;
+               xtext->do_underline_fills_only = FALSE;
+               xtext->hilight_ent = NULL;
+       }
+#endif
+       return FALSE;
+}
+
+static gint
+gtk_xtext_motion_notify (GtkWidget * widget, GdkEventMotion * event)
+{
+       GtkXText *xtext = GTK_XTEXT (widget);
+       int tmp, x, y, offset, len;
+       char *word;
+       textentry *word_ent, *old_ent;
+
+       gdk_window_get_pointer (widget->window, &x, &y, 0);
+
+       if (xtext->moving_separator)
+       {
+               if (x < (3 * widget->allocation.width) / 5 && x > 15)
+               {
+                       tmp = xtext->indent;
+                       xtext->indent = x;
+                       gtk_xtext_fix_indent (xtext);
+                       if (tmp != xtext->indent)
+                       {
+                               gtk_xtext_recalc_widths (xtext, FALSE);
+                               if (xtext->scrollbar_down)
+                                       gtk_adjustment_set_value (xtext->adj, xtext->adj->upper -
+                                                                                                         xtext->adj->page_size);
+                               if (xtext->io_tag == -1)
+                                       xtext->io_tag = gtk_timeout_add (REFRESH_TIMEOUT,
+                                                                                                                               (GtkFunction)
+                                                                                                                               gtk_xtext_adjustment_timeout,
+                                                                                                                               xtext);
+                       }
+               }
+               return FALSE;
+       }
+
+       if (xtext->button_down)
+       {
+               gtk_grab_add (widget);
+               /*gdk_pointer_grab (widget->window, TRUE,
+                                                                       GDK_BUTTON_RELEASE_MASK |
+                                                                       GDK_BUTTON_MOTION_MASK, NULL, NULL, 0);*/
+               xtext->select_end_x = x;
+               xtext->select_end_y = y;
+               gtk_xtext_selection_update (xtext, event, y);
+               return FALSE;
+       }
+#ifdef MOTION_MONITOR
+
+       if (xtext->urlcheck_function == NULL)
+               return FALSE;
+
+       word = gtk_xtext_get_word (xtext, x, y, &word_ent, &offset, &len);
+       if (word)
+       {
+               if (xtext->urlcheck_function (xtext, word) > 0)
+               {
+                       free (word);
+                       if (!xtext->cursor_hand ||
+                                xtext->hilight_ent != word_ent ||
+                                xtext->hilight_start != offset ||
+                                xtext->hilight_end != offset + len)
+                       {
+                               if (!xtext->cursor_hand)
+                               {
+                                       gdk_window_set_cursor (GTK_WIDGET (xtext)->window,
+                                                                                                       xtext->hand_cursor);
+                                       xtext->cursor_hand = TRUE;
+                               }
+                               old_ent = xtext->hilight_ent;
+                               xtext->hilight_ent = word_ent;
+                               xtext->hilight_start = offset;
+                               xtext->hilight_end = offset + len;
+                               xtext->skip_border_fills = TRUE;
+                               xtext->do_underline_fills_only = TRUE;
+                               gtk_xtext_render_ents (xtext, old_ent, word_ent, FALSE);
+                               xtext->skip_border_fills = FALSE;
+                               xtext->do_underline_fills_only = FALSE;
+                       }
+                       return FALSE;
+               }
+               free (word);
+       }
+
+       gtk_xtext_leave_notify (widget, NULL);
+
+#endif
+
+       return FALSE;
+}
+
+static gint
+gtk_xtext_button_release (GtkWidget * widget, GdkEventButton * event)
+{
+       GtkXText *xtext = GTK_XTEXT (widget);
+       char *word;
+
+       if (xtext->moving_separator)
+       {
+               xtext->moving_separator = FALSE;
+               if (event->x < (4 * widget->allocation.width) / 5 && event->x > 15)
+               {
+                       xtext->indent = event->x;
+               }
+               gtk_xtext_fix_indent (xtext);
+               gtk_xtext_recalc_widths (xtext, FALSE);
+               gtk_xtext_adjustment_set (xtext, TRUE);
+               gtk_xtext_render_page (xtext);
+               return FALSE;
+       }
+
+       if (xtext->word_or_line_select)
+       {
+               xtext->word_or_line_select = FALSE;
+               xtext->button_down = FALSE;
+               return FALSE;
+       }
+
+       if (event->button == 1)
+       {
+               xtext->button_down = FALSE;
+
+               gtk_grab_remove (widget);
+               /*gdk_pointer_ungrab (0);*/
+
+               if (xtext->select_start_x == event->x &&
+                        xtext->select_start_y == event->y)
+               {
+                       if (gtk_xtext_selection_clear (xtext))
+                               gtk_xtext_render_page (xtext);
+               } else
+               {
+                       word = gtk_xtext_get_word (xtext, event->x, event->y, 0, 0, 0);
+                       if (word)
+                       {
+                               gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK],
+                                                                         word, event);
+                               free (word);
+                               return FALSE;
+                       }
+               }
+       }
+
+       return FALSE;
+}
+
+static gint
+gtk_xtext_button_press (GtkWidget * widget, GdkEventButton * event)
+{
+       GtkXText *xtext = GTK_XTEXT (widget);
+       textentry *ent;
+       char *word;
+       int line_x, x, y, offset, len;
+       gfloat new_value;
+
+       gdk_window_get_pointer (widget->window, &x, &y, 0);
+
+       if (event->button == 3)           /* right click */
+       {
+               word = gtk_xtext_get_word (xtext, x, y, 0, 0, 0);
+               if (word)
+               {
+                       gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK],
+                                                                 word, event);
+                       free (word);
+               } else
+                       gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK],
+                                                                 "", event);
+               return FALSE;
+       }
+
+       if (event->button == 4)           /* mouse wheel pageUp */
+       {
+               new_value = xtext->adj->value - xtext->adj->page_increment;
+               if (new_value < xtext->adj->lower)
+                       new_value = xtext->adj->lower;
+               gtk_adjustment_set_value (xtext->adj, new_value);
+               return FALSE;
+       }
+
+       if (event->button == 5)           /* mouse wheel pageDn */
+       {
+               new_value = xtext->adj->value + xtext->adj->page_increment;
+               if (new_value > (xtext->adj->upper - xtext->adj->page_size))
+                       new_value = xtext->adj->upper - xtext->adj->page_size;
+               gtk_adjustment_set_value (xtext->adj, new_value);
+               return FALSE;
+       }
+
+       if (event->button == 2)
+       {
+               gtk_signal_emit (GTK_OBJECT (xtext), xtext_signals[WORD_CLICK], "", event);
+               return FALSE;
+       }
+
+       if (event->button != 1)           /* we only want left button */
+               return FALSE;
+
+       if (event->type == GDK_2BUTTON_PRESS)   /* WORD select */
+       {
+               word = gtk_xtext_get_word (xtext, x, y, &ent, &offset, &len);
+               if (word)
+               {
+                       free (word);
+                       if (len == 0)
+                               return FALSE;
+                       gtk_xtext_selection_clear (xtext);
+                       ent->mark_start = offset;
+                       ent->mark_end = offset + len;
+                       xtext->last_ent_start = ent;
+                       xtext->last_ent_end = ent;
+                       gtk_xtext_render_page (xtext);
+                       xtext->word_or_line_select = TRUE;
+                       gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, event->time);
+               }
+
+               return FALSE;
+       }
+
+       if (event->type == GDK_3BUTTON_PRESS)   /* LINE select */
+       {
+               word = gtk_xtext_get_word (xtext, x, y, &ent, 0, 0);
+               if (word)
+               {
+                       free (word);
+                       gtk_xtext_selection_clear (xtext);
+                       ent->mark_start = 0;
+                       ent->mark_end = ent->str_len;
+                       xtext->last_ent_start = ent;
+                       xtext->last_ent_end = ent;
+                       gtk_xtext_render_page (xtext);
+                       xtext->word_or_line_select = TRUE;
+                       gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, event->time);
+               }
+
+               return FALSE;
+       }
+
+       /* check if it was a separator-bar click */
+       if (xtext->separator && xtext->indent)
+       {
+               line_x = xtext->indent - ((xtext->space_width + 1) / 2);
+               if (line_x == x || line_x == x + 1 || line_x == x - 1)
+               {
+                       xtext->moving_separator = TRUE;
+                       gtk_xtext_render_page (xtext);
+                       return FALSE;
+               }
+       }
+
+       xtext->button_down = TRUE;
+
+       xtext->select_start_x = x;
+       xtext->select_start_y = y;
+
+       xtext->select_start_adj = xtext->adj->value;
+
+       return FALSE;
+}
+
+/* another program has claimed the selection */
+
+static gint
+gtk_xtext_selection_kill (GtkWidget * widget, GdkEventSelection * event)
+{
+       if (gtk_xtext_selection_clear (GTK_XTEXT (widget)))
+               gtk_xtext_render_page (GTK_XTEXT (widget));
+       return TRUE;
+}
+
+/* another program is asking for our selection */
+
+static void
+gtk_xtext_selection_get (GtkWidget * widget,
+                                                                GtkSelectionData * selection_data_ptr,
+                                                                guint info, guint time)
+{
+       GtkXText *xtext = GTK_XTEXT (widget);
+       textentry *ent;
+       char *txt;
+       char *pos;
+       char *stripped;
+       int len;
+       int first = TRUE;
+
+       /* first find out how much we need to malloc ... */
+       len = 0;
+       ent = xtext->text_first;
+       while (ent)
+       {
+               if (ent->mark_start != -1)
+               {
+                       if (ent->mark_end - ent->mark_start > 0)
+                               len += (ent->mark_end - ent->mark_start) + 1;
+                       else
+                               len++;
+               }
+               ent = ent->next;
+       }
+
+       /* now allocate mem and copy buffer */
+       pos = txt = malloc (len);
+       ent = xtext->text_first;
+       while (ent)
+       {
+               if (ent->mark_start != -1)
+               {
+                       if (!first)
+                       {
+                               *pos = '\n';
+                               pos++;
+                       }
+                       first = FALSE;
+                       if (ent->mark_end - ent->mark_start > 0)
+                       {
+                               memcpy (pos, ent->str + ent->mark_start,
+                                                 ent->mark_end - ent->mark_start);
+                               pos += ent->mark_end - ent->mark_start;
+                       }
+               }
+               ent = ent->next;
+       }
+       *pos = 0;
+
+       if (xtext->color_paste)
+       {
+               gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
+                                                                               8, txt, strlen (txt));
+       } else
+       {
+               stripped = gtk_xtext_strip_color (txt, strlen (txt), NULL, NULL);
+               gtk_selection_data_set (selection_data_ptr, GDK_SELECTION_TYPE_STRING,
+                                                                               8, stripped, strlen (stripped));
+               free (stripped);
+       }
+
+       free (txt);
+}
+
+static void
+gtk_xtext_class_init (GtkXTextClass * class)
+{
+       GtkObjectClass *object_class;
+       GtkWidgetClass *widget_class;
+       GtkXTextClass *xtext_class;
+
+       object_class = (GtkObjectClass *) class;
+       widget_class = (GtkWidgetClass *) class;
+       xtext_class = (GtkXTextClass *) class;
+
+       parent_class = gtk_type_class (gtk_widget_get_type ());
+
+       xtext_signals[WORD_CLICK] =
+               gtk_signal_new (/*name*/"word_click",
+                                                        /*GtkSignalRunType*/GTK_RUN_FIRST,
+                                                        /*GtkType*/object_class->type,
+                                                        /*funcoffset*/GTK_SIGNAL_OFFSET (GtkXTextClass, word_click),
+                                                        /*GtkSignalMarshaller*/gtk_marshal_NONE__POINTER_POINTER,
+                                                        /*returnval*/GTK_TYPE_NONE,
+                                                        /*num args*/2, /*args*/GTK_TYPE_POINTER, GTK_TYPE_POINTER);
+       gtk_object_class_add_signals (object_class, xtext_signals, LAST_SIGNAL);
+
+       object_class->destroy = gtk_xtext_destroy;
+
+       widget_class->realize = gtk_xtext_realize;
+       widget_class->size_request = gtk_xtext_size_request;
+       widget_class->size_allocate = gtk_xtext_size_allocate;
+       widget_class->button_press_event = gtk_xtext_button_press;
+       widget_class->button_release_event = gtk_xtext_button_release;
+       widget_class->motion_notify_event = gtk_xtext_motion_notify;
+       widget_class->leave_notify_event = gtk_xtext_leave_notify;
+       widget_class->draw = gtk_xtext_draw;
+       widget_class->expose_event = gtk_xtext_expose;
+
+       xtext_class->word_click = NULL;
+}
+
+guint gtk_xtext_get_type ()
+{
+       static guint xtext_type = 0;
+
+       if (!xtext_type)
+       {
+               GtkTypeInfo xtext_info = {
+                       "GtkXText",
+                       sizeof (GtkXText),
+                       sizeof (GtkXTextClass),
+                       (GtkClassInitFunc) gtk_xtext_class_init,
+                       (GtkObjectInitFunc) gtk_xtext_init,
+                       (GtkArgSetFunc) NULL,
+                       (GtkArgGetFunc) NULL,
+               };
+
+               xtext_type = gtk_type_unique (gtk_widget_get_type (), &xtext_info);
+       }
+
+       return xtext_type;
+}
+
+/*void
+gtk_xtext_thaw (GtkXText *xtext)
+{
+   if (xtext->frozen > 0)
+      xtext->frozen--;
+
+   if (xtext->frozen == 0)
+      gtk_xtext_render_page (xtext);
+}
+
+void
+gtk_xtext_freeze (GtkXText *xtext)
+{
+   xtext->frozen++;
+}*/
+
+/* strip MIRC colors and other attribs. */
+
+char *
+gtk_xtext_strip_color (unsigned char *text, int len, char *outbuf, int *newlen)
+{
+       int nc = 0;
+       int i = 0;
+       int col = FALSE;
+       char *new_str;
+
+       if (outbuf == NULL)
+               new_str = malloc (len + 2);
+       else
+               new_str = outbuf;
+
+       while (len > 0)
+       {
+               if ((col && isdigit (*text) && nc < 2) ||
+                        (col && *text == ',' && nc < 3))
+               {
+                       nc++;
+                       if (*text == ',')
+                               nc = 0;
+               } else
+               {
+                       if (col)
+                               col = FALSE;
+                       switch (*text)
+                       {
+                       case ATTR_COLOR:
+                               col = TRUE;
+                               nc = 0;
+                               break;
+                       case ATTR_BEEP:
+                       case ATTR_RESET:
+                       case ATTR_REVERSE:
+                       case ATTR_BOLD:
+                       case ATTR_UNDERLINE:
+                               break;
+                       default:
+                               new_str[i] = *text;
+                               i++;
+                       }
+               }
+               text++;
+               len--;
+       }
+
+       new_str[i] = 0;
+
+       if (newlen != NULL)
+               *newlen = i;
+
+       return new_str;
+}
+
+/* gives width of a 8bit string - with no mIRC codes in it */
+
+static int
+gtk_xtext_text_width_simple (GtkXText * xtext, unsigned char *str, int len)
+{
+       int width = 0;
+
+       if (xtext->fixed_width_font)
+               return (xtext->space_width * len);
+
+       while (len)
+       {
+               width += xtext->fontwidth[*str];
+               len--;
+               str++;
+       }
+
+       return width;
+}
+
+/* gives width of a string, excluding the mIRC codes */
+
+static int
+gtk_xtext_text_width (GtkXText * xtext, unsigned char *text, int len)
+{
+       unsigned char *tmp, *new_buf;
+       int width, new_len;
+
+       new_buf = gtk_xtext_strip_color (text, len, xtext->scratch_buffer, &new_len);
+
+       if (xtext->fonttype == FONT_1BYTE)
+       {
+               if (xtext->fixed_width_font)
+               {
+                       width = xtext->space_width * new_len;
+               } else
+               {
+                       width = 0;
+                       tmp = new_buf;
+                       while (*tmp)
+                       {
+                               width += xtext->fontwidth[*tmp];
+                               tmp++;
+                       }
+               }
+       } else
+       {
+               width = gdk_text_width (xtext->font, new_buf, new_len);
+       }
+
+       return width;
+}
+
+/* actually draw text to screen */
+
+static int
+gtk_xtext_render_flush (GtkXText * xtext, int x, int y, char *str, int len,
+                                                               GdkGC *gc)
+{
+       int str_width;
+       int dofill;
+#ifdef USE_XLIB
+       XFontStruct *xfont;
+       GC xgc;
+       Drawable xdraw_buf;
+       Display *xdisplay;
+#endif
+
+       if (xtext->dont_render)
+               return 0;
+
+       if (xtext->fonttype == FONT_1BYTE)
+               str_width = gtk_xtext_text_width_simple (xtext, str, len);
+       else
+               str_width = gdk_text_width (xtext->font, str, len);
+
+       if (str_width < 1)
+               return 0;
+
+       if (!xtext->backcolor && xtext->pixmap)
+       {
+               dofill = FALSE;
+               /* draw the background pixmap behind the text - CAUSES FLICKER HERE !! */
+               if (!xtext->double_buffer && !xtext->skip_fills)
+               {
+                       if (xtext->do_underline_fills_only)
+                       {
+                               gdk_draw_rectangle (GTK_WIDGET (xtext)->window, xtext->bgc, 1,
+                                                                                 x, y + 1, str_width, 1);
+                               if (xtext->underline)           /* optimization */
+                                       goto dounder;
+                       } else
+                       {
+                               gdk_draw_rectangle (GTK_WIDGET (xtext)->window, xtext->bgc, 1,
+                                                                                 x, y - xtext->font->ascent, str_width,
+                                                                                 xtext->fontsize);
+                       }
+               }
+       } else
+       {
+               dofill = TRUE;
+               if (xtext->skip_fills && !xtext->backcolor)
+                       dofill = FALSE;
+       }
+
+#ifdef USE_XLIB
+
+       xgc = GDK_GC_XGC (gc);
+       xdraw_buf = GDK_WINDOW_XWINDOW (xtext->draw_buf);
+       xdisplay = GDK_WINDOW_XDISPLAY (GTK_WIDGET (xtext)->window);
+       xfont = GDK_FONT_XFONT (xtext->font);
+
+       switch (xtext->fonttype)
+       {
+       case FONT_1BYTE:
+               if (dofill)
+                       XDrawImageString (xdisplay, xdraw_buf, xgc, x, y, str, len);
+               else
+                       XDrawString (xdisplay, xdraw_buf, xgc, x, y, str, len);
+               if (xtext->bold)
+                       XDrawString (xdisplay, xdraw_buf, xgc, x + 1, y, str, len);
+               break;
+
+       case FONT_2BYTE:
+               len /= 2;
+               if (dofill)
+                       XDrawImageString16 (xdisplay, xdraw_buf,
+                                                                         xgc, x, y, (XChar2b *) str, len);
+               else
+                       XDrawString16 (xdisplay, xdraw_buf,
+                                                               xgc, x, y, (XChar2b *) str, len);
+               if (xtext->bold)
+                       XDrawString16 (xdisplay, xdraw_buf,
+                                                               xgc, x + 1, y, (XChar2b *) str, len);
+               break;
+
+       case FONT_SET:
+               if (dofill)
+                       XmbDrawImageString (xdisplay, xdraw_buf,
+                                                                         (XFontSet) xfont, xgc, x, y, str, len);
+               else
+                       XmbDrawString (xdisplay, xdraw_buf,
+                                                               (XFontSet) xfont, xgc, x, y, str, len);
+               if (xtext->bold)
+                       XmbDrawString (xdisplay, xdraw_buf,
+                                                               (XFontSet) xfont, xgc, x + 1, y, str, len);
+       }
+
+#else
+
+       /* don't have Xlib, gdk version --- */
+       if (dofill)
+       {
+               GdkGCValues val;
+               gdk_gc_get_values (gc, &val);
+               xtext_set_fg (gc, val.background.pixel);
+               gdk_draw_rectangle (xtext->draw_buf, gc, 1,
+                                                               x, y - xtext->font->ascent, str_width,
+                                                               xtext->fontsize);
+               xtext_set_fg (gc, val.foreground.pixel);
+       }
+       gdk_draw_text (xtext->draw_buf, xtext->font, gc, x, y, str, len);
+       if (xtext->bold)
+               gdk_draw_text (xtext->draw_buf, xtext->font, gc, x + 1, y, str, len);
+
+#endif
+
+       if (xtext->underline)
+dounder:
+               gdk_draw_line (xtext->draw_buf, gc, x, y+1, x+str_width-1, y+1);
+
+       return str_width;
+}
+
+static void
+gtk_xtext_reset (GtkXText * xtext, int mark, int attribs)
+{
+       if (attribs)
+       {
+               xtext->underline = FALSE;
+               xtext->bold = FALSE;
+       }
+       if (!mark)
+       {
+               xtext->backcolor = FALSE;
+               if (xtext->col_fore != 18)
+                       xtext_set_fg (xtext->fgc, xtext->palette[18]);
+               if (xtext->col_back != 19)
+                       xtext_set_bg (xtext->fgc, xtext->palette[19]);
+       }
+       xtext->col_fore = 18;
+       xtext->col_back = 19;
+}
+
+/* render a single line, which WONT wrap, and parse mIRC colors */
+
+static void
+gtk_xtext_render_str (GtkXText * xtext, int y, textentry * ent, char *str,
+                                                        int len, int win_width, int indent, int line)
+{
+       GdkGC *gc;
+       int i = 0, x = indent, j = 0;
+       char *pstr = str;
+       int col_num, tmp;
+       int offset;
+       int mark = FALSE;
+       int hilight = FALSE;
+
+       offset = str - ent->str;
+
+       if (line < 255 && line >= 0)
+               xtext->grid_offset[line] = offset;
+
+       gc = xtext->fgc;                                  /* our foreground GC */
+
+       if (ent->mark_start != -1 &&
+                ent->mark_start <= i + offset && ent->mark_end > i + offset)
+       {
+               xtext_set_bg (gc, xtext->palette[16]);
+               xtext_set_fg (gc, xtext->palette[17]);
+               xtext->backcolor = TRUE;
+               mark = TRUE;
+       }
+#ifdef MOTION_MONITOR
+       if (xtext->hilight_ent == ent &&
+                xtext->hilight_start <= i + offset && xtext->hilight_end > i + offset)
+       {
+               xtext->underline = TRUE;
+/*      xtext->bold = TRUE;*/
+               hilight = TRUE;
+       }
+#endif
+
+       if (!xtext->double_buffer)
+       {
+               /* draw background to the left of the text */
+               if (str == ent->str && indent && xtext->time_stamp)
+               {
+                       /* don't overwrite the timestamp */
+                       if (indent > xtext->stamp_width)
+                       {
+                               if (!xtext->skip_border_fills)
+                                       gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 
+                                                                        xtext->stamp_width, y - xtext->font->ascent,
+                                                                        indent - xtext->stamp_width, xtext->fontsize);
+                       }
+               } else
+               {
+                       /* fill the indent area with background gc */
+                       if (!xtext->skip_border_fills)
+                               gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 
+                                                                0, y - xtext->font->ascent, indent, xtext->fontsize);
+               }
+       }
+
+       while (i < len)
+       {
+
+#ifdef MOTION_MONITOR
+               if (xtext->hilight_ent == ent && xtext->hilight_start == (i + offset))
+               {
+                       x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                       pstr += j;
+                       j = 0;
+                       xtext->underline = TRUE;
+/*         xtext->bold = TRUE;*/
+                       hilight = TRUE;
+               }
+#endif
+
+               if (!mark && ent->mark_start == (i + offset))
+               {
+                       x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                       pstr += j;
+                       j = 0;
+                       xtext_set_bg (gc, xtext->palette[16]);
+                       xtext_set_fg (gc, xtext->palette[17]);
+                       xtext->backcolor = TRUE;
+                       mark = TRUE;
+               }
+
+               if ((xtext->parsing_color && isdigit (str[i]) && xtext->nc < 2) ||
+                        (xtext->parsing_color && str[i] == ',' && xtext->nc < 3))
+               {
+                       pstr++;
+                       if (str[i] == ',')
+                       {
+                               xtext->parsing_backcolor = TRUE;
+                               if (xtext->nc)
+                               {
+                                       xtext->num[xtext->nc] = 0;
+                                       xtext->nc = 0;
+                                       col_num = atoi (xtext->num) % 16;
+                                       xtext->col_fore = col_num;
+                                       if (!mark)
+                                               xtext_set_fg (gc, xtext->palette[col_num]);
+                               }
+                       } else
+                       {
+                               xtext->num[xtext->nc] = str[i];
+                               if (xtext->nc < 7)
+                                       xtext->nc++;
+                       }
+               } else
+               {
+                       if (xtext->parsing_color)
+                       {
+                               xtext->parsing_color = FALSE;
+                               if (xtext->nc)
+                               {
+                                       xtext->num[xtext->nc] = 0;
+                                       xtext->nc = 0;
+                                       col_num = atoi (xtext->num);
+                                       if (col_num == 99)      /* mIRC lameness */
+                                               col_num = 19;
+                                       else
+                                               col_num = col_num % 16;
+                                       if (xtext->parsing_backcolor)
+                                       {
+                                               if (col_num == 1)
+                                                       xtext->backcolor = FALSE;
+                                               else
+                                                       xtext->backcolor = TRUE;
+                                               if (!mark)
+                                                       xtext_set_bg (gc, xtext->palette[col_num]);
+                                               xtext->col_back = col_num;
+                                       } else
+                                       {
+                                               if (!mark)
+                                                       xtext_set_fg (gc, xtext->palette[col_num]);
+                                               xtext->col_fore = col_num;
+                                       }
+                                       xtext->parsing_backcolor = FALSE;
+                               } else
+                               {
+                                       /* got a \003<non-digit>... i.e. reset colors */
+                                       x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                                       pstr += j;
+                                       j = 0;
+                                       gtk_xtext_reset (xtext, mark, FALSE);
+                               }
+                       }
+
+                       switch (str[i])
+                       {
+                       case '\t':
+                               str[i] = ' ';
+                               j++;
+                               break;
+                       case '\n':
+                       case ATTR_BEEP:
+                               break;
+                       case ATTR_REVERSE:
+                               x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                               pstr += j + 1;
+                               j = 0;
+                               tmp = xtext->col_fore;
+                               xtext->col_fore = xtext->col_back;
+                               xtext->col_back = tmp;
+                               if (!mark)
+                               {
+                                       xtext_set_fg (gc, xtext->palette[xtext->col_fore]);
+                                       xtext_set_bg (gc, xtext->palette[xtext->col_back]);
+                               }
+                               if (xtext->col_back != 19)
+                                       xtext->backcolor = TRUE;
+                               else
+                                       xtext->backcolor = FALSE;
+                               break;
+                       case ATTR_BOLD:
+                               x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                               xtext->bold = !xtext->bold;
+                               pstr += j + 1;
+                               j = 0;
+                               break;
+                       case ATTR_UNDERLINE:
+                               x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                               xtext->underline = !xtext->underline;
+                               pstr += j + 1;
+                               j = 0;
+                               break;
+                       case ATTR_RESET:
+                               x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                               pstr += j + 1;
+                               j = 0;
+                               gtk_xtext_reset (xtext, mark, !hilight);
+                               break;
+                       case ATTR_COLOR:
+                               x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                               xtext->parsing_color = TRUE;
+                               pstr += j + 1;
+                               j = 0;
+                               break;
+                       default:
+                               j++;
+                       }
+               }
+               i++;
+
+#ifdef MOTION_MONITOR
+               if (xtext->hilight_ent == ent && xtext->hilight_end == (i + offset))
+               {
+                       x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                       pstr += j;
+                       j = 0;
+                       xtext->underline = FALSE;
+/*         xtext->bold = FALSE;*/
+                       hilight = FALSE;
+               }
+#endif
+
+               if (mark && ent->mark_end == (i + offset))
+               {
+                       x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+                       pstr += j;
+                       j = 0;
+                       xtext_set_bg (gc, xtext->palette[xtext->col_back]);
+                       xtext_set_fg (gc, xtext->palette[xtext->col_fore]);
+                       if (xtext->col_back != 19)
+                               xtext->backcolor = TRUE;
+                       else
+                               xtext->backcolor = FALSE;
+                       mark = FALSE;
+               }
+       }
+
+       if (j)
+               x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc);
+
+       if (!xtext->double_buffer)
+       {
+               /* draw separator now so it doesn't appear to flicker */
+               gtk_xtext_draw_sep (xtext, y + 1);
+               /* draw background to the right of the text */
+               if (!xtext->skip_border_fills)
+                       gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 
+                                                        x, y - xtext->font->ascent, (win_width + MARGIN) - x,
+                                                        xtext->fontsize);
+       }
+}
+
+#ifdef USE_XLIB
+
+/* get the desktop/root window - thanks Eterm */
+
+static Window desktop_window = None;
+
+Window
+get_desktop_window (Window the_window)
+{
+       Atom prop, type, prop2;
+       int format;
+       unsigned long length, after;
+       unsigned char *data;
+       unsigned int nchildren;
+       Window w, root, *children, parent;
+
+       prop = XInternAtom (GDK_DISPLAY (), "_XROOTPMAP_ID", True);
+       prop2 = XInternAtom (GDK_DISPLAY (), "_XROOTCOLOR_PIXEL", True);
+
+       if (prop == None && prop2 == None)
+               return None;
+
+       for (w = the_window; w; w = parent)
+       {
+               if ((XQueryTree (GDK_DISPLAY (), w, &root, &parent, &children,
+                               &nchildren)) == False)
+                       return None;
+
+               if (nchildren)
+                       XFree (children);
+
+               if (prop != None)
+               {
+                       XGetWindowProperty (GDK_DISPLAY (), w, prop, 0L, 1L, False,
+                                                                         AnyPropertyType, &type, &format, &length, &after,
+                                                                         &data);
+               } else
+               {
+                       XGetWindowProperty (GDK_DISPLAY (), w, prop2, 0L, 1L, False,
+                                                                         AnyPropertyType, &type, &format, &length, &after,
+                                                                         &data);
+               }
+
+               if (data)
+                       XFree (data);
+
+               if (type != None)
+               {
+                       return (desktop_window = w);
+               }
+       }
+
+       return (desktop_window = None);
+}
+
+/* stolen from zvt, which was stolen from Eterm */
+
+static Pixmap
+get_pixmap_prop (Window the_window)
+{
+       Atom prop, type;
+       int format;
+       unsigned long length, after;
+       unsigned char *data;
+       Pixmap pix = None;
+
+       if (desktop_window == None)
+               desktop_window = get_desktop_window (the_window);
+       if (desktop_window == None)
+               desktop_window = GDK_ROOT_WINDOW ();
+
+       prop = XInternAtom (GDK_DISPLAY (), "_XROOTPMAP_ID", True);
+       if (prop == None)
+               return None;
+
+       XGetWindowProperty (GDK_DISPLAY (), desktop_window, prop, 0L, 1L, False,
+                                                         AnyPropertyType, &type, &format, &length, &after,
+                                                         &data);
+       if (data)
+       {
+               if (type == XA_PIXMAP)
+                       pix = *((Pixmap *) data);
+
+               XFree (data);
+       }
+
+       return pix;
+}
+
+#ifdef USE_GDK_PIXBUF
+
+static GdkPixmap *
+create_shaded_pixmap (GtkXText * xtext, Pixmap p, int x, int y, int w, int h)
+{
+       GdkPixmap *pp, *tmp, *shaded_pixmap;
+       GdkPixbuf *pixbuf;
+       GdkColormap *cmap;
+       GdkGC *tgc;
+       unsigned char *buf;
+       unsigned char *pbuf;
+       int width, height, depth;
+       int rowstride;
+       int pbwidth;
+       int pbheight;
+       int i, j;
+       int offset;
+       int r, g, b, a;
+
+       pp = gdk_pixmap_foreign_new (p);
+       cmap = gtk_widget_get_colormap (GTK_WIDGET (xtext));
+       gdk_window_get_geometry (pp, NULL, NULL, &width, &height, &depth);
+
+       if (width < x + w || height < y + h || x < 0 || y < 0)
+       {
+               tgc = gdk_gc_new (pp);
+               tmp = gdk_pixmap_new (pp, w, h, depth);
+               gdk_gc_set_tile (tgc, pp);
+               gdk_gc_set_fill (tgc, GDK_TILED);
+               gdk_gc_set_ts_origin (tgc, -x, -y);
+               gdk_draw_rectangle (tmp, tgc, TRUE, 0, 0, w, h);
+               gdk_gc_destroy (tgc);
+
+               pixbuf = gdk_pixbuf_get_from_drawable (NULL, tmp, cmap,
+                                                                                                                       0, 0, 0, 0, w, h);
+               gdk_pixmap_unref (tmp);
+       } else
+       {
+               pixbuf = gdk_pixbuf_get_from_drawable (NULL, pp, cmap,
+                                                                                                                       x, y, 0, 0, w, h);
+       }
+       gdk_xid_table_remove (GDK_WINDOW_XWINDOW (pp));
+       g_dataset_destroy (pp);
+       g_free (pp);
+
+       if (!pixbuf)
+               return NULL;
+
+       buf = gdk_pixbuf_get_pixels (pixbuf);
+       rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+       pbwidth = gdk_pixbuf_get_width (pixbuf);
+       pbheight = gdk_pixbuf_get_height (pixbuf);
+
+       a = 128;        /* alpha */
+       r = xtext->tint_red;
+       g = xtext->tint_green;
+       b = xtext->tint_blue;
+
+       if (gdk_pixbuf_get_has_alpha (pixbuf))
+               offset = 4;
+       else
+               offset = 3;
+
+       for (i=0;i<pbheight;i++)
+       {
+               pbuf = buf;
+               for (j=0;j<pbwidth;j++)
+               {
+                       pbuf[0] = ((pbuf[0] * r) >> 8);
+                       pbuf[1] = ((pbuf[1] * g) >> 8);
+                       pbuf[2] = ((pbuf[2] * b) >> 8);
+                       pbuf+=offset;
+               }
+               buf+=rowstride;
+       }
+
+       gdk_pixbuf_render_pixmap_and_mask (pixbuf, &shaded_pixmap, NULL, 0);
+       gdk_pixbuf_unref (pixbuf);
+
+       return shaded_pixmap;
+}
+
+#endif
+
+/* free transparency xtext->pixmap */
+
+static void
+gtk_xtext_free_trans (GtkXText * xtext)
+{
+       if (xtext->pixmap)
+       {
+               if (xtext->shaded)
+               {
+                       gdk_pixmap_unref (xtext->pixmap);
+               } else
+               {
+                       gdk_xid_table_remove (GDK_WINDOW_XWINDOW (xtext->pixmap));
+                       g_dataset_destroy (xtext->pixmap);
+                       g_free (xtext->pixmap);
+               }
+               xtext->pixmap = NULL;
+       }
+}
+
+/* grab pixmap from root window and set xtext->pixmap */
+
+static void
+gtk_xtext_load_trans (GtkXText * xtext)
+{
+       Pixmap rootpix;
+       Window childret;
+       GtkWidget *widget = GTK_WIDGET (xtext);
+       int x, y;
+
+       rootpix = get_pixmap_prop (GDK_WINDOW_XWINDOW (widget->window));
+       if (rootpix == None)
+       {
+               if (xtext->error_function)
+                       xtext->error_function ("Unable to get root window pixmap!\n\n"
+                                                                                 "You may need to use Esetroot or Gnome\n"
+                                                                                 "control-center to set your background.\n");
+               xtext->transparent = FALSE;
+               return;
+       }
+
+       XTranslateCoordinates (GDK_WINDOW_XDISPLAY (widget->window),
+                                                                 GDK_WINDOW_XWINDOW (widget->window),
+                                                                 GDK_ROOT_WINDOW (), 0, 0, &x, &y, &childret);
+
+#ifdef USE_GDK_PIXBUF
+       if (xtext->shaded)
+       {
+               int width, height;
+               gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+               xtext->pixmap =
+                       create_shaded_pixmap (xtext, rootpix, x, y, width, height);
+               gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+               gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+       } else
+       {
+#endif
+               xtext->pixmap = gdk_pixmap_foreign_new (rootpix);
+               gdk_gc_set_tile (xtext->bgc, xtext->pixmap);
+               gdk_gc_set_ts_origin (xtext->bgc, -x, -y);
+#ifdef USE_GDK_PIXBUF
+       }
+#endif
+       gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+}
+
+#endif
+
+/* render a single line, which may wrap to more lines */
+
+static int
+gtk_xtext_render_line (GtkXText * xtext, textentry * ent, char *str, int len,
+                                                         int line, int lines_max, int subline, int indent,
+                                                         int str_width)
+{
+       char *time_str;
+       int y;
+       int width;
+       int orig_len;
+       int ret = 1;
+       int tmp;
+
+       if (!str)
+               return 0;
+
+       if (len == -1)
+               len = strlen (str);
+       orig_len = len;
+
+       gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, 0);
+       width -= MARGIN;
+
+       if (xtext->time_stamp)
+       {
+               time_str = ctime (&ent->stamp) + 10;
+               time_str[0] = '[';
+               time_str[9] = ']';
+               time_str[10] = 0;
+               y = (xtext->fontsize * line) + xtext->font->ascent;
+               gtk_xtext_render_str (xtext, y, ent, time_str, 10, width, 2, line);
+       }
+
+ startrl:
+
+       y = (xtext->fontsize * line) + xtext->font->ascent;
+
+       if (str_width == -1)
+               str_width = gtk_xtext_text_width (xtext, str, len);
+
+       str_width += indent;
+
+       tmp = 0;
+       while (str_width > width || (!is_del (str[len]) && xtext->wordwrap))
+       {
+               if (str_width <= width && !tmp)
+                       tmp = len;
+               len--;
+               if (xtext->wordwrap && tmp - len > WORDWRAP_LIMIT)
+               {
+                       len = tmp;
+                       str_width = gtk_xtext_text_width (xtext, str, len) + indent;
+                       break;
+               }
+               if (len == 0)
+                       return 1;
+
+               /* this is quite a HACK but it speeds things up! */
+               if (str_width > width + 256)
+                       len -= 10;
+               /* -- */
+
+               str_width = gtk_xtext_text_width (xtext, str, len) + indent;
+       }
+
+       if (!subline)
+       {
+               gtk_xtext_render_str (xtext, y, ent, str, len, width, indent, line);
+       } else
+       {
+               xtext->dont_render = TRUE;
+               gtk_xtext_render_str (xtext, y, ent, str, len, width, indent, line);
+               xtext->dont_render = FALSE;
+               subline--;
+               line--;
+               ret--;
+       }
+
+       if (xtext->wordwrap && str[len] == ' ')
+               len++;
+
+       if (len != orig_len && lines_max > line + 1)
+       {                                                                         /* FIXME: recursion sux! */
+/*      ret += gtk_xtext_render_line (xtext, ent, str + len, -1, line+1, lines_max, subline, xtext->indent, -1);*/
+               ret++;
+               str += len;
+               len = orig_len = strlen (str);
+               line++;
+               indent = xtext->indent;
+               str_width = -1;
+               /* FIXME: gotos suck! */
+               goto startrl;
+       }
+
+       return ret;
+}
+
+void
+gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[])
+{
+       int i;
+
+       for (i = 0; i < 20; i++)
+               xtext->palette[i] = palette[i].pixel;
+
+       if (GTK_WIDGET_REALIZED (xtext))
+       {
+               xtext_set_fg (xtext->fgc, xtext->palette[18]);
+               xtext_set_bg (xtext->fgc, xtext->palette[19]);
+               xtext_set_fg (xtext->bgc, xtext->palette[19]);
+       }
+       xtext->col_fore = 18;
+       xtext->col_back = 19;
+}
+
+static void
+gtk_xtext_fix_indent (GtkXText * xtext)
+{
+       int j;
+
+       if (xtext->indent)                        /* make indent a multiple of the space width */
+       {
+               j = 0;
+               while (j < xtext->indent)
+               {
+                       j += xtext->space_width;
+               }
+               xtext->indent = j;
+       }
+}
+
+static void
+gtk_xtext_recalc_widths (GtkXText * xtext, int do_str_width)
+{
+       textentry *ent;
+
+       /* since we have a new font, we have to recalc the text widths */
+       ent = xtext->text_first;
+       while (ent)
+       {
+               if (do_str_width)
+               {
+                       ent->str_width =
+                               gtk_xtext_text_width (xtext, ent->str, ent->str_len);
+               }
+               if (ent->left_len != -1)
+               {
+                       ent->indent =
+                               (xtext->indent -
+                                gtk_xtext_text_width (xtext, ent->str,
+                                                                                         ent->left_len)) - xtext->space_width;
+                       if (ent->indent < MARGIN)
+                               ent->indent = MARGIN;
+               }
+               ent = ent->next;
+       }
+
+       gtk_xtext_calc_lines (xtext, FALSE);
+}
+
+void
+gtk_xtext_set_font (GtkXText * xtext, GdkFont * font, char *name)
+{
+#ifdef USE_XLIB
+       unsigned char i;
+       XFontStruct *xfont;
+#endif
+
+       if (xtext->font)
+               gdk_font_unref (xtext->font);
+
+       if (font)
+       {
+               xtext->font = font;
+               gdk_font_ref (font);
+       } else
+               font = xtext->font = gdk_font_load (name);
+
+       if (!font)
+               font = xtext->font = gdk_font_load ("fixed");
+
+       switch (font->type)
+       {
+       case GDK_FONT_FONT:
+                       xtext->fontsize = font->ascent + font->descent;
+#ifdef USE_XLIB
+                       xfont = GDK_FONT_XFONT (font);
+                       if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
+                       {
+                               xtext->fonttype = FONT_1BYTE;
+                               for (i = 0; i < 255; i++)
+                               {
+                                       xtext->fontwidth[i] = gdk_char_width (font, i);
+                               }
+                               xtext->space_width = xtext->fontwidth[' '];
+                       } else
+                       {
+#endif
+                               /* without X11 pretend they are all 2BYTE- This is ok, just
+                                       a bit slower. */
+                               xtext->fonttype = FONT_2BYTE;
+                               xtext->space_width = gdk_char_width (font, ' ');
+#ifdef USE_XLIB
+                       }
+#endif
+                       break;
+
+       case GDK_FONT_FONTSET:
+                       xtext->fontsize = gdk_text_height (font, " ", 1);
+                       xtext->fonttype = FONT_SET;
+                       xtext->space_width = gdk_char_width (font, ' ');
+                       break;
+       }
+
+#ifdef USE_XLIB
+       xfont = GDK_FONT_XFONT (font);
+       /* check if it's a fixed width font */
+       if (xfont->min_bounds.width == xfont->max_bounds.width)
+               xtext->fixed_width_font = TRUE;
+       else
+               xtext->fixed_width_font = FALSE;
+#else
+       /* kudgy fixed-width font checking */
+       if (xtext->space_width == gdk_char_width (xtext->font, 'Z'))
+               xtext->fixed_width_font = TRUE;
+       else
+               xtext->fixed_width_font = FALSE;
+#endif
+
+       xtext->stamp_width =
+               gtk_xtext_text_width (xtext, "[88:88:88]", 10) + MARGIN;
+
+       gtk_xtext_fix_indent (xtext);
+
+       if (GTK_WIDGET_REALIZED (xtext))
+       {
+               if (xtext->fonttype != FONT_SET)
+                       gdk_gc_set_font (xtext->fgc, xtext->font);
+
+               gtk_xtext_recalc_widths (xtext, TRUE);
+       }
+}
+
+void
+gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap, int trans,
+                                                                 int shaded)
+{
+       GdkGCValues val;
+
+#ifndef USE_GDK_PIXBUF
+       shaded = FALSE;
+#endif
+
+#ifndef USE_XLIB
+       shaded = FALSE;
+       trans = FALSE;
+#endif
+
+       if (xtext->pixmap)
+       {
+#ifdef USE_XLIB
+               if (xtext->transparent)
+                       gtk_xtext_free_trans (xtext);
+               else
+#endif
+                       gdk_pixmap_unref (xtext->pixmap);
+               xtext->pixmap = NULL;
+       }
+
+       xtext->transparent = trans;
+
+#ifdef USE_XLIB
+       if (trans)
+       {
+               xtext->shaded = shaded;
+               if (GTK_WIDGET_REALIZED (xtext))
+                       gtk_xtext_load_trans (xtext);
+               return;
+       }
+#endif
+
+       xtext->pixmap = pixmap;
+
+       if (pixmap != 0)
+       {
+               gdk_pixmap_ref (pixmap);
+               if (GTK_WIDGET_REALIZED (xtext))
+               {
+                       gdk_gc_set_tile (xtext->bgc, pixmap);
+                       gdk_gc_set_ts_origin (xtext->bgc, 0, 0);
+                       gdk_gc_set_fill (xtext->bgc, GDK_TILED);
+               }
+       } else
+       {
+               if (GTK_WIDGET_REALIZED (xtext))
+               {
+                       gdk_gc_destroy (xtext->bgc);
+                       val.subwindow_mode = GDK_INCLUDE_INFERIORS;
+                       val.graphics_exposures = 0;
+                       xtext->bgc = gdk_gc_new_with_values (GTK_WIDGET (xtext)->window,
+                                                                       &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW);
+                       xtext_set_fg (xtext->bgc, xtext->palette[19]);
+               }
+       }
+}
+
+gchar *
+gtk_xtext_get_chars (GtkXText * xtext)
+{
+       int lenght = 0;
+       gchar *chars;
+       textentry *tentry = xtext->text_first;
+       while (tentry != NULL)
+       {
+               lenght += tentry->str_len + 1;
+               tentry = tentry->next;
+       }
+       if (lenght == 0)
+               return NULL;
+       chars = g_malloc (lenght + 1);
+       *chars = 0;
+
+       tentry = xtext->text_first;
+       while (tentry != NULL)
+       {
+               strcat (chars, tentry->str);
+               strcat (chars, "\n");
+               tentry = tentry->next;
+       }
+
+       return chars;
+}
+
+static int
+gtk_xtext_lines_taken (GtkXText * xtext, textentry * ent)
+{
+       int tmp, orig_len, indent, len, win_width, str_width, lines = 0;
+       char *str;
+
+       str = ent->str;
+       len = orig_len = ent->str_len;
+       indent = ent->indent;
+
+       if (len < 2)
+               return 1;
+
+       win_width = GTK_WIDGET (xtext)->allocation.width - MARGIN;
+       str_width = ent->str_width + indent;
+
+       while (1)
+       {
+               lines++;
+               if (str_width <= win_width)
+                       break;
+               tmp = 0;
+               while (str_width > win_width || (!is_del (str[len]) && xtext->wordwrap))
+               {
+                       if (str_width <= win_width && !tmp)
+                               tmp = len;
+                       len--;
+                       if (xtext->wordwrap && tmp - len > WORDWRAP_LIMIT)
+                       {
+                               len = tmp;
+                               str_width = gtk_xtext_text_width (xtext, str, len) + indent;
+                               break;
+                       }
+                       if (len == 0)
+                               return 1;
+                       if (str_width > win_width + 256)        /* this might not work 100% but it */
+                               len -= 10;                        /* sure speeds things up ALOT!     */
+                       str_width = gtk_xtext_text_width (xtext, str, len) + indent;
+               }
+
+               if (len == orig_len)
+                       break;
+
+               if (xtext->wordwrap && str[len] == ' ')
+                       len++;
+
+               str += len;
+               indent = xtext->indent;
+               len = strlen (str);
+               str_width = gtk_xtext_text_width (xtext, str, len) + indent;
+       }
+       return lines;
+}
+
+/* Calculate number of actual lines (with wraps), to set adj->lower. *
+ * This should only be called when the window resizes.               */
+
+static void
+gtk_xtext_calc_lines (GtkXText * xtext, int fire_signal)
+{
+       textentry *ent;
+       int width;
+       int height;
+       int lines;
+
+       width = GTK_WIDGET (xtext)->allocation.width - MARGIN;
+       height = GTK_WIDGET (xtext)->allocation.height;
+
+       if (width < 30 || height < xtext->fontsize || width < xtext->indent + 30)
+               return;
+
+       lines = 0;
+       ent = xtext->text_first;
+       while (ent)
+       {
+               ent->lines_taken = gtk_xtext_lines_taken (xtext, ent);
+               lines += ent->lines_taken;
+               ent = ent->next;
+       }
+
+       xtext->pagetop_ent = NULL;
+       xtext->num_lines = lines;
+       gtk_xtext_adjustment_set (xtext, fire_signal);
+}
+
+/* find the n-th line in the linked list, this includes wrap calculations */
+
+static textentry *
+gtk_xtext_nth (GtkXText * xtext, textentry * ent, int line, int width,
+                                       int *subline)
+{
+       int lines = 0;
+
+       if (ent == NULL)
+       {
+               ent = xtext->text_first;
+               line += xtext->adj->value;
+       } else
+       {
+               lines -= *subline;
+       }
+
+       while (ent)
+       {
+               lines += ent->lines_taken;
+               if (lines > line)
+               {
+                       *subline = ent->lines_taken - (lines - line);
+                       return ent;
+               }
+               ent = ent->next;
+       }
+       return 0;
+}
+
+static void
+gtk_xtext_draw_sep (GtkXText * xtext, int y)
+{
+       int x, height;
+       GdkGC *light, *dark;
+
+       if (y == -1)
+       {
+               y = 2;
+               height = GTK_WIDGET (xtext)->allocation.height - 2;
+       } else
+       {
+               height = xtext->fontsize;
+       }
+
+       /* draw the separator line */
+       if (xtext->separator && xtext->indent)
+       {
+               light = xtext->light_gc;
+               dark = xtext->dark_gc;
+
+               x = xtext->indent - ((xtext->space_width + 1) / 2);
+               if (x < 1)
+                       return;
+
+               if (xtext->thinline)
+               {
+                       if (xtext->moving_separator)
+                               gdk_draw_line (xtext->draw_buf, light, x, y, x, height);
+                       else
+                               gdk_draw_line (xtext->draw_buf, dark, x, y, x, height);
+               } else
+               {
+                       if (xtext->moving_separator)
+                       {
+                               gdk_draw_line (xtext->draw_buf, light, x - 1, y, x - 1, height);
+                               gdk_draw_line (xtext->draw_buf, dark, x, y, x, height);
+                       } else
+                       {
+                               gdk_draw_line (xtext->draw_buf, dark, x - 1, y, x - 1, height);
+                               gdk_draw_line (xtext->draw_buf, light, x, y, x, height);
+                       }
+               }
+       }
+}
+
+/* render 2 ents (or an inclusive range) */
+
+static void
+gtk_xtext_render_ents (GtkXText * xtext, textentry * enta, textentry * entb,
+                                                         int inclusive)
+{
+       textentry *ent, *orig_ent, *tmp_ent;
+       int line;
+       int lines_taken;
+       int lines_max;
+       int width;
+       int height;
+       int subline;
+       int drawing = FALSE;
+
+       if (xtext->double_buffer)
+       {
+               gtk_xtext_render_page (xtext);
+               return;
+       }
+
+       if (xtext->indent < MARGIN)
+               xtext->indent = MARGIN;   /* 2 pixels is our left margin */
+
+       gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+       width -= MARGIN;
+
+       if (width < 32 || height < xtext->fontsize || width < xtext->indent + 30)
+               return;
+
+       lines_max = (height - xtext->font->descent) / xtext->fontsize;
+       line = 0;
+       orig_ent = xtext->pagetop_ent;
+       subline = xtext->pagetop_subline;
+
+       /* check if enta is before the start of this page */
+       if (inclusive)
+       {
+               tmp_ent = orig_ent;
+               while (tmp_ent)
+               {
+                       if (tmp_ent == enta)
+                               break;
+                       if (tmp_ent == entb)
+                       {
+                               drawing = TRUE;
+                               break;
+                       }
+                       tmp_ent = tmp_ent->next;
+               }
+       }
+
+       line = 0;
+
+       ent = orig_ent;
+       while (ent)
+       {
+               if (inclusive && ent == enta)
+                       drawing = TRUE;
+
+               if (drawing || ent == entb || ent == enta)
+               {
+                       gtk_xtext_reset (xtext, FALSE, TRUE);
+                       lines_taken = gtk_xtext_render_line (xtext, ent, ent->str,
+                                                                                                                        ent->str_len, line, lines_max,
+                                                                                                                        subline, ent->indent,
+                                                                                                                        ent->str_width);
+                       line += ent->lines_taken;
+                       line -= subline;
+                       if (ent == orig_ent)
+                               subline = 0;
+               } else
+               {
+                       if (ent == orig_ent)
+                       {
+                               line -= subline;
+                               subline = 0;
+                       }
+                       line += ent->lines_taken;
+               }
+
+               if (inclusive && ent == entb)
+                       break;
+
+               if (line >= lines_max)
+                       break;
+
+               ent = ent->next;
+       }
+
+       /* draw the separator line */
+       gtk_xtext_draw_sep (xtext, -1);
+}
+
+/* render a whole page/window, starting from 'startline' */
+
+static void
+gtk_xtext_render_page (GtkXText * xtext)
+{
+       textentry *ent;
+       int line;
+       int lines_max;
+       int width;
+       int height;
+       int subline;
+       int startline = xtext->adj->value;
+
+       if (xtext->indent < MARGIN)
+               xtext->indent = MARGIN;   /* 2 pixels is our left margin */
+
+       gdk_window_get_size (GTK_WIDGET (xtext)->window, &width, &height);
+       width -= MARGIN;
+
+       if (width < 32 || height < xtext->fontsize || width < xtext->indent + 30)
+               return;
+
+       lines_max = (height - xtext->font->descent) / xtext->fontsize;
+
+       subline = line = 0;
+       ent = xtext->text_first;
+
+       if (startline > 0)
+               ent = gtk_xtext_nth (xtext, ent, startline, width, &subline);
+
+       xtext->pagetop_ent = ent;
+       xtext->pagetop_subline = subline;
+
+       if (xtext->double_buffer)
+       {
+               xtext->tmp_pix = gdk_pixmap_new (((GtkWidget*)xtext)->window,
+                                                                                               width + MARGIN, height, xtext->depth);
+               xtext->draw_buf = xtext->tmp_pix;
+               /* render the backdrop */
+               gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1, 0, 0,
+                                                                       width + MARGIN, height);
+       }
+
+       while (ent)
+       {
+               gtk_xtext_reset (xtext, FALSE, TRUE);
+               line +=
+                       gtk_xtext_render_line (xtext, ent, ent->str, ent->str_len, line,
+                                                                                 lines_max, subline, ent->indent,
+                                                                                 ent->str_width);
+               subline = 0;
+
+               if (line >= lines_max)
+                       break;
+
+               ent = ent->next;
+       }
+
+       if (!xtext->double_buffer)
+       {
+               line = (xtext->fontsize * line);
+               /* fill any space below the last line with our background GC */
+               gdk_draw_rectangle (xtext->draw_buf, xtext->bgc, 1,
+                                                        0, line, width + MARGIN, height - line);
+       }
+
+       /* draw the separator line */
+       gtk_xtext_draw_sep (xtext, -1);
+
+       if (xtext->double_buffer)
+       {
+               /* send our double buffer to the actual window */
+               gdk_draw_pixmap (((GtkWidget*)xtext)->window, xtext->fgc, xtext->tmp_pix,
+                                                               0, 0, 0, 0, width + MARGIN, height);
+               gdk_pixmap_unref (xtext->tmp_pix);
+       }
+}
+
+void
+gtk_xtext_refresh (GtkXText * xtext, int do_trans)
+{
+       if (GTK_WIDGET_REALIZED (GTK_WIDGET (xtext)))
+       {
+#ifdef USE_XLIB
+               if (xtext->transparent && do_trans)
+               {
+                       gtk_xtext_free_trans (xtext);
+                       gtk_xtext_load_trans (xtext);
+               }
+#endif
+               gtk_xtext_render_page (xtext);
+       }
+}
+
+/* remove the topline from the list */
+
+static void
+gtk_xtext_remove_top (GtkXText * xtext)
+{
+       textentry *ent;
+
+       ent = xtext->text_first;
+       xtext->num_lines -= ent->lines_taken;
+       xtext->pagetop_ent = NULL;
+       xtext->text_first = ent->next;
+       free (ent);
+}
+
+void
+gtk_xtext_remove_lines (GtkXText * xtext, int lines, int refresh)
+{
+       textentry *next;
+
+       while (xtext->text_first && lines)
+       {
+               next = xtext->text_first->next;
+               free (xtext->text_first);
+               xtext->text_first = next;
+               lines--;
+       }
+       if (!xtext->text_first)
+               xtext->text_last = NULL;
+
+       if (refresh)
+       {
+               gtk_xtext_calc_lines (xtext, TRUE);
+               gtk_xtext_refresh (xtext, 0);
+       }
+}
+
+void *
+gtk_xtext_search (GtkXText * xtext, char *text, void *start)
+{
+       textentry *ent, *fent;
+       char *str;
+       int line;
+
+       gtk_xtext_selection_clear (xtext);
+
+       if (start)
+               ent = ((textentry *) start)->next;
+       else
+               ent = xtext->text_first;
+       while (ent)
+       {
+               if ((str = nocasestrstr (ent->str, text)))
+               {
+                       ent->mark_start = str - ent->str;
+                       ent->mark_end = ent->mark_start + strlen (text);
+                       break;
+               }
+               ent = ent->next;
+       }
+
+       fent = ent;
+       ent = xtext->text_first;
+       line = 0;
+       while (ent)
+       {
+               line += ent->lines_taken;
+               ent = ent->next;
+               if (ent == fent)
+                       break;
+       }
+       while (line > xtext->adj->upper - xtext->adj->page_size)
+               line--;
+
+       if (fent != 0)
+       {
+               xtext->adj->value = line;
+               xtext->scrollbar_down = FALSE;
+               gtk_adjustment_changed (xtext->adj);
+       }
+       gtk_xtext_render_page (xtext);
+
+       return fent;
+}
+
+static int
+gtk_xtext_render_page_timeout (GtkXText * xtext)
+{
+       GtkAdjustment *adj = xtext->adj;
+       gfloat val;
+
+       if (xtext->scrollbar_down)
+       {
+               gtk_xtext_adjustment_set (xtext, FALSE);
+               gtk_adjustment_set_value (adj, adj->upper - adj->page_size);
+       } else
+       {
+               val = adj->value;
+               gtk_xtext_adjustment_set (xtext, TRUE);
+               gtk_adjustment_set_value (adj, val);
+       }
+
+       if (adj->value >= adj->upper - adj->page_size || adj->value < 1)
+               gtk_xtext_render_page (xtext);
+
+       xtext->add_io_tag = -1;
+
+       return 0;
+}
+
+/* append a textentry to our linked list */
+
+static void
+gtk_xtext_append_entry (GtkXText * xtext, textentry * ent)
+{
+       ent->stamp = time (0);
+       ent->str_width = gtk_xtext_text_width (xtext, ent->str, ent->str_len);
+       ent->mark_start = -1;
+       ent->mark_end = -1;
+       ent->next = NULL;
+
+       if (ent->indent < MARGIN)
+               ent->indent = MARGIN;     /* 2 pixels is the left margin */
+
+       /* append to our linked list */
+       if (xtext->text_last)
+               xtext->text_last->next = ent;
+       else
+               xtext->text_first = ent;
+       xtext->text_last = ent;
+
+       ent->lines_taken = gtk_xtext_lines_taken (xtext, ent);
+       xtext->num_lines += ent->lines_taken;
+
+       if (xtext->max_lines > 2 && xtext->max_lines < xtext->num_lines)
+       {
+               gtk_xtext_remove_top (xtext);
+       }
+
+/*   if (xtext->frozen == 0 && xtext->add_io_tag == -1)*/
+       if (xtext->add_io_tag == -1)
+       {
+               xtext->add_io_tag = gtk_timeout_add (REFRESH_TIMEOUT * 2,
+                                                                                                                (GtkFunction)
+                                                                                                                gtk_xtext_render_page_timeout,
+                                                                                                                xtext);
+       }
+}
+
+/* the main two public functions */
+
+void
+gtk_xtext_append_indent (GtkXText * xtext,
+                                                                char *left_text, int left_len,
+                                                                char *right_text, int right_len)
+{
+       textentry *ent;
+       char *str;
+       int space;
+
+       if (left_len == -1)
+               left_len = strlen (left_text);
+
+       if (right_len == -1)
+               right_len = strlen (right_text);
+
+       ent = malloc (left_len + right_len + 2 + sizeof (textentry));
+       str = (char *) ent + sizeof (textentry);
+
+       space = xtext->indent - gtk_xtext_text_width (xtext, left_text, left_len);
+
+       memcpy (str, left_text, left_len);
+       str[left_len] = ' ';
+       memcpy (str + left_len + 1, right_text, right_len);
+       str[left_len + 1 + right_len] = 0;
+
+       ent->left_len = left_len;
+       ent->str = str;
+       ent->str_len = left_len + 1 + right_len;
+       ent->indent = space - xtext->space_width;
+
+       if (xtext->time_stamp)
+               space = xtext->stamp_width;
+       else
+               space = 0;
+
+       /* do we need to auto adjust the separator position? */
+       if (xtext->auto_indent && ent->indent < MARGIN + space)
+       {
+               xtext->indent -= ent->indent;
+               xtext->indent += MARGIN;
+               xtext->indent += space;
+               if (xtext->indent > xtext->max_auto_indent)
+                       xtext->indent = xtext->max_auto_indent;
+               gtk_xtext_fix_indent (xtext);
+               gtk_xtext_recalc_widths (xtext, FALSE);
+               space =
+                       xtext->indent - gtk_xtext_text_width (xtext, left_text, left_len);
+               ent->indent = space - xtext->space_width;
+       }
+
+       gtk_xtext_append_entry (xtext, ent);
+}
+
+void
+gtk_xtext_append (GtkXText * xtext, char *text, int len)
+{
+       textentry *ent;
+
+       if (len == -1)
+               len = strlen (text);
+
+       ent = malloc (len + 1 + sizeof (textentry));
+       ent->str = (char *) ent + sizeof (textentry);
+       ent->str_len = len;
+       if (len)
+               memcpy (ent->str, text, len);
+       ent->str[len] = 0;
+       ent->indent = 0;
+       ent->left_len = -1;
+
+       gtk_xtext_append_entry (xtext, ent);
+}
diff --git a/apps/silcer/src/xtext.h b/apps/silcer/src/xtext.h
new file mode 100644 (file)
index 0000000..d1b995a
--- /dev/null
@@ -0,0 +1,182 @@
+#ifndef __XTEXT_H__
+#define __XTEXT_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+#include <time.h>
+
+/*#define GTK_XTEXT(obj)          GTK_CHECK_CAST (obj, gtk_xtext_get_type (), GtkXText)*/
+#define GTK_XTEXT(obj) ((GtkXText*)obj)
+#define GTK_XTEXT_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, gtk_xtext_get_type (), GtkXTextClass)
+#define GTK_IS_XTEXT(obj)       GTK_CHECK_TYPE (obj, gtk_xtext_get_type ())
+
+#define FONT_1BYTE 0
+#define FONT_2BYTE 1
+#define FONT_SET 2
+
+#define ATTR_BOLD '\002'
+#define ATTR_COLOR '\003'
+#define ATTR_BEEP '\007'
+#define ATTR_RESET '\017'
+#define ATTR_REVERSE '\026'
+#define ATTR_ESCAPE '\033'
+#define ATTR_UNDERLINE '\037'
+
+typedef struct _GtkXText GtkXText;
+typedef struct _GtkXTextClass GtkXTextClass;
+
+typedef struct textentry
+{
+       struct textentry *next;
+       char *str;
+       int str_width;
+       time_t stamp;
+       short str_len;
+       short mark_start;
+       short mark_end;
+       short indent;
+       short lines_taken;
+       short left_len;
+}
+textentry;
+
+struct _GtkXText
+{
+       GtkWidget widget;
+
+       GtkAdjustment *adj;
+       gfloat old_value;                                       /* last known adj->value */
+       GdkPixmap *pixmap;                              /* 0 = use palette[19] */
+       GdkDrawable *draw_buf;                  /* points to ->window or ->tmp_pix */
+       GdkPixmap *tmp_pix;                             /* double buffer */
+       GdkCursor *hand_cursor;
+
+       int ts_orig_x;
+       int ts_orig_y;
+
+       int last_win_x;
+       int last_win_y;
+       int last_win_h;
+       int last_win_w;
+
+       int tint_red;
+       int tint_green;
+       int tint_blue;
+
+       GdkGC *bgc;                                               /* backing pixmap */
+       GdkGC *fgc;                                               /* text foreground color */
+       GdkGC *light_gc;                                  /* sep bar */
+       GdkGC *dark_gc;
+       gulong palette[20];
+
+       textentry *text_first;
+       textentry *text_last;
+
+       gint io_tag;                                      /* for delayed refresh events */
+       gint add_io_tag;                                  /* "" when adding new text */
+       gint scroll_tag;                                  /* marking-scroll timeout */
+
+       GdkFont *font;
+       int fontsize;
+       int fonttype;
+       guint16 fontwidth[256];           /* each char's width, only for FONT_1BYTE type */
+       int space_width;                                  /* width (pixels) of the space " " character */
+       int stamp_width;                                  /* width of "[88:88:88]" */
+
+       int indent;                                               /* position of separator (pixels) from left */
+       int max_auto_indent;
+
+       int select_start_adj;             /* the adj->value when the selection started */
+       int select_start_x;
+       int select_start_y;
+       int select_end_x;
+       int select_end_y;
+
+       textentry *last_ent_start;        /* this basically describes the last rendered */
+       textentry *last_ent_end;          /* selection. */
+       int last_offset_start;
+       int last_offset_end;
+
+       textentry *old_ent_start;
+       textentry *old_ent_end;
+
+       int num_lines;
+       int max_lines;
+
+       int pagetop_subline;
+       textentry *pagetop_ent;                 /* what's at xtext->adj->value */
+
+       int col_fore;
+       int col_back;
+
+       int depth;                                                /* gdk window depth */
+
+/*   int frozen;*/
+
+       char num[8];                                      /* for parsing mirc color */
+       int nc;                                                   /* offset into xtext->num */
+
+       textentry *hilight_ent;
+       int hilight_start;
+       int hilight_end;
+
+       short grid_offset[256];
+
+       GtkWidget *(*error_function) (char *text);
+       int (*urlcheck_function) (GtkXText * xtext, char *word);
+
+       unsigned char scratch_buffer[4096];
+
+       unsigned int fixed_width_font:1;
+       unsigned int double_buffer:1;
+       unsigned int auto_indent:1;
+       unsigned int moving_separator:1;
+       unsigned int time_stamp:1;
+       unsigned int scrollbar_down:1;
+       unsigned int word_or_line_select:1;
+       unsigned int color_paste:1;
+       unsigned int thinline:1;
+       unsigned int parsing_backcolor:1;
+       unsigned int parsing_color:1;
+       unsigned int backcolor:1;
+       unsigned int button_down:1;
+       unsigned int bold:1;
+       unsigned int underline:1;
+       unsigned int reverse:1;
+       unsigned int transparent:1;
+       unsigned int separator:1;
+       unsigned int shaded:1;
+       unsigned int wordwrap:1;
+       unsigned int dont_render:1;
+       unsigned int cursor_hand:1;
+       unsigned int skip_fills:1;
+       unsigned int skip_border_fills:1;
+       unsigned int do_underline_fills_only:1;
+};
+
+struct _GtkXTextClass
+{
+       GtkWidgetClass parent_class;
+       void (*word_click) (GtkXText * xtext, char *word, GdkEventButton * event);
+};
+
+GtkWidget *gtk_xtext_new (int indent, int separator);
+guint gtk_xtext_get_type (void);
+void gtk_xtext_append (GtkXText * xtext, char *text, int len);
+void gtk_xtext_append_indent (GtkXText * xtext,
+                                                                               char *left_text, int left_len,
+                                                                               char *right_text, int right_len);
+void gtk_xtext_set_font (GtkXText * xtext, GdkFont * font, char *name);
+void gtk_xtext_set_background (GtkXText * xtext, GdkPixmap * pixmap,
+                                                                                int trans, int shaded);
+void gtk_xtext_set_palette (GtkXText * xtext, GdkColor palette[]);
+void gtk_xtext_remove_lines (GtkXText * xtext, int lines, int refresh);
+gchar *gtk_xtext_get_chars (GtkXText * xtext);
+void gtk_xtext_refresh (GtkXText * xtext, int do_trans);
+void gtk_xtext_thaw (GtkXText * xtext);
+void gtk_xtext_freeze (GtkXText * xtext);
+void *gtk_xtext_search (GtkXText * xtext, char *text, void *start);
+char *gtk_xtext_strip_color (unsigned char *text, int len, char *outbuf, int *newlen);
+
+#endif
diff --git a/apps/silcer/stamp-h.in b/apps/silcer/stamp-h.in
new file mode 100644 (file)
index 0000000..9788f70
--- /dev/null
@@ -0,0 +1 @@
+timestamp
diff --git a/apps/silcer/ui/SilcerConfirmPkDlg.glade b/apps/silcer/ui/SilcerConfirmPkDlg.glade
new file mode 100644 (file)
index 0000000..21fdd95
--- /dev/null
@@ -0,0 +1,136 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>Gabber</name>
+  <program_name>gabber</program_name>
+  <directory></directory>
+  <source_directory>src</source_directory>
+  <pixmaps_directory>pixmaps</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>True</gnome_support>
+  <gettext_support>True</gettext_support>
+  <use_widget_names>True</use_widget_names>
+  <gnome_help_support>True</gnome_help_support>
+</project>
+
+<widget>
+  <class>GnomeDialog</class>
+  <name>dialog2</name>
+  <title>Confirm New Public Key</title>
+  <type>GTK_WINDOW_DIALOG</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <modal>False</modal>
+  <allow_shrink>True</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+  <auto_close>False</auto_close>
+  <hide_on_close>False</hide_on_close>
+
+  <widget>
+    <class>GtkVBox</class>
+    <child_name>GnomeDialog:vbox</child_name>
+    <name>dialog-vbox2</name>
+    <homogeneous>False</homogeneous>
+    <spacing>8</spacing>
+    <child>
+      <padding>4</padding>
+      <expand>True</expand>
+      <fill>True</fill>
+    </child>
+
+    <widget>
+      <class>GtkHButtonBox</class>
+      <child_name>GnomeDialog:action_area</child_name>
+      <name>dialog-action_area2</name>
+      <layout_style>GTK_BUTTONBOX_END</layout_style>
+      <spacing>13</spacing>
+      <child_min_width>85</child_min_width>
+      <child_min_height>27</child_min_height>
+      <child_ipad_x>3</child_ipad_x>
+      <child_ipad_y>0</child_ipad_y>
+      <child>
+       <padding>0</padding>
+       <expand>False</expand>
+       <fill>True</fill>
+       <pack>GTK_PACK_END</pack>
+      </child>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>button7</name>
+       <can_default>True</can_default>
+       <can_focus>True</can_focus>
+       <label>View...</label>
+      </widget>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>button8</name>
+       <can_default>True</can_default>
+       <can_focus>True</can_focus>
+       <stock_button>GNOME_STOCK_BUTTON_YES</stock_button>
+      </widget>
+
+      <widget>
+       <class>GtkButton</class>
+       <name>button9</name>
+       <can_default>True</can_default>
+       <can_focus>True</can_focus>
+       <stock_button>GNOME_STOCK_BUTTON_NO</stock_button>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkHBox</class>
+      <name>hbox97</name>
+      <homogeneous>False</homogeneous>
+      <spacing>0</spacing>
+      <child>
+       <padding>0</padding>
+       <expand>True</expand>
+       <fill>True</fill>
+      </child>
+
+      <widget>
+       <class>GtkPixmap</class>
+       <name>pixmap1</name>
+       <filename>folder.xpm</filename>
+       <xalign>0.5</xalign>
+       <yalign>0.15</yalign>
+       <xpad>0</xpad>
+       <ypad>0</ypad>
+       <build_insensitive>True</build_insensitive>
+       <child>
+         <padding>3</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+      </widget>
+
+      <widget>
+       <class>GtkLabel</class>
+       <name>label353</name>
+       <label>You have received the remote users public key. Would you
+like to accept and save the public key?
+
+Public Key Fingerprint:
+
+8321 1A83 88AB 091B 9BBF  6017 A72E 2971 89AB CAE9</label>
+       <justify>GTK_JUSTIFY_LEFT</justify>
+       <wrap>False</wrap>
+       <xalign>0.5</xalign>
+       <yalign>0.5</yalign>
+       <xpad>0</xpad>
+       <ypad>0</ypad>
+       <child>
+         <padding>10</padding>
+         <expand>False</expand>
+         <fill>False</fill>
+       </child>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/apps/silcer/ui/SilcerFirstsetupDlg.glade b/apps/silcer/ui/SilcerFirstsetupDlg.glade
new file mode 100644 (file)
index 0000000..25085c5
--- /dev/null
@@ -0,0 +1,439 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>Project2</name>
+  <program_name>project2</program_name>
+  <directory></directory>
+  <source_directory>src</source_directory>
+  <pixmaps_directory>pixmaps</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>True</gnome_support>
+  <gettext_support>True</gettext_support>
+</project>
+
+<widget>
+  <class>GtkWindow</class>
+  <name>SilcerFirstsetupDlg</name>
+  <title>window1</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <modal>False</modal>
+  <allow_shrink>False</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>False</auto_shrink>
+
+  <widget>
+    <class>GnomeDruid</class>
+    <name>druid1</name>
+
+    <widget>
+      <class>GnomeDruidPageStart</class>
+      <name>druidpagestart1</name>
+      <title>Silcer - First Time Setup</title>
+      <text>Silcer Gnome SILC Client
+
+You are running the Silcer for first time. This
+setup wizard will guide you through the setup
+procedure.  You will need to do this only once!
+
+PRESS NEXT TO CONTINUE</text>
+      <title_color>255,255,255</title_color>
+      <text_color>0,0,0</text_color>
+      <background_color>25,25,112</background_color>
+      <logo_background_color>255,255,255</logo_background_color>
+      <textbox_color>255,255,255</textbox_color>
+    </widget>
+
+    <widget>
+      <class>GnomeDruidPageStandard</class>
+      <name>druidpagestandard1</name>
+      <title>Personal Information</title>
+      <title_color>255,255,255</title_color>
+      <background_color>25,25,112</background_color>
+      <logo_background_color>255,255,255</logo_background_color>
+
+      <widget>
+       <class>GtkVBox</class>
+       <child_name>GnomeDruidPageStandard:vbox</child_name>
+       <name>druid-vbox1</name>
+       <homogeneous>False</homogeneous>
+       <spacing>0</spacing>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+
+       <widget>
+         <class>GtkFrame</class>
+         <name>frame1</name>
+         <border_width>5</border_width>
+         <label>Your Information</label>
+         <label_xalign>0.02</label_xalign>
+         <shadow_type>GTK_SHADOW_ETCHED_OUT</shadow_type>
+         <child>
+           <padding>0</padding>
+           <expand>True</expand>
+           <fill>True</fill>
+         </child>
+
+         <widget>
+           <class>GtkTable</class>
+           <name>table1</name>
+           <border_width>45</border_width>
+           <rows>5</rows>
+           <columns>2</columns>
+           <homogeneous>False</homogeneous>
+           <row_spacing>6</row_spacing>
+           <column_spacing>10</column_spacing>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label2</name>
+             <label>Real Name:</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <left_attach>0</left_attach>
+               <right_attach>1</right_attach>
+               <top_attach>0</top_attach>
+               <bottom_attach>1</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>False</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkEntry</class>
+             <name>entry1</name>
+             <can_focus>True</can_focus>
+             <editable>True</editable>
+             <text_visible>True</text_visible>
+             <text_max_length>0</text_max_length>
+             <text></text>
+             <child>
+               <left_attach>1</left_attach>
+               <right_attach>2</right_attach>
+               <top_attach>0</top_attach>
+               <bottom_attach>1</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>True</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label3</name>
+             <label>Hostname:</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <left_attach>0</left_attach>
+               <right_attach>1</right_attach>
+               <top_attach>1</top_attach>
+               <bottom_attach>2</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>False</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label4</name>
+             <label>EMail:</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <left_attach>0</left_attach>
+               <right_attach>1</right_attach>
+               <top_attach>2</top_attach>
+               <bottom_attach>3</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>False</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkEntry</class>
+             <name>entry2</name>
+             <can_focus>True</can_focus>
+             <editable>True</editable>
+             <text_visible>True</text_visible>
+             <text_max_length>0</text_max_length>
+             <text></text>
+             <child>
+               <left_attach>1</left_attach>
+               <right_attach>2</right_attach>
+               <top_attach>1</top_attach>
+               <bottom_attach>2</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>True</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkEntry</class>
+             <name>entry3</name>
+             <can_focus>True</can_focus>
+             <editable>True</editable>
+             <text_visible>True</text_visible>
+             <text_max_length>0</text_max_length>
+             <text></text>
+             <child>
+               <left_attach>1</left_attach>
+               <right_attach>2</right_attach>
+               <top_attach>2</top_attach>
+               <bottom_attach>3</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>True</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label5</name>
+             <label>Username:</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <left_attach>0</left_attach>
+               <right_attach>1</right_attach>
+               <top_attach>3</top_attach>
+               <bottom_attach>4</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>False</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkEntry</class>
+             <name>entry4</name>
+             <can_focus>True</can_focus>
+             <editable>True</editable>
+             <text_visible>True</text_visible>
+             <text_max_length>0</text_max_length>
+             <text></text>
+             <child>
+               <left_attach>1</left_attach>
+               <right_attach>2</right_attach>
+               <top_attach>3</top_attach>
+               <bottom_attach>4</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>True</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label6</name>
+             <label>
+               PRESS NEXT TO CONTINUE!</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <left_attach>1</left_attach>
+               <right_attach>2</right_attach>
+               <top_attach>4</top_attach>
+               <bottom_attach>5</bottom_attach>
+               <xpad>0</xpad>
+               <ypad>0</ypad>
+               <xexpand>False</xexpand>
+               <yexpand>False</yexpand>
+               <xshrink>False</xshrink>
+               <yshrink>False</yshrink>
+               <xfill>True</xfill>
+               <yfill>False</yfill>
+             </child>
+           </widget>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GnomeDruidPageStandard</class>
+      <name>druidpagestandard2</name>
+      <title>Authentication Key Generation</title>
+      <title_color>255,255,255</title_color>
+      <background_color>25,25,112</background_color>
+      <logo_background_color>255,255,255</logo_background_color>
+
+      <widget>
+       <class>GtkVBox</class>
+       <child_name>GnomeDruidPageStandard:vbox</child_name>
+       <name>druid-vbox2</name>
+       <homogeneous>False</homogeneous>
+       <spacing>0</spacing>
+       <child>
+         <padding>0</padding>
+         <expand>True</expand>
+         <fill>True</fill>
+       </child>
+
+       <widget>
+         <class>GtkFrame</class>
+         <name>frame2</name>
+         <border_width>5</border_width>
+         <label_xalign>0</label_xalign>
+         <shadow_type>GTK_SHADOW_ETCHED_OUT</shadow_type>
+         <child>
+           <padding>0</padding>
+           <expand>True</expand>
+           <fill>True</fill>
+         </child>
+
+         <widget>
+           <class>GtkVBox</class>
+           <name>vbox1</name>
+           <border_width>30</border_width>
+           <homogeneous>False</homogeneous>
+           <spacing>0</spacing>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label7</name>
+             <label>Random pool generation in progress...</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0.5</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <padding>0</padding>
+               <expand>False</expand>
+               <fill>False</fill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkProgressBar</class>
+             <name>progressbar1</name>
+             <value>0</value>
+             <lower>0</lower>
+             <upper>100</upper>
+             <bar_style>GTK_PROGRESS_CONTINUOUS</bar_style>
+             <orientation>GTK_PROGRESS_LEFT_TO_RIGHT</orientation>
+             <activity_mode>True</activity_mode>
+             <show_text>False</show_text>
+             <format>%P %%</format>
+             <text_xalign>0.5</text_xalign>
+             <text_yalign>0.5</text_yalign>
+             <child>
+               <padding>1</padding>
+               <expand>False</expand>
+               <fill>False</fill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label8</name>
+             <label>
+
+Setup Wizard is generating random data and entropy for
+Authentication Key Generation process.  Please, move
+your mouse pointer around to generate random data.</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0.5</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <padding>0</padding>
+               <expand>False</expand>
+               <fill>False</fill>
+             </child>
+           </widget>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GnomeDruidPageFinish</class>
+      <name>druidpagefinish1</name>
+      <title>Setup Completed</title>
+      <text>The Setup Wizard is completed! 
+
+PRESS FINISH TO CONTINUE!</text>
+      <background_color>25,25,112</background_color>
+      <logo_background_color>255,255,255</logo_background_color>
+      <textbox_color>255,255,255</textbox_color>
+      <text_color>0,0,0</text_color>
+      <title_color>255,255,255</title_color>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/apps/silcer/ui/SilcerMainDlg.glade b/apps/silcer/ui/SilcerMainDlg.glade
new file mode 100644 (file)
index 0000000..8f704d2
--- /dev/null
@@ -0,0 +1,429 @@
+<?xml version="1.0"?>
+<GTK-Interface>
+
+<project>
+  <name>Silcer</name>
+  <program_name>silcer</program_name>
+  <directory></directory>
+  <source_directory>src</source_directory>
+  <pixmaps_directory>pixmaps</pixmaps_directory>
+  <language>C</language>
+  <gnome_support>True</gnome_support>
+  <gettext_support>True</gettext_support>
+  <use_widget_names>True</use_widget_names>
+  <gnome_help_support>True</gnome_help_support>
+</project>
+
+<widget>
+  <class>GnomeApp</class>
+  <name>SilcerMainDlg</name>
+  <visible>False</visible>
+  <title>Silcer</title>
+  <type>GTK_WINDOW_TOPLEVEL</type>
+  <position>GTK_WIN_POS_NONE</position>
+  <modal>False</modal>
+  <default_width>600</default_width>
+  <default_height>400</default_height>
+  <allow_shrink>True</allow_shrink>
+  <allow_grow>True</allow_grow>
+  <auto_shrink>True</auto_shrink>
+  <enable_layout_config>True</enable_layout_config>
+
+  <widget>
+    <class>GnomeDock</class>
+    <child_name>GnomeApp:dock</child_name>
+    <name>dock1</name>
+    <allow_floating>True</allow_floating>
+    <child>
+      <padding>0</padding>
+      <expand>True</expand>
+      <fill>True</fill>
+    </child>
+
+    <widget>
+      <class>GnomeDockItem</class>
+      <name>dockitem2</name>
+      <border_width>2</border_width>
+      <placement>GNOME_DOCK_TOP</placement>
+      <band>0</band>
+      <position>0</position>
+      <offset>0</offset>
+      <locked>False</locked>
+      <exclusive>False</exclusive>
+      <never_floating>False</never_floating>
+      <never_vertical>False</never_vertical>
+      <never_horizontal>False</never_horizontal>
+      <shadow_type>GTK_SHADOW_OUT</shadow_type>
+
+      <widget>
+       <class>GtkMenuBar</class>
+       <name>SilcerMainDlgMenuBar</name>
+       <shadow_type>GTK_SHADOW_OUT</shadow_type>
+
+       <widget>
+         <class>GtkMenuItem</class>
+         <name>silcer2</name>
+         <signal>
+           <name>activate</name>
+           <handler>on_silcer2_activate</handler>
+           <last_modification_time>Fri, 16 Nov 2001 19:37:56 GMT</last_modification_time>
+         </signal>
+         <label>_Silcer</label>
+         <right_justify>False</right_justify>
+
+         <widget>
+           <class>GtkMenu</class>
+           <name>silcer2_menu</name>
+
+           <widget>
+             <class>GtkPixmapMenuItem</class>
+             <name>dfghdfh1</name>
+             <label>dfghdfh</label>
+             <right_justify>False</right_justify>
+             <stock_icon>GNOME_STOCK_MENU_SAVE</stock_icon>
+           </widget>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GnomeDockItem</class>
+      <name>dockitem1</name>
+      <border_width>1</border_width>
+      <placement>GNOME_DOCK_TOP</placement>
+      <band>1</band>
+      <position>0</position>
+      <offset>0</offset>
+      <locked>False</locked>
+      <exclusive>True</exclusive>
+      <never_floating>False</never_floating>
+      <never_vertical>True</never_vertical>
+      <never_horizontal>False</never_horizontal>
+      <shadow_type>GTK_SHADOW_OUT</shadow_type>
+
+      <widget>
+       <class>GtkHBox</class>
+       <name>hbox3</name>
+       <border_width>1</border_width>
+       <homogeneous>False</homogeneous>
+       <spacing>4</spacing>
+
+       <widget>
+         <class>GtkToolbar</class>
+         <name>SilcerMainDlgToolbar</name>
+         <orientation>GTK_ORIENTATION_HORIZONTAL</orientation>
+         <type>GTK_TOOLBAR_ICONS</type>
+         <space_size>8</space_size>
+         <space_style>GTK_TOOLBAR_SPACE_LINE</space_style>
+         <relief>GTK_RELIEF_NONE</relief>
+         <tooltips>True</tooltips>
+         <child>
+           <padding>0</padding>
+           <expand>False</expand>
+           <fill>False</fill>
+         </child>
+
+         <widget>
+           <class>GtkButton</class>
+           <child_name>Toolbar:button</child_name>
+           <name>button1</name>
+           <label>button1</label>
+           <stock_pixmap>GNOME_STOCK_PIXMAP_NEW</stock_pixmap>
+         </widget>
+
+         <widget>
+           <class>GtkButton</class>
+           <child_name>Toolbar:button</child_name>
+           <name>button2</name>
+           <label>button2</label>
+           <stock_pixmap>GNOME_STOCK_PIXMAP_OPEN</stock_pixmap>
+           <child>
+             <new_group>True</new_group>
+           </child>
+         </widget>
+
+         <widget>
+           <class>GtkButton</class>
+           <child_name>Toolbar:button</child_name>
+           <name>SMainHistory</name>
+           <width>30</width>
+           <height>30</height>
+           <label>_History</label>
+           <stock_pixmap>GNOME_STOCK_PIXMAP_INDEX</stock_pixmap>
+           <child>
+             <new_group>True</new_group>
+           </child>
+         </widget>
+       </widget>
+
+       <widget>
+         <class>GtkHBox</class>
+         <name>hbox5</name>
+         <border_width>1</border_width>
+         <homogeneous>False</homogeneous>
+         <spacing>3</spacing>
+         <child>
+           <padding>0</padding>
+           <expand>True</expand>
+           <fill>True</fill>
+         </child>
+
+         <widget>
+           <class>GtkLabel</class>
+           <name>label6</name>
+           <label>_Topic:</label>
+           <justify>GTK_JUSTIFY_CENTER</justify>
+           <wrap>False</wrap>
+           <xalign>0.5</xalign>
+           <yalign>0.5</yalign>
+           <xpad>0</xpad>
+           <ypad>0</ypad>
+           <default_focus_target>SMainTopicEntry</default_focus_target>
+           <child>
+             <padding>0</padding>
+             <expand>False</expand>
+             <fill>False</fill>
+           </child>
+         </widget>
+
+         <widget>
+           <class>GtkEntry</class>
+           <name>SMainTopicEntry</name>
+           <can_focus>True</can_focus>
+           <editable>True</editable>
+           <text_visible>True</text_visible>
+           <text_max_length>0</text_max_length>
+           <text></text>
+           <child>
+             <padding>5</padding>
+             <expand>True</expand>
+             <fill>True</fill>
+           </child>
+         </widget>
+       </widget>
+      </widget>
+    </widget>
+
+    <widget>
+      <class>GtkNotebook</class>
+      <child_name>GnomeDock:contents</child_name>
+      <name>SilcerMainDlgTab</name>
+      <can_focus>True</can_focus>
+      <show_tabs>True</show_tabs>
+      <show_border>True</show_border>
+      <tab_pos>GTK_POS_TOP</tab_pos>
+      <scrollable>False</scrollable>
+      <tab_hborder>2</tab_hborder>
+      <tab_vborder>2</tab_vborder>
+      <popup_enable>False</popup_enable>
+
+      <widget>
+       <class>GtkFrame</class>
+       <child_name>GnomeDock:contents</child_name>
+       <name>frame1</name>
+       <label_xalign>0</label_xalign>
+       <shadow_type>GTK_SHADOW_OUT</shadow_type>
+
+       <widget>
+         <class>GtkVBox</class>
+         <child_name>GnomeDock:contents</child_name>
+         <name>vbox3</name>
+         <border_width>4</border_width>
+         <homogeneous>False</homogeneous>
+         <spacing>8</spacing>
+
+         <widget>
+           <class>GtkHBox</class>
+           <name>hbox1</name>
+           <visible>False</visible>
+           <homogeneous>False</homogeneous>
+           <spacing>8</spacing>
+           <child>
+             <padding>0</padding>
+             <expand>False</expand>
+             <fill>False</fill>
+           </child>
+
+           <widget>
+             <class>GnomePixmap</class>
+             <name>pixmap1</name>
+             <filename>glade-groupchat-menu.xpm</filename>
+             <child>
+               <padding>0</padding>
+               <expand>False</expand>
+               <fill>True</fill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label3</name>
+             <label></label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <padding>0</padding>
+               <expand>True</expand>
+               <fill>True</fill>
+             </child>
+           </widget>
+
+           <widget>
+             <class>GtkLabel</class>
+             <name>label4</name>
+             <label>0 users</label>
+             <justify>GTK_JUSTIFY_CENTER</justify>
+             <wrap>False</wrap>
+             <xalign>0.5</xalign>
+             <yalign>0.5</yalign>
+             <xpad>0</xpad>
+             <ypad>0</ypad>
+             <child>
+               <padding>0</padding>
+               <expand>False</expand>
+               <fill>False</fill>
+             </child>
+           </widget>
+         </widget>
+
+         <widget>
+           <class>GtkHPaned</class>
+           <name>hpaned1</name>
+           <handle_size>7</handle_size>
+           <gutter_size>8</gutter_size>
+           <child>
+             <padding>0</padding>
+             <expand>True</expand>
+             <fill>True</fill>
+           </child>
+
+           <widget>
+             <class>GtkVPaned</class>
+             <name>vpaned1</name>
+             <handle_size>7</handle_size>
+             <gutter_size>8</gutter_size>
+             <child>
+               <shrink>True</shrink>
+               <resize>True</resize>
+             </child>
+
+             <widget>
+               <class>GtkHBox</class>
+               <name>SilcerMainDlgOutputBox</name>
+               <homogeneous>False</homogeneous>
+               <spacing>0</spacing>
+               <child>
+                 <shrink>True</shrink>
+                 <resize>True</resize>
+               </child>
+
+               <widget>
+                 <class>Placeholder</class>
+               </widget>
+
+               <widget>
+                 <class>Placeholder</class>
+               </widget>
+             </widget>
+
+             <widget>
+               <class>GtkScrolledWindow</class>
+               <name>scrolledwindow1</name>
+               <height>40</height>
+               <hscrollbar_policy>GTK_POLICY_NEVER</hscrollbar_policy>
+               <vscrollbar_policy>GTK_POLICY_AUTOMATIC</vscrollbar_policy>
+               <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+               <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+               <child>
+                 <shrink>True</shrink>
+                 <resize>False</resize>
+               </child>
+
+               <widget>
+                 <class>GtkText</class>
+                 <name>SilcerMainDlgInputBox</name>
+                 <can_default>True</can_default>
+                 <has_default>True</has_default>
+                 <can_focus>True</can_focus>
+                 <has_focus>True</has_focus>
+                 <editable>True</editable>
+                 <text></text>
+               </widget>
+             </widget>
+           </widget>
+
+           <widget>
+             <class>GtkVBox</class>
+             <name>vbox4</name>
+             <width>120</width>
+             <homogeneous>False</homogeneous>
+             <spacing>8</spacing>
+             <child>
+               <shrink>True</shrink>
+               <resize>False</resize>
+             </child>
+
+             <widget>
+               <class>GtkScrolledWindow</class>
+               <name>scrolledwindow2</name>
+               <hscrollbar_policy>GTK_POLICY_AUTOMATIC</hscrollbar_policy>
+               <vscrollbar_policy>GTK_POLICY_AUTOMATIC</vscrollbar_policy>
+               <hupdate_policy>GTK_UPDATE_CONTINUOUS</hupdate_policy>
+               <vupdate_policy>GTK_UPDATE_CONTINUOUS</vupdate_policy>
+               <child>
+                 <padding>0</padding>
+                 <expand>True</expand>
+                 <fill>True</fill>
+               </child>
+
+               <widget>
+                 <class>GtkCList</class>
+                 <name>clist1</name>
+                 <can_focus>True</can_focus>
+                 <columns>1</columns>
+                 <column_widths>80</column_widths>
+                 <selection_mode>GTK_SELECTION_BROWSE</selection_mode>
+                 <show_titles>True</show_titles>
+                 <shadow_type>GTK_SHADOW_IN</shadow_type>
+
+                 <widget>
+                   <class>GtkLabel</class>
+                   <child_name>CList:title</child_name>
+                   <name>label5</name>
+                   <label>0 users</label>
+                   <justify>GTK_JUSTIFY_CENTER</justify>
+                   <wrap>False</wrap>
+                   <xalign>0.5</xalign>
+                   <yalign>0.5</yalign>
+                   <xpad>0</xpad>
+                   <ypad>0</ypad>
+                 </widget>
+               </widget>
+             </widget>
+           </widget>
+         </widget>
+       </widget>
+      </widget>
+
+      <widget>
+       <class>GtkLabel</class>
+       <child_name>Notebook:tab</child_name>
+       <name>SilcerMainDlgTabLabel1</name>
+       <label></label>
+       <justify>GTK_JUSTIFY_CENTER</justify>
+       <wrap>False</wrap>
+       <xalign>0.5</xalign>
+       <yalign>0.5</yalign>
+       <xpad>0</xpad>
+       <ypad>0</ypad>
+      </widget>
+    </widget>
+  </widget>
+</widget>
+
+</GTK-Interface>
diff --git a/apps/silcer/xml-i18n-extract.in b/apps/silcer/xml-i18n-extract.in
new file mode 100644 (file)
index 0000000..49a5598
--- /dev/null
@@ -0,0 +1,303 @@
+#!@XML_I18N_TOOLS_PERL@ -w 
+# -*- Mode: perl; indent-tabs-mode: nil; c-basic-offset: 4  -*-
+
+#
+#  The XML Translation Extractor
+#
+#  Copyright (C) 2000 Free Software Foundation.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License as
+#  published by the Free Software Foundation; either version 2 of the
+#  License, or (at your option) any later version.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this library; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#  Authors: Kenneth Christiansen <kenneth@gnu.org>
+#           Darin Adler <darin@eazel.com>
+#
+
+## Release information
+my $PROGRAM      = "xml-i18n-extract";
+my $PACKAGE      = "xml-i18n-tools";
+my $VERSION      = "0.9";
+
+## Script options - Enable by setting value to 1
+my $ENABLE_INI   = "1"; ## desktop and alike files
+my $ENABLE_KEYS  = "1"; ## mimetype descriptions
+my $ENABLE_GLADE = "1"; ## glade files
+my $ENABLE_XML          = "1"; ## generic xml files
+
+## Loaded modules
+use strict; 
+use File::Basename;
+use Getopt::Long;
+
+## Scalars used by the option stuff
+my $TYPE_ARG   = "0";
+my $LOCAL_ARG  = "0";
+my $HELP_ARG   = "0";
+my $VERSION_ARG = "0";
+my $UPDATE_ARG  = "0";
+my $QUIET_ARG   = "0";
+
+my $FILE;
+my $OUTFILE;
+
+my $gettext_type = "";
+my $input;
+my %messages = ();
+
+## Always print first
+$| = 1;
+
+## Handle options
+GetOptions (
+           "type=s"     => \$TYPE_ARG,
+            "local|l"    => \$LOCAL_ARG,
+            "help|h|?"   => \$HELP_ARG,
+            "version|v"  => \$VERSION_ARG,
+            "update"     => \$UPDATE_ARG,
+           "quiet|q"    => \$QUIET_ARG,
+            ) or &error;
+
+&split_on_argument;
+
+
+## Check for options. 
+## This section will check for the different options.
+
+sub split_on_argument {
+
+    if ($VERSION_ARG) {
+        &version;
+
+    } elsif ($HELP_ARG) {
+       &help;
+        
+    } elsif ($LOCAL_ARG) {
+        &place_local;
+        &extract;
+
+    } elsif ($UPDATE_ARG) {
+       &place_normal;
+       &extract;
+
+    } elsif (@ARGV > 0) {
+       &place_normal;
+       &message;
+       &extract;
+
+    } else {
+       &help;
+
+    }  
+}    
+
+sub place_normal {
+    $FILE       = $ARGV[0];
+    $OUTFILE     = "$FILE.h";
+}   
+
+sub place_local {
+    $OUTFILE     = fileparse($FILE, ());
+    if (!-e "tmp/") { 
+        system("mkdir tmp/"); 
+    }
+    $OUTFILE     = "./tmp/$OUTFILE.h"
+}
+
+sub determine_type {
+   if ($TYPE_ARG =~ /^gettext\/(.*)/) {
+       $gettext_type=$1
+   }
+}
+
+## Sub for printing release information
+sub version{
+    print "${PROGRAM} (${PACKAGE}) $VERSION\n";
+    print "Copyright (C) 2000 Free Software Foundation, Inc.\n";
+    print "Written by Kenneth Christiansen, 2000.\n\n";
+    print "This is free software; see the source for copying conditions. There is NO\n";
+    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
+    exit;
+}
+
+## Sub for printing usage information
+sub help{
+    print "Usage: ${PROGRAM} [FILENAME] [OPTIONS] ...\n";
+    print "Generates a header file from an xml source file.\n\nGrabs all strings ";
+    print "between <_translatable_node> and it's end tag,\nwhere tag are all allowed ";
+    print "xml tags. Read the docs for more info.\n\n"; 
+    print "  -v, --version                shows the version\n";
+    print "  -h, --help                   shows this help page\n";
+    print "  -q, --quiet                  quiet mode\n";
+    print "\nReport bugs to <kenneth\@gnu.org>.\n";
+    exit;
+}
+
+## Sub for printing error messages
+sub error{
+#   print "xml-i18n-extract: invalid option @ARGV\n";
+    print "Try `${PROGRAM} --help' for more information.\n";
+    exit;
+}
+
+sub message {
+    print "Generating C format header file for translation.\n";
+}
+
+sub extract {
+    &determine_type;
+
+    &convert ($FILE);
+
+    open OUT, ">$OUTFILE";
+    &msg_write;
+    close OUT;
+
+    print "Wrote $OUTFILE\n" unless $QUIET_ARG;
+}
+
+sub convert($) {
+
+    ## Reading the file
+    {
+       local (*IN);
+       local $/; #slurp mode
+       open (IN, "<$FILE") || die "can't open $FILE: $!";
+       $input = <IN>;
+    }
+
+    &type_ini;
+    &type_keys;
+    &type_xml;
+    &type_glade;
+}
+
+sub type_ini {
+
+    if ($ENABLE_INI) { 
+        
+        ### For generic translatable desktop files ###
+    
+        if ($gettext_type eq "ini"){    
+
+            while ($input =~ /^_.*=(.*)$/mg) {
+                $messages{$1} = [];
+            }
+        }
+    }
+}
+
+sub type_keys {
+    
+    if ($ENABLE_KEYS) {
+    
+        ### For generic translatable mime/keys files ###
+
+        if ($gettext_type eq "keys"){
+            while ($input =~ /^\s*_\w+=(.*)$/mg) {
+                $messages{$1} = [];
+            }
+        }
+    }
+}
+
+sub type_xml {
+
+    if ($ENABLE_XML) {
+
+       ### For generic translatable XML files ###
+        
+        if ($gettext_type eq "xml"){
+
+            while ($input =~ /[\t\n\s]_\w+=\"([^\"]+)\"/sg) {
+                $messages{$1} = [];
+            }
+
+            while ($input =~ /<_(\w+)>([^<]+)<\/_\1>/sg) {
+                $messages{$2} = [];
+            }
+
+       }
+    }
+}
+
+sub type_glade {
+
+    if ($ENABLE_GLADE) {
+        
+        ### For translatable Glade XML files ###
+
+        if ($gettext_type eq "glade"){
+
+            my $translate = "label|title|text|format|copyright|comments|
+                             preview_text|tooltip";
+
+            while ($input =~ /<($translate)>([^<]+)<\/($translate)>/sg) {
+
+                # Glade has some bugs, especially it uses translations tags to contain little
+                # non-translatable content. We work around this, by not including these
+                # strings that only includes something like: label4, and window1
+                if ($2 !~ /^(window|label)[0-9]+$/) {
+                    $messages{$2} = [];
+                }
+            }
+            
+            while ($input =~ /<items>(..[^<]*)<\/items>/sg) {
+                my @items =  split (/\n/, $1);
+                for (my $n = 0; $n < @items; $n++) {
+                    $messages{$items[$n]} = [];
+                }
+            }
+
+       }
+    }
+
+}
+
+sub msg_write {
+    
+    foreach my $message (sort keys %messages) { 
+        
+        my ($tag) = @{ $messages{$message} };
+        
+        # Replace XML entities for some special characters with
+        # the appropriate gettext syntax for those characters.
+       $message =~ s/&quot;/\\"/mg; # "
+       $message =~ s/&lt;/</mg;
+       $message =~ s/&gt;/>/mg;
+       $message =~ s/&amp;/&/mg;
+       
+       print OUT "/* xgettext:no-c-format */\n" if $message =~ /%/;
+       print OUT "/* $tag */\n" if $tag;
+        
+       my @lines = split (/\n/, $message);
+
+       for (my $n = 0; $n < @lines; $n++) {
+
+            if ($n == 0) { 
+               print OUT "char *s = N_(\""; 
+            } else {  
+               print OUT "             \""; 
+           }
+
+            print OUT $lines[$n];
+
+            if ($n < @lines - 1) { 
+               print OUT "\\n\"\n"; 
+           } else { 
+               print OUT "\");\n";  
+           }
+        }
+    }
+}
+
diff --git a/apps/silcer/xml-i18n-merge.in b/apps/silcer/xml-i18n-merge.in
new file mode 100644 (file)
index 0000000..5c3a581
--- /dev/null
@@ -0,0 +1,459 @@
+#!@XML_I18N_TOOLS_PERL@ -w
+
+#
+#  The XML Translation Merge Tool
+#
+#  Copyright (C) 2000 Free Software Foundation.
+#  Copyright (C) 2000, 2001 Eazel, Inc
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License as
+#  published by the Free Software Foundation; either version 2 of the
+#  License, or (at your option) any later version.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this library; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#  Authors:  Maciej Stachowiak <mjs@eazel.com>
+#            Kenneth Christiansen <kenneth@gnu.org>
+#            Darin Adler <darin@eazel.com>
+#
+
+
+## Release information
+my $PROGRAM      = "xml-i18n-merge";
+my $PACKAGE      = "xml-i18n-tools";
+my $VERSION      = "0.9";
+
+## Script options - Enable by setting value to 1
+my $ENABLE_XML   = "1";
+
+## Loaded modules
+use strict; 
+use File::Basename;
+use Getopt::Long;
+
+## Scalars used by the option stuff
+my $HELP_ARG   = "0";
+my $VERSION_ARG = "0";
+my $OAF_STYLE_ARG = "0";
+my $XML_STYLE_ARG = "0";
+my $KEYS_STYLE_ARG = "0";
+my $DESKTOP_STYLE_ARG = "0";
+my $QUIET_ARG = "0";
+
+
+## Handle options
+GetOptions (
+           "help|h|?" => \$HELP_ARG,
+           "version|v" => \$VERSION_ARG,
+            "quiet|q" => \$QUIET_ARG,
+           "oaf-style|o" => \$OAF_STYLE_ARG,
+           "xml-style|x" => \$XML_STYLE_ARG,
+           "keys-style|k" => \$KEYS_STYLE_ARG,
+           "desktop-style|d" => \$DESKTOP_STYLE_ARG
+           ) or &error;
+
+
+my $PO_DIR;
+my $FILE;
+my $OUTFILE;
+
+my @languages;
+my %po_files_by_lang = ();
+my %translations = ();
+
+&split_on_argument;
+
+
+## Check for options. 
+## This section will check for the different options.
+
+sub split_on_argument {
+
+    if ($VERSION_ARG) {
+       &version;
+
+    } elsif ($HELP_ARG) {
+       &help;
+    } elsif ($OAF_STYLE_ARG && @ARGV > 2) {
+       &place_normal;
+       &message;
+       &preparation;
+       &oaf_merge_translations;
+    } elsif ($XML_STYLE_ARG && @ARGV > 2) {
+       &place_normal;
+       &message;
+       &preparation;
+       &xml_merge_translations;
+    } elsif ($KEYS_STYLE_ARG && @ARGV > 2) {
+        &place_normal;
+        &message;
+        &preparation;
+        &keys_merge_translations;
+    } elsif ($DESKTOP_STYLE_ARG && @ARGV > 2) {
+        &place_normal;
+        &message;
+        &preparation;
+        &desktop_merge_translations;
+    } else {
+       &help;
+    }  
+}    
+
+
+sub place_normal {
+    $PO_DIR = $ARGV[0];
+    $FILE = $ARGV[1];
+    $OUTFILE = $ARGV[2];
+}   
+
+
+## Sub for printing release information
+sub version{
+    print "${PROGRAM} (${PACKAGE}) ${VERSION}\n";
+    print "Written by Maciej Stachowiak and Kenneth Christiansen, 2000.\n\n";
+    print "Copyright (C) 2000 Free Software Foundation, Inc.\n";
+    print "Copyright (C) 2000, 2001 Eazel, Inc.\n";
+    print "This is free software; see the source for copying conditions.  There is NO\n";
+    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
+    exit;
+}
+
+## Sub for printing usage information
+sub help{
+    print "Usage: ${PROGRAM} [OPTIONS] PO_DIRECTORY FILENAME OUTPUT_FILE\n";
+    print "Generates an xml file that includes translated versions of some attributes,\n";
+    print "from an untranslated source and a po directory that includes translations.\n";
+    print "  -v, --version                shows the version\n";
+    print "  -h, --help                   shows this help page\n";
+    print "  -q, --quiet                  quiet mode\n";
+    print "  -o, --oaf-style              includes translations in the oaf style\n";
+    print "  -x, --xml-style              includes translations in the xml style\n";
+    print "  -k, --keys-style            includes translations in the keys style\n";
+    print "  -d, --desktop-style          includes translations in the desktop style\n";
+    print "\nReport bugs to <mjs\@eazel.com>.\n";
+    exit;
+}
+
+
+## Sub for printing error messages
+sub error{
+#   print "xml-i18n-merge: invalid option @ARGV\n";
+    print "Try `${PROGRAM} --help' for more information.\n";
+    exit;
+}
+
+
+sub message {
+    print "Merging translations into $OUTFILE.\n" unless $QUIET_ARG;
+}
+
+
+
+sub preparation {
+   &gather_po_files;
+   &create_translation_database;   
+}
+
+
+
+# General-purpose code for looking up translations in .po files
+
+sub gather_po_files
+{
+    my @po_files = glob("${PO_DIR}/*.po");
+
+    @languages = map (&po_file2lang, @po_files);
+
+    foreach my $lang (@languages) {
+       $po_files_by_lang{$lang} = shift (@po_files);
+    }
+}
+
+sub po_file2lang 
+{ 
+    my $tmp = $_; 
+    $tmp =~ s/^.*\/(.*)\.po$/$1/; 
+    return $tmp; 
+}
+
+
+sub create_translation_database
+{
+    foreach my $lang (@languages) {
+
+       my $po_file = $po_files_by_lang{$lang};
+
+       open PO_FILE, "<$po_file";      
+
+        while (<PO_FILE>) {
+            if (/^#,.*fuzzy/) {
+                $_ = <PO_FILE>; next;
+            }
+            if (/^msgid "(.*)"/ ) {
+               my $msgid = $1;
+                $_ = <PO_FILE>;
+               
+               if (/^msgstr "(.+)"/) {
+                   my $msgstr = $1;
+                   $translations{$lang . "|" . $msgid} = $msgstr; 
+                   # print "[$lang]$msgstr\n";
+               }
+           }            
+        }
+    }
+}
+
+sub lookup_translations 
+{
+    my ($value) = @_;
+    my %transmap = ();
+
+    foreach my $lang (@languages) {
+        my $translation = lookup_translation ($value, $lang);
+            
+        if ($translation) {
+            $transmap{$lang} = $translation;
+        }
+    }
+
+    return %transmap;
+}
+
+
+sub lookup_translation
+{
+    my ($string, $lang) = @_;
+    $string =~ s/\+/\\+/g;
+  
+    my $salt = "$lang|$string";
+      
+    if ($translations{$salt}) {
+        return $translations{$salt};
+    }
+  
+    return "";
+}
+
+
+sub entity_encode_translations
+{
+    my %transmap = @_;
+
+    foreach my $key (keys %transmap) {
+       $transmap{$key} = entity_encode ($transmap{$key});
+    }
+
+    return %transmap;
+}
+
+
+sub entity_encode
+{
+    my ($pre_encoded) = @_;
+
+    $pre_encoded =~ s/\\(.)/$1/g;
+    my @list_of_chars = unpack ('C*', $pre_encoded);
+
+    return join ('', map (&entity_encode_int, @list_of_chars));
+}
+
+sub entity_encode_int
+{
+    if ($_ > 127 || $_ == 34 || $_ == 38) {
+       return "&#" . $_ . ";";
+    } else {
+       return chr $_;
+    }
+}
+
+
+## XML/OAF-specific merge code
+sub oaf_merge_translations
+{
+    my $xml_source; {
+       local (*INPUT);
+       local $/; # slurp mode
+       open INPUT, "<$FILE" or die "can't open $FILE: $!";
+       $xml_source = <INPUT>;
+       close INPUT;
+    }
+
+    open OUTPUT, ">$OUTFILE";
+
+    while ($xml_source =~ /[ \t]*<[^<]*\s_\w+="[^"]*"[^<]*>/m) {
+        print OUTPUT $`;
+        my $orig_node = $&;
+       $xml_source = $';
+
+        my $non_translated_line = $orig_node;
+        $non_translated_line =~ s/_(\w+)="/$1="/;
+            
+        my $new_node = $non_translated_line;
+            
+        my $value_str = $orig_node;
+        $value_str =~ s/.*_\w+="([^"]*)".*/$1/s;
+
+        if ($value_str) {
+            my %value_translation_map = entity_encode_translations
+                (lookup_translations ($value_str));
+
+            foreach my $key (sort keys %value_translation_map) {
+                my $translation = $value_translation_map{$key};
+                    
+                my $translated_line = $orig_node;
+                $translated_line =~ s/name="([^"]*)"/name="$1-$key"/;
+                $translated_line =~ s/(\s*)_(\w+)="[^"]*"/$1$2="$translation"/;
+
+                $new_node .= "\n$translated_line";
+            }
+        }
+
+       $xml_source = $new_node . $xml_source;
+    }
+
+    print OUTPUT $xml_source;
+
+    close OUTPUT;
+}
+
+
+## XML (non-OAF) merge code
+sub xml_merge_translations
+{
+    my $xml_source; {
+       local (*INPUT);
+       local $/; # slurp mode
+       open INPUT, "<$FILE" or die "can't open $FILE: $!";
+       $xml_source = <INPUT>;
+       close INPUT;
+    }
+
+    open OUTPUT, ">$OUTFILE";
+
+    # FIXME: support attribute translations
+
+    # First just unmark for translation all empty nodes
+    # for example <_foo/> is just replaced by <foo/>
+    $xml_source =~ s/<_(\w+)\/>/<$1\/>/mg;
+
+    # Support for XML <_foo>blah</_foo> style translations
+    while ($xml_source =~ /([ \t]*)<_(\w+)>([^<]+)<\/_\2>/m) {
+        print OUTPUT $`;
+       $xml_source = $';
+
+        my $spaces = $1;
+        my $tag_name = $2;
+        my $value_str = $3;
+
+        my $non_translated_line = "$spaces<$tag_name>$value_str</$tag_name>";
+            
+        my $new_node = $non_translated_line;
+
+        if ($value_str) {
+            my %value_translation_map = entity_encode_translations
+                (lookup_translations ($value_str));
+
+            foreach my $key (sort keys %value_translation_map) {
+                my $translation = $value_translation_map{$key};
+
+                $new_node .= "\n$spaces<$tag_name xml:lang=\"$key\">$translation</$tag_name>";
+            }
+        }
+
+       $xml_source = $new_node . $xml_source;
+    }
+
+    print OUTPUT $xml_source;
+
+    close OUTPUT;
+}
+
+sub keys_merge_translations
+{       
+    open INPUT, "<${FILE}";
+
+    open OUTPUT, ">${OUTFILE}";
+
+    while (<INPUT>) {
+        chomp;
+        if (/^\s*_\w+=.*/)  {
+            my $orig_line = $_;
+    
+            my $non_translated_line = $orig_line;
+            $non_translated_line =~ s/_([^="]*)=/$1=/;
+            
+            print OUTPUT "${non_translated_line}\n";
+            
+            my $value_str = $orig_line;
+            $value_str =~ s/.*_\w+=(.*)/$1/;
+            
+            if ($value_str) {
+                my %value_translation_map = lookup_translations ($value_str);
+            
+                foreach my $key (sort keys %value_translation_map) {
+                    my $translation = $value_translation_map{$key};
+
+                    my $translated_line = $orig_line;  
+                    $translated_line =~ s/_([^="]*)=([^\n]*)/\[$key]$1=$translation/;
+                    print OUTPUT "$translated_line\n";
+                }
+            }
+        } else {
+            print OUTPUT "$_\n";
+        }
+    }
+                 
+    close OUTPUT;
+    close INPUT;
+}
+
+sub desktop_merge_translations
+{
+    open INPUT, "<${FILE}";
+
+    open OUTPUT, ">${OUTFILE}";
+
+    while (<INPUT>) {
+        chomp;
+        if (/^\s*_\w+=.*/)  {
+            my $orig_line = $_;
+
+            my $non_translated_line = $orig_line;
+            $non_translated_line =~ s/_([^="]*)=/$1=/;
+
+            print OUTPUT "${non_translated_line}\n";
+
+            my $value_str = $orig_line;
+            $value_str =~ s/.*_\w+=(.*)/$1/;
+
+            if ($value_str) {
+                my %value_translation_map = lookup_translations ($value_str);
+
+                foreach my $key (sort keys %value_translation_map) {
+                    my $translation = $value_translation_map{$key};
+
+                    my $translated_line = $orig_line;
+                    $translated_line =~ s/^_([^="]*)=([^\n]*)/$1\[$key]=$translation/;
+                    print OUTPUT "$translated_line\n";
+                }
+            }
+        } else {
+            print OUTPUT "$_\n";
+        }
+    }
+
+    close OUTPUT;
+    close INPUT;
+
+}
diff --git a/apps/silcer/xml-i18n-update.in b/apps/silcer/xml-i18n-update.in
new file mode 100644 (file)
index 0000000..9e5b09a
--- /dev/null
@@ -0,0 +1,516 @@
+#!@XML_I18N_TOOLS_PERL@ -w
+
+#  The GNOME Translation Update Tool
+#
+#  Copyright (C) 2000 Free Software Foundation.
+#
+#  This library is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License as
+#  published by the Free Software Foundation; either version 2 of the
+#  License, or (at your option) any later version.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#  General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this library; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#  Author(s): Kenneth Christiansen
+
+## Release information
+my $PROGRAM  = "xml-i18n-update";
+my $VERSION  = "0.9";
+my $_PACKAGE = "xml-i18n-tools";
+
+## Loaded modules
+use strict;
+use Getopt::Long;
+use Cwd;
+use File::Copy;
+use File::Find;
+
+## Scalars used by the option stuff
+my $LANG          = $ARGV[0];
+my $HELP_ARG      = "0";
+my $VERSION_ARG    = "0";
+my $DIST_ARG      = "0";
+my $POT_ARG       = "0";
+my $HEADERS_ARG    = "0";
+my $MAINTAIN_ARG   = "0";
+my $REPORT_ARG     = "0";
+my $VERBOSE       = "0";
+
+my @languages;
+my %po_files_by_lang = ();
+
+my $xml_extension = 
+"xml(\.in)*|".         # .in is not required
+"ui|".
+"glade(\.in)*|".       # .in is not required
+"desktop(\.in)+|".
+"directory(\.in)+|".
+"soundlist(\.in)+|".
+"keys(\.in)+|".
+"oaf(\.in)+|".
+"server(\.in)+|".
+"etspec|".
+"pong(\.in)+";
+
+my $PACKAGE = &find_package_name;
+
+## Always print as the first thing
+$| = 1;
+
+## Give error if script is run without an argument
+if (! $LANG){
+    print "${PROGRAM}:  missing file arguments\n";
+    print "Try `${PROGRAM} --help' for more information.\n";
+    exit;
+}
+
+## Handle options
+GetOptions (
+           "help|h|?"          => \$HELP_ARG,
+           "version|v"         => \$VERSION_ARG,
+           "dist|d"            => \$DIST_ARG,
+           "pot|p"             => \$POT_ARG,
+           "headers|s"         => \$HEADERS_ARG,
+           "maintain|m"        => \$MAINTAIN_ARG,
+           "report|r"          => \$REPORT_ARG,
+           "verbose|x"         => \$VERBOSE
+           ) or &invalid_option;
+
+
+## Use the supplied arguments
+## Check for options.
+## This section will check for the different options.
+
+sub split_on_argument {
+
+    if ($VERSION_ARG) {
+       &version;
+
+    } elsif ($HELP_ARG) {
+       &help;
+
+    } elsif ($DIST_ARG) {
+        &merging;
+        &status;
+
+    } elsif ($POT_ARG) {
+        &gen_headers;
+        &generate_pot;
+
+    } elsif ($HEADERS_ARG) {
+        &gen_headers;
+        exit;
+
+    } elsif ($MAINTAIN_ARG) {
+        &maintain;
+
+    } elsif ($REPORT_ARG) {
+        &show_status;
+
+    } elsif ($LANG) {
+       if ($LANG =~ /^-/){ ## not an option
+           &help;
+       } else {
+           &main;
+       }
+
+    } else {
+       &help;
+    }
+}
+
+&split_on_argument;
+
+sub main
+{
+   if(-s "$LANG.po"){
+       print "Working, please wait..." unless $VERBOSE;
+        &gen_headers;
+       &generate_pot;
+       &merging;
+       &status;
+   }
+
+   ## Report error if the language file supplied
+   ## to the command line is non-existent
+   else {
+       &not_existing;
+   }
+}
+
+sub determine_type($) {
+   my $type = $_;
+
+   my $gettext_type;
+
+   if ($type =~ /\[type: (gettext\/[^\]].*)]/) {
+        $gettext_type=$1;
+   }
+   elsif ($type =~ /(?:xml(\.in)*|ui|oaf(?:\.in)+|pong(?:\.in)+|etspec)$/) {
+        $gettext_type="gettext\/xml";
+   }
+   elsif ($type =~ /glade(\.in)*$/) {
+        $gettext_type="gettext\/glade";
+   }
+   elsif ($type =~ /(?:desktop(?:\.in)+|directory(?:\.in)+|soundlist(?:\.in)+)$/) {
+        $gettext_type="gettext\/ini";
+   }
+   elsif ($type =~ /keys(\.in)+$/) {
+        $gettext_type="gettext\/keys";
+   }
+   else { $gettext_type=""; }
+
+   return $gettext_type;
+}
+
+sub version{
+
+    ## Print version information
+    print "${PROGRAM} (${_PACKAGE}) $VERSION\n";
+    print "Written by Kenneth Christiansen <kenneth\@gnome.org>, 2000.\n\n";
+    print "Copyright (C) 2000 Free Software Foundation, Inc.\n";
+    print "This is free software; see the source for copying conditions.  There is NO\n";
+    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
+    exit;
+}
+
+sub help
+{
+    ## Print usage information
+    print "Usage: ./${PROGRAM} [OPTIONS] ...LANGCODE\n";
+    print "Updates pot files and merge them with the translations.\n\n";
+    print "  -H, --help                   shows this help page\n";
+    print "  -P, --pot                    generate the pot file only\n";
+    print "  -S, --headers                generate the XML headerfiles in POTFILES.in\n";
+    print "  -M, --maintain               search for missing files in POTFILES.in\n";
+    print "  -R, --report                 creates a status report for the module.\n";
+    print "  -X, --verbose                show lots of feedback\n";
+    print "  -V, --version                shows the version\n";
+    print "\nExamples of use:\n";
+    print "${PROGRAM} --pot    just creates a new pot file from the source\n";
+    print "${PROGRAM} da       created new pot file and updated the da.po file\n\n";
+    print "Report bugs to <kenneth\@gnome.org>.\n";
+    exit;
+}
+
+sub maintain
+{
+    my (@buf_i18n_plain,
+       @buf_i18n_xml,
+       @buf_potfiles,
+       @buf_potfiles_ignore,
+       @buf_allfiles,
+       @buf_allfiles_sorted,
+       @buf_potfiles_sorted
+    );
+
+    ## Search and find all translatable files
+    find sub { push @buf_i18n_plain, "$File::Find::name" if /\.(c|y|cc|c\+\+|h|gob)$/ }, "..";
+    find sub { push @buf_i18n_xml, "$File::Find::name" if /\.($xml_extension)$/ }, "..";
+
+    open(POTFILES, "POTFILES.in") || die "$PROGRAM:  there's no POTFILES.in!!!\n";
+    @buf_potfiles = <POTFILES>;
+
+    print "Searching for missing translatable files...\n";
+
+    ## Check if we should ignore some found files, when
+    ## comparing with POTFILES.in
+    if (-s "POTFILES.skip"){
+        open FILE, "POTFILES.skip";
+        while (<FILE>) {
+            if (/^[^#]/){
+                push @buf_potfiles_ignore, $_;
+            }
+        }
+        print "Found POTFILES.skip: Ignoring files...\n";
+        @buf_potfiles = (@buf_potfiles_ignore, @buf_potfiles);
+    }
+
+    foreach my $file (@buf_i18n_plain){
+        open FILE, "<$file";
+        while (<FILE>) {
+            if (/_\(\"/){
+               ## Remove the first 3 chars and add newline
+               push @buf_allfiles, unpack("x3 A*", $file) . "\n";
+                last;
+            }
+        }
+    }
+
+    foreach my $file (@buf_i18n_xml){
+        open FILE, "<$file";
+        while (<FILE>) {
+            if (/\s_(.*)=\"/){
+                ## Remove the first 3 chars and add newline
+                push @buf_allfiles, unpack("x3 A*", $file) . "\n";
+                last;
+            }
+        }
+    }
+
+    @buf_allfiles_sorted = sort (@buf_allfiles);
+    @buf_potfiles_sorted = sort (@buf_potfiles);
+
+    my %in2;
+    foreach (@buf_potfiles_sorted) {
+        $in2{$_} = 1;
+    }
+
+    my @result;
+
+    foreach (@buf_allfiles_sorted){
+        if (!exists($in2{$_})){
+            push @result, $_
+        }
+    }
+
+    ## Save file with information about the files missing
+    ## if any, and give information about this proceedier
+    if(@result){
+        open OUT, ">missing";
+        print OUT @result;
+        print "\nHere is the result:\n\n", @result, "\n";
+        print "The file \"missing\" has been placed in the current directory.\n";
+        print "Files supposed to be ignored should be placed in \"POTFILES.skip\"\n";
+    }
+
+    ## If there is nothing to complain about, notice the user
+    else{
+        print "\nWell, it's all perfect! Congratulation!\n";
+    }
+}
+
+sub invalid_option
+{
+    ## Handle invalid arguments
+    print "${PROGRAM}: invalid option -- $LANG\n";
+    print "Try `${PROGRAM} --help' for more information.\n";
+    exit 1;
+}
+
+sub gen_headers
+{
+    my $XML_I18N_EXTRACT = `which xml-i18n-extract 2>/dev/null`;
+    chomp $XML_I18N_EXTRACT;
+
+    $XML_I18N_EXTRACT = $ENV{"XML_I18N_EXTRACT"} if $ENV{"XML_I18N_EXTRACT"};
+
+    ## Generate the .h header files, so we can allow glade and
+    ## xml translation support
+    if (! -s $XML_I18N_EXTRACT)
+    {
+       print "\n *** The xml-i18n-extract script wasn't found!"
+            ."\n *** Without this xml-i18n-update can not generate files.\n";
+       exit;
+    }
+    else
+    {
+        open FILE, "<POTFILES.in";
+        while (<FILE>) {
+           chomp;
+
+           ## Find xml files in POTFILES.in and generate the
+           ## files with help from the xml-i18n-extract script
+
+          my $gettext_type=&determine_type($1);
+
+           if (/\.($xml_extension)$/ || /^\[/){
+              $_ =~ s/^\[[^\[].*]\s*//;
+               my $filename = "../$_";
+
+               if ($VERBOSE){
+                   system($XML_I18N_EXTRACT, "--update", "--type=$gettext_type", $filename);
+               } else {
+                  system($XML_I18N_EXTRACT, "--update", "--type=$gettext_type", "--quiet", $filename);
+               }
+           }
+       }
+       close FILE;
+   }
+}
+
+sub generate_pot
+{
+    ## Generate the potfiles from the POTFILES.in file
+
+    print "Building the $PACKAGE.pot...\n" if $VERBOSE;
+
+    move("POTFILES.in", "POTFILES.in.old");
+
+    open INFILE, "<POTFILES.in.old";
+    open OUTFILE, ">POTFILES.in";
+    while (<INFILE>) {
+        s/\.($xml_extension)$/$&.h/;
+        s/^\[.*]\s*(.*)/$1.h/;
+        print OUTFILE $_;
+    }
+    close OUTFILE;
+    close INFILE;
+
+    my $gettext_test   ="test \! -f $PACKAGE\.po \|\| \( rm -f \.\/$PACKAGE\.pot "
+                       ."&& mv $PACKAGE\.po \.\/$PACKAGE\.pot \)";
+
+    system("xgettext", "--default-domain\=$PACKAGE", "--directory\=\.\.",
+          "--add-comments", "--keyword\=\_", "--keyword\=N\_",
+          "--files-from\=\.\/POTFILES\.in");
+
+    system($gettext_test);
+
+    print "Wrote $PACKAGE.pot\n" if $VERBOSE;
+
+    move("POTFILES.in.old", "POTFILES.in");
+
+    print "Removing generated header (.h) files..." if $VERBOSE;
+
+    open FILE, "<POTFILES.in";
+    while (<FILE>)
+    {
+        chomp;
+        unlink "../$_.h" if /\.($xml_extension)$/;
+    }
+    close FILE;
+    print "done\n" if $VERBOSE;
+}
+
+sub merging
+{
+    if ($ARGV[1]){
+        $LANG   = $ARGV[1];
+    } else {
+       $LANG   = $ARGV[0];
+    }
+
+    if ($ARGV[0] ne "--dist" && $ARGV[0] ne "-D") {
+        print "Merging $LANG.po with $PACKAGE.pot..." if $VERBOSE;
+    }
+
+    &perform_merge($LANG);
+    ## Remove the "messages" trash file generated by gettext
+    unlink "messages";
+}
+
+sub perform_merge
+{
+    my ($LANG) = @_;
+
+    copy("$LANG.po", "$LANG.po.old") || die "copy failed: $!";
+
+    ## Preform merge
+    system("msgmerge", "$LANG.po.old", "$PACKAGE.pot", "-o", "$LANG.po");
+
+    ## Remove the backup file
+    unlink "$LANG.po.old";
+}
+
+sub not_existing
+{
+    ## Report error if supplied language file is non-existing
+    print "$PROGRAM:  sorry, $LANG.po does not exist!\n";
+    print "Try `$PROGRAM --help' for more information.\n";
+    exit;
+}
+
+sub gather_po_files
+{
+    my @po_files = glob("./*.po");
+
+    @languages = map (&po_file2lang, @po_files);
+
+    foreach my $lang (@languages) {
+       $po_files_by_lang{$lang} = shift (@po_files);
+    }
+}
+
+sub po_file2lang
+{
+    my $tmp = $_;
+    $tmp =~ s/^.*\/(.*)\.po$/$1/;
+    return $tmp;
+}
+
+sub status
+{
+    ## Print statistics
+    system("msgfmt", "--statistics", "$LANG.po");
+    print "\n";
+}
+
+sub show_status
+{
+    &gen_headers;
+    &generate_pot;
+    &gather_po_files;
+
+    foreach my $lang (@languages){
+       print "$lang: ";
+       &perform_merge($lang);
+    }
+
+    print "\n\n * Current translation support in $PACKAGE \n\n";
+
+    foreach my $lang (@languages){
+        print "$lang: ";
+       ## Print statistics
+       system("msgfmt", "--statistics", "$lang.po");
+    }
+}
+
+sub find_package_name
+{
+    my $base_dirname = getcwd();
+    $base_dirname =~ s@.*/@@;
+
+    my ($conf_in, $src_dir);
+
+    if ($base_dirname eq "po") {
+        if (-f "../configure.in") {
+            $conf_in = "../configure.in";
+        } else {
+           my $makefile_source;
+           local (*IN);
+           open (IN, "<Makefile") || die "can't open Makefile: $!";
+
+           while (<IN>) {
+               if (/^top_srcdir[ \t]*=/) {
+                   $src_dir = $_;
+                   # print "${src_dir}\n";
+
+                   $src_dir =~ s/^top_srcdir[ \t]*=[ \t]*([^ \t\n\r]*)/$1/;
+                   # print "${src_dir}\n";
+                   chomp $src_dir;
+                   $conf_in = "$src_dir" . "/configure.in" . "\n";
+                   last;
+               }
+           }
+           $conf_in || die "Cannot find top_srcdir in Makefile."
+        }
+
+        my $conf_source; {
+           local (*IN);
+           local $/; # slurp mode
+           open (IN, "<$conf_in") || die "can't open $conf_in: $!";
+           $conf_source = <IN>;
+        }
+
+        if ($conf_source =~ /AM_INIT_AUTOMAKE\(([^,]*),(.*)/) {
+            my $package_name = $1;
+            if ($package_name =~ /^[\$](.*)/){
+                if ($conf_source =~ /$1=(.*)/) {
+                    $package_name = $1;
+                }
+            }
+           return $package_name;
+        }
+    }
+
+    print "$PROGRAM: Unable to determine package name.\n" .
+         "Make sure to run this script inside the po directory.\n";
+    exit;
+}
index 1d27287d5bc6a06b62816cc19bcd2ca594c44933..e1b5871708010cf0962a293681cd941b915125e3 100755 (executable)
@@ -1,6 +1,7 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999
+#   Free Software Foundation, Inc.
 #
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -23,7 +24,7 @@
 
 # Written by Per Bothner <bothner@cygnus.com>.
 # The master version of this file is at the FSF in /home/gd/gnu/lib.
-# Please send patches to the Autoconf mailing list <autoconf@gnu.org>.
+# Please send patches to <autoconf-patches@gnu.org>.
 #
 # This script attempts to guess a canonical system name similar to
 # config.sub.  If it succeeds, it prints the system name on stdout, and
 # (but try to keep the structure clean).
 #
 
+# Use $HOST_CC if defined. $CC may point to a cross-compiler
+if test x"$CC_FOR_BUILD" = x; then
+  if test x"$HOST_CC" != x; then
+    CC_FOR_BUILD="$HOST_CC"
+  else
+    if test x"$CC" != x; then
+      CC_FOR_BUILD="$CC"
+    else
+      CC_FOR_BUILD=cc
+    fi
+  fi
+fi
+
+
 # This is needed to find uname on a Pyramid OSx when run in the BSD universe.
 # (ghazi@noc.rutgers.edu 8/24/94.)
 if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
@@ -78,7 +93,7 @@ main:
        ret \$31,(\$26),1
        .end main
 EOF
-       ${CC-cc} $dummy.s -o $dummy 2>/dev/null
+       $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
        if test "$?" = 0 ; then
                ./$dummy
                case "$?" in
@@ -100,7 +115,13 @@ EOF
                esac
        fi
        rm -f $dummy.s $dummy
-       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]`
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
        exit 0 ;;
     21064:Windows_NT:50:3)
        echo alpha-dec-winnt3.5
@@ -135,6 +156,9 @@ EOF
     wgrisc:OpenBSD:*:*)
        echo mipsel-unknown-openbsd${UNAME_RELEASE}
        exit 0 ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit 0 ;;
     arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
        echo arm-acorn-riscix${UNAME_RELEASE}
        exit 0;;
@@ -144,7 +168,7 @@ EOF
     SR2?01:HI-UX/MPP:*:*)
        echo hppa1.1-hitachi-hiuxmpp
        exit 0;;
-    Pyramid*:OSx*:*:*|MIS*:OSx*:*:*|MIS*:SMP_DC-OSx*:*:*)
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
        # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
        if test "`(/bin/universe) 2>/dev/null`" = att ; then
                echo pyramid-pyramid-sysv3
@@ -203,6 +227,32 @@ EOF
     atari*:OpenBSD:*:*)
        echo m68k-unknown-openbsd${UNAME_RELEASE}
        exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor 
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
     sun3*:NetBSD:*:*)
        echo m68k-sun-netbsd${UNAME_RELEASE}
        exit 0 ;;
@@ -236,7 +286,7 @@ EOF
     VAX*:ULTRIX*:*:*)
        echo vax-dec-ultrix${UNAME_RELEASE}
        exit 0 ;;
-    2020:CLIX:*:*)
+    2020:CLIX:*:* | 2430:CLIX:*:*)
        echo clipper-intergraph-clix${UNAME_RELEASE}
        exit 0 ;;
     mips:*:*:UMIPS | mips:*:*:RISCos)
@@ -260,7 +310,7 @@ EOF
          exit (-1);
        }
 EOF
-       ${CC-cc} $dummy.c -o $dummy \
+       $CC_FOR_BUILD $dummy.c -o $dummy \
          && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
          && rm $dummy.c $dummy && exit 0
        rm -f $dummy.c $dummy
@@ -281,15 +331,18 @@ EOF
     AViiON:dgux:*:*)
         # DG/UX returns AViiON for all architectures
         UNAME_PROCESSOR=`/usr/bin/uname -p`
-        if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
-       if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
-            -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
                echo m88k-dg-dgux${UNAME_RELEASE}
-       else
+           else
                echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
        fi
-        else echo i586-dg-dgux${UNAME_RELEASE}
-        fi
        exit 0 ;;
     M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
        echo m88k-dolphin-sysv3
@@ -326,7 +379,7 @@ EOF
                        exit(0);
                        }
 EOF
-               ${CC-cc} $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+               $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
                rm -f $dummy.c $dummy
                echo rs6000-ibm-aix3.2.5
        elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
@@ -374,7 +427,7 @@ EOF
        case "${UNAME_MACHINE}" in
            9000/31? )            HP_ARCH=m68000 ;;
            9000/[34]?? )         HP_ARCH=m68k ;;
-           9000/6?? | 9000/7?? | 9000/80[024] | 9000/8?[136790] | 9000/892 )
+           9000/[678][0-9][0-9])
               sed 's/^              //' << EOF >$dummy.c
               #include <stdlib.h>
               #include <unistd.h>
@@ -406,7 +459,7 @@ EOF
                   exit (0);
               }
 EOF
-       (${CC-cc} $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+       (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
        rm -f $dummy.c $dummy
        esac
        HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
@@ -438,7 +491,7 @@ EOF
          exit (0);
        }
 EOF
-       ${CC-cc} $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+       $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
        rm -f $dummy.c $dummy
        echo unknown-hitachi-hiuxwe2
        exit 0 ;;
@@ -448,10 +501,7 @@ EOF
     9000/8??:4.3bsd:*:*)
        echo hppa1.0-hp-bsd
        exit 0 ;;
-    *9??*:MPE*:*:*)
-       echo hppa1.0-hp-mpeix
-       exit 0 ;;
-    *9??*:MPE*:*:*)
+    *9??*:MPE/iX:*:*)
        echo hppa1.0-hp-mpeix
        exit 0 ;;
     hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
@@ -470,6 +520,9 @@ EOF
     parisc*:Lites*:*:*)
        echo hppa1.1-hp-lites
        exit 0 ;;
+    hppa*:OpenBSD:*:*)
+       echo hppa-unknown-openbsd
+       exit 0 ;;
     C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
        echo c1-convex-bsd
         exit 0 ;;
@@ -503,13 +556,13 @@ EOF
        echo t90-cray-unicos${UNAME_RELEASE}
        exit 0 ;;
     CRAY*T3E:*:*:*)
-       echo t3e-cray-unicosmk${UNAME_RELEASE}
+       echo alpha-cray-unicosmk${UNAME_RELEASE}
        exit 0 ;;
     CRAY-2:*:*:*)
        echo cray2-cray-unicos
         exit 0 ;;
     F300:UNIX_System_V:*:*)
-        FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
         FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
         echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
         exit 0 ;;
@@ -522,12 +575,12 @@ EOF
     hp300:OpenBSD:*:*)
        echo m68k-unknown-openbsd${UNAME_RELEASE}
        exit 0 ;;
-    sparc*:BSD/OS:*:*)
-       echo sparc-unknown-bsdi${UNAME_RELEASE}
-       exit 0 ;;
     i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
        echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
        exit 0 ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
     *:BSD/OS:*:*)
        echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
        exit 0 ;;
@@ -541,7 +594,7 @@ EOF
        echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
        exit 0 ;;
     *:NetBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+       echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*//'`
        exit 0 ;;
     *:OpenBSD:*:*)
        echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
@@ -552,6 +605,15 @@ EOF
     i*:MINGW*:*)
        echo ${UNAME_MACHINE}-pc-mingw32
        exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i386-pc-interix
+       exit 0 ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit 0 ;;
     p*:CYGWIN*:*)
        echo powerpcle-unknown-cygwin
        exit 0 ;;
@@ -562,16 +624,11 @@ EOF
        echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
        exit 0 ;;
     *:Linux:*:*)
-#      # uname on the ARM produces all sorts of strangeness, and we need to
-#      # filter it out.
-#      case "$UNAME_MACHINE" in
-#        armv*)                      UNAME_MACHINE=$UNAME_MACHINE ;;
-#        arm* | sa110*)              UNAME_MACHINE="arm" ;;
-#      esac
 
        # The BFD linker knows what the default object file format is, so
-       # first see if it will tell us.
-       ld_help_string=`ld --help 2>&1`
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       ld_help_string=`cd /; ld --help 2>&1`
        ld_supported_emulations=`echo $ld_help_string \
                         | sed -ne '/supported emulations:/!d
                                    s/[         ][      ]*/ /g
@@ -579,13 +636,70 @@ EOF
                                    s/ .*//
                                    p'`
         case "$ld_supported_emulations" in
-         i?86linux)  echo "${UNAME_MACHINE}-pc-linux-gnuaout"      ; exit 0 ;;
-         i?86coff)   echo "${UNAME_MACHINE}-pc-linux-gnucoff"      ; exit 0 ;;
-         sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
-         armlinux)   echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
-         m68klinux)  echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
-         elf32arm)   echo "${UNAME_MACHINE}-unknown-linux-gnu"     ; exit 0 ;;
-         elf32ppc)   echo "powerpc-unknown-linux-gnu"              ; exit 0 ;;
+         *ia64)
+               echo "${UNAME_MACHINE}-unknown-linux"
+               exit 0
+               ;;
+         i?86linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit 0
+               ;;
+         i?86coff)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit 0
+               ;;
+         sparclinux)
+               echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+               exit 0
+               ;;
+         armlinux)
+               echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+               exit 0
+               ;;
+         elf32arm*)
+               echo "${UNAME_MACHINE}-unknown-linux-gnu"
+               exit 0
+               ;;
+         armelf_linux*)
+               echo "${UNAME_MACHINE}-unknown-linux-gnu"
+               exit 0
+               ;;
+         m68klinux)
+               echo "${UNAME_MACHINE}-unknown-linux-gnuaout"
+               exit 0
+               ;;
+         elf32ppc)
+               # Determine Lib Version
+               cat >$dummy.c <<EOF
+#include <features.h>
+#if defined(__GLIBC__)
+extern char __libc_version[];
+extern char __libc_release[];
+#endif
+main(argc, argv)
+     int argc;
+     char *argv[];
+{
+#if defined(__GLIBC__)
+  printf("%s %s\n", __libc_version, __libc_release);
+#else
+  printf("unkown\n");
+#endif
+  return 0;
+}
+EOF
+               LIBC=""
+               $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null
+               if test "$?" = 0 ; then
+                       ./$dummy | grep 1\.99 > /dev/null
+                       if test "$?" = 0 ; then
+                               LIBC="libc1"
+                       fi
+               fi      
+               rm -f $dummy.c $dummy
+               echo powerpc-unknown-linux-gnu${LIBC}
+               exit 0
+               ;;
        esac
 
        if test "${UNAME_MACHINE}" = "alpha" ; then
@@ -607,7 +721,7 @@ EOF
                .end main
 EOF
                LIBC=""
-               ${CC-cc} $dummy.s -o $dummy 2>/dev/null
+               $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
                if test "$?" = 0 ; then
                        ./$dummy
                        case "$?" in
@@ -652,7 +766,7 @@ EOF
   return 0;
 }
 EOF
-         ${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+         $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
          rm -f $dummy.c $dummy
        else
          # Either a pre-BFD a.out linker (linux-gnuoldld)
@@ -695,7 +809,7 @@ EOF
   return 0;
 }
 EOF
-         ${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+         $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
          rm -f $dummy.c $dummy
        fi ;;
 # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.  earlier versions
@@ -712,10 +826,20 @@ EOF
        echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
        exit 0 ;;
     i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
        if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
-               echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit 0 ;;
+    i?86:*:5:7*)
+        # Fixed at (any) Pentium or better
+        UNAME_MACHINE=i586
+        if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then
+           echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION}
        else
-               echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+           echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
        fi
        exit 0 ;;
     i?86:*:3.2:*)
@@ -727,18 +851,15 @@ EOF
                (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
                (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
                        && UNAME_MACHINE=i586
+               (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
                echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
        else
                echo ${UNAME_MACHINE}-pc-sysv32
        fi
        exit 0 ;;
-    i?86:UnixWare:*:*)
-       if /bin/uname -X 2>/dev/null >/dev/null ; then
-         (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
-           && UNAME_MACHINE=i586
-       fi
-       echo ${UNAME_MACHINE}-unixware-${UNAME_RELEASE}-${UNAME_VERSION}
-       exit 0 ;;
     pc:*:*:*)
         # uname -m prints for DJGPP always 'pc', but it prints nothing about
         # the processor, so we play safe by assuming i386.
@@ -825,7 +946,7 @@ EOF
     news*:NEWS-OS:*:6*)
        echo mips-sony-newsos6
        exit 0 ;;
-    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R4000:UNIX_SV:*:*)
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
        if [ -d /usr/nec ]; then
                echo mips-nec-sysv${UNAME_RELEASE}
        else
@@ -853,6 +974,9 @@ EOF
     *:Rhapsody:*:*)
        echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
        exit 0 ;;
+    *:QNX:*:4*)
+       echo i386-qnx-qnx${UNAME_VERSION}
+       exit 0 ;;
 esac
 
 #echo '(No uname command or uname output not recognized.)' 1>&2
@@ -959,7 +1083,7 @@ main ()
 }
 EOF
 
-${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
 rm -f $dummy.c $dummy
 
 # Apollos put the system type in the environment.
index ecf770cea1c44bab8155178bee51c97c62c7432a..12ebc7875a338e7bd8966295ac129848d96f8dce 100755 (executable)
@@ -1,6 +1,10 @@
 #! /bin/sh
-# Configuration validation subroutine script, version 1.1.
-#   Copyright (C) 1991, 92-97, 1998 Free Software Foundation, Inc.
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-08-13'
+
 # This file is (in principle) common to ALL GNU software.
 # The presence of a machine in this file suggests that SOME GNU software
 # can handle that machine.  It does not imply ALL GNU software can.
@@ -25,6 +29,8 @@
 # configuration script generated by Autoconf, you may include it under
 # the same distribution terms that you use for the rest of that program.
 
+# Please send patches to <config-patches@gnu.org>.
+#
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
 # If it is invalid, we print an error message on stderr and exit with code 1.
 #      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
 # It is wrong to echo any other type of specification.
 
-if [ x$1 = x ]
-then
-       echo Configuration name missing. 1>&2
-       echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
-       echo "or     $0 ALIAS" 1>&2
-       echo where ALIAS is a recognized configuration type. 1>&2
-       exit 1
-fi
+me=`echo "$0" | sed -e 's,.*/,,'`
 
-# First pass through any local machine types.
-case $1 in
-       *local*)
-               echo $1
-               exit 0
-               ;;
-       *)
-       ;;
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
 esac
 
 # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
 # Here we must recognize all the valid KERNEL-OS combinations.
 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
-  linux-gnu*)
+  nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
     os=-$maybe_os
     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
     ;;
@@ -94,15 +143,33 @@ case $os in
        -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
        -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
        -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-       -apple)
+       -apple | -axis)
                os=
                basic_machine=$1
                ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
        -hiux*)
                os=-hiuxwe2
                ;;
        -sco5)
-               os=sco3.2v5
+               os=-sco3.2v5
                basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
                ;;
        -sco4)
@@ -121,6 +188,9 @@ case $os in
                os=-sco3.2v2
                basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
                ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
        -isc)
                os=-isc2.2
                basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
@@ -143,27 +213,59 @@ case $os in
        -psos*)
                os=-psos
                ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
 esac
 
 # Decode aliases for certain CPU-COMPANY combinations.
 case $basic_machine in
        # Recognize the basic CPU types without company name.
        # Some are omitted here because they have special meanings below.
-       tahoe | i860 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
-               | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
-               | 580 | i960 | h8300 | hppa | hppa1.0 | hppa1.1 | hppa2.0 \
-               | hppa2.0w \
-               | alpha | alphaev5 | alphaev56 | we32k | ns16k | clipper \
-               | i370 | sh | powerpc | powerpcle | 1750a | dsp16xx | pdp11 \
-               | mips64 | mipsel | mips64el | mips64orion | mips64orionel \
-               | mipstx39 | mipstx39el | armv[34][lb] \
-               | sparc | sparclet | sparclite | sparc64 | v850)
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+       | c4x | clipper \
+       | d10v | d30v | dsp16xx \
+       | fr30 \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | m32r | m68000 | m68k | m88k | mcore \
+       | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+       | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+       | mipsbe | mipsel | mipsle | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | ns16k | ns32k \
+       | openrisc \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | s390 | s390x \
+       | sh | sh[34] | sh[34]eb | shbe | shle \
+       | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+       | strongarm \
+       | tahoe | thumb | tic80 | tron \
+       | v850 \
+       | we32k \
+       | x86 | xscale \
+       | z8k)
                basic_machine=$basic_machine-unknown
                ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+
        # We use `pc' rather than `unknown'
        # because (1) that's what they normally are, and
        # (2) the word "unknown" tends to confuse beginning users.
-       i[34567]86)
+       i*86 | x86_64)
          basic_machine=$basic_machine-pc
          ;;
        # Object if more than one company name word.
@@ -172,28 +274,64 @@ case $basic_machine in
                exit 1
                ;;
        # Recognize the basic CPU types with company name.
-       vax-* | tahoe-* | i[34567]86-* | i860-* | m32r-* | m68k-* | m68000-* \
-             | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
-             | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
-             | power-* | none-* | 580-* | cray2-* | h8300-* | i960-* \
-             | xmp-* | ymp-* | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* \
-             | hppa2.0w-* \
-             | alpha-* | alphaev5-* | alphaev56-* | we32k-* | cydra-* \
-             | ns16k-* | pn-* | np1-* | xps100-* | clipper-* | orion-* \
-             | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
-             | sparc64-* | mips64-* | mipsel-* | armv[34][lb]-*\
-             | mips64el-* | mips64orion-* | mips64orionel-*  \
-             | mipstx39-* | mipstx39el-* \
-             | f301-* | armv*-*)
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alphapca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armv*-* \
+       | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c54x-* \
+       | clipper-* | cray2-* | cydra-* \
+       | d10v-* | d30v-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fr30-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | m32r-* \
+       | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | mcore-* \
+       | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+       | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipsel-* \
+       | mipsle-* | mipstx39-* | mipstx39el-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | s390-* | s390x-* \
+       | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+       | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+       | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* \
+       | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+       | v850-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \
+       | ymp-* \
+       | z8k-*)
                ;;
        # Recognize the various machine names and aliases which stand
        # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
        3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
                basic_machine=m68000-att
                ;;
        3b*)
                basic_machine=we32k-att
                ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
        alliant | fx80)
                basic_machine=fx80-alliant
                ;;
@@ -209,20 +347,24 @@ case $basic_machine in
                os=-sysv
                ;;
        amiga | amiga-*)
-               basic_machine=m68k-cbm
+               basic_machine=m68k-unknown
                ;;
        amigaos | amigados)
-               basic_machine=m68k-cbm
+               basic_machine=m68k-unknown
                os=-amigaos
                ;;
        amigaunix | amix)
-               basic_machine=m68k-cbm
+               basic_machine=m68k-unknown
                os=-sysv4
                ;;
        apollo68)
                basic_machine=m68k-apollo
                os=-sysv
                ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
        aux)
                basic_machine=m68k-apple
                os=-aux
@@ -259,13 +401,16 @@ case $basic_machine in
                basic_machine=cray2-cray
                os=-unicos
                ;;
-       [ctj]90-cray)
-               basic_machine=c90-cray
+       [cjt]90)
+               basic_machine=${basic_machine}-cray
                os=-unicos
                ;;
        crds | unos)
                basic_machine=m68k-crds
                ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
        da30 | da30-*)
                basic_machine=m68k-da30
                ;;
@@ -299,6 +444,10 @@ case $basic_machine in
        encore | umax | mmax)
                basic_machine=ns32k-encore
                ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
        fx2800)
                basic_machine=i860-alliant
                ;;
@@ -309,6 +458,10 @@ case $basic_machine in
                basic_machine=tron-gmicro
                os=-sysv
                ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
        h3050r* | hiux*)
                basic_machine=hppa1.1-hitachi
                os=-hiuxwe2
@@ -317,6 +470,14 @@ case $basic_machine in
                basic_machine=h8300-hitachi
                os=-hms
                ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
        harris)
                basic_machine=m88k-harris
                os=-sysv3
@@ -332,13 +493,30 @@ case $basic_machine in
                basic_machine=m68k-hp
                os=-hpux
                ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
        hp9k2[0-9][0-9] | hp9k31[0-9])
                basic_machine=m68000-hp
                ;;
        hp9k3[2-9][0-9])
                basic_machine=m68k-hp
                ;;
-       hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7)
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
                basic_machine=hppa1.1-hp
                ;;
        hp9k8[0-9][0-9] | hp8[0-9][0-9])
@@ -347,35 +525,42 @@ case $basic_machine in
        hppa-next)
                os=-nextstep3
                ;;
-       hp3k9[0-9][0-9] | hp9[0-9][0-9])
-               basic_machine=hppa1.0-hp
-               os=-mpeix
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
                ;;
-       hp3k9[0-9][0-9] | hp9[0-9][0-9])
-               basic_machine=hppa1.0-hp
-               os=-mpeix
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
                ;;
        i370-ibm* | ibm*)
                basic_machine=i370-ibm
-               os=-mvs
                ;;
 # I'm not sure what "Sysv32" means.  Should this be sysv3.2?
-       i[34567]86v32)
+       i*86v32)
                basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
                os=-sysv32
                ;;
-       i[34567]86v4*)
+       i*86v4*)
                basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
                os=-sysv4
                ;;
-       i[34567]86v)
+       i*86v)
                basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
                os=-sysv
                ;;
-       i[34567]86sol2)
+       i*86sol2)
                basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
                os=-solaris2
                ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
        iris | iris4d)
                basic_machine=mips-sgi
                case $os in
@@ -401,9 +586,17 @@ case $basic_machine in
                basic_machine=ns32k-utek
                os=-sysv
                ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
        miniframe)
                basic_machine=m68000-convergent
                ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
        mipsel*-linux*)
                basic_machine=mipsel-unknown
                os=-linux-gnu
@@ -418,12 +611,32 @@ case $basic_machine in
        mips3*)
                basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
                ;;
+       mmix*)
+               basic_machine=mmix-knuth
+               os=-mmixware
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
        ncr3000)
                basic_machine=i486-ncr
                os=-sysv4
                ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
        netwinder)
-               basic_machine=armv4l-corel
+               basic_machine=armv4l-rebel
                os=-linux
                ;;
        news | news700 | news800 | news900)
@@ -438,6 +651,10 @@ case $basic_machine in
                basic_machine=mips-sony
                os=-newsos
                ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
        next | m*-next )
                basic_machine=m68k-next
                case $os in
@@ -463,9 +680,32 @@ case $basic_machine in
                basic_machine=i960-intel
                os=-nindy
                ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
        np1)
                basic_machine=np1-gould
                ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
        pa-hitachi)
                basic_machine=hppa1.1-hitachi
                os=-hiuxwe2
@@ -483,28 +723,28 @@ case $basic_machine in
         pc532 | pc532-*)
                basic_machine=ns32k-pc532
                ;;
-       pentium | p5 | k5 | nexen)
+       pentium | p5 | k5 | k6 | nexgen)
                basic_machine=i586-pc
                ;;
-       pentiumpro | p6 | k6 | 6x86)
+       pentiumpro | p6 | 6x86 | athlon)
                basic_machine=i686-pc
                ;;
        pentiumii | pentium2)
-               basic_machine=i786-pc
+               basic_machine=i686-pc
                ;;
-       pentium-* | p5-* | k5-* | nexen-*)
+       pentium-* | p5-* | k5-* | k6-* | nexgen-*)
                basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
                ;;
-       pentiumpro-* | p6-* | k6-* | 6x86-*)
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
                basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
                ;;
        pentiumii-* | pentium2-*)
-               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
                ;;
        pn)
                basic_machine=pn-gould
                ;;
-       power)  basic_machine=rs6000-ibm
+       power)  basic_machine=power-ibm
                ;;
        ppc)    basic_machine=powerpc-unknown
                ;;
@@ -516,15 +756,37 @@ case $basic_machine in
        ppcle-* | powerpclittle-*)
                basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
                ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
        ps2)
                basic_machine=i386-ibm
                ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
        rm[46]00)
                basic_machine=mips-siemens
                ;;
        rtpc | rtpc-*)
                basic_machine=romp-ibm
                ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
        sequent)
                basic_machine=i386-sequent
                ;;
@@ -532,6 +794,10 @@ case $basic_machine in
                basic_machine=sh-hitachi
                os=-hms
                ;;
+       sparclite-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
        sps7)
                basic_machine=m68k-bull
                os=-sysv2
@@ -539,6 +805,13 @@ case $basic_machine in
        spur)
                basic_machine=spur-unknown
                ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
        sun2)
                basic_machine=m68000-sun
                ;;
@@ -579,10 +852,22 @@ case $basic_machine in
        sun386 | sun386i | roadrunner)
                basic_machine=i386-sun
                ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
        symmetry)
                basic_machine=i386-sequent
                os=-dynix
                ;;
+       t3e)
+               basic_machine=t3e-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
        tx39)
                basic_machine=mipstx39-unknown
                ;;
@@ -600,6 +885,10 @@ case $basic_machine in
                basic_machine=a29k-nyu
                os=-sym1
                ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
        vaxv)
                basic_machine=vax-dec
                os=-sysv
@@ -623,6 +912,18 @@ case $basic_machine in
                basic_machine=a29k-wrs
                os=-vxworks
                ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       windows32)
+               basic_machine=i386-pc
+               os=-windows32-msvcrt
+               ;;
        xmp)
                basic_machine=xmp-cray
                os=-unicos
@@ -630,6 +931,10 @@ case $basic_machine in
         xps | xps100)
                basic_machine=xps100-honeywell
                ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
        none)
                basic_machine=none-none
                os=-none
@@ -637,6 +942,15 @@ case $basic_machine in
 
 # Here we handle the default manufacturer of certain CPU types.  It is in
 # some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
        mips)
                if [ x$os = x-linux-gnu ]; then
                        basic_machine=mips-unknown
@@ -653,13 +967,20 @@ case $basic_machine in
        vax)
                basic_machine=vax-dec
                ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
        pdp11)
                basic_machine=pdp11-dec
                ;;
        we32k)
                basic_machine=we32k-att
                ;;
-       sparc)
+       sh3 | sh4 | sh3eb | sh4eb)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv9 | sparcv9b)
                basic_machine=sparc-sun
                ;;
         cydra)
@@ -671,6 +992,19 @@ case $basic_machine in
        orion105)
                basic_machine=clipper-highlevel
                ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       c4x*)
+               basic_machine=c4x-none
+               os=-coff
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
        *)
                echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
                exit 1
@@ -724,14 +1058,37 @@ case $os in
              | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
              | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
              | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
-             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \
+             | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
              | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
              | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
              | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -rhapsody* \
-             | -openstep* | -mpeix* | -oskit*)
+             | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos*)
        # Remember, each alternative MUST END IN *, to match a version number.
                ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto*)
+               os=-nto-qnx
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
        -linux*)
                os=`echo $os | sed -e 's|linux|linux-gnu|'`
                ;;
@@ -741,6 +1098,12 @@ case $os in
        -sunos6*)
                os=`echo $os | sed -e 's|sunos6|solaris3|'`
                ;;
+       -opened*)
+               os=-openedition
+               ;;
+       -wince*)
+               os=-wince
+               ;;
        -osfrose*)
                os=-osfrose
                ;;
@@ -756,12 +1119,18 @@ case $os in
        -acis*)
                os=-aos
                ;;
+       -386bsd)
+               os=-bsd
+               ;;
        -ctix* | -uts*)
                os=-sysv
                ;;
        -ns2 )
                os=-nextstep2
                ;;
+       -nsk*)
+               os=-nsk
+               ;;
        # Preserve the version number of sinix5.
        -sinix5.*)
                os=`echo $os | sed -e 's|sinix|sysv|'`
@@ -787,9 +1156,18 @@ case $os in
        # This must come after -sysvr4.
        -sysv*)
                ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
        -xenix)
                os=-xenix
                ;;
+        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
        -none)
                ;;
        *)
@@ -815,12 +1193,15 @@ case $basic_machine in
        *-acorn)
                os=-riscix1.2
                ;;
-       arm*-corel)
+       arm*-rebel)
                os=-linux
                ;;
        arm*-semi)
                os=-aout
                ;;
+       pdp10-*)
+               os=-tops20
+               ;;
         pdp11-*)
                os=-none
                ;;
@@ -839,6 +1220,15 @@ case $basic_machine in
                # default.
                # os=-sunos4
                ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
        *-tti)  # must be before sparc entry or we get the wrong os.
                os=-sysv3
                ;;
@@ -851,6 +1241,15 @@ case $basic_machine in
        *-ibm)
                os=-aix
                ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
        *-hp)
                os=-hpux
                ;;
@@ -911,9 +1310,21 @@ case $basic_machine in
        *-masscomp)
                os=-rtu
                ;;
-       f301-fujitsu)
+       f30[01]-fujitsu | f700-fujitsu)
                os=-uxpv
                ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
        *)
                os=-none
                ;;
@@ -935,10 +1346,10 @@ case $basic_machine in
                        -aix*)
                                vendor=ibm
                                ;;
-                       -hpux*)
-                               vendor=hp
+                       -beos*)
+                               vendor=be
                                ;;
-                       -mpeix*)
+                       -hpux*)
                                vendor=hp
                                ;;
                        -mpeix*)
@@ -959,7 +1370,7 @@ case $basic_machine in
                        -genix*)
                                vendor=ns
                                ;;
-                       -mvs*)
+                       -mvs* | -opened*)
                                vendor=ibm
                                ;;
                        -ptx*)
@@ -971,9 +1382,29 @@ case $basic_machine in
                        -aux*)
                                vendor=apple
                                ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
                esac
                basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
                ;;
 esac
 
 echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure.in b/configure.in
deleted file mode 100644 (file)
index a138211..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-#  configure.in
-#
-#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-#
-#  Copyright (C) 2000 Pekka Riikonen
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-
-AC_INIT(includes/version.h)
-
-# Compiler settings
-CFLAGS="-Wall $CFLAGS"
-
-#
-# Put here any platform specific stuff
-#
-AC_CANONICAL_SYSTEM
-case "$target" in
-  *-*-linux*|*-*-mklinux*)
-    CFLAGS="-D_GNU_SOURCE $CFLAGS"
-    ;;
-  *)
-    ;;
-esac
-
-AM_INIT_AUTOMAKE(silc, 28062000)
-AC_PREREQ(2.3)
-AM_CONFIG_HEADER(includes/silcdefs.h)
-
-AC_PROG_CC
-AC_C_INLINE
-AC_C_CONST
-AC_ARG_PROGRAM
-
-AC_PROG_LN_S
-AC_SUBST(LN_S)
-
-# XXX
-# Compiler flags
-if test "$GCC"; then
-  CFLAGS="-finline-functions $CFLAGS"
-else
-  # Currently GCC is only supported compiler
-  AC_MSG_ERROR(GCC is only supported compiler currently)
-fi
-
-# Program checking
-AC_PROG_INSTALL
-AC_PROG_RANLIB
-AC_PROG_MAKE_SET
-
-# Header checking
-AC_HEADER_STDC
-AC_HEADER_TIME
-AC_HEADER_STAT
-
-# More header checking
-AC_CHECK_HEADERS(unistd.h string.h getopt.h errno.h fcntl.h assert.h)
-AC_CHECK_HEADERS(sys/types.h sys/stat.h sys/time.h)
-AC_CHECK_HEADERS(netinet/in.h netinet/tcp.h netdb.h)
-AC_CHECK_HEADERS(pwd.h grp.h termcap.h)
-AC_CHECK_HEADERS(ncurses.h signal.h ctype.h)
-AC_CHECK_HEADERS(arpa/inet.h sys/mman.h)
-
-# Data type checking
-AC_TYPE_SIGNAL
-AC_TYPE_SIZE_T
-AC_TYPE_MODE_T
-AC_TYPE_UID_T
-AC_TYPE_PID_T
-
-# Function checking
-AC_CHECK_FUNCS(chmod stat fstat getenv putenv strerror ctime gettimeofday)
-AC_CHECK_FUNCS(getpid getgid getsid getpgid getpgrp getuid)
-AC_CHECK_FUNCS(strchr strstr strcpy strncpy memcpy memset memmove)
-AC_CHECK_FUNCS(gethostname gethostbyname gethostbyaddr)
-AC_CHECK_FUNCS(select socket listen bind shutdown close connect)
-AC_CHECK_FUNCS(fcntl setsockopt)
-AC_CHECK_FUNCS(getservbyname getservbyport)
-AC_CHECK_FUNCS(getopt_long time)
-AC_CHECK_FUNCS(mlock munlock)
-
-# SIM support checking
-# XXX These needs to be changed as more supported platforms appear.
-# XXX This probably needs to be made platform dependant check.
-AC_CHECKING(for SIM support)
-AC_CHECK_HEADERS(dlfcn.h, 
-  AC_CHECK_LIB(dl, dlopen, 
-    AC_DEFINE(SILC_SIM) 
-    AC_MSG_RESULT(enabled SIM support)
-    LIBS="$LIBS -ldl",
-    AC_MSG_RESULT(no SIM support found)),
-  AC_MSG_RESULT(no SIM support found))
-
-# Debug checking
-AC_MSG_CHECKING(for enabled debugging)
-AC_ARG_ENABLE(debug,
-[  --enable-debug          Enable debugging (warning: it is heavy!)],
-[ case "${enableval}" in
-  yes) 
-    AC_MSG_RESULT(yes)
-    AC_DEFINE(SILC_DEBUG)
-    CFLAGS="-O -g $CFLAGS"
-    ;;
-  *)
-    AC_MSG_RESULT(no)
-    CFLAGS="-O2 $CFLAGS"
-    ;;
-esac ], CFLAGS="-O2 $CFLAGS"
-        AC_MSG_RESULT(no))
-
-# XXX
-#LIBS="$LIBS -lefence"
-
-AC_ARG_WITH(silcd-config-file,
-[  --with-silcd-config-file[=PATH]
-                          Use PATH as default configuration file in SILC
-                          server.],
-[ AC_DEFINE_UNQUOTED(SILC_SERVER_CONFIG_FILE, "$withval") ])
-
-# Other configure scripts
-#AC_CONFIG_SUBDIRS(lib/zlib)
-AC_CONFIG_SUBDIRS(lib/silcmath/gmp-3.0.1)
-
-AC_OUTPUT( \
-Makefile
-doc/Makefile
-includes/Makefile
-lib/Makefile
-lib/silccore/Makefile
-lib/silccrypt/Makefile
-lib/silcmath/Makefile
-lib/silcsim/Makefile
-lib/silcsim/modules/Makefile
-lib/silcske/Makefile
-silc/Makefile
-silcd/Makefile)
diff --git a/configure.in.pre b/configure.in.pre
new file mode 100644 (file)
index 0000000..8472feb
--- /dev/null
@@ -0,0 +1,827 @@
+#
+#  configure.in
+#
+#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#
+#  Copyright (C) 2000 - 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AC_INIT(includes/version.h)
+
+#
+# Put here any platform specific stuff
+#
+AC_CANONICAL_SYSTEM
+case "$target" in
+  *-*-*bsd*)
+    check_threads=false
+    ;;
+  *)
+    check_threads=true
+    ;;
+esac
+
+# ./prepare script will automatically put the correct version. Do not edit!
+AM_INIT_AUTOMAKE(SILC_PACKAGE, SILC_VERSION)
+AC_PREREQ(2.52)
+AM_CONFIG_HEADER(includes/silcdefs.h)
+
+AC_PROG_CC
+AC_C_INLINE
+AC_C_CONST
+
+AC_PROG_LN_S
+AC_SUBST(LN_S)
+
+# Distribution definition. ./prepare will automatically add here a correct
+# value. Do not edit!
+silc_dist=SILC_PACKAGE
+AC_DEFINE(SILC_DIST_DEFINE)
+
+# XXX
+# Compiler flags
+if test "$GCC"; then
+  CFLAGS="-Wall -finline-functions $CFLAGS"
+fi
+
+# Program checking
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PROG_MAKE_SET
+AC_PROG_LIBTOOL
+
+# Header checking
+AC_HEADER_STDC
+AC_HEADER_TIME
+AC_HEADER_STAT
+
+# More header checking
+AC_CHECK_HEADERS(unistd.h string.h getopt.h errno.h fcntl.h assert.h)
+AC_CHECK_HEADERS(sys/types.h sys/stat.h sys/time.h stddef.h)
+AC_CHECK_HEADERS(netinet/in.h netinet/tcp.h xti.h netdb.h)
+AC_CHECK_HEADERS(pwd.h grp.h termcap.h paths.h)
+AC_CHECK_HEADERS(ncurses.h signal.h ctype.h regex.h)
+AC_CHECK_HEADERS(arpa/inet.h sys/mman.h limits.h)
+
+# Data type checking
+AC_TYPE_SIGNAL
+AC_TYPE_SIZE_T
+AC_TYPE_MODE_T
+AC_TYPE_UID_T
+AC_TYPE_PID_T
+
+AC_CHECK_SIZEOF(long long, 0)
+AC_DEFINE_UNQUOTED(SILC_SIZEOF_LONG_LONG, $ac_cv_sizeof_long_long)
+AC_CHECK_SIZEOF(long, 0)
+AC_DEFINE_UNQUOTED(SILC_SIZEOF_LONG, $ac_cv_sizeof_long)
+AC_CHECK_SIZEOF(int, 0)
+AC_DEFINE_UNQUOTED(SILC_SIZEOF_INT, $ac_cv_sizeof_int)
+AC_CHECK_SIZEOF(short, 0)
+AC_DEFINE_UNQUOTED(SILC_SIZEOF_SHORT, $ac_cv_sizeof_short)
+AC_CHECK_SIZEOF(char, 0)
+AC_DEFINE_UNQUOTED(SILC_SIZEOF_CHAR, $ac_cv_sizeof_char)
+AC_CHECK_SIZEOF(void *, 0)
+AC_DEFINE_UNQUOTED(SILC_SIZEOF_VOID_P, $ac_cv_sizeof_void_p)
+
+dnl Curses detection: Munged from Midnight Commander's configure.in
+AC_DEFUN(AC_CHECK_CURSES,[
+       search_ncurses=true
+       screen_manager=""
+       has_curses=false
+
+       CFLAGS=${CFLAGS--O}
+
+       AC_SUBST(CURSES_LIBS)
+       AC_SUBST(CURSES_INCLUDEDIR)
+
+       AC_ARG_WITH(sunos-curses,
+         [  --with-sunos-curses     Used to force SunOS 4.x curses],[
+         if test x$withval = xyes; then
+               AC_USE_SUNOS_CURSES
+         fi
+       ])
+
+       AC_ARG_WITH(osf1-curses,
+         [  --with-osf1-curses      Used to force OSF/1 curses],[
+         if test x$withval = xyes; then
+               AC_USE_OSF1_CURSES
+         fi
+       ])
+
+       AC_ARG_WITH(vcurses,
+         [  --with-vcurses[=incdir] Used to force SysV curses],
+         if test x$withval != xyes; then
+               CURSES_INCLUDEDIR="-I$withval"
+         fi
+         AC_USE_SYSV_CURSES
+       )
+
+       AC_ARG_WITH(ncurses,
+         [  --with-ncurses[=dir]    Compile with ncurses/locate base dir],
+         if test x$withval = xno ; then
+               search_ncurses=false
+         elif test x$withval != xyes ; then
+               AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, "ncurses on $withval/include")
+         fi
+       )
+
+       if $search_ncurses
+       then
+               AC_SEARCH_NCURSES()
+       fi
+])
+
+
+AC_DEFUN(AC_USE_SUNOS_CURSES, [
+       search_ncurses=false
+       screen_manager="SunOS 4.x /usr/5include curses"
+       AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
+       AC_DEFINE(USE_SUNOS_CURSES)
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(NO_COLOR_CURSES)
+       AC_DEFINE(USE_SYSV_CURSES)
+       CURSES_INCLUDEDIR="-I/usr/5include"
+       CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
+       AC_MSG_RESULT(Please note that some screen refreshs may fail)
+])
+
+AC_DEFUN(AC_USE_OSF1_CURSES, [
+       AC_MSG_RESULT(Using OSF1 curses)
+       search_ncurses=false
+       screen_manager="OSF1 curses"
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(NO_COLOR_CURSES)
+       AC_DEFINE(USE_SYSV_CURSES)
+       CURSES_LIBS="-lcurses"
+])
+
+AC_DEFUN(AC_USE_SYSV_CURSES, [
+       AC_MSG_RESULT(Using SysV curses)
+       AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       AC_DEFINE(USE_SYSV_CURSES)
+       search_ncurses=false
+       screen_manager="SysV/curses"
+       CURSES_LIBS="-lcurses"
+])
+
+dnl
+dnl Parameters: directory filename cureses_LIBS curses_INCLUDEDIR nicename
+dnl
+AC_DEFUN(AC_NCURSES, [
+    if $search_ncurses
+    then
+        if test -f $1/$2
+       then
+           AC_MSG_RESULT(Found ncurses on $1/$2)
+
+           CURSES_LIBS="$3"
+           AC_CHECK_LIB(ncurses, initscr, , [
+                CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
+               AC_CHECK_LIB(curses, initscr, [
+                       CURSES_LIBS="$CHECKLIBS"
+               ],, $CHECKLIBS)
+           ], $CURSES_LIBS)
+           CURSES_INCLUDEDIR="$4"
+           search_ncurses=false
+           screen_manager=$5
+            AC_DEFINE(HAS_CURSES)
+            has_curses=true
+           has_ncurses=true
+           AC_DEFINE(USE_NCURSES)
+       fi
+    fi
+])
+
+AC_DEFUN(AC_SEARCH_NCURSES, [
+    AC_CHECKING("location of ncurses.h file")
+
+    AC_NCURSES(/usr/include, ncurses.h, -lncurses,, "ncurses on /usr/include")
+    AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses, "ncurses on /usr/include/ncurses")
+    AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include, "ncurses on /usr/local")
+    AC_NCURSES(/usr/pkg/include, ncurses.h, -L/usr/pkg/lib -lncurses, -I/usr/pkg/include, "ncurses on /usr/pkg")
+    AC_NCURSES(/usr/contrib/include, ncurses.h, -L/usr/contrib/lib -lncurses, -I/usr/contrib/include, "ncurses on /usr/contrib")
+    AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -L/usr/local/lib/ncurses -lncurses, -I/usr/local/include/ncurses, "ncurses on /usr/local/include/ncurses")
+
+    AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/local/.../ncurses")
+
+    AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES, "renamed ncurses on /usr/include/ncurses")
+
+    dnl
+    dnl We couldn't find ncurses, try SysV curses
+    dnl
+    if $search_ncurses 
+    then
+        AC_EGREP_HEADER(init_color, /usr/include/curses.h,
+           AC_USE_SYSV_CURSES)
+       AC_EGREP_CPP(USE_NCURSES,[
+#include <curses.h>
+#ifdef __NCURSES_H
+#undef USE_NCURSES
+USE_NCURSES
+#endif
+],[
+       CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
+        AC_DEFINE(HAS_CURSES)
+       has_curses=true
+       has_ncurses=true
+        AC_DEFINE(USE_NCURSES)
+        search_ncurses=false
+        screen_manager="ncurses installed as curses"
+])
+    fi
+
+    dnl
+    dnl Try SunOS 4.x /usr/5{lib,include} ncurses
+    dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
+    dnl should be replaced by a more fine grained selection routine
+    dnl
+    if $search_ncurses
+    then
+       if test -f /usr/5include/curses.h
+       then
+           AC_USE_SUNOS_CURSES
+        fi
+    fi
+
+    dnl use whatever curses there happens to be
+    if $search_ncurses
+    then
+       if test -f /usr/include/curses.h
+       then
+         CURSES_LIBS="-lcurses"
+         AC_DEFINE(HAS_CURSES)
+         has_curses=true
+         search_ncurses=false
+         screen_manager="curses"
+       fi
+    fi
+])
+
+AC_CHECK_CURSES
+LIBS="$LIBS $CURSES_LIBS"
+
+# Function and library checking
+AC_CHECK_FUNC(gethostbyname, ac_gethostbyname_found=1,
+ac_gethostbyname_found=0)
+if test x$ac_gethostbyname_found = x0; then
+    AC_CHECK_LIB(nsl, gethostbyname, LIBS="$LIBS -lnsl")
+    AC_CHECK_FUNC(res_gethostbyname, ac_res_ghbn_found=1, ac_res_ghbn_found=0)
+    if test x$ac_res_ghbn_found = x0; then
+        AC_CHECK_LIB(resolv, res_gethostbyname,  LIBS="$LIBS -lresolv")
+    fi
+fi
+AC_CHECK_FUNC(socket, ac_socket_found=1, ac_socket_found=0)
+if test x$ac_socket_found = x0; then
+    AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket")
+fi
+AC_CHECK_FUNCS(gethostname gethostbyaddr getservbyname getservbyport)
+AC_CHECK_FUNCS(select listen bind shutdown close connect)
+AC_CHECK_FUNCS(fcntl setsockopt)
+AC_CHECK_FUNCS(getopt_long time)
+AC_CHECK_FUNCS(chmod stat fstat getenv putenv strerror ctime gettimeofday)
+AC_CHECK_FUNCS(getpid getgid getsid getpgid getpgrp getuid setgroups initgroups)
+AC_CHECK_FUNCS(strchr strstr strcpy strncpy memcpy memset memmove)
+AC_CHECK_FUNCS(pthread_create)
+
+# SIM support checking
+# XXX These needs to be changed as more supported platforms appear.
+# XXX This probably needs to be made platform dependant check.
+sim_support=false
+AM_CONDITIONAL(SILC_SIM, test x$sim_support = xtrue)
+AC_CHECKING(for SIM support)
+AC_CHECK_HEADERS(dlfcn.h, 
+  AC_CHECK_LIB(dl, dlopen, 
+    AC_DEFINE(SILC_SIM) 
+    sim_support=true
+    AM_CONDITIONAL(SILC_SIM, test x$sim_support = xtrue)
+    AC_MSG_RESULT(enabled SIM support)
+    LIBS="$LIBS -ldl",
+    AC_MSG_RESULT(no SIM support found)),
+  AC_MSG_RESULT(no SIM support found))
+
+#
+# Installation
+#
+
+# Default installation destination
+AC_PREFIX_DEFAULT(/usr/local/silc)
+
+if test "x$prefix" != xNONE; then
+       silc_prefix="$prefix"
+else
+       silc_prefix="$ac_default_prefix"
+fi
+
+# etc directory
+#ETCDIR="/etc/silc"
+if test "x$sysconfdir" != 'x${prefix}/etc'; then
+       ETCDIR="$sysconfdir"
+else
+       ETCDIR="$silc_prefix/etc"
+fi
+AC_ARG_WITH(etcdir,
+[  --with-etcdir[=PATH]    Directory for system files [/etc/silc]],
+[ case "$withval" in
+  no)
+    ;;
+  yes)
+    ETCDIR="$withval"
+    ;;
+  *)
+    ETCDIR="$withval"
+    ;;
+  esac ],
+)
+AC_SUBST(ETCDIR)
+AC_DEFINE_UNQUOTED(SILC_ETCDIR, "$ETCDIR")
+
+# help directory
+#HELPDIR="help"
+HELPDIR="$silc_prefix/help"
+AC_ARG_WITH(helpdir,
+[  --with-helpdir[=PATH]   Directory for SILC help files [PREFIX/help]],
+[ case "$withval" in
+  no)
+    ;;
+  yes)
+    HELPDIR="$withval"
+    ;;
+  *)
+    HELPDIR="$withval"
+    ;;
+  esac ],
+)
+AC_SUBST(HELPDIR)
+AC_DEFINE_UNQUOTED(SILC_HELPDIR, "$HELPDIR")
+
+# doc directory
+#DOCDIR="doc"
+DOCDIR="$silc_prefix/doc"
+AC_ARG_WITH(docdir,
+[  --with-docdir[=PATH]    Directory for SILC documentation [PREFIX/doc]],
+[ case "$withval" in
+  no)
+    ;;
+  yes)
+    DOCDIR="$withval"
+    ;;
+  *)
+    DOCDIR="$withval"
+    ;;
+  esac ],
+)
+AC_SUBST(DOCDIR)
+AC_DEFINE_UNQUOTED(SILC_DOCDIR, "$DOCDIR")
+
+# SIM modules directory
+#MODULESDIR="modules"
+MODULESDIR="$silc_prefix/modules"
+AC_ARG_WITH(simdir,
+[  --with-simdir[=PATH]    Directory for SIM modules [PREFIX/modules]],
+[ case "$withval" in
+  no)
+    ;;
+  yes)
+    MODULESDIR="$withval"
+    ;;
+  *)
+    MODULESDIR="$withval"
+    ;;
+  esac ],
+)
+AC_SUBST(MODULESDIR)
+AC_DEFINE_UNQUOTED(SILC_MODULESDIR, "$MODULESDIR")
+
+# Logs directory
+#LOGSDIR="logs"
+LOGSDIR="$silc_prefix/logs"
+AC_ARG_WITH(logsdir,
+[  --with-logsdir[=PATH]   Directory for Server logs [PREFIX/logs]],
+[ case "$withval" in
+  no)
+    ;;
+  yes)
+    LOGSDIR="$withval"
+    ;;
+  *)
+    LOGSDIR="$withval"
+    ;;
+  esac ],
+)
+AC_SUBST(LOGSDIR)
+AC_DEFINE_UNQUOTED(SILC_LOGSDIR, "$LOGSDIR")
+
+# SOCKS4 support checking
+AC_MSG_CHECKING(whether to support SOCKS4)
+AC_ARG_WITH(socks4,
+[  --with-socks4[=PATH]    Compile with SOCKS4 support],
+[ case "$withval" in
+  no)
+    AC_MSG_RESULT(no)
+    ;;
+  *)
+    AC_MSG_RESULT(yes)
+    socks=4
+
+    if test -d "$withval/include"; then
+      CFLAGS="$CFLAGS -I$withval/include"
+    else
+      CFLAGS="$CFLAGS -I$withval"
+    fi
+    if test -d "$withval/lib"; then
+      withval="-L$withval/lib -lsocks"
+    else
+      withval="-L$withval -lsocks"
+    fi
+
+    LIBS="$withval $LIBS"
+
+    AC_TRY_LINK([],
+                [ Rconnect(); ],
+                [],
+                [ AC_MSG_ERROR(Could not find SOCKS4 library.)])
+      ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)   
+
+# SOCKS5 support checking
+AC_MSG_CHECKING(whether to support SOCKS5)
+AC_ARG_WITH(socks5,
+[  --with-socks5[=PATH]    Compile with SOCKS5 support],
+[ case "$withval" in
+  no)
+    AC_MSG_RESULT(no)
+    ;;
+  *)
+    AC_MSG_RESULT(yes)
+    socks=5
+
+    if test -d "$withval/include"; then
+      CFLAGS="$CFLAGS -I$withval/include"
+    else
+      CFLAGS="$CFLAGS -I$withval"
+    fi
+    if test -d "$withval/lib"; then
+      withval="-L$withval/lib -lsocks5"
+    else
+      withval="-L$withval -lsocks5"
+    fi 
+
+    LIBS="$withval $LIBS"
+
+    AC_TRY_LINK([],
+                [ SOCKSconnect(); ],
+                [],
+                [ AC_MSG_ERROR(Could not find SOCKS5 library.)])
+      ;;
+  esac ],
+  AC_MSG_RESULT(no)
+)   
+
+if test "x$socks" = "x4"; then
+  AC_DEFINE(SOCKS)
+  CFLAGS="$CFLAGS -Dconnect=Rconnect -Dgetsockname=Rgetsockname -Dbind=Rbind -Daccept=Raccept -Dlisten=Rlisten -Dselect=Rselect"
+fi
+
+if test "x$socks" = "x5"; then
+  AC_DEFINE(SOCKS)
+  AC_DEFINE(SOCKS5)
+  AC_DEFINE(Rconnect, SOCKSconnect)
+  AC_DEFINE(Rgetsockname, SOCKSgetsockname)
+  AC_DEFINE(Rgetpeername, SOCKSgetpeername)
+  AC_DEFINE(Rbind, SOCKSbind)
+  AC_DEFINE(Raccept, SOCKSaccept)
+  AC_DEFINE(Rlisten, SOCKSlisten)
+  AC_DEFINE(Rselect, SOCKSselect)
+  AC_DEFINE(Rrecvfrom, SOCKSrecvfrom)
+  AC_DEFINE(Rsendto, SOCKSsendto)
+  AC_DEFINE(Rrecv, SOCKSrecv)
+  AC_DEFINE(Rsend, SOCKSsend)
+  AC_DEFINE(Rread, SOCKSread)
+  AC_DEFINE(Rwrite, SOCKSwrite)
+  AC_DEFINE(Rrresvport, SOCKSrresvport)
+  AC_DEFINE(Rshutdown, SOCKSshutdown)
+  AC_DEFINE(Rlisten, SOCKSlisten)
+  AC_DEFINE(Rclose, SOCKSclose)
+  AC_DEFINE(Rdup, SOCKSdup)
+  AC_DEFINE(Rdup2, SOCKSdup2)
+  AC_DEFINE(Rfclose, SOCKSfclose)
+  AC_DEFINE(Rgethostbyname, SOCKSgethostbyname)
+fi
+
+#
+# MP library checking. First check whether user wants to use GMP and use
+# it if found. If not or not defined then compile the MPI library in the
+# source tree.
+#
+AC_MSG_CHECKING(whether to support GMP)
+AC_ARG_WITH(gmp,
+[  --with-gmp[=PATH]       Compile with GMP support instead of MPI],
+[ case "$withval" in
+  no)
+    AC_MSG_RESULT(no)
+    mp_gmp=false
+    ;;
+  *)
+    AC_MSG_RESULT(yes)
+
+    if test -d "$withval/include"; then
+      CFLAGS="$CFLAGS -I$withval/include"
+    else
+      CFLAGS="$CFLAGS -I$withval"
+    fi
+    if test -d "$withval/lib"; then
+      withval="-L$withval/lib -lgmp"
+    else
+      withval="-L$withval -lgmp"
+    fi
+
+    LIBS="$withval $LIBS"
+    mp_gmp=false
+    AC_CHECK_LIB(gmp, __gmpz_init,
+      mp_gmp=true
+      AC_DEFINE(SILC_MP_GMP)
+      AC_MSG_RESULT(Using GMP as MP library)
+    )
+      ;;
+  esac ],
+  AC_MSG_RESULT(no)
+  mp_gmp=false
+)
+
+AM_CONDITIONAL(SILC_MP_GMP, test x$mp_gmp = xtrue)
+AM_CONDITIONAL(SILC_MP_NSS_MPI, test x$mp_gmp = xfalse)
+if test x$mp_gmp = xfalse; then
+  AC_DEFINE(SILC_MP_NSS_MPI)
+  AC_MSG_RESULT(Using NSS MPI as MP library)
+fi
+
+AC_ARG_WITH(silcd-config-file,
+[  --with-silcd-config-file[=PATH]
+                          Use PATH as default configuration file in SILC
+                          server [/etc/silc/silcd.conf]],
+[ AC_DEFINE_UNQUOTED(SILC_SERVER_CONFIG_FILE, "$withval") ])
+
+if test "x$localstatedir" != 'x${prefix}/var'; then
+       PIDFILE="$localstatedir/silcd.pid"
+else
+       PIDFILE="$silc_prefix/var/silcd.pid"
+fi
+AC_ARG_WITH(silcd-pid-file,
+[  --with-silcd-pid-file[=PATH]
+                          Use PATH as default pid file in SILC
+                          server [/var/run/silcd.pid]],
+[ case "$withval" in
+       no)
+               ;;
+       yes)
+               PIDFILE="$withval"
+               ;;
+       *)
+               PIDFILE="$withval"
+               ;;
+       esac ],
+)
+AC_SUBST(PIDFILE)
+
+#
+# Native WIN32 compilation under cygwin
+#
+AC_ARG_WITH(win32,
+[  --with-win32            Compile native WIN32 code (-mno-cygwin)],
+[ AC_DEFINE(SILC_WIN32)
+  win32-support=true
+  CFLAGS="-mno-cygwin $CFLAGS" 
+  LIBS="$LIBS -lwsock32" ])
+
+AM_CONDITIONAL(SILC_WIN32, test x$win32-support = xtrue)
+
+#
+# Native EPOC support (disabled by default)
+#
+AM_CONDITIONAL(SILC_EPOC, test xfalse = xtrue)
+
+#
+# IPv6 support
+#
+AC_MSG_CHECKING(for IPv6 support)
+AC_ARG_ENABLE(ipv6,
+[  --enable-ipv6           Enable IPv6 support],
+[ case "${enableval}" in
+  yes) 
+    want_ipv6=true
+    check_ipv6=false
+    AC_DEFINE(HAVE_IPV6)
+    AC_MSG_RESULT(yes)
+    ;;
+  *)
+    want_ipv6=false
+    check_ipv6=false
+    AC_MSG_RESULT(no)
+    ;;
+esac ], check_ipv6=true)
+
+if test x$check_ipv6 = xtrue; then
+  AC_TRY_COMPILE([#ifdef HAVE_NETINET_TCP_H
+                 #include <netinet/tcp.h>
+                 #endif
+                 #ifdef HAVE_NETDB_H
+                 #include <netdb.h>
+                 #endif
+                 #include <sys/socket.h>
+                 #ifdef HAVE_NETDB_IN_H
+                 #include <netinet/in.h>
+                 #endif],
+                 [struct sockaddr_in6 sin6;
+                  int family = AF_INET6;
+                 ], [AC_DEFINE(HAVE_IPV6)
+                     AC_MSG_RESULT(yes)], [AC_MSG_RESULT(no)])
+fi
+
+#
+# Debug checking
+#
+AC_MSG_CHECKING(for enabled debugging)
+AC_ARG_ENABLE(debug,
+[  --enable-debug          Enable debugging],
+[ case "${enableval}" in
+  yes) 
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(SILC_DEBUG)
+    CFLAGS="-O -g $CFLAGS"
+    ;;
+  *)
+    AC_MSG_RESULT(no)
+    CFLAGS="-O2 -g $CFLAGS"
+    ;;
+esac ], CFLAGS="-O2 -g $CFLAGS"
+        AC_MSG_RESULT(no))
+
+#
+# Disable all assembler optimizations
+#
+AC_ARG_ENABLE(asm,
+[  --disable-asm           Do not use assembler optimizations ],
+[])
+
+#
+# Threads support
+#
+AC_ARG_ENABLE(threads,
+[  --disable-threads       Do not compile with multi-thread support],
+[ case "${enableval}" in
+  yes)
+    want_threads=true
+    check_threads=true
+    ;;
+  *)
+    want_threads=false
+    check_threads=false
+    ;;
+esac ])
+
+if test x$check_threads = xtrue; then
+want_threads=false
+AC_CHECK_HEADERS(pthread.h, 
+       [ AC_DEFINE(SILC_HAVE_PTHREAD) 
+          want_threads=true ],
+       [ if test -f /usr/pkg/include/pthread.h ; then
+           AC_DEFINE(SILC_HAVE_PTHREAD)
+           AC_MSG_RESULT(Found pthread.h in /usr/pkg/include/)
+           CFLAGS="$CFLAGS -I/usr/pkg/include"
+            want_threads=true
+          elif test -f /usr/contrib/include/pthread.h ; then
+           AC_DEFINE(SILC_HAVE_PTHREAD)
+           AC_MSG_RESULT(Found pthread.h in /usr/contrib/include/)
+           CFLAGS="$CFLAGS -I/usr/contrib/include"
+            want_threads=true
+          fi
+       ])
+
+AM_CONDITIONAL(SILC_THREADS, test x$want_threads = xtrue)
+if test x$want_threads = xtrue; then
+  TMP_LIBS="$LIBS"
+  LIBS="-lpthread"
+  AC_TRY_LINK([#include <pthread.h>],
+             [pthread_attr_t attr; pthread_attr_init(&attr);], 
+  AC_DEFINE(SILC_THREADS),
+  LIBS="-L/usr/pkg/lib -lpthread"
+  AC_TRY_LINK([#include <pthread.h>],
+             [pthread_attr_t attr; pthread_attr_init(&attr);],
+  AC_DEFINE(SILC_THREADS),
+  LIBS="-L/usr/contrib/lib -lpthread"
+  AC_TRY_LINK([#include <pthread.h>],
+             [pthread_attr_t attr; pthread_attr_init(&attr);], 
+  AC_DEFINE(SILC_THREADS),
+  LIBS=""
+  )))
+
+  CFLAGS="$CFLAGS -D_REENTRANT"
+  case $host in
+    *-aix*)
+      CFLAGS="$CFLAGS -D_THREAD_SAFE"
+      if test x"$GCC" = xyes; then
+        CFLAGS="$CFLAGS -mthreads"  
+      fi
+      ;;
+    *-freebsd2.2*)
+      CFLAGS="$CFLAGS -D_THREAD_SAFE"
+      ;;
+    *-sysv5uw7*)  # UnixWare 7
+      if test "$GCC" != "yes"; then
+        CFLAGS="$CFLAGS -Kthread"
+      else
+        CFLAGS="$CFLAGS -pthread"
+      fi
+      ;;
+    *-dg-dgux*)  # DG/UX
+      CFLAGS="$CFLAGS -D_POSIX4A_DRAFT10_SOURCE"
+      ;;
+    esac
+
+  LIBS="$TMP_LIBS $LIBS"
+fi
+fi
+
+#
+# Other configure scripts
+#
+AC_CONFIG_SUBDIRS(irssi)
+AC_CONFIG_SUBDIRS(lib/silcmath/mpi)
+#AC_CONFIG_SUBDIRS(lib/zlib)
+
+SILC_TOP_SRCDIR=`pwd`
+AC_SUBST(SILC_TOP_SRCDIR)
+#SILC_INSTALL_PREFIX=$ac_default_prefix
+#AC_SUBST(SILC_INSTALL_PREFIX)
+AC_SUBST(LIBS)
+INCLUDE_DEFINES_INT="include \$(top_srcdir)/Makefile.defines_int"
+AC_SUBST(INCLUDE_DEFINES_INT)
+
+#
+# Makefile outputs
+#
+AC_CONFIG_FILES( \
+Makefile
+Makefile.defines
+Makefile.defines_int
+doc/Makefile  
+includes/Makefile
+lib/Makefile
+lib/contrib/Makefile
+lib/silccore/Makefile
+lib/silccrypt/Makefile 
+lib/silcmath/Makefile
+lib/silcmath/mpi/Makefile.defines
+lib/silcmath/mpi/Makefile.defines_int
+lib/silcsim/Makefile
+lib/silcske/Makefile
+lib/silcutil/Makefile
+lib/silcutil/unix/Makefile
+lib/silcutil/win32/Makefile
+lib/silcsftp/Makefile
+lib/silcsftp/tests/Makefile
+doc/example_silc.conf
+doc/example_silcd.conf
+)     
+
+if test "x$silc_dist" = "xsilc-client" || 
+   test "x$silc_dist" = "xsilc-toolkit"; then
+AC_CONFIG_FILES( \
+lib/silcclient/Makefile
+irssi/Makefile.defines
+irssi/Makefile.defines_int
+)
+fi
+
+if test "x$silc_dist" = "xsilc-server" ||
+   test "x$silc_dist" = "xsilc-toolkit"; then
+AC_CONFIG_FILES( \
+silcd/Makefile
+)
+fi
+
+if test "x$silc_dist" = "xsilc-toolkit"; then
+AC_CONFIG_FILES( \
+silc/Makefile
+win32/Makefile
+win32/libsilc/Makefile
+win32/libsilcclient/Makefile
+)
+fi
+
+AC_OUTPUT
diff --git a/distributions b/distributions
new file mode 100644 (file)
index 0000000..ae8e787
--- /dev/null
@@ -0,0 +1,80 @@
+#
+# Distributions
+#
+# Distributions file describing the different distributions that are
+# created out of the SILC source tree.  The ./prepare script will read
+# this file to determine what kind of distributions are created.
+#
+# Give the name of the distribution and the version as arguments to the
+# ./prepare script to prepare the source tree for the specific
+# distribution.
+#
+# The format of this file is as follows:
+#
+# <name>_SUBDIRS                       Subdirs for the distribution
+# <name>_SUBDIRS_<dir>                 Subdirs under <dir>
+# <name>_DISTLABEL                     Preprocessor label
+# <name>_EXTRA_DIST                    List of extra files or directories
+#
+# The _SUBDIRS define all the subdirectories that the Makefile should
+# traverse.  The SUBDIRS_<dir> defines all subdirectories in the 
+# subdirectory <dir>.  The DISTLABEL defines a preprocessor label
+# that can be used in the source code like `#ifdef SILC_DIST_CLIENT' to
+# define code only for the specific distribution.  The label can also be
+# used in the Makefiles by `if SILC_DIST_CLIENT' to define something in
+# the Makefile only for the specific distribution.
+#
+# The DISTRIBUTIONS defines all distributions in this file.  The
+# EXTRA_DIST is used to add extra files or directories to the
+# distribution. The files may be in subdirectories.
+#
+# NOTE: For now this supports only one sublevel of the directories.
+#       Also make sure that the <dir> directory includes Makefile.am.pre
+#       instead of Makefile.am, otherwise it won't work.  The Makefile.am.pre
+#       must have DIST_SUBDIRS = SILC_DISTRIBUTIONS_SUBDIRS line in it.
+#
+# NOTE: Also note that if any subdirectory has Makefile.am.pre then you
+#       must list it here as <name>_SUBDIRS_<dir>. To define all sub
+#       directories use $(COMMONDIRS) that must be defined in the Makefile
+#       and list all subdirs.
+#
+# Example:
+#
+# xyz_SUBDIRS=lib irssi doc includes
+# xyz_SUBDIRS_lib=$(COMMONDIRS)
+# xyz_DISTLABEL=SILC_DIST_XYZ
+# xyz_EXTRA_DIST=README.XYZ
+#
+# To prepare the distribution give command ./prepare xyz 1.0.4
+#
+
+# Default Toolkit distribution
+toolkit_SUBDIRS=lib irssi silc silcd doc includes win32
+toolkit_SUBDIRS_lib=$(COMMONDIRS)
+toolkit_SUBDIRS_doc=$(COMMONDIRS)
+toolkit_DISTLABEL=SILC_DIST_TOOLKIT
+toolkit_EXTRA_DIST=README.CVS README.WIN32 silcer
+
+# Irssi SILC Client distribution
+client_SUBDIRS=lib irssi doc includes
+client_SUBDIRS_lib=contrib silccore silccrypt silcsim silcmath silcske silcutil silcclient silcsftp
+client_SUBDIRS_doc=$(COMMONDIRS)
+client_DISTLABEL=SILC_DIST_CLIENT
+client_EXTRA_DIST=#
+
+# SILC Server distribution
+server_SUBDIRS=lib silcd doc includes
+server_SUBDIRS_lib=contrib silccore silccrypt silcsim silcmath silcske silcutil silcsftp
+server_SUBDIRS_doc=$(COMMONDIRS)
+server_DISTLABEL=SILC_DIST_SERVER
+server_EXTRA_DIST=#
+
+# Native WIN32 SILC library distribution (will include only the libraries)
+# XXX This is not really used at all!
+win32dll_SUBDIRS=lib doc includes
+win32dll_SUBDIRS_lib=silccore silccrypt silcsim silcmath silcske silcutil silcclient
+win32dll_SUBDIRS_doc=$(COMMONDIRS)
+win32dll_DISTLABEL=SILC_DIST_WIN32DLL
+win32dll_EXTRA_DIST=README.CVS README.WIN32
+
+DISTRIBUTIONS=toolkit client server win32dll
index 0393f347ca1e73a3555fa04eca0a0873afa55760..1a89142443bad5ecc04ff1d94f7f8b3fafd2c0fe 100644 (file)
@@ -1,7 +1,7 @@
 Coding Style in SILC source tree
 ================================
 
-This documents describes the coding style and coding conventions used
+This document describes the coding style and coding conventions used
 in the SILC source tree.  The purpose of the document is to describe the
 common way to program for SILC and thus should be learned when programming
 new code.  The document describes various conventions regarding variable
@@ -28,7 +28,7 @@ the common naming convention while the lower level routines uses what
 ever they want.  For example, ciphers are implemented currently in this
 way.  They define common SILC Cipher API but the actual implementation
 of algorithms uses their own naming convention.  Another example is
-the GMP math library that uses its own function naming but we have our
+the MPI math library that uses its own function naming but we have our
 own SILC MP API over it that has been defined using common SILC naming
 convention.
 
@@ -152,7 +152,7 @@ The <module> is the module you are programming currently.  You should
 have a pretty good idea what you are programming and what the module
 does.  For example, <cipher>, <config>, <command>, <packet>, etc.
 
-The <function> is the describtion of the functionality of the function
+The <function> is the description of the functionality of the function
 you are writing.  Naturally it should be self explanatory and weird
 short names should be avoided.  It is better to have long function
 names than some odd name that does not tell what it is about.  Function
@@ -270,11 +270,11 @@ be commented.  If nothing more a line of comment telling what the function
 is about helps a lot when you go back to it after six months.  Static
 functions should be commented as well.
 
-The commenting of functions in SILC has been made into the source files,
-and not in the header files where the function prototypes reside.  Header
-files usually includes structure comments, macro comments and perhaps
-some other relevant commenting but usually not function comments.
-It is also Ok to comment the code inside function when it is needed.
+When writing a new header it is preferred that the header file is 
+immediately written in the ROBOdoc documentation format.  This is 
+important when you are doing library code under lib/.  There are plenty
+of examples of this format.  The ROBOdoc is used automatically generate
+the Toolkit documentation.
 
 Comments should use normal C-language comments /* */ and not C++ comments.
 
@@ -283,7 +283,7 @@ General Appearance
 ==================
 
 The code should be clean and good to eye, although the function of it
-must always superseed the appearance.  However, it is nice to read code
+must always supersede the appearance.  However, it is nice to read code
 that looks good.  Here are some issues on general appearance.
 
        o Use empty lines when appropriate but not too much.  There
@@ -305,15 +305,16 @@ that looks good.  Here are some issues on general appearance.
 
        o If you are not sure about how something should be done or
          the code you've done is not finished, it should be commented
-         with XXX plus explanation what is going on.
+         with XXX plus explanation what is going on.  For example,
+          /* XXX hmm... how is this flushed? */
 
 
 Source Files
 
 All source files starts with header that includes the name of the author,
 copyright notice and the copyright policy, usually part of GNU GPL licence.
-Now, if this really isn't that important but some sort of header should
-be in all source files.
+Now, this really isn't that important but some sort of header should be in
+all source files.
 
 In the start of the source files should include the #include's that are
 needed.  All library source files must include `silcincludes.h', this is
@@ -336,6 +337,16 @@ functions if any of those exist.  After macros should include the
 public prototypes of the functions.  Go see any header file as an example.
 
 
+Using gotos
+===========
+
+Gotos are used in the SILC code quite often.  If you know how to use
+goto's properly then it is ok to use them for example to optimize the
+code.  If you use goto's then use them only to make forward jumps, try
+to avoid backward jumps at all cost.  If you don't know how to use goto's 
+do not use them.
+
+
 Debug Messages
 ==============
 
@@ -388,7 +399,7 @@ must not be used directly.  There are functions like,
 
 You should always use silc_calloc instead of silc_malloc because
 silc_calloc automatically zeroes the allocated memory area.  This is
-imporant especially with structures because generally we want that all
+important especially with structures because generally we want that all
 fields, by default, are zero.
 
 So, instead of doing
@@ -411,13 +422,16 @@ by memset() before freeing the memory.  Common way to do is,
        memset(ptr, 'F', sizeof(*ptr));
        silc_free(ptr);
 
-Where 'F' indicates free'd memory if you ever check it with debugger.
+Where 'F' indicates free'd memory if you'd ever check it with debugger.
 Other choice is to use 0 instead of 'F'.  The pointer after freeing 
 should be set to NULL if appropriate, ptr = NULL.
 
 Note that some functions in the SILC library handles the zeroing of
 the memory area automatically, like for example, silc_buffer_free.
 
+Also note that all allocation routines assert()'s if the memory allocation
+fails, ie. system does not have free memory.
+
 
 Callback Programming
 ====================
@@ -558,10 +572,66 @@ ass sometimes.  But after the grand idea behind callback functions
 becomes clear they are a wonderful tool.
 
 
+Lists
+=====
+
+SILC has two different list API's.  The List API and the Dynamic List API.
+For definitions of List API see lib/silcutil/silclist.h and for Dynamic 
+List API see lib/silcutil/silcdlist.h.  Following short example of the 
+List API.
+
+List API
+
+typedef struct SilcDummyStruct {
+  int dummy;
+  void *context;
+  struct SilcDummyStruct *next;
+} SilcDummy;
+
+int main()
+{
+  SilcList list;
+  SilcDummy *dummy;
+  SilcDummy *entry;
+
+  /* Initialize the list */
+  silc_list_init(list, struct SilcDummyStruct, next);
+
+  /* Allocate one list entry */        
+  dummy = silc_calloc(1, sizeof(*dummy));
+  dummy->dummy = 100;
+  dummy->context = NULL;
+
+  /* Add the entry to the list */
+  silc_list_add(list, dummy);
+
+  /* Allocate second list entry */     
+  dummy = silc_calloc(1, sizeof(*dummy));
+  dummy->dummy = 3000;
+  dummy->context = NULL;
+
+  /* Add the entry to the list */
+  silc_list_add(list, dummy);
+       
+  /* Then traverse the list, print the values, remove from list and free
+     memory */
+  silc_list_start(list);
+  while ((entry = silc_list_get(list)) != SILC_LIST_END) {
+    fprintf(stderr, "%d\n", entry->dummy);
+
+    /* Remove from list and free memory */
+    silc_list_del(list, entry);  
+    silc_free(entry);
+  }
+
+  return 0;
+}
+
+
 Copyrights of the Code
 ======================
 
-The original code in SILC is GPL licensed.  GMP is GPL licensed as well
+The original code in SILC is GPL licensed.  MPI is GPL licensed as well
 and zlib is with free license as well.  New code will be accepted to
 the official SILC source tree if it is coded in GPL or similiar free
 license as GPL is, and of course if it is public domain.  Code with
diff --git a/doc/FAQ b/doc/FAQ
index b6da03030d589fe692adfa199b12d37753c1d8a6..3869f9162dd7d87f7607bd10942d2dc8f1828fc6 100644 (file)
--- a/doc/FAQ
+++ b/doc/FAQ
-Frequently Asked Questions
+   Frequently Asked Questions
 
+   1. General Questions
+        1.1 What is SILC?
+        1.2 When was SILC Project started?
+        1.3 Why SILC in the first place?
+        1.4 What license covers the SILC release?
+        1.5 Why SILC? Why not IRC3?
+        1.6 What platforms SILC supports?
+        1.7 How do you pronounce SILC?
+        1.8 Where can I find more information?
+        1.9 I would like to help out, what can I do?
 
-Q: What is SILC?
-A: SILC (Secure Internet Live Conferencing) is a protocol which provides
-   secure conferencing services in the Internet over insecure channel.
-   SILC is IRC like although internally they are very different.  Biggest
-   similiarity between SILC and IRC is that they both provide conferencing
-   services and that SILC has almost same commands as IRC.  Other than
-   that they are nothing alike.
+   2. Protocol Questions
+        2.1 What is the status of SILC protocol in the IETF?
+        2.2 How much the SILC protocol is based on IRC?
+        2.3 Why use SILC? Why not IRC with SSL?
+        2.4 Can I talk from SILC network to IRC network?
+        2.5 Does SILC support file transfer?
+        2.6 Does SILC support DCC or alike?
+        2.7 I am behind a firewall, can I use SILC?
+        2.8 How secure SILC really is?
+        2.9 Does SILC support instant messaging?
+        2.10 Why SILC does not have LINKS command like in IRC?
+        2.11 Why SILC does not have STATS command like in IRC?
+        2.12 Is anyone outside a channel able to see the channel
+   messages?
+        2.13 Is it true that all messages are encrypted in SILC?
+        2.14 Can server or SILC operator gain operator mode on a channel?
+        2.15 I have suggestions to SILC Protocol, what can I do?
+
+   3. Client Questions
+        3.1 Where can I find SILC clients?
+        3.2 Can I use SILC with IRC client and vice versa?
+        3.3 The default theme sucks, where can I find a better one?
+        3.4 How do I send a private message?
+        3.5 How do I negotiate secret key with another user?
+        3.6 How do I negotiate secret keys behind a NAT?
+        3.7 How do I change channel modes?
+        3.8 What does the founder mode on channel mean, and how do I set
+   it?
+        3.9 I am founder of invite only channel, how can I join the
+   channel after I have left it?
+        3.10 How can I op or deop somebody on channel?
+        3.11 How do I set private key for channel, and what does that
+   mean exactly?
+        3.12 How do I transfer a file?
+        3.13 How can I get other users public keys?
+        3.14 How can I see the fingerprint of my public key?
+        3.15 I gave WHOIS to a nick, and it returned multiple replies,
+   why?
+        3.16 Is there a command to see all linked servers?
+        3.17 How do I list the users of a channel?
+        3.18 What is the difference between OPER and SILCOPER commands?
+
+   4. Server Questions
+        4.1 Where can I find SILC servers?
+        4.2 Can I run my own SILC server?
+        4.3 What is the difference between SILC server and SILC router?
+        4.4 Why server says permission denied to write to a log file?
+        4.5 When I connect to to my server, it says "server does not
+   support one of your proposed cipher", what is wrong?
+        4.6 Why SILC server runs on privileged port 706?
+        4.7 I see [Unknown] in the log file, what does it mean?
+
+   5. Toolkit Questions
+        5.1 What is SILC Toolkit?
+        5.2 Is the SILC Toolkit Reference Manual Available?
+        5.3 How do I compile the Toolkit on Unix?
+        5.4 How do I compile the Toolkit on Win32?
+        5.5 Does the Toolkit package include any sample code?
+
+   1. General Questions
+
+   Q: What is SILC?
+   A: SILC (Secure Internet Live Conferencing) is a protocol which
+   provides secure conferencing services in the Internet over insecure
+   channel. SILC is IRC like although internally they are very different.
+   Biggest similarity between SILC and IRC is that they both provide
+   conferencing services and that SILC has almost same commands as IRC.
+   Other than that they are nothing alike.
 
    Biggest differences are that SILC is secure what IRC is not in any
-   way.  The network model is also entirely different compared to IRC.
+   way. The network model is also entirely different compared to IRC.
+
+   Q: When was SILC Project started?
+   A: The SILC development started in 1996 and early 1997. But, for
+   various reasons it suspended many times until it finally got some wind
+   under its wings in 1999. First public release was in summer 2000.
+
+   Q: Why SILC in the first place?
+   A: Simply for fun, nothing more. And actually for need back in the
+   days when it was started. When SILC was first developed there really
+   did not exist anything like this. SILC has been very interesting and
+   educational project.
+
+   Q: What license covers the SILC release?
+   A: The SILC software developed here at silcnet.org, the SILC Client,
+   the SILC Server and the SILC Toolkit are covered by the GNU General
+   Public License.
+
+   Q: Why SILC? Why not IRC3?
+   A: Question that is justified no doubt of that. SILC was not started
+   to become a replacement for IRC. SILC was something that didn't exist
+   in 1996 or even today except that SILC is now released. However, I did
+   check out the IRC3 project in 1997 when I started coding and planning
+   the SILC protocol.
+
+   But, IRC3 is problematic. Why? Because it still doesn't exist. The
+   project is almost at the same spot where it was in 1997 when I checked
+   it out. And it was old project back then as well. That's the problem
+   of IRC3 project. The same almost happened to SILC as well as I wasn't
+   making real progress over the years. I talked to the original author
+   of IRC, Jarkko Oikarinen, in 1997 and he directed me to the IRC3
+   project, although he said that IRC3 is a lot of talking and not that
+   much of anything else. I am not trying to put down the IRC3 project
+   but its problem is that no one in the project is able to make a
+   decision what is the best way to go about making the IRC3 and I wasn't
+   going to be part of that. The fact is that if I would've gone to IRC3
+   project, nor IRC3 or SILC would exist today. I think IRC3 could be
+   something really great if they just would get their act together and
+   start coding the thing.
+
+   Q: What platforms SILC supports?
+   A: The SILC Client is available on various Unix systems and is
+   reported to work under cygwin on Windows. The SILC Server also works
+   on various Unix systems. However, the server has not been tested under
+   cygwin as far as we know. The SILC Toolkit is distributed for all
+   platforms, Unix, Cygwin and native Windows.
+
+   Q: How do you pronounce SILC?
+   A: SILC is usually pronounced as `silk', but you are free to pronounce
+   it the way you want.
+
+   Q: Where can I find more information?
+   A: For more technical information we suggest reading the SILC Protocol
+   specifications. You might also want to take a look at the
+   documentation page on the web page.
+
+   Q: I would like to help out, what can I do?
+   A: You might want to take a look at the Contributing page and the TODO
+   list. You might also want to join the SILC development mailing list.
 
+   2. Protocol Questions
 
-Q: Can I use SILC with IRC client?  What about can I use IRC with SILC
-   client?
-A: Answer for both question is no.  IRC client is in no way compatible
-   with SILC server.  SILC client cannot currenly use IRC but this may
-   change in the future if IRC support is added to the SILC client.  
-   After that one could use both SILC and IRC with the same client.
-   Although, even then one cannot talk from SILC network to IRC network.
-   That just is not possible.
+   Q: What is the status of SILC protocol in the IETF?
+   A: The SILC protocol specifications has been submitted currently as
+   individual submissions. There does not currently exist a working group
+   for this sort of project. Our goal is to fully standardize the SILC
+   and thus submit it as RFC to the IETF at a later time. This can happen
+   only after we have requested the IETF to accept SILC as RFC. As of
+   today, we have not yet even requested this from the IETF. We want to
+   let the protocol mature a bit more.
 
+   Q: How much SILC Protocol is based on IRC?
+   A: SILC is not based on IRC. The client superficially resembles IRC
+   client but everything that happens under the hood is nothing alike
+   IRC. SILC could *never* support IRC because the entire network
+   toppology is different (hopefully more scalable and powerful). So no,
+   SILC protocol (client or server) is not based on IRC. Instead, We've
+   taken good things from IRC and left all the bad things behind and not
+   even tried to burden the SILC with the IRCs problems that will burden
+   IRC and future IRC projects till the end. SILC client resembles IRC
+   client because it is easier for new users to start using SILC when
+   they already know all the commands.
 
-Q: How secure SILC really is?
-A: A good question which I don't have a answer.  SILC has been tried to
-   make as secure as possible.  However, there is no security protocol
-   or security software that has not been vulnerable to some sort of
-   attacks.  SILC is in no means different from this.  So, it is suspected 
-   that there are security holes in the SILC.  These holes just needs to 
-   be found so that they can be fixed.
+   Q: Why use SILC? Why not IRC with SSL?
+   A: Sure, that is possible, although, does that secure the entire IRC
+   network? And does that increase or decrease the lags and splits in the
+   IRC network? Does that provide user based security where some specific
+   private message are secured? Does that provide security where some
+   specific channel messages are secured? And I know, you can answer yes
+   to some of these questions. But, security is not just about applying
+   encryption to traffic and SILC is not just about `encrypting the
+   traffic`. You cannot make insecure protocol suddenly secure just by
+   encrypting the traffic. SILC is not meant to be IRC replacement. IRC
+   is good for some things, SILC is good for same and some other things.
+
+   Q: Can I talk from SILC network to IRC network?
+   A: Simple answer for this is No. The protocols are not compatible
+   which makes it impossible to directly talk from SILC network to IRC
+   network or vice versa. Developing a gateway between these two networks
+   would technically be possible but from security point of view strongly
+   not recommended. We have no plans for developing such a gateway.
+
+   Q: Does SILC support file transfer?
+   A: Yes. The SILC protocol support SFTP as mandatory file transfer
+   protocol. It provides simple client to client file transfer, but also
+   a possibility for file and directory manipulation. Even though the
+   SFTP is the file transfer protocol the support for file transferring
+   has been done so that practically any file transfer protocol may be
+   used with SILC protocol.
+
+   Q: Does SILC support DCC or alike?
+   A: SILC does not support the DCC commonly used in IRC. It does not
+   need it since it has builtin support for same features that DCC have.
+   You can transfer files securely and encrypted directly with another
+   client. You can also negotiate secret key material with another client
+   directly to use it in private message encryption. The private messages
+   are not, however sent directly between clients. The protocol, on the
+   hand does not prohibit sending messages directly between clients if
+   the implementation would support it. The current SILC Client
+   implementation does not support it. This means that private messages
+   travel through the SILC Network. SILC protocol also has a capability
+   to support DCC and CTCP like protocols with SILC. None of them,
+   however have not been defined to be used with SILC at the present
+   time.
+
+   Q: I am behind a firewall, can I use SILC?
+   A: Yes. If your network administrator can open the remote port 706
+   (TCP) you can use SILC without problems. You may also compile your
+   SILC client with SOCKS support which will proxy your SILC session
+   through the firewall.
+
+   Q: How secure SILC really is?
+   A: We have tried to make SILC as secure as possible. However, there is
+   no security protocol or security software that has not been vulnerable
+   to some sort of attacks. SILC is in no means different from this. So,
+   it is suspected that there are security holes in the SILC. These holes
+   just need to be found so that they can be fixed. SILC's security
+   features has been developed from attacker's point of view, and we've
+   tried to find all the possible attacks and guard the protocol against
+   them.
 
    But to give you some parameters of security SILC uses the most secure
-   crytographic algorithms such as Blowfish, RC5, Twofish, etc.  SILC
-   does not have DES or 3DES as DES is insecure and 3DES is just too
-   slow.  SILC also uses cryptographically strong random number generator
-   when it needs random numbers.  Public key cryptography uses RSA
-   and Diffie Hellman algorithms.  Key lengths for ciphers are initially
-   set to 128 bits but many algorithm supports longer keys.  For public
-   key algorithms the starting key length is 1024 bits.
-
-   But the best answer for this question is that SILC is as secure as
-   its weakest link.  SILC is open and the protocol is open and in public
-   thus open for security analyzes.
+   crytographic algorithms such as AES (Rijndael), Twofish, Blowfish,
+   RC5, etc. SILC does not have DES or 3DES as DES is insecure and 3DES
+   is just too slow. SILC also uses cryptographically strong random
+   number generator when it needs random numbers. Public key cryptography
+   uses RSA (PKCS #1) and Diffie-Hellman algorithms. Key lengths for
+   ciphers are initially set to 256. For public key algorithms the
+   starting key length is 1024 bits.
+
+   But the best answer for this question is that SILC is as secure as its
+   weakest link. SILC is open and the protocol is open and in public thus
+   open for security analysis.
 
    To give a list of attacks that are ineffective against SILC:
 
-      o Man-in-the-middle attacks are ineffective if proper public key
-        infrastructure is used.  SILC is vulnerable to this attack if
-        the public keys used in the SILC are not verified to be trusted.
+   - Man-in-the-middle attacks are ineffective if proper public key
+   infrastructure is used, and if all public keys are always verified.
+   - IP spoofing is ineffective (because of encryption and trusted keys).
+   - Attacks that change the contents of the data or add extra data to
+   the packets are ineffective (because of encryption and integrity
+   checks).
+   - Passive attacks (listenning network traffic) are ineffective
+   (because of encryption). Everything is encrypted including
+   authentication data such as passwords when they are needed.
+   - Any sort of cryptanalytic attacks are tried to make ineffective by
+   using the best cryptographic algorithms out there, and by designing
+   the protocol to guard against them.
 
-      o IP spoofing is ineffective (because of encryption and trusted 
-        server keys).
+   Q: Does SILC support instant messaging?
+   A: SILC is not an instant message (IM) system, like ICQ and the
+   others. SILC is more IRC like system, "real-time", connection-oriented
+   chat and that kind of stuff. But I guess IRC is too sometimes called
+   an Instant Messaging system.
 
-      o Attacks that change the contents of the data or add extra
-        data to the packets are ineffective (because of encryption and
-        integrity checks).
+   Q: Why SILC does not have LINKS command like in IRC?
+   A: It was felt that this information as an own command in SILC is not
+   necessary. Moreover, the topology of the network might be undisclosed
+   information even though the servers and routers in the network are
+   still open. We feel that the network topology information, if it is
+   wanted to be public, and the list of accessible servers can be made
+   available in other ways than providing command like LINKS, which shows
+   the active server links in IRC.
 
-      o Passive attacks (listenning network traffic) are ineffective
-        (because of encryption).  Everything is encrypted including
-        authentication data such as passwords when they are needed.
+   Q: Why SILC does not have STATS command like in IRC?
+   A: This too was considered as information that the protocol should not
+   address. We feel that server implementations will need to implement
+   some sort of adminstrative plugin, or module which provides various
+   means of accessing statistical and other information in the server.
+   And, we do consider this implementation issue, not protocol design
+   issue.
 
-      o Any sort of cryptanalytic attacks are tried to make ineffective
-        by using the best cryptographic algorithms out there.
+   Q: Is anyone outside a channel able to see the channel messages?
+   A: A short answer is simply No. A longer answer involves assumptions
+   about security conditions. Initially channel keys are generated by the
+   server, so if the server would get compromised it would be possible
+   for an adversary to see the messages. However, users on the channel
+   can prevent this even if the server would be compromised. It is
+   possible to set so called channel private key that only the users on
+   the channel know about. The servers does not know about the key, and
+   therefore cannot see the messages even if they would be compromised.
+   So, longer answer results into same as the short one; No.
 
+   Q: Is it true that all messages are encrypted in SILC?
+   A: Most definitely yes. The SILC protocol makes it impossible to send
+   unencrypted messages or packets to the SILC network. All messages are
+   always encrypted, either using session keys, or other secret keys such
+   as channel keys or private message keys.
 
-Q: Why SILC? Why not IRC3?
-A: Question that is justified no doubt of that.  I didn't start doing SILC
-   to be replacement for IRC.  SILC was something that didn't exist in
-   1996 or even today except that SILC is now released.  However, I did
-   check out the IRC3 project in 1997 when I started coding and planning
-   the SILC protocol.
+   Q: Can server or SILC operator gain operator mode on a channel?
+   A: They cannot get operator status, founder status, join invite only
+   channels, escape active bans, escape user limits or anything alike,
+   without explicitly being allowed. Only way to get channel operator
+   status is that someone ops him. Server and SILC operators in the
+   network are normal users with the extra privileges of being able to
+   adminstrate their server. They cannot do anything more than a normal
+   user.
+
+   Q: I have suggestions to SILC Protocol, what can I do?
+   A: All suggestions and improvements are of course welcome. You should
+   read the protocol specifications first to check out whether your idea
+   is covered by them already. The best place to make your idea public is
+   the SILC development mailing list. You might want to checkout the TODO
+   list from the CVS as well.
+
+   3. Client Questions
+
+   Q: Where can I find SILC clients?
+   A: The SILC client is available for free download from the silcnet.org
+   web page. Some people have also mentioned words Java and Perl when
+   talking about SILC clients. Nothing has appeared yet, though.
+
+   Q: Can I use SILC with IRC client and vice versa?
+   A: Generally the answer would be no for both. However, there exist
+   already at least one IRC client that supports SILC, the Irssi client.
+   The current SILC client is actually based on the user interface of the
+   Irssi client. So, yes it is possible to use SILC with some IRC clients
+   and vice versa. But, this does not mean that you can talk from SILC
+   network to IRC network, that is not possible.
+
+   Q: The default theme sucks, where can I find a better one?
+   A: The Irssi SILC client's theme files are almost 100% compatible with
+   the original Irssi IRC client's themes. You can get those theme files
+   from the Irssi project website. You can also try to make a better
+   theme by yourself.
+
+   Q: How do I send a private message?
+   A: Sending private message is done by using the MSG command. For
+   example, command: /MSG john hello, will send a `hello' message to a
+   nickname `john'. By default private messages are secured with session
+   keys, and the message is re-encrypted by the servers when the message
+   travels to the receiver. If you would like to secure the private
+   messages with a private key, you can negotiate a secret key with the
+   receiver. Always remember to give WHOIS command before sending a
+   private message to assure that you are sending the message to correct
+   person.
+
+   Q: How do I negotiate secret key with another user?
+   A: It is important to negotiate secret keys if you cannot trust the
+   servers and the network you are using. By negotiating a key with the
+   user you want to talk to assures that no one except you and your
+   friend is able to encrypt and decrypt the messages. The secret key
+   negotiation is done with the KEY command. Here is an example of how to
+   negotiate keys for securing private messages.
+
+   By giving command: /KEY MSG john agreement 192.168.2.100, you will
+   send a key negotiation request to a nickname `john'. The 192.168.2.100
+   IP address would be your machine's IP address. You can also define an
+   port to the KEY command after the IP address. If you do not do that
+   the operating system will bind to a port of its choosing. John will
+   receive a notification on the screen that you would like to negotiate
+   secret keys with him, and he will receive the IP address and port
+   where you are listenning for the negotiation. When he gives command:
+   /KEY MSG You negotiate 192.168.2.100 31382, the key negotiation is
+   started. During the key negotiation you will be prompted on the screen
+   to verify and accept John's public key if you do not have his public
+   key already. The John will be prompted to accept your public key as
+   well. After the key negotiation is over all private messages sent
+   between you and John are secured with the negotiated secret key. Note
+   that you must verify the public key you are prompted for, and this is
+   very important since someone could be doing man-in-the-middle attack.
+
+   Q: How do I negotiate secret keys behind a NAT?
+   A: If only you are behind a NAT, or firewall then key negotiation
+   works, but if both you and your friend are behind a NAT then key
+   negotiation will not work, since it is done peer to peer. If you are
+   behind a NAT then you obviously cannot receive key negotiations, and
+   cannot bind to any IP address and port. However, you can still use KEY
+   command to negotiate the keys.
+
+   By giving command: /KEY MSG john agreement, without any other
+   arguments (such as IP address and port) you will send a negotiation
+   request to John, but do not provide an address and port for the John
+   to connect to. When John receives the notification on the screen that
+   you would like to perform key negotiation, he can give command: /KEY
+   MSG You agreement 172.16.100.78, which will send key negotiation
+   request back to you. You will receive the IP address and port where
+   you need to connect in order to perform the negotiation. After
+   receiving the notification you can give command: /KEY MSG john
+   negotiate 172.16.100.78 31181, which will start the key negotiation
+   with John. This way you can negotiate the keys if you are behind a
+   NAT.
+
+   Q: How do I change channel modes?
+   A: The command to manage channel modes is CMODE. With this command you
+   can change the channel status (to change it to secret channel for
+   example), set user limit on the channel, passphrase for the channel,
+   set the channel to use private keys on channel, and set the founder
+   mode.
+
+   Q: What does the founder mode on channel mean, and how do I set it?
+   A: Who ever creates the channel by being the first user to join the
+   channel becomes automatically the founder of the channel. Founder has
+   some extra privileges on the channel. For example, it is not possible
+   to kick the founder off the channel, and there are some channel modes
+   that only the founder of the channel can change. If the creator of the
+   channel wishes to preserve the channel founder mode even if he leave
+   the channel he can set the founder mode for the channel.
+
+   The mode is set by giving command: /CMODE #channel +f -pubkey. This
+   will set the founder mode and will use the public key of the founder
+   as authenticator when the user is reclaiming the mode back. If the
+   founder leaves the channel he will be able to get the founder mode
+   back by using JOIN or CUMODE commmands. Giving command /JOIN #channel
+   -founder -pubkey, will get the founder mode back at the same time he
+   joins the channel, or giving commmand /CUMODE #channel +f -pubkey,
+   will also give the founder mode back on the channel after he has
+   joined the channel.
+
+   If the channel is destroyed after the last client leaves the channel,
+   the founder mode is also reset. Who ever creates the channel after
+   that will also get the channel founder mode automatically. Note also
+   that the founder mode is local. You can reclaim the mode back only on
+   the same server where you set the founder mode in the first place.
+
+   Q: I am founder of invite only channel, how can I join the channel
+   after I have left it?
+   A: Founder can override the invite only status by reclaiming the
+   founder status on the channel using the JOIN command. The channel must
+   have the founder mode set in order for it to work. Reclaiming founder
+   status using JOIN command is important also if the channel has user
+   limit set, and has active bans. Founder can override these conditions
+   as well. However, founder cannot override the passphrase of the
+   channel if it is set. To get the founder mode during JOIN and to
+   override the invite only condition, give command: /JOIN #channel
+   -founder -pubkey. This will join the channel and attempt to reclaim
+   the founder status back to you. Note that you need to be on the same
+   server where you gave the founder mode for the channel for this to
+   work.
+
+   Q: How can I op or deop somebody on channel?
+   A: Giving operator status, or removing the operator status on a
+   channel requires you to have at least operator status, or founder
+   status on the channel. You can give operator status to another user by
+   using CUMODE command. To give ops give the command: /CUMODE #channel
+   +o john, and to remove ops give command: /CUMODE #channel -o john. To
+   indicate current channel you can also use `*' character in #channel's
+   stead.
+
+   Q: How do I set private key for channel, and what does that mean
+   exactly?
+   A: Setting private key for channel requires first to set the private
+   key mode for the channel. You need to be the founder of the channel to
+   be able to do this. Give the command: /CMODE #channel +k. After this
+   mode is set the old channel key will not be used to encrypt and
+   decrypt channel messages. To set the key for the channel use the KEY
+   command. Every user on the channel must do the same thing and set the
+   same key. If some user on the channel does not set the key (or does
+   not know the key) he won't be able to see any messages on the channel.
+   Give the command: /KEY CHANNEL #channel set verysecretkey. This
+   command will set the `verysecretkey' passphrase as key to the
+   #channel. How exactly other users will know this key is out of scope
+   of the SILC protocol. SILC does not provide yet a possibility of
+   negotiating secret key with many users at the same time. For this
+   reason the secret key on the channel is usually a passphrase or a
+   password that all users on the channel have to know. Setting a private
+   key for channel means that only the users on the channel who know the
+   key is able to encrypt and decrypt messages. Servers do not know the
+   key at all. If you remove the private key mode from the channel, all
+   users will start automatically using a new channel key to secure
+   channel messages.
+
+   Q: How do I transfer a file?
+   A: You can transfer files securely using the FILE command. This
+   command will automatically negotiate secret key with the remote user
+   and the file transfer stream is secured using that key. The file
+   transfer stream is always sent peer to peer. If you would like to send
+   a file to another user you can give command: /FILE SEND
+   path/to/the/file john. This command sends, or actually makes the
+   `path/to/the/file' available for download for the user `john'. The
+   John will decide whether he wants to actually download the file. When
+   John gives the command: /FILE RECEIVE, the key negotiation is started.
+   You and John will be prompted to verify and accept each other's public
+   key if you do not have it cached already. After key negotiation is
+   over the file transfer process starts. If you want to cancel the file
+   transfer session, or if John wants to reject the file transfer
+   request, giving the command: /FILE CLOSE will close the session.
+
+   Q: How can I get other users public keys?
+   A: You can get a user's public key using the GETKEY command. This
+   command will fetch the user's public key from the server where the
+   user has connected to. The server has verified that the user posesses
+   the corresponding private key, however, you will be prompted to verify
+   and accept the public key. All client public keys are saved in your
+   local key directory in ~/.silc/clientkeys/. You can also receive
+   clients public keys during key negotiation and file transfers. The
+   GETKEY command can be used to fetch a server's public key as well.
+   Those keys are saved in ~/.silc/serverkeys/ directory.
+
+   Q: How can I see the fingerprint of my public key?
+   A: You can check out your own fingerprint by giving just WHOIS command
+   without any arguments. Additionally you can also dump the contents of
+   the key file using the silc program and giving -S option to it. Your
+   own public key is always saved in ~/.silc/public_key.pub file. To dump
+   your key run silc as: silc -S .silc/public_key.pub. The same way you
+   can dump the contents of any public key inside ~/.silc/clientkeys/ and
+   ~/.silc/serverkeys/ directories. The WHOIS command will also show
+   other users public key fingerprints.
+
+   Q: I gave WHOIS to a nick, and it returned multiple replies, why?
+   A: This will happen if there are several same nicknames in the network
+   at the same time. As you may already know nicknames are not unique in
+   SILC network. This means there can be multiple same nicknames. This
+   also means that you can always have the nickname you want. If WHOIS
+   returns multiple replies, you can distinguish the users by their
+   realname, username, hostname and ultimately by the fingerprint of
+   their public key, which the WHOIS will also show. You will also notice
+   an additional nickname inside a parenthesis. It may show for example:
+   nickname: John (John@otaku). The real nickname is `John', but since
+   there are many John's in the network you can access this one using
+   `John@otaku'. So, if you were to send private message to this
+   particular John you can do it by giving command: /MSG John@otaku
+   hello. This will send `hello' message to the John@otaku.
+
+   Q: Is there a command to see all linked servers?
+   A: No there is not. For longer answer see also this FAQ.
+
+   Q: How do I list the users of a channel?
+   A: The command to list all users on a particular channel is USERS. It
+   is also aliased to WHO command in Irssi SILC Client. To see the users
+   of the current channel give the command: /USERS *. You can replace the
+   `*' with the channel name of your choosing. If the channel is private
+   or secret channel, and you have not joined the channel, you cannot
+   list the users of that channel.
+
+   Q: What is the difference between OPER and SILCOPER commands?
+   A: The OPER command is used to gain server operator privileges on
+   normal SILC server, while SILCOPER is used to gain router operator
+   (also known as SILC operator) privileges on router server. You cannot
+   use SILCOPER command on normal SILC server, it works only on router
+   server.
+
+   4. Server Questions
+
+   Q: Where can I find SILC servers?
+   A: The SILC server is available for free download from the silcnet.org
+   web page. We are not aware of any other SILC server implementations,
+   so far.
+
+   Q: Can I run my own SILC server?
+   A: Yes of course. Download the SILC server package, compile and
+   install it. Be sure to check out the installation instructions and the
+   README file. You also should decide whether you want to run SILC
+   server or SILC router.
+
+   Q: What is the difference between SILC server and SILC router?
+   A: The topology of the SILC network includes SILC routers and the SILC
+   servers (and SILC clients of course). Normal SILC server does not have
+   direct connections with other SILC servers. They connect directly to
+   the SILC router. SILC Routers may have several server connections and
+   they may connect to several SILC routers. The SILC routers are the
+   servers in the network that know everything about everything. The SILC
+   servers know only local information and query global information from
+   the router when necessary.
+
+   If you are running SILC server you want to run it as router only if
+   you want to have server connections in it and are prepared to accept
+   server connections. You also need to get the router connected to some
+   other router to be able to join the SILC network. You may run the
+   server as normal SILC server if you do not want to accept other server
+   connections or cannot run it as router.
+
+   Q: Why server says permission denied to write to a log file?
+   A: The owner of the log files must be same user that the server is run
+   under, by default it is user `nobody'. Just change the permissions and
+   try again.
+
+   Q: When I connect to my server it says "server does not support one of
+   your proposed ciphers", what is wrong?
+   A: Most likely the ciphers and others has not been compiled as SIMs
+   (modules) and they are configured as modules in the silcd.conf. If
+   they are not compiled as modules remove the module paths from the
+   ciphers and hash functions from the silcd.conf, so that the server use
+   the builtin ciphers. Then try connecting to the server again. It is
+   also possible that the client IS proposing some ciphers that your
+   server does not support.
+
+   Q: Why SILC server runs on privileged port 706?
+   A: Ports 706/tcp and 706/udp have been assigned for the SILC protocol
+   by IANA. Server on the network listening above privileged ports
+   (>1023) SHOULD NOT be trusted as it could have been set up by
+   untrusted party. The server normally drops root privileges after
+   startup and then run as user previously defined in silcd.conf.
+
+   Q: I see [Unknown] in the log file, what does it mean?
+   A: You can see in the log file for example: [Info] Closing connection
+   192.168.78.139:3214 [Unknown]. The [Unknown] means that the connection
+   was not authenticated yet, and it is not known whether the connection
+   was a client, server or router. There will appear [Client], [Server]
+   or [Router] if the connection is authenticated at that point.
+
+   5. Toolkit Questions
+
+   Q: What is SILC Toolkit?
+   A: SILC Toolkit is a package intended for software developers who
+   would like to develope their own SILC based applications or help in
+   the development of the SILC. The Toolkit includes SILC Protocol Core
+   library, SILC Crypto library, SILC Key Exchange (SKE) library, SILC
+   Math library, SILC Modules (SIM) library, SILC Utility library, SILC
+   Client library and few other libraries.
+
+   Q: Is the SILC Toolkit Reference Manual Available?
+   A: Yes, partially completed reference manual is available in the
+   Toolkit releases as HTML package and they are available from the
+   silcnet.org website as well at the documentation page.
+
+   Q: How do I compile the Toolkit on Unix?
+   A: You should read the INSTALL file from the package and follow its
+   instructions. The compilation on Unix is as simple as compiling any
+   other SILC package. Give, `./configure' command and then `make'
+   command.
+
+   Q: How do I compile the Toolkit on Win32?
+   A: We have prepared instructions to compile the Toolkit on Win32 in
+   the Toolkit package. Please, read the README.WIN32 file from the
+   package for detailed instructions how to compile the Toolkit for
+   Cygwin, MinGW and native Win32 systems. We have also prepared ready
+   MSVC++ Workspace files in the win32/ directory in the package that
+   will compile automatically the Toolkit.
+
+   Q: Does the Toolkit package include any sample code?
+   A: Yes, naturally. It includes sample codes for two different SILC
+   Client implementations, and SILC Server. The silcer/ directory
+   includes a simple GUI client based on GTK--, and Win32 samples are
+   included in the win32/ directory, for simple client.
 
-   But, IRC3 is problematic. Why? Because it still doesn't exist.  The
-   project is at the same spot where it was in 1997 when I checked it out.
-   And it was old project back then as well.  Couple of months ago I 
-   checked it again and nothing were happening.  That's the problem of IRC3
-   project.  The same almost to happened to SILC as well as I wasn't making
-   real progress over the years.  I talked to the original author of IRC,
-   Jarkko Oikarinen, in 1997 and he directed me to the IRC3 project, 
-   although he said that IRC3 is a lot of talking and not that much of 
-   anything else.  I am not trying to put down the IRC3 project but its
-   problem is that no one in the project is able to make a decision what
-   is the best way to go about making the IRC3 and I wasn't going to be
-   part of that.  The fact is that if I would've gone to IRC3 project,
-   nor IRC3 or SILC would exist today.  I think IRC3 could be something
-   really great if they just would get their act together and start
-   coding the thing.  I hope that the release of SILC gives a boost to
-   the IRC3 project as well.
diff --git a/doc/Makefile.am.pre b/doc/Makefile.am.pre
new file mode 100644 (file)
index 0000000..3945541
--- /dev/null
@@ -0,0 +1,95 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#
+#  Copyright (C) 2000 - 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+COMMONDIRS = .
+SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+DIST_SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+makerfc = ../scripts/makerfc
+
+all:
+       touch draft-riikonen-silc-spec-04.txt
+       touch draft-riikonen-silc-pp-04.txt
+       touch draft-riikonen-silc-ke-auth-04.txt
+       touch draft-riikonen-silc-commands-02.txt
+       -cd ..
+
+if SILC_DIST_CLIENT
+dist-hook:
+       rm draft-riikonen*.txt
+       touch draft-riikonen-silc-spec-04.txt
+       touch draft-riikonen-silc-pp-04.txt
+       touch draft-riikonen-silc-ke-auth-04.txt
+       touch draft-riikonen-silc-commands-02.txt
+else
+if SILC_DIST_TOOLKIT
+dist-hook:
+       -@if test -f ../util/robodoc/Source/robodoc ; then \
+         echo Generating Toolkit Reference Manual ; \
+         rm -rf toolkit ; mkdir toolkit ; cd ../scripts/silcdoc ; \
+         ./silcdoc HTML ../../lib/ ../../doc/toolkit \
+         ../../util/robodoc/Source/robodoc ; \
+       else  \
+         echo ROBODoc is not compiled! Cannot generate documentation! ; \
+        fi
+       touch draft-riikonen-silc-spec-04.txt
+       touch draft-riikonen-silc-pp-04.txt
+       touch draft-riikonen-silc-ke-auth-04.txt
+       touch draft-riikonen-silc-commands-02.txt
+       $(makerfc) draft-riikonen-silc-spec-04.nroff \
+               draft-riikonen-silc-spec-04.txt
+       $(makerfc) draft-riikonen-silc-pp-04.nroff \
+               draft-riikonen-silc-pp-04.txt
+       $(makerfc) draft-riikonen-silc-ke-auth-04.nroff \
+               draft-riikonen-silc-ke-auth-04.txt
+       $(makerfc) draft-riikonen-silc-commands-02.nroff \
+               draft-riikonen-silc-commands-02.txt
+else
+dist-hook:
+       touch draft-riikonen-silc-spec-04.txt
+       touch draft-riikonen-silc-pp-04.txt
+       touch draft-riikonen-silc-ke-auth-04.txt
+       touch draft-riikonen-silc-commands-02.txt
+       $(makerfc) draft-riikonen-silc-spec-04.nroff \
+               draft-riikonen-silc-spec-04.txt
+       $(makerfc) draft-riikonen-silc-pp-04.nroff \
+               draft-riikonen-silc-pp-04.txt
+       $(makerfc) draft-riikonen-silc-ke-auth-04.nroff \
+               draft-riikonen-silc-ke-auth-04.txt
+       $(makerfc) draft-riikonen-silc-commands-02.nroff \
+               draft-riikonen-silc-commands-02.txt
+endif
+endif
+
+if SILC_DIST_TOOLKIT
+SILC_EXTRA_DIST = toolkit examples *.gif
+else
+if SILC_DIST_SERVER
+SILC_EXTRA_DIST = examples
+else
+SILC_EXTRA_DIST =
+endif
+endif
+
+EXTRA_DIST = \
+       CodingStyle \
+       FAQ \
+       example_silcd.conf \
+       example_silc.conf \
+       draft-riikonen*.txt $(SILC_EXTRA_DIST)
diff --git a/doc/Makefile.in b/doc/Makefile.in
deleted file mode 100644 (file)
index ed43946..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-# Makefile.in generated automatically by automake 1.3 from Makefile.am
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-#
-#  Makefile.am
-#
-#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-#
-#  Copyright (C) 2000 Pekka Riikonen
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-
-
-SHELL = /bin/sh
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-prefix = @prefix@
-exec_prefix = @exec_prefix@
-
-bindir = @bindir@
-sbindir = @sbindir@
-libexecdir = @libexecdir@
-datadir = @datadir@
-sysconfdir = @sysconfdir@
-sharedstatedir = @sharedstatedir@
-localstatedir = @localstatedir@
-libdir = @libdir@
-infodir = @infodir@
-mandir = @mandir@
-includedir = @includedir@
-oldincludedir = /usr/include
-
-DISTDIR =
-
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-
-top_builddir = ..
-
-ACLOCAL = @ACLOCAL@
-AUTOCONF = @AUTOCONF@
-AUTOMAKE = @AUTOMAKE@
-AUTOHEADER = @AUTOHEADER@
-
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-transform = @program_transform_name@
-
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_alias = @build_alias@
-build_triplet = @build@
-host_alias = @host_alias@
-host_triplet = @host@
-target_alias = @target_alias@
-target_triplet = @target@
-CC = @CC@
-LN_S = @LN_S@
-MAKEINFO = @MAKEINFO@
-PACKAGE = @PACKAGE@
-RANLIB = @RANLIB@
-VERSION = @VERSION@
-
-AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
-
-EXTRA_DIST = \
-       CodingStyle \
-       example_silcd.conf \
-       example_silc.conf \
-       draft-riikonen*.txt
-mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
-CONFIG_HEADER = ../includes/silcdefs.h
-CONFIG_CLEAN_FILES = 
-DIST_COMMON =  Makefile.am Makefile.in
-
-
-DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
-
-TAR = tar
-GZIP = --best
-all: Makefile
-
-.SUFFIXES:
-$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
-       cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile
-
-Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
-       cd $(top_builddir) \
-         && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
-
-tags: TAGS
-TAGS:
-
-
-distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
-
-subdir = doc
-
-distdir: $(DISTFILES)
-       @for file in $(DISTFILES); do \
-         d=$(srcdir); \
-         test -f $(distdir)/$$file \
-         || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
-         || cp -p $$d/$$file $(distdir)/$$file; \
-       done
-       $(MAKE) top_distdir="$(top_distdir)" distdir="$(distdir)" dist-hook
-info:
-dvi:
-check: all
-       $(MAKE)
-installcheck:
-install-exec: 
-       @$(NORMAL_INSTALL)
-
-install-data: 
-       @$(NORMAL_INSTALL)
-
-install: install-exec install-data all
-       @:
-
-uninstall: 
-
-install-strip:
-       $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install
-installdirs:
-
-
-mostlyclean-generic:
-       -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
-
-clean-generic:
-       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
-       -rm -f Makefile $(DISTCLEANFILES)
-       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
-       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-
-maintainer-clean-generic:
-       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-mostlyclean:  mostlyclean-generic
-
-clean:  clean-generic mostlyclean
-
-distclean:  distclean-generic clean
-       -rm -f config.status
-
-maintainer-clean:  maintainer-clean-generic distclean
-       @echo "This command is intended for maintainers to use;"
-       @echo "it deletes files that may require special tools to rebuild."
-
-.PHONY: tags distdir info dvi installcheck install-exec install-data \
-install uninstall all installdirs mostlyclean-generic distclean-generic \
-clean-generic maintainer-clean-generic clean mostlyclean distclean \
-maintainer-clean
-
-
-all:
-       touch draft-riikonen-silc-spec-00.txt
-       touch draft-riikonen-silc-pp-00.txt
-       touch draft-riikonen-silc-ke-auth-00.txt
-       -cd ..
-
-dist-hook:
-       -rm -f draft-riikonen*.txt
-       ./makerfc draft-riikonen-silc-spec-00.nroff \
-               draft-riikonen-silc-spec-00.txt
-       ./makerfc draft-riikonen-silc-pp-00.nroff \
-               draft-riikonen-silc-pp-00.txt
-       ./makerfc draft-riikonen-silc-ke-auth-00.nroff \
-               draft-riikonen-silc-ke-auth-00.txt
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/doc/draft-riikonen-silc-commands-00.nroff b/doc/draft-riikonen-silc-commands-00.nroff
new file mode 100644 (file)
index 0000000..47a5afc
--- /dev/null
@@ -0,0 +1,1907 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 25 April 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-commands-00.txt                      25 April 2001
+Expires: 25 October 2001
+
+.in 3
+
+.ce 2
+SILC Commands
+<draft-riikonen-silc-commands-00.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  The
+SILC Commands are very important part of the SILC protocol.  Usually
+the commands are used by SILC clients to manage the SILC session, but
+also SILC servers may use the commands.  This memo specifies detailed
+command messages and command reply messages.
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  2
+2 SILC Commands .................................................  2
+  2.1 SILC Commands Syntax ......................................  2
+  2.2 SILC Commands List ........................................  4
+  2.3 SILC Command Status Types ................................. 32
+      2.3.1 SILC Command Status Payload ......................... 32
+      2.3.2 SILC Command Status List ............................ 32
+3 Security Considerations ....................................... 37
+4 References .................................................... 37
+5 Author's Address .............................................. 39
+
+
+.ti 0
+1. Introduction
+
+This document describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+document specifies detailed command messages and command reply messages.
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+See the [SILC1] for the requirements and the restrictions for the usage
+of the SILC commands.  The [SILC2] defines the command packet type and
+the Command Payload which is actually used to deliver the commands and
+command reply messages.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Commands
+
+.ti 0
+2.1 SILC Commands Syntax
+
+This section briefly describes the syntax of the command notions
+in this document.  Every field in command is separated from each
+other by whitespaces (` ') indicating that each field is independent
+argument and each argument MUST have own Command Argument Payload.
+The number of maximum arguments are defined with each command
+separately.  The Command Argument Payload is described in [SILC2].
+
+Every command defines specific number for each argument.  Currently,
+they are defined in ascending order; first argument has number one 
+(1), second has number two (2) and so on.  This number is set into the
+Argument Type field in the Command Argument Payload.  This makes it
+possible to send the arguments in free order as the number MUST be
+used to identify the type of the argument.  This makes is it also
+possible to have multiple optional arguments in commands and in
+command replies.  The number of argument is marked in parentheses
+before the actual argument.
+
+
+
+.in 6
+Example:  Arguments:  (1) <nickname> (2) <username@host>
+.in 3
+   
+
+Every command replies with Status Payload.  This payload tells the
+sender of the command whether the command was completed successfully or
+whether there was an error.  If error occurred the payload includes the
+error type.  In the next section the Status Payload is not described 
+as it is common to all commands and has been described here.  Commands 
+MAY reply with other arguments as well.  These arguments are command 
+specific and are described in the next section.
+
+Example command:
+.in 6
+
+EXAMPLE_COMMAND
+
+.in 8
+Max Arguments:  3
+    Arguments:  (1) <nickname>[@<server>]  (2) <message>
+                (3) [<count>]
+
+The command has maximum of 3 arguments.  However, only first
+and second arguments are mandatory.
+
+First argument <nickname> is mandatory but may have optional
+<nickname@server> format as well.  Second argument is mandatory
+<message> argument.  Third argument is optional <count> argument.
+
+The numbers in parentheses are the argument specific numbers
+that specify the type of the argument in Command Argument Payload.
+The receiver always knows that, say, argument number two (2) is
+<message> argument, regardless of the ordering of the arguments in
+the Command Payload.
+
+Reply messages to the command:
+
+Max Arguments:  4
+    Arguments:  (1) <Status Payload>  (2) [<channel list>]
+                (3) <idle time>       (4) [<away message>]
+
+This command may reply with maximum of 4 arguments.  However,
+only the first and third arguments are mandatory.  The numbers
+in the parentheses have the same meaning as in the upper
+command sending specification.
+
+Every command reply with <Status Payload>, it is mandatory 
+argument for all command replies and for this reason it is not
+described in the command reply descriptions.
+
+
+
+Status messages:
+
+    SILC_STATUS_OK
+    SILC_STATUS_ERR_TOO_MANY_TARGETS
+    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+    SILC_STATUS_ERR_NO_SUCH_NICK
+
+Every command reply also defines set of status message that it
+may return inside the <Status Payload>.  All status messages
+are defined in the section 2.3 SILC Command Status Types.
+
+.in 3
+Every command that has some kind of ID as argument (for example
+<Client ID>) are actually ID Payloads, defined in [SILC2] that includes
+the type of the ID, length of the ID and the actual ID data.  This
+way variable length ID's can be sent as arguments.
+
+
+.ti 0
+2.2 SILC Commands List
+
+This section lists all SILC commands, however, it is expected that a
+implementation and especially client implementation has many more
+commands that has only local affect.  These commands are official
+SILC commands that has both client and server sides and cannot be
+characterized as local commands.
+
+List of all defined commands in SILC follows.
+
+.in 0
+   0    SILC_COMMAND_NONE
+
+        None.  This is reserved command and MUST NOT be sent.
+
+
+   1    SILC_COMMAND_WHOIS
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Whois command is used to query various information about specific
+        user.  The user may be requested by their nickname and server name.
+        The query may find multiple matching users as there are no unique
+        nicknames in the SILC.  The <count> option may be given to narrow
+        down the number of accepted results.  If this is not defined there
+        are no limit of accepted results.  The query may also be narrowed
+        down by defining the server name of the nickname.
+
+        It is also possible to search the user by Client ID.  If the 
+        <Client ID> is provided server MUST use it as the search value
+        instead of the <nickname>.  One of the arguments MUST be given.
+        It is also possible to define multiple Client ID's to search
+        multiple users sending only one WHOIS command.  In this case the
+        Client ID's are appended as normal arguments.  The server replies
+        in this case with only one reply message for all requested users.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  It is not allowed
+        to request all users on some server.  The WHOIS requests MUST 
+        be based on specific nickname request.
+
+        The WHOIS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still MUST
+        search its locally connected clients.  The router MUST send
+        this command to the server which owns the requested client.  That
+        server MUST reply to the command.  Server MUST NOT send whois
+        replies to the client until it has received the reply from its
+        router.
+
+        Reply messages to the command:
+
+        Max Arguments:  8
+            Arguments:  (1) <Status Payload>       (2) <Client ID> 
+                        (3) <nickname>[@<server>]  (4) <username@host> 
+                        (5) <real name>            (6) [<Channel Payload 
+                                                         list>] 
+                        (7) [<user mode>]          (8) [<idle time>]
+
+
+        This command may reply with several command reply messages to
+        form a list of results.  In this case the status payload will
+        include STATUS_LIST_START status in the first reply and
+        STATUS_LIST_END in the last reply to indicate the end of the
+        list.  If there are only one reply the status is set to normal
+        STATUS_OK.
+
+        The command replies include the Client ID of the nickname,
+        nickname and server name, user name and host name and user's real
+        name.  Client SHOULD process these replies only after the last
+        reply has been received with the STATUS_LIST_END status.  If the
+        <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        The server MAY return the list of channel the client has joined.
+        In this case the list is list of Channel Payloads.  The Mode Mask
+        in the Channel Payload (see [SILC2] and section 2.3.2.3 for the
+        Channel Payload) is the client's mode on the channel.  The list
+        is encoded by adding the Channel Payloads one after the other.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   2    SILC_COMMAND_WHOWAS
+
+        Max Arguments:  2
+            Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
+
+        Whowas.  This command is used to query history information about
+        specific user.  The user may be requested by their nickname and 
+        server name.  The query may find multiple matching users as there
+        are no unique nicknames in the SILC.  The <count> option may be
+        given to narrow down the number of accepted results.  If this
+        is not defined there are no limit of accepted results.  The query
+        may also be narrowed down by defining the server name of the 
+        nickname.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  The WHOWAS requests MUST 
+        be based on specific nickname request.
+
+        The WHOWAS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still must
+        search its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>        (2) <Client ID>
+                        (3) <nickname>[@<server>]   (4) <username@host>
+                        (5) [<real name>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with nickname and user name and host name.
+        Every server MUST keep history for some period of time of its
+        locally connected clients.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   3    SILC_COMMAND_IDENTIFY
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Identify.  Identify command is almost analogous to WHOIS command,
+        except that it does not return as much information.  Only relevant
+        information such as Client ID is returned.  This is usually used
+        to get the Client ID of a client used in the communication with
+        the client.
+
+        The query may find multiple matching users as there are no unique 
+        nicknames in the SILC.  The <count> option may be given to narrow 
+        down the number of accepted results.  If this is not defined there 
+        are no limit of accepted results.  The query may also be narrowed 
+        down by defining the server name of the nickname.
+
+        It is also possible to search the user by Client ID.  If the
+        <Client ID> is provided server must use it as the search value
+        instead of the <nickname>.  One of the arguments must be given.
+        It is also possible to define multiple Client ID's to search
+        multiple users sending only one IDENTIFY command.  In this case
+        the Client ID's are appended as normal arguments.  The server
+        replies in this case with only one reply message for all requested
+        users.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  It is not allowed
+        to request all users on some server.  The IDENTIFY requests MUST
+        be based on specific nickname request.
+
+        Implementations may not want to give interface access to this
+        command as it is hardly a command that would be used by an end user.
+        However, it must be implemented as it is used with private message
+        sending.
+
+        The IDENTIFY MUST be always sent to the router by server so that
+        all users are searched.  However, server MUST still search its
+        locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>         (2) <Client ID>
+                        (3) [<nickname>[@<server>]]  (4) [<username@host>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with Client ID of the nickname and if more
+        information is available it MAY reply with nickname and user name
+        and host name.  If the <count> option were defined in the query
+        there will be only <count> many replies from the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   4    SILC_COMMAND_NICK
+
+        Max Arguments:  1
+            Arguments:  (1) <nickname>
+
+        Set/change nickname.  This command is used to set nickname for
+        user.  Nickname MUST NOT include any spaces (` '), non-printable
+        characters, commas (`,') and any wildcard characters.  Note that
+        nicknames in SILC are case-sensitive which must be taken into
+        account when searching clients by nickname.
+
+        When nickname is changed new Client ID is generated.  Server MUST
+        distribute SILC_NOTIFY_TYPE_NICK_CHANGE to local clients on the
+        channels (if any) the client is joined on.  Then it MUST send
+        SILC_PACKET_REPLACE_ID to its primary route to replace the old
+        Client ID with the new one.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <New ID Payload>
+
+        This command is replied always with New ID Payload that is
+        generated by the server every time user changes their nickname.
+        Client receiving this payload MUST start using the received
+        Client ID as its current valid Client ID.  The New ID Payload
+        is described in [SILC2].
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NICKNAME_IN_USE
+            SILC_STATUS_ERR_BAD_NICKNAME
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   5    SILC_COMMAND_LIST
+
+        Max Arguments:  1
+            Arguments:  (1) [<Channel ID>]
+
+        The list command is used to list channels and their topics on the
+        current server.  If the <Channel ID> parameter is used, only the
+        status of that channel is displayed.  Secret channels are not
+        listed at all.  Private channels are listed with status indicating
+        that the channel is private.  Router MAY reply with all channels
+        it knows about.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel>         (4) [<topic>]
+                        (5) [<user count>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        This command replies with Channel ID, name and the topic of the
+        channel.  If the channel is private channel the <topic> SHOULD
+        include the "*private*" string.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   6    SILC_COMMAND_TOPIC
+
+        Max Arguments:  2
+            Arguments:  (1) <Channel ID>  (2) [<topic>]
+
+        This command is used to change or view the topic of a channel.
+        The topic for channel <Channel ID> is returned if there is no
+        <topic> given.  If the <topic> parameter is present, the topic
+        for that channel will be changed, if the channel modes permit
+        this action.
+
+        After setting the topic the server MUST send the notify type
+        SILC_NOTIFY_TYPE_TOPIC_SET to its primary router and then to
+        the channel which topic was changed.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <Channel ID> 
+                        (3) [<topic>]
+
+        The command may reply with the topic of the channel if it is
+        set.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   7    SILC_COMMAND_INVITE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>       (2) [<Client ID>]
+                        (3) [<adding client>]  (4) [<removing client>]
+
+        This command is used to invite other clients to join to the
+        channel.  The <Client ID> argument is the target client's ID that
+        is being invited.  The <Channel ID> is the Channel ID of the
+        requested channel.  The sender of this command MUST be on the
+        channel.  The server MUST also send the notify type
+        SILC_NOTIFY_TYPE_INVITE to its primary router and then to the
+        client indicated by the <Client ID>.
+
+        The <adding client> and <removing client> can be used to add to
+        and remove from the invite list.  The format of the <adding client>
+        and <removing client> is as follows:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        When adding to or removing from the invite list the server MUST
+        send the notify type SILC_NOTIFY_TYPE_INVITE to its primary router
+        and MUST NOT send it to the client which was added to the list.
+        The client which executes this command MUST have at least channel
+        operator privileges to be able to add to or remove from the invite
+        list.  The wildcards MAY be used with this command.  If adding or
+        removing more than one client then the lists are an comma (`,')
+        separated.
+
+        Note that the <Client ID> provided MUST be resolved into correct
+        nickname and host name and add to the invite list before sending
+        the notify packet.
+        
+        When this command is given with only <Channel ID> argument then
+        the command merely returns the invite list of the channel.   This
+        command MUST fail if the requested channel does not exist, the
+        requested <Client ID> is already on the channel or if the channel
+        is invite only channel and the caller of this command does not
+        have at least channel operator privileges.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<invite list>]
+
+       This command replies with the invite list of the channel if it
+       exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   8    SILC_COMMAND_QUIT
+
+        Max Arguments:  1
+            Arguments:  (1) [<quit message>]
+
+        This command is used by client to end SILC session.  The server
+        must close the connection to a client which sends this command.
+        if <quit message> is given it will be sent to other clients on
+        channel if the client is on channel when quitting.
+
+        Reply messages to the command:
+
+        This command does not reply anything.
+
+
+    9   SILC_COMMAND_KILL
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) [<comment>]
+
+        This command is used by SILC operators to remove a client from
+        SILC network.  The removing has temporary effects and client may
+        reconnect to SILC network.  The <Client ID> is the client to be
+        removed from SILC.  The <comment> argument may be provided to 
+        give to the removed client some information why it was removed
+        from the network.
+
+        When killing a client the router MUST first send notify type
+        SILC_NOTIFY_TYPE_KILLED to all channels the client has joined.
+        The packet MUST NOT be sent to the killed client on the channels.
+        Then, the router MUST send the same notify type to its primary
+        router.  Finally, the router MUST send the same notify type 
+        directly to the client which was killed.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   10   SILC_COMMAND_INFO
+
+        Max Arguments:  2
+            Arguments:  (1) [<server>]  (2) [<Server ID>]
+
+        This command is used to fetch various information about a server.
+        If <server> argument is specified the command MUST be sent to
+        the requested server.
+
+        If the <Server ID> is specified the server information if fetched
+        by the provided Server ID.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) <server name>     (4) <string>
+
+        This command replies with the Server ID of the server and a
+        string which tells the information about the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SERVER_ID
+
+
+   11   SILC_COMMAND_CONNECT
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used by operators to force a server to try to
+        establish a new connection to remote server or router.  The
+        Operator MUST specify the server/router to be connected by
+        setting <remote server> argument.  The port is 32 bit MSB value.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   12   SILC_COMMAND_PING
+
+        Max Arguments:  1
+            Arguments:  (1) <Server ID>
+
+        This command is used by client and server to test the communication
+        channel to its server if one suspects that the communication is not
+        working correctly.  The <Server ID> is the ID of the server the
+        sender is connected to.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.  Server returns
+        SILC_STATUS_OK in Status Payload if pinging was successful.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NOT_REGISTERED
+
+
+   13   SILC_COMMAND_OPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain server operator
+        privileges on some server or router.  Note that router operator
+        has router privileges that supersedes the server operator
+        privileges and this does not obtain those privileges.  Client
+        MUST use SILCOPER command to obtain router level privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).
+
+        After changing the mode the server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   14   SILC_COMMAND_JOIN
+
+        Max Arguments:  5
+            Arguments:  (1) <channel>       (2) <Client ID>
+                        (3) [<passphrase>]  (4) [<cipher>]
+                        (5) [<hmac>]
+
+        Join to channel/create new channel.  This command is used to
+        join to a channel.  If the channel does not exist the channel is
+        created.  If server is normal server this command MUST be sent
+        to router which will create the channel.  The channel MAY be
+        protected with passphrase.  If this is the case the passphrase
+        MUST be sent along the join command.
+
+        The name of the <channel> MUST NOT include any spaces (` '),
+        non-printable characters, commas (`,') or any wildcard characters.
+
+        The second argument <Client ID> is the Client ID of the client
+        which is joining to the client.  When client sends this command
+        to the server the <Client ID> MUST be the client's own ID.
+
+        Cipher to be used to secure the traffic on the channel MAY be
+        requested by sending the name of the requested <cipher>.  This
+        is used only if the channel does not exist and is created.  If
+        the channel already exists the cipher set previously for the
+        channel will be used to secure the traffic.  The computed MACs
+        of the channel message are produced by the default HMAC or by
+        the <hmac> provided for the command.
+
+        The server MUST check whether the user is allowed to join to
+        the requested channel.  Various modes set to the channel affect
+        the ability of the user to join the channel.  These conditions
+        are:
+
+            o  The user MUST be invited to the channel if the channel
+               is invite-only channel.
+
+            o  The Client ID/nickname/username/host name MUST NOT match
+               any active bans.
+
+            o  The correct passphrase MUST be provided if passphrase 
+               is set to the channel.
+
+            o  The user count limit, if set, MUST NOT be reached.
+
+        Reply messages to the command:
+
+        Max Arguments:  14
+            Arguments:  (1) <Status Payload>        (2) <channel> 
+                        (3) <Channel ID>            (4) <Client ID>
+                        (5) <channel mode mask>     (6) <created>
+                        (7) [<Channel Key Payload>] (8) [<ban list>]
+                        (9) [<invite list>]         (10) [<topic>]
+                        (11) [<hmac>]               (12) <list count>
+                        (13) <Client ID list>       (14) <client mode list>
+
+        This command replies with the channel name requested by the
+        client, channel ID of the channel and topic of the channel
+        if it exists.  The <Client ID> is the Client ID which was joined
+        to the channel.  It also replies with the channel mode mask
+        which tells all the modes set on the channel.  If the
+        channel is created the mode mask is zero (0).  If ban mask
+        and/or invite list is set they are sent as well.
+
+        The <list count>, <Client ID list> and <client mode list> are
+        the clients currently on the channel and their modes on the
+        channel.  The <Client ID list> is formed by adding the ID Payloads
+        one after the other.  The <client mode list> is formed by adding
+        32 bit MSB first order values one after the other.
+
+        Client receives the channel key in the reply message as well
+        inside <Channel Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_BAD_PASSWORD
+            SILC_STATUS_ERR_CHANNEL_IS_FULL
+            SILC_STATUS_ERR_NOT_INVITED
+            SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+            SILC_STATUS_ERR_BAD_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+
+
+   15   SILC_COMMAND_MOTD
+
+        Max Arguments:  1
+            Arguments:  (1) <server>
+
+        This command is used to query the Message of the Day of the server.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) [<motd>]
+
+        This command replies with the motd message if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   16   SILC_COMMAND_UMODE
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) <client mode mask>
+
+        This command is used by client to set/unset modes for itself.
+        However, there are some modes that the client MUST NOT set itself,
+        but they will be set by server.  However, client MAY unset any
+        mode.  Modes may be masked together ORing them thus having
+        several modes set.  Client MUST keep its client mode mask
+        locally so that the mode setting/unsetting would work without
+        problems.  Client may change only its own modes.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        The following client modes are defined:
+
+           0x0000    SILC_UMODE_NONE
+
+              No specific mode for client.  This is the initial
+              setting when new client is created.  The client is
+              normal client now.
+
+
+           0x0001    SILC_UMODE_SERVER_OPERATOR
+
+              Marks the user as server operator.  Client MUST NOT
+              set this mode itself.  Server sets this mode to the
+              client when client attains the server operator
+              privileges by SILC_COMMAND_OPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0002    SILC_UMODE_ROUTER_OPERATOR
+
+              Marks the user as router (SILC) operator.  Client
+              MUST NOT this mode itself.  Router sets this mode to
+              the client when client attains the router operator
+              privileges by SILC_COMMAND_SILCOPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0004    SILC_UMODE_GONE
+
+              Marks that the user is not currently present in the
+              SILC Network.  Client MAY set and unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <client mode mask>
+
+        This command replies with the changed client mode mask that
+        the client MUST to keep locally.
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_BAD_CLIENT_ID
+            SILC_STATUS_ERR_NOT_YOU
+            SILC_STATUS_ERR_PERM_DENIED
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   17   SILC_COMMAND_CMODE
+
+        Max Arguments:  7
+            Arguments:  (1) <Channel ID>      (2) <channel mode mask>
+                        (3) [<user limit>]    (4) [<passphrase>]
+                        (5) [<cipher>]        (6) [<hmac>]
+                        (7) [<auth payload>]
+
+        This command is used by client to set or change channel flags on
+        a channel.  Channel has several modes that set various properties
+        of a channel.  Modes may be masked together by ORing them thus
+        having several modes set.  The <Channel ID> is the ID of the
+        target channel.  The client changing channel mode MUST be on
+        the same channel and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CMODE_CHANGE notify
+        type MUST be distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CMODE_NONE
+
+              No specific mode on channel.  This is the default when
+              channel is created.  This means that channel is just plain
+              normal channel.
+
+
+           0x0001    SILC_CMODE_PRIVATE
+
+              Channel is private channel.  Private channels are shown
+              in the channel list listed with SILC_COMMAND_LIST command
+              with indication that the channel is private.  Also,
+              client on private channel will no be detected to be on
+              the channel as the channel is not shown in the client's
+              currently joined channel list.  Channel founder and 
+              channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]p on user interface
+              to set/unset this mode.
+
+
+           0x0002    SILC_CMODE_SECRET
+
+              Channel is secret channel.  Secret channels are not shown
+              in the list listed with SILC_COMMAND_LIST command.  Secret
+              channels can be considered to be invisible channels.
+              Channel founder and channel operator MAY set/unset this
+              mode.
+
+              Typical implementation would use [+|-]s on user interface
+              to set/unset this mode.
+
+
+           0x0004    SILC_CMODE_PRIVKEY
+
+              Channel uses private channel key to protect the traffic
+              on the channel.  When this mode is set the client will be
+              responsible to set the key it wants to use to encrypt and
+              decrypt the traffic on channel.  Server generated channel
+              keys are not used at all.  This mode provides additional
+              security as clients on channel may agree to use private
+              channel key that even servers do not know.  Naturally,
+              this requires that every client on the channel knows
+              the key before hand (it is considered to be pre-shared-
+              key).  The key material is RECOMMENDED to be processed
+              as stated in the [SILC3] in the section Processing the
+              Key Material.
+
+              As it is local setting it is possible to have several
+              private channel keys on one channel.  In this case several
+              clients can talk on same channel but only those clients
+              that share the key with the message sender will be able
+              to hear the talking.  Client SHOULD NOT display those
+              message for the end user that it is not able to decrypt
+              when this mode is set.
+
+              Only channel founder MAY set/unset this mode.  If this
+              mode is unset the server will distribute new channel
+              key to all clients on the channel which will be used
+              thereafter.
+
+              Typical implementation would use [+|-]k on user interface
+              to set/unset this mode.
+
+
+           0x0008    SILC_CMODE_INVITE
+
+              Channel is invite only channel.  Client may join to this
+              channel only if it is invited to the channel.  Channel
+              founder and channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]i on user interface
+              to set/unset this mode.
+
+
+           0x0010    SILC_CMODE_TOPIC
+
+              The topic of the channel may only be set by client that
+              is channel founder or channel operator.  Normal clients
+              on channel will not be able to set topic when this mode
+              is set.  Channel founder and channel operator MAY set/
+              unset this mode.
+
+              Typical implementation would use [+|-]t on user interface
+              to set/unset this mode.
+
+
+           0x0020    SILC_CMODE_ULIMIT
+
+              User limit has been set to the channel.  New clients
+              may not join to the channel when the limit set is
+              reached.  Channel founder and channel operator MAY set/
+              unset the limit.  The <user limit> argument is the
+              number of limited users.
+
+              Typical implementation would use [+|-]l on user interface
+              to set/unset this mode.
+
+
+           0x0040    SILC_CMODE_PASSPHRASE
+
+              Passphrase has been set to the channel.  Client may
+              join to the channel only if it is able to provide the
+              correct passphrase.  Setting passphrases to channel
+              is entirely safe as all commands are protected in the
+              SILC network.  Only channel founder MAY set/unset
+              the passphrase.  The <passphrase> argument is the
+              set passphrase.
+
+              Typical implementation would use [+|-]a on user interface
+              to set/unset this mode.
+
+
+           0x0080    SILC_CMODE_CIPHER
+
+              Sets specific cipher to be used to protect channel
+              traffic.  The <cipher> argument is the requested cipher.
+              When set or unset the server must re-generate new
+              channel key.  Only channel founder MAY set the cipher of 
+              the channel.  When unset the new key is generated using
+              default cipher for the channel.
+
+              Typical implementation would use [+|-]c on user interface
+              to set/unset this mode.
+
+
+           0x0100    SILC_CMODE_HMAC
+
+              Sets specific hmac to be used to compute the MACs of the
+              channel message.  The <hmac> argument is the requested hmac.
+              Only channel founder may set the hmac of the channel.
+
+              Typical implementation would use [+|-]h on user interface
+              to set/unset this mode.
+
+
+           0x0200    SILC_CMODE_FOUNDER_AUTH
+
+              Channel founder may set this mode to be able to regain
+              channel founder rights even if the client leaves the 
+              channel.  The <auth payload> is the Authentication Payload
+              consisting of the authentication method and authentication
+              data to be used in the authentication.  The server MUST
+              NOT accept NONE authentication method.  Also, if the 
+              method is public key authentication the server MUST NOT
+              save the authentication data from the payload as the
+              data is different on all authentications.  In this case the
+              server only saves the authentication method.
+
+              Note that this mode is effective only in the current server.
+              The client MUST connect to the same server later to be able
+              to regain the channel founder rights.  The server MUST save
+              the public key of the channel founder and use that to identify
+              the client which is claiming the channel founder rights.
+              The rights may be claimed by the SILC_CUMODE_FOUNDER 
+              channel user mode using SILC_COMMAND_CUMODE command.  The
+              set authentication data remains valid as long as the channel
+              exists or until the founder unsets this mode.
+
+              Typical implementation would use [+|-]f on user interface
+              to set/unset this mode.
+
+        To make the mode system work, client MUST keep the channel mode
+        mask locally so that the mode setting and unsetting would work
+        without problems.  The client receives the initial channel mode
+        mask when it joins to the channel.  When the mode changes on
+        channel the server MUST distribute the changed channel mode mask
+        to all clients on the channel by sending the notify type
+        SILC_NOTIFY_TYPE_CMODE_CHANGE.  The notify type MUST also be sent
+        to the server's primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <channel mode mask>
+
+        This command replies with the changed channel mode mask that
+        client MUST keep locally.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+
+   18   SILC_COMMAND_CUMODE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>    (2) <mode mask>
+                        (3) <Client ID>     (4) [<auth payload>]
+
+        This command is used by client to change channel user modes on
+        channel.  Users on channel may have some special modes and this
+        command is used by channel operators to set or change these modes.
+        The <Channel ID> is the ID of the target channel.  The <mode mask>
+        is OR'ed mask of modes.  The <Client ID> is the target client.
+        The client changing channel user modes MUST be on the same channel
+        as the target client and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CUMODE_CHANGE notify
+        type is distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CUMODE_NONE
+
+              No specific mode.  This is the normal situation for client.
+              Also, this is the mode set when removing all modes from
+              the target client.
+
+
+           0x0001    SILC_CUMODE_FOUNDER
+
+              The client is channel founder of the channel.  Usually this
+              mode is set only by the server when the channel was created.
+              However, if the SILC_CMODE_FOUNDER_AUTH channel mode has
+              been set, the client can claim channel founder privileges
+              by providing the <auth payload> that the server will use
+              to authenticate the client.  The client MAY remove this
+              mode at any time.
+
+
+           0x0002    SILC_CUMODE_OPERATOR
+
+              Sets channel operator privileges on the channel for a
+              client on the channel.  Channel founder and channel operator
+              MAY set/unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <channel user mode mask>
+                        (3) <Client ID>
+
+        This command replies with the changed channel user mode mask that
+        client MUST keep locally.  The <Client ID> is the target client.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   19   SILC_COMMAND_KICK
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>  (2) <Client ID>  
+                        (3) [<comment>]
+
+        This command is used by channel operators to remove a client from
+        channel.  The <channel> argument is the channel the client to be
+        removed is on currently.  Note that the "kicker" must be on the same
+        channel.  If <comment> is provided it will be sent to the removed
+        client.
+
+        After kicking the client the server MUST send the notify type
+        SILC_NOTIFY_TYPE_KICKED to the channel and to its primary router.
+        The channel key MUST also be re-generated after kicking, unless
+        the SILC_CMODE_PRIVKEY mode is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   20   SILC_COMMAND_BAN
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                        (3) [<removing client>]
+
+        This command is used to manage the ban list of the channel
+        indicated by the <Channel ID>.  A client that is banned from
+        channel is no longer able to join the channel.  The client which
+        is executing this command MUST have at least channel operator
+        privileges on the channel.
+
+        The <adding client> and <removing client> are used to add to and
+        remove from the ban list.  The format of the <adding client> and
+        the <removing client> is of following format:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        The server MUST send the notify type SILC_NOTIFY_TYPE_BAN to its
+        primary router after adding to or removing from the ban list.
+        The wildcards MAY be used with this command.  If adding or removing
+        from than one clients then the lists are an comma (`,') separated.
+
+        If this command is executed without the ban arguments the command
+        merely replies with the current ban list.
+
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<ban list>]
+
+        This command replies with the <Channel ID> of the channel and
+        the current <ban list> of the channel if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   21   SILC_COMMAND_CLOSE
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used only by operator to close connection to a
+        remote site.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   22   SILC_COMMAND_SHUTDOWN
+
+        Max Arguments:  0
+            Arguments:  None
+
+        This command is used only by operator to shutdown the server.
+        All connections to the server will be closed and the server is
+        shutdown.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+
+
+   23   SILC_COMMAND_SILCOPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain router operator
+        privileges (also known as SILC operator) on the router.  Note
+        that router operator has privileges that supersedes the server
+        operator privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).
+
+        Difference between router operator and server operator is that
+        router operator is able to handle cell level properties while
+        server operator (even on router server) is able to handle only
+        local properties, such as, local connections and normal server
+        administration.  The router operator is also able to use the
+        SILC_COMMAND_KILL command.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   24   SILC_COMMAND_LEAVE
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used by client to leave a channel the client is
+        joined to. 
+
+        When leaving channel the server MUST send the notify type
+        SILC_NOTIFY_TYPE_LEAVE to its primary router and to the channel.
+        The channel key MUST also be re-generated when leaving the channel
+        and distribute it to all clients still currently on the channel.
+        The key MUST NOT be re-generated if the SILC_CMODE_PRIVKEY mode
+        is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+
+
+   25   SILC_COMMAND_USERS
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used to list user names currently on the requested
+        channel; argument <Channel ID>.  The server MUST resolve the
+        user names and send a comma (`,') separated list of user names
+        on the channel.  Server or router MAY resolve the names by sending
+        SILC_COMMAND_WHOIS or SILC_COMMAND_IDENTIFY commands.
+
+        If the requested channel is a private or secret channel, this
+        command MUST NOT send the list of users, as private and secret
+        channels cannot be seen by outside.  In this case the returned
+        name list MAY include a indication that the server could not 
+        resolve the names of the users on the channel.  Also, in this case
+        Client ID's or client modes are not sent either.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <list count>      (4) <Client ID list>
+                        (5) <client mode list>
+
+        This command replies with the Channel ID of the requested channel
+        Client ID list of the users on the channel and list of their modes.
+        The Client ID list has Client ID's of all users in the list.  The 
+        <Client ID list> is formed by adding Client ID's one after another.
+        The <client mode list> is formed by adding client's user modes on
+        the channel one after another (4 bytes (32 bits) each).  The <list 
+        count> of length of 4 bytes (32 bits), tells the number of entries
+        in the lists.  Both lists MUST have equal number of entries.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+
+   26   SILC_COMMAND_GETKEY
+
+        Max Arguments:  1
+            Arguments:  (1) <ID Payload>
+
+        This command is used to fetch the public key of the client or
+        server indicated by the <ID Payload>.  The public key is fetched
+        from the server where to the client is connected.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>      (2) <ID Payload>
+                        (3) <Public Key Payload>
+
+        This command replies with the client's or server's ID and with
+        the <Public Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   27 - 199
+
+        Currently undefined commands.
+
+
+   200 - 254
+
+        These commands are reserved for private use and will not be defined
+        in this document.
+
+
+   255  SILC_COMMAND_MAX   
+
+        Reserved command.  This must not be sent.
+.in 3
+
+
+.ti 0
+2.3 SILC Command Status Types
+
+.ti 0
+2.3.1 SILC Command Status Payload
+
+Command Status Payload is sent in command reply messages to indicate
+the status of the command.  The payload is one of argument in the
+command thus this is the data area in Command Argument Payload described
+in [SILC2].  The payload is only 2 bytes of length.  The following diagram
+represents the Command Status Payload (field is always in MSB order).
+
+
+.in 21
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Status Message         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  SILC Command Status Payload
+
+
+.in 6
+o Status Message (2 bytes) - Indicates the status message.
+  All Status messages are described in the next section.
+.in 3
+
+
+.ti 0
+2.3.2 SILC Command Status List
+
+Command Status messages are returned in the command reply messages
+to indicate whether the command were executed without errors.  If error
+has occurred the status indicates which error occurred.  Status payload
+only sends numeric reply about the status.  Receiver of the payload must
+convert the numeric values into human readable error messages.  The
+list of status messages below has an example human readable error
+messages that client may display for the user.
+
+List of all defined command status messages following.
+
+.in 0
+   Generic status messages:
+
+   0    SILC_STATUS_OK
+
+        Ok status.  Everything went Ok.  The status payload maybe
+        safely ignored in this case.
+
+   1    SILC_STATUS_LIST_START
+
+        Start of the list.  There will be several command replies and
+        this reply is the start of the list.
+
+   2    SILC_STATUS_LIST_ITEM
+
+        Item in the list.  This is one of the item in the list but not the
+        first or last one.
+
+   3    SILC_STATUS_LIST_END
+
+        End of the list.  There were several command replies and this
+        reply is the last of the list.  There won't be other replies
+        belonging to this list after this one.
+
+   4 - 9
+
+        Currently undefined and has been reserved for the future.
+
+
+   Error status message:
+
+
+
+   10   SILC_STATUS_ERR_NO_SUCH_NICK
+
+        "No such nickname".  Requested nickname does not exist.
+
+   11   SILC_STATUS_ERR_NO_SUCH_CHANNEL
+
+        "No such channel".  Requested channel name does not exist.
+
+   12   SILC_STATUS_ERR_NO_SUCH_SERVER
+
+        "No such server".  Requested server name does not exist.
+
+   13   SILC_STATUS_ERR_TOO_MANY_TARGETS
+
+        "Duplicate recipients. No message delivered".  Message were
+        tried to be sent to recipient which has several occurrences in 
+        the recipient list.
+
+   14   SILC_STATUS_ERR_NO_RECIPIENT
+
+        "No recipient given".  Command required recipient which was
+        not provided.
+
+   15   SILC_STATUS_ERR_UNKNOWN_COMMAND
+
+        "Unknown command".  Command sent to server is unknown by the
+        server.
+
+   16   SILC_STATUS_ERR_WILDCARDS
+
+        "Wildcards cannot be used".  Wildcards were provided but they
+        weren't permitted.
+
+   17   SILC_STATUS_ERR_NO_CLIENT_ID
+
+        "No Client ID given".  Client ID were expected as command
+        parameter but were not found.
+
+   18   SILC_STATUS_ERR_NO_CHANNEL_ID
+
+        "No Channel ID given".  Channel ID were expected as command
+        parameter but were not found.
+
+   19   SILC_STATUS_ERR_NO_SERVER_ID
+
+        "No Serve ID given".  Server ID were expected as command
+        parameter but were not found.
+
+   20   SILC_STATUS_ERR_BAD_CLIENT_ID
+
+        "Bad Client ID".  Client ID provided were erroneous.
+
+   21   SILC_STATUS_ERR_BAD_CHANNEL_ID
+
+        "Bad Channel ID".  Channel ID provided were erroneous.
+
+   22   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+        "No such Client ID".  Client ID provided does not exist.
+
+   23   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+
+        "No such Channel ID".  Channel ID provided does not exist.
+
+   24   SILC_STATUS_ERR_NICKNAME_IN_USE
+
+        "Nickname already exists".  Nickname created could not be 
+        registered because number of same nicknames were already set to
+        maximum.  This is not expected to happen in real life but is
+        possible to occur.
+
+   25   SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+        "You are not on that channel".  The command were specified for
+        channel user is not currently on.
+
+   26   SILC_STATUS_ERR_USER_NOT_ON_CHANNEL
+
+        "They are not on channel".  The requested target client is not
+        on requested channel.
+
+   27   SILC_STATUS_ERR_USER_ON_CHANNEL
+
+        "User already on channel".  User were invited on channel they
+        already are on.
+
+   28   SILC_STATUS_ERR_NOT_REGISTERED
+
+        "You have not registered".  User executed command that requires
+        the client to be registered on the server before it may be
+        executed.
+
+   29   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+
+        "Not enough parameters".  Command requires more parameters
+        than provided.
+
+   30   SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+        "Too many parameters".  Too many parameters were provided
+        for the command.
+
+   31   SILC_STATUS_ERR_PERM_DENIED
+
+        "Permission denied".  Generic permission denied error status
+        to indicate disallowed access.
+
+   32   SILC_STATUS_ERR_BANNED_FROM_SERVER
+
+        "You are banned from this server".  The client tried to register
+        on server that has explicitly denied this host to connect.
+
+   33   SILC_STATUS_ERR_BAD_PASSWORD
+
+        "Cannot join channel. Incorrect password".  Password provided for 
+        channel were not accepted.
+
+   34   SILC_STATUS_ERR_CHANNEL_IS_FULL
+
+        "Cannot join channel. Channel is full".  The channel is full
+        and client cannot be joined to it.
+
+   35   SILC_STATUS_ERR_NOT_INVITED
+
+        "Cannot join channel. You have not been invited".  The channel
+        is invite only channel and client has not been invited.
+
+   36   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+
+        "Cannot join channel. You have been banned".  The client has
+        been banned from the channel.
+
+   37   SILC_STATUS_ERR_UNKNOWN_MODE
+
+        "Unknown mode".  Mode provided by the client were unknown to
+        the server.
+
+   38   SILC_STATUS_ERR_NOT_YOU
+
+        "Cannot change mode for other users".  User tried to change
+        someone else's mode.
+
+   39   SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+        "Permission denied. You are not channel operator".  Command may 
+        be executed only by channel operator.
+
+   40   SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
+
+        "Permission denied. You are not channel founder".  Command may 
+        be executed only by channel operator.
+
+   41   SILC_STATUS_ERR_NO_SERVER_PRIV
+
+        "Permission denied. You are not server operator".  Command may
+        be executed only by server operator.
+
+   42   SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+        "Permission denied. You are not SILC operator".  Command may be
+        executed only by router (SILC) operator.
+
+   43   SILC_STATUS_ERR_BAD_NICKNAME
+
+        "Bad nickname".  Nickname requested contained illegal characters
+        or were malformed.
+
+   44   SILC_STATUS_ERR_BAD_CHANNEL
+
+        "Bad channel name".  Channel requested contained illegal characters
+        or were malformed.
+
+   45   SILC_STATUS_ERR_AUTH_FAILED
+
+        "Authentication failed".  The authentication data sent as 
+        argument were wrong and thus authentication failed.
+
+   46   SILC_STATUS_ERR_UNKOWN_ALGORITHM
+
+        "The algorithm was not supported."  The server does not support the
+        requested algorithm.
+
+   47   SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+        "No such Server ID".  Server ID provided does not exist.
+
+.in 3
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 25 October 2001 
diff --git a/doc/draft-riikonen-silc-commands-01.nroff b/doc/draft-riikonen-silc-commands-01.nroff
new file mode 100644 (file)
index 0000000..34d3c2c
--- /dev/null
@@ -0,0 +1,1935 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 21 August 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-commands-01.txt                     21 August 2001
+Expires: 21 February 2002
+
+.in 3
+
+.ce 2
+SILC Commands
+<draft-riikonen-silc-commands-01.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  The
+SILC Commands are very important part of the SILC protocol.  Usually
+the commands are used by SILC clients to manage the SILC session, but
+also SILC servers may use the commands.  This memo specifies detailed
+command messages and command reply messages.
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  2
+2 SILC Commands .................................................  2
+  2.1 SILC Commands Syntax ......................................  2
+  2.2 SILC Commands List ........................................  4
+  2.3 SILC Command Status Types ................................. 32
+      2.3.1 SILC Command Status Payload ......................... 32
+      2.3.2 SILC Command Status List ............................ 32
+3 Security Considerations ....................................... 37
+4 References .................................................... 38
+5 Author's Address .............................................. 39
+
+
+.ti 0
+1. Introduction
+
+This document describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+document specifies detailed command messages and command reply messages.
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+See the [SILC1] for the requirements and the restrictions for the usage
+of the SILC commands.  The [SILC2] defines the command packet type and
+the Command Payload which is actually used to deliver the commands and
+command reply messages.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Commands
+
+.ti 0
+2.1 SILC Commands Syntax
+
+This section briefly describes the syntax of the command notions
+in this document.  Every field in command is separated from each
+other by whitespaces (` ') indicating that each field is independent
+argument and each argument MUST have own Command Argument Payload.
+The number of maximum arguments are defined with each command
+separately.  The Command Argument Payload is described in [SILC2].
+
+Every command defines specific number for each argument.  Currently,
+they are defined in ascending order; first argument has number one 
+(1), second has number two (2) and so on.  This number is set into the
+Argument Type field in the Command Argument Payload.  This makes it
+possible to send the arguments in free order as the number MUST be
+used to identify the type of the argument.  This makes is it also
+possible to have multiple optional arguments in commands and in
+command replies.  The number of argument is marked in parentheses
+before the actual argument.
+
+
+
+.in 6
+Example:  Arguments:  (1) <nickname> (2) <username@host>
+.in 3
+   
+
+Every command replies with Status Payload.  This payload tells the
+sender of the command whether the command was completed successfully or
+whether there was an error.  If error occurred the payload includes the
+error type.  In the next section the Status Payload is not described 
+as it is common to all commands and has been described here.  Commands 
+MAY reply with other arguments as well.  These arguments are command 
+specific and are described in the next section.
+
+Example command:
+.in 6
+
+EXAMPLE_COMMAND
+
+.in 8
+Max Arguments:  3
+    Arguments:  (1) <nickname>[@<server>]  (2) <message>
+                (3) [<count>]
+
+The command has maximum of 3 arguments.  However, only first
+and second arguments are mandatory.
+
+First argument <nickname> is mandatory but may have optional
+<nickname@server> format as well.  Second argument is mandatory
+<message> argument.  Third argument is optional <count> argument.
+
+The numbers in parentheses are the argument specific numbers
+that specify the type of the argument in Command Argument Payload.
+The receiver always knows that, say, argument number two (2) is
+<message> argument, regardless of the ordering of the arguments in
+the Command Payload.
+
+Reply messages to the command:
+
+Max Arguments:  4
+    Arguments:  (1) <Status Payload>  (2) [<channel list>]
+                (3) <idle time>       (4) [<away message>]
+
+This command may reply with maximum of 4 arguments.  However,
+only the first and third arguments are mandatory.  The numbers
+in the parentheses have the same meaning as in the upper
+command sending specification.
+
+Every command reply with <Status Payload>, it is mandatory 
+argument for all command replies and for this reason it is not
+described in the command reply descriptions.
+
+
+
+Status messages:
+
+    SILC_STATUS_OK
+    SILC_STATUS_ERR_TOO_MANY_TARGETS
+    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+    SILC_STATUS_ERR_NO_SUCH_NICK
+
+Every command reply also defines set of status message that it
+may return inside the <Status Payload>.  All status messages
+are defined in the section 2.3 SILC Command Status Types.
+
+.in 3
+Every command that has some kind of ID as argument (for example
+<Client ID>) are actually ID Payloads, defined in [SILC2] that includes
+the type of the ID, length of the ID and the actual ID data.  This
+way variable length ID's can be sent as arguments.
+
+
+.ti 0
+2.2 SILC Commands List
+
+This section lists all SILC commands, however, it is expected that a
+implementation and especially client implementation has many more
+commands that has only local affect.  These commands are official
+SILC commands that has both client and server sides and cannot be
+characterized as local commands.
+
+List of all defined commands in SILC follows.
+
+.in 0
+   0    SILC_COMMAND_NONE
+
+        None.  This is reserved command and MUST NOT be sent.
+
+
+   1    SILC_COMMAND_WHOIS
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Whois command is used to query various information about specific
+        user.  The user may be requested by their nickname and server name.
+        The query may find multiple matching users as there are no unique
+        nicknames in the SILC.  The <count> option may be given to narrow
+        down the number of accepted results.  If this is not defined there
+        are no limit of accepted results.  The query may also be narrowed
+        down by defining the server name of the nickname.  The <count> is
+        int string format.
+
+        It is also possible to search the user by Client ID.  If the 
+        <Client ID> is provided server MUST use it as the search value
+        instead of the <nickname>.  One of the arguments MUST be given.
+        It is also possible to define multiple Client ID's to search
+        multiple users sending only one WHOIS command.  In this case the
+        Client ID's are appended as normal arguments.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  It is not allowed
+        to request all users on some server.  The WHOIS requests MUST 
+        be based on specific nickname request.
+
+        The WHOIS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still MUST
+        search its locally connected clients.  The router MUST send
+        this command to the server which owns the requested client.  That
+        server MUST reply to the command.  Server MUST NOT send whois
+        replies to the client until it has received the reply from its
+        router.
+
+        Reply messages to the command:
+
+        Max Arguments:  8
+            Arguments:  (1) <Status Payload>       (2) <Client ID> 
+                        (3) <nickname>[@<server>]  (4) <username@host> 
+                        (5) <real name>            (6) [<Channel Payload 
+                                                         list>] 
+                        (7) [<user mode>]          (8) [<idle time>]
+
+
+        This command may reply with several command reply messages to
+        form a list of results.  In this case the status payload will
+        include STATUS_LIST_START status in the first reply and
+        STATUS_LIST_END in the last reply to indicate the end of the
+        list.  If there are only one reply the status is set to normal
+        STATUS_OK.
+
+        The command replies include the Client ID of the nickname,
+        nickname and server name, user name and host name and user's real
+        name.  Client SHOULD process these replies only after the last
+        reply has been received with the STATUS_LIST_END status.  If the
+        <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        The server MAY return the list of channel the client has joined.
+        In this case the list is list of Channel Payloads.  The Mode Mask
+        in the Channel Payload (see [SILC2] and section 2.3.2.3 for the
+        Channel Payload) is the client's mode on the channel.  The list
+        is encoded by adding the Channel Payloads one after the other.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   2    SILC_COMMAND_WHOWAS
+
+        Max Arguments:  2
+            Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
+
+        Whowas.  This command is used to query history information about
+        specific user.  The user may be requested by their nickname and 
+        server name.  The query may find multiple matching users as there
+        are no unique nicknames in the SILC.  The <count> option may be
+        given to narrow down the number of accepted results.  If this
+        is not defined there are no limit of accepted results.  The query
+        may also be narrowed down by defining the server name of the 
+        nickname.  The <count> is in string format.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  The WHOWAS requests MUST 
+        be based on specific nickname request.
+
+        The WHOWAS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still must
+        search its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>        (2) <Client ID>
+                        (3) <nickname>[@<server>]   (4) <username@host>
+                        (5) [<real name>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with nickname and user name and host name.
+        Every server MUST keep history for some period of time of its
+        locally connected clients.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   3    SILC_COMMAND_IDENTIFY
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<server name>]
+                        (3) [<channel name>]         (4) [<count>]
+                        (5) [<ID Payload>]           (n) [...]
+
+        Identify command is used to query information about an entity by
+        the entity's name or ID.  This command can be used to query
+        information about clients, server and channels.
+
+        The query may find multiple matching entities.  The <count> option
+        may be given to narrow down the number of accepted results.  If
+        this is not defined there are no limit of accepted results.  The
+        <count> is in string format.
+
+        It is also possible to search the entity by its ID.  If the
+        <ID Payload> is provided server must use it as the search value
+        instead of the entity's name.  One of the arguments must be given.
+        It is also possible to define multiple ID Payloads to search
+        multiple entities sending only one IDENTIFY command.  In this case
+        the ID Payloads are appended as normal arguments.  The type of the
+        entity is defined by the type of the ID Payload.
+
+        To prevent miss-use of this command wildcards in the names are
+        not permitted.  It is not allowed to request for example all users
+        on server.
+
+        Implementations may not want to give interface access to this
+        command as it is hardly a command that would be used by an end
+        user.  However, it must be implemented as it is used with private
+        message sending.
+
+        The IDENTIFY command MUST be always sent to the router by server
+        so that all users are searched.  However, server MUST still search
+        its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>   (2) <Client ID>
+                        (3) [<entity's name>]  (4) [<info>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        When querying clients the <entity's name> must include the client's
+        nickname in the following format: nickname>[@server].  The
+        <info> must include the client's username and host in the following
+        format: username@host.
+
+        When querying servers the <entity's name> must include the server's
+        full name.  The <info> may be omitted.
+
+        When querying channels the <entity's name> must include the
+        channel's name.  The <info> may be omitted.
+
+        If the <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   4    SILC_COMMAND_NICK
+
+        Max Arguments:  1
+            Arguments:  (1) <nickname>
+
+        Set/change nickname.  This command is used to set nickname for
+        user.  Nickname MUST NOT include any spaces (` '), non-printable
+        characters, commas (`,') and any wildcard characters.  Note that
+        nicknames in SILC are case-sensitive which must be taken into
+        account when searching clients by nickname.
+
+        When nickname is changed new Client ID is generated.  Server MUST
+        distribute SILC_NOTIFY_TYPE_NICK_CHANGE to local clients on the
+        channels (if any) the client is joined on.  Then it MUST send
+        SILC_PACKET_REPLACE_ID to its primary route to replace the old
+        Client ID with the new one.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <New ID Payload>
+
+        This command is replied always with New ID Payload that is
+        generated by the server every time user changes their nickname.
+        Client receiving this payload MUST start using the received
+        Client ID as its current valid Client ID.  The New ID Payload
+        is described in [SILC2].
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NICKNAME_IN_USE
+            SILC_STATUS_ERR_BAD_NICKNAME
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   5    SILC_COMMAND_LIST
+
+        Max Arguments:  1
+            Arguments:  (1) [<Channel ID>]
+
+        The list command is used to list channels and their topics on the
+        current server.  If the <Channel ID> parameter is used, only the
+        status of that channel is displayed.  Secret channels are not
+        listed at all.  Private channels are listed with status indicating
+        that the channel is private.  Router MAY reply with all channels
+        it knows about.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel>         (4) [<topic>]
+                        (5) [<user count>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        This command replies with Channel ID, name and the topic of the
+        channel.  If the channel is private channel the <topic> SHOULD
+        include the "*private*" string.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   6    SILC_COMMAND_TOPIC
+
+        Max Arguments:  2
+            Arguments:  (1) <Channel ID>  (2) [<topic>]
+
+        This command is used to change or view the topic of a channel.
+        The topic for channel <Channel ID> is returned if there is no
+        <topic> given.  If the <topic> parameter is present, the topic
+        for that channel will be changed, if the channel modes permit
+        this action.
+
+        After setting the topic the server MUST send the notify type
+        SILC_NOTIFY_TYPE_TOPIC_SET to its primary router and then to
+        the channel which topic was changed.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <Channel ID> 
+                        (3) [<topic>]
+
+        The command may reply with the topic of the channel if it is
+        set.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   7    SILC_COMMAND_INVITE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>       (2) [<Client ID>]
+                        (3) [<adding client>]  (4) [<removing client>]
+
+        This command is used to invite other clients to join to the
+        channel.  The <Client ID> argument is the target client's ID that
+        is being invited.  The <Channel ID> is the Channel ID of the
+        requested channel.  The sender of this command MUST be on the
+        channel.  The server MUST also send the notify type
+        SILC_NOTIFY_TYPE_INVITE to its primary router and then to the
+        client indicated by the <Client ID>.
+
+        The <adding client> and <removing client> can be used to add to
+        and remove from the invite list.  The format of the <adding client>
+        and <removing client> is as follows:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        When adding to or removing from the invite list the server MUST
+        send the notify type SILC_NOTIFY_TYPE_INVITE to its primary router
+        and MUST NOT send it to the client which was added to the list.
+        The client which executes this command MUST have at least channel
+        operator privileges to be able to add to or remove from the invite
+        list.  The wildcards MAY be used with this command.  If adding or
+        removing more than one client then the lists are an comma (`,')
+        separated.
+
+        Note that the <Client ID> provided MUST be resolved into correct
+        nickname and host name and add to the invite list before sending
+        the notify packet.
+        
+        When this command is given with only <Channel ID> argument then
+        the command merely returns the invite list of the channel.   This
+        command MUST fail if the requested channel does not exist, the
+        requested <Client ID> is already on the channel or if the channel
+        is invite only channel and the caller of this command does not
+        have at least channel operator privileges.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<invite list>]
+
+       This command replies with the invite list of the channel if it
+       exists.  The <invite list> may be omitted if the list was not
+        altered.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   8    SILC_COMMAND_QUIT
+
+        Max Arguments:  1
+            Arguments:  (1) [<quit message>]
+
+        This command is used by client to end SILC session.  The server
+        must close the connection to a client which sends this command.
+        if <quit message> is given it will be sent to other clients on
+        channel if the client is on channel when quitting.
+
+        Reply messages to the command:
+
+        This command does not reply anything.
+
+
+    9   SILC_COMMAND_KILL
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) [<comment>]
+
+        This command is used by SILC operators to remove a client from
+        SILC network.  The removing has temporary effects and client may
+        reconnect to SILC network.  The <Client ID> is the client to be
+        removed from SILC.  The <comment> argument may be provided to 
+        give to the removed client some information why it was removed
+        from the network.
+
+        When killing a client the router MUST first send notify type
+        SILC_NOTIFY_TYPE_KILLED to all channels the client has joined.
+        The packet MUST NOT be sent to the killed client on the channels.
+        Then, the router MUST send the same notify type to its primary
+        router.  Finally, the router MUST send the same notify type 
+        directly to the client which was killed.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   10   SILC_COMMAND_INFO
+
+        Max Arguments:  2
+            Arguments:  (1) [<server>]  (2) [<Server ID>]
+
+        This command is used to fetch various information about a server.
+        If <server> argument is specified the command MUST be sent to
+        the requested server.
+
+        If the <Server ID> is specified the server information if fetched
+        by the provided Server ID.  One of the arguments must always be
+        present.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) <server name>     (4) <string>
+
+        This command replies with the Server ID of the server and a
+        string which tells the information about the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SERVER_ID
+
+
+   11   SILC_COMMAND_CONNECT
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used by operators to force a server to try to
+        establish a new connection to remote server or router.  The
+        Operator MUST specify the server/router to be connected by
+        setting <remote server> argument.  The port is 32 bit MSB value.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   12   SILC_COMMAND_PING
+
+        Max Arguments:  1
+            Arguments:  (1) <Server ID>
+
+        This command is used by client and server to test the communication
+        channel to its server if one suspects that the communication is not
+        working correctly.  The <Server ID> is the ID of the server the
+        sender is connected to.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.  Server returns
+        SILC_STATUS_OK in Status Payload if pinging was successful.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NOT_REGISTERED
+
+
+   13   SILC_COMMAND_OPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain server operator
+        privileges on some server or router.  Note that router operator
+        has router privileges that supersedes the server operator
+        privileges and this does not obtain those privileges.  Client
+        MUST use SILCOPER command to obtain router level privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).  The public
+        key that server will use to verify the signature found in the
+        payload should be verified.  It is recommended that the public
+        key is saved locally in the server and server would not use
+        any public keys received during the SKE.
+
+        After changing the mode the server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   14   SILC_COMMAND_JOIN
+
+        Max Arguments:  5
+            Arguments:  (1) <channel>       (2) <Client ID>
+                        (3) [<passphrase>]  (4) [<cipher>]
+                        (5) [<hmac>]
+
+        Join to channel/create new channel.  This command is used to
+        join to a channel.  If the channel does not exist the channel is
+        created.  If server is normal server this command MUST be sent
+        to router which will create the channel.  The channel MAY be
+        protected with passphrase.  If this is the case the passphrase
+        MUST be sent along the join command.
+
+        The name of the <channel> MUST NOT include any spaces (` '),
+        non-printable characters, commas (`,') or any wildcard characters.
+
+        The second argument <Client ID> is the Client ID of the client
+        which is joining to the client.  When client sends this command
+        to the server the <Client ID> MUST be the client's own ID.
+
+        Cipher to be used to secure the traffic on the channel MAY be
+        requested by sending the name of the requested <cipher>.  This
+        is used only if the channel does not exist and is created.  If
+        the channel already exists the cipher set previously for the
+        channel will be used to secure the traffic.  The computed MACs
+        of the channel message are produced by the default HMAC or by
+        the <hmac> provided for the command.
+
+        The server MUST check whether the user is allowed to join to
+        the requested channel.  Various modes set to the channel affect
+        the ability of the user to join the channel.  These conditions
+        are:
+
+            o  The user MUST be invited to the channel if the channel
+               is invite-only channel.
+
+            o  The Client ID/nickname/username/host name MUST NOT match
+               any active bans.
+
+            o  The correct passphrase MUST be provided if passphrase 
+               is set to the channel.
+
+            o  The user count limit, if set, MUST NOT be reached.
+
+        Reply messages to the command:
+
+        Max Arguments:  14
+            Arguments:  (1) <Status Payload>        (2) <channel> 
+                        (3) <Channel ID>            (4) <Client ID>
+                        (5) <channel mode mask>     (6) <created>
+                        (7) [<Channel Key Payload>] (8) [<ban list>]
+                        (9) [<invite list>]         (10) [<topic>]
+                        (11) [<hmac>]               (12) <list count>
+                        (13) <Client ID list>       (14) <client mode list>
+
+        This command replies with the channel name requested by the
+        client, channel ID of the channel and topic of the channel
+        if it exists.  The <Client ID> is the Client ID which was joined
+        to the channel.  It also replies with the channel mode mask
+        which tells all the modes set on the channel.  If the
+        channel is created the mode mask is zero (0).  If ban mask
+        and/or invite list is set they are sent as well.
+
+        The <list count>, <Client ID list> and <client mode list> are
+        the clients currently on the channel and their modes on the
+        channel.  The <Client ID list> is formed by adding the ID Payloads
+        one after the other.  The <client mode list> is formed by adding
+        32 bit MSB first order values one after the other.
+
+        Client receives the channel key in the reply message as well
+        inside <Channel Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_BAD_PASSWORD
+            SILC_STATUS_ERR_CHANNEL_IS_FULL
+            SILC_STATUS_ERR_NOT_INVITED
+            SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+            SILC_STATUS_ERR_BAD_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+
+
+   15   SILC_COMMAND_MOTD
+
+        Max Arguments:  1
+            Arguments:  (1) <server>
+
+        This command is used to query the Message of the Day of the server.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) [<motd>]
+
+        This command replies with the motd message if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   16   SILC_COMMAND_UMODE
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) <client mode mask>
+
+        This command is used by client to set/unset modes for itself.
+        However, there are some modes that the client MUST NOT set itself,
+        but they will be set by server.  However, client MAY unset any
+        mode.  Modes may be masked together ORing them thus having
+        several modes set.  Client MUST keep its client mode mask
+        locally so that the mode setting/unsetting would work without
+        problems.  Client may change only its own modes.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        The following client modes are defined:
+
+           0x0000    SILC_UMODE_NONE
+
+              No specific mode for client.  This is the initial
+              setting when new client is created.  The client is
+              normal client now.
+
+
+           0x0001    SILC_UMODE_SERVER_OPERATOR
+
+              Marks the user as server operator.  Client MUST NOT
+              set this mode itself.  Server sets this mode to the
+              client when client attains the server operator
+              privileges by SILC_COMMAND_OPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0002    SILC_UMODE_ROUTER_OPERATOR
+
+              Marks the user as router (SILC) operator.  Client
+              MUST NOT this mode itself.  Router sets this mode to
+              the client when client attains the router operator
+              privileges by SILC_COMMAND_SILCOPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0004    SILC_UMODE_GONE
+
+              Marks that the user is not currently present in the
+              SILC Network.  Client MAY set and unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <client mode mask>
+
+        This command replies with the changed client mode mask that
+        the client MUST to keep locally.
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_BAD_CLIENT_ID
+            SILC_STATUS_ERR_NOT_YOU
+            SILC_STATUS_ERR_PERM_DENIED
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   17   SILC_COMMAND_CMODE
+
+        Max Arguments:  7
+            Arguments:  (1) <Channel ID>      (2) <channel mode mask>
+                        (3) [<user limit>]    (4) [<passphrase>]
+                        (5) [<cipher>]        (6) [<hmac>]
+                        (7) [<auth payload>]
+
+        This command is used by client to set or change channel flags on
+        a channel.  Channel has several modes that set various properties
+        of a channel.  Modes may be masked together by ORing them thus
+        having several modes set.  The <Channel ID> is the ID of the
+        target channel.  The client changing channel mode MUST be on
+        the same channel and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CMODE_CHANGE notify
+        type MUST be distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CMODE_NONE
+
+              No specific mode on channel.  This is the default when
+              channel is created.  This means that channel is just plain
+              normal channel.
+
+
+           0x0001    SILC_CMODE_PRIVATE
+
+              Channel is private channel.  Private channels are shown
+              in the channel list listed with SILC_COMMAND_LIST command
+              with indication that the channel is private.  Also,
+              client on private channel will no be detected to be on
+              the channel as the channel is not shown in the client's
+              currently joined channel list.  Channel founder and 
+              channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]p on user interface
+              to set/unset this mode.
+
+
+           0x0002    SILC_CMODE_SECRET
+
+              Channel is secret channel.  Secret channels are not shown
+              in the list listed with SILC_COMMAND_LIST command.  Secret
+              channels can be considered to be invisible channels.
+              Channel founder and channel operator MAY set/unset this
+              mode.
+
+              Typical implementation would use [+|-]s on user interface
+              to set/unset this mode.
+
+
+           0x0004    SILC_CMODE_PRIVKEY
+
+              Channel uses private channel key to protect the traffic
+              on the channel.  When this mode is set the client will be
+              responsible to set the key it wants to use to encrypt and
+              decrypt the traffic on channel.  Server generated channel
+              keys are not used at all.  This mode provides additional
+              security as clients on channel may agree to use private
+              channel key that even servers do not know.  Naturally,
+              this requires that every client on the channel knows
+              the key before hand (it is considered to be pre-shared-
+              key).  The key material is RECOMMENDED to be processed
+              as stated in the [SILC3] in the section Processing the
+              Key Material.
+
+              As it is local setting it is possible to have several
+              private channel keys on one channel.  In this case several
+              clients can talk on same channel but only those clients
+              that share the key with the message sender will be able
+              to hear the talking.  Client SHOULD NOT display those
+              message for the end user that it is not able to decrypt
+              when this mode is set.
+
+              Only channel founder MAY set/unset this mode.  If this
+              mode is unset the server will distribute new channel
+              key to all clients on the channel which will be used
+              thereafter.
+
+              Typical implementation would use [+|-]k on user interface
+              to set/unset this mode.
+
+
+           0x0008    SILC_CMODE_INVITE
+
+              Channel is invite only channel.  Client may join to this
+              channel only if it is invited to the channel.  Channel
+              founder and channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]i on user interface
+              to set/unset this mode.
+
+
+           0x0010    SILC_CMODE_TOPIC
+
+              The topic of the channel may only be set by client that
+              is channel founder or channel operator.  Normal clients
+              on channel will not be able to set topic when this mode
+              is set.  Channel founder and channel operator MAY set/
+              unset this mode.
+
+              Typical implementation would use [+|-]t on user interface
+              to set/unset this mode.
+
+
+           0x0020    SILC_CMODE_ULIMIT
+
+              User limit has been set to the channel.  New clients
+              may not join to the channel when the limit set is
+              reached.  Channel founder and channel operator MAY set/
+              unset the limit.  The <user limit> argument is the
+              number of limited users.
+
+              Typical implementation would use [+|-]l on user interface
+              to set/unset this mode.
+
+
+           0x0040    SILC_CMODE_PASSPHRASE
+
+              Passphrase has been set to the channel.  Client may
+              join to the channel only if it is able to provide the
+              correct passphrase.  Setting passphrases to channel
+              is entirely safe as all commands are protected in the
+              SILC network.  Only channel founder MAY set/unset
+              the passphrase.  The <passphrase> argument is the
+              set passphrase.
+
+              Typical implementation would use [+|-]a on user interface
+              to set/unset this mode.
+
+
+           0x0080    SILC_CMODE_CIPHER
+
+              Sets specific cipher to be used to protect channel
+              traffic.  The <cipher> argument is the requested cipher.
+              When set or unset the server must re-generate new
+              channel key.  Only channel founder MAY set the cipher of 
+              the channel.  When unset the new key is generated using
+              default cipher for the channel.
+
+              Typical implementation would use [+|-]c on user interface
+              to set/unset this mode.
+
+
+           0x0100    SILC_CMODE_HMAC
+
+              Sets specific hmac to be used to compute the MACs of the
+              channel message.  The <hmac> argument is the requested hmac.
+              Only channel founder may set the hmac of the channel.
+
+              Typical implementation would use [+|-]h on user interface
+              to set/unset this mode.
+
+
+           0x0200    SILC_CMODE_FOUNDER_AUTH
+
+              Channel founder may set this mode to be able to regain
+              channel founder rights even if the client leaves the 
+              channel.  The <auth payload> is the Authentication Payload
+              consisting of the authentication method and authentication
+              data to be used in the authentication.  The server MUST
+              NOT accept NONE authentication method.  Also, if the 
+              method is public key authentication the server MUST NOT
+              save the authentication data from the payload as the
+              data is different on all authentications.  In this case the
+              server only saves the authentication method.  However,
+              server MUST verify the sent authentication payload and
+              set the mode only if the verification was successful.
+
+              Note that this mode is effective only in the current server.
+              The client MUST connect to the same server later to be able
+              to regain the channel founder rights.  The server MUST save
+              the public key of the channel founder and use that to identify
+              the client which is claiming the channel founder rights.
+              The rights may be claimed by the SILC_CUMODE_FOUNDER 
+              channel user mode using SILC_COMMAND_CUMODE command.  The
+              set authentication data remains valid as long as the channel
+              exists or until the founder unsets this mode.
+
+              Typical implementation would use [+|-]f on user interface
+              to set/unset this mode.
+
+        To make the mode system work, client MUST keep the channel mode
+        mask locally so that the mode setting and unsetting would work
+        without problems.  The client receives the initial channel mode
+        mask when it joins to the channel.  When the mode changes on
+        channel the server MUST distribute the changed channel mode mask
+        to all clients on the channel by sending the notify type
+        SILC_NOTIFY_TYPE_CMODE_CHANGE.  The notify type MUST also be sent
+        to the server's primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel mode mask>
+
+        This command replies with the changed channel mode mask that
+        client MUST keep locally.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   18   SILC_COMMAND_CUMODE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>    (2) <mode mask>
+                        (3) <Client ID>     (4) [<auth payload>]
+
+        This command is used by client to change channel user modes on
+        channel.  Users on channel may have some special modes and this
+        command is used by channel operators to set or change these modes.
+        The <Channel ID> is the ID of the target channel.  The <mode mask>
+        is OR'ed mask of modes.  The <Client ID> is the target client.
+        The client changing channel user modes MUST be on the same channel
+        as the target client and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CUMODE_CHANGE notify
+        type is distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CUMODE_NONE
+
+              No specific mode.  This is the normal situation for client.
+              Also, this is the mode set when removing all modes from
+              the target client.
+
+
+           0x0001    SILC_CUMODE_FOUNDER
+
+              The client is channel founder of the channel.  Usually this
+              mode is set only by the server when the channel was created.
+              However, if the SILC_CMODE_FOUNDER_AUTH channel mode has
+              been set, the client can claim channel founder privileges
+              by providing the <auth payload> that the server will use
+              to authenticate the client.  The public key that server will
+              use to verify the <auth payload> must the same public key
+              that was saved when the SILC_CMODE_FOUNDER_AUTH channel
+              mode was set.  The client MAY remove this mode at any time.
+
+
+           0x0002    SILC_CUMODE_OPERATOR
+
+              Sets channel operator privileges on the channel for a
+              client on the channel.  Channel founder and channel operator
+              MAY set/unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <channel user mode mask>
+                        (3) <Channel ID>      (4) <Client ID>
+
+        This command replies with the changed channel user mode mask that
+        client MUST keep locally. The <Channel ID> is the specified
+        channel.  The <Client ID> is the target client.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   19   SILC_COMMAND_KICK
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>  (2) <Client ID>  
+                        (3) [<comment>]
+
+        This command is used by channel operators to remove a client from
+        channel.  The <channel> argument is the channel the client to be
+        removed is on currently.  Note that the "kicker" must be on the same
+        channel.  If <comment> is provided it will be sent to the removed
+        client.
+
+        After kicking the client the server MUST send the notify type
+        SILC_NOTIFY_TYPE_KICKED to the channel and to its primary router.
+        The channel key MUST also be re-generated after kicking, unless
+        the SILC_CMODE_PRIVKEY mode is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   20   SILC_COMMAND_BAN
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                        (3) [<removing client>]
+
+        This command is used to manage the ban list of the channel
+        indicated by the <Channel ID>.  A client that is banned from
+        channel is no longer able to join the channel.  The client which
+        is executing this command MUST have at least channel operator
+        privileges on the channel.
+
+        The <adding client> and <removing client> are used to add to and
+        remove from the ban list.  The format of the <adding client> and
+        the <removing client> is of following format:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        The server MUST send the notify type SILC_NOTIFY_TYPE_BAN to its
+        primary router after adding to or removing from the ban list.
+        The wildcards MAY be used with this command.  If adding or removing
+        from than one clients then the lists are an comma (`,') separated.
+
+        If this command is executed without the ban arguments the command
+        merely replies with the current ban list.
+
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<ban list>]
+
+        This command replies with the <Channel ID> of the channel and
+        the current <ban list> of the channel if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   21   SILC_COMMAND_CLOSE
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used only by operator to close connection to a
+        remote site.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   22   SILC_COMMAND_SHUTDOWN
+
+        Max Arguments:  0
+            Arguments:  None
+
+        This command is used only by operator to shutdown the server.
+        All connections to the server will be closed and the server is
+        shutdown.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+
+
+   23   SILC_COMMAND_SILCOPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain router operator
+        privileges (also known as SILC operator) on the router.  Note
+        that router operator has privileges that supersedes the server
+        operator privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).  The public
+        key that router will use to verify the signature found in the
+        payload should be verified.  It is recommended that the public
+        key is saved locally in the router and router would not use
+        any public keys received during the SKE.
+
+        Difference between router operator and server operator is that
+        router operator is able to handle cell level properties while
+        server operator (even on router server) is able to handle only
+        local properties, such as, local connections and normal server
+        administration.  The router operator is also able to use the
+        SILC_COMMAND_KILL command.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   24   SILC_COMMAND_LEAVE
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used by client to leave a channel the client is
+        joined to. 
+
+        When leaving channel the server MUST send the notify type
+        SILC_NOTIFY_TYPE_LEAVE to its primary router and to the channel.
+        The channel key MUST also be re-generated when leaving the channel
+        and distribute it to all clients still currently on the channel.
+        The key MUST NOT be re-generated if the SILC_CMODE_PRIVKEY mode
+        is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+
+
+   25   SILC_COMMAND_USERS
+
+        Max Arguments:  2
+            Arguments:  (1) [<Channel ID>]  (2) [<channel name>]
+
+        This command is used to list user names currently on the requested
+        channel; either the argument <Channel ID> or the <channel name>. 
+        One of these arguments must be present.  The server MUST resolve
+        the user names and send a comma (`,') separated list of user names
+        on the channel.  Server or router MAY resolve the names by sending
+        SILC_COMMAND_WHOIS or SILC_COMMAND_IDENTIFY commands.
+
+        If the requested channel is a private or secret channel, this
+        command MUST NOT send the list of users, as private and secret
+        channels cannot be seen by outside.  In this case the returned
+        name list MAY include a indication that the server could not 
+        resolve the names of the users on the channel.  Also, in this case
+        Client ID's or client modes are not sent either.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <list count>      (4) <Client ID list>
+                        (5) <client mode list>
+
+        This command replies with the Channel ID of the requested channel
+        Client ID list of the users on the channel and list of their modes.
+        The Client ID list has Client ID's of all users in the list.  The 
+        <Client ID list> is formed by adding Client ID's one after another.
+        The <client mode list> is formed by adding client's user modes on
+        the channel one after another (4 bytes (32 bits) each).  The <list 
+        count> of length of 4 bytes (32 bits), tells the number of entries
+        in the lists.  Both lists MUST have equal number of entries.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+
+   26   SILC_COMMAND_GETKEY
+
+        Max Arguments:  1
+            Arguments:  (1) <ID Payload>
+
+        This command is used to fetch the public key of the client or
+        server indicated by the <ID Payload>.  The public key is fetched
+        from the server where to the client is connected.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>      (2) <ID Payload>
+                        (3) [<Public Key Payload>]
+
+        This command replies with the client's or server's ID and with
+        the <Public Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   27 - 199
+
+        Currently undefined commands.
+
+
+   200 - 254
+
+        These commands are reserved for private use and will not be defined
+        in this document.
+
+
+   255  SILC_COMMAND_MAX   
+
+        Reserved command.  This must not be sent.
+.in 3
+
+
+.ti 0
+2.3 SILC Command Status Types
+
+.ti 0
+2.3.1 SILC Command Status Payload
+
+Command Status Payload is sent in command reply messages to indicate
+the status of the command.  The payload is one of argument in the
+command thus this is the data area in Command Argument Payload described
+in [SILC2].  The payload is only 2 bytes of length.  The following diagram
+represents the Command Status Payload (field is always in MSB order).
+
+
+.in 21
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Status Message         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  SILC Command Status Payload
+
+
+.in 6
+o Status Message (2 bytes) - Indicates the status message.
+  All Status messages are described in the next section.
+.in 3
+
+
+.ti 0
+2.3.2 SILC Command Status List
+
+Command Status messages are returned in the command reply messages
+to indicate whether the command were executed without errors.  If error
+has occurred the status indicates which error occurred.  Status payload
+only sends numeric reply about the status.  Receiver of the payload must
+convert the numeric values into human readable error messages.  The
+list of status messages below has an example human readable error
+messages that client may display for the user.
+
+List of all defined command status messages following.
+
+.in 0
+   Generic status messages:
+
+   0    SILC_STATUS_OK
+
+        Ok status.  Everything went Ok.  The status payload maybe
+        safely ignored in this case.
+
+   1    SILC_STATUS_LIST_START
+
+        Start of the list.  There will be several command replies and
+        this reply is the start of the list.
+
+   2    SILC_STATUS_LIST_ITEM
+
+        Item in the list.  This is one of the item in the list but not the
+        first or last one.
+
+   3    SILC_STATUS_LIST_END
+
+        End of the list.  There were several command replies and this
+        reply is the last of the list.  There won't be other replies
+        belonging to this list after this one.
+
+   4 - 9
+
+        Currently undefined and has been reserved for the future.
+
+
+   Error status message:
+
+
+
+   10   SILC_STATUS_ERR_NO_SUCH_NICK
+
+        "No such nickname".  Requested nickname does not exist.
+
+   11   SILC_STATUS_ERR_NO_SUCH_CHANNEL
+
+        "No such channel".  Requested channel name does not exist.
+
+   12   SILC_STATUS_ERR_NO_SUCH_SERVER
+
+        "No such server".  Requested server name does not exist.
+
+   13   SILC_STATUS_ERR_TOO_MANY_TARGETS
+
+        "Duplicate recipients. No message delivered".  Message were
+        tried to be sent to recipient which has several occurrences in 
+        the recipient list.
+
+   14   SILC_STATUS_ERR_NO_RECIPIENT
+
+        "No recipient given".  Command required recipient which was
+        not provided.
+
+   15   SILC_STATUS_ERR_UNKNOWN_COMMAND
+
+        "Unknown command".  Command sent to server is unknown by the
+        server.
+
+   16   SILC_STATUS_ERR_WILDCARDS
+
+        "Wildcards cannot be used".  Wildcards were provided but they
+        weren't permitted.
+
+   17   SILC_STATUS_ERR_NO_CLIENT_ID
+
+        "No Client ID given".  Client ID were expected as command
+        parameter but were not found.
+
+   18   SILC_STATUS_ERR_NO_CHANNEL_ID
+
+        "No Channel ID given".  Channel ID were expected as command
+        parameter but were not found.
+
+   19   SILC_STATUS_ERR_NO_SERVER_ID
+
+        "No Serve ID given".  Server ID were expected as command
+        parameter but were not found.
+
+   20   SILC_STATUS_ERR_BAD_CLIENT_ID
+
+        "Bad Client ID".  Client ID provided were erroneous.
+
+   21   SILC_STATUS_ERR_BAD_CHANNEL_ID
+
+        "Bad Channel ID".  Channel ID provided were erroneous.
+
+   22   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+        "No such Client ID".  Client ID provided does not exist.
+
+   23   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+
+        "No such Channel ID".  Channel ID provided does not exist.
+
+   24   SILC_STATUS_ERR_NICKNAME_IN_USE
+
+        "Nickname already exists".  Nickname created could not be 
+        registered because number of same nicknames were already set to
+        maximum.  This is not expected to happen in real life but is
+        possible to occur.
+
+   25   SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+        "You are not on that channel".  The command were specified for
+        channel user is not currently on.
+
+   26   SILC_STATUS_ERR_USER_NOT_ON_CHANNEL
+
+        "They are not on channel".  The requested target client is not
+        on requested channel.
+
+   27   SILC_STATUS_ERR_USER_ON_CHANNEL
+
+        "User already on channel".  User were invited on channel they
+        already are on.
+
+   28   SILC_STATUS_ERR_NOT_REGISTERED
+
+        "You have not registered".  User executed command that requires
+        the client to be registered on the server before it may be
+        executed.
+
+   29   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+
+        "Not enough parameters".  Command requires more parameters
+        than provided.
+
+   30   SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+        "Too many parameters".  Too many parameters were provided
+        for the command.
+
+   31   SILC_STATUS_ERR_PERM_DENIED
+
+        "Permission denied".  Generic permission denied error status
+        to indicate disallowed access.
+
+   32   SILC_STATUS_ERR_BANNED_FROM_SERVER
+
+        "You are banned from this server".  The client tried to register
+        on server that has explicitly denied this host to connect.
+
+   33   SILC_STATUS_ERR_BAD_PASSWORD
+
+        "Cannot join channel. Incorrect password".  Password provided for 
+        channel were not accepted.
+
+   34   SILC_STATUS_ERR_CHANNEL_IS_FULL
+
+        "Cannot join channel. Channel is full".  The channel is full
+        and client cannot be joined to it.
+
+   35   SILC_STATUS_ERR_NOT_INVITED
+
+        "Cannot join channel. You have not been invited".  The channel
+        is invite only channel and client has not been invited.
+
+   36   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+
+        "Cannot join channel. You have been banned".  The client has
+        been banned from the channel.
+
+   37   SILC_STATUS_ERR_UNKNOWN_MODE
+
+        "Unknown mode".  Mode provided by the client were unknown to
+        the server.
+
+   38   SILC_STATUS_ERR_NOT_YOU
+
+        "Cannot change mode for other users".  User tried to change
+        someone else's mode.
+
+   39   SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+        "Permission denied. You are not channel operator".  Command may 
+        be executed only by channel operator.
+
+   40   SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
+
+        "Permission denied. You are not channel founder".  Command may 
+        be executed only by channel operator.
+
+   41   SILC_STATUS_ERR_NO_SERVER_PRIV
+
+        "Permission denied. You are not server operator".  Command may
+        be executed only by server operator.
+
+   42   SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+        "Permission denied. You are not SILC operator".  Command may be
+        executed only by router (SILC) operator.
+
+   43   SILC_STATUS_ERR_BAD_NICKNAME
+
+        "Bad nickname".  Nickname requested contained illegal characters
+        or were malformed.
+
+   44   SILC_STATUS_ERR_BAD_CHANNEL
+
+        "Bad channel name".  Channel requested contained illegal characters
+        or were malformed.
+
+   45   SILC_STATUS_ERR_AUTH_FAILED
+
+        "Authentication failed".  The authentication data sent as 
+        argument were wrong and thus authentication failed.
+
+   46   SILC_STATUS_ERR_UNKOWN_ALGORITHM
+
+        "The algorithm was not supported."  The server does not support the
+        requested algorithm.
+
+   47   SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+        "No such Server ID".  Server ID provided does not exist.
+
+.in 3
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 21 February 2002
diff --git a/doc/draft-riikonen-silc-commands-02.nroff b/doc/draft-riikonen-silc-commands-02.nroff
new file mode 100644 (file)
index 0000000..df91216
--- /dev/null
@@ -0,0 +1,1949 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 13 November 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-commands-02.txt                     13 November 2001
+Expires: 13 May 2002
+
+.in 3
+
+.ce 2
+SILC Commands
+<draft-riikonen-silc-commands-02.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  The
+SILC Commands are very important part of the SILC protocol.  Usually
+the commands are used by SILC clients to manage the SILC session, but
+also SILC servers may use the commands.  This memo specifies detailed
+command messages and command reply messages.
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  2
+2 SILC Commands .................................................  2
+  2.1 SILC Commands Syntax ......................................  2
+  2.2 SILC Commands List ........................................  4
+  2.3 SILC Command Status Types ................................. 33
+      2.3.1 SILC Command Status Payload ......................... 33
+      2.3.2 SILC Command Status List ............................ 33
+3 Security Considerations ....................................... 38
+4 References .................................................... 38
+5 Author's Address .............................................. 40
+
+
+.ti 0
+1. Introduction
+
+This document describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+document specifies detailed command messages and command reply messages.
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+See the [SILC1] for the requirements and the restrictions for the usage
+of the SILC commands.  The [SILC2] defines the command packet type and
+the Command Payload which is actually used to deliver the commands and
+command reply messages.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Commands
+
+.ti 0
+2.1 SILC Commands Syntax
+
+This section briefly describes the syntax of the command notions
+in this document.  Every field in command is separated from each
+other by whitespaces (` ') indicating that each field is independent
+argument and each argument MUST have own Command Argument Payload.
+The number of maximum arguments are defined with each command
+separately.  The Command Argument Payload is described in [SILC2].
+
+Every command defines specific number for each argument.  Currently,
+they are defined in ascending order; first argument has number one 
+(1), second has number two (2) and so on.  This number is set into the
+Argument Type field in the Command Argument Payload.  This makes it
+possible to send the arguments in free order as the number MUST be
+used to identify the type of the argument.  This makes is it also
+possible to have multiple optional arguments in commands and in
+command replies.  The number of argument is marked in parentheses
+before the actual argument.
+
+
+
+.in 6
+Example:  Arguments:  (1) <nickname> (2) <username@host>
+.in 3
+   
+
+Every command replies with Status Payload.  This payload tells the
+sender of the command whether the command was completed successfully or
+whether there was an error.  If error occurred the payload includes the
+error type.  In the next section the Status Payload is not described 
+as it is common to all commands and has been described here.  Commands 
+MAY reply with other arguments as well.  These arguments are command 
+specific and are described in the next section.
+
+Example command:
+.in 6
+
+EXAMPLE_COMMAND
+
+.in 8
+Max Arguments:  3
+    Arguments:  (1) <nickname>[@<server>]  (2) <message>
+                (3) [<count>]
+
+The command has maximum of 3 arguments.  However, only first
+and second arguments are mandatory.
+
+First argument <nickname> is mandatory but may have optional
+<nickname@server> format as well.  Second argument is mandatory
+<message> argument.  Third argument is optional <count> argument.
+
+The numbers in parentheses are the argument specific numbers
+that specify the type of the argument in Command Argument Payload.
+The receiver always knows that, say, argument number two (2) is
+<message> argument, regardless of the ordering of the arguments in
+the Command Payload.
+
+Reply messages to the command:
+
+Max Arguments:  4
+    Arguments:  (1) <Status Payload>  (2) [<channel list>]
+                (3) <idle time>       (4) [<away message>]
+
+This command may reply with maximum of 4 arguments.  However,
+only the first and third arguments are mandatory.  The numbers
+in the parentheses have the same meaning as in the upper
+command sending specification.
+
+Every command reply with <Status Payload>, it is mandatory 
+argument for all command replies and for this reason it is not
+described in the command reply descriptions.
+
+
+
+Status messages:
+
+    SILC_STATUS_OK
+    SILC_STATUS_ERR_TOO_MANY_TARGETS
+    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+    SILC_STATUS_ERR_NO_SUCH_NICK
+
+Every command reply also defines set of status message that it
+may return inside the <Status Payload>.  All status messages
+are defined in the section 2.3 SILC Command Status Types.
+
+.in 3
+Every command that has some kind of ID as argument (for example
+<Client ID>) are actually ID Payloads, defined in [SILC2] that includes
+the type of the ID, length of the ID and the actual ID data.  This
+way variable length ID's can be sent as arguments.
+
+
+.ti 0
+2.2 SILC Commands List
+
+This section lists all SILC commands, however, it is expected that a
+implementation and especially client implementation has many more
+commands that has only local affect.  These commands are official
+SILC commands that has both client and server sides and cannot be
+characterized as local commands.
+
+List of all defined commands in SILC follows.
+
+.in 0
+   0    SILC_COMMAND_NONE
+
+        None.  This is reserved command and MUST NOT be sent.
+
+
+   1    SILC_COMMAND_WHOIS
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Whois command is used to query various information about specific
+        user.  The user may be requested by their nickname and server name.
+        The query may find multiple matching users as there are no unique
+        nicknames in the SILC.  The <count> option may be given to narrow
+        down the number of accepted results.  If this is not defined there
+        are no limit of accepted results.  The query may also be narrowed
+        down by defining the server name of the nickname.  The <count> is
+        int string format.
+
+        It is also possible to search the user by Client ID.  If the 
+        <Client ID> is provided server MUST use it as the search value
+        instead of the <nickname>.  One of the arguments MUST be given.
+        It is also possible to define multiple Client ID's to search
+        multiple users sending only one WHOIS command.  In this case the
+        Client ID's are appended as normal arguments.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  It is not allowed
+        to request all users on some server.  The WHOIS requests MUST 
+        be based on specific nickname request.
+
+        The WHOIS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still MUST
+        search its locally connected clients.  The router MUST send
+        this command to the server which owns the requested client.  That
+        server MUST reply to the command.  Server MUST NOT send whois
+        replies to the client until it has received the reply from its
+        router.
+
+        Reply messages to the command:
+
+        Max Arguments:  9
+            Arguments:  (1) <Status Payload>       (2) <Client ID> 
+                        (3) <nickname>[@<server>]  (4) <username@host> 
+                        (5) <real name>            (6) [<Channel Payload 
+                                                         list>] 
+                        (7) [<user mode>]          (8) [<idle time>]
+                        (9) [<fingerprint>]
+
+
+        This command may reply with several command reply messages to
+        form a list of results.  In this case the status payload will
+        include STATUS_LIST_START status in the first reply and
+        STATUS_LIST_END in the last reply to indicate the end of the
+        list.  If there are only one reply the status is set to normal
+        STATUS_OK.
+
+        The command replies include the Client ID of the nickname,
+        nickname and server name, user name and host name and user's real
+        name.  Client SHOULD process these replies only after the last
+        reply has been received with the STATUS_LIST_END status.  If the
+        <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        The server may return the list of channel the client has joined.
+        In this case the list is list of Channel Payloads.  The Mode Mask
+        in the Channel Payload (see [SILC2] and section 2.3.2.3 for the
+        Channel Payload) is the client's mode on the channel.  The list
+        is encoded by adding the Channel Payloads one after the other.
+
+        The server may also send client's user mode, idle time, and the
+        fingerprint of the client's public key.  The <fingerprint> is the
+        binary hash digest of the public key.  The fingerprint MUST NOT
+        be sent if the server has not verified the proof of posession of
+        the corresponding private key.  Server can do this during the
+        SILC Key Exchange protocol.  The <fingerprint> is SHA1 digest.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   2    SILC_COMMAND_WHOWAS
+
+        Max Arguments:  2
+            Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
+
+        Whowas.  This command is used to query history information about
+        specific user.  The user may be requested by their nickname and 
+        server name.  The query may find multiple matching users as there
+        are no unique nicknames in the SILC.  The <count> option may be
+        given to narrow down the number of accepted results.  If this
+        is not defined there are no limit of accepted results.  The query
+        may also be narrowed down by defining the server name of the 
+        nickname.  The <count> is in string format.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  The WHOWAS requests MUST 
+        be based on specific nickname request.
+
+        The WHOWAS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still must
+        search its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>        (2) <Client ID>
+                        (3) <nickname>[@<server>]   (4) <username@host>
+                        (5) [<real name>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with nickname and user name and host name.
+        Every server MUST keep history for some period of time of its
+        locally connected clients.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   3    SILC_COMMAND_IDENTIFY
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<server name>]
+                        (3) [<channel name>]         (4) [<count>]
+                        (5) [<ID Payload>]           (n) [...]
+
+        Identify command is used to query information about an entity by
+        the entity's name or ID.  This command can be used to query
+        information about clients, server and channels.
+
+        The query may find multiple matching entities.  The <count> option
+        may be given to narrow down the number of accepted results.  If
+        this is not defined there are no limit of accepted results.  The
+        <count> is in string format.
+
+        It is also possible to search the entity by its ID.  If the
+        <ID Payload> is provided server must use it as the search value
+        instead of the entity's name.  One of the arguments must be given.
+        It is also possible to define multiple ID Payloads to search
+        multiple entities sending only one IDENTIFY command.  In this case
+        the ID Payloads are appended as normal arguments.  The type of the
+        entity is defined by the type of the ID Payload.
+
+        To prevent miss-use of this command wildcards in the names are
+        not permitted.  It is not allowed to request for example all users
+        on server.
+
+        Implementations may not want to give interface access to this
+        command as it is hardly a command that would be used by an end
+        user.  However, it must be implemented as it is used with private
+        message sending.
+
+        The IDENTIFY command MUST be always sent to the router by server
+        so that all users are searched.  However, server MUST still search
+        its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>   (2) <ID Payload>
+                        (3) [<entity's name>]  (4) [<info>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        When querying clients the <entity's name> must include the client's
+        nickname in the following format: nickname[@server].  The
+        <info> must include the client's username and host in the following
+        format: username@host.
+
+        When querying servers the <entity's name> must include the server's
+        full name.  The <info> may be omitted.
+
+        When querying channels the <entity's name> must include the
+        channel's name.  The <info> may be omitted.
+
+        If the <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   4    SILC_COMMAND_NICK
+
+        Max Arguments:  1
+            Arguments:  (1) <nickname>
+
+        Set/change nickname.  This command is used to set nickname for
+        user.  Nickname MUST NOT include any spaces (` '), non-printable
+        characters, commas (`,') and any wildcard characters.  Note that
+        nicknames in SILC are case-sensitive which must be taken into
+        account when searching clients by nickname.
+
+        When nickname is changed new Client ID is generated.  Server MUST
+        distribute SILC_NOTIFY_TYPE_NICK_CHANGE to local clients on the
+        channels (if any) the client is joined on.  Then it MUST send
+        SILC_PACKET_REPLACE_ID to its primary route to replace the old
+        Client ID with the new one.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <New ID Payload>
+
+        This command is replied always with New ID Payload that is
+        generated by the server every time user changes their nickname.
+        Client receiving this payload MUST start using the received
+        Client ID as its current valid Client ID.  The New ID Payload
+        is described in [SILC2].
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NICKNAME_IN_USE
+            SILC_STATUS_ERR_BAD_NICKNAME
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   5    SILC_COMMAND_LIST
+
+        Max Arguments:  1
+            Arguments:  (1) [<Channel ID>]
+
+        The list command is used to list channels and their topics on the
+        current server.  If the <Channel ID> parameter is used, only the
+        status of that channel is displayed.  Secret channels are not
+        listed at all.  Private channels are listed with status indicating
+        that the channel is private.  Router MAY reply with all channels
+        it knows about.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel>         (4) [<topic>]
+                        (5) [<user count>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        This command replies with Channel ID, name and the topic of the
+        channel.  If the channel is private channel the <topic> SHOULD
+        include the "*private*" string.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   6    SILC_COMMAND_TOPIC
+
+        Max Arguments:  2
+            Arguments:  (1) <Channel ID>  (2) [<topic>]
+
+        This command is used to change or view the topic of a channel.
+        The topic for channel <Channel ID> is returned if there is no
+        <topic> given.  If the <topic> parameter is present, the topic
+        for that channel will be changed, if the channel modes permit
+        this action.
+
+        After setting the topic the server MUST send the notify type
+        SILC_NOTIFY_TYPE_TOPIC_SET to its primary router and then to
+        the channel which topic was changed.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <Channel ID> 
+                        (3) [<topic>]
+
+        The command may reply with the topic of the channel if it is
+        set.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   7    SILC_COMMAND_INVITE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>       (2) [<Client ID>]
+                        (3) [<adding client>]  (4) [<removing client>]
+
+        This command is used to invite other clients to join to the
+        channel.  The <Client ID> argument is the target client's ID that
+        is being invited.  The <Channel ID> is the Channel ID of the
+        requested channel.  The sender of this command MUST be on the
+        channel.  The server MUST also send the notify type
+        SILC_NOTIFY_TYPE_INVITE to its primary router and then to the
+        client indicated by the <Client ID>.
+
+        The <adding client> and <removing client> can be used to add to
+        and remove from the invite list.  The format of the <adding client>
+        and <removing client> is as follows:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        When adding to or removing from the invite list the server MUST
+        send the notify type SILC_NOTIFY_TYPE_INVITE to its primary router
+        and MUST NOT send it to the client which was added to the list.
+        The client which executes this command MUST have at least channel
+        operator privileges to be able to add to or remove from the invite
+        list.  The wildcards MAY be used with this command.  If adding or
+        removing more than one client then the lists are an comma (`,')
+        separated.
+
+        Note that the <Client ID> provided MUST be resolved into correct
+        nickname and host name and add to the invite list before sending
+        the notify packet.
+        
+        When this command is given with only <Channel ID> argument then
+        the command merely returns the invite list of the channel.   This
+        command MUST fail if the requested channel does not exist, the
+        requested <Client ID> is already on the channel or if the channel
+        is invite only channel and the caller of this command does not
+        have at least channel operator privileges.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<invite list>]
+
+       This command replies with the invite list of the channel if it
+       exists.  The <invite list> may be omitted if the list was not
+        altered.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   8    SILC_COMMAND_QUIT
+
+        Max Arguments:  1
+            Arguments:  (1) [<quit message>]
+
+        This command is used by client to end SILC session.  The server
+        must close the connection to a client which sends this command.
+        if <quit message> is given it will be sent to other clients on
+        channel if the client is on channel when quitting.
+
+        Reply messages to the command:
+
+        This command does not reply anything.
+
+
+    9   SILC_COMMAND_KILL
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) [<comment>]
+
+        This command is used by SILC operators to remove a client from
+        SILC network.  The removing has temporary effects and client may
+        reconnect to SILC network.  The <Client ID> is the client to be
+        removed from SILC.  The <comment> argument may be provided to 
+        give to the removed client some information why it was removed
+        from the network.
+
+        When killing a client the router MUST first send notify type
+        SILC_NOTIFY_TYPE_KILLED to all channels the client has joined.
+        The packet MUST NOT be sent to the killed client on the channels.
+        Then, the router MUST send the same notify type to its primary
+        router.  Finally, the router MUST send the same notify type 
+        directly to the client which was killed.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   10   SILC_COMMAND_INFO
+
+        Max Arguments:  2
+            Arguments:  (1) [<server>]  (2) [<Server ID>]
+
+        This command is used to fetch various information about a server.
+        If <server> argument is specified the command MUST be sent to
+        the requested server.
+
+        If the <Server ID> is specified the server information if fetched
+        by the provided Server ID.  One of the arguments must always be
+        present.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) <server name>     (4) <string>
+
+        This command replies with the Server ID of the server and a
+        string which tells the information about the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SERVER_ID
+
+
+   11   SILC_COMMAND_CONNECT
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used by operators to force a server to try to
+        establish a new connection to remote server or router.  The
+        Operator MUST specify the server/router to be connected by
+        setting <remote server> argument.  The port is 32 bit MSB value.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   12   SILC_COMMAND_PING
+
+        Max Arguments:  1
+            Arguments:  (1) <Server ID>
+
+        This command is used by client and server to test the communication
+        channel to its server if one suspects that the communication is not
+        working correctly.  The <Server ID> is the ID of the server the
+        sender is connected to.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.  Server returns
+        SILC_STATUS_OK in Status Payload if pinging was successful.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NOT_REGISTERED
+
+
+   13   SILC_COMMAND_OPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain server operator
+        privileges on some server or router.  Note that router operator
+        has router privileges that supersedes the server operator
+        privileges and this does not obtain those privileges.  Client
+        MUST use SILCOPER command to obtain router level privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).  The public
+        key that server will use to verify the signature found in the
+        payload should be verified.  It is recommended that the public
+        key is saved locally in the server and server would not use
+        any public keys received during the SKE.
+
+        After changing the mode the server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   14   SILC_COMMAND_JOIN
+
+        Max Arguments:  5
+            Arguments:  (1) <channel>       (2) <Client ID>
+                        (3) [<passphrase>]  (4) [<cipher>]
+                        (5) [<hmac>]
+
+        Join to channel/create new channel.  This command is used to
+        join to a channel.  If the channel does not exist the channel is
+        created.  If server is normal server this command MUST be sent
+        to router which will create the channel.  The channel MAY be
+        protected with passphrase.  If this is the case the passphrase
+        MUST be sent along the join command.
+
+        The name of the <channel> MUST NOT include any spaces (` '),
+        non-printable characters, commas (`,') or any wildcard characters.
+
+        The second argument <Client ID> is the Client ID of the client
+        which is joining to the client.  When client sends this command
+        to the server the <Client ID> MUST be the client's own ID.
+
+        Cipher to be used to secure the traffic on the channel MAY be
+        requested by sending the name of the requested <cipher>.  This
+        is used only if the channel does not exist and is created.  If
+        the channel already exists the cipher set previously for the
+        channel will be used to secure the traffic.  The computed MACs
+        of the channel message are produced by the default HMAC or by
+        the <hmac> provided for the command.
+
+        The server MUST check whether the user is allowed to join to
+        the requested channel.  Various modes set to the channel affect
+        the ability of the user to join the channel.  These conditions
+        are:
+
+            o  The user MUST be invited to the channel if the channel
+               is invite-only channel.
+
+            o  The Client ID/nickname/username/host name MUST NOT match
+               any active bans.
+
+            o  The correct passphrase MUST be provided if passphrase 
+               is set to the channel.
+
+            o  The user count limit, if set, MUST NOT be reached.
+
+        Reply messages to the command:
+
+        Max Arguments:  14
+            Arguments:  (1) <Status Payload>        (2) <channel> 
+                        (3) <Channel ID>            (4) <Client ID>
+                        (5) <channel mode mask>     (6) <created>
+                        (7) [<Channel Key Payload>] (8) [<ban list>]
+                        (9) [<invite list>]         (10) [<topic>]
+                        (11) [<hmac>]               (12) <list count>
+                        (13) <Client ID list>       (14) <client mode list>
+
+        This command replies with the channel name requested by the
+        client, channel ID of the channel and topic of the channel
+        if it exists.  The <Client ID> is the Client ID which was joined
+        to the channel.  It also replies with the channel mode mask
+        which tells all the modes set on the channel.  If the
+        channel is created the mode mask is zero (0).  If ban mask
+        and/or invite list is set they are sent as well.
+
+        The <list count>, <Client ID list> and <client mode list> are
+        the clients currently on the channel and their modes on the
+        channel.  The <Client ID list> is formed by adding the ID Payloads
+        one after the other.  The <client mode list> is formed by adding
+        32 bit MSB first order values one after the other.
+
+        Client receives the channel key in the reply message as well
+        inside <Channel Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_BAD_PASSWORD
+            SILC_STATUS_ERR_CHANNEL_IS_FULL
+            SILC_STATUS_ERR_NOT_INVITED
+            SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+            SILC_STATUS_ERR_BAD_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+
+
+   15   SILC_COMMAND_MOTD
+
+        Max Arguments:  1
+            Arguments:  (1) <server>
+
+        This command is used to query the Message of the Day of the server.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) [<motd>]
+
+        This command replies with the motd message if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   16   SILC_COMMAND_UMODE
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) <client mode mask>
+
+        This command is used by client to set/unset modes for itself.
+        However, there are some modes that the client MUST NOT set itself,
+        but they will be set by server.  However, client MAY unset any
+        mode.  Modes may be masked together ORing them thus having
+        several modes set.  Client MUST keep its client mode mask
+        locally so that the mode setting/unsetting would work without
+        problems.  Client may change only its own modes.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        The following client modes are defined:
+
+           0x0000    SILC_UMODE_NONE
+
+              No specific mode for client.  This is the initial
+              setting when new client is created.  The client is
+              normal client now.
+
+
+           0x0001    SILC_UMODE_SERVER_OPERATOR
+
+              Marks the user as server operator.  Client MUST NOT
+              set this mode itself.  Server sets this mode to the
+              client when client attains the server operator
+              privileges by SILC_COMMAND_OPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0002    SILC_UMODE_ROUTER_OPERATOR
+
+              Marks the user as router (SILC) operator.  Client
+              MUST NOT this mode itself.  Router sets this mode to
+              the client when client attains the router operator
+              privileges by SILC_COMMAND_SILCOPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0004    SILC_UMODE_GONE
+
+              Marks that the user is not currently present in the
+              SILC Network.  Client MAY set and unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <client mode mask>
+
+        This command replies with the changed client mode mask that
+        the client MUST to keep locally.
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_BAD_CLIENT_ID
+            SILC_STATUS_ERR_NOT_YOU
+            SILC_STATUS_ERR_PERM_DENIED
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   17   SILC_COMMAND_CMODE
+
+        Max Arguments:  7
+            Arguments:  (1) <Channel ID>      (2) <channel mode mask>
+                        (3) [<user limit>]    (4) [<passphrase>]
+                        (5) [<cipher>]        (6) [<hmac>]
+                        (7) [<auth payload>]
+
+        This command is used by client to set or change channel flags on
+        a channel.  Channel has several modes that set various properties
+        of a channel.  Modes may be masked together by ORing them thus
+        having several modes set.  The <Channel ID> is the ID of the
+        target channel.  The client changing channel mode MUST be on
+        the same channel and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CMODE_CHANGE notify
+        type MUST be distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CMODE_NONE
+
+              No specific mode on channel.  This is the default when
+              channel is created.  This means that channel is just plain
+              normal channel.
+
+
+           0x0001    SILC_CMODE_PRIVATE
+
+              Channel is private channel.  Private channels are shown
+              in the channel list listed with SILC_COMMAND_LIST command
+              with indication that the channel is private.  Also,
+              client on private channel will no be detected to be on
+              the channel as the channel is not shown in the client's
+              currently joined channel list.  Channel founder and 
+              channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]p on user interface
+              to set/unset this mode.
+
+
+           0x0002    SILC_CMODE_SECRET
+
+              Channel is secret channel.  Secret channels are not shown
+              in the list listed with SILC_COMMAND_LIST command.  Secret
+              channels can be considered to be invisible channels.
+              Channel founder and channel operator MAY set/unset this
+              mode.
+
+              Typical implementation would use [+|-]s on user interface
+              to set/unset this mode.
+
+
+           0x0004    SILC_CMODE_PRIVKEY
+
+              Channel uses private channel key to protect the traffic
+              on the channel.  When this mode is set the client will be
+              responsible to set the key it wants to use to encrypt and
+              decrypt the traffic on channel.  Server generated channel
+              keys are not used at all.  This mode provides additional
+              security as clients on channel may agree to use private
+              channel key that even servers do not know.  Naturally,
+              this requires that every client on the channel knows
+              the key before hand (it is considered to be pre-shared-
+              key).  The key material is RECOMMENDED to be processed
+              as stated in the [SILC3] in the section Processing the
+              Key Material.
+
+              As it is local setting it is possible to have several
+              private channel keys on one channel.  In this case several
+              clients can talk on same channel but only those clients
+              that share the key with the message sender will be able
+              to hear the talking.  Client SHOULD NOT display those
+              message for the end user that it is not able to decrypt
+              when this mode is set.
+
+              Only channel founder MAY set/unset this mode.  If this
+              mode is unset the server will distribute new channel
+              key to all clients on the channel which will be used
+              thereafter.
+
+              Typical implementation would use [+|-]k on user interface
+              to set/unset this mode.
+
+
+           0x0008    SILC_CMODE_INVITE
+
+              Channel is invite only channel.  Client may join to this
+              channel only if it is invited to the channel.  Channel
+              founder and channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]i on user interface
+              to set/unset this mode.
+
+
+           0x0010    SILC_CMODE_TOPIC
+
+              The topic of the channel may only be set by client that
+              is channel founder or channel operator.  Normal clients
+              on channel will not be able to set topic when this mode
+              is set.  Channel founder and channel operator MAY set/
+              unset this mode.
+
+              Typical implementation would use [+|-]t on user interface
+              to set/unset this mode.
+
+
+           0x0020    SILC_CMODE_ULIMIT
+
+              User limit has been set to the channel.  New clients
+              may not join to the channel when the limit set is
+              reached.  Channel founder and channel operator MAY set/
+              unset the limit.  The <user limit> argument is the
+              number of limited users.
+
+              Typical implementation would use [+|-]l on user interface
+              to set/unset this mode.
+
+
+           0x0040    SILC_CMODE_PASSPHRASE
+
+              Passphrase has been set to the channel.  Client may
+              join to the channel only if it is able to provide the
+              correct passphrase.  Setting passphrases to channel
+              is entirely safe as all commands are protected in the
+              SILC network.  Only channel founder MAY set/unset
+              the passphrase.  The <passphrase> argument is the
+              set passphrase.
+
+              Typical implementation would use [+|-]a on user interface
+              to set/unset this mode.
+
+
+           0x0080    SILC_CMODE_CIPHER
+
+              Sets specific cipher to be used to protect channel
+              traffic.  The <cipher> argument is the requested cipher.
+              When set or unset the server must re-generate new
+              channel key.  Only channel founder MAY set the cipher of 
+              the channel.  When unset the new key is generated using
+              default cipher for the channel.
+
+              Typical implementation would use [+|-]c on user interface
+              to set/unset this mode.
+
+
+           0x0100    SILC_CMODE_HMAC
+
+              Sets specific hmac to be used to compute the MACs of the
+              channel message.  The <hmac> argument is the requested hmac.
+              Only channel founder may set the hmac of the channel.
+
+              Typical implementation would use [+|-]h on user interface
+              to set/unset this mode.
+
+
+           0x0200    SILC_CMODE_FOUNDER_AUTH
+
+              Channel founder may set this mode to be able to regain
+              channel founder rights even if the client leaves the 
+              channel.  The <auth payload> is the Authentication Payload
+              consisting of the authentication method and authentication
+              data to be used in the authentication.  The server MUST
+              NOT accept NONE authentication method.  Also, if the 
+              method is public key authentication the server MUST NOT
+              save the authentication data from the payload as the
+              data is different on all authentications.  In this case the
+              server only saves the authentication method.  However,
+              server MUST verify the sent authentication payload and
+              set the mode only if the verification was successful.
+
+              Note that this mode is effective only in the current server.
+              The client MUST connect to the same server later to be able
+              to regain the channel founder rights.  The server MUST save
+              the public key of the channel founder and use that to identify
+              the client which is claiming the channel founder rights.
+              The rights may be claimed by the SILC_CUMODE_FOUNDER 
+              channel user mode using SILC_COMMAND_CUMODE command.  The
+              set authentication data remains valid as long as the channel
+              exists or until the founder unsets this mode.
+
+              Typical implementation would use [+|-]f on user interface
+              to set/unset this mode.
+
+        To make the mode system work, client MUST keep the channel mode
+        mask locally so that the mode setting and unsetting would work
+        without problems.  The client receives the initial channel mode
+        mask when it joins to the channel.  When the mode changes on
+        channel the server MUST distribute the changed channel mode mask
+        to all clients on the channel by sending the notify type
+        SILC_NOTIFY_TYPE_CMODE_CHANGE.  The notify type MUST also be sent
+        to the server's primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel mode mask>
+
+        This command replies with the changed channel mode mask that
+        client MUST keep locally.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   18   SILC_COMMAND_CUMODE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>    (2) <mode mask>
+                        (3) <Client ID>     (4) [<auth payload>]
+
+        This command is used by client to change channel user modes on
+        channel.  Users on channel may have some special modes and this
+        command is used by channel operators to set or change these modes.
+        The <Channel ID> is the ID of the target channel.  The <mode mask>
+        is OR'ed mask of modes.  The <Client ID> is the target client.
+        The client changing channel user modes MUST be on the same channel
+        as the target client and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CUMODE_CHANGE notify
+        type is distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CUMODE_NONE
+
+              No specific mode.  This is the normal situation for client.
+              Also, this is the mode set when removing all modes from
+              the target client.
+
+
+           0x0001    SILC_CUMODE_FOUNDER
+
+              The client is channel founder of the channel.  Usually this
+              mode is set only by the server when the channel was created.
+              However, if the SILC_CMODE_FOUNDER_AUTH channel mode has
+              been set, the client can claim channel founder privileges
+              by providing the <auth payload> that the server will use
+              to authenticate the client.  The public key that server will
+              use to verify the <auth payload> must the same public key
+              that was saved when the SILC_CMODE_FOUNDER_AUTH channel
+              mode was set.  The client MAY remove this mode at any time.
+
+
+           0x0002    SILC_CUMODE_OPERATOR
+
+              Sets channel operator privileges on the channel for a
+              client on the channel.  Channel founder and channel operator
+              MAY set/unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <channel user mode mask>
+                        (3) <Channel ID>      (4) <Client ID>
+
+        This command replies with the changed channel user mode mask that
+        client MUST keep locally. The <Channel ID> is the specified
+        channel.  The <Client ID> is the target client.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   19   SILC_COMMAND_KICK
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>  (2) <Client ID>  
+                        (3) [<comment>]
+
+        This command is used by channel operators to remove a client from
+        channel.  The <channel> argument is the channel the client to be
+        removed is on currently.  Note that the "kicker" must be on the same
+        channel.  If <comment> is provided it will be sent to the removed
+        client.
+
+        After kicking the client the server MUST send the notify type
+        SILC_NOTIFY_TYPE_KICKED to the channel and to its primary router.
+        The channel key MUST also be re-generated after kicking, unless
+        the SILC_CMODE_PRIVKEY mode is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   20   SILC_COMMAND_BAN
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                        (3) [<removing client>]
+
+        This command is used to manage the ban list of the channel
+        indicated by the <Channel ID>.  A client that is banned from
+        channel is no longer able to join the channel.  The client which
+        is executing this command MUST have at least channel operator
+        privileges on the channel.
+
+        The <adding client> and <removing client> are used to add to and
+        remove from the ban list.  The format of the <adding client> and
+        the <removing client> is of following format:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        The server MUST send the notify type SILC_NOTIFY_TYPE_BAN to its
+        primary router after adding to or removing from the ban list.
+        The wildcards MAY be used with this command.  If adding or removing
+        from than one clients then the lists are an comma (`,') separated.
+
+        If this command is executed without the ban arguments the command
+        merely replies with the current ban list.
+
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<ban list>]
+
+        This command replies with the <Channel ID> of the channel and
+        the current <ban list> of the channel if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   21   SILC_COMMAND_CLOSE
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used only by operator to close connection to a
+        remote site.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   22   SILC_COMMAND_SHUTDOWN
+
+        Max Arguments:  0
+            Arguments:  None
+
+        This command is used only by operator to shutdown the server.
+        All connections to the server will be closed and the server is
+        shutdown.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+
+
+   23   SILC_COMMAND_SILCOPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain router operator
+        privileges (also known as SILC operator) on the router.  Note
+        that router operator has privileges that supersedes the server
+        operator privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).  The public
+        key that router will use to verify the signature found in the
+        payload should be verified.  It is recommended that the public
+        key is saved locally in the router and router would not use
+        any public keys received during the SKE.
+
+        Difference between router operator and server operator is that
+        router operator is able to handle cell level properties while
+        server operator (even on router server) is able to handle only
+        local properties, such as, local connections and normal server
+        administration.  The router operator is also able to use the
+        SILC_COMMAND_KILL command.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   24   SILC_COMMAND_LEAVE
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used by client to leave a channel the client is
+        joined to. 
+
+        When leaving channel the server MUST send the notify type
+        SILC_NOTIFY_TYPE_LEAVE to its primary router and to the channel.
+        The channel key MUST also be re-generated when leaving the channel
+        and distribute it to all clients still currently on the channel.
+        The key MUST NOT be re-generated if the SILC_CMODE_PRIVKEY mode
+        is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+
+
+   25   SILC_COMMAND_USERS
+
+        Max Arguments:  2
+            Arguments:  (1) [<Channel ID>]  (2) [<channel name>]
+
+        This command is used to list user names currently on the requested
+        channel; either the argument <Channel ID> or the <channel name>. 
+        One of these arguments must be present.  The server MUST resolve
+        the user names and send a comma (`,') separated list of user names
+        on the channel.  Server or router MAY resolve the names by sending
+        SILC_COMMAND_WHOIS or SILC_COMMAND_IDENTIFY commands.
+
+        If the requested channel is a private or secret channel, this
+        command MUST NOT send the list of users, as private and secret
+        channels cannot be seen by outside.  In this case the returned
+        name list MAY include a indication that the server could not 
+        resolve the names of the users on the channel.  Also, in this case
+        Client ID's or client modes are not sent either.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <list count>      (4) <Client ID list>
+                        (5) <client mode list>
+
+        This command replies with the Channel ID of the requested channel
+        Client ID list of the users on the channel and list of their modes.
+        The Client ID list has Client ID's of all users in the list.  The 
+        <Client ID list> is formed by adding Client ID's one after another.
+        The <client mode list> is formed by adding client's user modes on
+        the channel one after another (4 bytes (32 bits) each).  The <list 
+        count> of length of 4 bytes (32 bits), tells the number of entries
+        in the lists.  Both lists MUST have equal number of entries.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+
+   26   SILC_COMMAND_GETKEY
+
+        Max Arguments:  1
+            Arguments:  (1) <ID Payload>
+
+        This command is used to fetch the public key of the client or
+        server indicated by the <ID Payload>.  The public key is fetched
+        from the server where to the client is connected.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>      (2) <ID Payload>
+                        (3) [<Public Key Payload>]
+
+        This command replies with the client's or server's ID and with
+        the <Public Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   27 - 199
+
+        Currently undefined commands.
+
+
+   200 - 254
+
+        These commands are reserved for private use and will not be defined
+        in this document.
+
+
+   255  SILC_COMMAND_MAX   
+
+        Reserved command.  This must not be sent.
+.in 3
+
+
+.ti 0
+2.3 SILC Command Status Types
+
+.ti 0
+2.3.1 SILC Command Status Payload
+
+Command Status Payload is sent in command reply messages to indicate
+the status of the command.  The payload is one of argument in the
+command thus this is the data area in Command Argument Payload described
+in [SILC2].  The payload is only 2 bytes of length.  The following diagram
+represents the Command Status Payload (field is always in MSB order).
+
+
+.in 21
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Status Message         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  SILC Command Status Payload
+
+
+.in 6
+o Status Message (2 bytes) - Indicates the status message.
+  All Status messages are described in the next section.
+.in 3
+
+
+.ti 0
+2.3.2 SILC Command Status List
+
+Command Status messages are returned in the command reply messages
+to indicate whether the command were executed without errors.  If error
+has occurred the status indicates which error occurred.  Status payload
+only sends numeric reply about the status.  Receiver of the payload must
+convert the numeric values into human readable error messages.  The
+list of status messages below has an example human readable error
+messages that client may display for the user.
+
+List of all defined command status messages following.
+
+.in 0
+   Generic status messages:
+
+   0    SILC_STATUS_OK
+
+        Ok status.  Everything went Ok.  The status payload maybe
+        safely ignored in this case.
+
+   1    SILC_STATUS_LIST_START
+
+        Start of the list.  There will be several command replies and
+        this reply is the start of the list.
+
+   2    SILC_STATUS_LIST_ITEM
+
+        Item in the list.  This is one of the item in the list but not the
+        first or last one.
+
+   3    SILC_STATUS_LIST_END
+
+        End of the list.  There were several command replies and this
+        reply is the last of the list.  There won't be other replies
+        belonging to this list after this one.
+
+   4 - 9
+
+        Currently undefined and has been reserved for the future.
+
+
+   Error status message:
+
+
+
+   10   SILC_STATUS_ERR_NO_SUCH_NICK
+
+        "No such nickname".  Requested nickname does not exist.
+
+   11   SILC_STATUS_ERR_NO_SUCH_CHANNEL
+
+        "No such channel".  Requested channel name does not exist.
+
+   12   SILC_STATUS_ERR_NO_SUCH_SERVER
+
+        "No such server".  Requested server name does not exist.
+
+   13   SILC_STATUS_ERR_TOO_MANY_TARGETS
+
+        "Duplicate recipients. No message delivered".  Message were
+        tried to be sent to recipient which has several occurrences in 
+        the recipient list.
+
+   14   SILC_STATUS_ERR_NO_RECIPIENT
+
+        "No recipient given".  Command required recipient which was
+        not provided.
+
+   15   SILC_STATUS_ERR_UNKNOWN_COMMAND
+
+        "Unknown command".  Command sent to server is unknown by the
+        server.
+
+   16   SILC_STATUS_ERR_WILDCARDS
+
+        "Wildcards cannot be used".  Wildcards were provided but they
+        weren't permitted.
+
+   17   SILC_STATUS_ERR_NO_CLIENT_ID
+
+        "No Client ID given".  Client ID were expected as command
+        parameter but were not found.
+
+   18   SILC_STATUS_ERR_NO_CHANNEL_ID
+
+        "No Channel ID given".  Channel ID were expected as command
+        parameter but were not found.
+
+   19   SILC_STATUS_ERR_NO_SERVER_ID
+
+        "No Serve ID given".  Server ID were expected as command
+        parameter but were not found.
+
+   20   SILC_STATUS_ERR_BAD_CLIENT_ID
+
+        "Bad Client ID".  Client ID provided were erroneous.
+
+   21   SILC_STATUS_ERR_BAD_CHANNEL_ID
+
+        "Bad Channel ID".  Channel ID provided were erroneous.
+
+   22   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+        "No such Client ID".  Client ID provided does not exist.
+
+   23   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+
+        "No such Channel ID".  Channel ID provided does not exist.
+
+   24   SILC_STATUS_ERR_NICKNAME_IN_USE
+
+        "Nickname already exists".  Nickname created could not be 
+        registered because number of same nicknames were already set to
+        maximum.  This is not expected to happen in real life but is
+        possible to occur.
+
+   25   SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+        "You are not on that channel".  The command were specified for
+        channel user is not currently on.
+
+   26   SILC_STATUS_ERR_USER_NOT_ON_CHANNEL
+
+        "They are not on channel".  The requested target client is not
+        on requested channel.
+
+   27   SILC_STATUS_ERR_USER_ON_CHANNEL
+
+        "User already on channel".  User were invited on channel they
+        already are on.
+
+   28   SILC_STATUS_ERR_NOT_REGISTERED
+
+        "You have not registered".  User executed command that requires
+        the client to be registered on the server before it may be
+        executed.
+
+   29   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+
+        "Not enough parameters".  Command requires more parameters
+        than provided.
+
+   30   SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+        "Too many parameters".  Too many parameters were provided
+        for the command.
+
+   31   SILC_STATUS_ERR_PERM_DENIED
+
+        "Permission denied".  Generic permission denied error status
+        to indicate disallowed access.
+
+   32   SILC_STATUS_ERR_BANNED_FROM_SERVER
+
+        "You are banned from this server".  The client tried to register
+        on server that has explicitly denied this host to connect.
+
+   33   SILC_STATUS_ERR_BAD_PASSWORD
+
+        "Cannot join channel. Incorrect password".  Password provided for 
+        channel were not accepted.
+
+   34   SILC_STATUS_ERR_CHANNEL_IS_FULL
+
+        "Cannot join channel. Channel is full".  The channel is full
+        and client cannot be joined to it.
+
+   35   SILC_STATUS_ERR_NOT_INVITED
+
+        "Cannot join channel. You have not been invited".  The channel
+        is invite only channel and client has not been invited.
+
+   36   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+
+        "Cannot join channel. You have been banned".  The client has
+        been banned from the channel.
+
+   37   SILC_STATUS_ERR_UNKNOWN_MODE
+
+        "Unknown mode".  Mode provided by the client were unknown to
+        the server.
+
+   38   SILC_STATUS_ERR_NOT_YOU
+
+        "Cannot change mode for other users".  User tried to change
+        someone else's mode.
+
+   39   SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+        "Permission denied. You are not channel operator".  Command may 
+        be executed only by channel operator.
+
+   40   SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
+
+        "Permission denied. You are not channel founder".  Command may 
+        be executed only by channel operator.
+
+   41   SILC_STATUS_ERR_NO_SERVER_PRIV
+
+        "Permission denied. You are not server operator".  Command may
+        be executed only by server operator.
+
+   42   SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+        "Permission denied. You are not SILC operator".  Command may be
+        executed only by router (SILC) operator.
+
+   43   SILC_STATUS_ERR_BAD_NICKNAME
+
+        "Bad nickname".  Nickname requested contained illegal characters
+        or were malformed.
+
+   44   SILC_STATUS_ERR_BAD_CHANNEL
+
+        "Bad channel name".  Channel requested contained illegal characters
+        or were malformed.
+
+   45   SILC_STATUS_ERR_AUTH_FAILED
+
+        "Authentication failed".  The authentication data sent as 
+        argument were wrong and thus authentication failed.
+
+   46   SILC_STATUS_ERR_UNKOWN_ALGORITHM
+
+        "The algorithm was not supported."  The server does not support the
+        requested algorithm.
+
+   47   SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+        "No such Server ID".  Server ID provided does not exist.
+
+.in 3
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 13 May 2002
diff --git a/doc/draft-riikonen-silc-commands-03.nroff b/doc/draft-riikonen-silc-commands-03.nroff
new file mode 100644 (file)
index 0000000..64e3d44
--- /dev/null
@@ -0,0 +1,1965 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH XXX
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-commands-03.txt                     XXX
+Expires: XXX
+
+.in 3
+
+.ce 2
+SILC Commands
+<draft-riikonen-silc-commands-03.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  The
+SILC Commands are very important part of the SILC protocol.  Usually
+the commands are used by SILC clients to manage the SILC session, but
+also SILC servers may use the commands.  This memo specifies detailed
+command messages and command reply messages.
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  2
+2 SILC Commands .................................................  2
+  2.1 SILC Commands Syntax ......................................  2
+  2.2 SILC Commands List ........................................  4
+  2.3 SILC Command Status Types ................................. 33
+      2.3.1 SILC Command Status Payload ......................... 33
+      2.3.2 SILC Command Status List ............................ 33
+3 Security Considerations ....................................... 38
+4 References .................................................... 38
+5 Author's Address .............................................. 40
+
+
+.ti 0
+1. Introduction
+
+This document describes the commands used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+document specifies detailed command messages and command reply messages.
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+See the [SILC1] for the requirements and the restrictions for the usage
+of the SILC commands.  The [SILC2] defines the command packet type and
+the Command Payload which is actually used to deliver the commands and
+command reply messages.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Commands
+
+.ti 0
+2.1 SILC Commands Syntax
+
+This section briefly describes the syntax of the command notions
+in this document.  Every field in command is separated from each
+other by whitespaces (` ') indicating that each field is independent
+argument and each argument MUST have own Command Argument Payload.
+The number of maximum arguments are defined with each command
+separately.  The Command Argument Payload is described in [SILC2].
+
+Every command defines specific number for each argument.  Currently,
+they are defined in ascending order; first argument has number one 
+(1), second has number two (2) and so on.  This number is set into the
+Argument Type field in the Command Argument Payload.  This makes it
+possible to send the arguments in free order as the number MUST be
+used to identify the type of the argument.  This makes is it also
+possible to have multiple optional arguments in commands and in
+command replies.  The number of argument is marked in parentheses
+before the actual argument.
+
+
+
+.in 6
+Example:  Arguments:  (1) <nickname> (2) <username@host>
+.in 3
+   
+
+Every command replies with Status Payload.  This payload tells the
+sender of the command whether the command was completed successfully or
+whether there was an error.  If error occurred the payload includes the
+error type.  In the next section the Status Payload is not described 
+as it is common to all commands and has been described here.  Commands 
+MAY reply with other arguments as well.  These arguments are command 
+specific and are described in the next section.
+
+Example command:
+.in 6
+
+EXAMPLE_COMMAND
+
+.in 8
+Max Arguments:  3
+    Arguments:  (1) <nickname>[@<server>]  (2) <message>
+                (3) [<count>]
+
+The command has maximum of 3 arguments.  However, only first
+and second arguments are mandatory.
+
+First argument <nickname> is mandatory but may have optional
+<nickname@server> format as well.  Second argument is mandatory
+<message> argument.  Third argument is optional <count> argument.
+
+The numbers in parentheses are the argument specific numbers
+that specify the type of the argument in Command Argument Payload.
+The receiver always knows that, say, argument number two (2) is
+<message> argument, regardless of the ordering of the arguments in
+the Command Payload.
+
+Reply messages to the command:
+
+Max Arguments:  4
+    Arguments:  (1) <Status Payload>  (2) [<channel list>]
+                (3) <idle time>       (4) [<away message>]
+
+This command may reply with maximum of 4 arguments.  However,
+only the first and third arguments are mandatory.  The numbers
+in the parentheses have the same meaning as in the upper
+command sending specification.
+
+Every command reply with <Status Payload>, it is mandatory 
+argument for all command replies and for this reason it is not
+described in the command reply descriptions.
+
+
+
+Status messages:
+
+    SILC_STATUS_OK
+    SILC_STATUS_ERR_TOO_MANY_TARGETS
+    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+    SILC_STATUS_ERR_NO_SUCH_NICK
+
+Every command reply also defines set of status message that it
+may return inside the <Status Payload>.  All status messages
+are defined in the section 2.3 SILC Command Status Types.
+
+.in 3
+Every command that has some kind of ID as argument (for example
+<Client ID>) are actually ID Payloads, defined in [SILC2] that includes
+the type of the ID, length of the ID and the actual ID data.  This
+way variable length ID's can be sent as arguments.
+
+
+.ti 0
+2.2 SILC Commands List
+
+This section lists all SILC commands, however, it is expected that a
+implementation and especially client implementation has many more
+commands that has only local affect.  These commands are official
+SILC commands that has both client and server sides and cannot be
+characterized as local commands.
+
+List of all defined commands in SILC follows.
+
+.in 0
+   0    SILC_COMMAND_NONE
+
+        None.  This is reserved command and MUST NOT be sent.
+
+
+   1    SILC_COMMAND_WHOIS
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Whois command is used to query various information about specific
+        user.  The user may be requested by their nickname and server name.
+        The query may find multiple matching users as there are no unique
+        nicknames in the SILC.  The <count> option may be given to narrow
+        down the number of accepted results.  If this is not defined there
+        are no limit of accepted results.  The query may also be narrowed
+        down by defining the server name of the nickname.  The <count> is
+        int string format.
+
+        It is also possible to search the user by Client ID.  If the 
+        <Client ID> is provided server MUST use it as the search value
+        instead of the <nickname>.  One of the arguments MUST be given.
+        It is also possible to define multiple Client ID's to search
+        multiple users sending only one WHOIS command.  In this case the
+        Client ID's are appended as normal arguments.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  It is not allowed
+        to request all users on some server.  The WHOIS requests MUST 
+        be based on specific nickname request.
+
+        The WHOIS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still MUST
+        search its locally connected clients.  The router MUST send
+        this command to the server which owns the requested client.  That
+        server MUST reply to the command.  Server MUST NOT send whois
+        replies to the client until it has received the reply from its
+        router.
+
+        Reply messages to the command:
+
+        Max Arguments:  9
+            Arguments:  (1) <Status Payload>       (2) <Client ID> 
+                        (3) <nickname>[@<server>]  (4) <username@host> 
+                        (5) <real name>            (6) [<Channel Payload 
+                                                         list>] 
+                        (7) [<user mode>]          (8) [<idle time>]
+                        (9) [<fingerprint>]
+
+
+        This command may reply with several command reply messages to
+        form a list of results.  In this case the status payload will
+        include STATUS_LIST_START status in the first reply and
+        STATUS_LIST_END in the last reply to indicate the end of the
+        list.  If there are only one reply the status is set to normal
+        STATUS_OK.
+
+        The command replies include the Client ID of the nickname,
+        nickname and server name, user name and host name and user's real
+        name.  Client SHOULD process these replies only after the last
+        reply has been received with the STATUS_LIST_END status.  If the
+        <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        The server may return the list of channel the client has joined.
+        In this case the list is list of Channel Payloads.  The Mode Mask
+        in the Channel Payload (see [SILC2] and section 2.3.2.3 for the
+        Channel Payload) is the client's mode on the channel.  The list
+        is encoded by adding the Channel Payloads one after the other.
+
+        The server may also send client's user mode, idle time, and the
+        fingerprint of the client's public key.  The <fingerprint> is the
+        binary hash digest of the public key.  The fingerprint MUST NOT
+        be sent if the server has not verified the proof of posession of
+        the corresponding private key.  Server can do this during the
+        SILC Key Exchange protocol.  The <fingerprint> is SHA1 digest.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   2    SILC_COMMAND_WHOWAS
+
+        Max Arguments:  2
+            Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
+
+        Whowas.  This command is used to query history information about
+        specific user.  The user may be requested by their nickname and 
+        server name.  The query may find multiple matching users as there
+        are no unique nicknames in the SILC.  The <count> option may be
+        given to narrow down the number of accepted results.  If this
+        is not defined there are no limit of accepted results.  The query
+        may also be narrowed down by defining the server name of the 
+        nickname.  The <count> is in string format.
+
+        To prevent miss-use of this command wildcards in the nickname
+        or in the server name are not permitted.  The WHOWAS requests MUST 
+        be based on specific nickname request.
+
+        The WHOWAS request MUST be always sent to the router by server
+        so that all users are searched.  However, the server still must
+        search its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>        (2) <Client ID>
+                        (3) <nickname>[@<server>]   (4) <username@host>
+                        (5) [<real name>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with nickname and user name and host name.
+        Every server MUST keep history for some period of time of its
+        locally connected clients.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   3    SILC_COMMAND_IDENTIFY
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<server name>]
+                        (3) [<channel name>]         (4) [<count>]
+                        (5) [<ID Payload>]           (n) [...]
+
+        Identify command is used to query information about an entity by
+        the entity's name or ID.  This command can be used to query
+        information about clients, server and channels.
+
+        The query may find multiple matching entities.  The <count> option
+        may be given to narrow down the number of accepted results.  If
+        this is not defined there are no limit of accepted results.  The
+        <count> is in string format.
+
+        It is also possible to search the entity by its ID.  If the
+        <ID Payload> is provided server must use it as the search value
+        instead of the entity's name.  One of the arguments must be given.
+        It is also possible to define multiple ID Payloads to search
+        multiple entities sending only one IDENTIFY command.  In this case
+        the ID Payloads are appended as normal arguments.  The type of the
+        entity is defined by the type of the ID Payload.
+
+        To prevent miss-use of this command wildcards in the names are
+        not permitted.  It is not allowed to request for example all users
+        on server.
+
+        Implementations may not want to give interface access to this
+        command as it is hardly a command that would be used by an end
+        user.  However, it must be implemented as it is used with private
+        message sending.
+
+        The IDENTIFY command MUST be always sent to the router by server
+        so that all users are searched.  However, server MUST still search
+        its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>   (2) <ID Payload>
+                        (3) [<entity's name>]  (4) [<info>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        When querying clients the <entity's name> must include the client's
+        nickname in the following format: nickname[@server].  The
+        <info> must include the client's username and host in the following
+        format: username@host.
+
+        When querying servers the <entity's name> must include the server's
+        full name.  The <info> may be omitted.
+
+        When querying channels the <entity's name> must include the
+        channel's name.  The <info> may be omitted.
+
+        If the <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   4    SILC_COMMAND_NICK
+
+        Max Arguments:  1
+            Arguments:  (1) <nickname>
+
+        Set/change nickname.  This command is used to set nickname for
+        user.  Nickname MUST NOT include any spaces (` '), non-printable
+        characters, commas (`,') and any wildcard characters.  Note that
+        nicknames in SILC are case-sensitive which must be taken into
+        account when searching clients by nickname.
+
+        When nickname is changed new Client ID is generated.  Server MUST
+        distribute SILC_NOTIFY_TYPE_NICK_CHANGE to local clients on the
+        channels (if any) the client is joined on.  Then it MUST send
+        SILC_PACKET_REPLACE_ID to its primary route to replace the old
+        Client ID with the new one.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <New ID Payload>
+
+        This command is replied always with New ID Payload that is
+        generated by the server every time user changes their nickname.
+        Client receiving this payload MUST start using the received
+        Client ID as its current valid Client ID.  The New ID Payload
+        is described in [SILC2].
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NICKNAME_IN_USE
+            SILC_STATUS_ERR_BAD_NICKNAME
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   5    SILC_COMMAND_LIST
+
+        Max Arguments:  1
+            Arguments:  (1) [<Channel ID>]
+
+        The list command is used to list channels and their topics on the
+        current server.  If the <Channel ID> parameter is used, only the
+        status of that channel is displayed.  Secret channels are not
+        listed at all.  Private channels are listed with status indicating
+        that the channel is private.  Router MAY reply with all channels
+        it knows about.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel>         (4) [<topic>]
+                        (5) [<user count>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        This command replies with Channel ID, name and the topic of the
+        channel.  If the channel is private channel the <topic> SHOULD
+        include the "*private*" string.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   6    SILC_COMMAND_TOPIC
+
+        Max Arguments:  2
+            Arguments:  (1) <Channel ID>  (2) [<topic>]
+
+        This command is used to change or view the topic of a channel.
+        The topic for channel <Channel ID> is returned if there is no
+        <topic> given.  If the <topic> parameter is present, the topic
+        for that channel will be changed, if the channel modes permit
+        this action.
+
+        After setting the topic the server MUST send the notify type
+        SILC_NOTIFY_TYPE_TOPIC_SET to its primary router and then to
+        the channel which topic was changed.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <Channel ID> 
+                        (3) [<topic>]
+
+        The command may reply with the topic of the channel if it is
+        set.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   7    SILC_COMMAND_INVITE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>       (2) [<Client ID>]
+                        (3) [<adding client>]  (4) [<removing client>]
+
+        This command is used to invite other clients to join to the
+        channel.  The <Client ID> argument is the target client's ID that
+        is being invited.  The <Channel ID> is the Channel ID of the
+        requested channel.  The sender of this command MUST be on the
+        channel.  The server MUST also send the notify type
+        SILC_NOTIFY_TYPE_INVITE to its primary router and then to the
+        client indicated by the <Client ID>.
+
+        The <adding client> and <removing client> can be used to add to
+        and remove from the invite list.  The format of the <adding client>
+        and <removing client> is as follows:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        When adding to or removing from the invite list the server MUST
+        send the notify type SILC_NOTIFY_TYPE_INVITE to its primary router
+        and MUST NOT send it to the client which was added to the list.
+        The client which executes this command MUST have at least channel
+        operator privileges to be able to add to or remove from the invite
+        list.  The wildcards MAY be used with this command.  If adding or
+        removing more than one client then the lists are an comma (`,')
+        separated.
+
+        Note that the <Client ID> provided MUST be resolved into correct
+        nickname and host name and add to the invite list before sending
+        the notify packet.
+        
+        When this command is given with only <Channel ID> argument then
+        the command merely returns the invite list of the channel.   This
+        command MUST fail if the requested channel does not exist, the
+        requested <Client ID> is already on the channel or if the channel
+        is invite only channel and the caller of this command does not
+        have at least channel operator privileges.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<invite list>]
+
+       This command replies with the invite list of the channel if it
+       exists.  The <invite list> may be omitted if the list was not
+        altered.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   8    SILC_COMMAND_QUIT
+
+        Max Arguments:  1
+            Arguments:  (1) [<quit message>]
+
+        This command is used by client to end SILC session.  The server
+        must close the connection to a client which sends this command.
+        if <quit message> is given it will be sent to other clients on
+        channel if the client is on channel when quitting.
+
+        Reply messages to the command:
+
+        This command does not reply anything.
+
+
+    9   SILC_COMMAND_KILL
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) [<comment>]
+
+        This command is used by SILC operators to remove a client from
+        SILC network.  The removing has temporary effects and client may
+        reconnect to SILC network.  The <Client ID> is the client to be
+        removed from SILC.  The <comment> argument may be provided to 
+        give to the removed client some information why it was removed
+        from the network.
+
+        When killing a client the router MUST first send notify type
+        SILC_NOTIFY_TYPE_KILLED to all channels the client has joined.
+        The packet MUST NOT be sent to the killed client on the channels.
+        Then, the router MUST send the same notify type to its primary
+        router.  Finally, the router MUST send the same notify type 
+        directly to the client which was killed.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   10   SILC_COMMAND_INFO
+
+        Max Arguments:  2
+            Arguments:  (1) [<server>]  (2) [<Server ID>]
+
+        This command is used to fetch various information about a server.
+        If <server> argument is specified the command MUST be sent to
+        the requested server.
+
+        If the <Server ID> is specified the server information if fetched
+        by the provided Server ID.  One of the arguments must always be
+        present.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) <server name>     (4) <string>
+
+        This command replies with the Server ID of the server and a
+        string which tells the information about the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SERVER_ID
+
+
+   11   SILC_COMMAND_CONNECT
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used by operators to force a server to try to
+        establish a new connection to remote server or router.  The
+        Operator MUST specify the server/router to be connected by
+        setting <remote server> argument.  The port is 32 bit MSB value.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   12   SILC_COMMAND_PING
+
+        Max Arguments:  1
+            Arguments:  (1) <Server ID>
+
+        This command is used by client and server to test the communication
+        channel to its server if one suspects that the communication is not
+        working correctly.  The <Server ID> is the ID of the server the
+        sender is connected to.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.  Server returns
+        SILC_STATUS_OK in Status Payload if pinging was successful.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NOT_REGISTERED
+
+
+   13   SILC_COMMAND_OPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain server operator
+        privileges on some server or router.  Note that router operator
+        has router privileges that supersedes the server operator
+        privileges and this does not obtain those privileges.  Client
+        MUST use SILCOPER command to obtain router level privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).  The public
+        key that server will use to verify the signature found in the
+        payload should be verified.  It is recommended that the public
+        key is saved locally in the server and server would not use
+        any public keys received during the SKE.
+
+        After changing the mode the server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   14   SILC_COMMAND_JOIN
+
+        Max Arguments:  6
+            Arguments:  (1) <channel>       (2) <Client ID>
+                        (3) [<passphrase>]  (4) [<cipher>]
+                        (5) [<hmac>]        (6) [<founder auth>]
+
+        Join to channel/create new channel.  This command is used to
+        join to a channel.  If the channel does not exist the channel is
+        created.  If server is normal server this command MUST be sent
+        to router which will create the channel.  The channel MAY be
+        protected with passphrase.  If this is the case the passphrase
+        MUST be sent along the join command.
+
+        The name of the <channel> MUST NOT include any spaces (` '),
+        non-printable characters, commas (`,') or any wildcard characters.
+
+        The second argument <Client ID> is the Client ID of the client
+        which is joining to the client.  When client sends this command
+        to the server the <Client ID> MUST be the client's own ID.
+
+        Cipher to be used to secure the traffic on the channel MAY be
+        requested by sending the name of the requested <cipher>.  This
+        is used only if the channel does not exist and is created.  If
+        the channel already exists the cipher set previously for the
+        channel will be used to secure the traffic.  The computed MACs
+        of the channel message are produced by the default HMAC or by
+        the <hmac> provided for the command.
+
+        The <founder auth> is Authentication Payload providing the
+        authentication for gaining founder privileges on the channel
+        when joining the channel.  The client may provide this if it
+        knows that it is the founder of the channel and that the 
+        SILC_CMODE_FOUNDER_AUTH mode is set on the channel.  The server
+        MUST verify whether the client is able to gain the founder
+        privileges the same way as the client had given the
+        SILC_COMMAND_CUMODE command to gain founder privileges.  The
+        client is still able to join the channel even if the founder
+        privileges could not be gained.
+
+        The server MUST check whether the user is allowed to join to
+        the requested channel.  Various modes set to the channel affect
+        the ability of the user to join the channel.  These conditions
+        are:
+
+            o  The user MUST be invited to the channel if the channel
+               is invite-only channel.
+
+            o  The Client ID/nickname/username/host name MUST NOT match
+               any active bans.
+
+            o  The correct passphrase MUST be provided if passphrase 
+               is set to the channel.
+
+            o  The user count limit, if set, MUST NOT be reached.
+
+        If the client provided correct <founder auth> payload it can
+        override these conditions, except the condition for the passphrase.
+        The correct passphrase MUST be provided even if <founder auth>
+        payload is provided.
+
+        Reply messages to the command:
+
+        Max Arguments:  14
+            Arguments:  (1) <Status Payload>        (2) <channel> 
+                        (3) <Channel ID>            (4) <Client ID>
+                        (5) <channel mode mask>     (6) <created>
+                        (7) [<Channel Key Payload>] (8) [<ban list>]
+                        (9) [<invite list>]         (10) [<topic>]
+                        (11) [<hmac>]               (12) <list count>
+                        (13) <Client ID list>       (14) <client mode list>
+
+        This command replies with the channel name requested by the
+        client, channel ID of the channel and topic of the channel
+        if it exists.  The <Client ID> is the Client ID which was joined
+        to the channel.  It also replies with the channel mode mask
+        which tells all the modes set on the channel.  If the
+        channel is created the mode mask is zero (0).  If ban mask
+        and/or invite list is set they are sent as well.
+
+        The <list count>, <Client ID list> and <client mode list> are
+        the clients currently on the channel and their modes on the
+        channel.  The <Client ID list> is formed by adding the ID Payloads
+        one after the other.  The <client mode list> is formed by adding
+        32 bit MSB first order values one after the other.
+
+        Client receives the channel key in the reply message as well
+        inside <Channel Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_BAD_PASSWORD
+            SILC_STATUS_ERR_CHANNEL_IS_FULL
+            SILC_STATUS_ERR_NOT_INVITED
+            SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+            SILC_STATUS_ERR_BAD_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+
+
+   15   SILC_COMMAND_MOTD
+
+        Max Arguments:  1
+            Arguments:  (1) <server>
+
+        This command is used to query the Message of the Day of the server.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) [<motd>]
+
+        This command replies with the motd message if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   16   SILC_COMMAND_UMODE
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) <client mode mask>
+
+        This command is used by client to set/unset modes for itself.
+        However, there are some modes that the client MUST NOT set itself,
+        but they will be set by server.  However, client MAY unset any
+        mode.  Modes may be masked together ORing them thus having
+        several modes set.  Client MUST keep its client mode mask
+        locally so that the mode setting/unsetting would work without
+        problems.  Client may change only its own modes.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        The following client modes are defined:
+
+           0x0000    SILC_UMODE_NONE
+
+              No specific mode for client.  This is the initial
+              setting when new client is created.  The client is
+              normal client now.
+
+
+           0x0001    SILC_UMODE_SERVER_OPERATOR
+
+              Marks the user as server operator.  Client MUST NOT
+              set this mode itself.  Server sets this mode to the
+              client when client attains the server operator
+              privileges by SILC_COMMAND_OPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0002    SILC_UMODE_ROUTER_OPERATOR
+
+              Marks the user as router (SILC) operator.  Client
+              MUST NOT this mode itself.  Router sets this mode to
+              the client when client attains the router operator
+              privileges by SILC_COMMAND_SILCOPER command.  Client
+              MAY unset the mode itself.
+
+
+           0x0004    SILC_UMODE_GONE
+
+              Marks that the user is not currently present in the
+              SILC Network.  Client MAY set and unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <client mode mask>
+
+        This command replies with the changed client mode mask that
+        the client MUST to keep locally.
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_BAD_CLIENT_ID
+            SILC_STATUS_ERR_NOT_YOU
+            SILC_STATUS_ERR_PERM_DENIED
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   17   SILC_COMMAND_CMODE
+
+        Max Arguments:  7
+            Arguments:  (1) <Channel ID>      (2) <channel mode mask>
+                        (3) [<user limit>]    (4) [<passphrase>]
+                        (5) [<cipher>]        (6) [<hmac>]
+                        (7) [<auth payload>]
+
+        This command is used by client to set or change channel flags on
+        a channel.  Channel has several modes that set various properties
+        of a channel.  Modes may be masked together by ORing them thus
+        having several modes set.  The <Channel ID> is the ID of the
+        target channel.  The client changing channel mode MUST be on
+        the same channel and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CMODE_CHANGE notify
+        type MUST be distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CMODE_NONE
+
+              No specific mode on channel.  This is the default when
+              channel is created.  This means that channel is just plain
+              normal channel.
+
+
+           0x0001    SILC_CMODE_PRIVATE
+
+              Channel is private channel.  Private channels are shown
+              in the channel list listed with SILC_COMMAND_LIST command
+              with indication that the channel is private.  Also,
+              client on private channel will no be detected to be on
+              the channel as the channel is not shown in the client's
+              currently joined channel list.  Channel founder and 
+              channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]p on user interface
+              to set/unset this mode.
+
+
+           0x0002    SILC_CMODE_SECRET
+
+              Channel is secret channel.  Secret channels are not shown
+              in the list listed with SILC_COMMAND_LIST command.  Secret
+              channels can be considered to be invisible channels.
+              Channel founder and channel operator MAY set/unset this
+              mode.
+
+              Typical implementation would use [+|-]s on user interface
+              to set/unset this mode.
+
+
+           0x0004    SILC_CMODE_PRIVKEY
+
+              Channel uses private channel key to protect the traffic
+              on the channel.  When this mode is set the client will be
+              responsible to set the key it wants to use to encrypt and
+              decrypt the traffic on channel.  Server generated channel
+              keys are not used at all.  This mode provides additional
+              security as clients on channel may agree to use private
+              channel key that even servers do not know.  Naturally,
+              this requires that every client on the channel knows
+              the key before hand (it is considered to be pre-shared-
+              key).  The key material is RECOMMENDED to be processed
+              as stated in the [SILC3] in the section Processing the
+              Key Material.
+
+              As it is local setting it is possible to have several
+              private channel keys on one channel.  In this case several
+              clients can talk on same channel but only those clients
+              that share the key with the message sender will be able
+              to hear the talking.  Client SHOULD NOT display those
+              message for the end user that it is not able to decrypt
+              when this mode is set.
+
+              Only channel founder MAY set/unset this mode.  If this
+              mode is unset the server will distribute new channel
+              key to all clients on the channel which will be used
+              thereafter.
+
+              Typical implementation would use [+|-]k on user interface
+              to set/unset this mode.
+
+
+           0x0008    SILC_CMODE_INVITE
+
+              Channel is invite only channel.  Client may join to this
+              channel only if it is invited to the channel.  Channel
+              founder and channel operator MAY set/unset this mode.
+
+              Typical implementation would use [+|-]i on user interface
+              to set/unset this mode.
+
+
+           0x0010    SILC_CMODE_TOPIC
+
+              The topic of the channel may only be set by client that
+              is channel founder or channel operator.  Normal clients
+              on channel will not be able to set topic when this mode
+              is set.  Channel founder and channel operator MAY set/
+              unset this mode.
+
+              Typical implementation would use [+|-]t on user interface
+              to set/unset this mode.
+
+
+           0x0020    SILC_CMODE_ULIMIT
+
+              User limit has been set to the channel.  New clients
+              may not join to the channel when the limit set is
+              reached.  Channel founder and channel operator MAY set/
+              unset the limit.  The <user limit> argument is the
+              number of limited users.
+
+              Typical implementation would use [+|-]l on user interface
+              to set/unset this mode.
+
+
+           0x0040    SILC_CMODE_PASSPHRASE
+
+              Passphrase has been set to the channel.  Client may
+              join to the channel only if it is able to provide the
+              correct passphrase.  Setting passphrases to channel
+              is entirely safe as all commands are protected in the
+              SILC network.  Only channel founder MAY set/unset
+              the passphrase.  The <passphrase> argument is the
+              set passphrase.
+
+              Typical implementation would use [+|-]a on user interface
+              to set/unset this mode.
+
+
+           0x0080    SILC_CMODE_CIPHER
+
+              Sets specific cipher to be used to protect channel
+              traffic.  The <cipher> argument is the requested cipher.
+              When set or unset the server must re-generate new
+              channel key.  Only channel founder MAY set the cipher of 
+              the channel.  When unset the new key is generated using
+              default cipher for the channel.
+
+              Typical implementation would use [+|-]c on user interface
+              to set/unset this mode.
+
+
+           0x0100    SILC_CMODE_HMAC
+
+              Sets specific hmac to be used to compute the MACs of the
+              channel message.  The <hmac> argument is the requested hmac.
+              Only channel founder may set the hmac of the channel.
+
+              Typical implementation would use [+|-]h on user interface
+              to set/unset this mode.
+
+
+           0x0200    SILC_CMODE_FOUNDER_AUTH
+
+              Channel founder may set this mode to be able to regain
+              channel founder rights even if the client leaves the 
+              channel.  The <auth payload> is the Authentication Payload
+              consisting of the authentication method and authentication
+              data to be used in the authentication.  The server MUST
+              NOT accept NONE authentication method.  Also, if the 
+              method is public key authentication the server MUST NOT
+              save the authentication data from the payload as the
+              data is different on all authentications.  In this case the
+              server only saves the authentication method.  However,
+              server MUST verify the sent authentication payload and
+              set the mode only if the verification was successful.
+
+              Note that this mode is effective only in the current server.
+              The client MUST connect to the same server later to be able
+              to regain the channel founder rights.  The server MUST save
+              the public key of the channel founder and use that to identify
+              the client which is claiming the channel founder rights.
+              The rights may be claimed by the SILC_CUMODE_FOUNDER 
+              channel user mode using SILC_COMMAND_CUMODE command.  The
+              set authentication data remains valid as long as the channel
+              exists or until the founder unsets this mode.
+
+              Typical implementation would use [+|-]f on user interface
+              to set/unset this mode.
+
+        To make the mode system work, client MUST keep the channel mode
+        mask locally so that the mode setting and unsetting would work
+        without problems.  The client receives the initial channel mode
+        mask when it joins to the channel.  When the mode changes on
+        channel the server MUST distribute the changed channel mode mask
+        to all clients on the channel by sending the notify type
+        SILC_NOTIFY_TYPE_CMODE_CHANGE.  The notify type MUST also be sent
+        to the server's primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel mode mask>
+
+        This command replies with the changed channel mode mask that
+        client MUST keep locally.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   18   SILC_COMMAND_CUMODE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>    (2) <mode mask>
+                        (3) <Client ID>     (4) [<auth payload>]
+
+        This command is used by client to change channel user modes on
+        channel.  Users on channel may have some special modes and this
+        command is used by channel operators to set or change these modes.
+        The <Channel ID> is the ID of the target channel.  The <mode mask>
+        is OR'ed mask of modes.  The <Client ID> is the target client.
+        The client changing channel user modes MUST be on the same channel
+        as the target client and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CUMODE_CHANGE notify
+        type is distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CUMODE_NONE
+
+              No specific mode.  This is the normal situation for client.
+              Also, this is the mode set when removing all modes from
+              the target client.
+
+
+           0x0001    SILC_CUMODE_FOUNDER
+
+              The client is channel founder of the channel.  Usually this
+              mode is set only by the server when the channel was created.
+              However, if the SILC_CMODE_FOUNDER_AUTH channel mode has
+              been set, the client can claim channel founder privileges
+              by providing the <auth payload> that the server will use
+              to authenticate the client.  The public key that server will
+              use to verify the <auth payload> must the same public key
+              that was saved when the SILC_CMODE_FOUNDER_AUTH channel
+              mode was set.  The client MAY remove this mode at any time.
+
+
+           0x0002    SILC_CUMODE_OPERATOR
+
+              Sets channel operator privileges on the channel for a
+              client on the channel.  Channel founder and channel operator
+              MAY set/unset this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <channel user mode mask>
+                        (3) <Channel ID>      (4) <Client ID>
+
+        This command replies with the changed channel user mode mask that
+        client MUST keep locally. The <Channel ID> is the specified
+        channel.  The <Client ID> is the target client.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   19   SILC_COMMAND_KICK
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>  (2) <Client ID>  
+                        (3) [<comment>]
+
+        This command is used by channel operators to remove a client from
+        channel.  The <channel> argument is the channel the client to be
+        removed is on currently.  Note that the "kicker" must be on the same
+        channel.  If <comment> is provided it will be sent to the removed
+        client.
+
+        After kicking the client the server MUST send the notify type
+        SILC_NOTIFY_TYPE_KICKED to the channel and to its primary router.
+        The channel key MUST also be re-generated after kicking, unless
+        the SILC_CMODE_PRIVKEY mode is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   20   SILC_COMMAND_BAN
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                        (3) [<removing client>]
+
+        This command is used to manage the ban list of the channel
+        indicated by the <Channel ID>.  A client that is banned from
+        channel is no longer able to join the channel.  The client which
+        is executing this command MUST have at least channel operator
+        privileges on the channel.
+
+        The <adding client> and <removing client> are used to add to and
+        remove from the ban list.  The format of the <adding client> and
+        the <removing client> is of following format:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        The server MUST send the notify type SILC_NOTIFY_TYPE_BAN to its
+        primary router after adding to or removing from the ban list.
+        The wildcards MAY be used with this command.  If adding or removing
+        from than one clients then the lists are an comma (`,') separated.
+
+        If this command is executed without the ban arguments the command
+        merely replies with the current ban list.
+
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<ban list>]
+
+        This command replies with the <Channel ID> of the channel and
+        the current <ban list> of the channel if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   21   SILC_COMMAND_CLOSE
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used only by operator to close connection to a
+        remote site.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   22   SILC_COMMAND_SHUTDOWN
+
+        Max Arguments:  0
+            Arguments:  None
+
+        This command is used only by operator to shutdown the server.
+        All connections to the server will be closed and the server is
+        shutdown.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+
+
+   23   SILC_COMMAND_SILCOPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain router operator
+        privileges (also known as SILC operator) on the router.  Note
+        that router operator has privileges that supersedes the server
+        operator privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).  The public
+        key that router will use to verify the signature found in the
+        payload should be verified.  It is recommended that the public
+        key is saved locally in the router and router would not use
+        any public keys received during the SKE.
+
+        Difference between router operator and server operator is that
+        router operator is able to handle cell level properties while
+        server operator (even on router server) is able to handle only
+        local properties, such as, local connections and normal server
+        administration.  The router operator is also able to use the
+        SILC_COMMAND_KILL command.
+
+        After changing the mode server MUST send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   24   SILC_COMMAND_LEAVE
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used by client to leave a channel the client is
+        joined to. 
+
+        When leaving channel the server MUST send the notify type
+        SILC_NOTIFY_TYPE_LEAVE to its primary router and to the channel.
+        The channel key MUST also be re-generated when leaving the channel
+        and distribute it to all clients still currently on the channel.
+        The key MUST NOT be re-generated if the SILC_CMODE_PRIVKEY mode
+        is set.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+
+
+   25   SILC_COMMAND_USERS
+
+        Max Arguments:  2
+            Arguments:  (1) [<Channel ID>]  (2) [<channel name>]
+
+        This command is used to list user names currently on the requested
+        channel; either the argument <Channel ID> or the <channel name>. 
+        One of these arguments must be present.  The server MUST resolve
+        the user names and send a comma (`,') separated list of user names
+        on the channel.  Server or router MAY resolve the names by sending
+        SILC_COMMAND_WHOIS or SILC_COMMAND_IDENTIFY commands.
+
+        If the requested channel is a private or secret channel, this
+        command MUST NOT send the list of users, as private and secret
+        channels cannot be seen by outside.  In this case the returned
+        name list MAY include a indication that the server could not 
+        resolve the names of the users on the channel.  Also, in this case
+        Client ID's or client modes are not sent either.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <list count>      (4) <Client ID list>
+                        (5) <client mode list>
+
+        This command replies with the Channel ID of the requested channel
+        Client ID list of the users on the channel and list of their modes.
+        The Client ID list has Client ID's of all users in the list.  The 
+        <Client ID list> is formed by adding Client ID's one after another.
+        The <client mode list> is formed by adding client's user modes on
+        the channel one after another (4 bytes (32 bits) each).  The <list 
+        count> of length of 4 bytes (32 bits), tells the number of entries
+        in the lists.  Both lists MUST have equal number of entries.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+
+   26   SILC_COMMAND_GETKEY
+
+        Max Arguments:  1
+            Arguments:  (1) <ID Payload>
+
+        This command is used to fetch the public key of the client or
+        server indicated by the <ID Payload>.  The public key is fetched
+        from the server where to the client is connected.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>      (2) <ID Payload>
+                        (3) [<Public Key Payload>]
+
+        This command replies with the client's or server's ID and with
+        the <Public Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   27 - 199
+
+        Currently undefined commands.
+
+
+   200 - 254
+
+        These commands are reserved for private use and will not be defined
+        in this document.
+
+
+   255  SILC_COMMAND_MAX   
+
+        Reserved command.  This must not be sent.
+.in 3
+
+
+.ti 0
+2.3 SILC Command Status Types
+
+.ti 0
+2.3.1 SILC Command Status Payload
+
+Command Status Payload is sent in command reply messages to indicate
+the status of the command.  The payload is one of argument in the
+command thus this is the data area in Command Argument Payload described
+in [SILC2].  The payload is only 2 bytes of length.  The following diagram
+represents the Command Status Payload (field is always in MSB order).
+
+
+.in 21
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Status Message         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  SILC Command Status Payload
+
+
+.in 6
+o Status Message (2 bytes) - Indicates the status message.
+  All Status messages are described in the next section.
+.in 3
+
+
+.ti 0
+2.3.2 SILC Command Status List
+
+Command Status messages are returned in the command reply messages
+to indicate whether the command were executed without errors.  If error
+has occurred the status indicates which error occurred.  Status payload
+only sends numeric reply about the status.  Receiver of the payload must
+convert the numeric values into human readable error messages.  The
+list of status messages below has an example human readable error
+messages that client may display for the user.
+
+List of all defined command status messages following.
+
+.in 0
+   Generic status messages:
+
+   0    SILC_STATUS_OK
+
+        Ok status.  Everything went Ok.  The status payload maybe
+        safely ignored in this case.
+
+   1    SILC_STATUS_LIST_START
+
+        Start of the list.  There will be several command replies and
+        this reply is the start of the list.
+
+   2    SILC_STATUS_LIST_ITEM
+
+        Item in the list.  This is one of the item in the list but not the
+        first or last one.
+
+   3    SILC_STATUS_LIST_END
+
+        End of the list.  There were several command replies and this
+        reply is the last of the list.  There won't be other replies
+        belonging to this list after this one.
+
+   4 - 9
+
+        Currently undefined and has been reserved for the future.
+
+
+   Error status message:
+
+
+
+   10   SILC_STATUS_ERR_NO_SUCH_NICK
+
+        "No such nickname".  Requested nickname does not exist.
+
+   11   SILC_STATUS_ERR_NO_SUCH_CHANNEL
+
+        "No such channel".  Requested channel name does not exist.
+
+   12   SILC_STATUS_ERR_NO_SUCH_SERVER
+
+        "No such server".  Requested server name does not exist.
+
+   13   SILC_STATUS_ERR_TOO_MANY_TARGETS
+
+        "Duplicate recipients. No message delivered".  Message were
+        tried to be sent to recipient which has several occurrences in 
+        the recipient list.
+
+   14   SILC_STATUS_ERR_NO_RECIPIENT
+
+        "No recipient given".  Command required recipient which was
+        not provided.
+
+   15   SILC_STATUS_ERR_UNKNOWN_COMMAND
+
+        "Unknown command".  Command sent to server is unknown by the
+        server.
+
+   16   SILC_STATUS_ERR_WILDCARDS
+
+        "Wildcards cannot be used".  Wildcards were provided but they
+        weren't permitted.
+
+   17   SILC_STATUS_ERR_NO_CLIENT_ID
+
+        "No Client ID given".  Client ID were expected as command
+        parameter but were not found.
+
+   18   SILC_STATUS_ERR_NO_CHANNEL_ID
+
+        "No Channel ID given".  Channel ID were expected as command
+        parameter but were not found.
+
+   19   SILC_STATUS_ERR_NO_SERVER_ID
+
+        "No Serve ID given".  Server ID were expected as command
+        parameter but were not found.
+
+   20   SILC_STATUS_ERR_BAD_CLIENT_ID
+
+        "Bad Client ID".  Client ID provided were erroneous.
+
+   21   SILC_STATUS_ERR_BAD_CHANNEL_ID
+
+        "Bad Channel ID".  Channel ID provided were erroneous.
+
+   22   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+        "No such Client ID".  Client ID provided does not exist.
+
+   23   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+
+        "No such Channel ID".  Channel ID provided does not exist.
+
+   24   SILC_STATUS_ERR_NICKNAME_IN_USE
+
+        "Nickname already exists".  Nickname created could not be 
+        registered because number of same nicknames were already set to
+        maximum.  This is not expected to happen in real life but is
+        possible to occur.
+
+   25   SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+        "You are not on that channel".  The command were specified for
+        channel user is not currently on.
+
+   26   SILC_STATUS_ERR_USER_NOT_ON_CHANNEL
+
+        "They are not on channel".  The requested target client is not
+        on requested channel.
+
+   27   SILC_STATUS_ERR_USER_ON_CHANNEL
+
+        "User already on channel".  User were invited on channel they
+        already are on.
+
+   28   SILC_STATUS_ERR_NOT_REGISTERED
+
+        "You have not registered".  User executed command that requires
+        the client to be registered on the server before it may be
+        executed.
+
+   29   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+
+        "Not enough parameters".  Command requires more parameters
+        than provided.
+
+   30   SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+        "Too many parameters".  Too many parameters were provided
+        for the command.
+
+   31   SILC_STATUS_ERR_PERM_DENIED
+
+        "Permission denied".  Generic permission denied error status
+        to indicate disallowed access.
+
+   32   SILC_STATUS_ERR_BANNED_FROM_SERVER
+
+        "You are banned from this server".  The client tried to register
+        on server that has explicitly denied this host to connect.
+
+   33   SILC_STATUS_ERR_BAD_PASSWORD
+
+        "Cannot join channel. Incorrect password".  Password provided for 
+        channel were not accepted.
+
+   34   SILC_STATUS_ERR_CHANNEL_IS_FULL
+
+        "Cannot join channel. Channel is full".  The channel is full
+        and client cannot be joined to it.
+
+   35   SILC_STATUS_ERR_NOT_INVITED
+
+        "Cannot join channel. You have not been invited".  The channel
+        is invite only channel and client has not been invited.
+
+   36   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+
+        "Cannot join channel. You have been banned".  The client has
+        been banned from the channel.
+
+   37   SILC_STATUS_ERR_UNKNOWN_MODE
+
+        "Unknown mode".  Mode provided by the client were unknown to
+        the server.
+
+   38   SILC_STATUS_ERR_NOT_YOU
+
+        "Cannot change mode for other users".  User tried to change
+        someone else's mode.
+
+   39   SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+        "Permission denied. You are not channel operator".  Command may 
+        be executed only by channel operator.
+
+   40   SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
+
+        "Permission denied. You are not channel founder".  Command may 
+        be executed only by channel operator.
+
+   41   SILC_STATUS_ERR_NO_SERVER_PRIV
+
+        "Permission denied. You are not server operator".  Command may
+        be executed only by server operator.
+
+   42   SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+        "Permission denied. You are not SILC operator".  Command may be
+        executed only by router (SILC) operator.
+
+   43   SILC_STATUS_ERR_BAD_NICKNAME
+
+        "Bad nickname".  Nickname requested contained illegal characters
+        or were malformed.
+
+   44   SILC_STATUS_ERR_BAD_CHANNEL
+
+        "Bad channel name".  Channel requested contained illegal characters
+        or were malformed.
+
+   45   SILC_STATUS_ERR_AUTH_FAILED
+
+        "Authentication failed".  The authentication data sent as 
+        argument were wrong and thus authentication failed.
+
+   46   SILC_STATUS_ERR_UNKOWN_ALGORITHM
+
+        "The algorithm was not supported."  The server does not support the
+        requested algorithm.
+
+   47   SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+        "No such Server ID".  Server ID provided does not exist.
+
+.in 3
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires XXX
index e4be714352a498e70e1c1109fe4de750647d63ec..18d604b32e6890695d407114ce0c4b8fac886983 100644 (file)
@@ -7,42 +7,43 @@
 .ds LF Riikonen
 .ds RF FORMFEED[Page %]
 .ds CF
-.ds LH Internet Draft
-.ds RH 27 June 2000
-.ds CH Key Exchange and Authentication
+.ds LH INTERNET-DRAFT
+.ds RH 13 September 2000
+.ds CH
 .na
 .hy 0
 .in 0
 .nf
 Network Working Group                                      P. Riikonen
-Internet-Draft
-draft-riikonen-silc-ke-auth-00.txt                        27 June 2000
-Expires: 27 Jan 2001
+INTERNET-DRAFT
+draft-riikonen-silc-ke-auth-00.txt                   13 September 2000
+Expires: 13 May 2001
 
 .in 3
 
-.ce
+.ce 2
 SILC Key Exchange and Authentication Protocols
+<draft-riikonen-silc-ke-auth-00.txt>
 
 .ti 0
 Status of this Memo
 
-This document is an Internet-Draft.  Internet-Drafts are working
-documents of the Internet Engineering Task Force (IETF), its areas,
-and its working groups.  Note that other groups may also distribute
-working documents as Internet-Drafts.
+This document is an Internet-Draft and is in full conformance with
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are
+working documents of the Internet Engineering Task Force (IETF), its
+areas, and its working groups.  Note that other groups may also
+distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time.  It is inappropriate to use Internet-Drafts as reference
+material or to cite them other than as "work in progress."
 
-Internet-Drafts are draft documents valid for a maximum of six
-months and may be updated, replaced, or obsoleted by other 
-documents at any time. It is inappropriate to use Internet-Drafts  
-as reference material or to cite them other than as 
-``work in progress.''
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
 
-To learn the current status of any Internet-Draft, please check the
-``1id-abstracts.txt'' listing contained in the Internet-Drafts
-Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe),
-munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or
-ftp.isi.edu (US West Coast).
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html
 
 The distribution of this memo is unlimited.
 
@@ -241,6 +242,12 @@ not include spaces (` ').
 +                                                               +
 |                                                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Version String Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Version String                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |   Key Exchange Grp Length     |                               |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
 |                                                               |
@@ -307,38 +314,47 @@ o Flags (1 byte) - Indicates flags to be used in the key
        must not be set.
 
 o Payload Length (2 bytes) - Length of the entire Key Exchange
-  Start payload.
+  Start payload, not including any other field.
 
 o Cookie (16 bytes) - Cookie that uniforms this payload so
   that each of the party cannot determine the payload before
   hand.
 
+o Version String Length (2 bytes) - The length of the Version
+  String field, not including any other field.
+
+o Version String (variable length) - Indicates the version of
+  the sender of this payload.  Initiator sets this when sending
+  the payload and responder sets this when it replies by sending
+  this payload.  See [SILC1] for definition of the version
+  string format.
+
 o Key Exchange Grp Length (2 bytes) - The length of the
-  key exchange group list, including this field as well.
+  key exchange group list, not including any other field.
 
 o Key Exchange Group (variable length) - The list of
   key exchange groups.  See the section 2.1.2 SILC Key Exchange
   Groups for definitions of these groups.
 
 o PKCS Alg Length (2 bytes) - The length of the PKCS algorithms
-  list, including this field as well.
+  list, not including any other field.
 
 o PKCS Algorithms (variable length) - The list of PKCS 
   algorithms.
 
 o Encryption Alg Length (2 bytes) - The length of the encryption
-  algorithms list, including this field as well.
+  algorithms list, not including any other field.
 
 o Encryption Algorithms (variable length) - The list of
   encryption algorithms.
 
 o Hash Alg Length (2 bytes) - The length of the Hash algorithms
-  list, including this field as well.
+  list, not including any other field.
 
 o Hash Algorithms (variable length) - The list of Hash algorithms.
 
 o Compression Alg Length (2 bytes) - The length of the
-  compression algorithms list, including this field as well.
+  compression algorithms list, not including any other field.
 
 o Compression Algorithms (variable length) - The list of 
   compression algorithms.
@@ -401,9 +417,8 @@ Figure 2:  Key Exchange 1 Payload
 
 
 .in 6
-o Public Key Length (2 bytes) - The length of the public key
-  (or certificate), including this field and public key type
-  field as well.
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
 
 o Public Key Type (2 bytes) - The public key (or certificate) 
   type.  This field indicates the type of the public key in 
@@ -425,8 +440,8 @@ o Public Key Type (2 bytes) - The public key (or certificate)
   sending SILC_PACKET_FAILURE message.
 
 o Public Data Length (2 bytes) - The length of the public
-  data computed by the responder, including this field
-  as well.
+  data computed by the responder, not including any other
+  field.
 
 o Public Data (variable length) - The public data to be
   sent to the responder.  See section 2.2 Key Exchange 
@@ -479,9 +494,8 @@ Figure 3:  Key Exchange 2 Payload
 
 
 .in 6
-o Public Key Length (2 bytes) - The length of the public key
-  (or certificate), including this field and public key type
-  field as well.
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
 
 o Public Key Type (2 bytes) - The public key (or certificate) 
   type.  This field indicates the type of the public key in 
@@ -494,8 +508,8 @@ o Public Key of the host (variable length) - The public
   is indicated by previous Public Key Type field.
 
 o Public Data Length (2 bytes) - The length of the public
-  data computed by the responder, including this field
-  as well.
+  data computed by the responder, not including any other
+  field.
 
 o Public Data (variable length) - The public data computed
   by the responder.  See section 2.2 Key Exchange Procedure
@@ -503,7 +517,7 @@ o Public Data (variable length) - The public data computed
   value is binary encoded.
 
 o Signature Length (2 bytes) - The length of the signature,
-  including the length of this field as well.
+  not including any other field.
 
 o Signature Data (variable length) - The signature signed
   by the responder.  The receiver of this signature must
@@ -1036,3 +1050,6 @@ Kasarmikatu 11 A4
 Finland
 
 EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 13 May 2001 
+
diff --git a/doc/draft-riikonen-silc-ke-auth-01.nroff b/doc/draft-riikonen-silc-ke-auth-01.nroff
new file mode 100644 (file)
index 0000000..0adf9b8
--- /dev/null
@@ -0,0 +1,1099 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet-Draft
+.ds RH 6 October 2000
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-ke-auth-01.txt                      6 October 2000
+Expires: 6 Jun 2001
+
+.in 3
+
+.ce 2
+SILC Key Exchange and Authentication Protocols
+<draft-riikonen-silc-ke-auth-01.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are
+working documents of the Internet Engineering Task Force (IETF), its
+areas, and its working groups.  Note that other groups may also
+distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time.  It is inappropriate to use Internet-Drafts as reference
+material or to cite them other than as "work in progress."
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html
+
+The distribution of this memo is unlimited.
+
+
+.ti 0
+Abstract
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification internet-draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol [OAKLEY].
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, passphrase
+(pre-shared- secret) or public key (and certificate).
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+2 SILC Key Exchange Protocol ....................................  3
+  2.1 Key Exchange Payloads .....................................  3
+      2.1.1 Key Exchange Start Payload ..........................  4
+      2.1.2 Key Exchange 1 Payload ..............................  7
+      2.1.3 Key Exchange 2 Payload ..............................  9
+  2.2 Key Exchange Procedure .................................... 10
+  2.3 Processing the Key Material ............................... 12
+  2.4 SILC Key Exchange Groups .................................. 13
+      2.4.1 diffie-hellman-group1 ............................... 13
+      2.4.2 diffie-hellman-group2 ............................... 14
+  2.5 Key Exchange Status Types ................................. 14
+3 SILC Connection Authentication Protocol ....................... 16
+  3.1 Connection Auth Payload ................................... 17
+  3.2 Connection Authentication Types ........................... 18
+      3.2.1 Passphrase Authentication ........................... 18
+      3.2.2 Public Key Authentication ........................... 18
+  3.3 Connection Authentication Status Types .................... 19
+4 Security Considerations ....................................... 19
+5 References .................................................... 19
+6 Author's Address .............................................. 20
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  Key Exchange Start Payload
+Figure 2:  Key Exchange 1 Payload
+Figure 3:  Key Exchange 2 Payload
+Figure 4:  Connection Auth Payload
+
+
+.ti 0
+1 Introduction
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet-Draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol.
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, passphrase
+(pre-shared- secret) or public key (and certificate).
+
+The basis of secure SILC session requires strong and secure key exchange
+protocol and authentication.  The authentication protocol is entirely
+secured and no authentication data is ever sent in the network without
+encrypting and authenticating it first.  Thus, authentication protocol
+may be used only after the key exchange protocol has been successfully
+completed.
+
+This document refers constantly to other SILC protocol specification
+Internet Drafts that are a must read for those who wants to understand
+the function of these protocols.  The most important references are
+the Secure Internet Live Conferencing, Protocol Specification [SILC1]
+and SILC Packet Protocol [SILC2] Internet Drafts.
+
+The protocol is intended to be used with the SILC protocol thus it
+does not define own framework that could be used.  The framework is
+provided by the SILC protocol.
+
+
+.ti 0
+2 SILC Key Exchange Protocol
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key
+material used to secure the communication channel.  The protocol uses
+Diffie-Hellman key exchange algorithm and its functionality is derived
+from several key exchange protocols.  SKE uses best parts of the SSH2
+Key Exchange protocol, Station-To-Station (STS) protocol and the OAKLEY
+Key Determination protocol.  The protocol does not claim any conformance
+to any of these protocols, they were merely used as a reference when
+designing this protocol.
+
+The purpose of SILC Key Exchange protocol is to create session keys to
+be used in current SILC session.  The keys are valid only for some period
+of time (usually an hour) or at most until the session ends.  These keys
+are used to protect packets like commands, command replies and other
+communication between two entities.  If connection is server to server
+connection, the keys are used to protect all traffic between those
+servers.  In client connections usually all the packets are protected
+with this key except channel messages; channels has their own keys and 
+they are not exchanged with this protocol.
+
+The Diffie-Hellman implementation used in the SILC should be compliant
+to the PKCS #3.
+
+
+.ti 0
+2.1 Key Exchange Payloads
+
+During the key exchange procedure public data is sent between initiator
+and responder.  This data is later used in the key exchange procedure.
+There are several payloads used in the key exchange.  As for all SILC
+packets, SILC Packet Header, described in [SILC2], is at the start of all
+packets, the same is done with these payloads as well.  All fields in
+all payloads are always in MSB (most significant byte first) order.
+Following descriptions of these payloads.
+
+
+.ti 0
+2.1.1 Key Exchange Start Payload
+
+Key exchange between two entities always begins with a
+SILC_PACKET_KEY_EXCHANGE packet containing Key Exchange Start Payload.
+Initiator sends the Key Exchange Start Payload to the responder filled with
+all security properties it supports.  The responders then checks whether
+it supports the security properties.
+
+It then sends a Key Exchange Start Payload to the initiator filled with
+security properties it selected from the original payload.  The payload sent
+by responder must include only one chosen property per list.
+
+The Key Exchange Start Payload is used to tell connecting entities what
+security properties and algorithms should be used in the communication.
+If perfect forward secrecy (PFS) is not desired (PFS is undefined by
+default) Key Exchange Start Payload is sent only once per session, thus,
+for example, re-keying will not cause sending of a new payload.  If PFS
+is desired, re-keying will always cause new key exchange thus causes
+sending of a new Key Exchange Start Payload.
+
+When performing first key exchange this payload is never encrypted, as
+there are no existing keys to encrypt it with.  If performing re-keying
+(PFS was selected) this payload is encrypted with the existing key and
+encryption algorithm.
+
+A cookie is also sent in this payload.  A cookie is used to uniform the
+payload so that none of the key exchange parties can determine this
+payload before hand.  The cookie must be returned to the original sender
+by the responder.
+
+Following diagram represents the Key Exchange Start Payload.  The lists
+mentioned below are always comma (`,') separated and the list must
+not include spaces (` ').
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   RESERVED    |     Flags     |         Payload Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++                                                               +  
+|                                                               |
++                            Cookie                             +
+|                                                               |
++                                                               +
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Version String Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Version String                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Key Exchange Grp Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Key Exchange Groups                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        PKCS Alg Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         PKCS Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Encryption Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Encryption Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Hash Alg Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Hash Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         HMAC Length           |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                             HMACs                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|    Compression Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                     Compression Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 1:  Key Exchange Start Payload
+
+
+
+.in 6
+o RESERVED (1 byte) - Reserved field.  Sender fills this with
+  zeroes (0).
+
+o Flags (1 byte) - Indicates flags to be used in the key
+  exchange.  Several flags can be set at once by ORing the
+  flags together.  Following flags are reserved for this field.
+
+     No flags              0x00
+
+       In this case the field is ignored.
+
+     No Reply              0x01
+
+       If set the receiver of the payload does not reply to 
+       the packet.
+
+     PFS                   0x02
+
+       Perfect Forward Secrecy (PFS) to be used in the
+       key exchange protocol.  If not set, re-keying
+       is performed using the old key.  When PFS is used, 
+       re-keying and creating new keys for any particular 
+       purpose will cause new key exchange.
+
+       Rest of the flags are reserved for the future and
+       must not be set.
+
+o Payload Length (2 bytes) - Length of the entire Key Exchange
+  Start payload, not including any other field.
+
+o Cookie (16 bytes) - Cookie that uniforms this payload so
+  that each of the party cannot determine the payload before
+  hand.
+
+o Version String Length (2 bytes) - The length of the Version
+  String field, not including any other field.
+
+o Version String (variable length) - Indicates the version of
+  the sender of this payload.  Initiator sets this when sending
+  the payload and responder sets this when it replies by sending
+  this payload.  See [SILC1] for definition of the version
+  string format.
+
+o Key Exchange Grp Length (2 bytes) - The length of the
+  key exchange group list, not including any other field.
+
+o Key Exchange Group (variable length) - The list of
+  key exchange groups.  See the section 2.1.2 SILC Key Exchange
+  Groups for definitions of these groups.
+
+o PKCS Alg Length (2 bytes) - The length of the PKCS algorithms
+  list, not including any other field.
+
+o PKCS Algorithms (variable length) - The list of PKCS 
+  algorithms.
+
+o Encryption Alg Length (2 bytes) - The length of the encryption
+  algorithms list, not including any other field.
+
+o Encryption Algorithms (variable length) - The list of
+  encryption algorithms.
+
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
+  list, not including any other field.
+
+o Hash Algorithms (variable length) - The list of Hash
+  algorithms.  The hash algorithms are mainly used in the
+  SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+  including any other field.
+
+o HMACs (variable length) - The list of HMACs.  The HMAC's
+  are used to compute the Message Authentication Codes (MAC)
+  of the SILC packets.
+
+o Compression Alg Length (2 bytes) - The length of the
+  compression algorithms list, not including any other field.
+
+o Compression Algorithms (variable length) - The list of 
+  compression algorithms.
+.in 3
+
+
+.ti 0
+2.1.2 Key Exchange 1 Payload
+
+Key Exchange 1 Payload is used to deliver computed public data from 
+initiator to responder.  This data is used to compute the shared secret,
+later by all parties.  Key Exchange 1 Payload is only sent after the 
+SILC_PACKET_KEY_EXCHANGE packet and the Key Exchange Start Payload has
+been processed by all the parties.
+
+This payload sends the initiator's public key to the responder.  Responder
+may need the public key in which case it should be checked to be trusted
+by the responder.
+The payload may only be sent with SILC_PACKET_KEY_EXCHANGE_1 packet.
+It must not be sent in any other packet type.  Following diagram 
+represent the Key Exchange 1 Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the Host (or certificate)            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Public Data Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                 Public Data (e = g ^ x mod p)                 ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  Key Exchange 1 Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  Following types are defined:
+
+     1    SILC style public key (mandatory)
+     2    SSH2 style public key (optional)
+     3    X.509 Version 3 certificate (optional)
+     4    OpenPGP certificate (optional)
+     5    SPKI certificate (optional)
+
+  The only required type to support is type number 1.  See 
+  [SILC1] for the SILC public key specification.  See
+  SSH public key specification in [SSH-TRANS].  See X.509v3
+  certificate specification in [PKIX-Part1].  See OpenPGP
+  certificate specification in [PGP].  See SPKI certificate
+  specification in [SPKI].  If this field includes zero (0)
+  or unsupported type number the protocol must be aborted
+  sending SILC_PACKET_FAILURE message and the connection should
+  be closed immediately.
+
+o Public Data Length (2 bytes) - The length of the public
+  data computed by the responder, not including any other
+  field.
+
+o Public Data (variable length) - The public data to be
+  sent to the responder.  See section 2.2 Key Exchange 
+  Procedure for detailed description how this field is
+  computed.  This value is binary encoded.
+.in 3
+
+
+.ti 0
+2.1.3 Key Exchange 2 Payload
+
+Key Exchange 2 Payload is used to deliver public key, computed public
+data and signature from responder to initiator.  Initiator uses these
+public parts of the key exchange protocol to compute the shared secret.
+
+The payload may only be sent with SILC_PACKET_KEY_EXCHANGE_2 packet.
+It must not be sent in any other packet type.  Following diagram 
+represent the Key Exchange 2 Payload.
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the Host (or certificate)            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Data Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                 Public Data (f = g ^ y mod p)                 ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Signature Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Signature Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 3:  Key Exchange 2 Payload
+
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  See previous sections for defined public key
+  types.
+
+o Public Key of the host (variable length) - The public
+  key of the sender (or its certificate).  This is verified
+  by the receiver of the packet.  The type of this field
+  is indicated by previous Public Key Type field.
+
+o Public Data Length (2 bytes) - The length of the public
+  data computed by the responder, not including any other
+  field.
+
+o Public Data (variable length) - The public data computed
+  by the responder.  See section 2.2 Key Exchange Procedure
+  for detailed description how this field is computed.  This
+  value is binary encoded.
+
+o Signature Length (2 bytes) - The length of the signature,
+  not including any other field.
+
+o Signature Data (variable length) - The signature signed
+  by the responder.  The receiver of this signature must
+  verify it.  The verification is done using the public
+  key received in this same payload.  See section 2.2
+  Key Exchange Procedure for detailed description how
+  to produce the signature.
+
+
+.ti 0
+2.2 Key Exchange Procedure
+
+The key exchange begins by sending SILC_PACKET_KEY_EXCHANGE packet with
+Key Exchange Start Payload to select the security properties to be used
+in the key exchange and later in the  communication.
+
+After Key Exchange Start Payload has been processed by both of the
+parties the protocol proceeds as follows:
+
+
+Setup:  p is a large and public safe prime.  This is one of the
+        Diffie Hellman groups.  q is order of subgroup (largest
+        prime factor of p).  g is a generator and is defined
+        along with the Diffie Hellman group.
+
+    1.  Initiator generates a random number x, where 1 < x < q, 
+        and computes e = g ^ x mod p.  The result e is then 
+        encoded into Key Exchange 1 Payload and sent
+        to the responder.
+
+
+    2.  Responder generates a random number y, where 1 < y < q,
+        and computes f = g ^ y mod p.  It then computes the
+        shared secret KEY = e ^ y mod p, and, a hash value 
+        HASH = hash(Key Exchange Start Payload data | Host public 
+        key (or certificate) | e | f | KEY).  It then signs
+        the HASH value with its private key resulting a signature
+        SIGN.  
+
+        It then encodes its public key (or certificate), f and 
+        SIGN into Key Exchange 2 Payload and sends it to the 
+        initiator.
+
+
+    3.  Initiator verifies that the public key provided in
+        the payload is authentic, or if certificates are used
+        it verifies the certificate.  Initiator may accept
+        the public key without verifying it, however, doing
+        so may result to insecure key exchange (accepting the
+        public key without verifying may be desirable for 
+        practical reasons on many environments.  For long term
+        use this is never desirable, in which case certificates
+        would be the preferred method to use).
+
+        Initiator then computes the shared secret KEY = 
+        f ^ x mod p, and, a hash value HASH in the same way as
+        responder did in phase 2.  It then verifies the 
+        signature SIGN from the payload with the hash value
+        HASH using the received public key.
+
+
+If any of these phases is to fail SILC_PACKET_FAILURE is sent to
+indicate that the key exchange protocol has failed, and the connection
+should be closed immediately.  Any other packets must not be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
+
+The result of this protocol is a shared secret key material KEY and
+a hash value HASH.  The key material itself is not fit to be used as 
+a key, it needs to be processed further to derive the actual keys to be
+used.  The key material is also used to produce other security parameters
+later used in the communication.  See section 2.3 Processing the Key
+Material for detailed description how to process the key material.
+
+After the keys are processed the protocol is ended by sending the
+SILC_PACKET_SUCCESS packet.  Both entities send this packet to 
+each other.  After this both parties will start using the new keys.
+
+
+
+
+.ti 0
+2.3 Processing the Key Material
+
+Key Exchange protocol produces secret shared key material KEY.  This
+key material is used to derive the actual keys used in the encryption
+of the communication channel.  The key material is also used to derive
+other security parameters used in the communication.  Key Exchange
+protocol produces a hash value HASH as well.  This is used in the key
+deriving process as a session identifier.
+
+Keys are derived from the key material as follows:
+
+.in 6
+Sending Initial Vector (IV)     = hash(0 | KEY | HASH)
+Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
+Sending Encryption Key          = hash(2 | KEY | HASH)
+Receiving Encryption Key        = hash(3 | KEY | HASH)
+HMAC Key                        = hash(4 | KEY | HASH)
+.in 3
+
+
+The Initial Vector (IV) is used in the encryption when doing for
+example CBC mode.  As many bytes as needed are taken from the start of
+the hash output for IV.  Sending IV is for sending key and receiving IV
+is for receiving key.  For receiving party, the receiving IV is actually
+sender's sending IV, and, the sending IV is actually sender's receiving
+IV.  Initiator uses IV's as they are (sending IV for sending and
+receiving IV for receiving).
+
+The Encryption Keys are derived as well from the hash().  If the hash()
+output is too short for the encryption algorithm more key material is
+produced in following manner:
+
+.in 6
+K1 = hash(2 | KEY | HASH)
+K2 = hash(KEY | K1)
+K3 = hash(KEY | K1 | K2)  ...
+
+Sending Encryption Key = K1 | K2 | K3 ...
+
+
+K1 = hash(3 | KEY | HASH)
+K2 = hash(KEY | K1)
+K3 = hash(KEY | K1 | K2)  ...
+
+Receiving Encryption Key = K1 | K2 | K3 ...
+.in 3
+
+
+The key is distributed by hashing the previous hash with the original
+key material.  The final key is a concatenation of the hash values.
+For Receiving Encryption Key the procedure is equivalent.  Sending key
+is used only for encrypting data to be sent.  The receiving key is used
+only to decrypt received data.  For receiving party, the receive key is
+actually sender's sending key, and, the sending key is actually sender's
+receiving key.  Initiator uses generated keys as they are (sending key
+for sending and receiving key for sending).
+
+The HMAC key is used to create MAC values to packets in the communication
+channel.  As many bytes as needed are taken from the start of the hash
+output.
+
+These procedures are performed by all parties of the key exchange
+protocol.  This must be done before the protocol has been ended by
+sending the SILC_PACKET_SUCCESS packet.
+
+
+.ti 0
+2.4 SILC Key Exchange Groups
+
+Following groups may be used in the SILC Key Exchange protocol.  The 
+first group diffie-hellman-group1 is mandatory, other groups maybe 
+negotiated to be used in the connection with Key Exchange Start Payload
+and SILC_PACKET_KEY_EXCHANGE packet.  However, the first group must be
+proposed in the Key Exchange Start Payload regardless of any other
+requested group (however, it does not have to be the first on the list).
+
+
+.ti 0
+2.4.1 diffie-hellman-group1
+
+The length of this group is 1024 bits.  This is mandatory group.
+The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+
+Its decimal value is
+
+.in 6
+179769313486231590770839156793787453197860296048756011706444
+423684197180216158519368947833795864925541502180565485980503
+646440548199239100050792877003355816639229553136239076508735
+759914822574862575007425302077447712589550957937778424442426
+617334727629299387668709205606050270810842907692932019128194
+467627007
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
+FFFFFFFF FFFFFFFF
+.in 3
+
+
+The generator used with this prime is g = 2. The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.4.2 diffie-hellman-group2
+
+The length of this group is 1536 bits.  This is optional group.
+The prime is 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }.
+
+Its decimal value is
+
+.in 6
+241031242692103258855207602219756607485695054850245994265411
+694195810883168261222889009385826134161467322714147790401219
+650364895705058263194273070680500922306273474534107340669624
+601458936165977404102716924945320037872943417032584377865919
+814376319377685986952408894019557734611984354530154704374720
+774996976375008430892633929555996888245787241299381012913029
+459299994792636526405928464720973038494721168143446471443848
+8520940127459844288859336526896320919633919
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
+.in 3
+
+The generator used with this prime is g = 2. The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.5 Key Exchange Status Types
+
+This section defines all key exchange protocol status types that may be
+returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets to
+indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_SKE_STATUS_OK type must be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes). Following status types are 
+defined:
+
+.in 6
+0   SILC_SKE_STATUS_OK
+
+    Protocol were executed successfully.
+
+
+1   SILC_SKE_STATUS_ERROR
+
+    Unknown error occured.  No specific error type is defined.
+
+
+2   SILC_SKE_STATUS_BAD_PAYLOAD
+
+    Provided KE payload were malformed or included bad fields.
+
+
+3   SILC_SKE_STATUS_UNSUPPORTED_GROUP
+
+    None of the provided groups were supported.
+
+
+4   SILC_SKE_STATUS_UNSUPPORTED_CIPHER
+
+    None of the provided ciphers were supported.
+
+
+5   SILC_SKE_STATUS_UNSUPPORTED_PKCS
+
+    None of the provided public key algorithms were supported.
+
+
+6   SILC_SKE_STATUS_UNSUPPORTED_HASH_FUNCTION
+
+    None of the provided hash functions were supported.
+
+
+7   SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+    None of the provided HMACs were supported.
+
+
+8   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+
+    Provided public key type is not supported.
+
+
+9   SILC_SKE_STATUS_INCORRECT_SIGNATURE
+
+    Provided signature was incorrect.
+
+
+10   SILC_SKE_STATUS_BAD_VERSION
+
+    Provided version string was not acceptable.
+.in 3
+
+
+
+
+
+.ti 0
+3 SILC Connection Authentication Protocol
+
+Purpose of Connection Authentication protocol is to authenticate the
+connecting party with server.  Usually connecting party is client but
+server may connect to server as well.  Its other purpose is to provide
+information for the server about which type of connection this is.
+The type defines whether this is client, server or router connection.
+Server uses this information to create the ID for the connection.  After
+the authentication protocol has been successfully completed 
+SILC_PACKET_NEW_ID must be sent to the connecting party by the server.
+See section New ID Payload in [SILC2] for detailed description for this
+packet's payload.
+
+Server must verify the authentication data received and if it is to fail
+the authentication must be failed by sending SILC_PACKET_FAILURE packet.
+If everything checks out fine the protocol is ended by server by sending
+SILC_PACKET_SUCCESS packet.
+
+The protocol is executed after the SILC Key Exchange protocol.  It must
+not be executed in any other time.  As it is performed after key exchange
+protocol all traffic in the connection authentication protocol is
+encrypted with the exchanged keys.
+
+The protocol is started by the connecting party by sending
+SILC_PACKET_CONNECTION_AUTH packet with Connection Auth Payload,
+described in the next section.  This payload must include the
+authentication data.  Authentication data is set according
+authentication method that must be known by both parties.  If connecting
+party does not know what is the mandatory authentication method it may
+request it from the server by sending SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  This packet is not part of this protocol and is described in
+section Connection Auth Request Payload in [SILC2].  However, if
+connecting party already knows the mandatory authentication method
+sending the request is not necessary.
+
+See [SILC1] and section Connection Auth Request Payload in [SILC2] also
+for the list of different authentication methods.  Authentication method
+may also be NONE, in which case the server does not require
+authentication at all.  However, in this case the protocol still must be
+executed; the authentication data just is empty indicating no
+authentication is required.
+
+If authentication method is passphrase the authentication data is
+plaintext passphrase.  As the payload is entirely encrypted it is safe
+to have plaintext passphrase.  3.2.1 Passphrase Authentication for
+more information.
+
+
+If authentication method is public key authentication the authentication
+data is signature of the hash value HASH plus Key Exchange Start Payload,
+established by the SILC Key Exchange protocol.  This signature must then
+be verified by the server.  See section 3.2.2 Public Key Authentication
+for more information.
+
+The connecting party of this protocol must wait after successful execution
+of this protocol for the SILC_PACKET_NEW_ID packet where it will receive
+the ID it will be using in the SILC network.  Connecting party cannot
+start normal SILC session (sending messages or commands) until it has
+received its ID.  The ID's are always created by the server except
+for server to server connection where servers create their own ID's.
+
+
+
+.ti 0
+3.1 Connection Auth Payload
+
+Client sends this payload to authenticate itself to the server.  Server
+connecting to another server also sends this payload.  Server receiving
+this payload must verify all the data in it and if something is to fail
+the authentication must be failed by sending SILC_PACKET_FAILURE packet.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH packet.
+It must not be sent in any other packet type.  Following diagram 
+represent the Connection Auth Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |        Connection Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                     Authentication Data                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 4:  Connection Auth Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire Connection 
+  Auth Payload.
+
+o Connection Type (2 bytes) - Indicates the type of the 
+  connection.  See section Connection Auth Request Payload
+  in [SILC2] for the list of connection types.  This field must 
+  include valid connection type or the packet must be discarded 
+  and authentication must be failed. 
+
+o Authentication Data (variable length) - The actual 
+  authentication data.  Contents of this depends on the 
+  authentication method known by both parties.  If no
+  authentication is required this field does not exist.
+.in 3
+
+
+.ti 0
+3.2 Connection Authentication Types
+
+SILC supports two authentication types to be used in the connection
+authentication protocol; passphrase or public key based authentication.
+Following sections defines the authentication methods.  See [SILC2]
+for defined numerical authentication method types.
+
+
+.ti 0
+3.2.1 Passphrase Authentication
+
+Passphrase authentication or pre-shared-key base authentication is 
+simply an authentication where the party that wants to authenticate 
+itself to the other end sends the passphrase that is required by
+the other end, for example server.
+
+If the passphrase matches with the one in the server's end the
+authentication is successful.  Otherwise SILC_PACKET_FAILURE must be
+sent to the sender and the protocol execution fails.
+
+This is required authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.2.2 Public Key Authentication
+
+Public key authentication may be used if passphrase based authentication
+is not desired.  The public key authentication works by sending a
+signature as authentication data to the other end, say, server.  The
+server must then verify the signature by the public key of the sender,
+which the server has received earlier in SKE protocol.
+
+The signature is computed using the private key of the sender by signing
+the HASH value provided by the SKE protocol previously, and the Key
+Exchange Start Payload from SKE protocol that was sent to the server.
+The server must verify the data, thus it must keep the HASH and the
+Key Exchange Start Payload saved during SKE and authentication protocols.
+
+If the verified signature matches the sent signature, the authentication
+were successful and SILC_PACKET_SUCCESS is sent.  If it failed the protocol
+execution is stopped and SILC_PACKET_FAILURE is sent.
+
+This is required authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.3 Connection Authentication Status Types
+
+This section defines all connection authentication status types that
+may be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_AUTH_STATUS_OK type must be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes). Following status types are 
+defined:
+
+0   SILC_AUTH_OK
+
+    Protocol was executed successfully.
+
+
+1   SILC_AUTH_FAILED
+
+    Authentication failed.
+
+
+.ti 0
+4 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+5 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, June 2000.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             June 2000.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+
+.ti 0
+6 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 6 Jun 2001 
+
diff --git a/doc/draft-riikonen-silc-ke-auth-02.nroff b/doc/draft-riikonen-silc-ke-auth-02.nroff
new file mode 100644 (file)
index 0000000..3751db0
--- /dev/null
@@ -0,0 +1,1087 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet-Draft
+.ds RH 25 April 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-ke-auth-02.txt                       25 April 2001
+Expires: 25 October 2001
+
+.in 3
+
+.ce 2
+SILC Key Exchange and Authentication Protocols
+<draft-riikonen-silc-ke-auth-02.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are
+working documents of the Internet Engineering Task Force (IETF), its
+areas, and its working groups.  Note that other groups may also
+distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time.  It is inappropriate to use Internet-Drafts as reference
+material or to cite them other than as "work in progress."
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html
+
+The distribution of this memo is unlimited.
+
+
+.ti 0
+Abstract
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification internet-draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol [OAKLEY].
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, passphrase
+(pre-shared-secret) or public key (and certificate).
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  3
+2 SILC Key Exchange Protocol ....................................  3
+  2.1 Key Exchange Payloads .....................................  4
+      2.1.1 Key Exchange Start Payload ..........................  4
+      2.1.2 Key Exchange Payload ................................  8
+  2.2 Key Exchange Procedure .................................... 10
+  2.3 Processing the Key Material ............................... 12
+  2.4 SILC Key Exchange Groups .................................. 13
+      2.4.1 diffie-hellman-group1 ............................... 14
+      2.4.2 diffie-hellman-group2 ............................... 14
+  2.5 Key Exchange Status Types ................................. 15
+3 SILC Connection Authentication Protocol ....................... 16
+  3.1 Connection Auth Payload ................................... 17
+  3.2 Connection Authentication Types ........................... 18
+      3.2.1 Passphrase Authentication ........................... 18
+      3.2.2 Public Key Authentication ........................... 19
+  3.3 Connection Authentication Status Types .................... 19
+4 Security Considerations ....................................... 20
+5 References .................................................... 20
+6 Author's Address .............................................. 21
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  Key Exchange Start Payload
+Figure 2:  Key Exchange Payload
+Figure 3:  Connection Auth Payload
+
+
+.ti 0
+1 Introduction
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet-Draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol.
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, pass phrase
+(pre-shared- secret) or public key (and certificate).
+
+The basis of secure SILC session requires strong and secure key exchange
+protocol and authentication.  The authentication protocol is entirely
+secured and no authentication data is ever sent in the network without
+encrypting and authenticating it first.  Thus, authentication protocol
+may be used only after the key exchange protocol has been successfully
+completed.
+
+This document refers constantly to other SILC protocol specification
+Internet Drafts that are a must read for those who wants to understand
+the function of these protocols.  The most important references are
+the Secure Internet Live Conferencing, Protocol Specification [SILC1]
+and the SILC Packet Protocol [SILC2] Internet Drafts.
+
+The protocol is intended to be used with the SILC protocol thus it
+does not define own framework that could be used.  The framework is
+provided by the SILC protocol.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Key Exchange Protocol
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key
+material used to secure the communication channel.  The protocol uses
+Diffie-Hellman key exchange algorithm and its functionality is derived
+from several key exchange protocols.  SKE uses best parts of the SSH2
+Key Exchange protocol, Station-To-Station (STS) protocol and the OAKLEY
+Key Determination protocol.  The protocol does not claim any conformance
+to any of these protocols, they were merely used as a reference when
+designing this protocol.
+
+The purpose of SILC Key Exchange protocol is to create session keys to
+be used in current SILC session.  The keys are valid only for some period
+of time (usually an hour) or at most until the session ends.  These keys
+are used to protect packets like commands, command replies and other
+communication between two entities.  If connection is server to router
+connection, the keys are used to protect all traffic between those
+servers.  In client connections usually all the packets are protected
+with this key except channel messages; channels has their own keys and 
+they are not exchanged with this protocol.
+
+The Diffie-Hellman implementation used in the SILC SHOULD be compliant
+to the PKCS #3.
+
+
+.ti 0
+2.1 Key Exchange Payloads
+
+During the key exchange procedure public data is sent between initiator
+and responder.  This data is later used in the key exchange procedure.
+There are several payloads used in the key exchange.  As for all SILC
+packets, SILC Packet Header, described in [SILC2], is at the start of
+all packets. The same is done with these payloads as well.  All the
+fields in the payloads are always in MSB (most significant byte first)
+order.  Following descriptions of these payloads.
+
+
+.ti 0
+2.1.1 Key Exchange Start Payload
+
+The key exchange between two entities MUST be started by sending the
+SILC_PACKET_KEY_EXCHANGE packet containing Key Exchange Start Payload.
+Initiator sends the Key Exchange Start Payload to the responder filled
+with all security properties it supports.  The responder then checks
+whether it supports the security properties.
+
+It then sends a Key Exchange Start Payload to the initiator filled with
+security properties it selected from the original payload.  The payload
+sent by responder MUST include only one chosen property per list.
+
+The Key Exchange Start Payload is used to tell connecting entities what
+security properties and algorithms should be used in the communication.
+The Key Exchange Start Payload is sent only once per session.  Even if
+the PFS (Perfect Forward Secrecy) flag is set the Key Exchange Start
+Payload is not re-sent.  When PFS is desired the Key Exchange Payloads
+are sent to negotiate new key material.  The procedure is equivalent to
+the very first negotiation except that the Key Exchange Start Payload
+is not sent.
+
+As this payload is used only with the very first key exchange the payload
+is never encrypted, as there are no keys to encrypt it with.
+
+A cookie is also sent in this payload.  A cookie is used to randomize the
+payload so that none of the key exchange parties can determine this
+payload before the key exchange procedure starts.  The cookie MUST be
+returned to the original sender by the responder.
+
+Following diagram represents the Key Exchange Start Payload.  The lists
+mentioned below are always comma (`,') separated and the list MUST
+not include spaces (` ').
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   RESERVED    |     Flags     |         Payload Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++                                                               +  
+|                                                               |
++                            Cookie                             +
+|                                                               |
++                                                               +
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Version String Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Version String                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Key Exchange Grp Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Key Exchange Groups                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        PKCS Alg Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         PKCS Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Encryption Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Encryption Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Hash Alg Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Hash Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         HMAC Length           |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                             HMACs                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|    Compression Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                     Compression Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 1:  Key Exchange Start Payload
+
+
+
+.in 6
+o RESERVED (1 byte) - Reserved field.  Sender fills this with
+  zero (0) value.
+
+o Flags (1 byte) - Indicates flags to be used in the key
+  exchange.  Several flags can be set at once by ORing the
+  flags together.  The following flags are reserved for this
+  field:
+
+     No flags                 0x00
+
+       In this case the field is ignored.
+
+     No Reply                 0x01
+
+       If set the receiver of the payload does not reply to 
+       the packet.
+
+     PFS                      0x02
+
+       Perfect Forward Secrecy (PFS) to be used in the
+       key exchange protocol.  If not set, re-keying
+       is performed using the old key.  See the [SILC1]
+       for more information on this issue.  When PFS is
+       used, re-keying and creating new keys for any
+       particular purpose MUST cause new key exchange.
+       In this key exchange only the Key Exchange Payload
+       is sent and the Key Exchange Start Payload MUST
+       NOT be sent.  When doing PFS the Key Exchange
+       Payloads are encrypted with the old keys.  With
+       the PFS, the Mutual Authentication flag MUST be
+       ignored.
+
+     Mutual Authentication    0x04
+
+       Both of the parties will perform authentication
+       by providing signed data for the other party to
+       verify.  By default, only responder will provide
+       the signature data.  If this is set then the
+       initiator must also provide it.  Initiator MAY
+       set this but also responder MAY set this even if
+       initiator did not set it.
+
+     Rest of the flags are reserved for the future and
+     MUST NOT be set.
+
+o Payload Length (2 bytes) - Length of the entire Key Exchange
+  Start payload, not including any other field.
+
+o Cookie (16 bytes) - Cookie that randomize this payload so
+  that each of the party cannot determine the payload before
+  hand.
+
+o Version String Length (2 bytes) - The length of the Version
+  String field, not including any other field.
+
+o Version String (variable length) - Indicates the version of
+  the sender of this payload.  Initiator sets this when sending
+  the payload and responder sets this when it replies by sending
+  this payload.  See [SILC1] for definition of the version
+  string format.
+
+o Key Exchange Grp Length (2 bytes) - The length of the
+  key exchange group list, not including any other field.
+
+o Key Exchange Group (variable length) - The list of
+  key exchange groups.  See the section 2.4 SILC Key Exchange
+  Groups for definitions of these groups.
+
+o PKCS Alg Length (2 bytes) - The length of the PKCS algorithms
+  list, not including any other field.
+
+o PKCS Algorithms (variable length) - The list of PKCS 
+  algorithms.
+
+o Encryption Alg Length (2 bytes) - The length of the encryption
+  algorithms list, not including any other field.
+
+o Encryption Algorithms (variable length) - The list of
+  encryption algorithms.
+
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
+  list, not including any other field.
+
+o Hash Algorithms (variable length) - The list of Hash
+  algorithms.  The hash algorithms are mainly used in the
+  SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+  including any other field.
+
+o HMACs (variable length) - The list of HMACs.  The HMAC's
+  are used to compute the Message Authentication Codes (MAC)
+  of the SILC packets.
+
+o Compression Alg Length (2 bytes) - The length of the
+  compression algorithms list, not including any other field.
+
+o Compression Algorithms (variable length) - The list of 
+  compression algorithms.
+.in 3
+
+
+.ti 0
+2.1.2 Key Exchange Payload
+
+Key Exchange payload is used to deliver the public key (or certificate),
+the computed Diffie-Hellman public value and possibly signature data
+from one party to the other.  When initiator is using this payload
+and the Mutual Authentication flag is not set then the initiator MUST
+NOT provide the signature data.  If the flag is set then the initiator
+MUST provide the signature data so that the responder can verify it.
+
+The Mutual Authentication flag is usually used only if a separate
+authentication protocol will not be executed for the initiator of the
+protocol.  This is case for example when the SKE is performed between
+two SILC clients.  In normal case, where client is connecting to the
+server, or server is connecting to the router the Mutual Authentication
+flag is not necessary.
+
+When performing re-key with PFS selected this is the only payload that
+is sent in the SKE protocol.  The Key Exchange Start Payload MUST NOT
+be sent at all.  However, this payload does not have all the fields
+present.  In the re-key with PFS the public key and a possible signature
+data SHOULD NOT be present.  If they are present they MUST be ignored.
+The only field that is present is the Public Data that is used to create
+the new key material.  In the re-key the Mutual Authentication flag MUST
+also be ignored.
+
+This payload is sent inside SILC_PACKET_KEY_EXCHANGE_1 and inside
+SILC_PACKET_KEY_EXCHANGE_2 packet types.  The initiator uses the 
+SILC_PACKET_KEY_EXCHANGE_1 and the responder the latter.
+
+The following diagram represent the Key Exchange Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Data Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Public Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Signature Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Signature Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  Key Exchange Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  Following types are defined:
+
+     1    SILC style public key (mandatory)
+     2    SSH2 style public key (optional)
+     3    X.509 Version 3 certificate (optional)
+     4    OpenPGP certificate (optional)
+     5    SPKI certificate (optional)
+
+  The only required type to support is type number 1.  See 
+  [SILC1] for the SILC public key specification.  See
+  SSH public key specification in [SSH-TRANS].  See X.509v3
+  certificate specification in [PKIX-Part1].  See OpenPGP
+  certificate specification in [PGP].  See SPKI certificate
+  specification in [SPKI].  If this field includes zero (0)
+  or unsupported type number the protocol MUST be aborted
+  sending SILC_PACKET_FAILURE message and the connection SHOULD
+  be closed immediately.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+
+o Public Data Length (2 bytes) - The length of the Public Data
+  field, not including any other field.
+
+o Public Data (variable length) - The public data to be
+  sent to the receiver.  See section 2.2 Key Exchange 
+  Procedure for detailed description how this field is
+  computed.  This value is binary encoded.
+
+o Signature Length (2 bytes) - The length of the signature,
+  not including any other field.
+
+o Signature Data (variable length) - The signature signed
+  by the sender.  The receiver of this signature MUST
+  verify it.  The verification is done using the sender's
+  public key.  See section 2.2 Key Exchange Procedure for
+  detailed description how to produce the signature.  If
+  the Mutual Authentication flag is not set then initiator
+  MUST NOT provide this field and the Signature Length field
+  MUST be set to zero (0) value.  If the flag is set then
+  also the initiator MUST provide this field.  The responder
+  MUST always provide this field.
+.in 3
+
+
+.ti 0
+2.2 Key Exchange Procedure
+
+The key exchange begins by sending SILC_PACKET_KEY_EXCHANGE packet with
+Key Exchange Start Payload to select the security properties to be used
+in the key exchange and later in the communication.
+
+After Key Exchange Start Payload has been processed by both of the
+parties the protocol proceeds as follows:
+
+
+Setup:  p is a large and public safe prime.  This is one of the
+        Diffie Hellman groups.  q is order of subgroup (largest
+        prime factor of p).  g is a generator and is defined
+        along with the Diffie Hellman group.
+
+    1.  Initiator generates a random number x, where 1 < x < q, 
+        and computes e = g ^ x mod p.  The result e is then 
+        encoded into Key Exchange Payload and sent to the
+        responder.
+
+        If the Mutual Authentication flag is set then initiator
+        MUST also produce signature data SIGN_i which the responder
+        will verify.  The initiator MUST compute a hash value
+        HASH_i = hash(Key Exchange Start Payload | public key
+        (or certificate) | e).  It then signs the HASH_i value with
+        its private key resulting a signature SIGN_i.
+
+    2.  Responder generates a random number y, where 1 < y < q,
+        and computes f = g ^ y mod p.  It then computes the
+        shared secret KEY = e ^ y mod p, and, a hash value 
+        HASH = hash(Key Exchange Start Payload data | public 
+        key (or certificate) | e | f | KEY).  It then signs
+        the HASH value with its private key resulting a signature
+        SIGN.  
+
+        It then encodes its public key (or certificate), f and 
+        SIGN into Key Exchange Payload and sends it to the 
+        initiator.
+
+        If the Mutual Authentication flag is set then the responder
+        SHOULD verify that the public key provided in the payload
+        is authentic, or if certificates are used it verifies the
+        certificate.  The responder MAY accept the public key without
+        verifying it, however, doing so may result to insecure key
+        exchange (accepting the public key without verifying may be
+        desirable for practical reasons on many environments.  For
+        long term use this is never desirable, in which case
+        certificates would be the preferred method to use).  It then
+        computes the HASH_i value the same way initiator did in the
+        phase 1.  It then verifies the signature SIGN_i from the
+        payload with the hash value HASH_i using the received public
+        key.
+
+    3.  Initiator verifies that the public key provided in
+        the payload is authentic, or if certificates are used
+        it verifies the certificate.  The initiator MAY accept
+        the public key without verifying it, however, doing
+        so may result to insecure key exchange (accepting the
+        public key without verifying may be desirable for 
+        practical reasons on many environments.  For long term
+        use this is never desirable, in which case certificates
+        would be the preferred method to use).
+
+        Initiator then computes the shared secret KEY = 
+        f ^ x mod p, and, a hash value HASH in the same way as
+        responder did in phase 2.  It then verifies the 
+        signature SIGN from the payload with the hash value
+        HASH using the received public key.
+
+
+If any of these phases is to fail the SILC_PACKET_FAILURE MUST be sent
+to indicate that the key exchange protocol has failed, and the connection
+SHOULD be closed immediately.  Any other packets MUST NOT be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
+
+The result of this protocol is a shared secret key material KEY and
+a hash value HASH.  The key material itself is not fit to be used as 
+a key, it needs to be processed further to derive the actual keys to be
+used.  The key material is also used to produce other security parameters
+later used in the communication.  See section 2.3 Processing the Key
+Material for detailed description how to process the key material.
+
+If the Mutual Authentication flag was set the protocol produces also
+a hash value HASH_i.  This value, however, must be discarded.
+
+After the keys are processed the protocol is ended by sending the
+SILC_PACKET_SUCCESS packet.  Both entities send this packet to 
+each other.  After this both parties will start using the new keys.
+
+
+.ti 0
+2.3 Processing the Key Material
+
+Key Exchange protocol produces secret shared key material KEY.  This
+key material is used to derive the actual keys used in the encryption
+of the communication channel.  The key material is also used to derive
+other security parameters used in the communication.  Key Exchange
+protocol produces a hash value HASH as well.
+
+The keys MUST be derived from the key material as follows:
+
+.in 6
+Sending Initial Vector (IV)     = hash(0 | KEY | HASH)
+Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
+Sending Encryption Key          = hash(2 | KEY | HASH)
+Receiving Encryption Key        = hash(3 | KEY | HASH)
+HMAC Key                        = hash(4 | KEY | HASH)
+.in 3
+
+
+The Initial Vector (IV) is used in the encryption when doing for
+example CBC mode.  As many bytes as needed are taken from the start of
+the hash output for IV.  Sending IV is for sending key and receiving IV
+is for receiving key.  For receiving party, the receiving IV is actually
+sender's sending IV, and, the sending IV is actually sender's receiving
+IV.  Initiator uses IV's as they are (sending IV for sending and
+receiving IV for receiving).
+
+The Encryption Keys are derived as well from the hash().  If the hash()
+output is too short for the encryption algorithm more key material MUST
+be produced in the following manner:
+
+.in 6
+K1 = hash(2 | KEY | HASH)
+K2 = hash(KEY | K1)
+K3 = hash(KEY | K1 | K2)  ...
+
+Sending Encryption Key = K1 | K2 | K3 ...
+
+
+K1 = hash(3 | KEY | HASH)
+K2 = hash(KEY | K1)
+K3 = hash(KEY | K1 | K2)  ...
+
+Receiving Encryption Key = K1 | K2 | K3 ...
+.in 3
+
+
+The key is distributed by hashing the previous hash with the original
+key material.  The final key is a concatenation of the hash values.
+For Receiving Encryption Key the procedure is equivalent.  Sending key
+is used only for encrypting data to be sent.  The receiving key is used
+only to decrypt received data.  For receiving party, the receive key is
+actually sender's sending key, and, the sending key is actually sender's
+receiving key.  Initiator uses generated keys as they are (sending key
+for sending and receiving key for receiving).
+
+The HMAC key is used to create MAC values to packets in the communication
+channel.  As many bytes as needed are taken from the start of the hash
+output.
+
+These procedures are performed by all parties of the key exchange
+protocol.  This MUST be done before the protocol has been ended by
+sending the SILC_PACKET_SUCCESS packet.
+
+This same procedure is used in the SILC in some other circumstances
+as well.  Any changes to this procedure is mentioned separately when
+this procedure is needed.  See the [SILC1] and the [SILC2] for these
+circumstances.
+
+
+.ti 0
+2.4 SILC Key Exchange Groups
+
+The Following groups may be used in the SILC Key Exchange protocol.
+The first group diffie-hellman-group1 is REQUIRED, other groups MAY be 
+negotiated to be used in the connection with Key Exchange Start Payload
+and SILC_PACKET_KEY_EXCHANGE packet.  However, the first group MUST be
+proposed in the Key Exchange Start Payload regardless of any other
+requested group (however, it does not have to be the first in the list).
+
+
+.ti 0
+2.4.1 diffie-hellman-group1
+
+The length of this group is 1024 bits.  This is REQUIRED group.
+The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+
+Its decimal value is
+
+.in 6
+179769313486231590770839156793787453197860296048756011706444
+423684197180216158519368947833795864925541502180565485980503
+646440548199239100050792877003355816639229553136239076508735
+759914822574862575007425302077447712589550957937778424442426
+617334727629299387668709205606050270810842907692932019128194
+467627007
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
+FFFFFFFF FFFFFFFF
+.in 3
+
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.4.2 diffie-hellman-group2
+
+The length of this group is 1536 bits.  This is OPTIONAL group.
+The prime is 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }.
+
+Its decimal value is
+
+.in 6
+241031242692103258855207602219756607485695054850245994265411
+694195810883168261222889009385826134161467322714147790401219
+650364895705058263194273070680500922306273474534107340669624
+601458936165977404102716924945320037872943417032584377865919
+814376319377685986952408894019557734611984354530154704374720
+774996976375008430892633929555996888245787241299381012913029
+459299994792636526405928464720973038494721168143446471443848
+8520940127459844288859336526896320919633919
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
+.in 3
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.5 Key Exchange Status Types
+
+This section defines all key exchange protocol status types that may
+be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_SKE_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+.in 6
+0   SILC_SKE_STATUS_OK
+
+    Protocol were executed successfully.
+
+
+1   SILC_SKE_STATUS_ERROR
+
+    Unknown error occurred.  No specific error type is defined.
+
+
+2   SILC_SKE_STATUS_BAD_PAYLOAD
+
+    Provided KE payload were malformed or included bad fields.
+
+
+3   SILC_SKE_STATUS_UNSUPPORTED_GROUP
+
+    None of the provided groups were supported.
+
+
+4   SILC_SKE_STATUS_UNSUPPORTED_CIPHER
+
+    None of the provided ciphers were supported.
+
+
+5   SILC_SKE_STATUS_UNSUPPORTED_PKCS
+
+    None of the provided public key algorithms were supported.
+
+
+6   SILC_SKE_STATUS_UNSUPPORTED_HASH_FUNCTION
+
+    None of the provided hash functions were supported.
+
+
+7   SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+    None of the provided HMACs were supported.
+
+
+8   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+
+    Provided public key type is not supported.
+
+
+9   SILC_SKE_STATUS_INCORRECT_SIGNATURE
+
+    Provided signature was incorrect.
+
+
+10  SILC_SKE_STATUS_BAD_VERSION
+
+    Provided version string was not acceptable.
+.in 3
+
+
+.ti 0
+3 SILC Connection Authentication Protocol
+
+Purpose of Connection Authentication protocol is to authenticate the
+connecting party with server.  Usually connecting party is client but
+server may connect to router server as well.  Its other purpose is to
+provide information for the server about which type of connection this
+is.  The type defines whether this is client, server or router
+connection.  Server uses this information to create the ID for the
+connection.
+
+After the authentication protocol has been successfully completed
+SILC_PACKET_NEW_ID must be sent to the connecting client by the server.
+See the [SILC1] for the details of the connecting procedure.
+
+Server MUST verify the authentication data received and if it is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+If everything checks out fine the protocol is ended by server by sending
+SILC_PACKET_SUCCESS packet.
+
+The protocol is executed after the SILC Key Exchange protocol.  It MUST
+NOT be executed in any other time.  As it is performed after key exchange
+protocol all traffic in the connection authentication protocol is
+encrypted with the exchanged keys.
+
+The protocol MUST be started by the connecting party by sending the
+SILC_PACKET_CONNECTION_AUTH packet with Connection Auth Payload,
+described in the next section.  This payload MUST include the
+authentication data.  The authentication data is set according
+authentication method that MUST be known by both parties.  If connecting
+party does not know what is the mandatory authentication method it MAY
+request it from the server by sending SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  This packet is not part of this protocol and is described in
+section Connection Auth Request Payload in [SILC2].  However, if
+connecting party already knows the mandatory authentication method
+sending the request is not necessary.
+
+See [SILC1] and section Connection Auth Request Payload in [SILC2] also
+for the list of different authentication methods.  Authentication method
+MAY also be NONE, in which case the server does not require
+authentication at all.  However, in this case the protocol still MUST be
+executed; the authentication data just is empty indicating no
+authentication is required.
+
+If authentication method is passphrase the authentication data is
+plaintext passphrase.  As the payload is entirely encrypted it is safe
+to have plaintext passphrase.  See the section 3.2.1 Passphrase
+Authentication for more information.
+
+If authentication method is public key authentication the authentication
+data is signature of the hash value HASH plus Key Exchange Start Payload,
+established by the SILC Key Exchange protocol.  This signature MUST then
+be verified by the server.  See the section 3.2.2 Public Key
+Authentication for more information.
+
+The connecting client of this protocol MUST wait after successful execution
+of this protocol for the SILC_PACKET_NEW_ID packet where it will receive
+the ID it will be using in the SILC network.  The connecting client cannot
+start normal SILC session (sending messages or commands) until it has
+received its ID.  The ID's are always created by the server except
+for server to router connection where servers create their own ID's.
+
+
+.ti 0
+3.1 Connection Auth Payload
+
+Client sends this payload to authenticate itself to the server.  Server
+connecting to another server also sends this payload.  Server receiving
+this payload MUST verify all the data in it and if something is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represent the Connection Auth Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |        Connection Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                     Authentication Data                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 3:  Connection Auth Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire Connection 
+  Auth Payload.
+
+o Connection Type (2 bytes) - Indicates the type of the 
+  connection.  See section Connection Auth Request Payload
+  in [SILC2] for the list of connection types.  This field MUST
+  include valid connection type or the packet MUST be discarded
+  and authentication MUST be failed. 
+
+o Authentication Data (variable length) - The actual 
+  authentication data.  Contents of this depends on the 
+  authentication method known by both parties.  If no
+  authentication is required this field does not exist.
+.in 3
+
+
+.ti 0
+3.2 Connection Authentication Types
+
+SILC supports two authentication types to be used in the connection
+authentication protocol; passphrase or public key based authentication.
+The following sections defines the authentication methods.  See [SILC2]
+for defined numerical authentication method types.
+
+
+.ti 0
+3.2.1 Passphrase Authentication
+
+Passphrase authentication or pre-shared-key based authentication is 
+simply an authentication where the party that wants to authenticate 
+itself to the other end sends the passphrase that is required by
+the other end, for example server.
+
+If the passphrase matches with the one in the server's end the
+authentication is successful.  Otherwise SILC_PACKET_FAILURE MUST be
+sent to the sender and the protocol execution fails.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.2.2 Public Key Authentication
+
+Public key authentication may be used if passphrase based authentication
+is not desired.  The public key authentication works by sending a
+signature as authentication data to the other end, say, server.  The
+server MUST then verify the signature by the public key of the sender,
+which the server has received earlier in SKE protocol.
+
+The signature is computed using the private key of the sender by signing
+the HASH value provided by the SKE protocol previously, and the Key
+Exchange Start Payload from SKE protocol that was sent to the server.
+The server MUST verify the data, thus it must keep the HASH and the
+Key Exchange Start Payload saved during SKE and authentication protocols.
+
+If the verified signature matches the sent signature, the authentication
+were successful and SILC_PACKET_SUCCESS is sent.  If it failed the protocol
+execution is stopped and SILC_PACKET_FAILURE is sent.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.3 Connection Authentication Status Types
+
+This section defines all connection authentication status types that
+may be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_AUTH_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+0   SILC_AUTH_OK
+
+    Protocol was executed successfully.
+
+
+1   SILC_AUTH_FAILED
+
+    Authentication failed.
+
+
+.ti 0
+4 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+5 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+6 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 25 October 2001
diff --git a/doc/draft-riikonen-silc-ke-auth-03.nroff b/doc/draft-riikonen-silc-ke-auth-03.nroff
new file mode 100644 (file)
index 0000000..afc7d41
--- /dev/null
@@ -0,0 +1,1094 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet-Draft
+.ds RH 21 August 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-ke-auth-03.txt                      21 August 2001
+Expires: 21 February 2002
+
+.in 3
+
+.ce 2
+SILC Key Exchange and Authentication Protocols
+<draft-riikonen-silc-ke-auth-03.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are
+working documents of the Internet Engineering Task Force (IETF), its
+areas, and its working groups.  Note that other groups may also
+distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time.  It is inappropriate to use Internet-Drafts as reference
+material or to cite them other than as "work in progress."
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html
+
+The distribution of this memo is unlimited.
+
+
+.ti 0
+Abstract
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification internet-draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol [OAKLEY].
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, passphrase
+(pre-shared-secret) or public key (and certificate).
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  3
+2 SILC Key Exchange Protocol ....................................  3
+  2.1 Key Exchange Payloads .....................................  4
+      2.1.1 Key Exchange Start Payload ..........................  4
+      2.1.2 Key Exchange Payload ................................  8
+  2.2 Key Exchange Procedure .................................... 10
+  2.3 Processing the Key Material ............................... 12
+  2.4 SILC Key Exchange Groups .................................. 13
+      2.4.1 diffie-hellman-group1 ............................... 14
+      2.4.2 diffie-hellman-group2 ............................... 14
+  2.5 Key Exchange Status Types ................................. 15
+3 SILC Connection Authentication Protocol ....................... 16
+  3.1 Connection Auth Payload ................................... 18
+  3.2 Connection Authentication Types ........................... 18
+      3.2.1 Passphrase Authentication ........................... 19
+      3.2.2 Public Key Authentication ........................... 19
+  3.3 Connection Authentication Status Types .................... 19
+4 Security Considerations ....................................... 20
+5 References .................................................... 20
+6 Author's Address .............................................. 21
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  Key Exchange Start Payload
+Figure 2:  Key Exchange Payload
+Figure 3:  Connection Auth Payload
+
+
+.ti 0
+1 Introduction
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet-Draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol.
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, pass phrase
+(pre-shared- secret) or public key (and certificate).
+
+The basis of secure SILC session requires strong and secure key exchange
+protocol and authentication.  The authentication protocol is entirely
+secured and no authentication data is ever sent in the network without
+encrypting and authenticating it first.  Thus, authentication protocol
+may be used only after the key exchange protocol has been successfully
+completed.
+
+This document refers constantly to other SILC protocol specification
+Internet Drafts that are a must read for those who wants to understand
+the function of these protocols.  The most important references are
+the Secure Internet Live Conferencing, Protocol Specification [SILC1]
+and the SILC Packet Protocol [SILC2] Internet Drafts.
+
+The protocol is intended to be used with the SILC protocol thus it
+does not define own framework that could be used.  The framework is
+provided by the SILC protocol.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Key Exchange Protocol
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key
+material used to secure the communication channel.  The protocol uses
+Diffie-Hellman key exchange algorithm and its functionality is derived
+from several key exchange protocols.  SKE uses best parts of the SSH2
+Key Exchange protocol, Station-To-Station (STS) protocol and the OAKLEY
+Key Determination protocol.  The protocol does not claim any conformance
+to any of these protocols, they were merely used as a reference when
+designing this protocol.
+
+The purpose of SILC Key Exchange protocol is to create session keys to
+be used in current SILC session.  The keys are valid only for some period
+of time (usually an hour) or at most until the session ends.  These keys
+are used to protect packets like commands, command replies and other
+communication between two entities.  If connection is server to router
+connection, the keys are used to protect all traffic between those
+servers.  In client connections usually all the packets are protected
+with this key except channel messages; channels has their own keys and 
+they are not exchanged with this protocol.
+
+The Diffie-Hellman implementation used in the SILC SHOULD be compliant
+to the PKCS #3.
+
+
+.ti 0
+2.1 Key Exchange Payloads
+
+During the key exchange procedure public data is sent between initiator
+and responder.  This data is later used in the key exchange procedure.
+There are several payloads used in the key exchange.  As for all SILC
+packets, SILC Packet Header, described in [SILC2], is at the start of
+all packets. The same is done with these payloads as well.  All the
+fields in the payloads are always in MSB (most significant byte first)
+order.  Following descriptions of these payloads.
+
+
+.ti 0
+2.1.1 Key Exchange Start Payload
+
+The key exchange between two entities MUST be started by sending the
+SILC_PACKET_KEY_EXCHANGE packet containing Key Exchange Start Payload.
+Initiator sends the Key Exchange Start Payload to the responder filled
+with all security properties it supports.  The responder then checks
+whether it supports the security properties.
+
+It then sends a Key Exchange Start Payload to the initiator filled with
+security properties it selected from the original payload.  The payload
+sent by responder MUST include only one chosen property per list.
+
+The Key Exchange Start Payload is used to tell connecting entities what
+security properties and algorithms should be used in the communication.
+The Key Exchange Start Payload is sent only once per session.  Even if
+the PFS (Perfect Forward Secrecy) flag is set the Key Exchange Start
+Payload is not re-sent.  When PFS is desired the Key Exchange Payloads
+are sent to negotiate new key material.  The procedure is equivalent to
+the very first negotiation except that the Key Exchange Start Payload
+is not sent.
+
+As this payload is used only with the very first key exchange the payload
+is never encrypted, as there are no keys to encrypt it with.
+
+A cookie is also sent in this payload.  A cookie is used to randomize the
+payload so that none of the key exchange parties can determine this
+payload before the key exchange procedure starts.  The cookie MUST be
+returned to the original sender by the responder.
+
+Following diagram represents the Key Exchange Start Payload.  The lists
+mentioned below are always comma (`,') separated and the list MUST
+not include spaces (` ').
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   RESERVED    |     Flags     |         Payload Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++                                                               +  
+|                                                               |
++                            Cookie                             +
+|                                                               |
++                                                               +
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Version String Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Version String                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Key Exchange Grp Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Key Exchange Groups                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        PKCS Alg Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         PKCS Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Encryption Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Encryption Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Hash Alg Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Hash Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         HMAC Length           |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                             HMACs                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|    Compression Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                     Compression Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 1:  Key Exchange Start Payload
+
+
+
+.in 6
+o RESERVED (1 byte) - Reserved field.  Sender fills this with
+  zero (0) value.
+
+o Flags (1 byte) - Indicates flags to be used in the key
+  exchange.  Several flags can be set at once by ORing the
+  flags together.  The following flags are reserved for this
+  field:
+
+     No flags                 0x00
+
+       In this case the field is ignored.
+
+     No Reply                 0x01
+
+       If set the receiver of the payload does not reply to 
+       the packet.
+
+     PFS                      0x02
+
+       Perfect Forward Secrecy (PFS) to be used in the
+       key exchange protocol.  If not set, re-keying
+       is performed using the old key.  See the [SILC1]
+       for more information on this issue.  When PFS is
+       used, re-keying and creating new keys for any
+       particular purpose MUST cause new key exchange.
+       In this key exchange only the Key Exchange Payload
+       is sent and the Key Exchange Start Payload MUST
+       NOT be sent.  When doing PFS the Key Exchange
+       Payloads are encrypted with the old keys.  With
+       the PFS, the Mutual Authentication flag MUST be
+       ignored.
+
+     Mutual Authentication    0x04
+
+       Both of the parties will perform authentication
+       by providing signed data for the other party to
+       verify.  By default, only responder will provide
+       the signature data.  If this is set then the
+       initiator must also provide it.  Initiator MAY
+       set this but also responder MAY set this even if
+       initiator did not set it.
+
+     Rest of the flags are reserved for the future and
+     MUST NOT be set.
+
+o Payload Length (2 bytes) - Length of the entire Key Exchange
+  Start payload, not including any other field.
+
+o Cookie (16 bytes) - Cookie that randomize this payload so
+  that each of the party cannot determine the payload before
+  hand.
+
+o Version String Length (2 bytes) - The length of the Version
+  String field, not including any other field.
+
+o Version String (variable length) - Indicates the version of
+  the sender of this payload.  Initiator sets this when sending
+  the payload and responder sets this when it replies by sending
+  this payload.  See [SILC1] for definition of the version
+  string format.
+
+o Key Exchange Grp Length (2 bytes) - The length of the
+  key exchange group list, not including any other field.
+
+o Key Exchange Group (variable length) - The list of
+  key exchange groups.  See the section 2.4 SILC Key Exchange
+  Groups for definitions of these groups.
+
+o PKCS Alg Length (2 bytes) - The length of the PKCS algorithms
+  list, not including any other field.
+
+o PKCS Algorithms (variable length) - The list of PKCS 
+  algorithms.
+
+o Encryption Alg Length (2 bytes) - The length of the encryption
+  algorithms list, not including any other field.
+
+o Encryption Algorithms (variable length) - The list of
+  encryption algorithms.
+
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
+  list, not including any other field.
+
+o Hash Algorithms (variable length) - The list of Hash
+  algorithms.  The hash algorithms are mainly used in the
+  SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+  including any other field.
+
+o HMACs (variable length) - The list of HMACs.  The HMAC's
+  are used to compute the Message Authentication Codes (MAC)
+  of the SILC packets.
+
+o Compression Alg Length (2 bytes) - The length of the
+  compression algorithms list, not including any other field.
+
+o Compression Algorithms (variable length) - The list of 
+  compression algorithms.
+.in 3
+
+
+.ti 0
+2.1.2 Key Exchange Payload
+
+Key Exchange payload is used to deliver the public key (or certificate),
+the computed Diffie-Hellman public value and possibly signature data
+from one party to the other.  When initiator is using this payload
+and the Mutual Authentication flag is not set then the initiator MUST
+NOT provide the signature data.  If the flag is set then the initiator
+MUST provide the signature data so that the responder can verify it.
+
+The Mutual Authentication flag is usually used only if a separate
+authentication protocol will not be executed for the initiator of the
+protocol.  This is case for example when the SKE is performed between
+two SILC clients.  In normal case, where client is connecting to the
+server, or server is connecting to the router the Mutual Authentication
+flag is not necessary.
+
+When performing re-key with PFS selected this is the only payload that
+is sent in the SKE protocol.  The Key Exchange Start Payload MUST NOT
+be sent at all.  However, this payload does not have all the fields
+present.  In the re-key with PFS the public key and a possible signature
+data SHOULD NOT be present.  If they are present they MUST be ignored.
+The only field that is present is the Public Data that is used to create
+the new key material.  In the re-key the Mutual Authentication flag MUST
+also be ignored.
+
+This payload is sent inside SILC_PACKET_KEY_EXCHANGE_1 and inside
+SILC_PACKET_KEY_EXCHANGE_2 packet types.  The initiator uses the 
+SILC_PACKET_KEY_EXCHANGE_1 and the responder the latter.
+
+The following diagram represent the Key Exchange Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Data Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Public Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Signature Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Signature Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  Key Exchange Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  Following types are defined:
+
+     1    SILC style public key (mandatory)
+     2    SSH2 style public key (optional)
+     3    X.509 Version 3 certificate (optional)
+     4    OpenPGP certificate (optional)
+     5    SPKI certificate (optional)
+
+  The only required type to support is type number 1.  See 
+  [SILC1] for the SILC public key specification.  See
+  SSH public key specification in [SSH-TRANS].  See X.509v3
+  certificate specification in [PKIX-Part1].  See OpenPGP
+  certificate specification in [PGP].  See SPKI certificate
+  specification in [SPKI].  If this field includes zero (0)
+  or unsupported type number the protocol MUST be aborted
+  sending SILC_PACKET_FAILURE message and the connection SHOULD
+  be closed immediately.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+
+o Public Data Length (2 bytes) - The length of the Public Data
+  field, not including any other field.
+
+o Public Data (variable length) - The public data to be
+  sent to the receiver.  See section 2.2 Key Exchange 
+  Procedure for detailed description how this field is
+  computed.  This value is binary encoded.
+
+o Signature Length (2 bytes) - The length of the signature,
+  not including any other field.
+
+o Signature Data (variable length) - The signature signed
+  by the sender.  The receiver of this signature MUST
+  verify it.  The verification is done using the sender's
+  public key.  See section 2.2 Key Exchange Procedure for
+  detailed description how to produce the signature.  If
+  the Mutual Authentication flag is not set then initiator
+  MUST NOT provide this field and the Signature Length field
+  MUST be set to zero (0) value.  If the flag is set then
+  also the initiator MUST provide this field.  The responder
+  MUST always provide this field.
+.in 3
+
+
+.ti 0
+2.2 Key Exchange Procedure
+
+The key exchange begins by sending SILC_PACKET_KEY_EXCHANGE packet with
+Key Exchange Start Payload to select the security properties to be used
+in the key exchange and later in the communication.
+
+After Key Exchange Start Payload has been processed by both of the
+parties the protocol proceeds as follows:
+
+
+Setup:  p is a large and public safe prime.  This is one of the
+        Diffie Hellman groups.  q is order of subgroup (largest
+        prime factor of p).  g is a generator and is defined
+        along with the Diffie Hellman group.
+
+    1.  Initiator generates a random number x, where 1 < x < q, 
+        and computes e = g ^ x mod p.  The result e is then 
+        encoded into Key Exchange Payload and sent to the
+        responder.
+
+        If the Mutual Authentication flag is set then initiator
+        MUST also produce signature data SIGN_i which the responder
+        will verify.  The initiator MUST compute a hash value
+        HASH_i = hash(Key Exchange Start Payload | public key
+        (or certificate) | e).  It then signs the HASH_i value with
+        its private key resulting a signature SIGN_i.
+
+    2.  Responder generates a random number y, where 1 < y < q,
+        and computes f = g ^ y mod p.  It then computes the
+        shared secret KEY = e ^ y mod p, and, a hash value 
+        HASH = hash(Key Exchange Start Payload data | public 
+        key (or certificate) | e | f | KEY).  It then signs
+        the HASH value with its private key resulting a signature
+        SIGN.  
+
+        It then encodes its public key (or certificate), f and 
+        SIGN into Key Exchange Payload and sends it to the 
+        initiator.
+
+        If the Mutual Authentication flag is set then the responder
+        SHOULD verify that the public key provided in the payload
+        is authentic, or if certificates are used it verifies the
+        certificate.  The responder MAY accept the public key without
+        verifying it, however, doing so may result to insecure key
+        exchange (accepting the public key without verifying may be
+        desirable for practical reasons on many environments.  For
+        long term use this is never desirable, in which case
+        certificates would be the preferred method to use).  It then
+        computes the HASH_i value the same way initiator did in the
+        phase 1.  It then verifies the signature SIGN_i from the
+        payload with the hash value HASH_i using the received public
+        key.
+
+    3.  Initiator verifies that the public key provided in
+        the payload is authentic, or if certificates are used
+        it verifies the certificate.  The initiator MAY accept
+        the public key without verifying it, however, doing
+        so may result to insecure key exchange (accepting the
+        public key without verifying may be desirable for 
+        practical reasons on many environments.  For long term
+        use this is never desirable, in which case certificates
+        would be the preferred method to use).
+
+        Initiator then computes the shared secret KEY = 
+        f ^ x mod p, and, a hash value HASH in the same way as
+        responder did in phase 2.  It then verifies the 
+        signature SIGN from the payload with the hash value
+        HASH using the received public key.
+
+
+If any of these phases is to fail the SILC_PACKET_FAILURE MUST be sent
+to indicate that the key exchange protocol has failed, and the connection
+SHOULD be closed immediately.  Any other packets MUST NOT be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
+
+The result of this protocol is a shared secret key material KEY and
+a hash value HASH.  The key material itself is not fit to be used as 
+a key, it needs to be processed further to derive the actual keys to be
+used.  The key material is also used to produce other security parameters
+later used in the communication.  See section 2.3 Processing the Key
+Material for detailed description how to process the key material.
+
+If the Mutual Authentication flag was set the protocol produces also
+a hash value HASH_i.  This value, however, must be discarded.
+
+After the keys are processed the protocol is ended by sending the
+SILC_PACKET_SUCCESS packet.  Both entities send this packet to 
+each other.  After this both parties will start using the new keys.
+
+
+.ti 0
+2.3 Processing the Key Material
+
+Key Exchange protocol produces secret shared key material KEY.  This
+key material is used to derive the actual keys used in the encryption
+of the communication channel.  The key material is also used to derive
+other security parameters used in the communication.  Key Exchange
+protocol produces a hash value HASH as well.
+
+The keys MUST be derived from the key material as follows:
+
+.in 6
+Sending Initial Vector (IV)     = hash(0 | KEY | HASH)
+Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
+Sending Encryption Key          = hash(2 | KEY | HASH)
+Receiving Encryption Key        = hash(3 | KEY | HASH)
+HMAC Key                        = hash(4 | KEY | HASH)
+.in 3
+
+
+The Initial Vector (IV) is used in the encryption when doing for
+example CBC mode.  As many bytes as needed are taken from the start of
+the hash output for IV.  Sending IV is for sending key and receiving IV
+is for receiving key.  For receiving party, the receiving IV is actually
+sender's sending IV, and, the sending IV is actually sender's receiving
+IV.  Initiator uses IV's as they are (sending IV for sending and
+receiving IV for receiving).
+
+The Encryption Keys are derived as well from the hash().  If the hash()
+output is too short for the encryption algorithm more key material MUST
+be produced in the following manner:
+
+.in 6
+K1 = hash(2 | KEY | HASH)
+K2 = hash(KEY | HASH | K1)
+K3 = hash(KEY | HASH | K1 | K2)  ...
+
+Sending Encryption Key = K1 | K2 | K3 ...
+
+
+K1 = hash(3 | KEY | HASH)
+K2 = hash(KEY | HASH | K1)
+K3 = hash(KEY | HASH | K1 | K2)  ...
+
+Receiving Encryption Key = K1 | K2 | K3 ...
+.in 3
+
+
+The key is distributed by hashing the previous hash with the original
+key material.  The final key is a concatenation of the hash values.
+For Receiving Encryption Key the procedure is equivalent.  Sending key
+is used only for encrypting data to be sent.  The receiving key is used
+only to decrypt received data.  For receiving party, the receive key is
+actually sender's sending key, and, the sending key is actually sender's
+receiving key.  Initiator uses generated keys as they are (sending key
+for sending and receiving key for receiving).
+
+The HMAC key is used to create MAC values to packets in the communication
+channel.  As many bytes as needed are taken from the start of the hash
+output.
+
+These procedures are performed by all parties of the key exchange
+protocol.  This MUST be done before the protocol has been ended by
+sending the SILC_PACKET_SUCCESS packet.
+
+This same procedure is used in the SILC in some other circumstances
+as well.  Any changes to this procedure is mentioned separately when
+this procedure is needed.  See the [SILC1] and the [SILC2] for these
+circumstances.
+
+
+.ti 0
+2.4 SILC Key Exchange Groups
+
+The Following groups may be used in the SILC Key Exchange protocol.
+The first group diffie-hellman-group1 is REQUIRED, other groups MAY be 
+negotiated to be used in the connection with Key Exchange Start Payload
+and SILC_PACKET_KEY_EXCHANGE packet.  However, the first group MUST be
+proposed in the Key Exchange Start Payload regardless of any other
+requested group (however, it does not have to be the first in the list).
+
+
+.ti 0
+2.4.1 diffie-hellman-group1
+
+The length of this group is 1024 bits.  This is REQUIRED group.
+The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+
+Its decimal value is
+
+.in 6
+179769313486231590770839156793787453197860296048756011706444
+423684197180216158519368947833795864925541502180565485980503
+646440548199239100050792877003355816639229553136239076508735
+759914822574862575007425302077447712589550957937778424442426
+617334727629299387668709205606050270810842907692932019128194
+467627007
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
+FFFFFFFF FFFFFFFF
+.in 3
+
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.4.2 diffie-hellman-group2
+
+The length of this group is 1536 bits.  This is OPTIONAL group.
+The prime is 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }.
+
+Its decimal value is
+
+.in 6
+241031242692103258855207602219756607485695054850245994265411
+694195810883168261222889009385826134161467322714147790401219
+650364895705058263194273070680500922306273474534107340669624
+601458936165977404102716924945320037872943417032584377865919
+814376319377685986952408894019557734611984354530154704374720
+774996976375008430892633929555996888245787241299381012913029
+459299994792636526405928464720973038494721168143446471443848
+8520940127459844288859336526896320919633919
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
+.in 3
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.5 Key Exchange Status Types
+
+This section defines all key exchange protocol status types that may
+be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_SKE_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+.in 6
+0   SILC_SKE_STATUS_OK
+
+    Protocol were executed successfully.
+
+
+1   SILC_SKE_STATUS_ERROR
+
+    Unknown error occurred.  No specific error type is defined.
+
+
+2   SILC_SKE_STATUS_BAD_PAYLOAD
+
+    Provided KE payload were malformed or included bad fields.
+
+
+3   SILC_SKE_STATUS_UNSUPPORTED_GROUP
+
+    None of the provided groups were supported.
+
+
+4   SILC_SKE_STATUS_UNSUPPORTED_CIPHER
+
+    None of the provided ciphers were supported.
+
+
+5   SILC_SKE_STATUS_UNSUPPORTED_PKCS
+
+    None of the provided public key algorithms were supported.
+
+
+6   SILC_SKE_STATUS_UNSUPPORTED_HASH_FUNCTION
+
+    None of the provided hash functions were supported.
+
+
+7   SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+    None of the provided HMACs were supported.
+
+
+8   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+
+    Provided public key type is not supported.
+
+
+9   SILC_SKE_STATUS_INCORRECT_SIGNATURE
+
+    Provided signature was incorrect.
+
+
+10  SILC_SKE_STATUS_BAD_VERSION
+
+    Provided version string was not acceptable.
+
+11  SILC_SKE_STATUS_INVALID_COOKIE
+
+    The cookie in the Key Exchange Start Payload was malformed,
+    because responder modified the cookie.
+.in 3
+
+
+.ti 0
+3 SILC Connection Authentication Protocol
+
+Purpose of Connection Authentication protocol is to authenticate the
+connecting party with server.  Usually connecting party is client but
+server may connect to router server as well.  Its other purpose is to
+provide information for the server about which type of connection this
+is.  The type defines whether this is client, server or router
+connection.  Server uses this information to create the ID for the
+connection.
+
+After the authentication protocol has been successfully completed
+SILC_PACKET_NEW_ID must be sent to the connecting client by the server.
+See the [SILC1] for the details of the connecting procedure.
+
+Server MUST verify the authentication data received and if it is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+If everything checks out fine the protocol is ended by server by sending
+SILC_PACKET_SUCCESS packet.
+
+The protocol is executed after the SILC Key Exchange protocol.  It MUST
+NOT be executed in any other time.  As it is performed after key exchange
+protocol all traffic in the connection authentication protocol is
+encrypted with the exchanged keys.
+
+The protocol MUST be started by the connecting party by sending the
+SILC_PACKET_CONNECTION_AUTH packet with Connection Auth Payload,
+described in the next section.  This payload MUST include the
+authentication data.  The authentication data is set according
+authentication method that MUST be known by both parties.  If connecting
+party does not know what is the mandatory authentication method it MAY
+request it from the server by sending SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  This packet is not part of this protocol and is described in
+section Connection Auth Request Payload in [SILC2].  However, if
+connecting party already knows the mandatory authentication method
+sending the request is not necessary.
+
+See [SILC1] and section Connection Auth Request Payload in [SILC2] also
+for the list of different authentication methods.  Authentication method
+MAY also be NONE, in which case the server does not require
+authentication at all.  However, in this case the protocol still MUST be
+executed; the authentication data just is empty indicating no
+authentication is required.
+
+If authentication method is passphrase the authentication data is
+plaintext passphrase.  As the payload is entirely encrypted it is safe
+to have plaintext passphrase.  See the section 3.2.1 Passphrase
+Authentication for more information.
+
+If authentication method is public key authentication the authentication
+data is signature of the hash value HASH plus Key Exchange Start Payload,
+established by the SILC Key Exchange protocol.  This signature MUST then
+be verified by the server.  See the section 3.2.2 Public Key
+Authentication for more information.
+
+The connecting client of this protocol MUST wait after successful execution
+of this protocol for the SILC_PACKET_NEW_ID packet where it will receive
+the ID it will be using in the SILC network.  The connecting client cannot
+start normal SILC session (sending messages or commands) until it has
+received its ID.  The ID's are always created by the server except
+for server to router connection where servers create their own ID's.
+
+
+.ti 0
+3.1 Connection Auth Payload
+
+Client sends this payload to authenticate itself to the server.  Server
+connecting to another server also sends this payload.  Server receiving
+this payload MUST verify all the data in it and if something is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represent the Connection Auth Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |        Connection Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                     Authentication Data                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 3:  Connection Auth Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire Connection 
+  Auth Payload.
+
+o Connection Type (2 bytes) - Indicates the type of the 
+  connection.  See section Connection Auth Request Payload
+  in [SILC2] for the list of connection types.  This field MUST
+  include valid connection type or the packet MUST be discarded
+  and authentication MUST be failed. 
+
+o Authentication Data (variable length) - The actual 
+  authentication data.  Contents of this depends on the 
+  authentication method known by both parties.  If no
+  authentication is required this field does not exist.
+.in 3
+
+
+.ti 0
+3.2 Connection Authentication Types
+
+SILC supports two authentication types to be used in the connection
+authentication protocol; passphrase or public key based authentication.
+The following sections defines the authentication methods.  See [SILC2]
+for defined numerical authentication method types.
+
+
+.ti 0
+3.2.1 Passphrase Authentication
+
+Passphrase authentication or pre-shared-key based authentication is 
+simply an authentication where the party that wants to authenticate 
+itself to the other end sends the passphrase that is required by
+the other end, for example server.
+
+If the passphrase matches with the one in the server's end the
+authentication is successful.  Otherwise SILC_PACKET_FAILURE MUST be
+sent to the sender and the protocol execution fails.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.2.2 Public Key Authentication
+
+Public key authentication may be used if passphrase based authentication
+is not desired.  The public key authentication works by sending a
+signature as authentication data to the other end, say, server.  The
+server MUST then verify the signature by the public key of the sender,
+which the server has received earlier in SKE protocol.
+
+The signature is computed using the private key of the sender by signing
+the HASH value provided by the SKE protocol previously, and the Key
+Exchange Start Payload from SKE protocol that was sent to the server.
+The server MUST verify the data, thus it must keep the HASH and the
+Key Exchange Start Payload saved during SKE and authentication protocols.
+
+If the verified signature matches the sent signature, the authentication
+were successful and SILC_PACKET_SUCCESS is sent.  If it failed the protocol
+execution is stopped and SILC_PACKET_FAILURE is sent.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.3 Connection Authentication Status Types
+
+This section defines all connection authentication status types that
+may be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_AUTH_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+
+
+0   SILC_AUTH_OK
+
+    Protocol was executed successfully.
+
+
+1   SILC_AUTH_FAILED
+
+    Authentication failed.
+
+
+.ti 0
+4 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+5 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+6 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 21 February 2002
diff --git a/doc/draft-riikonen-silc-ke-auth-04.nroff b/doc/draft-riikonen-silc-ke-auth-04.nroff
new file mode 100644 (file)
index 0000000..da93c62
--- /dev/null
@@ -0,0 +1,1114 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet-Draft
+.ds RH 13 November 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-ke-auth-04.txt                      13 November 2001
+Expires: 13 May 2002
+
+.in 3
+
+.ce 2
+SILC Key Exchange and Authentication Protocols
+<draft-riikonen-silc-ke-auth-04.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are
+working documents of the Internet Engineering Task Force (IETF), its
+areas, and its working groups.  Note that other groups may also
+distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time.  It is inappropriate to use Internet-Drafts as reference
+material or to cite them other than as "work in progress."
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html
+
+The distribution of this memo is unlimited.
+
+
+.ti 0
+Abstract
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification internet-draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol [OAKLEY].
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, passphrase
+(pre-shared-secret) or public key (and certificate).
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  3
+2 SILC Key Exchange Protocol ....................................  3
+  2.1 Key Exchange Payloads .....................................  4
+      2.1.1 Key Exchange Start Payload ..........................  4
+      2.1.2 Key Exchange Payload ................................  8
+  2.2 Key Exchange Procedure .................................... 10
+  2.3 Processing the Key Material ............................... 12
+  2.4 SILC Key Exchange Groups .................................. 13
+      2.4.1 diffie-hellman-group1 ............................... 14
+      2.4.2 diffie-hellman-group2 ............................... 14
+  2.5 Key Exchange Status Types ................................. 15
+3 SILC Connection Authentication Protocol ....................... 16
+  3.1 Connection Auth Payload ................................... 18
+  3.2 Connection Authentication Types ........................... 19
+      3.2.1 Passphrase Authentication ........................... 19
+      3.2.2 Public Key Authentication ........................... 19
+  3.3 Connection Authentication Status Types .................... 20
+4 Security Considerations ....................................... 20
+5 References .................................................... 20
+6 Author's Address .............................................. 22
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  Key Exchange Start Payload
+Figure 2:  Key Exchange Payload
+Figure 3:  Connection Auth Payload
+
+
+.ti 0
+1 Introduction
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet-Draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol.
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, pass phrase
+(pre-shared- secret) or public key (and certificate).
+
+The basis of secure SILC session requires strong and secure key exchange
+protocol and authentication.  The authentication protocol is entirely
+secured and no authentication data is ever sent in the network without
+encrypting and authenticating it first.  Thus, authentication protocol
+may be used only after the key exchange protocol has been successfully
+completed.
+
+This document refers constantly to other SILC protocol specification
+Internet Drafts that are a must read for those who wants to understand
+the function of these protocols.  The most important references are
+the Secure Internet Live Conferencing, Protocol Specification [SILC1]
+and the SILC Packet Protocol [SILC2] Internet Drafts.
+
+The protocol is intended to be used with the SILC protocol thus it
+does not define own framework that could be used.  The framework is
+provided by the SILC protocol.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Key Exchange Protocol
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key
+material used to secure the communication channel.  The protocol uses
+Diffie-Hellman key exchange algorithm and its functionality is derived
+from several key exchange protocols.  SKE uses best parts of the SSH2
+Key Exchange protocol, Station-To-Station (STS) protocol and the OAKLEY
+Key Determination protocol.  The protocol does not claim any conformance
+to any of these protocols, they were merely used as a reference when
+designing this protocol.
+
+The purpose of SILC Key Exchange protocol is to create session keys to
+be used in current SILC session.  The keys are valid only for some period
+of time (usually an hour) or at most until the session ends.  These keys
+are used to protect packets like commands, command replies and other
+communication between two entities.  If connection is server to router
+connection, the keys are used to protect all traffic between those
+servers.  In client connections usually all the packets are protected
+with this key except channel messages; channels has their own keys and 
+they are not exchanged with this protocol.
+
+The Diffie-Hellman implementation used in the SILC SHOULD be compliant
+to the PKCS #3.
+
+
+.ti 0
+2.1 Key Exchange Payloads
+
+During the key exchange procedure public data is sent between initiator
+and responder.  This data is later used in the key exchange procedure.
+There are several payloads used in the key exchange.  As for all SILC
+packets, SILC Packet Header, described in [SILC2], is at the start of
+all packets. The same is done with these payloads as well.  All the
+fields in the payloads are always in MSB (most significant byte first)
+order.  Following descriptions of these payloads.
+
+
+.ti 0
+2.1.1 Key Exchange Start Payload
+
+The key exchange between two entities MUST be started by sending the
+SILC_PACKET_KEY_EXCHANGE packet containing Key Exchange Start Payload.
+Initiator sends the Key Exchange Start Payload to the responder filled
+with all security properties it supports.  The responder then checks
+whether it supports the security properties.
+
+It then sends a Key Exchange Start Payload to the initiator filled with
+security properties it selected from the original payload.  The payload
+sent by responder MUST include only one chosen property per list.
+
+The Key Exchange Start Payload is used to tell connecting entities what
+security properties and algorithms should be used in the communication.
+The Key Exchange Start Payload is sent only once per session.  Even if
+the PFS (Perfect Forward Secrecy) flag is set the Key Exchange Start
+Payload is not re-sent.  When PFS is desired the Key Exchange Payloads
+are sent to negotiate new key material.  The procedure is equivalent to
+the very first negotiation except that the Key Exchange Start Payload
+is not sent.
+
+As this payload is used only with the very first key exchange the payload
+is never encrypted, as there are no keys to encrypt it with.
+
+A cookie is also sent in this payload.  A cookie is used to randomize the
+payload so that none of the key exchange parties can determine this
+payload before the key exchange procedure starts.  The cookie MUST be
+returned to the original sender by the responder.
+
+Following diagram represents the Key Exchange Start Payload.  The lists
+mentioned below are always comma (`,') separated and the list MUST NOT
+include spaces (` ').
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   RESERVED    |     Flags     |         Payload Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++                                                               +  
+|                                                               |
++                            Cookie                             +
+|                                                               |
++                                                               +
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Version String Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Version String                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Key Exchange Grp Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Key Exchange Groups                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        PKCS Alg Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         PKCS Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Encryption Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Encryption Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Hash Alg Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Hash Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         HMAC Length           |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                             HMACs                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|    Compression Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                     Compression Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 1:  Key Exchange Start Payload
+
+
+
+.in 6
+o RESERVED (1 byte) - Reserved field.  Sender fills this with
+  zero (0) value.
+
+o Flags (1 byte) - Indicates flags to be used in the key
+  exchange.  Several flags can be set at once by ORing the
+  flags together.  The following flags are reserved for this
+  field:
+
+     No flags                 0x00
+
+       In this case the field is ignored.
+
+     No Reply                 0x01
+
+       If set the receiver of the payload does not reply to 
+       the packet.
+
+     PFS                      0x02
+
+       Perfect Forward Secrecy (PFS) to be used in the
+       key exchange protocol.  If not set, re-keying
+       is performed using the old key.  See the [SILC1]
+       for more information on this issue.  When PFS is
+       used, re-keying and creating new keys for any
+       particular purpose MUST cause new key exchange.
+       In this key exchange only the Key Exchange Payload
+       is sent and the Key Exchange Start Payload MUST
+       NOT be sent.  When doing PFS the Key Exchange
+       Payloads are encrypted with the old keys.
+
+     Mutual Authentication    0x04
+
+       Both of the parties will perform authentication
+       by providing signed data for the other party to
+       verify.  By default, only responder will provide
+       the signature data.  If this is set then the
+       initiator must also provide it.  Initiator MAY
+       set this but also responder MAY set this even if
+       initiator did not set it.
+
+     Rest of the flags are reserved for the future and
+     MUST NOT be set.
+
+o Payload Length (2 bytes) - Length of the entire Key Exchange
+  Start payload, not including any other field.
+
+o Cookie (16 bytes) - Cookie that randomize this payload so
+  that each of the party cannot determine the payload before
+  hand.
+
+o Version String Length (2 bytes) - The length of the Version
+  String field, not including any other field.
+
+o Version String (variable length) - Indicates the version of
+  the sender of this payload.  Initiator sets this when sending
+  the payload and responder sets this when it replies by sending
+  this payload.  See [SILC1] for definition of the version
+  string format.
+
+o Key Exchange Grp Length (2 bytes) - The length of the
+  key exchange group list, not including any other field.
+
+o Key Exchange Group (variable length) - The list of
+  key exchange groups.  See the section 2.4 SILC Key Exchange
+  Groups for definitions of these groups.
+
+o PKCS Alg Length (2 bytes) - The length of the PKCS algorithms
+  list, not including any other field.
+
+o PKCS Algorithms (variable length) - The list of PKCS 
+  algorithms.
+
+o Encryption Alg Length (2 bytes) - The length of the encryption
+  algorithms list, not including any other field.
+
+o Encryption Algorithms (variable length) - The list of
+  encryption algorithms.
+
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
+  list, not including any other field.
+
+o Hash Algorithms (variable length) - The list of Hash
+  algorithms.  The hash algorithms are mainly used in the
+  SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+  including any other field.
+
+o HMACs (variable length) - The list of HMACs.  The HMAC's
+  are used to compute the Message Authentication Codes (MAC)
+  of the SILC packets.
+
+o Compression Alg Length (2 bytes) - The length of the
+  compression algorithms list, not including any other field.
+
+o Compression Algorithms (variable length) - The list of 
+  compression algorithms.
+.in 3
+
+
+.ti 0
+2.1.2 Key Exchange Payload
+
+Key Exchange payload is used to deliver the public key (or certificate),
+the computed Diffie-Hellman public value and possibly signature data
+from one party to the other.  When initiator is using this payload
+and the Mutual Authentication flag is not set then the initiator MUST
+NOT provide the signature data.  If the flag is set then the initiator
+MUST provide the signature data so that the responder can verify it.
+
+The Mutual Authentication flag is usually used when a separate 
+authentication protocol will not be executed for the initiator of the
+protocol.  This is case for example when the SKE is performed between
+two SILC clients.  In normal case, where client is connecting to a
+server, or server is connecting to a router the Mutual Authentication
+flag may be omitted.  However, if the connection authentication protocol 
+for the connecting entity is not based on public key authentication (it
+is based on passphrase) then the Mutual Authentication flag SHOULD be 
+enabled.  This way the connecting entity has to provide proof of
+posession of the private key for the public key it will provide in
+SILC Key Exchange protocol.
+
+When performing re-key with PFS selected this is the only payload that
+is sent in the SKE protocol.  The Key Exchange Start Payload MUST NOT
+be sent at all.  However, this payload does not have all the fields
+present.  In the re-key with PFS the public key and a possible signature
+data SHOULD NOT be present.  If they are present they MUST be ignored.
+The only field that is present is the Public Data that is used to create
+the new key material.  In the re-key the Mutual Authentication flag, that
+may be set in the initial negotiation, MUST also be ignored.
+
+This payload is sent inside SILC_PACKET_KEY_EXCHANGE_1 and inside
+SILC_PACKET_KEY_EXCHANGE_2 packet types.  The initiator uses the 
+SILC_PACKET_KEY_EXCHANGE_1 and the responder the latter.
+
+The following diagram represent the Key Exchange Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Data Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Public Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Signature Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Signature Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  Key Exchange Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  Following types are defined:
+
+     1    SILC style public key (mandatory)
+     2    SSH2 style public key (optional)
+     3    X.509 Version 3 certificate (optional)
+     4    OpenPGP certificate (optional)
+     5    SPKI certificate (optional)
+
+  The only required type to support is type number 1.  See 
+  [SILC1] for the SILC public key specification.  See
+  SSH public key specification in [SSH-TRANS].  See X.509v3
+  certificate specification in [PKIX-Part1].  See OpenPGP
+  certificate specification in [PGP].  See SPKI certificate
+  specification in [SPKI].  If this field includes zero (0)
+  or unsupported type number the protocol MUST be aborted
+  sending SILC_PACKET_FAILURE message and the connection SHOULD
+  be closed immediately.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+
+o Public Data Length (2 bytes) - The length of the Public Data
+  field, not including any other field.
+
+o Public Data (variable length) - The public data to be
+  sent to the receiver.  See section 2.2 Key Exchange 
+  Procedure for detailed description how this field is
+  computed.  This value is binary encoded.
+
+o Signature Length (2 bytes) - The length of the signature,
+  not including any other field.
+
+o Signature Data (variable length) - The signature signed
+  by the sender.  The receiver of this signature MUST
+  verify it.  The verification is done using the sender's
+  public key.  See section 2.2 Key Exchange Procedure for
+  detailed description how to produce the signature.  If
+  the Mutual Authentication flag is not set then initiator
+  MUST NOT provide this field and the Signature Length field
+  MUST be set to zero (0) value.  If the flag is set then
+  also the initiator MUST provide this field.  The responder
+  MUST always provide this field.
+.in 3
+
+
+.ti 0
+2.2 Key Exchange Procedure
+
+The key exchange begins by sending SILC_PACKET_KEY_EXCHANGE packet with
+Key Exchange Start Payload to select the security properties to be used
+in the key exchange and later in the communication.
+
+After Key Exchange Start Payload has been processed by both of the
+parties the protocol proceeds as follows:
+
+
+Setup:  p is a large and public safe prime.  This is one of the
+        Diffie Hellman groups.  q is order of subgroup (largest
+        prime factor of p).  g is a generator and is defined
+        along with the Diffie Hellman group.
+
+    1.  Initiator generates a random number x, where 1 < x < q, 
+        and computes e = g ^ x mod p.  The result e is then 
+        encoded into Key Exchange Payload, with the public key
+        (or certificate) and sent to the responder.
+
+        If the Mutual Authentication flag is set then initiator
+        MUST also produce signature data SIGN_i which the responder
+        will verify.  The initiator MUST compute a hash value
+        HASH_i = hash(Key Exchange Start Payload | public key
+        (or certificate) | e).  It then signs the HASH_i value with
+        its private key resulting a signature SIGN_i.
+
+    2.  Responder generates a random number y, where 1 < y < q,
+        and computes f = g ^ y mod p.  It then computes the
+        shared secret KEY = e ^ y mod p, and, a hash value 
+        HASH = hash(Key Exchange Start Payload data | public 
+        key (or certificate) | Initiator's public key (or
+        certificate) | e | f | KEY).  It then signs
+        the HASH value with its private key resulting a signature
+        SIGN.  
+
+        It then encodes its public key (or certificate), f and 
+        SIGN into Key Exchange Payload and sends it to the 
+        initiator.
+
+        If the Mutual Authentication flag is set then the responder
+        SHOULD verify that the public key provided in the payload
+        is authentic, or if certificates are used it verifies the
+        certificate.  The responder MAY accept the public key without
+        verifying it, however, doing so may result to insecure key
+        exchange (accepting the public key without verifying may be
+        desirable for practical reasons on many environments.  For
+        long term use this is never desirable, in which case
+        certificates would be the preferred method to use).  It then
+        computes the HASH_i value the same way initiator did in the
+        phase 1.  It then verifies the signature SIGN_i from the
+        payload with the hash value HASH_i using the received public
+        key.
+
+    3.  Initiator verifies that the public key provided in
+        the payload is authentic, or if certificates are used
+        it verifies the certificate.  The initiator MAY accept
+        the public key without verifying it, however, doing
+        so may result to insecure key exchange (accepting the
+        public key without verifying may be desirable for 
+        practical reasons on many environments.  For long term
+        use this is never desirable, in which case certificates
+        would be the preferred method to use).
+
+        Initiator then computes the shared secret KEY = 
+        f ^ x mod p, and, a hash value HASH in the same way as
+        responder did in phase 2.  It then verifies the 
+        signature SIGN from the payload with the hash value
+        HASH using the received public key.
+
+
+If any of these phases is to fail the SILC_PACKET_FAILURE MUST be sent
+to indicate that the key exchange protocol has failed, and the connection
+SHOULD be closed immediately.  Any other packets MUST NOT be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
+
+The result of this protocol is a shared secret key material KEY and
+a hash value HASH.  The key material itself is not fit to be used as 
+a key, it needs to be processed further to derive the actual keys to be
+used.  The key material is also used to produce other security parameters
+later used in the communication.  See section 2.3 Processing the Key
+Material for detailed description how to process the key material.
+
+If the Mutual Authentication flag was set the protocol produces also
+a hash value HASH_i.  This value, however, must be discarded.
+
+After the keys are processed the protocol is ended by sending the
+SILC_PACKET_SUCCESS packet.  Both entities send this packet to 
+each other.  After this both parties will start using the new keys.
+
+
+.ti 0
+2.3 Processing the Key Material
+
+Key Exchange protocol produces secret shared key material KEY.  This
+key material is used to derive the actual keys used in the encryption
+of the communication channel.  The key material is also used to derive
+other security parameters used in the communication.  Key Exchange
+protocol produces a hash value HASH as well.
+
+The keys MUST be derived from the key material as follows:
+
+.in 6
+Sending Initial Vector (IV)     = hash(0 | KEY | HASH)
+Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
+Sending Encryption Key          = hash(2 | KEY | HASH)
+Receiving Encryption Key        = hash(3 | KEY | HASH)
+Sending HMAC Key                = hash(4 | KEY | HASH)
+Receiving HMAC Key              = hash(5 | KEY | HASH)
+.in 3
+
+
+The Initial Vector (IV) is used in the encryption when doing for
+example CBC mode.  As many bytes as needed are taken from the start of
+the hash output for IV.  Sending IV is for sending key and receiving IV
+is for receiving key.  For receiving party, the receiving IV is actually
+sender's sending IV, and, the sending IV is actually sender's receiving
+IV.  Initiator uses IV's as they are (sending IV for sending and
+receiving IV for receiving).
+
+The Encryption Keys are derived as well from the hash().  If the hash()
+output is too short for the encryption algorithm more key material MUST
+be produced in the following manner:
+
+.in 6
+K1 = hash(2 | KEY | HASH)
+K2 = hash(KEY | HASH | K1)
+K3 = hash(KEY | HASH | K1 | K2)  ...
+
+Sending Encryption Key = K1 | K2 | K3 ...
+
+
+K1 = hash(3 | KEY | HASH)
+K2 = hash(KEY | HASH | K1)
+K3 = hash(KEY | HASH | K1 | K2)  ...
+
+Receiving Encryption Key = K1 | K2 | K3 ...
+.in 3
+
+
+The key is distributed by hashing the previous hash with the original
+key material.  The final key is a concatenation of the hash values.
+For Receiving Encryption Key the procedure is equivalent.  Sending key
+is used only for encrypting data to be sent.  The receiving key is used
+only to decrypt received data.  For receiving party, the receive key is
+actually sender's sending key, and, the sending key is actually sender's
+receiving key.  Initiator uses generated keys as they are (sending key
+for sending and receiving key for receiving).
+
+The HMAC keys are used to create MAC values to packets in the
+communication channel.  As many bytes as needed are taken from the start
+of the hash output to generate the MAC keys.
+
+These procedures are performed by all parties of the key exchange
+protocol.  This MUST be done before the protocol has been ended by
+sending the SILC_PACKET_SUCCESS packet.
+
+This same procedure is used in the SILC in some other circumstances
+as well.  Any changes to this procedure is mentioned separately when
+this procedure is needed.  See the [SILC1] and the [SILC2] for these
+circumstances.
+
+
+.ti 0
+2.4 SILC Key Exchange Groups
+
+The Following groups may be used in the SILC Key Exchange protocol.
+The first group diffie-hellman-group1 is REQUIRED, other groups MAY be 
+negotiated to be used in the connection with Key Exchange Start Payload
+and SILC_PACKET_KEY_EXCHANGE packet.  However, the first group MUST be
+proposed in the Key Exchange Start Payload regardless of any other
+requested group (however, it does not have to be the first in the list).
+
+
+.ti 0
+2.4.1 diffie-hellman-group1
+
+The length of this group is 1024 bits.  This is REQUIRED group.
+The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+
+Its decimal value is
+
+.in 6
+179769313486231590770839156793787453197860296048756011706444
+423684197180216158519368947833795864925541502180565485980503
+646440548199239100050792877003355816639229553136239076508735
+759914822574862575007425302077447712589550957937778424442426
+617334727629299387668709205606050270810842907692932019128194
+467627007
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
+FFFFFFFF FFFFFFFF
+.in 3
+
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.4.2 diffie-hellman-group2
+
+The length of this group is 1536 bits.  This is OPTIONAL group.
+The prime is 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }.
+
+Its decimal value is
+
+.in 6
+241031242692103258855207602219756607485695054850245994265411
+694195810883168261222889009385826134161467322714147790401219
+650364895705058263194273070680500922306273474534107340669624
+601458936165977404102716924945320037872943417032584377865919
+814376319377685986952408894019557734611984354530154704374720
+774996976375008430892633929555996888245787241299381012913029
+459299994792636526405928464720973038494721168143446471443848
+8520940127459844288859336526896320919633919
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
+.in 3
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.5 Key Exchange Status Types
+
+This section defines all key exchange protocol status types that may
+be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_SKE_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+.in 6
+0   SILC_SKE_STATUS_OK
+
+    Protocol were executed successfully.
+
+
+1   SILC_SKE_STATUS_ERROR
+
+    Unknown error occurred.  No specific error type is defined.
+
+
+2   SILC_SKE_STATUS_BAD_PAYLOAD
+
+    Provided KE payload were malformed or included bad fields.
+
+
+3   SILC_SKE_STATUS_UNSUPPORTED_GROUP
+
+    None of the provided groups were supported.
+
+
+4   SILC_SKE_STATUS_UNSUPPORTED_CIPHER
+
+    None of the provided ciphers were supported.
+
+
+5   SILC_SKE_STATUS_UNSUPPORTED_PKCS
+
+    None of the provided public key algorithms were supported.
+
+
+6   SILC_SKE_STATUS_UNSUPPORTED_HASH_FUNCTION
+
+    None of the provided hash functions were supported.
+
+
+7   SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+    None of the provided HMACs were supported.
+
+
+8   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+
+    Provided public key type is not supported.
+
+
+9   SILC_SKE_STATUS_INCORRECT_SIGNATURE
+
+    Provided signature was incorrect.
+
+
+10  SILC_SKE_STATUS_BAD_VERSION
+
+    Provided version string was not acceptable.
+
+11  SILC_SKE_STATUS_INVALID_COOKIE
+
+    The cookie in the Key Exchange Start Payload was malformed,
+    because responder modified the cookie.
+.in 3
+
+
+.ti 0
+3 SILC Connection Authentication Protocol
+
+Purpose of Connection Authentication protocol is to authenticate the
+connecting party with server.  Usually connecting party is client but
+server may connect to router server as well.  Its other purpose is to
+provide information for the server about which type of connection this
+is.  The type defines whether this is client, server or router
+connection.  Server uses this information to create the ID for the
+connection.
+
+After the authentication protocol has been successfully completed
+SILC_PACKET_NEW_ID must be sent to the connecting client by the server.
+See the [SILC1] for the details of the connecting procedure.
+
+Server MUST verify the authentication data received and if it is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+If everything checks out fine the protocol is ended by server by sending
+SILC_PACKET_SUCCESS packet.
+
+The protocol is executed after the SILC Key Exchange protocol.  It MUST
+NOT be executed in any other time.  As it is performed after key exchange
+protocol all traffic in the connection authentication protocol is
+encrypted with the exchanged keys.
+
+The protocol MUST be started by the connecting party by sending the
+SILC_PACKET_CONNECTION_AUTH packet with Connection Auth Payload,
+described in the next section.  This payload MUST include the
+authentication data.  The authentication data is set according
+authentication method that MUST be known by both parties.  If connecting
+party does not know what is the mandatory authentication method it MAY
+request it from the server by sending SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  This packet is not part of this protocol and is described in
+section Connection Auth Request Payload in [SILC2].  However, if
+connecting party already knows the mandatory authentication method
+sending the request is not necessary.
+
+See [SILC1] and section Connection Auth Request Payload in [SILC2] also
+for the list of different authentication methods.  Authentication method
+MAY also be NONE, in which case the server does not require
+authentication at all.  However, in this case the protocol still MUST be
+executed; the authentication data just is empty indicating no
+authentication is required.
+
+If authentication method is passphrase the authentication data is
+plaintext passphrase.  As the payload is entirely encrypted it is safe
+to have plaintext passphrase.  See the section 3.2.1 Passphrase
+Authentication for more information.
+
+If authentication method is public key authentication the authentication
+data is a signature of the hash value of hash HASH plus Key Exchange
+Start Payload, established by the SILC Key Exchange protocol.  This
+signature MUST then be verified by the server.  See the section 3.2.2
+Public Key Authentication for more information.
+
+The connecting client of this protocol MUST wait after successful execution
+of this protocol for the SILC_PACKET_NEW_ID packet where it will receive
+the ID it will be using in the SILC network.  The connecting client cannot
+start normal SILC session (sending messages or commands) until it has
+received its ID.  The ID's are always created by the server except
+for server to router connection where servers create their own ID's.
+
+
+.ti 0
+3.1 Connection Auth Payload
+
+Client sends this payload to authenticate itself to the server.  Server
+connecting to another server also sends this payload.  Server receiving
+this payload MUST verify all the data in it and if something is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represent the Connection Auth Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |        Connection Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                     Authentication Data                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 3:  Connection Auth Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire Connection 
+  Auth Payload.
+
+o Connection Type (2 bytes) - Indicates the type of the 
+  connection.  See section Connection Auth Request Payload
+  in [SILC2] for the list of connection types.  This field MUST
+  include valid connection type or the packet MUST be discarded
+  and authentication MUST be failed. 
+
+o Authentication Data (variable length) - The actual 
+  authentication data.  Contents of this depends on the 
+  authentication method known by both parties.  If no
+  authentication is required this field does not exist.
+.in 3
+
+
+
+
+.ti 0
+3.2 Connection Authentication Types
+
+SILC supports two authentication types to be used in the connection
+authentication protocol; passphrase or public key based authentication.
+The following sections defines the authentication methods.  See [SILC2]
+for defined numerical authentication method types.
+
+
+.ti 0
+3.2.1 Passphrase Authentication
+
+Passphrase authentication or pre-shared-key based authentication is 
+simply an authentication where the party that wants to authenticate 
+itself to the other end sends the passphrase that is required by
+the other end, for example server.
+
+If the passphrase matches with the one in the server's end the
+authentication is successful.  Otherwise SILC_PACKET_FAILURE MUST be
+sent to the sender and the protocol execution fails.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+When password authentication is used it is RECOMMENDED that maximum
+amount of padding is applied to the SILC packet.  This way it is not
+possible to approximate the length of the password from the encrypted
+packet.
+
+
+.ti 0
+3.2.2 Public Key Authentication
+
+Public key authentication may be used if passphrase based authentication
+is not desired.  The public key authentication works by sending a
+signature as authentication data to the other end, say, server.  The
+server MUST then verify the signature by the public key of the sender,
+which the server has received earlier in SKE protocol.
+
+The signature is computed using the private key of the sender by signing
+the HASH value provided by the SKE protocol previously, and the Key
+Exchange Start Payload from SKE protocol that was sent to the server.
+These are concatenated and hash function is used to compute a hash value
+which is then signed.
+
+  auth_hash = hash(HASH | Key Exchange Start Payload);
+  signature = sign(auth_hash);
+
+The hash() function used to compute the value is the hash function
+negotiated in the SKE protocol.  The server MUST verify the data, thus
+it must keep the HASH and the Key Exchange Start Payload saved during
+SKE and authentication protocols.
+
+If the verified signature matches the sent signature, the authentication
+were successful and SILC_PACKET_SUCCESS is sent.  If it failed the
+protocol execution is stopped and SILC_PACKET_FAILURE is sent.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.3 Connection Authentication Status Types
+
+This section defines all connection authentication status types that
+may be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_AUTH_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+
+
+0   SILC_AUTH_OK
+
+    Protocol was executed successfully.
+
+
+1   SILC_AUTH_FAILED
+
+    Authentication failed.
+
+
+.ti 0
+4 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+5 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+6 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 13 May 2002
diff --git a/doc/draft-riikonen-silc-ke-auth-05.nroff b/doc/draft-riikonen-silc-ke-auth-05.nroff
new file mode 100644 (file)
index 0000000..8e95e13
--- /dev/null
@@ -0,0 +1,1114 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet-Draft
+.ds RH XXX
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-ke-auth-05.txt                      XXX
+Expires: XXX
+
+.in 3
+
+.ce 2
+SILC Key Exchange and Authentication Protocols
+<draft-riikonen-silc-ke-auth-05.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are
+working documents of the Internet Engineering Task Force (IETF), its
+areas, and its working groups.  Note that other groups may also
+distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time.  It is inappropriate to use Internet-Drafts as reference
+material or to cite them other than as "work in progress."
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html
+
+The distribution of this memo is unlimited.
+
+
+.ti 0
+Abstract
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification internet-draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol [OAKLEY].
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, passphrase
+(pre-shared-secret) or public key (and certificate).
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  2
+  1.1 Requirements Terminology ..................................  3
+2 SILC Key Exchange Protocol ....................................  3
+  2.1 Key Exchange Payloads .....................................  4
+      2.1.1 Key Exchange Start Payload ..........................  4
+      2.1.2 Key Exchange Payload ................................  8
+  2.2 Key Exchange Procedure .................................... 10
+  2.3 Processing the Key Material ............................... 12
+  2.4 SILC Key Exchange Groups .................................. 13
+      2.4.1 diffie-hellman-group1 ............................... 14
+      2.4.2 diffie-hellman-group2 ............................... 14
+  2.5 Key Exchange Status Types ................................. 15
+3 SILC Connection Authentication Protocol ....................... 16
+  3.1 Connection Auth Payload ................................... 18
+  3.2 Connection Authentication Types ........................... 19
+      3.2.1 Passphrase Authentication ........................... 19
+      3.2.2 Public Key Authentication ........................... 19
+  3.3 Connection Authentication Status Types .................... 20
+4 Security Considerations ....................................... 20
+5 References .................................................... 20
+6 Author's Address .............................................. 22
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  Key Exchange Start Payload
+Figure 2:  Key Exchange Payload
+Figure 3:  Connection Auth Payload
+
+
+.ti 0
+1 Introduction
+
+This memo describes two protocols used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet-Draft [SILC1].  The
+SILC Key Exchange (SKE) protocol provides secure key exchange between
+two parties resulting into shared secret key material.  The protocol
+is based on Diffie-Hellman key exchange algorithm and its functionality
+is derived from several key exchange protocols.  SKE uses best parts
+of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol
+and the OAKLEY Key Determination protocol.
+
+The SILC Connection Authentication protocol provides user level
+authentication used when creating connections in SILC network.  The
+protocol is transparent to the authentication data which means that it
+can be used to authenticate the user with, for example, pass phrase
+(pre-shared- secret) or public key (and certificate).
+
+The basis of secure SILC session requires strong and secure key exchange
+protocol and authentication.  The authentication protocol is entirely
+secured and no authentication data is ever sent in the network without
+encrypting and authenticating it first.  Thus, authentication protocol
+may be used only after the key exchange protocol has been successfully
+completed.
+
+This document refers constantly to other SILC protocol specification
+Internet Drafts that are a must read for those who wants to understand
+the function of these protocols.  The most important references are
+the Secure Internet Live Conferencing, Protocol Specification [SILC1]
+and the SILC Packet Protocol [SILC2] Internet Drafts.
+
+The protocol is intended to be used with the SILC protocol thus it
+does not define own framework that could be used.  The framework is
+provided by the SILC protocol.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Key Exchange Protocol
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key
+material used to secure the communication channel.  The protocol uses
+Diffie-Hellman key exchange algorithm and its functionality is derived
+from several key exchange protocols.  SKE uses best parts of the SSH2
+Key Exchange protocol, Station-To-Station (STS) protocol and the OAKLEY
+Key Determination protocol.  The protocol does not claim any conformance
+to any of these protocols, they were merely used as a reference when
+designing this protocol.
+
+The purpose of SILC Key Exchange protocol is to create session keys to
+be used in current SILC session.  The keys are valid only for some period
+of time (usually an hour) or at most until the session ends.  These keys
+are used to protect packets like commands, command replies and other
+communication between two entities.  If connection is server to router
+connection, the keys are used to protect all traffic between those
+servers.  In client connections usually all the packets are protected
+with this key except channel messages; channels has their own keys and 
+they are not exchanged with this protocol.
+
+The Diffie-Hellman implementation used in the SILC SHOULD be compliant
+to the PKCS #3.
+
+
+.ti 0
+2.1 Key Exchange Payloads
+
+During the key exchange procedure public data is sent between initiator
+and responder.  This data is later used in the key exchange procedure.
+There are several payloads used in the key exchange.  As for all SILC
+packets, SILC Packet Header, described in [SILC2], is at the start of
+all packets. The same is done with these payloads as well.  All the
+fields in the payloads are always in MSB (most significant byte first)
+order.  Following descriptions of these payloads.
+
+
+.ti 0
+2.1.1 Key Exchange Start Payload
+
+The key exchange between two entities MUST be started by sending the
+SILC_PACKET_KEY_EXCHANGE packet containing Key Exchange Start Payload.
+Initiator sends the Key Exchange Start Payload to the responder filled
+with all security properties it supports.  The responder then checks
+whether it supports the security properties.
+
+It then sends a Key Exchange Start Payload to the initiator filled with
+security properties it selected from the original payload.  The payload
+sent by responder MUST include only one chosen property per list.
+
+The Key Exchange Start Payload is used to tell connecting entities what
+security properties and algorithms should be used in the communication.
+The Key Exchange Start Payload is sent only once per session.  Even if
+the PFS (Perfect Forward Secrecy) flag is set the Key Exchange Start
+Payload is not re-sent.  When PFS is desired the Key Exchange Payloads
+are sent to negotiate new key material.  The procedure is equivalent to
+the very first negotiation except that the Key Exchange Start Payload
+is not sent.
+
+As this payload is used only with the very first key exchange the payload
+is never encrypted, as there are no keys to encrypt it with.
+
+A cookie is also sent in this payload.  A cookie is used to randomize the
+payload so that none of the key exchange parties can determine this
+payload before the key exchange procedure starts.  The cookie MUST be
+returned to the original sender by the responder.
+
+Following diagram represents the Key Exchange Start Payload.  The lists
+mentioned below are always comma (`,') separated and the list MUST NOT
+include spaces (` ').
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   RESERVED    |     Flags     |         Payload Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
++                                                               +  
+|                                                               |
++                            Cookie                             +
+|                                                               |
++                                                               +
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Version String Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Version String                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Key Exchange Grp Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Key Exchange Groups                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        PKCS Alg Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         PKCS Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Encryption Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Encryption Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Hash Alg Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Hash Algorithms                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         HMAC Length           |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                             HMACs                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|    Compression Alg Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                     Compression Algorithms                    ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 1:  Key Exchange Start Payload
+
+
+
+.in 6
+o RESERVED (1 byte) - Reserved field.  Sender fills this with
+  zero (0) value.
+
+o Flags (1 byte) - Indicates flags to be used in the key
+  exchange.  Several flags can be set at once by ORing the
+  flags together.  The following flags are reserved for this
+  field:
+
+     No flags                 0x00
+
+       In this case the field is ignored.
+
+     No Reply                 0x01
+
+       If set the receiver of the payload does not reply to 
+       the packet.
+
+     PFS                      0x02
+
+       Perfect Forward Secrecy (PFS) to be used in the
+       key exchange protocol.  If not set, re-keying
+       is performed using the old key.  See the [SILC1]
+       for more information on this issue.  When PFS is
+       used, re-keying and creating new keys for any
+       particular purpose MUST cause new key exchange.
+       In this key exchange only the Key Exchange Payload
+       is sent and the Key Exchange Start Payload MUST
+       NOT be sent.  When doing PFS the Key Exchange
+       Payloads are encrypted with the old keys.
+
+     Mutual Authentication    0x04
+
+       Both of the parties will perform authentication
+       by providing signed data for the other party to
+       verify.  By default, only responder will provide
+       the signature data.  If this is set then the
+       initiator must also provide it.  Initiator MAY
+       set this but also responder MAY set this even if
+       initiator did not set it.
+
+     Rest of the flags are reserved for the future and
+     MUST NOT be set.
+
+o Payload Length (2 bytes) - Length of the entire Key Exchange
+  Start payload, not including any other field.
+
+o Cookie (16 bytes) - Cookie that randomize this payload so
+  that each of the party cannot determine the payload before
+  hand.
+
+o Version String Length (2 bytes) - The length of the Version
+  String field, not including any other field.
+
+o Version String (variable length) - Indicates the version of
+  the sender of this payload.  Initiator sets this when sending
+  the payload and responder sets this when it replies by sending
+  this payload.  See [SILC1] for definition of the version
+  string format.
+
+o Key Exchange Grp Length (2 bytes) - The length of the
+  key exchange group list, not including any other field.
+
+o Key Exchange Group (variable length) - The list of
+  key exchange groups.  See the section 2.4 SILC Key Exchange
+  Groups for definitions of these groups.
+
+o PKCS Alg Length (2 bytes) - The length of the PKCS algorithms
+  list, not including any other field.
+
+o PKCS Algorithms (variable length) - The list of PKCS 
+  algorithms.
+
+o Encryption Alg Length (2 bytes) - The length of the encryption
+  algorithms list, not including any other field.
+
+o Encryption Algorithms (variable length) - The list of
+  encryption algorithms.
+
+o Hash Alg Length (2 bytes) - The length of the Hash algorithm
+  list, not including any other field.
+
+o Hash Algorithms (variable length) - The list of Hash
+  algorithms.  The hash algorithms are mainly used in the
+  SKE protocol.
+
+o HMAC Length (2 bytes) - The length of the HMAC list, not
+  including any other field.
+
+o HMACs (variable length) - The list of HMACs.  The HMAC's
+  are used to compute the Message Authentication Codes (MAC)
+  of the SILC packets.
+
+o Compression Alg Length (2 bytes) - The length of the
+  compression algorithms list, not including any other field.
+
+o Compression Algorithms (variable length) - The list of 
+  compression algorithms.
+.in 3
+
+
+.ti 0
+2.1.2 Key Exchange Payload
+
+Key Exchange payload is used to deliver the public key (or certificate),
+the computed Diffie-Hellman public value and possibly signature data
+from one party to the other.  When initiator is using this payload
+and the Mutual Authentication flag is not set then the initiator MUST
+NOT provide the signature data.  If the flag is set then the initiator
+MUST provide the signature data so that the responder can verify it.
+
+The Mutual Authentication flag is usually used when a separate 
+authentication protocol will not be executed for the initiator of the
+protocol.  This is case for example when the SKE is performed between
+two SILC clients.  In normal case, where client is connecting to a
+server, or server is connecting to a router the Mutual Authentication
+flag may be omitted.  However, if the connection authentication protocol 
+for the connecting entity is not based on public key authentication (it
+is based on passphrase) then the Mutual Authentication flag SHOULD be 
+enabled.  This way the connecting entity has to provide proof of
+posession of the private key for the public key it will provide in
+SILC Key Exchange protocol.
+
+When performing re-key with PFS selected this is the only payload that
+is sent in the SKE protocol.  The Key Exchange Start Payload MUST NOT
+be sent at all.  However, this payload does not have all the fields
+present.  In the re-key with PFS the public key and a possible signature
+data SHOULD NOT be present.  If they are present they MUST be ignored.
+The only field that is present is the Public Data that is used to create
+the new key material.  In the re-key the Mutual Authentication flag, that
+may be set in the initial negotiation, MUST also be ignored.
+
+This payload is sent inside SILC_PACKET_KEY_EXCHANGE_1 and inside
+SILC_PACKET_KEY_EXCHANGE_2 packet types.  The initiator uses the 
+SILC_PACKET_KEY_EXCHANGE_1 and the responder the latter.
+
+The following diagram represent the Key Exchange Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Data Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Public Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Signature Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Signature Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  Key Exchange Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  Following types are defined:
+
+     1    SILC style public key (mandatory)
+     2    SSH2 style public key (optional)
+     3    X.509 Version 3 certificate (optional)
+     4    OpenPGP certificate (optional)
+     5    SPKI certificate (optional)
+
+  The only required type to support is type number 1.  See 
+  [SILC1] for the SILC public key specification.  See
+  SSH public key specification in [SSH-TRANS].  See X.509v3
+  certificate specification in [PKIX-Part1].  See OpenPGP
+  certificate specification in [PGP].  See SPKI certificate
+  specification in [SPKI].  If this field includes zero (0)
+  or unsupported type number the protocol MUST be aborted
+  sending SILC_PACKET_FAILURE message and the connection SHOULD
+  be closed immediately.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+
+o Public Data Length (2 bytes) - The length of the Public Data
+  field, not including any other field.
+
+o Public Data (variable length) - The public data to be
+  sent to the receiver.  See section 2.2 Key Exchange 
+  Procedure for detailed description how this field is
+  computed.  This value is binary encoded.
+
+o Signature Length (2 bytes) - The length of the signature,
+  not including any other field.
+
+o Signature Data (variable length) - The signature signed
+  by the sender.  The receiver of this signature MUST
+  verify it.  The verification is done using the sender's
+  public key.  See section 2.2 Key Exchange Procedure for
+  detailed description how to produce the signature.  If
+  the Mutual Authentication flag is not set then initiator
+  MUST NOT provide this field and the Signature Length field
+  MUST be set to zero (0) value.  If the flag is set then
+  also the initiator MUST provide this field.  The responder
+  MUST always provide this field.
+.in 3
+
+
+.ti 0
+2.2 Key Exchange Procedure
+
+The key exchange begins by sending SILC_PACKET_KEY_EXCHANGE packet with
+Key Exchange Start Payload to select the security properties to be used
+in the key exchange and later in the communication.
+
+After Key Exchange Start Payload has been processed by both of the
+parties the protocol proceeds as follows:
+
+
+Setup:  p is a large and public safe prime.  This is one of the
+        Diffie Hellman groups.  q is order of subgroup (largest
+        prime factor of p).  g is a generator and is defined
+        along with the Diffie Hellman group.
+
+    1.  Initiator generates a random number x, where 1 < x < q, 
+        and computes e = g ^ x mod p.  The result e is then 
+        encoded into Key Exchange Payload, with the public key
+        (or certificate) and sent to the responder.
+
+        If the Mutual Authentication flag is set then initiator
+        MUST also produce signature data SIGN_i which the responder
+        will verify.  The initiator MUST compute a hash value
+        HASH_i = hash(Key Exchange Start Payload | public key
+        (or certificate) | e).  It then signs the HASH_i value with
+        its private key resulting a signature SIGN_i.
+
+    2.  Responder generates a random number y, where 1 < y < q,
+        and computes f = g ^ y mod p.  It then computes the
+        shared secret KEY = e ^ y mod p, and, a hash value 
+        HASH = hash(Key Exchange Start Payload data | public 
+        key (or certificate) | Initiator's public key (or
+        certificate) | e | f | KEY).  It then signs
+        the HASH value with its private key resulting a signature
+        SIGN.  
+
+        It then encodes its public key (or certificate), f and 
+        SIGN into Key Exchange Payload and sends it to the 
+        initiator.
+
+        If the Mutual Authentication flag is set then the responder
+        SHOULD verify that the public key provided in the payload
+        is authentic, or if certificates are used it verifies the
+        certificate.  The responder MAY accept the public key without
+        verifying it, however, doing so may result to insecure key
+        exchange (accepting the public key without verifying may be
+        desirable for practical reasons on many environments.  For
+        long term use this is never desirable, in which case
+        certificates would be the preferred method to use).  It then
+        computes the HASH_i value the same way initiator did in the
+        phase 1.  It then verifies the signature SIGN_i from the
+        payload with the hash value HASH_i using the received public
+        key.
+
+    3.  Initiator verifies that the public key provided in
+        the payload is authentic, or if certificates are used
+        it verifies the certificate.  The initiator MAY accept
+        the public key without verifying it, however, doing
+        so may result to insecure key exchange (accepting the
+        public key without verifying may be desirable for 
+        practical reasons on many environments.  For long term
+        use this is never desirable, in which case certificates
+        would be the preferred method to use).
+
+        Initiator then computes the shared secret KEY = 
+        f ^ x mod p, and, a hash value HASH in the same way as
+        responder did in phase 2.  It then verifies the 
+        signature SIGN from the payload with the hash value
+        HASH using the received public key.
+
+
+If any of these phases is to fail the SILC_PACKET_FAILURE MUST be sent
+to indicate that the key exchange protocol has failed, and the connection
+SHOULD be closed immediately.  Any other packets MUST NOT be sent or
+accepted during the key exchange except the SILC_PACKET_KEY_EXCHANGE_*,
+SILC_PACKET_FAILURE and SILC_PACKET_SUCCESS packets.
+
+The result of this protocol is a shared secret key material KEY and
+a hash value HASH.  The key material itself is not fit to be used as 
+a key, it needs to be processed further to derive the actual keys to be
+used.  The key material is also used to produce other security parameters
+later used in the communication.  See section 2.3 Processing the Key
+Material for detailed description how to process the key material.
+
+If the Mutual Authentication flag was set the protocol produces also
+a hash value HASH_i.  This value, however, must be discarded.
+
+After the keys are processed the protocol is ended by sending the
+SILC_PACKET_SUCCESS packet.  Both entities send this packet to 
+each other.  After this both parties will start using the new keys.
+
+
+.ti 0
+2.3 Processing the Key Material
+
+Key Exchange protocol produces secret shared key material KEY.  This
+key material is used to derive the actual keys used in the encryption
+of the communication channel.  The key material is also used to derive
+other security parameters used in the communication.  Key Exchange
+protocol produces a hash value HASH as well.
+
+The keys MUST be derived from the key material as follows:
+
+.in 6
+Sending Initial Vector (IV)     = hash(0 | KEY | HASH)
+Receiving Initial Vector (IV)   = hash(1 | KEY | HASH)
+Sending Encryption Key          = hash(2 | KEY | HASH)
+Receiving Encryption Key        = hash(3 | KEY | HASH)
+Sending HMAC Key                = hash(4 | KEY | HASH)
+Receiving HMAC Key              = hash(5 | KEY | HASH)
+.in 3
+
+
+The Initial Vector (IV) is used in the encryption when doing for
+example CBC mode.  As many bytes as needed are taken from the start of
+the hash output for IV.  Sending IV is for sending key and receiving IV
+is for receiving key.  For receiving party, the receiving IV is actually
+sender's sending IV, and, the sending IV is actually sender's receiving
+IV.  Initiator uses IV's as they are (sending IV for sending and
+receiving IV for receiving).
+
+The Encryption Keys are derived as well from the hash().  If the hash()
+output is too short for the encryption algorithm more key material MUST
+be produced in the following manner:
+
+.in 6
+K1 = hash(2 | KEY | HASH)
+K2 = hash(KEY | HASH | K1)
+K3 = hash(KEY | HASH | K1 | K2)  ...
+
+Sending Encryption Key = K1 | K2 | K3 ...
+
+
+K1 = hash(3 | KEY | HASH)
+K2 = hash(KEY | HASH | K1)
+K3 = hash(KEY | HASH | K1 | K2)  ...
+
+Receiving Encryption Key = K1 | K2 | K3 ...
+.in 3
+
+
+The key is distributed by hashing the previous hash with the original
+key material.  The final key is a concatenation of the hash values.
+For Receiving Encryption Key the procedure is equivalent.  Sending key
+is used only for encrypting data to be sent.  The receiving key is used
+only to decrypt received data.  For receiving party, the receive key is
+actually sender's sending key, and, the sending key is actually sender's
+receiving key.  Initiator uses generated keys as they are (sending key
+for sending and receiving key for receiving).
+
+The HMAC keys are used to create MAC values to packets in the
+communication channel.  As many bytes as needed are taken from the start
+of the hash output to generate the MAC keys.
+
+These procedures are performed by all parties of the key exchange
+protocol.  This MUST be done before the protocol has been ended by
+sending the SILC_PACKET_SUCCESS packet.
+
+This same procedure is used in the SILC in some other circumstances
+as well.  Any changes to this procedure is mentioned separately when
+this procedure is needed.  See the [SILC1] and the [SILC2] for these
+circumstances.
+
+
+.ti 0
+2.4 SILC Key Exchange Groups
+
+The Following groups may be used in the SILC Key Exchange protocol.
+The first group diffie-hellman-group1 is REQUIRED, other groups MAY be 
+negotiated to be used in the connection with Key Exchange Start Payload
+and SILC_PACKET_KEY_EXCHANGE packet.  However, the first group MUST be
+proposed in the Key Exchange Start Payload regardless of any other
+requested group (however, it does not have to be the first in the list).
+
+
+.ti 0
+2.4.1 diffie-hellman-group1
+
+The length of this group is 1024 bits.  This is REQUIRED group.
+The prime is 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
+
+Its decimal value is
+
+.in 6
+179769313486231590770839156793787453197860296048756011706444
+423684197180216158519368947833795864925541502180565485980503
+646440548199239100050792877003355816639229553136239076508735
+759914822574862575007425302077447712589550957937778424442426
+617334727629299387668709205606050270810842907692932019128194
+467627007
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
+FFFFFFFF FFFFFFFF
+.in 3
+
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.4.2 diffie-hellman-group2
+
+The length of this group is 1536 bits.  This is OPTIONAL group.
+The prime is 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }.
+
+Its decimal value is
+
+.in 6
+241031242692103258855207602219756607485695054850245994265411
+694195810883168261222889009385826134161467322714147790401219
+650364895705058263194273070680500922306273474534107340669624
+601458936165977404102716924945320037872943417032584377865919
+814376319377685986952408894019557734611984354530154704374720
+774996976375008430892633929555996888245787241299381012913029
+459299994792636526405928464720973038494721168143446471443848
+8520940127459844288859336526896320919633919
+.in 3
+
+Its hexadecimal value is
+
+.in 6
+FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF
+.in 3
+
+The generator used with this prime is g = 2.  The group order q is
+(p - 1) / 2.
+
+This group was taken from the OAKLEY specification.
+
+
+.ti 0
+2.5 Key Exchange Status Types
+
+This section defines all key exchange protocol status types that may
+be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_SKE_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+.in 6
+0   SILC_SKE_STATUS_OK
+
+    Protocol were executed successfully.
+
+
+1   SILC_SKE_STATUS_ERROR
+
+    Unknown error occurred.  No specific error type is defined.
+
+
+2   SILC_SKE_STATUS_BAD_PAYLOAD
+
+    Provided KE payload were malformed or included bad fields.
+
+
+3   SILC_SKE_STATUS_UNSUPPORTED_GROUP
+
+    None of the provided groups were supported.
+
+
+4   SILC_SKE_STATUS_UNSUPPORTED_CIPHER
+
+    None of the provided ciphers were supported.
+
+
+5   SILC_SKE_STATUS_UNSUPPORTED_PKCS
+
+    None of the provided public key algorithms were supported.
+
+
+6   SILC_SKE_STATUS_UNSUPPORTED_HASH_FUNCTION
+
+    None of the provided hash functions were supported.
+
+
+7   SILC_SKE_STATUS_UNSUPPORTED_HMAC
+
+    None of the provided HMACs were supported.
+
+
+8   SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY
+
+    Provided public key type is not supported.
+
+
+9   SILC_SKE_STATUS_INCORRECT_SIGNATURE
+
+    Provided signature was incorrect.
+
+
+10  SILC_SKE_STATUS_BAD_VERSION
+
+    Provided version string was not acceptable.
+
+11  SILC_SKE_STATUS_INVALID_COOKIE
+
+    The cookie in the Key Exchange Start Payload was malformed,
+    because responder modified the cookie.
+.in 3
+
+
+.ti 0
+3 SILC Connection Authentication Protocol
+
+Purpose of Connection Authentication protocol is to authenticate the
+connecting party with server.  Usually connecting party is client but
+server may connect to router server as well.  Its other purpose is to
+provide information for the server about which type of connection this
+is.  The type defines whether this is client, server or router
+connection.  Server uses this information to create the ID for the
+connection.
+
+After the authentication protocol has been successfully completed
+SILC_PACKET_NEW_ID must be sent to the connecting client by the server.
+See the [SILC1] for the details of the connecting procedure.
+
+Server MUST verify the authentication data received and if it is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+If everything checks out fine the protocol is ended by server by sending
+SILC_PACKET_SUCCESS packet.
+
+The protocol is executed after the SILC Key Exchange protocol.  It MUST
+NOT be executed in any other time.  As it is performed after key exchange
+protocol all traffic in the connection authentication protocol is
+encrypted with the exchanged keys.
+
+The protocol MUST be started by the connecting party by sending the
+SILC_PACKET_CONNECTION_AUTH packet with Connection Auth Payload,
+described in the next section.  This payload MUST include the
+authentication data.  The authentication data is set according
+authentication method that MUST be known by both parties.  If connecting
+party does not know what is the mandatory authentication method it MAY
+request it from the server by sending SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  This packet is not part of this protocol and is described in
+section Connection Auth Request Payload in [SILC2].  However, if
+connecting party already knows the mandatory authentication method
+sending the request is not necessary.
+
+See [SILC1] and section Connection Auth Request Payload in [SILC2] also
+for the list of different authentication methods.  Authentication method
+MAY also be NONE, in which case the server does not require
+authentication at all.  However, in this case the protocol still MUST be
+executed; the authentication data just is empty indicating no
+authentication is required.
+
+If authentication method is passphrase the authentication data is
+plaintext passphrase.  As the payload is entirely encrypted it is safe
+to have plaintext passphrase.  See the section 3.2.1 Passphrase
+Authentication for more information.
+
+If authentication method is public key authentication the authentication
+data is a signature of the hash value of hash HASH plus Key Exchange
+Start Payload, established by the SILC Key Exchange protocol.  This
+signature MUST then be verified by the server.  See the section 3.2.2
+Public Key Authentication for more information.
+
+The connecting client of this protocol MUST wait after successful execution
+of this protocol for the SILC_PACKET_NEW_ID packet where it will receive
+the ID it will be using in the SILC network.  The connecting client cannot
+start normal SILC session (sending messages or commands) until it has
+received its ID.  The ID's are always created by the server except
+for server to router connection where servers create their own ID's.
+
+
+.ti 0
+3.1 Connection Auth Payload
+
+Client sends this payload to authenticate itself to the server.  Server
+connecting to another server also sends this payload.  Server receiving
+this payload MUST verify all the data in it and if something is to fail
+the authentication MUST be failed by sending SILC_PACKET_FAILURE packet.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represent the Connection Auth Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |        Connection Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                     Authentication Data                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 3:  Connection Auth Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire Connection 
+  Auth Payload.
+
+o Connection Type (2 bytes) - Indicates the type of the 
+  connection.  See section Connection Auth Request Payload
+  in [SILC2] for the list of connection types.  This field MUST
+  include valid connection type or the packet MUST be discarded
+  and authentication MUST be failed. 
+
+o Authentication Data (variable length) - The actual 
+  authentication data.  Contents of this depends on the 
+  authentication method known by both parties.  If no
+  authentication is required this field does not exist.
+.in 3
+
+
+
+
+.ti 0
+3.2 Connection Authentication Types
+
+SILC supports two authentication types to be used in the connection
+authentication protocol; passphrase or public key based authentication.
+The following sections defines the authentication methods.  See [SILC2]
+for defined numerical authentication method types.
+
+
+.ti 0
+3.2.1 Passphrase Authentication
+
+Passphrase authentication or pre-shared-key based authentication is 
+simply an authentication where the party that wants to authenticate 
+itself to the other end sends the passphrase that is required by
+the other end, for example server.
+
+If the passphrase matches with the one in the server's end the
+authentication is successful.  Otherwise SILC_PACKET_FAILURE MUST be
+sent to the sender and the protocol execution fails.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+When password authentication is used it is RECOMMENDED that maximum
+amount of padding is applied to the SILC packet.  This way it is not
+possible to approximate the length of the password from the encrypted
+packet.
+
+
+.ti 0
+3.2.2 Public Key Authentication
+
+Public key authentication may be used if passphrase based authentication
+is not desired.  The public key authentication works by sending a
+signature as authentication data to the other end, say, server.  The
+server MUST then verify the signature by the public key of the sender,
+which the server has received earlier in SKE protocol.
+
+The signature is computed using the private key of the sender by signing
+the HASH value provided by the SKE protocol previously, and the Key
+Exchange Start Payload from SKE protocol that was sent to the server.
+These are concatenated and hash function is used to compute a hash value
+which is then signed.
+
+  auth_hash = hash(HASH | Key Exchange Start Payload);
+  signature = sign(auth_hash);
+
+The hash() function used to compute the value is the hash function
+negotiated in the SKE protocol.  The server MUST verify the data, thus
+it must keep the HASH and the Key Exchange Start Payload saved during
+SKE and authentication protocols.
+
+If the verified signature matches the sent signature, the authentication
+were successful and SILC_PACKET_SUCCESS is sent.  If it failed the
+protocol execution is stopped and SILC_PACKET_FAILURE is sent.
+
+This is REQUIRED authentication method to be supported by all SILC
+implementations.
+
+
+.ti 0
+3.3 Connection Authentication Status Types
+
+This section defines all connection authentication status types that
+may be returned in the SILC_PACKET_SUCCESS or SILC_PACKET_FAILURE packets
+to indicate the status of the protocol.  Implementations may map the
+status types to human readable error message.  All types except the
+SILC_AUTH_STATUS_OK type MUST be sent in SILC_PACKET_FAILURE packet.
+The length of status is 32 bits (4 bytes).  The following status types
+are defined:
+
+
+
+0   SILC_AUTH_OK
+
+    Protocol was executed successfully.
+
+
+1   SILC_AUTH_FAILED
+
+    Authentication failed.
+
+
+.ti 0
+4 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+5 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+6 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires XXX
index 7f907438a7c05785d7ac793be51376c7cfd65963..6aca37fb1339f1dc64b4acc5f4dfc47c4caf9cbc 100644 (file)
@@ -8,43 +8,44 @@
 .ds RF FORMFEED[Page %]
 .ds CF
 .ds LH Internet Draft
-.ds RH 27 June 2000
-.ds CH SILC Packet Protocol
+.ds RH 13 September 2000
+.ds CH
 .na
 .hy 0
 .in 0
 .nf
 Network Working Group                                      P. Riikonen
 Internet-Draft
-draft-riikonen-silc-pp-00.txt                             27 June 2000
-Expires: 27 Jan 2001
+draft-riikonen-silc-pp-00.txt                        13 September 2000
+Expires: 13 May 2001
 
 .in 3
 
-.ce
+.ce 2
 SILC Packet Protocol
+<draft-riikonen-silc-pp-00.txt>
 
 .ti 0
 Status of this Memo
 
-This document is an Internet-Draft.  Internet-Drafts are working
-documents of the Internet Engineering Task Force (IETF), its areas,
-and its working groups.  Note that other groups may also distribute
-working documents as Internet-Drafts.
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
 
-Internet-Drafts are draft documents valid for a maximum of six
-months and may be updated, replaced, or obsoleted by other 
-documents at any time. It is inappropriate to use Internet-Drafts  
-as reference material or to cite them other than as 
-``work in progress.''
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
 
-To learn the current status of any Internet-Draft, please check the
-``1id-abstracts.txt'' listing contained in the Internet-Drafts
-Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe),
-munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or
-ftp.isi.edu (US West Coast).
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
 
-The distribution of this memo is unlimited.
+The distribution of this memo is unlimited.  
 
 
 .ti 0
@@ -110,9 +111,10 @@ Table of Contents
   2.8 Packet Compression ........................................ 40
   2.9 Packet Sending ............................................ 40
   2.10 Packet Reception ......................................... 41
-  2.11 Packet Broadcasting ...................................... 41
-  2.12 Packet Routing ........................................... 42
-  2.13 Packet Tunneling ......................................... 42
+  2.11 Packet Routing ........................................... 42
+  2.12 Packet Forwarding ........................................
+  2.13 Packet Broadcasting ...................................... 41
+  2.14 Packet Tunneling ......................................... 42
 3 Security Considerations ....................................... 43
 4 References .................................................... 43
 5 Author's Address .............................................. 44
@@ -143,6 +145,7 @@ Figure 19:  New Channel Payload
 Figure 20:  New Channel User Payload
 Figure 21:  Replace ID Payload
 Figure 22:  Remove ID Payload
+Figure 23:  Remove Channel User Payload
 
 
 .ti 0
@@ -296,7 +299,16 @@ o Flags (1 byte) - Indicates flags to be used in packet
        Encryption And Decryption for more information.
 
 
-     Broadcast                 0x02
+     Forwarded                 0x02
+  
+       Marks the packet to be forwarded.  Some specific
+       packet types may be forwarded.  Receiver of packet
+       with this flag set must not forward the packet any
+       further.  See section 2.12 Packet Forwarding for
+       desribtion of packet forwarding.
+
+
+     Broadcast                 0x04
 
        Marks the packet to be broadcasted.  Client cannot
        send broadcast packet and normal server cannot send
@@ -305,16 +317,16 @@ o Flags (1 byte) - Indicates flags to be used in packet
        set must send (broadcast) the packet to its primary
        route.  If router has several router connections the
        packet may be sent only to the primary route.  See
-       section 2.11 Packet Broadcasting for description of 
+       section 2.13 Packet Broadcasting for description of 
        packet broadcasting.
 
 
-     Tunneled                  0x04
+     Tunneled                  0x08
 
        Marks that the packet is tunneled.  Tunneling means
        that extra SILC Packet Header has been applied to the
        original packet.  The outer header has this flag
-       set.  See section 2.13 Packet Tunneling for more
+       set.  See section 2.14 Packet Tunneling for more
        information.
 .in 3
 
@@ -437,7 +449,8 @@ List of SILC Packet types are defined as follows.
           This packet is sent when an error occurs.  Server may
           send this packet.  Client never sends this packet.  The
           client may entirely ignore the packet, however, server is
-          most likely to take action anyway.
+          most likely to take action anyway.  This packet may be sent
+          to entity that is indirectly connected to the sender.
 
           Payload of the packet:  See section 2.3.7 Error Payload.
 
@@ -709,7 +722,18 @@ List of SILC Packet types are defined as follows.
           Payload of the packet:  See section 2.3.24 Remove ID Payload
 
 
-     28   SILC_PACKET_REKEY
+     28   SILC_PACKET_REMOVE_CHANNEL_USER
+
+          This packet is used to remove user from a channel.  This is
+          used by router to notify other routers in the network that a
+          client has leaved a channel.  This packet maybe sent to entity
+          that is indirectly connected to the sender.
+
+          Payload of the packet:  See section 2.3.25 Remove Channel User
+                                  Payload
+
+
+     29   SILC_PACKET_REKEY
 
           This packet is used to indicate that re-key must be performed
           for session keys.  See section Session Key Regeneration in
@@ -717,9 +741,7 @@ List of SILC Packet types are defined as follows.
           a payload.
 
 
-
-
-     29   SILC_PACKET_REKEY_DONE
+     30   SILC_PACKET_REKEY_DONE
 
           This packet is used to indicate that re-key is performed and
           new keys must be used hereafter.  This is sent only if re-key
@@ -728,7 +750,7 @@ List of SILC Packet types are defined as follows.
           not have a payload.
 
 
-     30 - 254
+     31 - 254
 
          Currently undefined commands.
 
@@ -833,6 +855,7 @@ o Success Indication (variable length) - Indication of
 This is opposite of Success Payload.  Indication of failure of
 some protocol is sent in the payload.
 
+
 .in 5
 .nf
                      1                   2                   3
@@ -892,6 +915,9 @@ o Reject Indication (variable length) - Indication of
 .in 3
 
 
+
+
+
 .ti 0
 2.3.6 Notify Payload
 
@@ -905,15 +931,13 @@ The payload may only be sent with SILC_PACKET_NOTIFY packet.  It must
 not be sent in any other packet type.  Following diagram represents the
 Notify Payload.
 
-
-
-
-
 .in 5
 .nf
                      1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|          Notify Type          |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
 |                                                               |
 ~                        Notify Message                         ~
 |                                                               |
@@ -925,6 +949,9 @@ Figure 7:  Notify Payload
 
 
 .in 6
+o Notify Type (2 bytes) - Indicates the type of the notify
+  message.
+
 o Notify Message (variable length) - Human readable notify
   message.
 .in 3
@@ -981,7 +1008,7 @@ ever is larger.
 
 The SILC header in this packet is encrypted with the session key
 of the next receiver of the packet.  Nothing else is encrypted
-with that key.  Hence, the actual packet and padding to be
+with that key.  Thus, the actual packet and padding to be
 encrypted with the session key is SILC Header plus padding to it
 to make it multiple by eight (8) or multiple by the block size
 of the cipher, which ever is larger.
@@ -990,12 +1017,12 @@ Receiver of the the channel message packet is able to determine
 the channel the message is destined to by checking the destination
 ID from the SILC Packet header which tells the destination channel.
 The original sender of the packet is also determined by checking
-the source ID from the header which tells the who client sent
+the source ID from the header which tells the client who sent
 the message.
 
 The payload may only be sent with SILC_PACKET_CHANNEL_MESSAGE packet.
 It  must not be sent in any other packet type.  Following diagram 
-represents the Notify Payload.
+represents the Channel Message Payload.
 
 (*) indicates that the field is not encrypted.
 
@@ -1265,7 +1292,7 @@ flag into SILC Packet Header.
 
 The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE_KEY 
 packet.  It must not be sent in any other packet type.  Following 
-diagram represents the Private Message Payload.
+diagram represents the Private Message Key Payload.
 
 
 .in 5
@@ -1301,8 +1328,9 @@ o Private Message Key (variable length) - The actual private
 .ti 0
 2.3.12 Command Payload
 
-Command Payload is used to send SILC commands from client to server.  
-Following diagram represents the Command Payload.
+Command Payload is used to send SILC commands from client to server.
+Also server may send commands to other servers.  Following diagram
+represents the Command Payload.
 
 
 .in 5
@@ -1310,8 +1338,10 @@ Following diagram represents the Command Payload.
                      1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-| SILC Command  | Arguments Num |         Payload Length        |
+|         Payload Length        | SILC Command  | Arguments Num |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Command Unifier        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 .in 3
 
 .ce
@@ -1319,6 +1349,10 @@ Figure 13:  Command Payload
 
 
 .in 6
+o Payload Length (2 bytes) - Length of the entire command 
+  payload including any command argument payloads associated 
+  with this payload.
+
 o SILC Command (1 byte) - SILC Command identifier.  This must 
   be set to non-zero value.  If zero (0) value is found in this 
   field the packet must be discarded.
@@ -1328,9 +1362,14 @@ o Arguments Num (1 byte) - Indicates the number of arguments
   field is set to zero (0).  The arguments must follow the 
   command payload.
 
-o Payload Length (2 bytes) - Length of the entire command 
-  payload including any command argument payloads associated 
-  with this payload.
+o Command Unifier (2 bytes) - Unifies this command at the
+  sender's end.  The entity who replies to this command must
+  set the value found from this field into the Command Payload
+  used to send the reply to the sender.  This way the sender
+  can identify which command reply belongs to which originally
+  sent command.  What this field includes is implementation
+  issue but it is recommended that wrapping counter value is
+  used in the field.
 .in 3
 
 See [SILC1] for detailed description of different SILC commands,
@@ -1394,11 +1433,15 @@ o Argument Data (variable length) - Argument data.
 .ti 0
 2.3.13 Command Reply Payload
 
-Command Reply Payload is used to send replies to the commands sent
-by the client.  The Command Reply Payload is identical to the
-Command Payload hence see the upper sections for Command Payload
-and for Command Argument Payload specifications.  Command Reply
-message uses the Command Argument Payload as well.
+Command Reply Payload is used to send replies to the commands.  The
+Command Reply Payload is identical to the Command Payload thus see the
+upper sections for Command Payload and for Command Argument Payload
+specifications.  Command Reply message uses the Command Argument Payload
+as well.
+
+The entity who sends the reply packet must set the Command Unifier
+field in the reply packet's Command Payload to the value it received
+in the original command packet.
 
 See SILC Commands in [SILC1] for detailed description of different
 SILC commands, their arguments and their reply messages.
@@ -1676,7 +1719,7 @@ Information about newly created channel is broadcasted to all routers
 in the SILC network by sending this packet payload.  Channels are
 created by router of the cell.  Server never creates channels unless
 it is a standalone server and it does not have router connection,
-in this case server acts as router.  Normal server sends JOIN command
+in this case server acts as router.  Normal server forwards JOIN command
 to the router (after it has received JOIN command from client) which
 then processes the command and creates the channel.  Client never sends
 this packet.
@@ -1912,13 +1955,65 @@ o ID Type (2 bytes) - Indicates the type of the ID to be
   removed.  See section 2.4 SILC ID Types for list of defined
   ID types.
 
-o ID Length (2 bytes) - Length of the D Data area not including
+o ID Length (2 bytes) - Length of the ID Data area not including
   the length of any other fields in the payload.
 
 o ID Data (variable length) - The actual ID data to be removed.
 .in 3
 
 
+.ti 0
+2.3.25 Remove Channel User Payload
+
+Remove Channel User payload is used to remove a user from a channel network
+wide.  This is used by routers to notify other routers that a user has
+leaved a channel.  As routers keep information about users on channels a
+user leaving channel must be removed from all routers.  Normal server may
+send this payload as well.  Client must not send this payload.
+
+The payload may only be sent with SILC_PACKET_REMOVE_CHANNEL USER packet.
+It must not be sent in any other packet type.  Following diagram
+represents the Remove Payload Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Client ID Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Client ID Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel ID Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Channel ID Data                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 23:  Remove Channel User Payload
+
+
+.in 6
+o Client ID Length (2 bytes) - Length of the Client ID Data area
+  not including the length of any other fields in the payload.
+
+o Client ID Data (variable length) - The Client ID of the user
+  that has left the channel.
+
+o Channel ID Length (2 bytes) - Length of the Channel ID Data area
+  not including the length of any other fields in the payload.
+
+o Channel ID Data (variable length) - The Channel ID of the channel
+  the user has left.
+.in 3
+
+
 .ti 0
 2.4 SILC ID Types
 
@@ -2141,10 +2236,10 @@ server or router en route must not decompress the packet.
 
 The sender of the packet must assemble the SILC Packet Header with
 correct values.  It must set the Source ID of the header as its own
-ID.  It must also set the Destination ID of the header to the true
-destination.  If the destination is client it will be Client ID, if
-it is server it will be Server ID and if it is channel it will be
-Channel ID.
+ID, unless it is forwarding the packet.  It must also set the Destination
+ID of the header to the true destination.  If the destination is client
+it will be Client ID, if it is server it will be Server ID and if it is
+channel it will be Channel ID.
 
 If the sender wants to compress the packet it must apply the
 compression now.  Sender must also compute the padding as described
@@ -2177,34 +2272,7 @@ special packet types and their parsing.
 
 
 .ti 0
-2.11 Packet Broadcasting
-
-SILC packets may be broadcasted in SILC network.  However, only router
-server may send or receive broadcast packets.  Client and normal server
-must not send broadcast packets and they must ignore broadcast packets
-if they receive them.  Broadcast packets are sent by setting Broadcast
-flag to the SILC packet header.
-
-Broadcasting packets means that the packet is sent to all routers in
-the SILC network, except to the router that sent the packet.  The router
-receiving broadcast packet must send the packet to its primary route.
-The fact that SILC routers may have several router connections may
-cause problems, such as race conditions inside the SILC network, if
-care is not taken when broadcasting packets.  Router must not send
-the broadcast packet to any other route except to its primary route.
-
-If the primary route of the router is the original sender of the packet
-the packet must not be sent to the primary route.  This may happen
-if router has several router connections and some other router uses
-the router as its primary route.
-
-Routers use broadcast packets to broadcast for example information
-about newly registered clients, servers, channels etc. so that all the
-routers may keep these informations up to date.
-
-
-.ti 0
-2.12 Packet Routing
+2.11 Packet Routing
 
 Routers are the primary entities in the SILC network that takes care
 of packet routing.  However, normal servers routes packets as well, for
@@ -2236,7 +2304,62 @@ directly connected to the server.
 
 
 .ti 0
-2.13 Packet Tunneling
+2.12 Packet Forwarding
+
+Currently SILC command packets may be forwarded from one entity to another.
+Any other packet currently cannot be forwarded but support for more packet
+types may be added if needed.  Forwarding is usually used by server to
+forward some command request coming from client to the router as the server
+may be incapable to handle the request.  Forwarding may be only one hop
+long; the receiver of the packet with Forwarded flag set in the SILC   
+Packet header must not forward the packet any further.
+
+The normal scenario is that client sends JOIN command to the server which
+is not able to create the channel as there are no local clients on the
+channel.  Channels are created always by the router of the cell thus the
+packet must be forwarded to the router.  The server forwards the original
+packet coming from client to the router after it has set the Forwarded
+flag to the SILC Packet header.
+
+Router receiving the packet knows that the packet has to be processed
+specially by checking the flags and the Forwarded flag in the SILC Packet
+header.  After router has joined the client to the channel (and perhaps
+created a new channel) it sends normal command reply packet to the
+client.  However, as the router doesn't have direct connection to the
+client the packet is sent through the server.  Server detects that 
+the command reply packet is destined to the client and sends it to
+the client.
+
+
+.ti 0
+2.13 Packet Broadcasting
+
+SILC packets may be broadcasted in SILC network.  However, only router
+server may send or receive broadcast packets.  Client and normal server
+must not send broadcast packets and they must ignore broadcast packets
+if they receive them.  Broadcast packets are sent by setting Broadcast
+flag to the SILC packet header.
+
+Broadcasting packets means that the packet is sent to all routers in
+the SILC network, except to the router that sent the packet.  The router
+receiving broadcast packet must send the packet to its primary route.
+The fact that SILC routers may have several router connections may
+cause problems, such as race conditions inside the SILC network, if
+care is not taken when broadcasting packets.  Router must not send
+the broadcast packet to any other route except to its primary route.
+
+If the primary route of the router is the original sender of the packet
+the packet must not be sent to the primary route.  This may happen
+if router has several router connections and some other router uses
+the router as its primary route.
+
+Routers use broadcast packets to broadcast for example information
+about newly registered clients, servers, channels etc. so that all the
+routers may keep these informations up to date.
+
+
+.ti 0
+2.14 Packet Tunneling
 
 Tunneling is a feature that is available in SILC protocol.  Tunneling
 means that extra SILC Packet Header is applied to the original packet
@@ -2315,3 +2438,5 @@ Kasarmikatu 11 A4
 Finland
 
 EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 13 May 2001
diff --git a/doc/draft-riikonen-silc-pp-01.nroff b/doc/draft-riikonen-silc-pp-01.nroff
new file mode 100644 (file)
index 0000000..da02ad4
--- /dev/null
@@ -0,0 +1,2655 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 6 October 2000
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-pp-01.txt                           6 October 2000
+Expires: 6 Jun 2001
+
+.in 3
+
+.ce 2
+SILC Packet Protocol
+<draft-riikonen-silc-pp-01.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Packet Protocol used in the Secure Internet Live
+Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+2 SILC Packet Protocol ..........................................  4
+  2.1 SILC Packet ...............................................  4
+  2.2 SILC Packet Header ........................................  5
+  2.3 SILC Packet Types .........................................  7
+      2.3.1 SILC Packet Payloads ................................ 15
+      2.3.2 Generic payloads .................................... 16
+            2.3.2.1 ID Payload .................................. 16
+            2.3.2.2 Argument Payload ............................ 16
+            2.3.2.3 Channel Payload ............................. XXX
+      2.3.3 Disconnect Payload .................................. 17
+      2.3.4 Success Payload ..................................... 18
+      2.3.5 Failure Payload ..................................... 18
+      2.3.6 Reject Payload ...................................... 19
+      2.3.7 Notify Payload ...................................... 20
+      2.3.8 Error Payload ....................................... 21
+      2.3.9 Channel Message Payload ............................. 22
+      2.3.10 Channel Key Payload ................................ 24
+      2.3.11 Private Message Payload ............................ 26
+      2.3.12 Private Message Key Payload ........................ 27
+      2.3.13 Command Payload .................................... 28
+      2.3.14 Command Reply Payload .............................. 29
+      2.3.15 Connection Auth Request Payload .................... 29
+      2.3.16 New ID Payload ..................................... 30
+      2.3.17 New Client Payload ................................. 31
+      2.3.18 New Server Payload ................................. 32
+      2.3.19 New Channel Payload ................................ 33
+      2.3.20 Key Agreement Payload .............................. XXX
+  2.4 SILC ID Types ............................................. 39
+  2.5 Packet Encryption And Decryption .......................... 39
+      2.5.1 Normal Packet Encryption And Decryption ............. 39
+      2.5.2 Channel Message Encryption And Decryption ........... 40
+      2.5.3 Private Message Encryption And Decryption ........... 41
+  2.6 Packet MAC Generation ..................................... 41
+  2.7 Packet Padding Generation ................................. 42
+  2.8 Packet Compression ........................................ 42
+  2.9 Packet Sending ............................................ 43
+  2.10 Packet Reception ......................................... 43
+  2.11 Packet Routing ........................................... 44
+  2.12 Packet Broadcasting ...................................... 45
+  2.13 Packet Tunneling ......................................... 45
+3 Security Considerations ....................................... 46
+4 References .................................................... 46
+5 Author's Address .............................................. 47
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:   Typical SILC Packet
+Figure 2:   SILC Packet Header
+Figure 3:   ID Payload
+Figure 4:   Argument Payload
+Figure 5:   Channel Payload
+Figure 6:   Disconnect Payload
+Figure 7:   Success Payload
+Figure 8:   Failure Payload
+Figure 9:   Reject Payload
+Figure 10:  Notify Payload
+Figure 11:  Error Payload
+Figure 12:  Channel Message Payload
+Figure 13:  Channel Key Payload
+Figure 14:  Private Message Payload
+Figure 15:  Private Message Key Payload
+Figure 16:  Command Payload
+Figure 17:  Connection Auth Request Payload
+Figure 18:  New Client Payload
+Figure 19:  New Server Payload
+Figure 20:  Key Agreement Payload
+Figure 21:  Cell Routers Payload
+
+
+.ti 0
+1. Introduction
+
+This document describes a Packet Protocol used in the Secure Internet
+Live Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+The basis of SILC protocol relies in the SILC packets and it is with
+out a doubt the most important part of the protocol.  It is also probably
+the most complicated part of the protocol.  Packets are used all the
+time in the SILC network to send messages, commands and other information.
+All packets in SILC network are always encrypted and their integrity
+is assured by computed MACs.  The protocol defines several packet types
+and packet payloads.  Each packet type usually has a specific packet
+payload that actually defines the contents of the packet.  Each packet
+also includes a default SILC Packet Header that provides sufficient
+information about the origin of the packet and destination of the
+packet.
+
+
+.ti 0
+2 SILC Packet Protocol
+
+.ti 0
+2.1 SILC Packet
+
+SILC packets deliver messages from sender to receiver securely by
+encrypting important fields of the packet.  The packet consists of
+default SILC Packet Header, Padding, Packet Payload data, and, packet 
+MAC.
+
+The following diagram illustrates typical SILC packet.
+
+
+.in 5
+.nf
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+|   n bytes   | 1 - n bytes |      n bytes       |  n bytes       
+| SILC Header |   Padding   |    Data Payload    |    MAC    
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+.in 3
+
+.ce
+Figure 1:  Typical SILC Packet
+
+
+SILC Header is always the first part of the packet and its purpose
+is to provide information about the packet.  It provides for example
+the packet type, origin of the packet and the destination of the packet.
+The header is variable in length and first two (2) bytes of the
+header (thus first two bytes of the packet) are not encrypted.  The
+first two (2) bytes are the length of the packet which is not encrypted.
+See The following section for description of SILC Packet header.  Packets
+without SILC header or with malformed SILC header must be dropped.
+
+Padding follows the packet header.  The purpose of the padding is to
+make the packet multiple by eight (8) or by the block size of the
+cipher used in the encryption, which ever is larger.  The maximum
+length of padding is currently 16 bytes.  The padding is always
+encrypted.
+
+Data payload area follows padding and it is the actual data of the
+packet.  The packet data is the packet payloads defined in this
+protocol.  The data payload area is always encrypted.
+
+The last part of SILC packet is the packet MAC that assures the
+integrity of the packet.  The MAC is always computed from the packet
+before the encryption is applied to the packet.  If compression is used
+in the packet the MAC is computed after the compression has been
+applied.  The compression, on the other hand, is always applied before
+encryption.
+
+All fields in all packet payloads are always in MSB (most significant
+byte first) order.
+
+
+.ti 0
+2.2 SILC Packet Header
+
+The default SILC packet header is applied to all SILC packets and it is
+variable in length.  The purpose of SILC Packet header is to provide
+detailed information about the packet.  The receiver of the packet uses
+the packet header to parse the packet and gain other relevant parameters
+of the packet.
+
+The following diagram represents the default SILC header format.
+(*) indicates that this field is never encrypted.  Other fields are
+always encrypted.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length *       |     Flags     |  Packet Type  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source ID Length       |     Destination ID Length     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Src ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                           Source ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Dst ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                         Destination ID                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  SILC Packet Header
+
+
+.in 6
+o Payload Length (2 bytes) - Is the length of the packet
+  not including the padding of the packet.  This field must
+  not be encrypted but must always be authenticated.
+
+o Flags (1 byte) - Indicates flags to be used in packet
+  processing.  Several flags may be set by ORing the flags
+  together.
+
+  The following flags are reserved for this field:
+
+
+     No flags                  0x00
+
+       In this case the field is ignored.
+
+
+     Private Message Key       0x01
+
+       Indicates that the packet must include private
+       message that is encrypted using private key set by
+       client.  Servers does not know anything about this
+       key and this causes that the private message is
+       not handled by the server at all, it is just
+       passed along.  See section 2.5.3 Private Message
+       Encryption And Decryption for more information.
+
+
+     List                      0x02
+  
+       Indicates that the packet consists of list of
+       packet payloads indicated by the Packet Type field.
+       The payloads are added one after the other.  Note that
+       there are packet types that must not be used as
+       list.  Parsing of list packet is done by calculating
+       the length of each payload and parsing them one by
+       one.
+
+
+     Broadcast                 0x04
+
+       Marks the packet to be broadcasted.  Client cannot
+       send broadcast packet and normal server cannot send
+       broadcast packet.  Only router server may send broadcast
+       packet.  The router receiving of packet with this flag 
+       set must send (broadcast) the packet to its primary
+       route.  If router has several router connections the
+       packet may be sent only to the primary route.  See
+       section 2.13 Packet Broadcasting for description of 
+       packet broadcasting.
+
+
+     Tunneled                  0x08
+
+       Marks that the packet is tunneled.  Tunneling means
+       that extra SILC Packet Header has been applied to the
+       original packet.  The outer header has this flag
+       set.  See section 2.14 Packet Tunneling for more
+       information.
+.in 3
+
+
+
+o Packet Type (1 byte) - Is the type of the packet. Receiver 
+  uses this field to parse the packet.  See section 2.3
+  SILC Packets for list of defined packet types.
+
+o Source ID Length (2 bytes) - Indicates the length of the
+  Source ID field in the header, not including this or any
+  other fields.
+
+o Destination ID Length (2 bytes) - Indicates the length of the
+  Destination ID field in the header, not including this or
+  any other fields.
+
+o Src ID Type (1 byte) - Indicates the type of ID in the
+  Source ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Source ID (variable length) - The actual source ID that
+  indicates who is the original sender of the packet.
+
+o Dst ID Type (1 byte) - Indicates the type of ID in the
+  Destination ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Destination ID (variable length) - The actual source ID that
+  indicates who is the end receiver of the packet.
+
+
+.ti 0
+2.3 SILC Packet Types
+
+SILC packet types defines the contents of the packet and it is used by
+the receiver to parse the packet.  The packet type is 8 bits, as a one
+byte, in length.  The range for the packet types are from 0 - 255,
+where 0 is never sent and 255 is currently reserved for future
+extensions and must not be defined to any other purpose.  Every SILC
+specification compliant implementation should support all of these packet
+types.
+
+The below list of the SILC Packet types includes reference to the packet
+payload as well.  Packet payloads are the actual packet, that is, the data
+that the packet consists of.  Each packet type defines packet payload 
+which usually may only be sent with the specific packet type.
+
+Most of the packets are packets that must be destined directly to entity
+that is connected to the sender.  It is not allowed, for example, for
+router to send disconnect packet to client that is not directly connected
+to the router.  However, there are some special packet types that may
+be destined to some entity that the sender has not direct connection
+with.  These packets are for example private message packets, channel
+message packets, command packets and some other packets that may be
+broadcasted in the SILC network.  If the packet is allowed to be sent to
+indirectly connected entity it is mentioned separately in the packet
+description (unless it is obvious as in private and channel message
+packets).  Other packets must not be sent or accepted, if sent, to
+indirectly connected entities.
+
+List of SILC Packet types are defined as follows.
+
+.in 1
+     0    SILC_PACKET_NONE
+
+          This type is reserved and it is never sent.         
+
+
+     1    SILC_PACKET_DISCONNECT
+
+          This packet is sent to disconnect the remote end.  Reason of
+          the disconnection is sent inside the packet payload.  Client
+          usually does not send this packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.3 Disconnect Payload
+
+
+     2    SILC_PACKET_SUCCESS
+
+          This packet is sent upon successful execution of some protocol.
+          The status of the success is sent in the packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.4 Success Payload
+
+
+     3    SILC_PACKET_FAILURE
+
+          This packet is sent upon failure of some protocol.  The status
+          of the failure is sent in the packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.5 Failure Payload
+
+
+     4    SILC_PACKET_REJECT
+
+          This packet may be sent upon rejection of some protocol.
+          The status of the rejection is sent in the packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.6 Reject Payload
+
+
+     5    SILC_PACKET_NOTIFY
+
+          This packet is used to send notify message, usually from
+          server to client, although it may be sent from server to another
+          server as well.  Client never sends this packet.  Server may
+          send this packet to channel as well when the packet is 
+          distributed to all clients on the channel.
+
+          Payload of the packet:  See section 2.3.7 Notify Payload.
+
+
+     6    SILC_PACKET_ERROR
+
+          This packet is sent when an error occurs.  Server may
+          send this packet.  Client never sends this packet.  The
+          client may entirely ignore the packet, however, server is
+          most likely to take action anyway.  This packet may be sent
+          to entity that is indirectly connected to the sender.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.8 Error Payload.
+
+
+     7    SILC_PACKET_CHANNEL_MESSAGE
+
+          This packet is used to send messages to channels.  The packet
+          includes Channel ID of the channel and the actual message to
+          the channel.  Messages sent to the channel are always protected
+          by channel specific keys.  Channel Keys are distributed by
+          SILC_PACKET_CHANNEL_KEY packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.9 Channel Message 
+                                  Payload
+
+
+     8    SILC_PACKET_CHANNEL_KEY
+
+          This packet is used to distribute new key for particular
+          channel.  Each channel has their own independent keys that
+          is used to protect the traffic on the channel.  Only server
+          may send this packet.  This packet may be sent to entity
+          that is indirectly connected to the sender.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.10 Channel Key Payload
+
+
+     9    SILC_PACKET_PRIVATE_MESSAGE
+
+          This packet is used to send private messages from client
+          to another client.  By default, private messages are protected
+          by session keys established by normal key exchange protocol.
+          However, it is possible to use specific key to protect private
+          messages.  SILC_PACKET_PRIVATE_MESSAGE_KEY packet is used to 
+          agree the key with the remote client.  Pre-shared key may be 
+          used as well if both of the client knows it, however, it needs 
+          to be agreed outside SILC.  See more of this in [SILC1].
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.11 Private Message
+                                  Payload
+
+
+     10   SILC_PACKET_PRIVATE_MESSAGE_KEY
+
+          This packet is used to agree about a key to be used to protect
+          the private messages between two clients.  If this is not sent
+          the normal session key is used to protect the private messages
+          inside SILC network.  Agreeing to use specific key to protect
+          private messages adds security, as no server between the two
+          clients will be able to decrypt the private message.  However,
+          servers inside SILC network are considered to be trusted, thus
+          using normal session key to protect private messages does not
+          degree security.  Whether to agree to use specific keys by
+          default or to use normal session keys by default, is 
+          implementation specific issue.  See more of this in [SILC1].
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.12 Private Message
+                                  Key Payload
+
+
+     11   SILC_PACKET_COMMAND
+
+          This packet is used to send commands from client to server.
+          Server may send this packet to other servers as well.  All
+          commands are listed in their own section SILC Command Types
+          in [SILC1].  The contents of this packet is command specific.
+          This packet may be sent to entity that is indirectly connected
+          to the sender.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.13 Command Payload
+
+
+     12   SILC_PACKET_COMMAND_REPLY
+
+          This packet is send as reply to the SILC_PACKET_COMMAND packet.
+          The contents of this packet is command specific.  This packet
+          maybe sent to entity that is indirectly connected to the sender.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.14 Command Reply 
+                                  Payload and section 2.3.13 Command
+                                  Payload
+
+
+     13   SILC_PACKET_KEY_EXCHANGE
+
+          This packet is used to start SILC Key Exchange Protocol, 
+          described in detail in [SILC3].
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     14   SILC_PACKET_KEY_EXCHANGE_1
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     15   SILC_PACKET_KEY_EXCHANGE_2
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     16   SILC_PACKET_CONNECTION_AUTH_REQUEST
+
+          This packet is used to request the authentication method to
+          be used in the SILC Connection Authentication Protocol.  If 
+          initiator of the protocol does not know the mandatory 
+          authentication method this packet may be used to determine it.
+
+          The party receiving this payload must respond with the same
+          packet including the mandatory authentication method.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.15 Connection Auth
+                                  Request Payload
+
+
+     17   SILC_PACKET_CONNECTION_AUTH
+
+          This packet is used to start and perform the SILC Connection
+          Authentication Protocol.  This protocol is used to authenticate
+          the connecting party.  The protocol is described in detail in
+          [SILC3].
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Authentication
+                                  Protocol and it sub sections in [SILC].
+
+
+     18   SILC_PACKET_NEW_ID
+
+          This packet is used to distribute new ID's from server to
+          router and from router to all routers in the SILC network.
+          This is used when for example new client is registered to
+          SILC network.  The newly created ID's of these operations are
+          distributed by this packet.  Only server may send this packet,
+          however, client must be able to receive this packet.
+
+          Payload of the packet:  See section 2.3.16 New ID Payload
+
+
+     19   SILC_PACKET_NEW_CLIENT
+
+          This packet is used by client to register itself to the   
+          SILC network.  This is sent after key exchange and  
+          authentication protocols has been completed.  Client sends
+          various information about itself in this packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.17 New Client Payload
+
+
+     20   SILC_PACKET_NEW_SERVER
+
+          This packet is used by server to register itself to the
+          SILC network.  This is sent after key exchange and 
+          authentication protocols has been completed.  Server sends
+          this to the router it connected to, or, if router was
+          connecting, to the connected router.  Server sends
+          its Server ID and other information in this packet.
+          Client must not send or receive this packet.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+          Payload of the packet:  See section 2.3.18 New Server Payload
+
+
+     21   SILC_PACKET_NEW_CHANNEL
+
+          This packet is used to notify routers about newly created
+          channel.  Channels are always created by the router and it must
+          notify other routers about the created channel.  Router sends
+          this packet to its primary route.  Client must not send this
+          packet.  This packet maybe sent to entity that is indirectly
+          connected to the sender.
+
+          Payload of the packet:  See section 2.3.19 New Channel Payload
+
+
+     22   SILC_PACKET_REKEY
+
+          This packet is used to indicate that re-key must be performed
+          for session keys.  See section Session Key Regeneration in
+          [SILC1] for more information.  This packet does not have
+          a payload.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+
+     23   SILC_PACKET_REKEY_DONE
+
+          This packet is used to indicate that re-key is performed and
+          new keys must be used hereafter.  This is sent only if re-key
+          was done without PFS option.  If PFS is set, this is not sent
+          as SILC Key Exchange protocol is executed.  This packet does
+          not have a payload.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+     
+     24   SILC_PACKET_HEARTBEAT
+
+          This packet is used by clients, servers and routers to keep the
+          connection alive.  It is recommended that all servers implement
+          keepalive actions and perform it to both direction in a link.
+          This packet does not have a payload.
+
+          This packet must not be sent as list and the List flag must
+         not be set.
+
+
+     25   SILC_PACKET_KEY_AGREEMENT
+
+          This packet is used by clients to request key negotiation 
+          between another client in the SILC network.  If the negotiation
+          is started it is performed using the SKE protocol.  The result of
+          the negotiation, the secret key material, can be used for
+          example as private message key.  The server and router must not
+          send this packet.
+
+          Payload of the packet:  See section 2.3.20 Key Agreement Payload
+
+
+    26    SILC_PACKET_CELL_ROUTERS
+
+          This packet is used by primary router in the cell to notify its
+          primary router what other routers (backup routers) exist in the
+          cell.  In case of failure of the primary router in the cell the
+          first router in the list will act as primary router of the cell.
+          This packet may be sent at anytime after connection has been
+          registered to the primary router.  The client must not send this
+          packet.
+
+          Payload of the packet:  See section 2.3.21 Cell Routers Payload
+
+
+     27 - 199
+
+         Currently undefined commands.
+
+
+     200 - 254
+
+         These packet types are reserved for private use and they will not
+         be defined by this document.
+
+
+     255 SILC_PACKET_MAX
+
+         This type is reserved for future extensions and currently it 
+         is not sent.
+.in 3
+
+
+.ti 0
+2.3.1 SILC Packet Payloads
+
+All payloads resides in the main data area of the SILC packet.  However
+all payloads must be at the start of the data area after the default
+SILC packet header and padding.  All fields in the packet payload are
+always encrypted, as, they reside in the data area of the packet which
+is always encrypted.
+
+Payloads described in this section are common payloads that must be
+accepted anytime during SILC session.  Most of the payloads may only
+be sent with specific packet type which is defined in the description
+of the payload.
+
+There are a lot of other payloads in the SILC as well.  However, they
+are not common in the sense that they could be sent at any time. 
+These payloads are not described in this section.  These are payloads
+such as SILC Key Exchange payloads and so on.  These are described
+in [SILC1] and [SILC3].
+
+
+.ti 0
+2.3.2 Generic payloads
+
+This section describes generic payloads that are not associated to any
+specific packet type.  They can be used for example inside some other
+packet payloads.
+
+
+.ti 0
+2.3.2.1 ID Payload
+
+This payload can be used to send an ID.  ID's are variable length thus
+this payload provides a way to send variable length ID's.
+
+The following diagram represents the ID Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             ID Type           |           ID Length           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           ID Data                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 3:  ID Payload
+
+
+.in 6
+o ID Type (2 bytes) - Indicates the type of the ID.  See 
+  section 2.4 SILC ID Types for list of defined ID types.
+
+o ID Length (2 bytes) - Length of the ID Data area not 
+  including the length of any other fields in the payload.
+
+o ID Data (variable length) - The actual ID data.
+.in 3
+
+
+.ti 0
+2.3.2.2 Argument Payload
+
+Argument Payload is used to set arguments for any packet payload that
+needs and supports arguments, such as commands.  Number of arguments
+associated with a packet must be indicated by the packet payload who
+needs the arguments. Argument Payloads must always reside right after
+the packet payload needing the arguments.  Incorrect amount of argument
+payloads must cause rejection of the packet.  The following diagram represents
+the Argument Payload.
+
+The following diagram represents the Argument Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | Argument Type |               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               +
+|                                                               |
+~                        Argument Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 4:  Argument Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the argument payload data 
+  area not including the length of any other fields in the 
+  payload.
+
+o Argument Type (1 byte) - Indicates the type of the argument.  
+  Every argument may have a specific type that must be defined
+  by the packet payload needing the argument.  For example
+  every command specify a number for each argument that maybe 
+  associated with the command.  By using this number the receiver 
+  of the packet knows what type of argument this is.  If there is
+  no specific argument type this field is set to zero (0).
+
+o Argument Data (variable length) - Argument data.
+.in 3
+
+
+.ti 0
+2.3.2.3 Channel Payload
+
+Generic Channel Payload may be used information about channel, its name,
+the Channel ID and a mode.
+
+The following diagram represents the Channel Payload Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Name Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                           Mode Mask                           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  New Channel Payload
+
+
+.in 6
+o Channel Name Length (2 bytes) - Length of the channel name
+  field.
+
+o Channel Name (variable length) - The name of the channel.
+
+o Channel ID Length (2 bytes) - Length of the Channel ID field.
+
+o Channel ID (variable length) - The Channel ID.
+
+o Mode Mask (4 bytes) - A mode.  This can be the mode of the
+  channel but it can also be the mode of the client on the
+  channel.  The contents of this field is dependent of the
+  usage of this payload.  The usage is defined separately
+  when this payload is used.  This is a 32 bit MSB first value.
+.in 3
+
+
+.ti 0
+2.3.3 Disconnect Payload
+
+Disconnect payload is sent upon disconnection.  The payload is simple;
+reason of disconnection is sent to the disconnected party.
+
+The payload may only be sent with SILC_PACKET_DISCONNECT packet.  It
+must not be sent in any other packet type.  The following diagram represents
+the Disconnect Payload.
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Disconnect Message                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  Disconnect Payload
+
+
+
+
+.in 6
+o Disconnect Message (variable length) - Human readable
+  reason of the disconnection.
+.in 3
+
+
+.ti 0
+2.3.4 Success Payload
+
+Success payload is sent when some protocol execution is successfully
+completed.  The payload is simple; indication of the success is sent.
+This maybe any data, including binary or human readable data.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Success Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 7:  Success Payload
+
+
+.in 6
+o Success Indication (variable length) - Indication of
+  the success.  This maybe for example some flag that
+  indicates the protocol and the success status or human
+  readable success message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.5 Failure Payload
+
+This is opposite of Success Payload.  Indication of failure of
+some protocol is sent in the payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Failure Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 8:  Failure Payload
+
+
+.in 6
+o Failure Indication (variable length) - Indication of
+  the failure.  This maybe for example some flag that
+  indicates the protocol and the failure status or human
+  readable failure message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.6 Reject Payload
+
+This payload is sent when some protocol is rejected to be executed.
+Other operations may send this as well that was rejected.  The
+indication of the rejection is sent in the payload.  The indication
+may be binary or human readable data.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Reject Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 9:  Reject Payload
+
+
+.in 6
+o Reject Indication (variable length) - Indication of
+  the rejection.  This maybe for example some flag that
+  indicates the protocol and the rejection status or human
+  readable rejection message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+
+
+
+.ti 0
+2.3.7 Notify Payload
+
+Notify payload is used to send notify messages.  The payload is usually
+sent from server to client, however, server may send it to another
+server as well.  This payload may also be sent to a channel.  Client must
+not send this payload.  The receiver of this payload may totally ignore the
+contents of the payload, however, notify message should be audited.
+
+The payload may only be sent with SILC_PACKET_NOTIFY packet.  It must
+not be sent in any other packet type.  The following diagram represents the
+Notify Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|          Notify Type          |        Payload Length         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Argument Nums |
++-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 10:  Notify Payload
+
+
+.in 6
+o Notify Type (2 bytes) - Indicates the type of the notify
+  message.
+
+o Payload Length (2 bytes) - Length of the entire Notify Payload
+  including any associated Argument Payloads.
+
+o Argument Nums (2 bytes) - Indicates the number of Argument
+  Payloads associated to this payload.  Notify types may define
+  arguments to be send along the notify message.
+.in 3
+
+The following list of currently defined notify types.  The format for notify
+arguments is same as in SILC commands described in [SILC1].  Also, all
+ID's sent in arguments are sent inside ID Payload.
+
+.in 6
+0     SILC_NOTIFY_TYPE_NONE
+
+      If no specific notify type apply for the notify message this type
+      may be used.
+
+      Max Arguments:  1
+          Arguments:  (1) <message>
+
+      The <message> is implementation specific free text string.  Receiver
+      may ignore this message.
+
+
+1     SILC_NOTIFY_TYPE_INVITE
+
+      Sent when an client is invited to a channel.  This is also sent
+      when the invite list of the channel is changed.  This notify type
+      is sent between routers and if an client was invited to the 
+      client as well.  In this case the packet is destined to the client.
+
+      Max Arguments:  5
+          Arguments:  (1) <Channel ID>          (2) <channel name>
+                      (3) [<sender Client ID>]  (4) [<adding client>]
+                      (5) [<removing client>]
+
+      The <Channel ID> is the channel.  The <channel name> is the name
+      of the channel and is provided because the client which receives 
+      this notify packet may not have a way to resolve the name of the
+      channel from the <Channel ID>.  The <sender Client ID> is the
+      Client ID who invited the client to the channel.  The <adding client>
+      and the <removing client> indicates the added or removed client
+      from the channel's invite list.  The format of the <adding client
+      and the <removing client> is defined in the [SILC1] with
+      SILC_COMMAND_INVITE command.
+
+      The <adding client> and <removing client> is never sent when the
+      packet is destined to a client.
+
+
+2     SILC_NOTIFY_TYPE_JOIN
+
+      Sent when client has joined to a channel.  The server must distribute
+      this type only to the local clients on the channel and then send
+      it to its primary router.  The router or server receiving the packet
+      distributes this type to the local clients on the channel and
+      broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) [<Client ID>]       (2) <Channel ID>
+
+      The <Client ID> is the client that joined to the channel indicated
+      by the <Channel ID>.
+
+
+3     SILC_NOTIFY_TYPE_LEAVE
+
+      Sent when client has left a channel.  The server must distribute
+      this type only to the local clients on the channel and then send
+      it to its primary router.  The router or server receiving the packet
+      distributes this type to the local clients on the channel and
+      broadcast it to the network.
+
+      Max Arguments:  1
+          Arguments:  (1) <Client ID>
+
+      The <Client ID> is the client who left the channel.
+
+
+4     SILC_NOTIFY_TYPE_SIGNOFF
+
+      Sent when client signoffs from SILC network.  The server must
+      distribute this type only to the local clients on the channel and
+      then send it to its primary router.  The router or server receiving
+      the packet distributes this type to the local clients on the channel
+      and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <message>
+
+      The <Client ID> is the client who left SILC network.  The <message>
+      is free text string indicating the reason of signoff.
+
+
+5     SILC_NOTIFY_TYPE_TOPIC_SET
+
+      Sent when topic is set/changed on a channel.  This type must be sent
+      only to the clients who is joined on the channel whose topic was
+      set or changed.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <topic>
+
+      The <Client ID> is the client who set or changed the <topic>.
+
+
+6     SILC_NOTIFY_TYPE_NICK_CHANGE
+
+      Sent when client changes nick on a channel.  The server must
+      distribute this type only to the local clients on the channel and
+      then send it to its primary router. The router or server receiving
+      the packet distributes this type to the local clients on the channel
+      and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Client ID>  (2) <New Client ID>
+
+      The <Old Client ID> is the old ID of the client who changed the 
+      nickname.  The <New Client ID> is the new ID generated by the change
+      of the nickname.
+
+
+7     SILC_NOTIFY_TYPE_CMODE_CHANGE
+
+      Sent when channel mode has changed.  This type must be sent only to
+      the clients who is joined on the channel whose mode was changed.
+
+      Max Arguments:  4
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) [<cipher>]   (4) <[hmac>]     
+
+      The <ID Payload> is the ID (usually Client ID but it can be Server ID
+      as well when the router is enforcing channel mode change) of the
+      entity which changed the mode.  The <mode mask> is the new mode mask
+      of the channel.  The client can safely ignore the <cipher> argument
+      since the SILC_PACKET_CHANNEL_KEY packet will force the new channel
+      key change anyway.  The <hmac> argument is important since the client
+      is responsible of setting the new HMAC and the hmac key into use.
+
+
+8     SILC_NOTIFY_TYPE_CUMODE_CHANGE
+
+      Sent when user mode on channel has changed.  This type must be sent
+      only to the clients who is joined on the channel where the target 
+      client is on.
+
+      Max Arguments:  3
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+                      (3) <Target Client ID>
+
+      The <Client ID> is the client who changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The <Target Client ID> is the
+      client which mode was changed.
+
+
+9     SILC_NOTIFY_TYPE_MOTD
+
+      Sent when Message of the Day (motd) is sent to client.
+
+      Max Arguments:  1
+          Arguments:  (1) <motd>
+
+      The <motd> is the Message of the Day.
+
+
+10    SILC_NOTIFY_TYPE_CHANNEL_CHANGE
+
+      Sent when channel's ID has changed for a reason or another.  This 
+      is sent by normal server to the client.  This can also be sent by
+      router to other server to force the Channel ID change.  The Channel
+      ID must be changed to use the new one.  When sent to clients, this
+      type must be sent only to the clients who is joined on the channel.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Channel ID>  (2) <New Channel ID>
+
+      The <Old Channel ID> is the channel's old ID and the <New Channel ID>
+      is the new one that must replace the old one.
+
+
+11    SILC_NOTIFY_TYPE_SERVER_SIGNOFF
+
+      Sent when server quits SILC network.  Those clients from this server
+      that are on channels must be removed from the channel.
+
+      Max Arguments:  1
+          Arguments:  (1) <Server ID>
+
+      The <Server ID> is the server's ID.
+
+
+12    SILC_NOTIFY_TYPE_KICKED
+
+      Sent when a client has been kicked from a channel.  This is sent 
+      also to the client who was kicked from the channel.  The client
+      who was kicked from the channel must be removed from the channel.
+      This notify type is always destined to the channel.  The router or
+      server receiving the packet distributes this type to the local
+      clients on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client who was kicked from the channel.
+      The kicker may have set the <comment> to indicate the reason for
+      the kicking.
+
+
+13    SILC_NOTIFY_TYPE_KILLED
+
+      Sent when a client has been killed from the network.  This is sent 
+      also to the client who was killed from the network.  The client
+      who was killed from the network must be removed from the network.
+      This notify type is destined directly to the client who was killed
+      and to channel if the client is on any channel.  The router or
+      server receiving the packet distributes this type to the local
+      clients on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client who was killed from the network.
+      The killer may have set the <comment> to indicate the reason for
+      the killing.
+
+
+14    SILC_NOTIFY_TYPE_UMODE_CHANGE
+
+      Sent when user's mode in the SILC changes.  This type is sent only
+      between routers as broadcast packet.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+
+      The <Client ID> is the client which mode was changed.  The <mode mask>
+      is the new mode mask.
+
+
+15    SILC_NOTIFY_TYPE_BAN
+
+      Sent when the ban list of the channel is changed.  This type is sent
+      only between routers as broadcast packet.
+
+      Max Arguments:  3
+          Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                      (3) [<removing client>]
+
+      The <Channel ID> is the channel which ban list was changed.  The
+      <adding client> is used to indicate the a ban was added and the
+      <removing client> is used to indicate that a ban was removed from
+      the ban list.  The format of the <adding client> and the 
+      <removing client> is defined in the [SILC1] with SILC_COMMAND_BAN
+      command.
+
+.in 3
+
+Notify types starting from 16384 are reserved for private notify
+message types.
+
+
+.ti 0
+2.3.8 Error Payload
+
+Error payload is sent upon error.  Error may occur in various
+conditions when server sends this packet.  Client may not send this
+payload but must be able to accept it.  However, client may
+totally ignore the contents of the packet as server is going to
+take action on the error anyway.  However, it is recommended
+that the client takes error packet seriously.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Error Message                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 11:  Error Payload
+
+
+.in 6
+o Error Message (variable length) - Human readable error
+  message.
+.in 3
+
+
+.ti 0
+2.3.9 Channel Message Payload
+
+Channel messages are the most common messages sent in the SILC.
+Channel Message Payload is used to send message to channels.  These
+messages can only be sent if client has joined to some channel.
+Even though this packet is the most common in SILC it is still
+special packet.  Some special handling on sending and reception
+of channel message is required.
+
+Padding must be applied into this payload since the payload is
+encrypted separately from other parts of the packet with the
+channel specific key.  Hence the requirement of the padding.  
+The padding should be random data.  The packet must be made
+multiple by eight (8) or by the block size of the cipher, which
+ever is larger.
+
+The SILC header in this packet is encrypted with the session key
+of the next receiver of the packet.  Nothing else is encrypted
+with that key.  Thus, the actual packet and padding to be
+encrypted with the session key is SILC Header plus padding to it
+to make it multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.
+
+Receiver of the the channel message packet is able to determine
+the channel the message is destined to by checking the destination
+ID from the SILC Packet header which tells the destination channel.
+The original sender of the packet is also determined by checking
+the source ID from the header which tells the client who sent
+the message.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_MESSAGE packet.
+It  must not be sent in any other packet type.  The following diagram 
+represents the Channel Message Payload.
+
+(*) indicates that the field is not encrypted.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |         Message Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Message Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Padding Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                            Padding                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                              MAC                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Initial Vector *                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 12:  Channel Message Payload
+
+
+.in 6
+o Flags (2 bytes) - Includes the flags of the channel
+  messages.  The flags can indicate a reason or purpose
+  for the channel message.  Note, that the Private Message
+  Payload use these same flags for the same purpose.  The
+  following flags are defined:
+
+  0x0000  SILC_MESSAGE_FLAG_NONE
+
+          No specific flags set.
+
+  0x0001  SILC_MESSAGE_FLAG_AUTREPLY
+
+          This message is an automatic reply to a earlier
+          received message.
+
+  0x0002  SILC_MESSAGE_FLAG_NOREPLY
+
+          There should not be reply messages to this
+          message.
+
+  0x0004  SILC_MESSAGE_FLAG_ACTION
+
+          The sender is performing an action and the message
+          is the indication of the action.
+
+  0x0008  SILC_MESSAGE_FLAG_NOTICE
+
+          The message is for example and informational notice
+          type message.
+
+  0x0010  SILC_MESSAGE_FLAG_REQUEST
+
+          This is a generic request flag to send request
+          messages.
+
+  0x0020 - 0x0200 RESERVED
+
+          Reserved for future flags
+
+  0x0400 - 0x8000 PRIVATE RANGE
+
+         Private range for free use.
+
+o Message Length (2 bytes) - Indicates the length of the
+  the Message Data field in the payload, not including any 
+  other field.
+
+o Message Data (variable length) - The actual message to
+  the channel.
+
+o Padding Length (2 bytes) - Indicates the length of the
+  Padding field in the payload, not including any other
+  field.
+
+o Padding (variable length) - The padding that must be
+  applied because this payload is encrypted separately from
+  other parts of the packet.
+
+o MAC (variable legnth) - The MAC computed from the
+  Message Length, Message Data, Padding Length and Padding
+  fields.  This protects the integrity of the plaintext
+  channel message.  The receiver can verify from the MAC
+  whether the message decrypted correctly.  Also, if more than
+  one private key has been set for the channel, the receiver
+  can verify which of the keys decrypted the message 
+  correctly.  Note that, this field is encrypted and must
+  be added to the padding calculation.
+
+o Initial Vector (variable length) - The initial vector
+  that has been used in packet encryption.  It needs to be
+  used in the packet decryption as well.  What this field
+  includes is implementation issue.  However, it is 
+  recommended that it would be random data or, perhaps,
+  a timestamp.  It is not recommended to use zero (0) as
+  initial vector.  This field is not encrypted.  This field
+  is not included into the padding calculation.  Length
+  of this field equals the cipher's block size.  This field
+  is, however, authenticated.
+.in 3
+
+
+.ti 0
+2.3.10 Channel Key Payload
+
+All traffic in channels are protected by channel specific keys.
+Channel Key Payload is used to distribute channel keys to all
+clients on the particular channel.  Channel keys are sent when
+the channel is created, when new user joins to the channel and
+whenever a user has left a channel.  Server creates the new
+channel key and distributes it to the clients by encrypting this
+payload with the session key shared between the server and
+the client.  After that, client starts using the key received
+in this payload to protect the traffic on the channel.
+
+The client who is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity who sent the SILC_COMMAND_JOIN command.
+
+Channel keys are cell specific thus every router in cell have
+to create a channel key and distribute it if any client in the
+cell has joined to a channel.  Channel traffic between cell's
+are not encrypted using channel keys, they are encrypted using
+normal session keys between two routers.  Inside a cell, all
+channel traffic is encrypted with the specified channel key.
+Channel key should expire periodically, say, in one hour, in
+which case new channel key is created and distributed.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_KEY packet.
+It must not be sent in any other packet type.  The following diagram 
+represents the Channel Key Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Cipher Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Key Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Key                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 13:  Channel Key Payload
+
+
+
+.in 6
+o Channel ID Length (2 bytes) - Indicates the length of the
+  Channel ID field in the payload, not including any other
+  field.
+
+o Channel ID (variable length) - The Channel ID of the
+  channel this key is meant for.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher used
+  in the protection of channel traffic.  This name is
+  initially decided by the creator of the channel but it
+  may change during the life time of the channel as well.
+
+o Channel Key Length (2 bytes) - Indicates the length of the
+  Channel Key field in the payload, not including any other
+  field.
+
+o Channel Key (variable length) - The actual channel key
+  material.  This key is used as such as key material for
+  encryption function.
+.in 3
+
+
+.ti 0
+2.3.11 Private Message Payload
+
+Private Message Payload is used to send private message between
+two clients (or users for that matter).  The messages are sent only
+to the specified user and no other user inside SILC network is
+able to see the message.  The message is protected by the session 
+key established by the SILC Key Exchange Protocol.  However,
+it is also possible to agree to use specific keys to protect
+just the private messages.  See section 2.3.11 Private Message
+Key Payload for detailed description of how to agree to use
+specific key.
+
+If normal session key is used to protect the message, every
+server between the sender client and the receiving client needs
+to decrypt the packet and always re-encrypt it with the session
+key of the next receiver of the packet.  See section Client
+To Client in [SILC1].
+
+When specific key is used to protect the message, servers between 
+the sender and the receiver needs not to decrypt/re-encrypt the 
+packet.  Section 4.8.2 Client To Client in [SILC1] gives example of
+this scheme as well.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE 
+packet.  It must not be sent in any other packet type.  The following 
+diagram represents the Private Message Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |        Nickname Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                            Nickname                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Message Data Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Message Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                             Padding                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 14:  Private Message Payload
+
+
+.in 6
+o Flags (2 bytes) - This field includes the flags of the
+  private message.  They can indicate a different reason or
+  purpose for the private message.  See the section 2.3.9
+  Channel Message Payload for defined flags.  Note, that
+  the Channel Message Payload use the same flags for the
+  same purpose.
+
+o Nickname Length (2 bytes) - Indicates the length of the
+  Nickname field, not including any other field.
+
+o Nickname (variable length) - Nickname of the sender of the 
+  private message.  This should not be trusted as a definite 
+  sender of the private message.  The SILC Packet Header in 
+  the packet indicates the true sender of the packet and 
+  client should verify that the nickname sent here belongs 
+  to the Client ID in the SILC Packet Header.  This nickname 
+  is merely provided to be displayed by the client.
+
+o Message Data Length (2 bytes) - Indicates the length of the
+  Message Data field, not includes any other field.
+
+o Message Data (variable length) - The actual message to
+  the client.  Rest of the packet is reserved for the message
+  data.
+
+o Padding (variable length) - This field is present only
+  when the private message payload is encrypted with private
+  message key.  In this case the padding is applied to make
+  the packet multiple by eight (8), or by the block size of
+  the cipher, which ever is larger.  When encrypted with
+  normal session keys, this field must not be included.
+.in 3
+
+
+.ti 0
+2.3.12 Private Message Key Payload
+
+This payload is used to send key from client to another client that
+is going to be used to protect the private messages between these
+two clients.  If this payload is not sent normal session key 
+established by the SILC Key Exchange Protocol is used to protect
+the private messages.
+
+This payload may only be sent by client to another client.  Server
+must not send this payload at any time.  After sending this payload
+the sender of private messages must set the Private Message Key
+flag into SILC Packet Header.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE_KEY 
+packet.  It must not be sent in any other packet type.  The following 
+diagram represents the Private Message Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Private Message Key Length   |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Private Message Key                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Cipher Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 15:  Private Message Key Payload
+
+
+
+
+.in 6
+o Private Message Key Length (2 bytes) - Indicates the length 
+  of the Private Message Key field in the payload, not including 
+  any other field.
+
+o Private Message Key (variable length) - The actual private
+  message key material.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher Name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher to use
+  in the private message encryption.  If this field does not
+  exist then the default cipher of the SILC protocol is used.
+  See the [SILC1] for defined ciphers.
+.in 3
+
+
+
+.ti 0
+2.3.13 Command Payload
+
+Command Payload is used to send SILC commands from client to server.
+Also server may send commands to other servers.  The following diagram
+represents the Command Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | SILC Command  | Arguments Num |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Command Identifier      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 16:  Command Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire command 
+  payload including any command argument payloads associated 
+  with this payload.
+
+o SILC Command (1 byte) - Indicates the SILC command.  This must 
+  be set to non-zero value.  If zero (0) value is found in this 
+  field the packet must be discarded.
+
+o Arguments Num (1 byte) - Indicates the number of arguments
+  associated with the command.  If there are no arguments this
+  field is set to zero (0).  The arguments must follow the 
+  command payload.  See section 2.3.2.2 for definition of the
+  Argument Payload.
+
+o Command Identifier (2 bytes) - Identifies this command at the
+  sender's end.  The entity who replies to this command must
+  set the value found from this field into the Command Payload
+  used to send the reply to the sender.  This way the sender
+  can identify which command reply belongs to which originally
+  sent command.  What this field includes is implementation
+  issue but it is recommended that wrapping counter value is
+  used in the field.  Value zero (0) in this field means that
+  no specific value is set.
+.in 3
+
+See [SILC1] for detailed description of different SILC commands,
+their arguments and their reply messages.
+
+
+.ti 0
+2.3.14 Command Reply Payload
+
+Command Reply Payload is used to send replies to the commands.  The
+Command Reply Payload is identical to the Command Payload thus see the
+upper sections for Command Payload and for Command Argument Payload
+specifications.  Command Reply message uses the Command Argument Payload
+as well.
+
+The entity who sends the reply packet must set the Command Unifier
+field in the reply packet's Command Payload to the value it received
+in the original command packet.
+
+See SILC Commands in [SILC1] for detailed description of different
+SILC commands, their arguments and their reply messages.
+
+
+.ti 0
+2.3.15 Connection Auth Request Payload
+
+Client may send this payload to server to request the authentication
+method that must be used in authentication protocol.  If client knows 
+this information beforehand this payload is not necessary to be sent.
+Server performing authentication with another server may also send
+this payload to request the authentication method.  If the connecting
+server already knows this information this payload is not necessary
+to be sent.
+
+Server receiving this request must reply with same payload sending
+the mandatory authentication method.  Algorithms that may be required
+to be used by the authentication method are the ones already 
+established by the SILC Key Exchange protocol.  See section Key
+Exchange Start Payload in [SILC3] for detailed information.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  It must not be sent in any other packet type.  The following 
+diagram represents the Connection Auth Request Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Connection Type        |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 17:  Connection Auth Request Payload
+
+
+.in 6
+o Connection Type (2 bytes) - Indicates the type of the ID.
+  The following connection types are defined:
+
+     1    Client connection
+     2    Server connection
+     3    Router connection
+
+  If any other type is found in this field the packet must be
+  discarded and the authentication must be failed.
+
+o Authentication Method (2 bytes) - Indicates the authentication
+  method to be used in the authentication protocol.  The following
+  authentication methods are defined:
+
+
+
+     0    NONE        (mandatory)
+     1    password    (mandatory)
+     2    public key  (mandatory)
+
+  If any other type is found in this field the packet must be
+  discarded and the authentication must be failed.  If this
+  payload is sent as request to receive the mandatory 
+  authentication method this field must be set to zero (0),
+  indicating that receiver should send the mandatory 
+  authentication method.  The receiver sending this payload
+  to the requesting party, may also set this field to zero (0) 
+  to indicate that authentication is not required.  In this
+  case authentication protocol still must be started but
+  server is most likely to respond with SILC_PACKET_SUCCESS
+  immediately.
+.in 3
+
+
+.ti 0
+2.3.16 New ID Payload
+
+New ID Payload is a multipurpose payload.  It is used to send newly 
+created ID's from clients and servers.  When client connects to server
+and registers itself to the server by sending SILC_PACKET_NEW_CLIENT
+packet, server replies with this packet by sending the created ID for
+the client.  Server always creates the ID for the client.
+
+This payload is also used when server tells its router that new client
+has registered to the SILC network.  In this case the server sends
+the Client ID of the client to the router.  Similary when router
+distributes information to other routers about the client in the SILC
+network this payload is used.  
+
+Also, when server connects to router, router uses this payload to inform
+other routers about new server in the SILC network.  However, every 
+server (or router) creates their own ID's thus the ID distributed by 
+this payload is not created by the distributor in this case.  Servers
+create their own ID's.  Server registers itself to the network by sending
+SILC_PACKET_NEW_SERVER to the router it connected to.  The case is same
+when router connects to another router.
+
+However, this payload is not and must not be used to send information
+about new channels.  New channels are always distributed by sending the
+dedicated SILC_PACKET_NEW_CHANNEL packet.
+
+Hence, this payload is very important and used every time when some
+new entity is registered to the SILC network.  Client never sends this
+payload.  Both client and server (and router) may receive this payload.
+
+The packet uses generic ID Payload as New ID Payload.  See section
+2.3.2.1 for generic ID Payload.
+
+
+.ti 0
+2.3.17 New Client Payload
+
+When client is connected to the server, keys has been exchanged and
+connection has been authenticated client must register itself to the 
+server.  Clients first packet after key exchange and authentication 
+protocols must be SILC_PACKET_NEW_CLIENT.  This payload tells server all
+the relevant information about the connected user.  Server creates a new
+client ID for the client when received this payload and sends it to the
+client in New ID Payload.
+
+This payload sends username and real name of the user on the remote host
+which is connected to the SILC server with SILC client.  The server 
+creates the client ID according the information sent in this payload.
+The nickname of the user becomes the username sent in this payload.
+However, client should call NICK command after sending this payload to
+set the real nickname of the user which is then used to create new 
+client ID.
+
+The payload may only be sent with SILC_PACKET_NEW_CLIENT packet.  It
+must not be sent in any other packet type.  The following diagram
+represents the New Client Payload.
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Username Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Username                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Real Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Real Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 18:  New Client Payload
+
+
+.in 6
+o Username Length (2 bytes) - Length of the username.
+
+o Username (variable length) - The username of the user on
+  the host where connecting to the SILC server.
+
+o Real Name Length (2 bytes) - Length of the Real Name.
+
+o Real Name (variable length) - The real name of the user
+  on the host where connecting to the SILC server.
+.in 3
+
+
+.ti 0
+2.3.18 New Server Payload
+
+This payload is sent by server when it has completed successfully both
+key exchange and connection authentication protocols.  The server
+uses this payload to register itself to the SILC network.  The
+first packet after these key exchange and authentication protocols
+is SILC_PACKET_NEW_SERVER packet.  The payload includes the Server ID
+of the server that it has created by itself.  It also includes a
+name of the server that is associated to the Server ID.
+
+The payload may only be sent with SILC_PACKET_NEW_SERVER packet.  It
+must not be sent in any other packet type.  The following diagram represents
+the New Server Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Server ID Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Server ID Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Server Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Server Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 19:  New Server Payload
+
+
+.in 6
+o Server ID Length (2 bytes) - Length of the ID Data area not 
+  including the length of any other fields in the payload.
+
+o Server ID Data (variable length) - The actual Server ID
+   data.
+
+o Server Name Length (2 bytes) - Length of the server name.
+
+o Server Name (variable length) - The server name.
+.in 3
+
+
+.ti 0
+2.3.19 New Channel Payload
+
+Information about newly created channel is broadcasted to all routers
+in the SILC network by sending this packet payload.  Channels are
+created by router of the cell.  Server never creates channels unless
+it is a standalone server and it does not have router connection,
+in this case server acts as router.  Normal server send JOIN command
+to the router (after it has received JOIN command from client) which
+then processes the command and creates the channel.  Client never sends
+this packet.
+
+The packet uses generic Channel Payload as New Channel Payload.  See
+section 2.3.2.3 for generic Channel Payload.  The Mode Mask field in the
+Channel Payload is the mode of the channel.
+
+
+.ti 0
+2.3.20 Key Agreement Payload
+
+This payload is used by clients to request key negotiation between
+another client in the SILC Network.  The key agreement protocol used
+is the SKE protocol.  The result of the protocol, the secret key
+material, can be used for example as private message key between the
+two clients.  This significantly adds security as the key agreement
+is performed outside the SILC network.  The server and router must not
+send this payload.
+
+The sender may tell the receiver of this payload the hostname and the
+port where the SKE protocol is running in the sender's end.  The 
+receiver may then initiate the SKE negotiation with the sender.  The
+sender may also optionally not to include the hostname and the port
+of its SKE protocol.  In this case the receiver may reply to the
+request by sending the same payload filled with the receiver's hostname
+and the port where the SKE protocol is running.  The sender may then
+initiate the SKE negotiation with the receiver.
+
+The payload may only be sent with SILC_PACKET_KEY_AGREEMENT packet.
+It must not be sent in any other packet type.  The following diagram
+represents the Key Agreement Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 20:  Key Agreement Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the Hostname
+  field.
+
+o Hostname (variable length) - The hostname or IP address where
+  the SKE protocol is running.  The sender may fill this field
+  when sending the payload.  If the receiver sends this payload
+  as reply to the request it must fill this field.
+
+o Port (4 bytes) - The port where the SKE protocol is bound.
+  The sender may fill this field when sending the payload.  If
+  the receiver sends this payload as reply to the request it 
+  must fill this field.  This is a 32 bit MSB first order value.
+.in 3
+
+
+After the key material has been received from the SKE protocol it is
+processed as the [SILC3] describes.  If the key material is used as
+channel private key then the Sending Encryption Key, as defined in
+[SILC3] is used as the channel private key.  Other key material must
+be discarded.  The [SILC1] defines the way to use the key material if
+it is intended to be used as private message keys.  Any other use for
+the key material is undefined.
+
+
+.ti 0
+2.3.21 Cell Routers Payload
+
+Cell Routers payload is used by router to notify its primary router what
+other routers exist in the cell.  The other routers are considered to be
+backup routers and one of them will come active only in the case of
+failure of the primary router.  Normal server can send this packet if it
+is acting as backup router.  Client must not send this packet.  To send
+more than one backup router set the List flag and assemble the payloads
+as list.
+
+The payload may only be sent with SILC_PACKET_CELL_ROUTERS packet.  It
+must not be sent in any other packet type.  The Following diagram
+represents the Cell Routers Payload.
+
+         
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Server ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Server ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 21:  Cell Routers Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the Hostname
+  field.
+
+o Hostname (variable length) - The hostname or IP address of
+  the backup router.
+
+o Port (4 bytes) - The port of the backup router it currently uses.
+  This is a 32 bit MSB first order value.
+
+o Server ID Length (2 bytes) - Indicates the length of the Server
+  ID field.
+
+o Server ID (variable length) - Consists of the Server ID of the
+  backup router.
+.in 3
+
+
+.ti 0
+2.4 SILC ID Types
+
+ID's are extensively used in the SILC network to associate different
+entities.  The following ID's has been defined to be used in the SILC
+network.
+
+.in 6
+0    No ID
+
+   When ever specific ID cannot be used this is used.
+
+1    Server ID
+
+   Server ID to associate servers.  See the format of
+   this ID in [SILC1].
+
+2    Client ID
+
+   Client ID to associate clients.  See the format of
+   this ID in [SILC1].
+
+3    Channel ID
+
+   Channel ID to associate channels.  See the format of
+   this ID in [SILC1].
+.in 3
+
+
+.ti 0
+2.5 Packet Encryption And Decryption
+
+SILC packets are encrypted almost entirely.  Only small part of SILC
+header is not encrypted as described in section 5.2 SILC Packet Header.
+The SILC Packet header is the first part of a packet to be encrypted
+and it is always encrypted with the key of the next receiver of the
+packet.  The data payload area of the packet is always entirely 
+encrypted and it is usually encrypted with the next receiver's key.
+However, there are some special packet types and packet payloads
+that require special encryption process.  These special cases are
+described in the next sections.  First is described the normal packet
+encryption process.
+
+
+.ti 0
+2.5.1 Normal Packet Encryption And Decryption
+
+Normal SILC packets are encrypted with the session key of the next
+receiver of the packet.  The entire SILC Packet header and the packet
+data payload is is also encrypted with the same key.  Padding of the
+packet is also encrypted always with the session key, also in special
+cases.  Computed MAC of the packet must not be encrypted.
+
+Decryption process in these cases are straightforward.  The receiver
+of the packet must first decrypt the SILC Packet header, or some parts
+of it, usually first 16 bytes of it.  Then the receiver checks the
+packet type from the decrypted part of the header and can determine
+how the rest of the packet must be decrypted.  If the packet type is
+any of the special cases described in The following sections the packet
+decryption is special.  If the packet type is not among those special
+packet types rest of the packet may be decrypted with the same key.
+
+Also, note that two bytes of the SILC Packet header are not encrypted
+thus it must be noticed in the decryption process by starting the
+decryption from the second byte of the header.  This sets some rules
+to padding generation as well, see the section 2.7 Packet Padding
+Generation.
+
+With out a doubt, this sort of decryption processing causes some
+overhead to packet decryption, but never the less, is required.
+
+
+.ti 0
+2.5.2 Channel Message Encryption And Decryption
+
+Channel Messages (Channel Message Payload) are always encrypted with
+the channel specific key.  However, the SILC Packet header is not 
+encrypted with that key.  As in normal case, the header is encrypted
+with the key of the next receiver of the packet, who ever that might
+be.  Note that in this case the encrypted data area is not touched
+at all; it must not be re-encrypted with the session key.
+
+Receiver of a channel message, who ever that is, is required to decrypt
+the SILC Packet header to be able to even recognize the packet to be as
+channel message.  This is same procedure as for normal SILC packets.
+As the receiver founds the packet to be channel message, rest of the
+packet processing is special.  Rest of the SILC Packet header is
+decrypted with the same session key along with the padding of the
+packet.  After that the packet is protected with the channel specific
+key and thus can be decrypted only if the receiver is the client on
+the channel.  See section 2.7 Packet Padding Generation for more
+information about padding on special packets.
+
+If the receiver of the channel message is router who is routing the
+message to another router then it must decrypt the Channel Message
+payload.  Between routers (that is, between cells) channel messages
+are protected with session keys shared between the routers.  This
+causes another special packet processing for channel messages.  If
+the channel message is received from another router then the entire
+packet, including Channel Message payload, is encrypted with the
+session key shared between the routers.  In this case the packet
+decryption process is as with normal SILC packets.  Hence, if the
+router is sending channel message to another router the Channel
+Message payload must have been decrypted and must be re-encrypted
+with the session key shared between the another router.  In this
+case the packet encryption is as with any normal SILC packet.
+
+It must be noted that this is only when the channel messages are sent
+from router to another router.  In all other cases the channel
+message encryption and decryption is as described above.  This
+different processing of channel messages with router to router
+connection is because channel keys are cell specific.  All cells has
+their own channel keys thus the channel message traveling from one
+cell to another must be protected as it would be any normal SILC
+packet.
+
+If the SILC_CMODE_PRIVKEY channel mode has been set for the channel
+then the router cannot decrypt the packet as it does not know the
+private key.  In this case the entire packet is encrypted with the
+session key and sent to the router.  The router receiving the packet
+must check the channel mode and decrypt the packet accordingly.
+
+
+.ti 0
+2.5.3 Private Message Encryption And Decryption
+
+By default, private message in SILC are protected by session keys.
+In this case the private message encryption and decryption process is
+equivalent to normal packet encryption and decryption.
+
+However, private messages can be protected with private message key
+which causes the packet to be special packet.  The procedure in this
+case is very much alike to channel packets.  The actual private message
+is encrypted with the private message key and other parts of the
+packet is encrypted with the session key.  See 2.7 Packet Padding
+Generation for more information about padding on special packets.
+
+The difference from channel message processing is that server or router
+en route never decrypts the actual private message, as it does not
+have the key to do that.  Thus, when sending packets between router
+the processing is same as in any other case as well; the packet's header
+and padding is protected by the session key and the data area is not
+touched.
+
+The true receiver of the private message, client, that is, is able
+to decrypt the private message as it shares the key with the sender
+of the message.
+
+
+.ti 0
+2.6 Packet MAC Generation
+
+Data integrity of a packet is protected by including a message
+authentication code (MAC) at the end of the packet.  The MAC is computed
+from shared secret MAC key, that is established by the SILC Key Exchange
+protocol, and from the original contents of the packet.  The MAC is
+always computed before the packet is encrypted, although after it is
+compressed if compression is used.
+
+The MAC is computed from entire packet.  Every bit of data in the packet,
+including SILC Packet Header is used in the MAC computing.  This way
+the entire packet becomes authenticated.
+
+If the packet is special packet MAC is computed from the entire packet
+but part of the packet may be encrypted before the MAC is computed.
+This is case, for example, with channel messages where the message data
+is encrypted with key that server may not now.  In this case the MAC
+has been computed from the encrypted data.
+
+See [SILC1] for defined and allowed MAC algorithms.
+
+
+.ti 0
+2.7 Packet Padding Generation
+
+Padding is needed in the packet because the packet is encrypted.  It
+must always be multiple by eight (8) or multiple by the size of the
+cipher's block size, which ever is larger.  The padding is always
+encrypted.
+
+For normal packets the padding is added after the SILC Packet Header
+and between the Data Payload area.  The padding for normal packets
+are calculated as follows:
+
+.in 6
+padding length = 16 - ((packet length - 2) % 16)
+.in 3
+
+The 16 is the maximum padding allowed in SILC packet.  Two (2) is 
+subtracted from the true length of the packet because two (2) bytes
+is not encrypted in SILC Packet Header, see section 2.2 SILC Packet
+Header.  Those two bytes that are not encrypted must not be calculated
+to the padding length.
+
+For special packets the padding calculation may be different as special
+packets may be encrypted differently.  In these cases the encrypted
+data area must already be multiple by the block size thus in this case
+the padding is calculated only for SILC Packet Header, not for any
+other area of the packet.  The same algorithm works in this case as
+well, except that the `packet length' is now the SILC Packet Header
+length.  In this case, as well, two (2) is subtracted from the
+length.
+
+The padding must be random data, preferably, generated by 
+cryptographically strong random number generator.
+
+
+.ti 0
+2.8 Packet Compression
+
+SILC Packets may be compressed.  In this case the data payload area
+is compressed and all other areas of the packet must remain as they
+are.  After compression is performed for the data area, the length
+field of Packet Header must be set to the compressed length of the
+data.
+
+The compression must always be applied before encryption.  When
+the packet is received and decrypted the data area must be decompressed.
+Note that the true sender of the packet must apply the compression and
+the true receiver of the packet must apply the decompression.  Any
+server or router en route must not decompress the packet.
+
+
+
+.ti 0
+2.9 Packet Sending
+
+The sender of the packet must assemble the SILC Packet Header with
+correct values.  It must set the Source ID of the header as its own
+ID, unless it is forwarding the packet.  It must also set the Destination
+ID of the header to the true destination.  If the destination is client
+it will be Client ID, if it is server it will be Server ID and if it is
+channel it will be Channel ID.
+
+If the sender wants to compress the packet it must apply the
+compression now.  Sender must also compute the padding as described
+in above sections.  Then sender must compute the MAC of the packet.
+
+Then sender encrypts the packet as has been described in above
+sections according whether the packet is normal packet or special
+packet.  The computed MAC must not be encrypted.
+
+
+.ti 0
+2.10 Packet Reception
+
+On packet reception the receiver must check that all fields in the
+SILC Packet Header are valid.  It must check the flags of the
+header and act accordingly.  It must also check the MAC of the packet
+and if it is to be failed the packet must be discarded.  Also if the
+header of the packet includes any bad fields the packet must be
+discarded.
+
+See above sections on the decryption process of the received packet.
+The receiver must also check that the ID's in the header are valid
+ID's.  Unsupported ID types or malformed ID's must cause packet
+rejection.  The padding on the reception is always ignored.
+
+The receiver must also check the packet type and start parsing the
+packet according to the type.  However, note the above sections on
+special packet types and their parsing.
+
+
+.ti 0
+2.11 Packet Routing
+
+Routers are the primary entities in the SILC network that takes care
+of packet routing.  However, normal servers routes packets as well, for
+example, when they are routing channel message to the local clients.
+Routing is quite simple as every packet tells the true origin and the
+true destination of the packet.
+
+It is still recommended for routers that has several routing connections
+to create route cache for those destinations that has faster route than
+the router's primary route.  This information is available for the router
+when other router connects to the router.  The connecting party then
+sends all of its locally connected clients, server and channels.  These
+informations helps to create the route cache.  Also, when new channels
+are created to a cell its information is broadcasted to all routers
+in the network.  Channel ID's are based on router's ID thus it is easy
+to create route cache based on these informations.  If faster route for
+destination does not exist in router's route cache the packet must be
+routed to the primary route (default route).
+
+For server who receives a packet to be routed to its locally connected
+client the server must check whether the particular packet type is
+allowed to be routed to the client.  Not all packets may be sent by
+some odd entity to client that is indirectly connected to the sender.
+See section 2.3 SILC Packet Types and paragraph about indirectly connected
+entities and sending packets to them.  The section mentions the packets
+that may be sent to indirectly connected entities.  It is clear that some
+server cannot send, for example, disconnect packet to client that is not
+directly connected to the server.
+
+
+.ti 0
+2.12 Packet Broadcasting
+
+SILC packets may be broadcasted in SILC network.  However, only router
+server may send or receive broadcast packets.  Client and normal server
+must not send broadcast packets and they must ignore broadcast packets
+if they receive them.  Broadcast packets are sent by setting Broadcast
+flag to the SILC packet header.
+
+Broadcasting packets means that the packet is sent to all routers in
+the SILC network, except to the router that sent the packet.  The router
+receiving broadcast packet must send the packet to its primary route.
+The fact that SILC routers may have several router connections may
+cause problems, such as race conditions inside the SILC network, if
+care is not taken when broadcasting packets.  Router must not send
+the broadcast packet to any other route except to its primary route.
+
+If the primary route of the router is the original sender of the packet
+the packet must not be sent to the primary route.  This may happen
+if router has several router connections and some other router uses
+the router as its primary route.
+
+Routers use broadcast packets to broadcast for example information
+about newly registered clients, servers, channels etc. so that all the
+routers may keep these informations up to date.
+
+
+.ti 0
+2.13 Packet Tunneling
+
+Tunneling is a feature that is available in SILC protocol.  Tunneling
+means that extra SILC Packet Header is applied to the original packet
+and thus hiding the original packet entirely.  There can be some
+interesting applications using tunneling, such as, using ID's based on
+private network IP addresses inside in the tunneled packet.  This can
+open many interesting features relating to connecting to private network
+from the Internet with SILC and many more.  However, this feature is
+optional currently in SILC as there does not exist thorough analysis of
+this feature.  It is with out a doubt that there will be many more
+applications that has not yet been discovered.  Thus, it is left
+to Internet Community to investigate the use of tunneling in SILC
+protocol.  This document is updated according those investigations
+and additional documents on the issue may be written.
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, June 2000.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, June 2000.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 6 Jun 2001
diff --git a/doc/draft-riikonen-silc-pp-02.nroff b/doc/draft-riikonen-silc-pp-02.nroff
new file mode 100644 (file)
index 0000000..15354b3
--- /dev/null
@@ -0,0 +1,2696 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 25 April 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-pp-02.txt                            25 April 2001
+Expires: 25 October 2001
+
+.in 3
+
+.ce 2
+SILC Packet Protocol
+<draft-riikonen-silc-pp-02.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Packet Protocol used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Packet Protocol ..........................................  4
+  2.1 SILC Packet ...............................................  4
+  2.2 SILC Packet Header ........................................  5
+  2.3 SILC Packet Types .........................................  7
+      2.3.1 SILC Packet Payloads ................................ 16
+      2.3.2 Generic payloads .................................... 16
+            2.3.2.1 ID Payload .................................. 16
+            2.3.2.2 Argument Payload ............................ 17
+            2.3.2.3 Channel Payload ............................. 18
+            2.3.2.4 Public Key Payload .......................... 19
+      2.3.3 Disconnect Payload .................................. 19
+      2.3.4 Success Payload ..................................... 19
+      2.3.5 Failure Payload ..................................... 20
+      2.3.6 Reject Payload ...................................... 21
+      2.3.7 Notify Payload ...................................... 22
+      2.3.8 Error Payload ....................................... 21
+      2.3.9 Channel Message Payload ............................. 28
+      2.3.10 Channel Key Payload ................................ 31
+      2.3.11 Private Message Payload ............................ 33
+      2.3.12 Private Message Key Payload ........................ 34
+      2.3.13 Command Payload .................................... 36
+      2.3.14 Command Reply Payload .............................. 37
+      2.3.15 Connection Auth Request Payload .................... 37
+      2.3.16 New ID Payload ..................................... 38
+      2.3.17 New Client Payload ................................. 39
+      2.3.18 New Server Payload ................................. 40
+      2.3.19 New Channel Payload ................................ 41
+      2.3.20 Key Agreement Payload .............................. 42
+      2.3.21 Cell Routers Payload ............................... 43
+  2.4 SILC ID Types ............................................. 44
+  2.5 Packet Encryption And Decryption .......................... 44
+      2.5.1 Normal Packet Encryption And Decryption ............. 45
+      2.5.2 Channel Message Encryption And Decryption ........... 45
+      2.5.3 Private Message Encryption And Decryption ........... 46
+  2.6 Packet MAC Generation ..................................... 47
+  2.7 Packet Padding Generation ................................. 47
+  2.8 Packet Compression ........................................ 48
+  2.9 Packet Sending ............................................ 48
+  2.10 Packet Reception ......................................... 49
+  2.11 Packet Routing ........................................... 49
+  2.12 Packet Broadcasting ...................................... 50
+3 Security Considerations ....................................... 50
+4 References .................................................... 50
+5 Author's Address .............................................. 52
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:   Typical SILC Packet
+Figure 2:   SILC Packet Header
+Figure 3:   ID Payload
+Figure 4:   Argument Payload
+Figure 5:   Channel Payload
+Figure 6:   Public Key Payload
+Figure 7:   Disconnect Payload
+Figure 8:   Success Payload
+Figure 9:   Failure Payload
+Figure 10:   Reject Payload
+Figure 11:  Notify Payload
+Figure 12:  Error Payload
+Figure 13:  Channel Message Payload
+Figure 14:  Channel Key Payload
+Figure 15:  Private Message Payload
+Figure 16:  Private Message Key Payload
+Figure 17:  Command Payload
+Figure 18:  Connection Auth Request Payload
+Figure 19:  New Client Payload
+Figure 20:  New Server Payload
+Figure 21:  Key Agreement Payload
+Figure 22:  Cell Routers Payload
+
+
+.ti 0
+1. Introduction
+
+This document describes a Packet Protocol used in the Secure Internet
+Live Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+The basis of SILC protocol relies in the SILC packets and it is with
+out a doubt the most important part of the protocol.  It is also probably
+the most complicated part of the protocol.  Packets are used all the
+time in the SILC network to send messages, commands and other information.
+All packets in SILC network are always encrypted and their integrity
+is assured by computed MACs.  The protocol defines several packet types
+and packet payloads.  Each packet type usually has a specific packet
+payload that actually defines the contents of the packet.  Each packet
+also includes a default SILC Packet Header that provides sufficient
+information about the origin of the packet and destination of the
+packet.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Packet Protocol
+
+.ti 0
+2.1 SILC Packet
+
+SILC packets deliver messages from sender to receiver securely by
+encrypting important fields of the packet.  The packet consists of
+default SILC Packet Header, Padding, Packet Payload data, and, packet 
+MAC.
+
+The following diagram illustrates typical SILC packet.
+
+
+.in 5
+.nf
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+|   n bytes   | 1 - n bytes |      n bytes       |  n bytes       
+| SILC Header |   Padding   |    Data Payload    |    MAC    
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+.in 3
+
+.ce
+Figure 1:  Typical SILC Packet
+
+
+SILC Header is always the first part of the packet and its purpose
+is to provide information about the packet.  It provides for example
+the packet type, origin of the packet and the destination of the packet.
+The header is variable in length and first two (2) bytes of the
+header (thus first two bytes of the packet) are not encrypted.  The
+first two (2) bytes are the length of the packet which is not encrypted.
+See the following section for description of SILC Packet header.  Packets
+without SILC header or with malformed SILC header MUST be dropped.
+
+Padding follows the packet header.  The purpose of the padding is to
+make the packet multiple by eight (8) or by the block size of the
+cipher used in the encryption, which ever is larger.  The maximum
+length of padding is currently 16 bytes.  The padding is always
+encrypted.
+
+Data payload area follows padding and it is the actual data of the
+packet.  The packet data is the packet payloads defined in this
+protocol.  The data payload area is always encrypted.
+
+The last part of SILC packet is the packet MAC that assures the
+integrity of the packet.  The MAC is always computed from the packet
+before the encryption is applied to the packet.  If compression is used
+in the packet the MAC is computed after the compression has been
+applied.  The compression, on the other hand, is always applied before
+encryption.
+
+All fields in all packet payloads are always in MSB (most significant
+byte first) order.
+
+
+.ti 0
+2.2 SILC Packet Header
+
+The SILC packet header is applied to all SILC packets and it is
+variable in length.  The purpose of SILC Packet header is to provide
+detailed information about the packet.  The receiver of the packet
+uses the packet header to parse the packet and gain other relevant
+parameters of the packet.
+
+The following diagram represents the SILC packet header.  (*) indicates
+that this field is never encrypted.  Other fields are always encrypted.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length *       |     Flags     |  Packet Type  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source ID Length       |     Destination ID Length     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Src ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                           Source ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Dst ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                         Destination ID                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  SILC Packet Header
+
+
+.in 6
+o Payload Length (2 bytes) - Is the length of the packet
+  not including the padding of the packet.  This field must
+  not be encrypted but must always be authenticated.
+
+o Flags (1 byte) - Indicates flags to be used in packet
+  processing.  Several flags may be set by ORing the flags
+  together.
+
+  The following flags are reserved for this field:
+
+
+     No flags                  0x00
+
+       In this case the field is ignored.
+
+
+     Private Message Key       0x01
+
+       Indicates that the packet must include private
+       message that is encrypted using private key set by
+       client.  Servers does not know anything about this
+       key and this causes that the private message is
+       not handled by the server at all, it is just
+       passed along.  See section 2.5.3 Private Message
+       Encryption And Decryption for more information.
+
+
+     List                      0x02
+  
+       Indicates that the packet consists of list of
+       packet payloads indicated by the Packet Type field.
+       The payloads are added one after the other.  Note that
+       there are packet types that must not be used as
+       list.  Parsing of list packet is done by calculating
+       the length of each payload and parsing them one by
+       one.
+
+
+     Broadcast                 0x04
+
+       Marks the packet to be broadcasted.  Client cannot
+       send broadcast packet and normal server cannot send
+       broadcast packet.  Only router server may send broadcast
+       packet.  The router receiving of packet with this flag 
+       set MUST send (broadcast) the packet to its primary
+       route.  If router has several router connections the
+       packet may be sent only to the primary route.  See
+       section 2.12 Packet Broadcasting for description of 
+       packet broadcasting.
+
+.in 3
+
+
+
+
+o Packet Type (1 byte) - Is the type of the packet. Receiver 
+  uses this field to parse the packet.  See section 2.3
+  SILC Packets for list of defined packet types.
+
+o Source ID Length (2 bytes) - Indicates the length of the
+  Source ID field in the header, not including this or any
+  other fields.
+
+o Destination ID Length (2 bytes) - Indicates the length of the
+  Destination ID field in the header, not including this or
+  any other fields.
+
+o Src ID Type (1 byte) - Indicates the type of ID in the
+  Source ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Source ID (variable length) - The actual source ID that
+  indicates which is the original sender of the packet.
+
+o Dst ID Type (1 byte) - Indicates the type of ID in the
+  Destination ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Destination ID (variable length) - The actual destination
+  ID that indicates which is the end receiver of the packet.
+
+
+.ti 0
+2.3 SILC Packet Types
+
+SILC packet types defines the contents of the packet and it is used by
+the receiver to parse the packet.  The packet type is 8 bits, as a one
+byte, in length.  The range for the packet types are from 0 - 255,
+where 0 is never sent and 255 is currently reserved for future
+extensions and MUST NOT be defined to any other purpose.  Every SILC
+specification compliant implementation SHOULD support all of these packet
+types.
+
+The below list of the SILC Packet types includes reference to the packet
+payload as well.  Packet payloads are the actual packet, that is, the data
+that the packet consists of.  Each packet type defines packet payload 
+which usually may only be sent with the specific packet type.
+
+Most of the packets are packets that must be destined directly to entity
+that is connected to the sender.  It is not allowed, for example, for
+router to send disconnect packet to client that is not directly connected
+to the router.  However, there are some special packet types that may
+be destined to some entity that the sender has not direct connection
+with.  These packets are for example private message packets, channel
+message packets, command packets and some other packets that may be
+broadcasted in the SILC network.  If the packet is allowed to be sent to
+indirectly connected entity it is mentioned separately in the packet
+description (unless it is obvious as in private and channel message
+packets).  Other packets MUST NOT be sent or accepted, if sent, to
+indirectly connected entities.
+
+List of SILC Packet types are defined as follows.
+
+.in 1
+     0    SILC_PACKET_NONE
+
+          This type is reserved and it is never sent.         
+
+
+     1    SILC_PACKET_DISCONNECT
+
+          This packet is sent to disconnect the remote end.  Reason of
+          the disconnection is sent inside the packet payload.  Client
+          usually does not send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.3 Disconnect Payload
+
+
+     2    SILC_PACKET_SUCCESS
+
+          This packet is sent upon successful execution of some protocol.
+          The status of the success is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.4 Success Payload
+
+
+     3    SILC_PACKET_FAILURE
+
+          This packet is sent upon failure of some protocol.  The status
+          of the failure is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.5 Failure Payload
+
+
+     4    SILC_PACKET_REJECT
+
+          This packet MAY be sent upon rejection of some protocol.
+          The status of the rejection is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.6 Reject Payload
+
+
+     5    SILC_PACKET_NOTIFY
+
+          This packet is used to send notify message, usually from
+          server to client, although it MAY be sent from server to another
+          server as well.  Client MUST NOT send this packet.  Server MAY
+          send this packet to channel as well when the packet is 
+          distributed to all clients on the channel.
+
+          Payload of the packet:  See section 2.3.7 Notify Payload.
+
+
+     6    SILC_PACKET_ERROR
+
+          This packet is sent when an error occurs.  Server MAY
+          send this packet.  Client MUST NOT send this packet.  The
+          client MAY entirely ignore the packet, however, server is
+          most likely to take action anyway.  This packet MAY be sent
+          to entity that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.8 Error Payload.
+
+
+     7    SILC_PACKET_CHANNEL_MESSAGE
+
+          This packet is used to send messages to channels.  The packet
+          includes Channel ID of the channel and the actual message to
+          the channel.  Messages sent to the channel are always protected
+          by channel specific keys.  Channel Keys are distributed by
+          SILC_PACKET_CHANNEL_KEY packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.9 Channel Message 
+                                  Payload
+
+
+     8    SILC_PACKET_CHANNEL_KEY
+
+          This packet is used to distribute new key for particular
+          channel.  Each channel has their own independent keys that
+          is used to protect the traffic on the channel.  Only server
+          may send this packet.  This packet MAY be sent to entity
+          that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.10 Channel Key Payload
+
+
+     9    SILC_PACKET_PRIVATE_MESSAGE
+
+          This packet is used to send private messages from client
+          to another client.  By default, private messages are protected
+          by session keys established by normal key exchange protocol.
+          However, it is possible to use specific key to protect private
+          messages.  SILC_PACKET_PRIVATE_MESSAGE_KEY packet is used to 
+          agree the key with the remote client.  Pre-shared key MAY be 
+          used as well if both of the client knows it, however, it needs 
+          to be agreed outside SILC.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.11 Private Message
+                                  Payload
+
+
+     10   SILC_PACKET_PRIVATE_MESSAGE_KEY
+
+          This packet is used to agree about a key to be used to protect
+          the private messages between two clients.  If this is not sent
+          the normal session key is used to protect the private messages
+          inside SILC network.  Agreeing to use specific key to protect
+          private messages adds security, as no server between the two
+          clients will be able to decrypt the private message.  However,
+          servers inside SILC network are considered to be trusted, thus
+          using normal session key to protect private messages does not
+          degrade security.  Whether to agree to use specific keys by
+          default or to use normal session keys by default, is 
+          implementation specific issue.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.12 Private Message
+                                  Key Payload
+
+
+     11   SILC_PACKET_COMMAND
+
+          This packet is used to send commands from client to server.
+          Server MAY send this packet to other servers as well.  All
+          commands are listed in their own section SILC Command Types
+          in [SILC4].  The contents of this packet is command specific.
+          This packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.13 Command Payload
+
+
+     12   SILC_PACKET_COMMAND_REPLY
+
+          This packet is sent as reply to the SILC_PACKET_COMMAND packet.
+          The contents of this packet is command specific.  This packet
+          MAY be sent to entity that is indirectly connected to the
+          sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.14 Command Reply 
+                                  Payload and section 2.3.13 Command
+                                  Payload
+
+
+     13   SILC_PACKET_KEY_EXCHANGE
+
+          This packet is used to start SILC Key Exchange Protocol, 
+          described in detail in [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     14   SILC_PACKET_KEY_EXCHANGE_1
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     15   SILC_PACKET_KEY_EXCHANGE_2
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     16   SILC_PACKET_CONNECTION_AUTH_REQUEST
+
+          This packet is used to request the authentication method to
+          be used in the SILC Connection Authentication Protocol.  If 
+          initiator of the protocol does not know the mandatory 
+          authentication method this packet MAY be used to determine it.
+
+          The party receiving this payload MUST respond with the same
+          packet including the mandatory authentication method.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.15 Connection Auth
+                                  Request Payload
+
+
+
+
+     17   SILC_PACKET_CONNECTION_AUTH
+
+          This packet is used to start and perform the SILC Connection
+          Authentication Protocol.  This protocol is used to authenticate
+          the connecting party.  The protocol is described in detail in
+          [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Authentication
+                                  Protocol and it sub sections in [SILC].
+
+
+     18   SILC_PACKET_NEW_ID
+
+          This packet is used to distribute new ID's from server to
+          router and from router to all routers in the SILC network.
+          This is used when for example new client is registered to
+          SILC network.  The newly created ID's of these operations are
+          distributed by this packet.  Only server may send this packet,
+          however, client MUST be able to receive this packet.  This
+          packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          Payload of the packet:  See section 2.3.16 New ID Payload
+
+
+     19   SILC_PACKET_NEW_CLIENT
+
+          This packet is used by client to register itself to the   
+          SILC network.  This is sent after key exchange and  
+          authentication protocols has been completed.  Client sends
+          various information about itself in this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.17 New Client Payload
+
+
+     20   SILC_PACKET_NEW_SERVER
+
+          This packet is used by server to register itself to the
+          SILC network.  This is sent after key exchange and 
+          authentication protocols has been completed.  Server sends
+          this to the router it connected to, or, if router was
+          connecting, to the connected router.  Server sends its
+          Server ID and other information in this packet.  The client
+          MUST NOT send or receive this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.18 New Server Payload
+
+
+     21   SILC_PACKET_NEW_CHANNEL
+
+          This packet is used to notify routers about newly created
+          channel.  Channels are always created by the router and it MUST
+          notify other routers about the created channel.  Router sends
+          this packet to its primary route.  Client MUST NOT send this
+          packet.  This packet MAY be sent to entity that is indirectly
+          connected to the sender.
+
+          Payload of the packet:  See section 2.3.19 New Channel Payload
+
+
+     22   SILC_PACKET_REKEY
+
+          This packet is used to indicate that re-key must be performed
+          for session keys.  See section Session Key Regeneration in
+          [SILC1] for more information.  This packet does not have
+          a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     23   SILC_PACKET_REKEY_DONE
+
+          This packet is used to indicate that re-key is performed and
+          new keys must be used hereafter.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+     
+     24   SILC_PACKET_HEARTBEAT
+
+          This packet is used by clients, servers and routers to keep the
+          connection alive.  It is recommended that all servers implement
+          keepalive actions and perform it to both direction in a link.
+          This packet does not have a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     25   SILC_PACKET_KEY_AGREEMENT
+
+          This packet is used by clients to request key negotiation 
+          between another client in the SILC network.  If the negotiation
+          is started it is performed using the SKE protocol.  The result of
+          the negotiation, the secret key material, can be used for
+          example as private message key.  The server and router MUST NOT
+          send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.20 Key Agreement Payload
+
+
+    26    SILC_PACKET_CELL_ROUTERS
+
+          This packet is used by primary router in the cell to notify its
+          primary router what other routers (backup routers) exist in the
+          cell.  In case of failure of the primary router in the cell the
+          first router in the list will act as primary router of the cell.
+          This packet MAY be sent at anytime after connection has been
+          registered to the primary router.  The client MUST NOT send this
+          packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.21 Cell Routers Payload
+
+
+     27 - 199
+
+          Currently undefined commands.
+
+
+     200 - 254
+
+          These packet types are reserved for private use and they will
+          not be defined by this document.
+
+
+
+
+     255  SILC_PACKET_MAX
+
+          This type is reserved for future extensions and currently it 
+          MUST NOT be sent.
+.in 3
+
+
+.ti 0
+2.3.1 SILC Packet Payloads
+
+All payloads resides in the main data area of the SILC packet.  However
+all payloads MUST be at the start of the data area after the SILC
+packet header and padding.  All fields in the packet payload are always
+encrypted, as they reside in the data area of the packet which is
+always encrypted.
+
+Payloads described in this section are common payloads that MUST be
+accepted anytime during SILC session.  Most of the payloads may only
+be sent with specific packet type which is defined in the description
+of the payload.
+
+There are a lot of other payloads in the SILC as well.  However, they
+are not common in the sense that they could be sent at any time. 
+These payloads are not described in this section.  These are payloads
+such as SILC Key Exchange payloads and so on.  These are described
+in [SILC1], [SILC3] and [SILC4].
+
+
+.ti 0
+2.3.2 Generic payloads
+
+This section describes generic payloads that are not associated to any
+specific packet type.  They can be used for example inside some other
+packet payloads.
+
+
+.ti 0
+2.3.2.1 ID Payload
+
+This payload can be used to send an ID.  ID's are variable in length
+thus this payload provides a way to send variable length ID's.
+
+
+
+
+
+
+
+
+
+
+
+
+The following diagram represents the ID Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             ID Type           |           ID Length           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           ID Data                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 3:  ID Payload
+
+
+.in 6
+o ID Type (2 bytes) - Indicates the type of the ID.  See 
+  section 2.4 SILC ID Types for list of defined ID types.
+
+o ID Length (2 bytes) - Length of the ID Data area not 
+  including the length of any other fields in the payload.
+
+o ID Data (variable length) - The actual ID data.
+.in 3
+
+
+.ti 0
+2.3.2.2 Argument Payload
+
+Argument Payload is used to set arguments for any packet payload that
+needs and supports arguments, such as commands.  Number of arguments
+associated with a packet MUST be indicated by the packet payload which
+needs the arguments.  Argument Payloads MUST always reside right after
+the packet payload needing the arguments.  Incorrect amount of argument
+payloads MUST cause rejection of the packet.  The following diagram
+represents the Argument Payload.
+
+The following diagram represents the Argument Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | Argument Type |               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               +
+|                                                               |
+~                        Argument Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 4:  Argument Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the argument payload data 
+  area not including the length of any other fields in the 
+  payload.
+
+o Argument Type (1 byte) - Indicates the type of the argument.  
+  Every argument may have a specific type that MUST be defined
+  by the packet payload needing the argument.  For example
+  every command specify a number for each argument that maybe 
+  associated with the command.  By using this number the receiver 
+  of the packet knows what type of argument this is.  If there is
+  no specific argument type this field is set to zero (0).
+
+o Argument Data (variable length) - Argument data.
+.in 3
+
+
+.ti 0
+2.3.2.3 Channel Payload
+
+Generic Channel Payload may be used to send information about channel,
+its name, the Channel ID and a mode.
+
+The following diagram represents the Channel Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Name Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                           Mode Mask                           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  New Channel Payload
+
+
+.in 6
+o Channel Name Length (2 bytes) - Length of the channel name
+  field.
+
+o Channel Name (variable length) - The name of the channel.
+
+o Channel ID Length (2 bytes) - Length of the Channel ID field.
+
+o Channel ID (variable length) - The Channel ID.
+
+o Mode Mask (4 bytes) - A mode.  This can be the mode of the
+  channel but it can also be the mode of the client on the
+  channel.  The contents of this field is dependent of the
+  usage of this payload.  The usage is defined separately
+  when this payload is used.  This is a 32 bit MSB first value.
+.in 3
+
+
+.ti 0
+2.3.2.4 Public Key Payload
+
+Generic Public Key Payload may be used to send different types of
+public keys and certificates.
+
+The following diagram represents the Public Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  Public Key Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  See the [SILC3] for defined public key types.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+.in 3
+
+
+.ti 0
+2.3.3 Disconnect Payload
+
+Disconnect payload is sent upon disconnection.  The payload is simple;
+reason of disconnection is sent to the disconnected party.
+
+The payload may only be sent with SILC_PACKET_DISCONNECT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the Disconnect Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Disconnect Message                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 7:  Disconnect Payload
+
+
+
+
+.in 6
+o Disconnect Message (variable length) - Human readable
+  reason of the disconnection.
+.in 3
+
+
+.ti 0
+2.3.4 Success Payload
+
+Success payload is sent when some protocol execution is successfully
+completed.  The payload is simple; indication of the success is sent.
+This may be any data, including binary or human readable data.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Success Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 8:  Success Payload
+
+
+.in 6
+o Success Indication (variable length) - Indication of
+  the success.  This may be for example some flag that
+  indicates the protocol and the success status or human
+  readable success message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+
+.ti 0
+2.3.5 Failure Payload
+
+This is opposite of Success Payload.  Indication of failure of
+some protocol is sent in the payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Failure Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 9:  Failure Payload
+
+
+.in 6
+o Failure Indication (variable length) - Indication of
+  the failure.  This may be for example some flag that
+  indicates the protocol and the failure status or human
+  readable failure message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.6 Reject Payload
+
+This payload is sent when some protocol is rejected to be executed.
+Other operations MAY send this as well that was rejected.  The
+indication of the rejection is sent in the payload.  The indication
+may be binary or human readable data.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Reject Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 10:  Reject Payload
+
+
+.in 6
+o Reject Indication (variable length) - Indication of
+  the rejection.  This maybe for example some flag that
+  indicates the protocol and the rejection status or human
+  readable rejection message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.7 Notify Payload
+
+Notify payload is used to send notify messages.  The payload is usually
+sent from server to client, however, server MAY send it to another
+server as well.  This payload MAY also be sent to a channel.  Client
+MUST NOT send this payload.  The receiver of this payload MAY ignore
+the contents of the payload, however, notify message SHOULD be audited.
+
+The payload may only be sent with SILC_PACKET_NOTIFY packet.  It MUST
+not be sent in any other packet type.  The following diagram represents
+the Notify Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|          Notify Type          |        Payload Length         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Argument Nums |
++-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 11:  Notify Payload
+
+
+.in 6
+o Notify Type (2 bytes) - Indicates the type of the notify
+  message.
+
+o Payload Length (2 bytes) - Length of the entire Notify Payload
+  including any associated Argument Payloads.
+
+o Argument Nums (2 bytes) - Indicates the number of Argument
+  Payloads associated to this payload.  Notify types may define
+  arguments to be send along the notify message.
+.in 3
+
+The following list of currently defined notify types.  The format for
+notify arguments is same as in SILC commands described in [SILC4]. 
+Also, all ID's sent in arguments are sent inside ID Payload.
+
+.in 6
+0     SILC_NOTIFY_TYPE_NONE
+
+      If no specific notify type apply for the notify message this type
+      MAY be used.
+
+      Max Arguments:  1
+          Arguments:  (1) <message>
+
+      The <message> is implementation specific free text string.
+      Receiver MAY ignore this message.
+
+
+1     SILC_NOTIFY_TYPE_INVITE
+
+      Sent when an client is invited to a channel.  This is also sent
+      when the invite list of the channel is changed.  This notify type
+      is sent between routers and if an client was invited, to the 
+      client as well.  In this case the packet is destined to the client.
+
+      Max Arguments:  5
+          Arguments:  (1) <Channel ID>          (2) <channel name>
+                      (3) [<sender Client ID>]  (4) [<adding client>]
+                      (5) [<removing client>]
+
+      The <Channel ID> is the channel.  The <channel name> is the name
+      of the channel and is provided because the client which receives 
+      this notify packet may not have a way to resolve the name of the
+      channel from the <Channel ID>.  The <sender Client ID> is the
+      Client ID which invited the client to the channel.  The <adding
+      client> and the <removing client> indicates the added or removed
+      client from the channel's invite list.  The format of the <adding
+      client> and the <removing client> is defined in the [SILC4] with
+      SILC_COMMAND_INVITE command.
+
+      The <adding client> and <removing client> MUST NOT be sent when
+      the packet is destined to a client.
+
+
+2     SILC_NOTIFY_TYPE_JOIN
+
+      Sent when client has joined to a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) [<Client ID>]       (2) <Channel ID>
+
+      The <Client ID> is the client that joined to the channel indicated
+      by the <Channel ID>.
+
+
+3     SILC_NOTIFY_TYPE_LEAVE
+
+      Sent when client has left a channel.  The server must distribute
+      this type only to the local clients on the channel and then send
+      it to its primary router.  The router or server receiving the
+      packet distributes this type to the local clients on the channel
+      and broadcast it to the network.
+
+      Max Arguments:  1
+          Arguments:  (1) <Client ID>
+
+      The <Client ID> is the client which left the channel.
+
+
+4     SILC_NOTIFY_TYPE_SIGNOFF
+
+      Sent when client signoff from SILC network.  The server MUST
+      distribute this type only to the local clients on the channel and
+      then send it to its primary router.  The router or server receiving
+      the packet distributes this type to the local clients on the
+      channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <message>
+
+      The <Client ID> is the client which left SILC network.  The
+      <message> is free text string indicating the reason of the signoff.
+
+
+5     SILC_NOTIFY_TYPE_TOPIC_SET
+
+      Sent when topic is set/changed on a channel.  This type must be
+      sent only to the clients which is joined on the channel which
+      topic was set or changed.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <topic>
+
+      The <Client ID> is the client which set or changed the <topic>.
+
+
+6     SILC_NOTIFY_TYPE_NICK_CHANGE
+
+      Sent when client changes nick on a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Client ID>  (2) <New Client ID>
+
+      The <Old Client ID> is the old ID of the client which changed
+      the nickname.  The <New Client ID> is the new ID generated by
+      the change of the nickname.
+
+
+7     SILC_NOTIFY_TYPE_CMODE_CHANGE
+
+      Sent when channel mode has changed.  This type MUST be sent only
+      to the clients which is joined on the channel which mode was
+      changed.
+
+      Max Arguments:  4
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) [<cipher>]    (4) <[hmac>]     
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing channel mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The client can safely
+      ignore the <cipher> argument since the SILC_PACKET_CHANNEL_KEY
+      packet will force the new channel key change anyway.  The <hmac>
+      argument is important since the client is responsible of setting
+      the new HMAC and the hmac key into use.
+
+
+8     SILC_NOTIFY_TYPE_CUMODE_CHANGE
+
+      Sent when user mode on channel has changed.  This type MUST be
+      sent only to the clients which is joined on the channel where
+      the target client is on.
+
+      Max Arguments:  3
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+                      (3) <Target Client ID>
+
+      The <Client ID> is the client which changed the mode.  The
+      <mode mask> is the new mode mask of the channel.  The <Target
+      Client ID> is the client which mode was changed.
+
+
+9     SILC_NOTIFY_TYPE_MOTD
+
+      Sent when Message of the Day (motd) is sent to a client.
+
+      Max Arguments:  1
+          Arguments:  (1) <motd>
+
+      The <motd> is the Message of the Day.
+
+
+10    SILC_NOTIFY_TYPE_CHANNEL_CHANGE
+
+      Sent when channel's ID has changed for a reason or another.
+      This is sent by normal server to the client.  This can also be
+      sent by router to other server to force the Channel ID change.
+      The Channel ID MUST be changed to use the new one.  When sent
+      to clients, this type MUST be sent only to the clients which is
+      joined on the channel.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Channel ID>  (2) <New Channel ID>
+
+      The <Old Channel ID> is the channel's old ID and the <New
+      Channel ID> is the new one that MUST replace the old one.
+
+
+11    SILC_NOTIFY_TYPE_SERVER_SIGNOFF
+
+      Sent when server quits SILC network.  Those clients from this
+      server that are on channels must be removed from the channel.
+
+      Max Arguments:  2000
+          Arguments:  (1) <Server ID>   (n) [<Client ID>   [...]
+
+      The <Server ID> is the server's ID.  The rest of the arguments
+      are the Client ID's of the client's which are coming from this
+      server and are thus quitting the SILC network also.  If the
+      maximum number of arguments are reached another 
+      SILC_NOTIFY_TYPE_SERVER_SIGNOFF notify packet MUST be sent.
+      When this notify packet is sent between routers the Client ID's
+      MAY be omitted.
+
+
+12    SILC_NOTIFY_TYPE_KICKED
+
+      Sent when a client has been kicked from a channel.  This is
+      sent also to the client which was kicked from the channel.
+      The client which was kicked from the channel MUST be removed
+      from the channel.  This notify type is always destined to the
+      channel.  The router or server receiving the packet distributes
+      this type to the local clients on the channel and broadcast it
+      to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was kicked from the channel.
+      The kicker may have set the <comment> to indicate the reason for
+      the kicking.
+
+
+13    SILC_NOTIFY_TYPE_KILLED
+
+      Sent when a client has been killed from the network.  This is sent 
+      also to the client which was killed from the network.  The client
+      which was killed from the network MUST be removed from the network.
+      This notify type is destined directly to the client which was
+      killed and to channel if the client is on any channel.  The router
+      or server receiving the packet distributes this type to the local
+      clients on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was killed from the network.
+      The killer may have set the <comment> to indicate the reason for
+      the killing.
+
+
+14    SILC_NOTIFY_TYPE_UMODE_CHANGE
+
+      Sent when user's mode in the SILC changes.  This type is sent
+      only between routers as broadcast packet.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+
+      The <Client ID> is the client which mode was changed.  The
+      <mode mask> is the new mode mask.
+
+
+15    SILC_NOTIFY_TYPE_BAN
+
+      Sent when the ban list of the channel is changed.  This type is
+      sent only between routers as broadcast packet.
+
+      Max Arguments:  3
+          Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                      (3) [<removing client>]
+
+      The <Channel ID> is the channel which ban list was changed.  The
+      <adding client> is used to indicate that a ban was added and the
+      <removing client> is used to indicate that a ban was removed from
+      the ban list.  The format of the <adding client> and the 
+      <removing client> is defined in the [SILC4] with SILC_COMMAND_BAN
+      command.
+
+.in 3
+
+Notify types starting from 16384 are reserved for private notify
+message types.
+
+
+.ti 0
+2.3.8 Error Payload
+
+Error payload is sent upon error.  Error may occur in various
+conditions when server sends this packet.  Client MUST NOT send this
+payload but MUST be able to accept it.  However, client MAY
+totally ignore the contents of the packet as server is going to
+take action on the error anyway.  However, it is recommended
+that the client takes error packet seriously.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Error Message                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 12:  Error Payload
+
+
+.in 6
+o Error Message (variable length) - Human readable error
+  message.
+.in 3
+
+
+.ti 0
+2.3.9 Channel Message Payload
+
+Channel messages are the most common messages sent in the SILC.
+Channel Message Payload is used to send message to channels.  These
+messages can only be sent if client has joined to some channel.
+Even though this packet is the most common in SILC it is still
+special packet.  Some special handling on sending and reception
+of channel message is required.
+
+Padding MUST be applied into this payload since the payload is
+encrypted separately from other parts of the packet with the
+channel specific key.  Hence the requirement of the padding.  
+The padding SHOULD be random data.  The packet MUST be made
+multiple by eight (8) or by the block size of the cipher, which
+ever is larger.
+
+The SILC header in this packet is encrypted with the session key
+of the next receiver of the packet.  Nothing else is encrypted
+with that key.  Thus, the actual packet and padding to be
+encrypted with the session key is SILC Header plus padding to it
+to make it multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.
+
+Receiver of the the channel message packet is able to determine
+the channel the message is destined to by checking the destination
+ID from the SILC Packet header which tells the destination channel.
+The original sender of the packet is also determined by checking
+the source ID from the header which tells the client which sent
+the message.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_MESSAGE packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Message Payload.
+
+(*) indicates that the field is not encrypted.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |         Message Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Message Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Padding Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                            Padding                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                              MAC                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Initial Vector *                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 13:  Channel Message Payload
+
+
+.in 6
+o Flags (2 bytes) - Includes the flags of the channel
+  messages.  The flags can indicate a reason or purpose
+  for the channel message.  Note that the Private Message
+  Payload use these same flags for the same purpose.  The
+  following flags are defined:
+
+  0x0000  SILC_MESSAGE_FLAG_NONE
+
+          No specific flags set.
+
+  0x0001  SILC_MESSAGE_FLAG_AUTREPLY
+
+          This message is an automatic reply to an earlier
+          received message.
+
+  0x0002  SILC_MESSAGE_FLAG_NOREPLY
+
+          There should not be reply messages to this
+          message.
+
+  0x0004  SILC_MESSAGE_FLAG_ACTION
+
+          The sender is performing an action and the message
+          is the indication of the action.
+
+  0x0008  SILC_MESSAGE_FLAG_NOTICE
+
+          The message is for example an informational notice
+          type message.
+
+  0x0010  SILC_MESSAGE_FLAG_REQUEST
+
+          This is a generic request flag to send request
+          messages.
+
+  0x0020 - 0x0200 RESERVED
+
+          Reserved for future flags
+
+  0x0400 - 0x8000 PRIVATE RANGE
+
+          Private range for free use.
+
+o Message Length (2 bytes) - Indicates the length of the
+  the Message Data field in the payload, not including any 
+  other field.
+
+o Message Data (variable length) - The actual message to
+  the channel.
+
+o Padding Length (2 bytes) - Indicates the length of the
+  Padding field in the payload, not including any other
+  field.
+
+o Padding (variable length) - The padding that MUST be
+  applied because this payload is encrypted separately from
+  other parts of the packet.
+
+o MAC (variable length) - The MAC computed from the
+  Message Length, Message Data, Padding Length and Padding
+  fields.  This protects the integrity of the plaintext
+  channel message.  The receiver can verify from the MAC
+  whether the message decrypted correctly.  Also, if more than
+  one private key has been set for the channel, the receiver
+  can verify which of the keys decrypted the message 
+  correctly.  Note that, this field is encrypted and MUST
+  be added to the padding calculation.
+
+o Initial Vector (variable length) - The initial vector
+  that has been used in packet encryption.  It needs to be
+  used in the packet decryption as well.  What this field
+  includes is implementation issue.  However, it is 
+  RECOMMENDED that it would be random data or, perhaps,
+  a timestamp.  It is NOT RECOMMENDED to use zero (0) as an
+  initial vector.  This field is not encrypted.  This field
+  is not included into the padding calculation.  Length
+  of this field equals the cipher's block size.  This field
+  is, however, authenticated.
+.in 3
+
+
+.ti 0
+2.3.10 Channel Key Payload
+
+All traffic in channels are protected by channel specific keys.
+Channel Key Payload is used to distribute channel keys to all
+clients on the particular channel.  Channel keys are sent when
+the channel is created, when new user joins to the channel and
+whenever a user has left a channel.  Server creates the new
+channel key and distributes it to the clients by encrypting this
+payload with the session key shared between the server and
+the client.  After that, client starts using the key received
+in this payload to protect the traffic on the channel.
+
+The client which is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity which sent the SILC_COMMAND_JOIN
+command.
+
+Channel keys are cell specific thus every router in the cell have
+to create a channel key and distribute it if any client in the
+cell has joined to a channel.  Channel traffic between cell's
+are not encrypted using channel keys, they are encrypted using
+normal session keys between two routers.  Inside a cell, all
+channel traffic is encrypted with the specified channel key.
+Channel key should expire periodically, say, in one hour, in
+which case new channel key is created and distributed.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_KEY packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Cipher Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Key Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Key                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 14:  Channel Key Payload
+
+
+
+.in 6
+o Channel ID Length (2 bytes) - Indicates the length of the
+  Channel ID field in the payload, not including any other
+  field.
+
+o Channel ID (variable length) - The Channel ID of the
+  channel this key is meant for.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher used
+  in the protection of channel traffic.  This name is
+  initially decided by the creator of the channel but it
+  MAY change during the life time of the channel as well.
+
+o Channel Key Length (2 bytes) - Indicates the length of the
+  Channel Key field in the payload, not including any other
+  field.
+
+o Channel Key (variable length) - The actual channel key
+  material.
+.in 3
+
+
+.ti 0
+2.3.11 Private Message Payload
+
+Private Message Payload is used to send private message between
+two clients (or users for that matter).  The messages are sent only
+to the specified user and no other user inside SILC network is
+able to see the message.  The message is protected by the session 
+key established by the SILC Key Exchange Protocol.  However,
+it is also possible to agree to use a private key to protect
+just the private messages.  See section 2.3.11 Private Message
+Key Payload for detailed description of how to agree to use
+specific key.
+
+If normal session key is used to protect the message, every server
+between the sender client and the receiving client MUST decrypt the
+packet and always re-encrypt it with the session key of the next
+receiver of the packet.  See section Client To Client in [SILC1].
+
+When private key is used to protect the message, servers between 
+the sender and the receiver needs not to decrypt/re-encrypt the 
+packet.  Section Client To Client in [SILC1] gives example of this
+scheme as well.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Payload.
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |      Message Data Length      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                          Message Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                             Padding                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 15:  Private Message Payload
+
+
+.in 6
+o Flags (2 bytes) - This field includes the flags of the
+  private message.  They can indicate a different reason or
+  purpose for the private message.  See the section 2.3.9
+  Channel Message Payload for defined flags.  Note that
+  the Channel Message Payload use the same flags for the
+  same purpose.
+
+o Message Data Length (2 bytes) - Indicates the length of the
+  Message Data field, not includes any other field.
+
+o Message Data (variable length) - The actual message to
+  the client.  Rest of the packet is reserved for the message
+  data.
+
+o Padding (variable length) - This field is present only
+  when the private message payload is encrypted with private
+  message key.  In this case the padding is applied to make
+  the payload multiple by eight (8), or by the block size of
+  the cipher, which ever is larger.  When encrypted with
+  normal session keys, this field MUST NOT be included.
+.in 3
+
+
+.ti 0
+2.3.12 Private Message Key Payload
+
+This payload is used to send key from client to another client that
+is going to be used to protect the private messages between these
+two clients.  If this payload is not sent normal session key 
+established by the SILC Key Exchange Protocol is used to protect
+the private messages.
+
+This payload may only be sent by client to another client.  Server
+MUST NOT send this payload at any time.  After sending this payload
+the sender of private messages must set the Private Message Key
+flag into SILC Packet Header.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE_KEY 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Private Message Key Length   |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Private Message Key                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Cipher Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 16:  Private Message Key Payload
+
+
+
+
+.in 6
+o Private Message Key Length (2 bytes) - Indicates the length 
+  of the Private Message Key field in the payload, not including 
+  any other field.
+
+o Private Message Key (variable length) - The actual private
+  message key material.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher Name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher to use
+  in the private message encryption.  If this field does not
+  exist then the default cipher of the SILC protocol is used.
+  See the [SILC1] for defined ciphers.
+.in 3
+
+
+
+.ti 0
+2.3.13 Command Payload
+
+Command Payload is used to send SILC commands from client to server.
+Also server MAY send commands to other servers.  The following diagram
+represents the Command Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | SILC Command  | Arguments Num |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Command Identifier      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 17:  Command Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire command 
+  payload including any command argument payloads associated 
+  with this payload.
+
+o SILC Command (1 byte) - Indicates the SILC command.  This MUST 
+  be set to non-zero value.  If zero (0) value is found in this 
+  field the packet MUST be discarded.
+
+o Arguments Num (1 byte) - Indicates the number of arguments
+  associated with the command.  If there are no arguments this
+  field is set to zero (0).  The arguments MUST follow the 
+  command payload.  See section 2.3.2.2 for definition of the
+  Argument Payload.
+
+o Command Identifier (2 bytes) - Identifies this command at the
+  sender's end.  The entity which replies to this command MUST
+  set the value found from this field into the Command Payload
+  used to send the reply to the sender.  This way the sender
+  can identify which command reply belongs to which originally
+  sent command.  What this field includes is implementation
+  issue but it is RECOMMENDED that wrapping counter value is
+  used in the field.  Value zero (0) in this field means that
+  no specific value is set.
+.in 3
+
+See [SILC4] for detailed description of different SILC commands,
+their arguments and their reply messages.
+
+
+
+
+.ti 0
+2.3.14 Command Reply Payload
+
+Command Reply Payload is used to send replies to the commands.  The
+Command Reply Payload is identical to the Command Payload thus see
+the upper section for the Command Payload specification.
+
+The entity which sends the reply packet MUST set the Command Identifier
+field in the reply packet's Command Payload to the value it received
+in the original command packet.
+
+See SILC Commands in [SILC4] for detailed description of different
+SILC commands, their arguments and their reply messages.
+
+
+.ti 0
+2.3.15 Connection Auth Request Payload
+
+Client MAY send this payload to server to request the authentication
+method that must be used in authentication protocol.  If client knows 
+this information beforehand this payload is not necessary to be sent.
+Server performing authentication with another server MAY also send
+this payload to request the authentication method.  If the connecting
+server already knows this information this payload is not necessary
+to be sent.
+
+Server receiving this request MUST reply with same payload sending
+the mandatory authentication method.  Algorithms that may be required
+to be used by the authentication method are the ones already 
+established by the SILC Key Exchange protocol.  See section Key
+Exchange Start Payload in [SILC3] for detailed information.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Connection Auth Request Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Connection Type        |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 18:  Connection Auth Request Payload
+
+
+.in 6
+o Connection Type (2 bytes) - Indicates the type of the
+  connection.  The following connection types are defined:
+
+
+     1    Client connection
+     2    Server connection
+     3    Router connection
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.
+
+o Authentication Method (2 bytes) - Indicates the authentication
+  method to be used in the authentication protocol.  The following
+  authentication methods are defined:
+
+     0    NONE        (mandatory)
+     1    password    (mandatory)
+     2    public key  (mandatory)
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.  If this
+  payload is sent as request to receive the mandatory 
+  authentication method this field MUST be set to zero (0),
+  indicating that receiver should send the mandatory 
+  authentication method.  The receiver sending this payload
+  to the requesting party, MAY also set this field to zero (0) 
+  to indicate that authentication is not required.  In this
+  case authentication protocol still MUST be started but
+  server is most likely to respond with SILC_PACKET_SUCCESS
+  immediately.
+.in 3
+
+
+.ti 0
+2.3.16 New ID Payload
+
+New ID Payload is a multipurpose payload.  It is used to send newly 
+created ID's from clients and servers.  When client connects to server
+and registers itself to the server by sending SILC_PACKET_NEW_CLIENT
+packet, server replies with this packet by sending the created ID for
+the client.  Server always creates the ID for the client.
+
+This payload is also used when server tells its router that new client
+has registered to the SILC network.  In this case the server sends
+the Client ID of the client to the router.  Similarly when router
+distributes information to other routers about the client in the SILC
+network this payload is used.  
+
+Also, when server connects to router, router uses this payload to inform
+other routers about new server in the SILC network.  However, every 
+server (or router) creates their own ID's thus the ID distributed by 
+this payload is not created by the distributor in this case.  Servers
+create their own ID's.  Server registers itself to the network by sending
+SILC_PACKET_NEW_SERVER to the router it connected to.  The case is same
+when router connects to another router.
+
+However, this payload MUST NOT be used to send information about new
+channels.  New channels are always distributed by sending the dedicated
+SILC_PACKET_NEW_CHANNEL packet.
+
+Thus, this payload is very important and used every time when some
+new entity is registered to the SILC network.  Client MUST NOT send this
+payload.  Both client and server (and router) MAY receive this payload.
+
+The packet uses generic ID Payload as New ID Payload.  See section
+2.3.2.1 for generic ID Payload.
+
+
+.ti 0
+2.3.17 New Client Payload
+
+When client is connected to the server, keys has been exchanged and
+connection has been authenticated client MUST register itself to the 
+server.  Client's first packet after key exchange and authentication 
+protocols must be SILC_PACKET_NEW_CLIENT.  This payload tells server all
+the relevant information about the connected user.  Server creates a new
+client ID for the client when received this payload and sends it to the
+client in New ID Payload.
+
+This payload sends username and real name of the user on the remote host
+which is connected to the SILC server with SILC client.  The server 
+creates the client ID according the information sent in this payload.
+The nickname of the user becomes the username sent in this payload.
+However, client should call NICK command after sending this payload to
+set the real nickname of the user which is then used to create new 
+client ID.
+
+The payload may only be sent with SILC_PACKET_NEW_CLIENT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Client Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Username Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Username                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Real Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Real Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 19:  New Client Payload
+
+
+.in 6
+o Username Length (2 bytes) - Length of the Username field.
+
+o Username (variable length) - The username of the user on
+  the host where connecting to the SILC server.
+
+o Real Name Length (2 bytes) - Length of the Real Name field.
+
+o Real Name (variable length) - The real name of the user
+  on the host where connecting to the SILC server.
+.in 3
+
+
+.ti 0
+2.3.18 New Server Payload
+
+This payload is sent by server when it has completed successfully both
+key exchange and connection authentication protocols.  The server
+MUST register itself to the SILC Network by sending this payload.
+The first packet after these key exchange and authentication protocols
+is SILC_PACKET_NEW_SERVER packet.  The payload includes the Server ID
+of the server that it has created by itself.  It also includes a
+name of the server that is associated to the Server ID.
+
+The payload may only be sent with SILC_PACKET_NEW_SERVER packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Server Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Server ID Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Server ID Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Server Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Server Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 20:  New Server Payload
+
+
+.in 6
+o Server ID Length (2 bytes) - Length of the Server ID Data
+  field.
+
+o Server ID Data (variable length) - The actual Server ID
+   data.
+
+o Server Name Length (2 bytes) - Length of the server name
+  field.
+
+o Server Name (variable length) - The server name.
+.in 3
+
+
+.ti 0
+2.3.19 New Channel Payload
+
+Information about newly created channel is broadcasted to all routers
+in the SILC network by sending this packet payload.  Channels are
+created by router of the cell.  Server never creates channels unless
+it is a standalone server and it does not have router connection,
+in this case server acts as router.  Normal server send JOIN command
+to the router (after it has received JOIN command from client) which
+then processes the command and creates the channel.  Client MUST NOT
+send this packet.
+
+The packet uses generic Channel Payload as New Channel Payload.  See
+section 2.3.2.3 for generic Channel Payload.  The Mode Mask field in the
+Channel Payload is the mode of the channel.
+
+
+
+
+.ti 0
+2.3.20 Key Agreement Payload
+
+This payload is used by clients to request key negotiation between
+another client in the SILC Network.  The key agreement protocol used
+is the SKE protocol.  The result of the protocol, the secret key
+material, can be used for example as private message key between the
+two clients.  This significantly adds security as the key agreement
+is performed outside the SILC network.  The server and router MUST NOT
+send this payload.
+
+The sender MAY tell the receiver of this payload the hostname and the
+port where the SKE protocol is running in the sender's end.  The 
+receiver MAY then initiate the SKE negotiation with the sender.  The
+sender MAY also optionally not to include the hostname and the port
+of its SKE protocol.  In this case the receiver MAY reply to the
+request by sending the same payload filled with the receiver's hostname
+and the port where the SKE protocol is running.  The sender MAY then
+initiate the SKE negotiation with the receiver.
+
+The payload may only be sent with SILC_PACKET_KEY_AGREEMENT packet.
+It MUST NOT be sent in any other packet type.  The following diagram
+represents the Key Agreement Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 21:  Key Agreement Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the
+  Hostname field.
+
+o Hostname (variable length) - The hostname or IP address where
+  the SKE protocol is running.  The sender MAY fill this field
+  when sending the payload.  If the receiver sends this payload
+  as reply to the request it MUST fill this field.
+
+o Port (4 bytes) - The port where the SKE protocol is bound.
+  The sender MAY fill this field when sending the payload.  If
+  the receiver sends this payload as reply to the request it 
+  MUST fill this field.  This is a 32 bit MSB first order value.
+.in 3
+
+
+After the key material has been received from the SKE protocol it is
+processed as the [SILC3] describes.  If the key material is used as
+channel private key then the Sending Encryption Key, as defined in
+[SILC3] is used as the channel private key.  Other key material must
+be discarded.  The [SILC1] defines the way to use the key material if
+it is intended to be used as private message keys.  Any other use for
+the key material is undefined.
+
+
+.ti 0
+2.3.21 Cell Routers Payload
+
+Cell Routers payload is used by router to notify its primary router what
+other routers exist in the cell.  The other routers are considered to be
+backup routers and one of them will come active only in the case of
+failure of the primary router.  Normal server MAY send this packet if it
+is acting as backup router.  Client MUST NOT send this packet.  To send
+more than one backup router set the List flag and assemble the payloads
+as list.
+
+The payload may only be sent with SILC_PACKET_CELL_ROUTERS packet.  It
+MUST NOT be sent in any other packet type.  The Following diagram
+represents the Cell Routers Payload.
+
+         
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Server ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Server ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 22:  Cell Routers Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the Hostname
+  field.
+
+o Hostname (variable length) - The hostname or IP address of
+  the backup router.
+
+o Port (4 bytes) - The port of the backup router it currently uses.
+  This is a 32 bit MSB first order value.
+
+o Server ID Length (2 bytes) - Indicates the length of the Server
+  ID field.
+
+o Server ID (variable length) - Consists of the Server ID of the
+  backup router.
+.in 3
+
+
+.ti 0
+2.4 SILC ID Types
+
+ID's are extensively used in the SILC network to associate different
+entities.  The following ID's has been defined to be used in the SILC
+network.
+
+.in 6
+0    No ID
+
+     When ever specific ID cannot be used this is used.
+
+1    Server ID
+
+     Server ID to associate servers.  See the format of
+     this ID in [SILC1].
+
+2    Client ID
+
+     Client ID to associate clients.  See the format of
+     this ID in [SILC1].
+
+3    Channel ID
+
+     Channel ID to associate channels.  See the format of
+     this ID in [SILC1].
+.in 3
+
+
+.ti 0
+2.5 Packet Encryption And Decryption
+
+SILC packets are encrypted almost entirely.  Only small part of SILC
+header is not encrypted as described in section 5.2 SILC Packet Header.
+The SILC Packet header is the first part of a packet to be encrypted
+and it is always encrypted with the key of the next receiver of the
+packet.  The data payload area of the packet is always entirely 
+encrypted and it is usually encrypted with the next receiver's key.
+However, there are some special packet types and packet payloads
+that require special encryption process.  These special cases are
+described in the next sections.  First is described the normal packet
+encryption process.
+
+
+.ti 0
+2.5.1 Normal Packet Encryption And Decryption
+
+Normal SILC packets are encrypted with the session key of the next
+receiver of the packet.  The entire SILC Packet header and the packet
+data payload is is also encrypted with the same key.  Padding of the
+packet is also encrypted always with the session key, also in special
+cases.  Computed MAC of the packet must not be encrypted.
+
+Decryption process in these cases are straightforward.  The receiver
+of the packet MUST first decrypt the SILC Packet header, or some parts
+of it, usually first 16 bytes of it.  Then the receiver checks the
+packet type from the decrypted part of the header and can determine
+how the rest of the packet must be decrypted.  If the packet type is
+any of the special cases described in the following sections the packet
+decryption is special.  If the packet type is not among those special
+packet types rest of the packet can be decrypted with the same key.
+
+Also, note that two bytes of the SILC Packet header are not encrypted
+thus it must be noticed in the decryption process by starting the
+decryption from the second byte of the header.  This sets some rules
+to padding generation as well, see the section 2.7 Packet Padding
+Generation.
+
+With out a doubt, this sort of decryption processing causes some
+overhead to packet decryption, but never the less, is required.
+
+
+.ti 0
+2.5.2 Channel Message Encryption And Decryption
+
+Channel Messages (Channel Message Payload) are always encrypted with
+the channel specific key.  However, the SILC Packet header is not 
+encrypted with that key.  As in normal case, the header is encrypted
+with the key of the next receiver of the packet, who ever that might
+be.  Note that in this case the encrypted data area is not touched
+at all; it MUST NOT be re-encrypted with the session key.
+
+Receiver of a channel message, who ever that is, is REQUIRED to decrypt
+the SILC Packet header to be able to even recognize the packet to be as
+channel message.  This is same procedure as for normal SILC packets.
+As the receiver founds the packet to be channel message, rest of the
+packet processing is special.  Rest of the SILC Packet header is
+decrypted with the same session key along with the padding of the
+packet.  After that the packet is protected with the channel specific
+key and thus can be decrypted only if the receiver is the client on
+the channel.  See section 2.7 Packet Padding Generation for more
+information about padding on special packets.
+
+If the receiver of the channel message is router which is routing the
+message to another router then it MUST decrypt the Channel Message
+payload.  Between routers (that is, between cells) channel messages
+are protected with session keys shared between the routers.  This
+causes another special packet processing for channel messages.  If
+the channel message is received from another router then the entire
+packet, including Channel Message payload, MUST be encrypted with the
+session key shared between the routers.  In this case the packet
+decryption process is as with normal SILC packets.  Hence, if the
+router is sending channel message to another router the Channel
+Message payload MUST have been decrypted and MUST be re-encrypted
+with the session key shared between the another router.  In this
+case the packet encryption is as with any normal SILC packet.
+
+It must be noted that this is only when the channel messages are sent
+from router to another router.  In all other cases the channel
+message encryption and decryption is as described above.  This
+different processing of channel messages with router to router
+connection is because channel keys are cell specific.  All cells has
+their own channel keys thus the channel message traveling from one
+cell to another MUST be protected as it would be any normal SILC
+packet.
+
+If the SILC_CMODE_PRIVKEY channel mode has been set for the channel
+then the router cannot decrypt the packet as it does not know the
+private key.  In this case the entire packet MUST be encrypted with
+the session key and sent to the router.  The router receiving the
+packet MUST check the channel mode and decrypt the packet accordingly.
+
+
+.ti 0
+2.5.3 Private Message Encryption And Decryption
+
+By default, private message in SILC are protected by session keys.
+In this case the private message encryption and decryption process is
+equivalent to normal packet encryption and decryption.
+
+However, private messages MAY be protected with private message key
+which causes the packet to be special packet.  The procedure in this
+case is very much alike to channel packets.  The actual private message
+is encrypted with the private message key and other parts of the
+packet is encrypted with the session key.  See 2.7 Packet Padding
+Generation for more information about padding on special packets.
+
+The difference from channel message processing is that server or router
+en route never decrypts the actual private message, as it does not
+have the key to do that.  Thus, when sending packets between router
+the processing is same as in any other case as well; the packet's header
+and padding is protected by the session key and the data area is not
+touched.
+
+The true receiver of the private message, client, that is, is able
+to decrypt the private message as it shares the key with the sender
+of the message.
+
+
+.ti 0
+2.6 Packet MAC Generation
+
+Data integrity of a packet is protected by including a message
+authentication code (MAC) at the end of the packet.  The MAC is computed
+from shared secret MAC key, that is established by the SILC Key Exchange
+protocol, and from the original contents of the packet.  The MAC is
+always computed before the packet is encrypted, although after it is
+compressed if compression is used.
+
+The MAC is computed from entire packet.  Every bit of data in the packet,
+including SILC Packet Header is used in the MAC computing.  This way
+the entire packet becomes authenticated.
+
+If the packet is special packet MAC is computed from the entire packet
+but part of the packet may be encrypted before the MAC is computed.
+This is case, for example, with channel messages where the message data
+is encrypted with key that server may not now.  In this case the MAC
+has been computed from the encrypted data.
+
+See [SILC1] for defined and allowed MAC algorithms.
+
+
+.ti 0
+2.7 Packet Padding Generation
+
+Padding is needed in the packet because the packet is encrypted.  It
+MUST always be multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.  The padding is always encrypted.
+
+For normal packets the padding is added after the SILC Packet Header
+and between the Data Payload area.  The padding for normal packets
+are calculated as follows:
+
+.in 6
+padding length = 16 - ((packet length - 2) mod 16)
+.in 3
+
+The 16 is the maximum padding allowed in SILC packet.  Two (2) is 
+subtracted from the true length of the packet because two (2) bytes
+is not encrypted in SILC Packet Header, see section 2.2 SILC Packet
+Header.  Those two bytes that are not encrypted MUST NOT be calculated
+to the padding length.
+
+For special packets the padding calculation MAY be different as special
+packets may be encrypted differently.  In these cases the encrypted
+data area MUST already be multiple by the block size thus in this case
+the padding is calculated only for SILC Packet Header, not for any
+other area of the packet.  The same algorithm works in this case as
+well, except that the `packet length' is now the SILC Packet Header
+length.  In this case, as well, two (2) is subtracted from the
+length.
+
+The padding MUST be random data, preferably, generated by 
+cryptographically strong random number generator.
+
+
+.ti 0
+2.8 Packet Compression
+
+SILC Packets MAY be compressed.  In this case the data payload area
+is compressed and all other areas of the packet MUST remain as they
+are.  After compression is performed for the data area, the length
+field of Packet Header MUST be set to the compressed length of the
+data.
+
+The compression MUST always be applied before encryption.  When
+the packet is received and decrypted the data area MUST be decompressed.
+Note that the true sender of the packet MUST apply the compression and
+the true receiver of the packet MUST apply the decompression.  Any
+server or router en route MUST NOT decompress the packet.
+
+
+.ti 0
+2.9 Packet Sending
+
+The sender of the packet MUST assemble the SILC Packet Header with
+correct values.  It MUST set the Source ID of the header as its own
+ID, unless it is forwarding the packet.  It MUST also set the Destination
+ID of the header to the true destination.  If the destination is client
+it will be Client ID, if it is server it will be Server ID and if it is
+channel it will be Channel ID.
+
+If the sender wants to compress the packet it MUST apply the
+compression now.  Sender MUST also compute the padding as described
+in above sections.  Then sender MUST compute the MAC of the packet.
+
+Then sender MUST encrypt the packet as has been described in above
+sections according whether the packet is normal packet or special
+packet.  The computed MAC MUST NOT be encrypted.
+
+
+.ti 0
+2.10 Packet Reception
+
+On packet reception the receiver MUST check that all fields in the
+SILC Packet Header are valid.  It MUST check the flags of the
+header and act accordingly.  It MUST also check the MAC of the packet
+and if it is to be failed the packet MUST be discarded.  Also if the
+header of the packet includes any bad fields the packet MUST be
+discarded.
+
+See above sections on the decryption process of the received packet.
+The receiver MUST also check that the ID's in the header are valid
+ID's.  Unsupported ID types or malformed ID's MUST cause packet
+rejection.  The padding on the reception is always ignored.
+
+The receiver MUST also check the packet type and start parsing the
+packet according to the type.  However, note the above sections on
+special packet types and their parsing.
+
+
+.ti 0
+2.11 Packet Routing
+
+Routers are the primary entities in the SILC network that takes care
+of packet routing.  However, normal servers routes packets as well, for
+example, when they are routing channel message to the local clients.
+Routing is quite simple as every packet tells the true origin and the
+true destination of the packet.
+
+It is still RECOMMENDED for routers that has several routing connections
+to create route cache for those destinations that has faster route than
+the router's primary route.  This information is available for the router
+when other router connects to the router.  The connecting party then
+sends all of its locally connected clients, servers and channels.  These
+informations helps to create the route cache.  Also, when new channels
+are created to a cell its information is broadcasted to all routers
+in the network.  Channel ID's are based on router's ID thus it is easy
+to create route cache based on these informations.  If faster route for
+destination does not exist in router's route cache the packet MUST be
+routed to the primary route (default route).
+
+For server which receives a packet to be routed to its locally connected
+client the server MUST check whether the particular packet type is
+allowed to be routed to the client.  Not all packets may be sent by
+some odd entity to client that is indirectly connected to the sender.
+See section 2.3 SILC Packet Types and paragraph about indirectly connected
+entities and sending packets to them.  The section mentions the packets
+that may be sent to indirectly connected entities.  It is clear that some
+server cannot send, for example, disconnect packet to client that is not
+directly connected to the server.
+
+
+.ti 0
+2.12 Packet Broadcasting
+
+SILC packets MAY be broadcasted in SILC network.  However, only router
+server may send or receive broadcast packets.  Client and normal server
+MUST NOT send broadcast packets and they MUST ignore broadcast packets
+if they receive them.  Broadcast packets are sent by setting Broadcast
+flag to the SILC packet header.
+
+Broadcasting packets means that the packet is sent to all routers in
+the SILC network, except to the router that sent the packet.  The router
+receiving broadcast packet MUST send the packet to its primary route.
+The fact that SILC routers may have several router connections can
+cause problems, such as race conditions inside the SILC network, if
+care is not taken when broadcasting packets.  Router MUST NOT send
+the broadcast packet to any other route except to its primary route.
+
+If the primary route of the router is the original sender of the packet
+the packet MUST NOT be sent to the primary route.  This may happen
+if router has several router connections and some other router uses
+the router as its primary route.
+
+Routers use broadcast packets to broadcast for example information
+about newly registered clients, servers, channels etc. so that all the
+routers may keep these informations up to date.
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 25 October 2001
diff --git a/doc/draft-riikonen-silc-pp-03.nroff b/doc/draft-riikonen-silc-pp-03.nroff
new file mode 100644 (file)
index 0000000..72d25da
--- /dev/null
@@ -0,0 +1,2700 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 21 August 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-pp-03.txt                           21 August 2001
+Expires: 21 February 2002
+
+.in 3
+
+.ce 2
+SILC Packet Protocol
+<draft-riikonen-silc-pp-03.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Packet Protocol used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Packet Protocol ..........................................  4
+  2.1 SILC Packet ...............................................  4
+  2.2 SILC Packet Header ........................................  5
+  2.3 SILC Packet Types .........................................  7
+      2.3.1 SILC Packet Payloads ................................ 16
+      2.3.2 Generic payloads .................................... 16
+            2.3.2.1 ID Payload .................................. 16
+            2.3.2.2 Argument Payload ............................ 17
+            2.3.2.3 Channel Payload ............................. 18
+            2.3.2.4 Public Key Payload .......................... 19
+      2.3.3 Disconnect Payload .................................. 19
+      2.3.4 Success Payload ..................................... 19
+      2.3.5 Failure Payload ..................................... 20
+      2.3.6 Reject Payload ...................................... 21
+      2.3.7 Notify Payload ...................................... 22
+      2.3.8 Error Payload ....................................... 21
+      2.3.9 Channel Message Payload ............................. 28
+      2.3.10 Channel Key Payload ................................ 31
+      2.3.11 Private Message Payload ............................ 33
+      2.3.12 Private Message Key Payload ........................ 34
+      2.3.13 Command Payload .................................... 36
+      2.3.14 Command Reply Payload .............................. 37
+      2.3.15 Connection Auth Request Payload .................... 37
+      2.3.16 New ID Payload ..................................... 38
+      2.3.17 New Client Payload ................................. 39
+      2.3.18 New Server Payload ................................. 40
+      2.3.19 New Channel Payload ................................ 41
+      2.3.20 Key Agreement Payload .............................. 42
+      2.3.21 Cell Routers Payload ............................... 43
+  2.4 SILC ID Types ............................................. 44
+  2.5 Packet Encryption And Decryption .......................... 44
+      2.5.1 Normal Packet Encryption And Decryption ............. 45
+      2.5.2 Channel Message Encryption And Decryption ........... 45
+      2.5.3 Private Message Encryption And Decryption ........... 46
+  2.6 Packet MAC Generation ..................................... 47
+  2.7 Packet Padding Generation ................................. 47
+  2.8 Packet Compression ........................................ 48
+  2.9 Packet Sending ............................................ 48
+  2.10 Packet Reception ......................................... 49
+  2.11 Packet Routing ........................................... 49
+  2.12 Packet Broadcasting ...................................... 50
+3 Security Considerations ....................................... 50
+4 References .................................................... 50
+5 Author's Address .............................................. 52
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:   Typical SILC Packet
+Figure 2:   SILC Packet Header
+Figure 3:   ID Payload
+Figure 4:   Argument Payload
+Figure 5:   Channel Payload
+Figure 6:   Public Key Payload
+Figure 7:   Disconnect Payload
+Figure 8:   Success Payload
+Figure 9:   Failure Payload
+Figure 10:   Reject Payload
+Figure 11:  Notify Payload
+Figure 12:  Error Payload
+Figure 13:  Channel Message Payload
+Figure 14:  Channel Key Payload
+Figure 15:  Private Message Payload
+Figure 16:  Private Message Key Payload
+Figure 17:  Command Payload
+Figure 18:  Connection Auth Request Payload
+Figure 19:  New Client Payload
+Figure 20:  New Server Payload
+Figure 21:  Key Agreement Payload
+Figure 22:  Cell Routers Payload
+
+
+.ti 0
+1. Introduction
+
+This document describes a Packet Protocol used in the Secure Internet
+Live Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+The basis of SILC protocol relies in the SILC packets and it is with
+out a doubt the most important part of the protocol.  It is also probably
+the most complicated part of the protocol.  Packets are used all the
+time in the SILC network to send messages, commands and other information.
+All packets in SILC network are always encrypted and their integrity
+is assured by computed MACs.  The protocol defines several packet types
+and packet payloads.  Each packet type usually has a specific packet
+payload that actually defines the contents of the packet.  Each packet
+also includes a default SILC Packet Header that provides sufficient
+information about the origin of the packet and destination of the
+packet.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Packet Protocol
+
+.ti 0
+2.1 SILC Packet
+
+SILC packets deliver messages from sender to receiver securely by
+encrypting important fields of the packet.  The packet consists of
+default SILC Packet Header, Padding, Packet Payload data, and, packet 
+MAC.
+
+The following diagram illustrates typical SILC packet.
+
+
+.in 5
+.nf
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+|   n bytes   | 1 - n bytes |      n bytes       |  n bytes       
+| SILC Header |   Padding   |    Data Payload    |    MAC    
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+.in 3
+
+.ce
+Figure 1:  Typical SILC Packet
+
+
+SILC Header is always the first part of the packet and its purpose
+is to provide information about the packet.  It provides for example
+the packet type, origin of the packet and the destination of the packet.
+The header is variable in length and first two (2) bytes of the
+header (thus first two bytes of the packet) are not encrypted.  The
+first two (2) bytes are the length of the packet which is not encrypted.
+See the following section for description of SILC Packet header.  Packets
+without SILC header or with malformed SILC header MUST be dropped.
+
+Padding follows the packet header.  The purpose of the padding is to
+make the packet multiple by eight (8) or by the block size of the
+cipher used in the encryption, which ever is larger.  The maximum
+length of padding is currently 16 bytes.  The padding is always
+encrypted.
+
+Data payload area follows padding and it is the actual data of the
+packet.  The packet data is the packet payloads defined in this
+protocol.  The data payload area is always encrypted.
+
+The last part of SILC packet is the packet MAC that assures the
+integrity of the packet.  The MAC is always computed from the packet
+before the encryption is applied to the packet.  If compression is used
+in the packet the MAC is computed after the compression has been
+applied.  The compression, on the other hand, is always applied before
+encryption.
+
+All fields in all packet payloads are always in MSB (most significant
+byte first) order.
+
+
+.ti 0
+2.2 SILC Packet Header
+
+The SILC packet header is applied to all SILC packets and it is
+variable in length.  The purpose of SILC Packet header is to provide
+detailed information about the packet.  The receiver of the packet
+uses the packet header to parse the packet and gain other relevant
+parameters of the packet.
+
+The following diagram represents the SILC packet header.  (*) indicates
+that this field is never encrypted.  Other fields are always encrypted.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length *       |     Flags     |  Packet Type  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Source ID Length       |     Destination ID Length     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Src ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                           Source ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Dst ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                         Destination ID                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  SILC Packet Header
+
+
+.in 6
+o Payload Length (2 bytes) - Is the length of the packet
+  not including the padding of the packet.  This field must
+  not be encrypted but must always be authenticated.
+
+o Flags (1 byte) - Indicates flags to be used in packet
+  processing.  Several flags may be set by ORing the flags
+  together.
+
+  The following flags are reserved for this field:
+
+
+     No flags                  0x00
+
+       In this case the field is ignored.
+
+
+     Private Message Key       0x01
+
+       Indicates that the packet must include private
+       message that is encrypted using private key set by
+       client.  Servers does not know anything about this
+       key and this causes that the private message is
+       not handled by the server at all, it is just
+       passed along.  See section 2.5.3 Private Message
+       Encryption And Decryption for more information.
+
+
+     List                      0x02
+  
+       Indicates that the packet consists of list of
+       packet payloads indicated by the Packet Type field.
+       The payloads are added one after the other.  Note that
+       there are packet types that must not be used as
+       list.  Parsing of list packet is done by calculating
+       the length of each payload and parsing them one by
+       one.
+
+
+     Broadcast                 0x04
+
+       Marks the packet to be broadcasted.  Client cannot
+       send broadcast packet and normal server cannot send
+       broadcast packet.  Only router server may send broadcast
+       packet.  The router receiving of packet with this flag 
+       set MUST send (broadcast) the packet to its primary
+       route.  If router has several router connections the
+       packet may be sent only to the primary route.  See
+       section 2.12 Packet Broadcasting for description of 
+       packet broadcasting.
+
+.in 3
+
+
+
+
+o Packet Type (1 byte) - Is the type of the packet. Receiver 
+  uses this field to parse the packet.  See section 2.3
+  SILC Packets for list of defined packet types.
+
+o Source ID Length (2 bytes) - Indicates the length of the
+  Source ID field in the header, not including this or any
+  other fields.
+
+o Destination ID Length (2 bytes) - Indicates the length of the
+  Destination ID field in the header, not including this or
+  any other fields.
+
+o Src ID Type (1 byte) - Indicates the type of ID in the
+  Source ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Source ID (variable length) - The actual source ID that
+  indicates which is the original sender of the packet.
+
+o Dst ID Type (1 byte) - Indicates the type of ID in the
+  Destination ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Destination ID (variable length) - The actual destination
+  ID that indicates which is the end receiver of the packet.
+
+
+.ti 0
+2.3 SILC Packet Types
+
+SILC packet types defines the contents of the packet and it is used by
+the receiver to parse the packet.  The packet type is 8 bits, as a one
+byte, in length.  The range for the packet types are from 0 - 255,
+where 0 is never sent and 255 is currently reserved for future
+extensions and MUST NOT be defined to any other purpose.  Every SILC
+specification compliant implementation SHOULD support all of these packet
+types.
+
+The below list of the SILC Packet types includes reference to the packet
+payload as well.  Packet payloads are the actual packet, that is, the data
+that the packet consists of.  Each packet type defines packet payload 
+which usually may only be sent with the specific packet type.
+
+Most of the packets are packets that must be destined directly to entity
+that is connected to the sender.  It is not allowed, for example, for
+router to send disconnect packet to client that is not directly connected
+to the router.  However, there are some special packet types that may
+be destined to some entity that the sender has not direct connection
+with.  These packets are for example private message packets, channel
+message packets, command packets and some other packets that may be
+broadcasted in the SILC network.  If the packet is allowed to be sent to
+indirectly connected entity it is mentioned separately in the packet
+description (unless it is obvious as in private and channel message
+packets).  Other packets MUST NOT be sent or accepted, if sent, to
+indirectly connected entities.
+
+List of SILC Packet types are defined as follows.
+
+.in 1
+     0    SILC_PACKET_NONE
+
+          This type is reserved and it is never sent.         
+
+
+     1    SILC_PACKET_DISCONNECT
+
+          This packet is sent to disconnect the remote end.  Reason of
+          the disconnection is sent inside the packet payload.  Client
+          usually does not send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.3 Disconnect Payload
+
+
+     2    SILC_PACKET_SUCCESS
+
+          This packet is sent upon successful execution of some protocol.
+          The status of the success is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.4 Success Payload
+
+
+     3    SILC_PACKET_FAILURE
+
+          This packet is sent upon failure of some protocol.  The status
+          of the failure is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.5 Failure Payload
+
+
+     4    SILC_PACKET_REJECT
+
+          This packet MAY be sent upon rejection of some protocol.
+          The status of the rejection is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.6 Reject Payload
+
+
+     5    SILC_PACKET_NOTIFY
+
+          This packet is used to send notify message, usually from
+          server to client, although it MAY be sent from server to another
+          server as well.  Client MUST NOT send this packet.  Server MAY
+          send this packet to channel as well when the packet is 
+          distributed to all clients on the channel.
+
+          Payload of the packet:  See section 2.3.7 Notify Payload.
+
+
+     6    SILC_PACKET_ERROR
+
+          This packet is sent when an error occurs.  Server MAY
+          send this packet.  Client MUST NOT send this packet.  The
+          client MAY entirely ignore the packet, however, server is
+          most likely to take action anyway.  This packet MAY be sent
+          to entity that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.8 Error Payload.
+
+
+     7    SILC_PACKET_CHANNEL_MESSAGE
+
+          This packet is used to send messages to channels.  The packet
+          includes Channel ID of the channel and the actual message to
+          the channel.  Messages sent to the channel are always protected
+          by channel specific keys.  Channel Keys are distributed by
+          SILC_PACKET_CHANNEL_KEY packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.9 Channel Message 
+                                  Payload
+
+
+     8    SILC_PACKET_CHANNEL_KEY
+
+          This packet is used to distribute new key for particular
+          channel.  Each channel has their own independent keys that
+          is used to protect the traffic on the channel.  Only server
+          may send this packet.  This packet MAY be sent to entity
+          that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.10 Channel Key Payload
+
+
+     9    SILC_PACKET_PRIVATE_MESSAGE
+
+          This packet is used to send private messages from client
+          to another client.  By default, private messages are protected
+          by session keys established by normal key exchange protocol.
+          However, it is possible to use specific key to protect private
+          messages.  SILC_PACKET_PRIVATE_MESSAGE_KEY packet is used to 
+          agree the key with the remote client.  Pre-shared key MAY be 
+          used as well if both of the client knows it, however, it needs 
+          to be agreed outside SILC.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.11 Private Message
+                                  Payload
+
+
+     10   SILC_PACKET_PRIVATE_MESSAGE_KEY
+
+          This packet is used to agree about a key to be used to protect
+          the private messages between two clients.  If this is not sent
+          the normal session key is used to protect the private messages
+          inside SILC network.  Agreeing to use specific key to protect
+          private messages adds security, as no server between the two
+          clients will be able to decrypt the private message.  However,
+          servers inside SILC network are considered to be trusted, thus
+          using normal session key to protect private messages does not
+          degrade security.  Whether to agree to use specific keys by
+          default or to use normal session keys by default, is 
+          implementation specific issue.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.12 Private Message
+                                  Key Payload
+
+
+     11   SILC_PACKET_COMMAND
+
+          This packet is used to send commands from client to server.
+          Server MAY send this packet to other servers as well.  All
+          commands are listed in their own section SILC Command Types
+          in [SILC4].  The contents of this packet is command specific.
+          This packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.13 Command Payload
+
+
+     12   SILC_PACKET_COMMAND_REPLY
+
+          This packet is sent as reply to the SILC_PACKET_COMMAND packet.
+          The contents of this packet is command specific.  This packet
+          MAY be sent to entity that is indirectly connected to the
+          sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.14 Command Reply 
+                                  Payload and section 2.3.13 Command
+                                  Payload
+
+
+     13   SILC_PACKET_KEY_EXCHANGE
+
+          This packet is used to start SILC Key Exchange Protocol, 
+          described in detail in [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     14   SILC_PACKET_KEY_EXCHANGE_1
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     15   SILC_PACKET_KEY_EXCHANGE_2
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     16   SILC_PACKET_CONNECTION_AUTH_REQUEST
+
+          This packet is used to request the authentication method to
+          be used in the SILC Connection Authentication Protocol.  If 
+          initiator of the protocol does not know the mandatory 
+          authentication method this packet MAY be used to determine it.
+
+          The party receiving this payload MUST respond with the same
+          packet including the mandatory authentication method.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.15 Connection Auth
+                                  Request Payload
+
+
+
+
+     17   SILC_PACKET_CONNECTION_AUTH
+
+          This packet is used to start and perform the SILC Connection
+          Authentication Protocol.  This protocol is used to authenticate
+          the connecting party.  The protocol is described in detail in
+          [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Authentication
+                                  Protocol and it sub sections in [SILC].
+
+
+     18   SILC_PACKET_NEW_ID
+
+          This packet is used to distribute new ID's from server to
+          router and from router to all routers in the SILC network.
+          This is used when for example new client is registered to
+          SILC network.  The newly created ID's of these operations are
+          distributed by this packet.  Only server may send this packet,
+          however, client MUST be able to receive this packet.  This
+          packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          Payload of the packet:  See section 2.3.16 New ID Payload
+
+
+     19   SILC_PACKET_NEW_CLIENT
+
+          This packet is used by client to register itself to the   
+          SILC network.  This is sent after key exchange and  
+          authentication protocols has been completed.  Client sends
+          various information about itself in this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.17 New Client Payload
+
+
+     20   SILC_PACKET_NEW_SERVER
+
+          This packet is used by server to register itself to the
+          SILC network.  This is sent after key exchange and 
+          authentication protocols has been completed.  Server sends
+          this to the router it connected to, or, if router was
+          connecting, to the connected router.  Server sends its
+          Server ID and other information in this packet.  The client
+          MUST NOT send or receive this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.18 New Server Payload
+
+
+     21   SILC_PACKET_NEW_CHANNEL
+
+          This packet is used to notify routers about newly created
+          channel.  Channels are always created by the router and it MUST
+          notify other routers about the created channel.  Router sends
+          this packet to its primary route.  Client MUST NOT send this
+          packet.  This packet MAY be sent to entity that is indirectly
+          connected to the sender.
+
+          Payload of the packet:  See section 2.3.19 New Channel Payload
+
+
+     22   SILC_PACKET_REKEY
+
+          This packet is used to indicate that re-key must be performed
+          for session keys.  See section Session Key Regeneration in
+          [SILC1] for more information.  This packet does not have
+          a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     23   SILC_PACKET_REKEY_DONE
+
+          This packet is used to indicate that re-key is performed and
+          new keys must be used hereafter.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+     
+     24   SILC_PACKET_HEARTBEAT
+
+          This packet is used by clients, servers and routers to keep the
+          connection alive.  It is recommended that all servers implement
+          keepalive actions and perform it to both direction in a link.
+          This packet does not have a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     25   SILC_PACKET_KEY_AGREEMENT
+
+          This packet is used by clients to request key negotiation 
+          between another client in the SILC network.  If the negotiation
+          is started it is performed using the SKE protocol.  The result of
+          the negotiation, the secret key material, can be used for
+          example as private message key.  The server and router MUST NOT
+          send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.20 Key Agreement Payload
+
+
+    26    SILC_PACKET_CELL_ROUTERS
+
+          This packet is used by primary router in the cell to notify its
+          primary router what other routers (backup routers) exist in the
+          cell.  In case of failure of the primary router in the cell the
+          first router in the list will act as primary router of the cell.
+          This packet MAY be sent at anytime after connection has been
+          registered to the primary router.  The client MUST NOT send this
+          packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.21 Cell Routers Payload
+
+
+     27 - 199
+
+          Currently undefined commands.
+
+
+     200 - 254
+
+          These packet types are reserved for private use and they will
+          not be defined by this document.
+
+
+
+
+     255  SILC_PACKET_MAX
+
+          This type is reserved for future extensions and currently it 
+          MUST NOT be sent.
+.in 3
+
+
+.ti 0
+2.3.1 SILC Packet Payloads
+
+All payloads resides in the main data area of the SILC packet.  However
+all payloads MUST be at the start of the data area after the SILC
+packet header and padding.  All fields in the packet payload are always
+encrypted, as they reside in the data area of the packet which is
+always encrypted.
+
+Payloads described in this section are common payloads that MUST be
+accepted anytime during SILC session.  Most of the payloads may only
+be sent with specific packet type which is defined in the description
+of the payload.
+
+There are a lot of other payloads in the SILC as well.  However, they
+are not common in the sense that they could be sent at any time. 
+These payloads are not described in this section.  These are payloads
+such as SILC Key Exchange payloads and so on.  These are described
+in [SILC1], [SILC3] and [SILC4].
+
+
+.ti 0
+2.3.2 Generic payloads
+
+This section describes generic payloads that are not associated to any
+specific packet type.  They can be used for example inside some other
+packet payloads.
+
+
+.ti 0
+2.3.2.1 ID Payload
+
+This payload can be used to send an ID.  ID's are variable in length
+thus this payload provides a way to send variable length ID's.
+
+
+
+
+
+
+
+
+
+
+
+
+The following diagram represents the ID Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             ID Type           |           ID Length           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           ID Data                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 3:  ID Payload
+
+
+.in 6
+o ID Type (2 bytes) - Indicates the type of the ID.  See 
+  section 2.4 SILC ID Types for list of defined ID types.
+
+o ID Length (2 bytes) - Length of the ID Data area not 
+  including the length of any other fields in the payload.
+
+o ID Data (variable length) - The actual ID data.
+.in 3
+
+
+.ti 0
+2.3.2.2 Argument Payload
+
+Argument Payload is used to set arguments for any packet payload that
+needs and supports arguments, such as commands.  Number of arguments
+associated with a packet MUST be indicated by the packet payload which
+needs the arguments.  Argument Payloads MUST always reside right after
+the packet payload needing the arguments.  Incorrect amount of argument
+payloads MUST cause rejection of the packet.  The following diagram
+represents the Argument Payload.
+
+The following diagram represents the Argument Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | Argument Type |               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               +
+|                                                               |
+~                        Argument Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 4:  Argument Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the argument payload data 
+  area not including the length of any other fields in the 
+  payload.
+
+o Argument Type (1 byte) - Indicates the type of the argument.  
+  Every argument may have a specific type that MUST be defined
+  by the packet payload needing the argument.  For example
+  every command specify a number for each argument that maybe 
+  associated with the command.  By using this number the receiver 
+  of the packet knows what type of argument this is.  If there is
+  no specific argument type this field is set to zero (0).
+
+o Argument Data (variable length) - Argument data.
+.in 3
+
+
+.ti 0
+2.3.2.3 Channel Payload
+
+Generic Channel Payload may be used to send information about channel,
+its name, the Channel ID and a mode.
+
+The following diagram represents the Channel Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Name Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                           Mode Mask                           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  New Channel Payload
+
+
+.in 6
+o Channel Name Length (2 bytes) - Length of the channel name
+  field.
+
+o Channel Name (variable length) - The name of the channel.
+
+o Channel ID Length (2 bytes) - Length of the Channel ID field.
+
+o Channel ID (variable length) - The Channel ID.
+
+o Mode Mask (4 bytes) - A mode.  This can be the mode of the
+  channel but it can also be the mode of the client on the
+  channel.  The contents of this field is dependent of the
+  usage of this payload.  The usage is defined separately
+  when this payload is used.  This is a 32 bit MSB first value.
+.in 3
+
+
+.ti 0
+2.3.2.4 Public Key Payload
+
+Generic Public Key Payload may be used to send different types of
+public keys and certificates.
+
+The following diagram represents the Public Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  Public Key Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  See the [SILC3] for defined public key types.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+.in 3
+
+
+.ti 0
+2.3.3 Disconnect Payload
+
+Disconnect payload is sent upon disconnection.  The payload is simple;
+reason of disconnection is sent to the disconnected party.
+
+The payload may only be sent with SILC_PACKET_DISCONNECT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the Disconnect Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Disconnect Message                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 7:  Disconnect Payload
+
+
+
+
+.in 6
+o Disconnect Message (variable length) - Human readable
+  reason of the disconnection.
+.in 3
+
+
+.ti 0
+2.3.4 Success Payload
+
+Success payload is sent when some protocol execution is successfully
+completed.  The payload is simple; indication of the success is sent.
+This may be any data, including binary or human readable data.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Success Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 8:  Success Payload
+
+
+.in 6
+o Success Indication (variable length) - Indication of
+  the success.  This may be for example some flag that
+  indicates the protocol and the success status or human
+  readable success message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+
+.ti 0
+2.3.5 Failure Payload
+
+This is opposite of Success Payload.  Indication of failure of
+some protocol is sent in the payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Failure Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 9:  Failure Payload
+
+
+.in 6
+o Failure Indication (variable length) - Indication of
+  the failure.  This may be for example some flag that
+  indicates the protocol and the failure status or human
+  readable failure message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.6 Reject Payload
+
+This payload is sent when some protocol is rejected to be executed.
+Other operations MAY send this as well that was rejected.  The
+indication of the rejection is sent in the payload.  The indication
+may be binary or human readable data.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Reject Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 10:  Reject Payload
+
+
+.in 6
+o Reject Indication (variable length) - Indication of
+  the rejection.  This maybe for example some flag that
+  indicates the protocol and the rejection status or human
+  readable rejection message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.7 Notify Payload
+
+Notify payload is used to send notify messages.  The payload is usually
+sent from server to client, however, server MAY send it to another
+server as well.  This payload MAY also be sent to a channel.  Client
+MUST NOT send this payload.  The receiver of this payload MAY ignore
+the contents of the payload, however, notify message SHOULD be audited.
+
+The payload may only be sent with SILC_PACKET_NOTIFY packet.  It MUST
+not be sent in any other packet type.  The following diagram represents
+the Notify Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|          Notify Type          |        Payload Length         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Argument Nums |
++-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 11:  Notify Payload
+
+
+.in 6
+o Notify Type (2 bytes) - Indicates the type of the notify
+  message.
+
+o Payload Length (2 bytes) - Length of the entire Notify Payload
+  including any associated Argument Payloads.
+
+o Argument Nums (2 bytes) - Indicates the number of Argument
+  Payloads associated to this payload.  Notify types may define
+  arguments to be send along the notify message.
+.in 3
+
+The following list of currently defined notify types.  The format for
+notify arguments is same as in SILC commands described in [SILC4]. 
+Also, all ID's sent in arguments are sent inside ID Payload.
+
+.in 6
+0     SILC_NOTIFY_TYPE_NONE
+
+      If no specific notify type apply for the notify message this type
+      MAY be used.
+
+      Max Arguments:  1
+          Arguments:  (1) <message>
+
+      The <message> is implementation specific free text string.
+      Receiver MAY ignore this message.
+
+
+1     SILC_NOTIFY_TYPE_INVITE
+
+      Sent when an client is invited to a channel.  This is also sent
+      when the invite list of the channel is changed.  This notify type
+      is sent between routers and if an client was invited, to the 
+      client as well.  In this case the packet is destined to the client.
+
+      Max Arguments:  5
+          Arguments:  (1) <Channel ID>          (2) <channel name>
+                      (3) [<sender Client ID>]  (4) [<adding client>]
+                      (5) [<removing client>]
+
+      The <Channel ID> is the channel.  The <channel name> is the name
+      of the channel and is provided because the client which receives 
+      this notify packet may not have a way to resolve the name of the
+      channel from the <Channel ID>.  The <sender Client ID> is the
+      Client ID which invited the client to the channel.  The <adding
+      client> and the <removing client> indicates the added or removed
+      client from the channel's invite list.  The format of the <adding
+      client> and the <removing client> is defined in the [SILC4] with
+      SILC_COMMAND_INVITE command.
+
+      The <adding client> and <removing client> MUST NOT be sent when
+      the packet is destined to a client.
+
+
+2     SILC_NOTIFY_TYPE_JOIN
+
+      Sent when client has joined to a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) [<Client ID>]       (2) <Channel ID>
+
+      The <Client ID> is the client that joined to the channel indicated
+      by the <Channel ID>.
+
+
+3     SILC_NOTIFY_TYPE_LEAVE
+
+      Sent when client has left a channel.  The server must distribute
+      this type only to the local clients on the channel and then send
+      it to its primary router.  The router or server receiving the
+      packet distributes this type to the local clients on the channel
+      and broadcast it to the network.
+
+      Max Arguments:  1
+          Arguments:  (1) <Client ID>
+
+      The <Client ID> is the client which left the channel.
+
+
+4     SILC_NOTIFY_TYPE_SIGNOFF
+
+      Sent when client signoff from SILC network.  The server MUST
+      distribute this type only to the local clients on the channel and
+      then send it to its primary router.  The router or server receiving
+      the packet distributes this type to the local clients on the
+      channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <message>
+
+      The <Client ID> is the client which left SILC network.  The
+      <message> is free text string indicating the reason of the signoff.
+
+
+5     SILC_NOTIFY_TYPE_TOPIC_SET
+
+      Sent when topic is set/changed on a channel.  This type must be
+      sent only to the clients which is joined on the channel which
+      topic was set or changed.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <topic>
+
+      The <Client ID> is the client which set or changed the <topic>.
+
+
+6     SILC_NOTIFY_TYPE_NICK_CHANGE
+
+      Sent when client changes nick on a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Client ID>  (2) <New Client ID>
+
+      The <Old Client ID> is the old ID of the client which changed
+      the nickname.  The <New Client ID> is the new ID generated by
+      the change of the nickname.
+
+
+7     SILC_NOTIFY_TYPE_CMODE_CHANGE
+
+      Sent when channel mode has changed.  This type MUST be sent only
+      to the clients which is joined on the channel which mode was
+      changed.
+
+      Max Arguments:  4
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) [<cipher>]    (4) <[hmac>]     
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing channel mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The client can safely
+      ignore the <cipher> argument since the SILC_PACKET_CHANNEL_KEY
+      packet will force the new channel key change anyway.  The <hmac>
+      argument is important since the client is responsible of setting
+      the new HMAC and the hmac key into use.
+
+
+8     SILC_NOTIFY_TYPE_CUMODE_CHANGE
+
+      Sent when user mode on channel has changed.  This type MUST be
+      sent only to the clients which is joined on the channel where
+      the target client is on.
+
+      Max Arguments:  3
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) <Target Client ID>
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing user's mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The <Target Client ID>
+      is the client which mode was changed.
+
+
+9     SILC_NOTIFY_TYPE_MOTD
+
+      Sent when Message of the Day (motd) is sent to a client.
+
+      Max Arguments:  1
+          Arguments:  (1) <motd>
+
+      The <motd> is the Message of the Day.
+
+
+10    SILC_NOTIFY_TYPE_CHANNEL_CHANGE
+
+      Sent when channel's ID has changed for a reason or another.
+      This is sent by normal server to the client.  This can also be
+      sent by router to other server to force the Channel ID change.
+      The Channel ID MUST be changed to use the new one.  When sent
+      to clients, this type MUST be sent only to the clients which is
+      joined on the channel.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Channel ID>  (2) <New Channel ID>
+
+      The <Old Channel ID> is the channel's old ID and the <New
+      Channel ID> is the new one that MUST replace the old one.
+
+
+11    SILC_NOTIFY_TYPE_SERVER_SIGNOFF
+
+      Sent when server quits SILC network.  Those clients from this
+      server that are on channels must be removed from the channel.
+
+      Max Arguments:  2000
+          Arguments:  (1) <Server ID>   (n) [<Client ID>   [...]
+
+      The <Server ID> is the server's ID.  The rest of the arguments
+      are the Client ID's of the client's which are coming from this
+      server and are thus quitting the SILC network also.  If the
+      maximum number of arguments are reached another 
+      SILC_NOTIFY_TYPE_SERVER_SIGNOFF notify packet MUST be sent.
+      When this notify packet is sent between routers the Client ID's
+      MAY be omitted.
+
+
+12    SILC_NOTIFY_TYPE_KICKED
+
+      Sent when a client has been kicked from a channel.  This is
+      sent also to the client which was kicked from the channel.
+      The client which was kicked from the channel MUST be removed
+      from the channel.  This notify type is always destined to the
+      channel.  The router or server receiving the packet distributes
+      this type to the local clients on the channel and broadcast it
+      to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was kicked from the channel.
+      The kicker may have set the <comment> to indicate the reason for
+      the kicking.
+
+
+13    SILC_NOTIFY_TYPE_KILLED
+
+      Sent when a client has been killed from the network.  This is sent 
+      also to the client which was killed from the network.  The client
+      which was killed from the network MUST be removed from the network.
+      This notify type is destined directly to the client which was
+      killed and to channel if the client is on any channel.  The router
+      or server receiving the packet distributes this type to the local
+      clients on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was killed from the network.
+      The killer may have set the <comment> to indicate the reason for
+      the killing.
+
+
+14    SILC_NOTIFY_TYPE_UMODE_CHANGE
+
+      Sent when user's mode in the SILC changes.  This type is sent
+      only between routers as broadcast packet.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+
+      The <Client ID> is the client which mode was changed.  The
+      <mode mask> is the new mode mask.
+
+
+15    SILC_NOTIFY_TYPE_BAN
+
+      Sent when the ban list of the channel is changed.  This type is
+      sent only between routers as broadcast packet.
+
+      Max Arguments:  3
+          Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                      (3) [<removing client>]
+
+      The <Channel ID> is the channel which ban list was changed.  The
+      <adding client> is used to indicate that a ban was added and the
+      <removing client> is used to indicate that a ban was removed from
+      the ban list.  The format of the <adding client> and the 
+      <removing client> is defined in the [SILC4] with SILC_COMMAND_BAN
+      command.
+
+.in 3
+
+Notify types starting from 16384 are reserved for private notify
+message types.
+
+
+.ti 0
+2.3.8 Error Payload
+
+Error payload is sent upon error.  Error may occur in various
+conditions when server sends this packet.  Client MUST NOT send this
+payload but MUST be able to accept it.  However, client MAY
+totally ignore the contents of the packet as server is going to
+take action on the error anyway.  However, it is recommended
+that the client takes error packet seriously.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Error Message                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 12:  Error Payload
+
+
+.in 6
+o Error Message (variable length) - Human readable error
+  message.
+.in 3
+
+
+.ti 0
+2.3.9 Channel Message Payload
+
+Channel messages are the most common messages sent in the SILC.
+Channel Message Payload is used to send message to channels.  These
+messages can only be sent if client has joined to some channel.
+Even though this packet is the most common in SILC it is still
+special packet.  Some special handling on sending and reception
+of channel message is required.
+
+Padding MUST be applied into this payload since the payload is
+encrypted separately from other parts of the packet with the
+channel specific key.  Hence the requirement of the padding.  
+The padding SHOULD be random data.  The packet MUST be made
+multiple by eight (8) or by the block size of the cipher, which
+ever is larger.
+
+The SILC header in this packet is encrypted with the session key
+of the next receiver of the packet.  Nothing else is encrypted
+with that key.  Thus, the actual packet and padding to be
+encrypted with the session key is SILC Header plus padding to it
+to make it multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.
+
+Receiver of the the channel message packet is able to determine
+the channel the message is destined to by checking the destination
+ID from the SILC Packet header which tells the destination channel.
+The original sender of the packet is also determined by checking
+the source ID from the header which tells the client which sent
+the message.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_MESSAGE packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Message Payload.
+
+(*) indicates that the field is not encrypted.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |         Message Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Message Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Padding Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                            Padding                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                              MAC                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Initial Vector *                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 13:  Channel Message Payload
+
+
+.in 6
+o Flags (2 bytes) - Includes the flags of the channel
+  messages.  The flags can indicate a reason or purpose
+  for the channel message.  Note that the Private Message
+  Payload use these same flags for the same purpose.  The
+  following flags are defined:
+
+  0x0000  SILC_MESSAGE_FLAG_NONE
+
+          No specific flags set.
+
+  0x0001  SILC_MESSAGE_FLAG_AUTOREPLY
+
+          This message is an automatic reply to an earlier
+          received message.
+
+  0x0002  SILC_MESSAGE_FLAG_NOREPLY
+
+          There should not be reply messages to this
+          message.
+
+  0x0004  SILC_MESSAGE_FLAG_ACTION
+
+          The sender is performing an action and the message
+          is the indication of the action.
+
+  0x0008  SILC_MESSAGE_FLAG_NOTICE
+
+          The message is for example an informational notice
+          type message.
+
+  0x0010  SILC_MESSAGE_FLAG_REQUEST
+
+          This is a generic request flag to send request
+          messages.  A separate document should define any 
+          payloads associated to this flag.
+
+  0x0020  SILC_MESSAGE_FLAG_SIGNED
+
+          This flag indicates that the message is signed
+          with sender's private key and thus can be verified
+          by the receiver using the sender's public key.  A
+          separate document should define the detailed procedure
+          of the signing process and any associated payloads
+          of this flag.
+
+  0x0040 - 0x0200 RESERVED
+
+          Reserved for future flags
+
+  0x0400 - 0x8000 PRIVATE RANGE
+
+          Private range for free use.
+
+o Message Length (2 bytes) - Indicates the length of the
+  the Message Data field in the payload, not including any 
+  other field.
+
+o Message Data (variable length) - The actual message to
+  the channel.
+
+o Padding Length (2 bytes) - Indicates the length of the
+  Padding field in the payload, not including any other
+  field.
+
+o Padding (variable length) - The padding that MUST be
+  applied because this payload is encrypted separately from
+  other parts of the packet.
+
+o MAC (variable length) - The MAC computed from the
+  Message Length, Message Data, Padding Length and Padding
+  fields.  This protects the integrity of the plaintext
+  channel message.  The receiver can verify from the MAC
+  whether the message decrypted correctly.  Also, if more than
+  one private key has been set for the channel, the receiver
+  can verify which of the keys decrypted the message 
+  correctly.  Note that, this field is encrypted and MUST
+  be added to the padding calculation.
+
+o Initial Vector (variable length) - The initial vector
+  that has been used in packet encryption.  It needs to be
+  used in the packet decryption as well.  What this field
+  includes is implementation issue.  However, it is 
+  RECOMMENDED that it would be random data or, perhaps,
+  a timestamp.  It is NOT RECOMMENDED to use zero (0) as an
+  initial vector.  This field is not encrypted.  This field
+  is not included into the padding calculation.  Length
+  of this field equals the cipher's block size.  This field
+  is, however, authenticated.
+.in 3
+
+
+.ti 0
+2.3.10 Channel Key Payload
+
+All traffic in channels are protected by channel specific keys.
+Channel Key Payload is used to distribute channel keys to all
+clients on the particular channel.  Channel keys are sent when
+the channel is created, when new user joins to the channel and
+whenever a user has left a channel.  Server creates the new
+channel key and distributes it to the clients by encrypting this
+payload with the session key shared between the server and
+the client.  After that, client starts using the key received
+in this payload to protect the traffic on the channel.
+
+The client which is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity which sent the SILC_COMMAND_JOIN
+command.
+
+Channel keys are cell specific thus every router in the cell have
+to create a channel key and distribute it if any client in the
+cell has joined to a channel.  Channel traffic between cell's
+are not encrypted using channel keys, they are encrypted using
+normal session keys between two routers.  Inside a cell, all
+channel traffic is encrypted with the specified channel key.
+Channel key should expire periodically, say, in one hour, in
+which case new channel key is created and distributed.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_KEY packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Cipher Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Key Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Key                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 14:  Channel Key Payload
+
+
+
+.in 6
+o Channel ID Length (2 bytes) - Indicates the length of the
+  Channel ID field in the payload, not including any other
+  field.
+
+o Channel ID (variable length) - The Channel ID of the
+  channel this key is meant for.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher used
+  in the protection of channel traffic.  This name is
+  initially decided by the creator of the channel but it
+  MAY change during the life time of the channel as well.
+
+o Channel Key Length (2 bytes) - Indicates the length of the
+  Channel Key field in the payload, not including any other
+  field.
+
+o Channel Key (variable length) - The actual channel key
+  material.
+.in 3
+
+
+.ti 0
+2.3.11 Private Message Payload
+
+Private Message Payload is used to send private message between
+two clients (or users for that matter).  The messages are sent only
+to the specified user and no other user inside SILC network is
+able to see the message.  The message is protected by the session 
+key established by the SILC Key Exchange Protocol.  However,
+it is also possible to agree to use a private key to protect
+just the private messages.  See section 2.3.11 Private Message
+Key Payload for detailed description of how to agree to use
+specific key.
+
+If normal session key is used to protect the message, every server
+between the sender client and the receiving client MUST decrypt the
+packet and always re-encrypt it with the session key of the next
+receiver of the packet.  See section Client To Client in [SILC1].
+
+When private key is used to protect the message, servers between 
+the sender and the receiver needs not to decrypt/re-encrypt the 
+packet.  Section Client To Client in [SILC1] gives example of this
+scheme as well.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |      Message Data Length      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                          Message Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                             Padding                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 15:  Private Message Payload
+
+
+.in 6
+o Flags (2 bytes) - This field includes the flags of the
+  private message.  They can indicate a different reason or
+  purpose for the private message.  See the section 2.3.9
+  Channel Message Payload for defined flags.  Note that
+  the Channel Message Payload use the same flags for the
+  same purpose.
+
+o Message Data Length (2 bytes) - Indicates the length of the
+  Message Data field, not includes any other field.
+
+o Message Data (variable length) - The actual message to
+  the client.  Rest of the packet is reserved for the message
+  data.
+
+o Padding (variable length) - This field is present only
+  when the private message payload is encrypted with private
+  message key.  In this case the padding is applied to make
+  the payload multiple by eight (8), or by the block size of
+  the cipher, which ever is larger.  When encrypted with
+  normal session keys, this field MUST NOT be included.
+.in 3
+
+
+.ti 0
+2.3.12 Private Message Key Payload
+
+This payload is used to send key from client to another client that
+is going to be used to protect the private messages between these
+two clients.  If this payload is not sent normal session key 
+established by the SILC Key Exchange Protocol is used to protect
+the private messages.
+
+This payload may only be sent by client to another client.  Server
+MUST NOT send this payload at any time.  After sending this payload
+the sender of private messages must set the Private Message Key
+flag into SILC Packet Header.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE_KEY 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Private Message Key Length   |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Private Message Key                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Cipher Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 16:  Private Message Key Payload
+
+
+
+
+.in 6
+o Private Message Key Length (2 bytes) - Indicates the length 
+  of the Private Message Key field in the payload, not including 
+  any other field.
+
+o Private Message Key (variable length) - The actual private
+  message key material.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher Name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher to use
+  in the private message encryption.  If this field does not
+  exist then the default cipher of the SILC protocol is used.
+  See the [SILC1] for defined ciphers.
+.in 3
+
+
+
+.ti 0
+2.3.13 Command Payload
+
+Command Payload is used to send SILC commands from client to server.
+Also server MAY send commands to other servers.  The following diagram
+represents the Command Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | SILC Command  | Arguments Num |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Command Identifier      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 17:  Command Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire command 
+  payload including any command argument payloads associated 
+  with this payload.
+
+o SILC Command (1 byte) - Indicates the SILC command.  This MUST 
+  be set to non-zero value.  If zero (0) value is found in this 
+  field the packet MUST be discarded.
+
+o Arguments Num (1 byte) - Indicates the number of arguments
+  associated with the command.  If there are no arguments this
+  field is set to zero (0).  The arguments MUST follow the 
+  command payload.  See section 2.3.2.2 for definition of the
+  Argument Payload.
+
+o Command Identifier (2 bytes) - Identifies this command at the
+  sender's end.  The entity which replies to this command MUST
+  set the value found from this field into the Command Payload
+  used to send the reply to the sender.  This way the sender
+  can identify which command reply belongs to which originally
+  sent command.  What this field includes is implementation
+  issue but it is RECOMMENDED that wrapping counter value is
+  used in the field.  Value zero (0) in this field means that
+  no specific value is set.
+.in 3
+
+See [SILC4] for detailed description of different SILC commands,
+their arguments and their reply messages.
+
+
+
+
+.ti 0
+2.3.14 Command Reply Payload
+
+Command Reply Payload is used to send replies to the commands.  The
+Command Reply Payload is identical to the Command Payload thus see
+the upper section for the Command Payload specification.
+
+The entity which sends the reply packet MUST set the Command Identifier
+field in the reply packet's Command Payload to the value it received
+in the original command packet.
+
+See SILC Commands in [SILC4] for detailed description of different
+SILC commands, their arguments and their reply messages.
+
+
+.ti 0
+2.3.15 Connection Auth Request Payload
+
+Client MAY send this payload to server to request the authentication
+method that must be used in authentication protocol.  If client knows 
+this information beforehand this payload is not necessary to be sent.
+Server performing authentication with another server MAY also send
+this payload to request the authentication method.  If the connecting
+server already knows this information this payload is not necessary
+to be sent.
+
+Server receiving this request MUST reply with same payload sending
+the mandatory authentication method.  Algorithms that may be required
+to be used by the authentication method are the ones already 
+established by the SILC Key Exchange protocol.  See section Key
+Exchange Start Payload in [SILC3] for detailed information.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Connection Auth Request Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Connection Type        |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 18:  Connection Auth Request Payload
+
+
+.in 6
+o Connection Type (2 bytes) - Indicates the type of the
+  connection.  The following connection types are defined:
+
+
+     1    Client connection
+     2    Server connection
+     3    Router connection
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.
+
+o Authentication Method (2 bytes) - Indicates the authentication
+  method to be used in the authentication protocol.  The following
+  authentication methods are defined:
+
+     0    NONE        (mandatory)
+     1    password    (mandatory)
+     2    public key  (mandatory)
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.  If this
+  payload is sent as request to receive the mandatory 
+  authentication method this field MUST be set to zero (0),
+  indicating that receiver should send the mandatory 
+  authentication method.  The receiver sending this payload
+  to the requesting party, MAY also set this field to zero (0) 
+  to indicate that authentication is not required.  In this
+  case authentication protocol still MUST be started but
+  server is most likely to respond with SILC_PACKET_SUCCESS
+  immediately.
+.in 3
+
+
+.ti 0
+2.3.16 New ID Payload
+
+New ID Payload is a multipurpose payload.  It is used to send newly 
+created ID's from clients and servers.  When client connects to server
+and registers itself to the server by sending SILC_PACKET_NEW_CLIENT
+packet, server replies with this packet by sending the created ID for
+the client.  Server always creates the ID for the client.
+
+This payload is also used when server tells its router that new client
+has registered to the SILC network.  In this case the server sends
+the Client ID of the client to the router.  Similarly when router
+distributes information to other routers about the client in the SILC
+network this payload is used.  
+
+Also, when server connects to router, router uses this payload to inform
+other routers about new server in the SILC network.  However, every 
+server (or router) creates their own ID's thus the ID distributed by 
+this payload is not created by the distributor in this case.  Servers
+create their own ID's.  Server registers itself to the network by sending
+SILC_PACKET_NEW_SERVER to the router it connected to.  The case is same
+when router connects to another router.
+
+However, this payload MUST NOT be used to send information about new
+channels.  New channels are always distributed by sending the dedicated
+SILC_PACKET_NEW_CHANNEL packet.
+
+Thus, this payload is very important and used every time when some
+new entity is registered to the SILC network.  Client MUST NOT send this
+payload.  Both client and server (and router) MAY receive this payload.
+
+The packet uses generic ID Payload as New ID Payload.  See section
+2.3.2.1 for generic ID Payload.
+
+
+.ti 0
+2.3.17 New Client Payload
+
+When client is connected to the server, keys has been exchanged and
+connection has been authenticated client MUST register itself to the 
+server.  Client's first packet after key exchange and authentication 
+protocols must be SILC_PACKET_NEW_CLIENT.  This payload tells server all
+the relevant information about the connected user.  Server creates a new
+client ID for the client when received this payload and sends it to the
+client in New ID Payload.
+
+This payload sends username and real name of the user on the remote host
+which is connected to the SILC server with SILC client.  The server 
+creates the client ID according the information sent in this payload.
+The nickname of the user becomes the username sent in this payload.
+However, client should call NICK command after sending this payload to
+set the real nickname of the user which is then used to create new 
+client ID.
+
+The payload may only be sent with SILC_PACKET_NEW_CLIENT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Client Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Username Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Username                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Real Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Real Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 19:  New Client Payload
+
+
+.in 6
+o Username Length (2 bytes) - Length of the Username field.
+
+o Username (variable length) - The username of the user on
+  the host where connecting to the SILC server.
+
+o Real Name Length (2 bytes) - Length of the Real Name field.
+
+o Real Name (variable length) - The real name of the user
+  on the host where connecting to the SILC server.
+.in 3
+
+
+.ti 0
+2.3.18 New Server Payload
+
+This payload is sent by server when it has completed successfully both
+key exchange and connection authentication protocols.  The server
+MUST register itself to the SILC Network by sending this payload.
+The first packet after these key exchange and authentication protocols
+is SILC_PACKET_NEW_SERVER packet.  The payload includes the Server ID
+of the server that it has created by itself.  It also includes a
+name of the server that is associated to the Server ID.
+
+The payload may only be sent with SILC_PACKET_NEW_SERVER packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Server Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Server ID Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Server ID Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Server Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Server Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 20:  New Server Payload
+
+
+.in 6
+o Server ID Length (2 bytes) - Length of the Server ID Data
+  field.
+
+o Server ID Data (variable length) - The actual Server ID
+   data.
+
+o Server Name Length (2 bytes) - Length of the server name
+  field.
+
+o Server Name (variable length) - The server name.
+.in 3
+
+
+.ti 0
+2.3.19 New Channel Payload
+
+Information about newly created channel is broadcasted to all routers
+in the SILC network by sending this packet payload.  Channels are
+created by router of the cell.  Server never creates channels unless
+it is a standalone server and it does not have router connection,
+in this case server acts as router.  Normal server send JOIN command
+to the router (after it has received JOIN command from client) which
+then processes the command and creates the channel.  Client MUST NOT
+send this packet.
+
+The packet uses generic Channel Payload as New Channel Payload.  See
+section 2.3.2.3 for generic Channel Payload.  The Mode Mask field in the
+Channel Payload is the mode of the channel.
+
+
+
+
+.ti 0
+2.3.20 Key Agreement Payload
+
+This payload is used by clients to request key negotiation between
+another client in the SILC Network.  The key agreement protocol used
+is the SKE protocol.  The result of the protocol, the secret key
+material, can be used for example as private message key between the
+two clients.  This significantly adds security as the key agreement
+is performed outside the SILC network.  The server and router MUST NOT
+send this payload.
+
+The sender MAY tell the receiver of this payload the hostname and the
+port where the SKE protocol is running in the sender's end.  The 
+receiver MAY then initiate the SKE negotiation with the sender.  The
+sender MAY also optionally not to include the hostname and the port
+of its SKE protocol.  In this case the receiver MAY reply to the
+request by sending the same payload filled with the receiver's hostname
+and the port where the SKE protocol is running.  The sender MAY then
+initiate the SKE negotiation with the receiver.
+
+The payload may only be sent with SILC_PACKET_KEY_AGREEMENT packet.
+It MUST NOT be sent in any other packet type.  The following diagram
+represents the Key Agreement Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 21:  Key Agreement Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the
+  Hostname field.
+
+o Hostname (variable length) - The hostname or IP address where
+  the SKE protocol is running.  The sender MAY fill this field
+  when sending the payload.  If the receiver sends this payload
+  as reply to the request it MUST fill this field.
+
+o Port (4 bytes) - The port where the SKE protocol is bound.
+  The sender MAY fill this field when sending the payload.  If
+  the receiver sends this payload as reply to the request it 
+  MUST fill this field.  This is a 32 bit MSB first order value.
+.in 3
+
+
+After the key material has been received from the SKE protocol it is
+processed as the [SILC3] describes.  If the key material is used as
+channel private key then the Sending Encryption Key, as defined in
+[SILC3] is used as the channel private key.  Other key material must
+be discarded.  The [SILC1] defines the way to use the key material if
+it is intended to be used as private message keys.  Any other use for
+the key material is undefined.
+
+
+.ti 0
+2.3.21 Cell Routers Payload
+
+Cell Routers payload is used by router to notify its primary router what
+other routers exist in the cell.  The other routers are considered to be
+backup routers and one of them will come active only in the case of
+failure of the primary router.  Normal server MAY send this packet if it
+is acting as backup router.  Client MUST NOT send this packet.  To send
+more than one backup router set the List flag and assemble the payloads
+as list.
+
+The payload may only be sent with SILC_PACKET_CELL_ROUTERS packet.  It
+MUST NOT be sent in any other packet type.  The Following diagram
+represents the Cell Routers Payload.
+
+         
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Server ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Server ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 22:  Cell Routers Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the Hostname
+  field.
+
+o Hostname (variable length) - The hostname or IP address of
+  the backup router.
+
+o Port (4 bytes) - The port of the backup router it currently uses.
+  This is a 32 bit MSB first order value.
+
+o Server ID Length (2 bytes) - Indicates the length of the Server
+  ID field.
+
+o Server ID (variable length) - Consists of the Server ID of the
+  backup router.
+.in 3
+
+
+.ti 0
+2.4 SILC ID Types
+
+ID's are extensively used in the SILC network to associate different
+entities.  The following ID's has been defined to be used in the SILC
+network.
+
+.in 6
+0    No ID
+
+     When ever specific ID cannot be used this is used.
+
+1    Server ID
+
+     Server ID to associate servers.  See the format of
+     this ID in [SILC1].
+
+2    Client ID
+
+     Client ID to associate clients.  See the format of
+     this ID in [SILC1].
+
+3    Channel ID
+
+     Channel ID to associate channels.  See the format of
+     this ID in [SILC1].
+.in 3
+
+
+.ti 0
+2.5 Packet Encryption And Decryption
+
+SILC packets are encrypted almost entirely.  Only small part of SILC
+header is not encrypted as described in section 5.2 SILC Packet Header.
+The SILC Packet header is the first part of a packet to be encrypted
+and it is always encrypted with the key of the next receiver of the
+packet.  The data payload area of the packet is always entirely 
+encrypted and it is usually encrypted with the next receiver's key.
+However, there are some special packet types and packet payloads
+that require special encryption process.  These special cases are
+described in the next sections.  First is described the normal packet
+encryption process.
+
+
+.ti 0
+2.5.1 Normal Packet Encryption And Decryption
+
+Normal SILC packets are encrypted with the session key of the next
+receiver of the packet.  The entire SILC Packet header and the packet
+data payload is is also encrypted with the same key.  Padding of the
+packet is also encrypted always with the session key, also in special
+cases.  Computed MAC of the packet must not be encrypted.
+
+Decryption process in these cases are straightforward.  The receiver
+of the packet MUST first decrypt the SILC Packet header, or some parts
+of it, usually first 16 bytes of it.  Then the receiver checks the
+packet type from the decrypted part of the header and can determine
+how the rest of the packet must be decrypted.  If the packet type is
+any of the special cases described in the following sections the packet
+decryption is special.  If the packet type is not among those special
+packet types rest of the packet can be decrypted with the same key.
+
+Also, note that two bytes of the SILC Packet header are not encrypted
+thus it must be noticed in the decryption process by starting the
+decryption from the second byte of the header.  This sets some rules
+to padding generation as well, see the section 2.7 Packet Padding
+Generation.
+
+With out a doubt, this sort of decryption processing causes some
+overhead to packet decryption, but never the less, is required.
+
+
+.ti 0
+2.5.2 Channel Message Encryption And Decryption
+
+Channel Messages (Channel Message Payload) are always encrypted with
+the channel specific key.  However, the SILC Packet header is not 
+encrypted with that key.  As in normal case, the header is encrypted
+with the key of the next receiver of the packet, who ever that might
+be.  Note that in this case the encrypted data area is not touched
+at all; it MUST NOT be re-encrypted with the session key.
+
+Receiver of a channel message, who ever that is, is REQUIRED to decrypt
+the SILC Packet header to be able to even recognize the packet to be as
+channel message.  This is same procedure as for normal SILC packets.
+As the receiver founds the packet to be channel message, rest of the
+packet processing is special.  Rest of the SILC Packet header is
+decrypted with the same session key along with the padding of the
+packet.  After that the packet is protected with the channel specific
+key and thus can be decrypted only if the receiver is the client on
+the channel.  See section 2.7 Packet Padding Generation for more
+information about padding on special packets.
+
+If the receiver of the channel message is router which is routing the
+message to another router then it MUST decrypt the Channel Message
+payload.  Between routers (that is, between cells) channel messages
+are protected with session keys shared between the routers.  This
+causes another special packet processing for channel messages.  If
+the channel message is received from another router then the entire
+packet, including Channel Message payload, MUST be encrypted with the
+session key shared between the routers.  In this case the packet
+decryption process is as with normal SILC packets.  Hence, if the
+router is sending channel message to another router the Channel
+Message payload MUST have been decrypted and MUST be re-encrypted
+with the session key shared between the another router.  In this
+case the packet encryption is as with any normal SILC packet.
+
+It must be noted that this is only when the channel messages are sent
+from router to another router.  In all other cases the channel
+message encryption and decryption is as described above.  This
+different processing of channel messages with router to router
+connection is because channel keys are cell specific.  All cells has
+their own channel keys thus the channel message traveling from one
+cell to another MUST be protected as it would be any normal SILC
+packet.
+
+If the SILC_CMODE_PRIVKEY channel mode has been set for the channel
+then the router cannot decrypt the packet as it does not know the
+private key.  In this case the entire packet MUST be encrypted with
+the session key and sent to the router.  The router receiving the
+packet MUST check the channel mode and decrypt the packet accordingly.
+
+
+.ti 0
+2.5.3 Private Message Encryption And Decryption
+
+By default, private message in SILC are protected by session keys.
+In this case the private message encryption and decryption process is
+equivalent to normal packet encryption and decryption.
+
+However, private messages MAY be protected with private message key
+which causes the packet to be special packet.  The procedure in this
+case is very much alike to channel packets.  The actual private message
+is encrypted with the private message key and other parts of the
+packet is encrypted with the session key.  See 2.7 Packet Padding
+Generation for more information about padding on special packets.
+
+The difference from channel message processing is that server or router
+en route never decrypts the actual private message, as it does not
+have the key to do that.  Thus, when sending packets between router
+the processing is same as in any other case as well; the packet's header
+and padding is protected by the session key and the data area is not
+touched.
+
+The true receiver of the private message, client, that is, is able
+to decrypt the private message as it shares the key with the sender
+of the message.
+
+
+.ti 0
+2.6 Packet MAC Generation
+
+Data integrity of a packet is protected by including a message
+authentication code (MAC) at the end of the packet.  The MAC is computed
+from shared secret MAC key, that is established by the SILC Key Exchange
+protocol, and from the original contents of the packet.  The MAC is
+always computed before the packet is encrypted, although after it is
+compressed if compression is used.
+
+The MAC is computed from entire packet.  Every bit of data in the packet,
+including SILC Packet Header is used in the MAC computing.  This way
+the entire packet becomes authenticated.
+
+If the packet is special packet MAC is computed from the entire packet
+but part of the packet may be encrypted before the MAC is computed.
+This is case, for example, with channel messages where the message data
+is encrypted with key that server may not now.  In this case the MAC
+has been computed from the encrypted data.
+
+See [SILC1] for defined and allowed MAC algorithms.
+
+
+.ti 0
+2.7 Packet Padding Generation
+
+Padding is needed in the packet because the packet is encrypted.  It
+MUST always be multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.  The padding is always encrypted.
+
+For normal packets the padding is added after the SILC Packet Header
+and between the Data Payload area.  The padding for normal packets
+are calculated as follows:
+
+.in 6
+padding length = 16 - ((packet length - 2) mod 16)
+.in 3
+
+The 16 is the maximum padding allowed in SILC packet.  Two (2) is 
+subtracted from the true length of the packet because two (2) bytes
+is not encrypted in SILC Packet Header, see section 2.2 SILC Packet
+Header.  Those two bytes that are not encrypted MUST NOT be calculated
+to the padding length.
+
+For special packets the padding calculation MAY be different as special
+packets may be encrypted differently.  In these cases the encrypted
+data area MUST already be multiple by the block size thus in this case
+the padding is calculated only for SILC Packet Header, not for any
+other area of the packet.  The same algorithm works in this case as
+well, except that the `packet length' is now the SILC Packet Header
+length.  In this case, as well, two (2) is subtracted from the
+length.
+
+The padding MUST be random data, preferably, generated by 
+cryptographically strong random number generator.
+
+
+.ti 0
+2.8 Packet Compression
+
+SILC Packets MAY be compressed.  In this case the data payload area
+is compressed and all other areas of the packet MUST remain as they
+are.  After compression is performed for the data area, the length
+field of Packet Header MUST be set to the compressed length of the
+data.
+
+The compression MUST always be applied before encryption.  When
+the packet is received and decrypted the data area MUST be decompressed.
+Note that the true sender of the packet MUST apply the compression and
+the true receiver of the packet MUST apply the decompression.  Any
+server or router en route MUST NOT decompress the packet.
+
+
+.ti 0
+2.9 Packet Sending
+
+The sender of the packet MUST assemble the SILC Packet Header with
+correct values.  It MUST set the Source ID of the header as its own
+ID, unless it is forwarding the packet.  It MUST also set the Destination
+ID of the header to the true destination.  If the destination is client
+it will be Client ID, if it is server it will be Server ID and if it is
+channel it will be Channel ID.
+
+If the sender wants to compress the packet it MUST apply the
+compression now.  Sender MUST also compute the padding as described
+in above sections.  Then sender MUST compute the MAC of the packet.
+
+Then sender MUST encrypt the packet as has been described in above
+sections according whether the packet is normal packet or special
+packet.  The computed MAC MUST NOT be encrypted.
+
+
+.ti 0
+2.10 Packet Reception
+
+On packet reception the receiver MUST check that all fields in the
+SILC Packet Header are valid.  It MUST check the flags of the
+header and act accordingly.  It MUST also check the MAC of the packet
+and if it is to be failed the packet MUST be discarded.  Also if the
+header of the packet includes any bad fields the packet MUST be
+discarded.
+
+See above sections on the decryption process of the received packet.
+The receiver MUST also check that the ID's in the header are valid
+ID's.  Unsupported ID types or malformed ID's MUST cause packet
+rejection.  The padding on the reception is always ignored.
+
+The receiver MUST also check the packet type and start parsing the
+packet according to the type.  However, note the above sections on
+special packet types and their parsing.
+
+
+.ti 0
+2.11 Packet Routing
+
+Routers are the primary entities in the SILC network that takes care
+of packet routing.  However, normal servers routes packets as well, for
+example, when they are routing channel message to the local clients.
+Routing is quite simple as every packet tells the true origin and the
+true destination of the packet.
+
+It is still RECOMMENDED for routers that has several routing connections
+to create route cache for those destinations that has faster route than
+the router's primary route.  This information is available for the router
+when other router connects to the router.  The connecting party then
+sends all of its locally connected clients, servers and channels.  These
+informations helps to create the route cache.  Also, when new channels
+are created to a cell its information is broadcasted to all routers
+in the network.  Channel ID's are based on router's ID thus it is easy
+to create route cache based on these informations.  If faster route for
+destination does not exist in router's route cache the packet MUST be
+routed to the primary route (default route).
+
+For server which receives a packet to be routed to its locally connected
+client the server MUST check whether the particular packet type is
+allowed to be routed to the client.  Not all packets may be sent by
+some odd entity to client that is indirectly connected to the sender.
+See section 2.3 SILC Packet Types and paragraph about indirectly connected
+entities and sending packets to them.  The section mentions the packets
+that may be sent to indirectly connected entities.  It is clear that
+server cannot send, for example, disconnect packet to client that is not
+directly connected to the server.
+
+
+.ti 0
+2.12 Packet Broadcasting
+
+SILC packets MAY be broadcasted in SILC network.  However, only router
+server may send or receive broadcast packets.  Client and normal server
+MUST NOT send broadcast packets and they MUST ignore broadcast packets
+if they receive them.  Broadcast packets are sent by setting Broadcast
+flag to the SILC packet header.
+
+Broadcasting packets means that the packet is sent to all routers in
+the SILC network, except to the router that sent the packet.  The router
+receiving broadcast packet MUST send the packet to its primary route.
+The fact that SILC routers may have several router connections can
+cause problems, such as race conditions inside the SILC network, if
+care is not taken when broadcasting packets.  Router MUST NOT send
+the broadcast packet to any other route except to its primary route.
+
+If the primary route of the router is the original sender of the packet
+the packet MUST NOT be sent to the primary route.  This may happen
+if router has several router connections and some other router uses
+the router as its primary route.
+
+Routers use broadcast packets to broadcast for example information
+about newly registered clients, servers, channels etc. so that all the
+routers may keep these informations up to date.
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 21 February 2002
diff --git a/doc/draft-riikonen-silc-pp-04.nroff b/doc/draft-riikonen-silc-pp-04.nroff
new file mode 100644 (file)
index 0000000..3ea6a3b
--- /dev/null
@@ -0,0 +1,2815 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 13 November 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-pp-04.txt                           13 November 2001
+Expires: 13 May 2002
+
+.in 3
+
+.ce 2
+SILC Packet Protocol
+<draft-riikonen-silc-pp-04.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Packet Protocol used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Packet Protocol ..........................................  4
+  2.1 SILC Packet ...............................................  4
+  2.2 SILC Packet Header ........................................  5
+  2.3 SILC Packet Types .........................................  7
+      2.3.1 SILC Packet Payloads ................................ 16
+      2.3.2 Generic payloads .................................... 16
+            2.3.2.1 ID Payload .................................. 17
+            2.3.2.2 Argument Payload ............................ 18
+            2.3.2.3 Channel Payload ............................. 18
+            2.3.2.4 Public Key Payload .......................... 19
+      2.3.3 Disconnect Payload .................................. 20
+      2.3.4 Success Payload ..................................... 21
+      2.3.5 Failure Payload ..................................... 21
+      2.3.6 Reject Payload ...................................... 22
+      2.3.7 Notify Payload ...................................... 22
+      2.3.8 Error Payload ....................................... 28
+      2.3.9 Channel Message Payload ............................. 29
+      2.3.10 Channel Key Payload ................................ 32
+      2.3.11 Private Message Payload ............................ 34
+      2.3.12 Private Message Key Payload ........................ 35
+      2.3.13 Command Payload .................................... 37
+      2.3.14 Command Reply Payload .............................. 38
+      2.3.15 Connection Auth Request Payload .................... 38
+      2.3.16 New ID Payload ..................................... 39
+      2.3.17 New Client Payload ................................. 40
+      2.3.18 New Server Payload ................................. 41
+      2.3.19 New Channel Payload ................................ 42
+      2.3.20 Key Agreement Payload .............................. 43
+      2.3.21 Resume Router Payload .............................. 44
+      2.3.22 File Transfer Payload .............................. 44
+  2.4 SILC ID Types ............................................. 46
+  2.5 Packet Encryption And Decryption .......................... 46
+      2.5.1 Normal Packet Encryption And Decryption ............. 46
+      2.5.2 Channel Message Encryption And Decryption ........... 47
+      2.5.3 Private Message Encryption And Decryption ........... 48
+  2.6 Packet MAC Generation ..................................... 48
+  2.7 Packet Padding Generation ................................. 49
+  2.8 Packet Compression ........................................ 50
+  2.9 Packet Sending ............................................ 50
+  2.10 Packet Reception ......................................... 51
+  2.11 Packet Routing ........................................... 51
+  2.12 Packet Broadcasting ...................................... 52
+3 Security Considerations ....................................... 53
+4 References .................................................... 53
+5 Author's Address .............................................. 54
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:   Typical SILC Packet
+Figure 2:   SILC Packet Header
+Figure 3:   ID Payload
+Figure 4:   Argument Payload
+Figure 5:   Channel Payload
+Figure 6:   Public Key Payload
+Figure 7:   Disconnect Payload
+Figure 8:   Success Payload
+Figure 9:   Failure Payload
+Figure 10:   Reject Payload
+Figure 11:  Notify Payload
+Figure 12:  Error Payload
+Figure 13:  Channel Message Payload
+Figure 14:  Channel Key Payload
+Figure 15:  Private Message Payload
+Figure 16:  Private Message Key Payload
+Figure 17:  Command Payload
+Figure 18:  Connection Auth Request Payload
+Figure 19:  New Client Payload
+Figure 20:  New Server Payload
+Figure 21:  Key Agreement Payload
+Figure 22:  Resume Router Payload
+Figure 23:  File Transfer Payload
+
+
+.ti 0
+1. Introduction
+
+This document describes a Packet Protocol used in the Secure Internet
+Live Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+The basis of SILC protocol relies in the SILC packets and it is with
+out a doubt the most important part of the protocol.  It is also probably
+the most complicated part of the protocol.  Packets are used all the
+time in the SILC network to send messages, commands and other information.
+All packets in SILC network are always encrypted and their integrity
+is assured by computed MACs.  The protocol defines several packet types
+and packet payloads.  Each packet type usually has a specific packet
+payload that actually defines the contents of the packet.  Each packet
+also includes a default SILC Packet Header that provides sufficient
+information about the origin of the packet and destination of the
+packet.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Packet Protocol
+
+.ti 0
+2.1 SILC Packet
+
+SILC packets deliver messages from sender to receiver securely by
+encrypting important fields of the packet.  The packet consists of
+default SILC Packet Header, Padding, Packet Payload data, and, packet 
+MAC.
+
+The following diagram illustrates typical SILC packet.
+
+
+.in 5
+.nf
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+|   n bytes   | 1 - n bytes |      n bytes       |  n bytes       
+| SILC Header |   Padding   |    Data Payload    |    MAC    
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+.in 3
+
+.ce
+Figure 1:  Typical SILC Packet
+
+
+SILC Header is always the first part of the packet and its purpose
+is to provide information about the packet.  It provides for example
+the packet type, origin of the packet and the destination of the packet.
+The header is variable in length.  See the following section for
+description of SILC Packet header.  Packets without SILC header or
+with malformed SILC header MUST be dropped.
+
+Padding follows the packet header.  The purpose of the padding is to
+make the packet multiple by eight (8) or by the block size of the
+cipher used in the encryption, which ever is larger.  The maximum
+length of padding is currently 128 bytes.  The padding is always
+encrypted.  The padding is applied always, even if the packet is
+not encrypted.  See the section 2.7 Padding Generation for more
+detailed information.
+
+Data payload area follows padding and it is the actual data of the
+packet.  The packet data is the packet payloads defined in this
+protocol.  The data payload area is always encrypted.
+
+The last part of SILC packet is the packet MAC that assures the
+integrity of the packet.  The MAC is always computed from the packet
+before the encryption is applied to the packet.  If compression is used
+in the packet the MAC is computed after the compression has been
+applied.  The compression, on the other hand, is always applied before
+encryption.  See more details in the section 2.6 Packet MAC Generation.
+
+All fields in all packet payloads are always in MSB (most significant
+byte first) order.
+
+
+.ti 0
+2.2 SILC Packet Header
+
+The SILC packet header is applied to all SILC packets and it is
+variable in length.  The purpose of SILC Packet header is to provide
+detailed information about the packet.  The receiver of the packet
+uses the packet header to parse the packet and gain other relevant
+parameters of the packet.
+
+The following diagram represents the SILC packet header.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        |     Flags     |  Packet Type  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Pad Length  |    RESERVED   | Source ID Len |  Dest ID Len  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Src ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                           Source ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Dst ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                         Destination ID                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  SILC Packet Header
+
+
+.in 6
+o Payload Length (2 bytes) - Is the length of the packet
+  not including the padding of the packet.
+
+o Flags (1 byte) - Indicates flags to be used in packet
+  processing.  Several flags may be set by ORing the flags
+  together.
+
+  The following flags are reserved for this field:
+
+
+     No flags                  0x00
+
+       In this case the field is ignored.
+
+
+     Private Message Key       0x01
+
+       Indicates that the packet must include private
+       message that is encrypted using private key set by
+       client.  Servers does not know anything about this
+       key and this causes that the private message is
+       not handled by the server at all, it is just
+       passed along.  See section 2.5.3 Private Message
+       Encryption And Decryption for more information.
+
+
+     List                      0x02
+  
+       Indicates that the packet consists of list of
+       packet payloads indicated by the Packet Type field.
+       The payloads are added one after the other.  Note that
+       there are packet types that must not be used as
+       list.  Parsing of list packet is done by calculating
+       the length of each payload and parsing them one by
+       one.
+
+
+     Broadcast                 0x04
+
+       Marks the packet to be broadcasted.  Client cannot
+       send broadcast packet and normal server cannot send
+       broadcast packet.  Only router server may send broadcast
+       packet.  The router receiving of packet with this flag 
+       set MUST send (broadcast) the packet to its primary
+       route.  If router has several router connections the
+       packet may be sent only to the primary route.  See
+       section 2.12 Packet Broadcasting for description of 
+       packet broadcasting.
+
+.in 3
+
+
+
+
+o Packet Type (1 byte) - Is the type of the packet. Receiver 
+  uses this field to parse the packet.  See section 2.3
+  SILC Packets for list of defined packet types.
+
+o Pad Length (1 byte) - Indicates the length of the padding
+  applied after the SILC Packet header.  Maximum length for
+  padding is 128 bytes.
+
+o RESERVED (1 byte) - Reserved field and must include a
+  zero (0) value.
+
+o Source ID Length (1 byte) - Indicates the length of the
+  Source ID field in the header, not including this or any
+  other fields.
+
+o Destination ID Length (1 byte) - Indicates the length of the
+  Destination ID field in the header, not including this or
+  any other fields.
+
+o Src ID Type (1 byte) - Indicates the type of ID in the
+  Source ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Source ID (variable length) - The actual source ID that
+  indicates which is the original sender of the packet.
+
+o Dst ID Type (1 byte) - Indicates the type of ID in the
+  Destination ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Destination ID (variable length) - The actual destination
+  ID that indicates which is the end receiver of the packet.
+
+
+
+.ti 0
+2.3 SILC Packet Types
+
+SILC packet types defines the contents of the packet and it is used by
+the receiver to parse the packet.  The packet type is 8 bits, as a one
+byte, in length.  The range for the packet types are from 0 - 255,
+where 0 is never sent and 255 is currently reserved for future
+extensions and MUST NOT be defined to any other purpose.  Every SILC
+specification compliant implementation SHOULD support all of these packet
+types.
+
+The below list of the SILC Packet types includes reference to the packet
+payload as well.  Packet payloads are the actual packet, that is, the data
+that the packet consists of.  Each packet type defines packet payload 
+which usually may only be sent with the specific packet type.
+
+Most of the packets are packets that must be destined directly to entity
+that is connected to the sender.  It is not allowed, for example, for
+router to send disconnect packet to client that is not directly connected
+to the router.  However, there are some special packet types that may
+be destined to some entity that the sender has not direct connection
+with.  These packets are for example private message packets, channel
+message packets, command packets and some other packets that may be
+broadcasted in the SILC network.  If the packet is allowed to be sent to
+indirectly connected entity it is mentioned separately in the packet
+description (unless it is obvious as in private and channel message
+packets).  Other packets MUST NOT be sent or accepted, if sent, to
+indirectly connected entities.
+
+List of SILC Packet types are defined as follows.
+
+.in 1
+     0    SILC_PACKET_NONE
+
+          This type is reserved and it is never sent.         
+
+
+     1    SILC_PACKET_DISCONNECT
+
+          This packet is sent to disconnect the remote end.  Reason of
+          the disconnection is sent inside the packet payload.  Client
+          usually does not send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.3 Disconnect Payload
+
+
+     2    SILC_PACKET_SUCCESS
+
+          This packet is sent upon successful execution of some protocol.
+          The status of the success is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.4 Success Payload
+
+
+     3    SILC_PACKET_FAILURE
+
+          This packet is sent upon failure of some protocol.  The status
+          of the failure is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.5 Failure Payload
+
+
+     4    SILC_PACKET_REJECT
+
+          This packet MAY be sent upon rejection of some protocol.
+          The status of the rejection is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.6 Reject Payload
+
+
+     5    SILC_PACKET_NOTIFY
+
+          This packet is used to send notify message, usually from
+          server to client, although it MAY be sent from server to another
+          server as well.  Client MUST NOT send this packet.  Server MAY
+          send this packet to channel as well when the packet is 
+          distributed to all clients on the channel.
+
+          Payload of the packet:  See section 2.3.7 Notify Payload.
+
+
+     6    SILC_PACKET_ERROR
+
+          This packet is sent when an error occurs.  Server MAY
+          send this packet.  Client MUST NOT send this packet.  The
+          client MAY entirely ignore the packet, however, server is
+          most likely to take action anyway.  This packet MAY be sent
+          to entity that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.8 Error Payload.
+
+
+     7    SILC_PACKET_CHANNEL_MESSAGE
+
+          This packet is used to send messages to channels.  The packet
+          includes Channel ID of the channel and the actual message to
+          the channel.  Messages sent to the channel are always protected
+          by channel specific keys.  Channel Keys are distributed by
+          SILC_PACKET_CHANNEL_KEY packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.9 Channel Message 
+                                  Payload
+
+
+     8    SILC_PACKET_CHANNEL_KEY
+
+          This packet is used to distribute new key for particular
+          channel.  Each channel has their own independent keys that
+          is used to protect the traffic on the channel.  Only server
+          may send this packet.  This packet MAY be sent to entity
+          that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.10 Channel Key Payload
+
+
+     9    SILC_PACKET_PRIVATE_MESSAGE
+
+          This packet is used to send private messages from client
+          to another client.  By default, private messages are protected
+          by session keys established by normal key exchange protocol.
+          However, it is possible to use specific key to protect private
+          messages.  SILC_PACKET_PRIVATE_MESSAGE_KEY packet is used to 
+          agree the key with the remote client.  Pre-shared key MAY be 
+          used as well if both of the client knows it, however, it needs 
+          to be agreed outside SILC.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.11 Private Message
+                                  Payload
+
+
+     10   SILC_PACKET_PRIVATE_MESSAGE_KEY
+
+          This packet is used to agree about a key to be used to protect
+          the private messages between two clients.  If this is not sent
+          the normal session key is used to protect the private messages
+          inside SILC network.  Agreeing to use specific key to protect
+          private messages adds security, as no server between the two
+          clients will be able to decrypt the private message.  However,
+          servers inside SILC network are considered to be trusted, thus
+          using normal session key to protect private messages does not
+          degrade security.  Whether to agree to use specific keys by
+          default or to use normal session keys by default, is 
+          implementation specific issue.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.12 Private Message
+                                  Key Payload
+
+
+     11   SILC_PACKET_COMMAND
+
+          This packet is used to send commands from client to server.
+          Server MAY send this packet to other servers as well.  All
+          commands are listed in their own section SILC Command Types
+          in [SILC4].  The contents of this packet is command specific.
+          This packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.13 Command Payload
+
+
+     12   SILC_PACKET_COMMAND_REPLY
+
+          This packet is sent as reply to the SILC_PACKET_COMMAND packet.
+          The contents of this packet is command specific.  This packet
+          MAY be sent to entity that is indirectly connected to the
+          sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.14 Command Reply 
+                                  Payload and section 2.3.13 Command
+                                  Payload
+
+
+
+
+     13   SILC_PACKET_KEY_EXCHANGE
+
+          This packet is used to start SILC Key Exchange Protocol, 
+          described in detail in [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     14   SILC_PACKET_KEY_EXCHANGE_1
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     15   SILC_PACKET_KEY_EXCHANGE_2
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     16   SILC_PACKET_CONNECTION_AUTH_REQUEST
+
+          This packet is used to request the authentication method to
+          be used in the SILC Connection Authentication Protocol.  If 
+          initiator of the protocol does not know the mandatory 
+          authentication method this packet MAY be used to determine it.
+
+          The party receiving this payload MUST respond with the same
+          packet including the mandatory authentication method.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.15 Connection Auth
+                                  Request Payload
+
+
+
+
+     17   SILC_PACKET_CONNECTION_AUTH
+
+          This packet is used to start and perform the SILC Connection
+          Authentication Protocol.  This protocol is used to authenticate
+          the connecting party.  The protocol is described in detail in
+          [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Authentication
+                                  Protocol and it sub sections in [SILC].
+
+
+     18   SILC_PACKET_NEW_ID
+
+          This packet is used to distribute new ID's from server to
+          router and from router to all routers in the SILC network.
+          This is used when for example new client is registered to
+          SILC network.  The newly created ID's of these operations are
+          distributed by this packet.  Only server may send this packet,
+          however, client MUST be able to receive this packet.  This
+          packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          Payload of the packet:  See section 2.3.16 New ID Payload
+
+
+     19   SILC_PACKET_NEW_CLIENT
+
+          This packet is used by client to register itself to the   
+          SILC network.  This is sent after key exchange and  
+          authentication protocols has been completed.  Client sends
+          various information about itself in this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.17 New Client Payload
+
+
+     20   SILC_PACKET_NEW_SERVER
+
+          This packet is used by server to register itself to the
+          SILC network.  This is sent after key exchange and 
+          authentication protocols has been completed.  Server sends
+          this to the router it connected to, or, if router was
+          connecting, to the connected router.  Server sends its
+          Server ID and other information in this packet.  The client
+          MUST NOT send or receive this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.18 New Server Payload
+
+
+     21   SILC_PACKET_NEW_CHANNEL
+
+          This packet is used to notify routers about newly created
+          channel.  Channels are always created by the router and it MUST
+          notify other routers about the created channel.  Router sends
+          this packet to its primary route.  Client MUST NOT send this
+          packet.  This packet MAY be sent to entity that is indirectly
+          connected to the sender.
+
+          Payload of the packet:  See section 2.3.19 New Channel Payload
+
+
+     22   SILC_PACKET_REKEY
+
+          This packet is used to indicate that re-key must be performed
+          for session keys.  See section Session Key Regeneration in
+          [SILC1] for more information.  This packet does not have
+          a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     23   SILC_PACKET_REKEY_DONE
+
+          This packet is used to indicate that re-key is performed and
+          new keys must be used hereafter.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+     
+     24   SILC_PACKET_HEARTBEAT
+
+          This packet is used by clients, servers and routers to keep the
+          connection alive.  It is recommended that all servers implement
+          keepalive actions and perform it to both direction in a link.
+          This packet does not have a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     25   SILC_PACKET_KEY_AGREEMENT
+
+          This packet is used by clients to request key negotiation 
+          between another client in the SILC network.  If the negotiation
+          is started it is performed using the SKE protocol.  The result of
+          the negotiation, the secret key material, can be used for
+          example as private message key.  The server and router MUST NOT
+          send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.20 Key Agreement Payload
+
+
+     26   SILC_PACKET_RESUME_ROUTER
+
+          This packet is used during backup router protocol when the 
+          original primary router of the cell comes back online and wishes
+          to resume the position as being the primary router of the cell.
+
+          Payload of the packet:  See section 2.3.21 Resume Router Payload
+
+
+     27   SILC_PACKET_FTP
+
+          This packet is used to perform an file transfer protocol in the
+          SILC session with some entity in the network.  The packet is
+          multi purpose.  The packet is used to tell other entity in the
+          network that the sender wishes to perform an file transfer
+          protocol.  The packet is also used to actually tunnel the
+          file transfer protocol stream.  The file transfer protocol
+          stream is always protected with the SILC packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.22 File Transfer Payload
+
+
+     28 - 199
+
+          Currently undefined commands.
+
+
+     200 - 254
+
+          These packet types are reserved for private use and they will
+          not be defined by this document.
+
+
+
+
+     255  SILC_PACKET_MAX
+
+          This type is reserved for future extensions and currently it 
+          MUST NOT be sent.
+.in 3
+
+
+.ti 0
+2.3.1 SILC Packet Payloads
+
+All payloads resides in the main data area of the SILC packet.  However
+all payloads MUST be at the start of the data area after the SILC
+packet header and padding.  All fields in the packet payload are always
+encrypted, as they reside in the data area of the packet which is
+always encrypted.
+
+Payloads described in this section are common payloads that MUST be
+accepted anytime during SILC session.  Most of the payloads may only
+be sent with specific packet type which is defined in the description
+of the payload.
+
+There are a lot of other payloads in the SILC as well.  However, they
+are not common in the sense that they could be sent at any time. 
+These payloads are not described in this section.  These are payloads
+such as SILC Key Exchange payloads and so on.  These are described
+in [SILC1], [SILC3] and [SILC4].
+
+
+.ti 0
+2.3.2 Generic payloads
+
+This section describes generic payloads that are not associated to any
+specific packet type.  They can be used for example inside some other
+packet payloads.
+
+
+.ti 0
+2.3.2.1 ID Payload
+
+This payload can be used to send an ID.  ID's are variable in length
+thus this payload provides a way to send variable length ID's.
+
+The following diagram represents the ID Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             ID Type           |           ID Length           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           ID Data                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 3:  ID Payload
+
+
+.in 6
+o ID Type (2 bytes) - Indicates the type of the ID.  See 
+  section 2.4 SILC ID Types for list of defined ID types.
+
+o ID Length (2 bytes) - Length of the ID Data area not 
+  including the length of any other fields in the payload.
+
+o ID Data (variable length) - The actual ID data.
+.in 3
+
+
+.ti 0
+2.3.2.2 Argument Payload
+
+Argument Payload is used to set arguments for any packet payload that
+needs and supports arguments, such as commands.  Number of arguments
+associated with a packet MUST be indicated by the packet payload which
+needs the arguments.  Argument Payloads MUST always reside right after
+the packet payload needing the arguments.  Incorrect amount of argument
+payloads MUST cause rejection of the packet.
+
+
+
+
+
+
+
+The following diagram represents the Argument Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | Argument Type |               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               +
+|                                                               |
+~                        Argument Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 4:  Argument Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the argument payload data 
+  area not including the length of any other fields in the 
+  payload.
+
+o Argument Type (1 byte) - Indicates the type of the argument.  
+  Every argument may have a specific type that MUST be defined
+  by the packet payload needing the argument.  For example
+  every command specify a number for each argument that maybe 
+  associated with the command.  By using this number the receiver 
+  of the packet knows what type of argument this is.  If there is
+  no specific argument type this field is set to zero (0).
+
+o Argument Data (variable length) - Argument data.
+.in 3
+
+
+.ti 0
+2.3.2.3 Channel Payload
+
+Generic Channel Payload may be used to send information about channel,
+its name, the Channel ID and a mode.
+
+The following diagram represents the Channel Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Name Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                           Mode Mask                           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  New Channel Payload
+
+
+.in 6
+o Channel Name Length (2 bytes) - Length of the channel name
+  field.
+
+o Channel Name (variable length) - The name of the channel.
+
+o Channel ID Length (2 bytes) - Length of the Channel ID field.
+
+o Channel ID (variable length) - The Channel ID.
+
+o Mode Mask (4 bytes) - A mode.  This can be the mode of the
+  channel but it can also be the mode of the client on the
+  channel.  The contents of this field is dependent of the
+  usage of this payload.  The usage is defined separately
+  when this payload is used.  This is a 32 bit MSB first value.
+.in 3
+
+
+.ti 0
+2.3.2.4 Public Key Payload
+
+Generic Public Key Payload may be used to send different types of
+public keys and certificates.
+
+The following diagram represents the Public Key Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  Public Key Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  See the [SILC3] for defined public key types.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+.in 3
+
+
+.ti 0
+2.3.3 Disconnect Payload
+
+Disconnect payload is sent upon disconnection.  The payload is simple;
+reason of disconnection is sent to the disconnected party.
+
+The payload may only be sent with SILC_PACKET_DISCONNECT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the Disconnect Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Disconnect Message                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 7:  Disconnect Payload
+
+
+
+
+.in 6
+o Disconnect Message (variable length) - Human readable
+  reason of the disconnection.
+.in 3
+
+
+.ti 0
+2.3.4 Success Payload
+
+Success payload is sent when some protocol execution is successfully
+completed.  The payload is simple; indication of the success is sent.
+This may be any data, including binary or human readable data.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Success Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 8:  Success Payload
+
+
+.in 6
+o Success Indication (variable length) - Indication of
+  the success.  This may be for example some flag that
+  indicates the protocol and the success status or human
+  readable success message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+
+.ti 0
+2.3.5 Failure Payload
+
+This is opposite of Success Payload.  Indication of failure of
+some protocol is sent in the payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Failure Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 9:  Failure Payload
+
+
+.in 6
+o Failure Indication (variable length) - Indication of
+  the failure.  This may be for example some flag that
+  indicates the protocol and the failure status or human
+  readable failure message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.6 Reject Payload
+
+This payload is sent when some protocol is rejected to be executed.
+Other operations MAY send this as well that was rejected.  The
+indication of the rejection is sent in the payload.  The indication
+may be binary or human readable data.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Reject Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 10:  Reject Payload
+
+
+.in 6
+o Reject Indication (variable length) - Indication of
+  the rejection.  This maybe for example some flag that
+  indicates the protocol and the rejection status or human
+  readable rejection message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.7 Notify Payload
+
+Notify payload is used to send notify messages.  The payload is usually
+sent from server to client, however, server MAY send it to another
+server as well.  This payload MAY also be sent to a channel.  Client
+MUST NOT send this payload.  The receiver of this payload MAY ignore
+the contents of the payload, however, notify message SHOULD be audited.
+
+The payload may only be sent with SILC_PACKET_NOTIFY packet.  It MUST
+not be sent in any other packet type.  The following diagram represents
+the Notify Payload.
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|          Notify Type          |        Payload Length         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Argument Nums |
++-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 11:  Notify Payload
+
+
+.in 6
+o Notify Type (2 bytes) - Indicates the type of the notify
+  message.
+
+o Payload Length (2 bytes) - Length of the entire Notify Payload
+  including any associated Argument Payloads.
+
+o Argument Nums (2 bytes) - Indicates the number of Argument
+  Payloads associated to this payload.  Notify types may define
+  arguments to be send along the notify message.
+.in 3
+
+The following list of currently defined notify types.  The format for
+notify arguments is same as in SILC commands described in [SILC4]. 
+Also, all ID's sent in arguments are sent inside ID Payload.
+
+.in 6
+0     SILC_NOTIFY_TYPE_NONE
+
+      If no specific notify type apply for the notify message this type
+      MAY be used.
+
+      Max Arguments:  1
+          Arguments:  (1) <message>
+
+      The <message> is implementation specific free text string.
+      Receiver MAY ignore this message.
+
+
+1     SILC_NOTIFY_TYPE_INVITE
+
+      Sent when an client is invited to a channel.  This is also sent
+      when the invite list of the channel is changed.  This notify type
+      is sent between routers and if an client was invited, to the 
+      client as well.  In this case the packet is destined to the client.
+
+      Max Arguments:  5
+          Arguments:  (1) <Channel ID>          (2) <channel name>
+                      (3) [<sender Client ID>]  (4) [<adding client>]
+                      (5) [<removing client>]
+
+      The <Channel ID> is the channel.  The <channel name> is the name
+      of the channel and is provided because the client which receives 
+      this notify packet may not have a way to resolve the name of the
+      channel from the <Channel ID>.  The <sender Client ID> is the
+      Client ID which invited the client to the channel.  The <adding
+      client> and the <removing client> indicates the added or removed
+      client from the channel's invite list.  The format of the <adding
+      client> and the <removing client> is defined in the [SILC4] with
+      SILC_COMMAND_INVITE command.
+
+      The <adding client> and <removing client> MUST NOT be sent when
+      the packet is destined to a client.
+
+
+2     SILC_NOTIFY_TYPE_JOIN
+
+      Sent when client has joined to a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) [<Client ID>]       (2) <Channel ID>
+
+      The <Client ID> is the client that joined to the channel indicated
+      by the <Channel ID>.
+
+
+3     SILC_NOTIFY_TYPE_LEAVE
+
+      Sent when client has left a channel.  The server must distribute
+      this type only to the local clients on the channel and then send
+      it to its primary router.  The router or server receiving the
+      packet distributes this type to the local clients on the channel
+      and broadcast it to the network.
+
+      Max Arguments:  1
+          Arguments:  (1) <Client ID>
+
+      The <Client ID> is the client which left the channel.
+
+
+4     SILC_NOTIFY_TYPE_SIGNOFF
+
+      Sent when client signoff from SILC network.  The server MUST
+      distribute this type only to the local clients on the channel and
+      then send it to its primary router.  The router or server receiving
+      the packet distributes this type to the local clients on the
+      channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <message>
+
+      The <Client ID> is the client which left SILC network.  The
+      <message> is free text string indicating the reason of the signoff.
+
+
+5     SILC_NOTIFY_TYPE_TOPIC_SET
+
+      Sent when topic is set/changed on a channel.  This type must be
+      sent only to the clients which is joined on the channel which
+      topic was set or changed.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <topic>
+
+      The <Client ID> is the client which set or changed the <topic>.
+
+
+6     SILC_NOTIFY_TYPE_NICK_CHANGE
+
+      Sent when client changes nick on a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Client ID>  (2) <New Client ID>
+
+      The <Old Client ID> is the old ID of the client which changed
+      the nickname.  The <New Client ID> is the new ID generated by
+      the change of the nickname.
+
+
+7     SILC_NOTIFY_TYPE_CMODE_CHANGE
+
+      Sent when channel mode has changed.  This type MUST be sent only
+      to the clients which is joined on the channel which mode was
+      changed.
+
+      Max Arguments:  4
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) [<cipher>]    (4) <[hmac>]     
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing channel mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The client can safely
+      ignore the <cipher> argument since the SILC_PACKET_CHANNEL_KEY
+      packet will force the new channel key change anyway.  The <hmac>
+      argument is important since the client is responsible of setting
+      the new HMAC and the hmac key into use.
+
+
+8     SILC_NOTIFY_TYPE_CUMODE_CHANGE
+
+      Sent when user mode on channel has changed.  This type MUST be
+      sent only to the clients which is joined on the channel where
+      the target client is on.
+
+      Max Arguments:  3
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) <Target Client ID>
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing user's mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The <Target Client ID>
+      is the client which mode was changed.
+
+
+9     SILC_NOTIFY_TYPE_MOTD
+
+      Sent when Message of the Day (motd) is sent to a client.
+
+      Max Arguments:  1
+          Arguments:  (1) <motd>
+
+      The <motd> is the Message of the Day.
+
+
+10    SILC_NOTIFY_TYPE_CHANNEL_CHANGE
+
+      Sent when channel's ID has changed for a reason or another.
+      This is sent by normal server to the client.  This can also be
+      sent by router to other server to force the Channel ID change.
+      The Channel ID MUST be changed to use the new one.  When sent
+      to clients, this type MUST be sent only to the clients which is
+      joined on the channel.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Channel ID>  (2) <New Channel ID>
+
+      The <Old Channel ID> is the channel's old ID and the <New
+      Channel ID> is the new one that MUST replace the old one.
+
+
+11    SILC_NOTIFY_TYPE_SERVER_SIGNOFF
+
+      Sent when server quits SILC network.  Those clients from this
+      server that are on channels must be removed from the channel.
+
+      Max Arguments:  2000
+          Arguments:  (1) <Server ID>   (n) [<Client ID>]   [...]
+
+      The <Server ID> is the server's ID.  The rest of the arguments
+      are the Client ID's of the client's which are coming from this
+      server and are thus quitting the SILC network also.  If the
+      maximum number of arguments are reached another 
+      SILC_NOTIFY_TYPE_SERVER_SIGNOFF notify packet MUST be sent.
+      When this notify packet is sent between routers the Client ID's
+      MAY be omitted.  Server receiving the Client ID's in the payload
+      may use them directly to remove the client.
+
+
+12    SILC_NOTIFY_TYPE_KICKED
+
+      Sent when a client has been kicked from a channel.  This is
+      sent also to the client which was kicked from the channel.
+      The client which was kicked from the channel MUST be removed
+      from the channel.  This notify type is always destined to the
+      channel.  The router or server receiving the packet distributes
+      this type to the local clients on the channel and broadcast it
+      to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was kicked from the channel.
+      The kicker may have set the <comment> to indicate the reason for
+      the kicking.
+
+
+13    SILC_NOTIFY_TYPE_KILLED
+
+      Sent when a client has been killed from the network.  This is sent 
+      also to the client which was killed from the network.  The client
+      which was killed from the network MUST be removed from the network.
+      This notify type is destined directly to the client which was
+      killed and to channel if the client is on any channel.  The router
+      or server receiving the packet distributes this type to the local
+      clients on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was killed from the network.
+      The killer may have set the <comment> to indicate the reason for
+      the killing.
+
+
+14    SILC_NOTIFY_TYPE_UMODE_CHANGE
+
+      Sent when user's mode in the SILC changes.  This type is sent
+      only between routers as broadcast packet.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+
+      The <Client ID> is the client which mode was changed.  The
+      <mode mask> is the new mode mask.
+
+
+15    SILC_NOTIFY_TYPE_BAN
+
+      Sent when the ban list of the channel is changed.  This type is
+      sent only between routers as broadcast packet.
+
+      Max Arguments:  3
+          Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                      (3) [<removing client>]
+
+      The <Channel ID> is the channel which ban list was changed.  The
+      <adding client> is used to indicate that a ban was added and the
+      <removing client> is used to indicate that a ban was removed from
+      the ban list.  The format of the <adding client> and the 
+      <removing client> is defined in the [SILC4] with SILC_COMMAND_BAN
+      command.
+
+.in 3
+
+Notify types starting from 16384 are reserved for private notify
+message types.
+
+
+.ti 0
+2.3.8 Error Payload
+
+Error payload is sent upon error.  Error may occur in various
+conditions when server sends this packet.  Client MUST NOT send this
+payload but MUST be able to accept it.  However, client MAY
+totally ignore the contents of the packet as server is going to
+take action on the error anyway.  However, it is recommended
+that the client takes error packet seriously.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Error Message                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 12:  Error Payload
+
+
+.in 6
+o Error Message (variable length) - Human readable error
+  message.
+.in 3
+
+
+.ti 0
+2.3.9 Channel Message Payload
+
+Channel messages are the most common messages sent in the SILC.
+Channel Message Payload is used to send message to channels.  These
+messages can only be sent if client has joined to some channel.
+Even though this packet is the most common in SILC it is still
+special packet.  Some special handling on sending and reception
+of channel message is required.
+
+Padding MUST be applied into this payload since the payload is
+encrypted separately from other parts of the packet with the
+channel specific key.  Hence the requirement of the padding.  
+The padding SHOULD be random data.  The packet MUST be made
+multiple by eight (8) or by the block size of the cipher, which
+ever is larger.
+
+The SILC header in this packet is encrypted with the session key
+of the next receiver of the packet.  Nothing else is encrypted
+with that key.  Thus, the actual packet and padding to be
+encrypted with the session key is SILC Header plus padding to it
+to make it multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.
+
+Receiver of the the channel message packet is able to determine
+the channel the message is destined to by checking the destination
+ID from the SILC Packet header which tells the destination channel.
+The original sender of the packet is also determined by checking
+the source ID from the header which tells the client which sent
+the message.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_MESSAGE packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Message Payload.
+
+(*) indicates that the field is not encrypted.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |         Message Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Message Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Padding Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                            Padding                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                              MAC                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Initial Vector *                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 13:  Channel Message Payload
+
+
+.in 6
+o Flags (2 bytes) - Includes the flags of the channel
+  messages.  The flags can indicate a reason or purpose
+  for the channel message.  Note that the Private Message
+  Payload use these same flags for the same purpose.  The
+  following flags are defined:
+
+  0x0000  SILC_MESSAGE_FLAG_NONE
+
+          No specific flags set.
+
+  0x0001  SILC_MESSAGE_FLAG_AUTOREPLY
+
+          This message is an automatic reply to an earlier
+          received message.
+
+  0x0002  SILC_MESSAGE_FLAG_NOREPLY
+
+          There should not be reply messages to this
+          message.
+
+  0x0004  SILC_MESSAGE_FLAG_ACTION
+
+          The sender is performing an action and the message
+          is the indication of the action.
+
+  0x0008  SILC_MESSAGE_FLAG_NOTICE
+
+          The message is for example an informational notice
+          type message.
+
+  0x0010  SILC_MESSAGE_FLAG_REQUEST
+
+          This is a generic request flag to send request
+          messages.  A separate document should define any 
+          payloads associated to this flag.
+
+  0x0020  SILC_MESSAGE_FLAG_SIGNED
+
+          This flag indicates that the message is signed
+          with sender's private key and thus can be verified
+          by the receiver using the sender's public key.  A
+          separate document should define the detailed procedure
+          of the signing process and any associated payloads
+          of this flag.
+
+  0x0040 - 0x0200 RESERVED
+
+          Reserved for future flags
+
+  0x0400 - 0x8000 PRIVATE RANGE
+
+          Private range for free use.
+
+o Message Length (2 bytes) - Indicates the length of the
+  the Message Data field in the payload, not including any 
+  other field.
+
+o Message Data (variable length) - The actual message to
+  the channel.
+
+o Padding Length (2 bytes) - Indicates the length of the
+  Padding field in the payload, not including any other
+  field.
+
+o Padding (variable length) - The padding that MUST be
+  applied because this payload is encrypted separately from
+  other parts of the packet.
+
+o MAC (variable length) - The MAC computed from the
+  Message Length, Message Data, Padding Length and Padding
+  fields.  This protects the integrity of the plaintext
+  channel message.  The receiver can verify from the MAC
+  whether the message decrypted correctly.  Also, if more than
+  one private key has been set for the channel, the receiver
+  can verify which of the keys decrypted the message 
+  correctly.  Note that, this field is encrypted and MUST
+  be added to the padding calculation.
+
+o Initial Vector (variable length) - The initial vector
+  that has been used in packet encryption.  It needs to be
+  used in the packet decryption as well.  What this field
+  includes is implementation issue.  However, it is 
+  RECOMMENDED that it would be random data or, perhaps,
+  a timestamp.  It is NOT RECOMMENDED to use zero (0) as an
+  initial vector.  This field is not encrypted.  This field
+  is not included into the padding calculation.  Length
+  of this field equals the cipher's block size.  This field
+  is, however, authenticated.
+.in 3
+
+
+.ti 0
+2.3.10 Channel Key Payload
+
+All traffic in channels are protected by channel specific keys.
+Channel Key Payload is used to distribute channel keys to all
+clients on the particular channel.  Channel keys are sent when
+the channel is created, when new user joins to the channel and
+whenever a user has left a channel.  Server creates the new
+channel key and distributes it to the clients by encrypting this
+payload with the session key shared between the server and
+the client.  After that, client starts using the key received
+in this payload to protect the traffic on the channel.
+
+The client which is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity which sent the SILC_COMMAND_JOIN
+command.
+
+Channel keys are cell specific thus every router in the cell have
+to create a channel key and distribute it if any client in the
+cell has joined to a channel.  Channel traffic between cell's
+are not encrypted using channel keys, they are encrypted using
+normal session keys between two routers.  Inside a cell, all
+channel traffic is encrypted with the specified channel key.
+Channel key should expire periodically, say, in one hour, in
+which case new channel key is created and distributed.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_KEY packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Cipher Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Key Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Key                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 14:  Channel Key Payload
+
+
+
+.in 6
+o Channel ID Length (2 bytes) - Indicates the length of the
+  Channel ID field in the payload, not including any other
+  field.
+
+o Channel ID (variable length) - The Channel ID of the
+  channel this key is meant for.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher used
+  in the protection of channel traffic.  This name is
+  initially decided by the creator of the channel but it
+  MAY change during the life time of the channel as well.
+
+o Channel Key Length (2 bytes) - Indicates the length of the
+  Channel Key field in the payload, not including any other
+  field.
+
+o Channel Key (variable length) - The actual channel key
+  material.
+.in 3
+
+
+.ti 0
+2.3.11 Private Message Payload
+
+Private Message Payload is used to send private message between
+two clients (or users for that matter).  The messages are sent only
+to the specified user and no other user inside SILC network is
+able to see the message.  The message is protected by the session 
+key established by the SILC Key Exchange Protocol.  However,
+it is also possible to agree to use a private key to protect
+just the private messages.  See section 2.3.11 Private Message
+Key Payload for detailed description of how to agree to use
+specific key.
+
+If normal session key is used to protect the message, every server
+between the sender client and the receiving client MUST decrypt the
+packet and always re-encrypt it with the session key of the next
+receiver of the packet.  See section Client To Client in [SILC1].
+
+When private key is used to protect the message, servers between 
+the sender and the receiver needs not to decrypt/re-encrypt the 
+packet.  Section Client To Client in [SILC1] gives example of this
+scheme as well.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Payload.
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |      Message Data Length      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                          Message Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                             Padding                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 15:  Private Message Payload
+
+
+.in 6
+o Flags (2 bytes) - This field includes the flags of the
+  private message.  They can indicate a different reason or
+  purpose for the private message.  See the section 2.3.9
+  Channel Message Payload for defined flags.  Note that
+  the Channel Message Payload use the same flags for the
+  same purpose.
+
+o Message Data Length (2 bytes) - Indicates the length of the
+  Message Data field, not includes any other field.
+
+o Message Data (variable length) - The actual message to
+  the client.  Rest of the packet is reserved for the message
+  data.
+
+o Padding (variable length) - This field is present only
+  when the private message payload is encrypted with private
+  message key.  In this case the padding is applied to make
+  the payload multiple by eight (8), or by the block size of
+  the cipher, which ever is larger.  When encrypted with
+  normal session keys, this field MUST NOT be included.
+.in 3
+
+
+.ti 0
+2.3.12 Private Message Key Payload
+
+This payload is used to send key from client to another client that
+is going to be used to protect the private messages between these
+two clients.  If this payload is not sent normal session key 
+established by the SILC Key Exchange Protocol is used to protect
+the private messages.
+
+This payload may only be sent by client to another client.  Server
+MUST NOT send this payload at any time.  After sending this payload
+the sender of private messages must set the Private Message Key
+flag into SILC Packet Header.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE_KEY 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Private Message Key Length   |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Private Message Key                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Cipher Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 16:  Private Message Key Payload
+
+
+
+
+.in 6
+o Private Message Key Length (2 bytes) - Indicates the length 
+  of the Private Message Key field in the payload, not including 
+  any other field.
+
+o Private Message Key (variable length) - The actual private
+  message key material.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher Name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher to use
+  in the private message encryption.  If this field does not
+  exist then the default cipher of the SILC protocol is used.
+  See the [SILC1] for defined ciphers.
+.in 3
+
+
+
+.ti 0
+2.3.13 Command Payload
+
+Command Payload is used to send SILC commands from client to server.
+Also server MAY send commands to other servers.  The following diagram
+represents the Command Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | SILC Command  | Arguments Num |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Command Identifier      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 17:  Command Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire command 
+  payload including any command argument payloads associated 
+  with this payload.
+
+o SILC Command (1 byte) - Indicates the SILC command.  This MUST 
+  be set to non-zero value.  If zero (0) value is found in this 
+  field the packet MUST be discarded.
+
+o Arguments Num (1 byte) - Indicates the number of arguments
+  associated with the command.  If there are no arguments this
+  field is set to zero (0).  The arguments MUST follow the 
+  command payload.  See section 2.3.2.2 for definition of the
+  Argument Payload.
+
+o Command Identifier (2 bytes) - Identifies this command at the
+  sender's end.  The entity which replies to this command MUST
+  set the value found from this field into the Command Payload
+  used to send the reply to the sender.  This way the sender
+  can identify which command reply belongs to which originally
+  sent command.  What this field includes is implementation
+  issue but it is RECOMMENDED that wrapping counter value is
+  used in the field.  Value zero (0) in this field means that
+  no specific value is set.
+.in 3
+
+See [SILC4] for detailed description of different SILC commands,
+their arguments and their reply messages.
+
+
+
+
+.ti 0
+2.3.14 Command Reply Payload
+
+Command Reply Payload is used to send replies to the commands.  The
+Command Reply Payload is identical to the Command Payload thus see
+the upper section for the Command Payload specification.
+
+The entity which sends the reply packet MUST set the Command Identifier
+field in the reply packet's Command Payload to the value it received
+in the original command packet.
+
+See SILC Commands in [SILC4] for detailed description of different
+SILC commands, their arguments and their reply messages.
+
+
+.ti 0
+2.3.15 Connection Auth Request Payload
+
+Client MAY send this payload to server to request the authentication
+method that must be used in authentication protocol.  If client knows 
+this information beforehand this payload is not necessary to be sent.
+Server performing authentication with another server MAY also send
+this payload to request the authentication method.  If the connecting
+server already knows this information this payload is not necessary
+to be sent.
+
+Server receiving this request MUST reply with same payload sending
+the mandatory authentication method.  Algorithms that may be required
+to be used by the authentication method are the ones already 
+established by the SILC Key Exchange protocol.  See section Key
+Exchange Start Payload in [SILC3] for detailed information.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Connection Auth Request Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Connection Type        |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 18:  Connection Auth Request Payload
+
+
+.in 6
+o Connection Type (2 bytes) - Indicates the type of the
+  connection.  The following connection types are defined:
+
+
+     1    Client connection
+     2    Server connection
+     3    Router connection
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.
+
+o Authentication Method (2 bytes) - Indicates the authentication
+  method to be used in the authentication protocol.  The following
+  authentication methods are defined:
+
+     0    NONE        (mandatory)
+     1    password    (mandatory)
+     2    public key  (mandatory)
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.  If this
+  payload is sent as request to receive the mandatory 
+  authentication method this field MUST be set to zero (0),
+  indicating that receiver should send the mandatory 
+  authentication method.  The receiver sending this payload
+  to the requesting party, MAY also set this field to zero (0) 
+  to indicate that authentication is not required.  In this
+  case authentication protocol still MUST be started but
+  server is most likely to respond with SILC_PACKET_SUCCESS
+  immediately.
+.in 3
+
+
+.ti 0
+2.3.16 New ID Payload
+
+New ID Payload is a multipurpose payload.  It is used to send newly 
+created ID's from clients and servers.  When client connects to server
+and registers itself to the server by sending SILC_PACKET_NEW_CLIENT
+packet, server replies with this packet by sending the created ID for
+the client.  Server always creates the ID for the client.
+
+This payload is also used when server tells its router that new client
+has registered to the SILC network.  In this case the server sends
+the Client ID of the client to the router.  Similarly when router
+distributes information to other routers about the client in the SILC
+network this payload is used.  
+
+Also, when server connects to router, router uses this payload to inform
+other routers about new server in the SILC network.  However, every 
+server (or router) creates their own ID's thus the ID distributed by 
+this payload is not created by the distributor in this case.  Servers
+create their own ID's.  Server registers itself to the network by
+sending SILC_PACKET_NEW_SERVER to the router it connected to.  The case
+is same when router connects to another router.
+
+However, this payload MUST NOT be used to send information about new
+channels.  New channels are always distributed by sending the dedicated
+SILC_PACKET_NEW_CHANNEL packet.
+
+Thus, this payload is very important and used every time when some
+new entity is registered to the SILC network.  Client MUST NOT send this
+payload.  Both client and server (and router) MAY receive this payload.
+
+The packet uses generic ID Payload as New ID Payload.  See section
+2.3.2.1 for generic ID Payload.
+
+
+.ti 0
+2.3.17 New Client Payload
+
+When client is connected to the server, keys has been exchanged and
+connection has been authenticated client MUST register itself to the 
+server.  Client's first packet after key exchange and authentication 
+protocols must be SILC_PACKET_NEW_CLIENT.  This payload tells server all
+the relevant information about the connected user.  Server creates a new
+client ID for the client when received this payload and sends it to the
+client in New ID Payload.
+
+This payload sends username and real name of the user on the remote host
+which is connected to the SILC server with SILC client.  The server 
+creates the client ID according the information sent in this payload.
+The nickname of the user becomes the username sent in this payload.
+However, client should call NICK command after sending this payload to
+set the real nickname of the user which is then used to create new 
+client ID.
+
+The payload may only be sent with SILC_PACKET_NEW_CLIENT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Client Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Username Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Username                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Real Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Real Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 19:  New Client Payload
+
+
+.in 6
+o Username Length (2 bytes) - Length of the Username field.
+
+o Username (variable length) - The username of the user on
+  the host where connecting to the SILC server.
+
+o Real Name Length (2 bytes) - Length of the Real Name field.
+
+o Real Name (variable length) - The real name of the user
+  on the host where connecting to the SILC server.
+.in 3
+
+
+.ti 0
+2.3.18 New Server Payload
+
+This payload is sent by server when it has completed successfully both
+key exchange and connection authentication protocols.  The server
+MUST register itself to the SILC Network by sending this payload.
+The first packet after these key exchange and authentication protocols
+is SILC_PACKET_NEW_SERVER packet.  The payload includes the Server ID
+of the server that it has created by itself.  It also includes a
+name of the server that is associated to the Server ID.
+
+The payload may only be sent with SILC_PACKET_NEW_SERVER packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Server Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Server ID Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Server ID Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Server Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Server Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 20:  New Server Payload
+
+
+.in 6
+o Server ID Length (2 bytes) - Length of the Server ID Data
+  field.
+
+o Server ID Data (variable length) - The actual Server ID
+  data.
+
+o Server Name Length (2 bytes) - Length of the server name
+  field.
+
+o Server Name (variable length) - The server name.
+.in 3
+
+
+.ti 0
+2.3.19 New Channel Payload
+
+Information about newly created channel is broadcasted to all routers
+in the SILC network by sending this packet payload.  Channels are
+created by router of the cell.  Server never creates channels unless
+it is a standalone server and it does not have router connection,
+in this case server acts as router.  Normal server send JOIN command
+to the router (after it has received JOIN command from client) which
+then processes the command and creates the channel.  Client MUST NOT
+send this packet.  Server may send this packet to a router when it is
+announcing its existing channels to the router after it has connected
+to the router.
+
+The packet uses generic Channel Payload as New Channel Payload.  See
+section 2.3.2.3 for generic Channel Payload.  The Mode Mask field in the
+Channel Payload is the mode of the channel.
+
+
+.ti 0
+2.3.20 Key Agreement Payload
+
+This payload is used by clients to request key negotiation between
+another client in the SILC Network.  The key agreement protocol used
+is the SKE protocol.  The result of the protocol, the secret key
+material, can be used for example as private message key between the
+two clients.  This significantly adds security as the key agreement
+is performed outside the SILC network.  The server and router MUST NOT
+send this payload.
+
+The sender MAY tell the receiver of this payload the hostname and the
+port where the SKE protocol is running in the sender's end.  The 
+receiver MAY then initiate the SKE negotiation with the sender.  The
+sender MAY also optionally not to include the hostname and the port
+of its SKE protocol.  In this case the receiver MAY reply to the
+request by sending the same payload filled with the receiver's hostname
+and the port where the SKE protocol is running.  The sender MAY then
+initiate the SKE negotiation with the receiver.
+
+This payload may be sent with SILC_PACKET_KEY_AGREEMENT and 
+SILC_PACKET_FTP packet types.  It MUST NOT be sent in any other packet
+types.  The following diagram represents the Key Agreement Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 21:  Key Agreement Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the
+  Hostname field.
+
+o Hostname (variable length) - The hostname or IP address where
+  the SKE protocol is running.  The sender MAY fill this field
+  when sending the payload.  If the receiver sends this payload
+  as reply to the request it MUST fill this field.
+
+o Port (4 bytes) - The port where the SKE protocol is bound.
+  The sender MAY fill this field when sending the payload.  If
+  the receiver sends this payload as reply to the request it 
+  MUST fill this field.  This is a 32 bit MSB first order value.
+.in 3
+
+
+After the key material has been received from the SKE protocol it is
+processed as the [SILC3] describes.  If the key material is used as
+channel private key then the Sending Encryption Key, as defined in
+[SILC3] is used as the channel private key.  Other key material must
+be discarded.  The [SILC1] defines the way to use the key material if
+it is intended to be used as private message keys.  Any other use for
+the key material is undefined.
+
+
+.ti 0
+2.3.21 Resume Router Payload
+
+The payload may only be sent with SILC_PACKET_RESUME_ROUTER packet.  It
+MUST NOT be sent in any other packet type.  The Following diagram
+represents the Resume Router Payload.
+
+         
+.in 5
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Type     |  Session ID   |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 22:  Resume Router Payload
+
+
+.in 6
+o Type (1 byte) - Indicates the type of the backup resume
+  protocol packet.  The type values are defined in [SILC1].
+
+o Session ID (1 bytes) - Indicates the session ID for the
+  backup resume protocol.  The sender of the packet sets this
+  value and the receiver MUST set the same value in subsequent
+  reply packet.
+.in 3
+
+
+.ti 0
+2.3.22 File Transfer Payload
+
+File Transfer Payload is used to perform file transfer protocol
+between two entities in the network.  The actual file transfer
+protocol is always encapsulated inside the SILC Packet.  The actual
+data stream is also sent peer to peer outside SILC network.
+
+When an entity, usually a client wishes to perform file transfer
+protocol with another client in the network, they perform Key Agreement
+protocol as described in the section 2.3.20 Key Agreement Payload and
+in [SILC3], inside File Transfer Payload.  After the Key Agreement
+protocol has been performed the subsequent packets in the data stream
+will be protected using the new key material.  The actual file transfer
+protocol is also initialized in this stage.  All file transfer protocol
+packets are always encapsulated in the File Transfer Payload and
+protected with the negotiated key material.
+
+The payload may only be sent with SILC_PACKET_FTP packet.  It MUST NOT
+be sent in any other packet type.  The following diagram represents the
+File Transfer Payload
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Type      |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                             Data                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 23:  File Transfer Payload
+
+
+.in 6
+o Type (1 byte) - Indicates the type of the file transfer
+  protocol.  The following file transfer protocols has been
+  defined:
+
+    1    SSH File Transfer Protocol (SFTP) (mandatory)
+
+  If zero (0) value or any unsupported file transfer protocol
+  type is found in this field the packet must be discarded.
+  The currently mandatory file transfer protocol is SFTP.
+  The SFTP protocol is defined in [SFTP].
+
+o Data (variable length) - Arbitrary file transfer data.  The
+  contents and encoding of this field is dependent of the usage
+  of this payload and the type of the file transfer protocol.
+  When this payload is used to perform the Key Agreement 
+  protocol, this field include the Key Agreement Payload,
+  as defined in the section 2.3.20 Key Agreement Payload.
+  When this payload is used to send the actual file transfer
+  protocol data, the encoding is defined in the corresponding
+  file transfer protocol.
+.in 3
+
+
+
+
+.ti 0
+2.4 SILC ID Types
+
+ID's are extensively used in the SILC network to associate different
+entities.  The following ID's has been defined to be used in the SILC
+network.
+
+.in 6
+0    No ID
+
+     When ever specific ID cannot be used this is used.
+
+1    Server ID
+
+     Server ID to associate servers.  See the format of
+     this ID in [SILC1].
+
+2    Client ID
+
+     Client ID to associate clients.  See the format of
+     this ID in [SILC1].
+
+3    Channel ID
+
+     Channel ID to associate channels.  See the format of
+     this ID in [SILC1].
+.in 3
+
+When encoding different IDs into the ID Payload, all fields are always
+in MSB first order.  The IP address, port, and/or the random number
+are encoded in the MSB first order.
+
+
+.ti 0
+2.5 Packet Encryption And Decryption
+
+SILC packets are encrypted almost entirely.  Only small part of SILC
+header is not encrypted as described in section 5.2 SILC Packet Header.
+The SILC Packet header is the first part of a packet to be encrypted
+and it is always encrypted with the key of the next receiver of the
+packet.  The data payload area of the packet is always entirely 
+encrypted and it is usually encrypted with the next receiver's key.
+However, there are some special packet types and packet payloads
+that require special encryption process.  These special cases are
+described in the next sections.  First is described the normal packet
+encryption process.
+
+
+.ti 0
+2.5.1 Normal Packet Encryption And Decryption
+
+Normal SILC packets are encrypted with the session key of the next
+receiver of the packet.  The entire SILC Packet header and the packet
+data payload is is also encrypted with the same key.  Padding of the
+packet is also encrypted always with the session key, also in special
+cases.  Computed MAC of the packet must not be encrypted.
+
+Decryption process in these cases are straightforward.  The receiver
+of the packet MUST first decrypt the SILC Packet header, or some parts
+of it, usually first 16 bytes of it.  Then the receiver checks the
+packet type from the decrypted part of the header and can determine
+how the rest of the packet must be decrypted.  If the packet type is
+any of the special cases described in the following sections the packet
+decryption is special.  If the packet type is not among those special
+packet types rest of the packet can be decrypted with the same key.
+
+With out a doubt, this sort of decryption processing causes some
+overhead to packet decryption, but never the less, is required.
+
+
+.ti 0
+2.5.2 Channel Message Encryption And Decryption
+
+Channel Messages (Channel Message Payload) are always encrypted with
+the channel specific key.  However, the SILC Packet header is not 
+encrypted with that key.  As in normal case, the header is encrypted
+with the key of the next receiver of the packet, who ever that might
+be.  Note that in this case the encrypted data area is not touched
+at all; it MUST NOT be re-encrypted with the session key.
+
+Receiver of a channel message, who ever that is, is REQUIRED to decrypt
+the SILC Packet header to be able to even recognize the packet to be as
+channel message.  This is same procedure as for normal SILC packets.
+As the receiver founds the packet to be channel message, rest of the
+packet processing is special.  Rest of the SILC Packet header is
+decrypted with the same session key along with the padding of the
+packet.  After that the packet is protected with the channel specific
+key and thus can be decrypted only if the receiver is the client on
+the channel.  See section 2.7 Packet Padding Generation for more
+information about padding on special packets.
+
+If the receiver of the channel message is router which is routing the
+message to another router then it MUST decrypt the Channel Message
+payload.  Between routers (that is, between cells) channel messages
+are protected with session keys shared between the routers.  This
+causes another special packet processing for channel messages.  If
+the channel message is received from another router then the entire
+packet, including Channel Message payload, MUST be encrypted with the
+session key shared between the routers.  In this case the packet
+decryption process is as with normal SILC packets.  Hence, if the
+router is sending channel message to another router the Channel
+Message payload MUST have been decrypted and MUST be re-encrypted
+with the session key shared between the another router.  In this
+case the packet encryption is as with any normal SILC packet.
+
+It must be noted that this is only when the channel messages are sent
+from router to another router.  In all other cases the channel
+message encryption and decryption is as described above.  This
+different processing of channel messages with router to router
+connection is because channel keys are cell specific.  All cells have
+their own channel keys thus the channel message traveling from one
+cell to another MUST be protected as it would be any normal SILC
+packet.
+
+If the SILC_CMODE_PRIVKEY channel mode has been set for the channel
+then the router cannot decrypt the packet as it does not know the
+private key.  In this case the entire packet MUST be encrypted with
+the session key and sent to the router.  The router receiving the
+packet MUST check the channel mode and decrypt the packet accordingly.
+
+
+.ti 0
+2.5.3 Private Message Encryption And Decryption
+
+By default, private message in SILC are protected by session keys.
+In this case the private message encryption and decryption process is
+equivalent to normal packet encryption and decryption.
+
+However, private messages MAY be protected with private message key
+which causes the packet to be special packet.  The procedure in this
+case is very much alike to channel packets.  The actual private message
+is encrypted with the private message key and other parts of the
+packet is encrypted with the session key.  See 2.7 Packet Padding
+Generation for more information about padding on special packets.
+
+The difference from channel message processing is that server or router
+en route never decrypts the actual private message, as it does not
+have the key to do that.  Thus, when sending packets between router
+the processing is same as in any other case as well; the packet's header
+and padding is protected by the session key and the data area is not
+touched.
+
+The true receiver of the private message, client, that is, is able
+to decrypt the private message as it shares the key with the sender
+of the message.
+
+
+.ti 0
+2.6 Packet MAC Generation
+
+Data integrity of a packet is protected by including a message
+authentication code (MAC) at the end of the packet.  The MAC is computed
+from shared secret MAC key, that is established by the SILC Key Exchange
+protocol, from packet sequence number, and from the original contents
+of the packet.  The MAC is always computed before the packet is
+encrypted, although after it is compressed if compression is used.
+
+The MAC is computed from entire packet.  Every bit of data in the packet,
+including SILC Packet Header is used in the MAC computing.  This way
+the entire packet becomes authenticated.
+
+If the packet is special packet MAC is computed from the entire packet
+but part of the packet may be encrypted before the MAC is computed.
+This is case, for example, with channel messages where the message data
+is encrypted with key that server may not now.  In this case the MAC
+has been computed from the encrypted data.
+
+Hence, packet's MAC generation is as follows:
+
+  mac = MAC(key, sequence number | SILC packet)
+
+The MAC key is negotiated during the SKE protocol.  The sequence number
+is a 32 bit MSB first value starting from zero for first packet and
+increasing for subsequent packets, finally wrapping after 2^32 packets.
+The value is never reset, not even after rekey has been performed.  Note
+that the sequence number is incremented only when MAC is computed for a
+packet.  If packet is not encrypted and MAC is not computed then the
+sequence number is not incremented.  Hence, the sequence number is zero
+for first encrypted packet.
+
+See [SILC1] for defined and allowed MAC algorithms.
+
+
+.ti 0
+2.7 Packet Padding Generation
+
+Padding is needed in the packet because the packet is encrypted.  It
+MUST always be multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.  The padding is always encrypted.
+
+For normal packets the padding is added after the SILC Packet Header
+and between the Data Payload area.  The padding for normal packets
+may be calculated as follows:
+
+.in 6
+padding length = 16 - (packet_length mod block_size)
+.in 3
+
+The `block_size' is the block size of the cipher.  The maximum padding
+length is 128 bytes, and minimum is 1 byte.  The above algorithm calculates
+the padding to the next block size, and always returns the padding
+length between 1 - 16 bytes.  However, implementations may add padding
+up to 128 bytes.  For example packets that include a passphrase or a
+password for authentication purposes SHOULD pad the packet up to the
+maximum padding length.
+
+For special packets the padding calculation is different as special
+packets may be encrypted differently.  In these cases the encrypted
+data area MUST already be multiple by the block size thus in this case
+the padding is calculated only for SILC Packet Header, not for any
+other area of the packet.  The same algorithm works in this case as
+well, except that the `packet length' is now the SILC Packet Header
+length. 
+
+The padding MUST be random data, preferably, generated by 
+cryptographically strong random number generator.
+
+
+.ti 0
+2.8 Packet Compression
+
+SILC Packets MAY be compressed.  In this case the data payload area
+is compressed and all other areas of the packet MUST remain as they
+are.  After compression is performed for the data area, the length
+field of Packet Header MUST be set to the compressed length of the
+data.
+
+The compression MUST always be applied before encryption.  When
+the packet is received and decrypted the data area MUST be decompressed.
+Note that the true sender of the packet MUST apply the compression and
+the true receiver of the packet MUST apply the decompression.  Any
+server or router en route MUST NOT decompress the packet.
+
+
+.ti 0
+2.9 Packet Sending
+
+The sender of the packet MUST assemble the SILC Packet Header with
+correct values.  It MUST set the Source ID of the header as its own
+ID, unless it is forwarding the packet.  It MUST also set the Destination
+ID of the header to the true destination.  If the destination is client
+it will be Client ID, if it is server it will be Server ID and if it is
+channel it will be Channel ID.
+
+If the sender wants to compress the packet it MUST apply the
+compression now.  Sender MUST also compute the padding as described
+in above sections.  Then sender MUST compute the MAC of the packet.
+
+Then sender MUST encrypt the packet as has been described in above
+sections according whether the packet is normal packet or special
+packet.  The computed MAC MUST NOT be encrypted.
+
+
+.ti 0
+2.10 Packet Reception
+
+On packet reception the receiver MUST check that all fields in the
+SILC Packet Header are valid.  It MUST check the flags of the
+header and act accordingly.  It MUST also check the MAC of the packet
+and if it is to be failed the packet MUST be discarded.  Also if the
+header of the packet includes any bad fields the packet MUST be
+discarded.
+
+See above sections on the decryption process of the received packet.
+The receiver MUST also check that the ID's in the header are valid
+ID's.  Unsupported ID types or malformed ID's MUST cause packet
+rejection.  The padding on the reception is always ignored.
+
+The receiver MUST also check the packet type and start parsing the
+packet according to the type.  However, note the above sections on
+special packet types and their parsing.
+
+
+.ti 0
+2.11 Packet Routing
+
+Routers are the primary entities in the SILC network that takes care
+of packet routing.  However, normal servers routes packets as well, for
+example, when they are routing channel message to the local clients.
+Routing is quite simple as every packet tells the true origin and the
+true destination of the packet.
+
+It is still RECOMMENDED for routers that has several routing connections
+to create route cache for those destinations that has faster route than
+the router's primary route.  This information is available for the router
+when other router connects to the router.  The connecting party then
+sends all of its locally connected clients, servers and channels.  These
+informations helps to create the route cache.  Also, when new channels
+are created to a cell its information is broadcasted to all routers
+in the network.  Channel ID's are based on router's ID thus it is easy
+to create route cache based on these informations.  If faster route for
+destination does not exist in router's route cache the packet MUST be
+routed to the primary route (default route).
+
+However, there are some issues when routing channel messages to group
+of users.  Routers are responsible of routing the channel message to
+other routers, local servers and local clients as well.  Routers MUST
+send the channel message to only one router in the network, preferrably
+to the shortest route to reach the channel users.  The message can be
+routed into either upstream or downstream.  After the message is sent
+to a router in the network it MUST NOT be sent to any other router in
+either same route or other route.  The message MUST NOT be routed to
+the router it came from.
+
+When routing for example private messages they should be routed to the
+shortest route always to reach the destination client as fast as possible.
+
+For server which receives a packet to be routed to its locally connected
+client the server MUST check whether the particular packet type is
+allowed to be routed to the client.  Not all packets may be sent by
+some odd entity to client that is indirectly connected to the sender.
+See section 2.3 SILC Packet Types and paragraph about indirectly connected
+entities and sending packets to them.  The section mentions the packets
+that may be sent to indirectly connected entities.  It is clear that
+server cannot send, for example, disconnect packet to client that is not
+directly connected to the server.
+
+Routers form a ring in the SILC network.  However, routers may have other
+direct connections to other routers in the network too.  This can cause
+interesting routing problems in the network.  Since the network is a ring,
+the packets usually should be routed into counter clock-wise direction,
+or if it cannot be used then always clock-wise (primary route) direction.
+Problems may arise when a faster direct route exists and router is routing
+a channel message.  Currently channel messages must be routed either
+in upstream or downstream, they cannot be routed to other direct routes.
+The SILC protocol should have a shortest path discovery protocol, and some
+existing routing protocol, that can handle a ring network with other
+direct routes inside the ring (so called hybrid ring-mesh topology),
+MAY be defined to be used with the SILC protocol.  Additional
+specifications MAY be written on the subject to permeate this 
+specification.
+
+
+.ti 0
+2.12 Packet Broadcasting
+
+SILC packets MAY be broadcasted in SILC network.  However, only router
+server may send or receive broadcast packets.  Client and normal server
+MUST NOT send broadcast packets and they MUST ignore broadcast packets
+if they receive them.  Broadcast packets are sent by setting Broadcast
+flag to the SILC packet header.
+
+Broadcasting packets means that the packet is sent to all routers in
+the SILC network, except to the router that sent the packet.  The router
+receiving broadcast packet MUST send the packet to its primary route.
+The fact that SILC routers may have several router connections can
+cause problems, such as race conditions inside the SILC network, if
+care is not taken when broadcasting packets.  Router MUST NOT send
+the broadcast packet to any other route except to its primary route.
+
+If the primary route of the router is the original sender of the packet
+the packet MUST NOT be sent to the primary route.  This may happen
+if router has several router connections and some other router uses
+the router as its primary route.
+
+Routers use broadcast packets to broadcast for example information
+about newly registered clients, servers, channels etc. so that all the
+routers may keep these informations up to date.
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+[SFTP]       Ylonen T., and Lehtinen S., "Secure Shell File Transfer
+             Protocol", Internet Draft, March 2001.
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 13 May 2002
diff --git a/doc/draft-riikonen-silc-pp-05.nroff b/doc/draft-riikonen-silc-pp-05.nroff
new file mode 100644 (file)
index 0000000..c8a5a90
--- /dev/null
@@ -0,0 +1,2819 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH XXX
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-pp-05.txt                           XXX
+Expires: XXX
+
+.in 3
+
+.ce 2
+SILC Packet Protocol
+<draft-riikonen-silc-pp-05.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Packet Protocol used in the Secure Internet Live
+Conferencing (SILC) protocol, specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Packet Protocol ..........................................  4
+  2.1 SILC Packet ...............................................  4
+  2.2 SILC Packet Header ........................................  5
+  2.3 SILC Packet Types .........................................  7
+      2.3.1 SILC Packet Payloads ................................ 16
+      2.3.2 Generic payloads .................................... 16
+            2.3.2.1 ID Payload .................................. 17
+            2.3.2.2 Argument Payload ............................ 18
+            2.3.2.3 Channel Payload ............................. 18
+            2.3.2.4 Public Key Payload .......................... 19
+      2.3.3 Disconnect Payload .................................. 20
+      2.3.4 Success Payload ..................................... 21
+      2.3.5 Failure Payload ..................................... 21
+      2.3.6 Reject Payload ...................................... 22
+      2.3.7 Notify Payload ...................................... 22
+      2.3.8 Error Payload ....................................... 28
+      2.3.9 Channel Message Payload ............................. 29
+      2.3.10 Channel Key Payload ................................ 32
+      2.3.11 Private Message Payload ............................ 34
+      2.3.12 Private Message Key Payload ........................ 35
+      2.3.13 Command Payload .................................... 37
+      2.3.14 Command Reply Payload .............................. 38
+      2.3.15 Connection Auth Request Payload .................... 38
+      2.3.16 New ID Payload ..................................... 39
+      2.3.17 New Client Payload ................................. 40
+      2.3.18 New Server Payload ................................. 41
+      2.3.19 New Channel Payload ................................ 42
+      2.3.20 Key Agreement Payload .............................. 43
+      2.3.21 Resume Router Payload .............................. 44
+      2.3.22 File Transfer Payload .............................. 44
+  2.4 SILC ID Types ............................................. 46
+  2.5 Packet Encryption And Decryption .......................... 46
+      2.5.1 Normal Packet Encryption And Decryption ............. 46
+      2.5.2 Channel Message Encryption And Decryption ........... 47
+      2.5.3 Private Message Encryption And Decryption ........... 48
+  2.6 Packet MAC Generation ..................................... 48
+  2.7 Packet Padding Generation ................................. 49
+  2.8 Packet Compression ........................................ 50
+  2.9 Packet Sending ............................................ 50
+  2.10 Packet Reception ......................................... 51
+  2.11 Packet Routing ........................................... 51
+  2.12 Packet Broadcasting ...................................... 52
+3 Security Considerations ....................................... 53
+4 References .................................................... 53
+5 Author's Address .............................................. 54
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:   Typical SILC Packet
+Figure 2:   SILC Packet Header
+Figure 3:   ID Payload
+Figure 4:   Argument Payload
+Figure 5:   Channel Payload
+Figure 6:   Public Key Payload
+Figure 7:   Disconnect Payload
+Figure 8:   Success Payload
+Figure 9:   Failure Payload
+Figure 10:  Reject Payload
+Figure 11:  Notify Payload
+Figure 12:  Error Payload
+Figure 13:  Channel Message Payload
+Figure 14:  Channel Key Payload
+Figure 15:  Private Message Payload
+Figure 16:  Private Message Key Payload
+Figure 17:  Command Payload
+Figure 18:  Connection Auth Request Payload
+Figure 19:  New Client Payload
+Figure 20:  New Server Payload
+Figure 21:  Key Agreement Payload
+Figure 22:  Resume Router Payload
+Figure 23:  File Transfer Payload
+
+
+.ti 0
+1. Introduction
+
+This document describes a Packet Protocol used in the Secure Internet
+Live Conferencing (SILC) protocol specified in the Secure Internet Live
+Conferencing, Protocol Specification Internet Draft [SILC1].  This
+protocol describes the packet types and packet payloads which defines
+the contents of the packets.  The protocol provides secure binary packet
+protocol that assures that the contents of the packets are secured and
+authenticated.
+
+The basis of SILC protocol relies in the SILC packets and it is with
+out a doubt the most important part of the protocol.  It is also probably
+the most complicated part of the protocol.  Packets are used all the
+time in the SILC network to send messages, commands and other information.
+All packets in SILC network are always encrypted and their integrity
+is assured by computed MACs.  The protocol defines several packet types
+and packet payloads.  Each packet type usually has a specific packet
+payload that actually defines the contents of the packet.  Each packet
+also includes a default SILC Packet Header that provides sufficient
+information about the origin of the packet and destination of the
+packet.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2 SILC Packet Protocol
+
+.ti 0
+2.1 SILC Packet
+
+SILC packets deliver messages from sender to receiver securely by
+encrypting important fields of the packet.  The packet consists of
+default SILC Packet Header, Padding, Packet Payload data, and, packet 
+MAC.
+
+The following diagram illustrates typical SILC packet.
+
+
+.in 5
+.nf
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+|   n bytes   | 1 - n bytes |      n bytes       |  n bytes       
+| SILC Header |   Padding   |    Data Payload    |    MAC    
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+.in 3
+
+.ce
+Figure 1:  Typical SILC Packet
+
+
+SILC Header is always the first part of the packet and its purpose
+is to provide information about the packet.  It provides for example
+the packet type, origin of the packet and the destination of the packet.
+The header is variable in length.  See the following section for
+description of SILC Packet header.  Packets without SILC header or
+with malformed SILC header MUST be dropped.
+
+Padding follows the packet header.  The purpose of the padding is to
+make the packet multiple by eight (8) or by the block size of the
+cipher used in the encryption, which ever is larger.  The maximum
+length of padding is currently 128 bytes.  The padding is always
+encrypted.  The padding is applied always, even if the packet is
+not encrypted.  See the section 2.7 Padding Generation for more
+detailed information.
+
+Data payload area follows padding and it is the actual data of the
+packet.  The packet data is the packet payloads defined in this
+protocol.  The data payload area is always encrypted.
+
+The last part of SILC packet is the packet MAC that assures the
+integrity of the packet.  The MAC is always computed from the packet
+before the encryption is applied to the packet.  If compression is used
+in the packet the MAC is computed after the compression has been
+applied.  The compression, on the other hand, is always applied before
+encryption.  See more details in the section 2.6 Packet MAC Generation.
+
+All fields in all packet payloads are always in MSB (most significant
+byte first) order.
+
+
+.ti 0
+2.2 SILC Packet Header
+
+The SILC packet header is applied to all SILC packets and it is
+variable in length.  The purpose of SILC Packet header is to provide
+detailed information about the packet.  The receiver of the packet
+uses the packet header to parse the packet and gain other relevant
+parameters of the packet.
+
+The following diagram represents the SILC packet header.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        |     Flags     |  Packet Type  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Pad Length  |    RESERVED   | Source ID Len |  Dest ID Len  |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Src ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                           Source ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Dst ID Type  |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                         Destination ID                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 2:  SILC Packet Header
+
+
+.in 6
+o Payload Length (2 bytes) - Is the length of the packet
+  not including the padding of the packet.
+
+o Flags (1 byte) - Indicates flags to be used in packet
+  processing.  Several flags may be set by ORing the flags
+  together.
+
+  The following flags are reserved for this field:
+
+
+     No flags                  0x00
+
+       In this case the field is ignored.
+
+
+     Private Message Key       0x01
+
+       Indicates that the packet must include private
+       message that is encrypted using private key set by
+       client.  Servers does not know anything about this
+       key and this causes that the private message is
+       not handled by the server at all, it is just
+       passed along.  See section 2.5.3 Private Message
+       Encryption And Decryption for more information.
+
+
+     List                      0x02
+  
+       Indicates that the packet consists of list of
+       packet payloads indicated by the Packet Type field.
+       The payloads are added one after the other.  Note that
+       there are packet types that must not be used as
+       list.  Parsing of list packet is done by calculating
+       the length of each payload and parsing them one by
+       one.
+
+
+     Broadcast                 0x04
+
+       Marks the packet to be broadcasted.  Client cannot
+       send broadcast packet and normal server cannot send
+       broadcast packet.  Only router server may send broadcast
+       packet.  The router receiving of packet with this flag 
+       set MUST send (broadcast) the packet to its primary
+       route.  If router has several router connections the
+       packet may be sent only to the primary route.  See
+       section 2.12 Packet Broadcasting for description of 
+       packet broadcasting.
+
+.in 3
+
+
+
+
+o Packet Type (1 byte) - Is the type of the packet. Receiver 
+  uses this field to parse the packet.  See section 2.3
+  SILC Packets for list of defined packet types.
+
+o Pad Length (1 byte) - Indicates the length of the padding
+  applied after the SILC Packet header.  Maximum length for
+  padding is 128 bytes.
+
+o RESERVED (1 byte) - Reserved field and must include a
+  zero (0) value.
+
+o Source ID Length (1 byte) - Indicates the length of the
+  Source ID field in the header, not including this or any
+  other fields.
+
+o Destination ID Length (1 byte) - Indicates the length of the
+  Destination ID field in the header, not including this or
+  any other fields.
+
+o Src ID Type (1 byte) - Indicates the type of ID in the
+  Source ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Source ID (variable length) - The actual source ID that
+  indicates which is the original sender of the packet.
+
+o Dst ID Type (1 byte) - Indicates the type of ID in the
+  Destination ID field.  See section 2.4 SILC ID Types for
+  defined ID types.
+
+o Destination ID (variable length) - The actual destination
+  ID that indicates which is the end receiver of the packet.
+
+
+
+.ti 0
+2.3 SILC Packet Types
+
+SILC packet types defines the contents of the packet and it is used by
+the receiver to parse the packet.  The packet type is 8 bits, as a one
+byte, in length.  The range for the packet types are from 0 - 255,
+where 0 is never sent and 255 is currently reserved for future
+extensions and MUST NOT be defined to any other purpose.  Every SILC
+specification compliant implementation SHOULD support all of these packet
+types.
+
+The below list of the SILC Packet types includes reference to the packet
+payload as well.  Packet payloads are the actual packet, that is, the data
+that the packet consists of.  Each packet type defines packet payload 
+which usually may only be sent with the specific packet type.
+
+Most of the packets are packets that must be destined directly to entity
+that is connected to the sender.  It is not allowed, for example, for
+router to send disconnect packet to client that is not directly connected
+to the router.  However, there are some special packet types that may
+be destined to some entity that the sender has not direct connection
+with.  These packets are for example private message packets, channel
+message packets, command packets and some other packets that may be
+broadcasted in the SILC network.  If the packet is allowed to be sent to
+indirectly connected entity it is mentioned separately in the packet
+description (unless it is obvious as in private and channel message
+packets).  Other packets MUST NOT be sent or accepted, if sent, to
+indirectly connected entities.
+
+List of SILC Packet types are defined as follows.
+
+.in 1
+     0    SILC_PACKET_NONE
+
+          This type is reserved and it is never sent.         
+
+
+     1    SILC_PACKET_DISCONNECT
+
+          This packet is sent to disconnect the remote end.  Reason of
+          the disconnection is sent inside the packet payload.  Client
+          usually does not send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.3 Disconnect Payload
+
+
+     2    SILC_PACKET_SUCCESS
+
+          This packet is sent upon successful execution of some protocol.
+          The status of the success is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.4 Success Payload
+
+
+     3    SILC_PACKET_FAILURE
+
+          This packet is sent upon failure of some protocol.  The status
+          of the failure is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.5 Failure Payload
+
+
+     4    SILC_PACKET_REJECT
+
+          This packet MAY be sent upon rejection of some protocol.
+          The status of the rejection is sent in the packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.6 Reject Payload
+
+
+     5    SILC_PACKET_NOTIFY
+
+          This packet is used to send notify message, usually from
+          server to client, although it MAY be sent from server to another
+          server as well.  Client MUST NOT send this packet.  Server MAY
+          send this packet to channel as well when the packet is 
+          distributed to all clients on the channel.
+
+          Payload of the packet:  See section 2.3.7 Notify Payload.
+
+
+     6    SILC_PACKET_ERROR
+
+          This packet is sent when an error occurs.  Server MAY
+          send this packet.  Client MUST NOT send this packet.  The
+          client MAY entirely ignore the packet, however, server is
+          most likely to take action anyway.  This packet MAY be sent
+          to entity that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.8 Error Payload.
+
+
+     7    SILC_PACKET_CHANNEL_MESSAGE
+
+          This packet is used to send messages to channels.  The packet
+          includes Channel ID of the channel and the actual message to
+          the channel.  Messages sent to the channel are always protected
+          by channel specific keys.  Channel Keys are distributed by
+          SILC_PACKET_CHANNEL_KEY packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.9 Channel Message 
+                                  Payload
+
+
+     8    SILC_PACKET_CHANNEL_KEY
+
+          This packet is used to distribute new key for particular
+          channel.  Each channel has their own independent keys that
+          is used to protect the traffic on the channel.  Only server
+          may send this packet.  This packet MAY be sent to entity
+          that is indirectly connected to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.10 Channel Key Payload
+
+
+     9    SILC_PACKET_PRIVATE_MESSAGE
+
+          This packet is used to send private messages from client
+          to another client.  By default, private messages are protected
+          by session keys established by normal key exchange protocol.
+          However, it is possible to use specific key to protect private
+          messages.  SILC_PACKET_PRIVATE_MESSAGE_KEY packet is used to 
+          agree the key with the remote client.  Pre-shared key MAY be 
+          used as well if both of the client knows it, however, it needs 
+          to be agreed outside SILC.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.11 Private Message
+                                  Payload
+
+
+     10   SILC_PACKET_PRIVATE_MESSAGE_KEY
+
+          This packet is used to agree about a key to be used to protect
+          the private messages between two clients.  If this is not sent
+          the normal session key is used to protect the private messages
+          inside SILC network.  Agreeing to use specific key to protect
+          private messages adds security, as no server between the two
+          clients will be able to decrypt the private message.  However,
+          servers inside SILC network are considered to be trusted, thus
+          using normal session key to protect private messages does not
+          degrade security.  Whether to agree to use specific keys by
+          default or to use normal session keys by default, is 
+          implementation specific issue.  See more of this in [SILC1].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.12 Private Message
+                                  Key Payload
+
+
+     11   SILC_PACKET_COMMAND
+
+          This packet is used to send commands from client to server.
+          Server MAY send this packet to other servers as well.  All
+          commands are listed in their own section SILC Command Types
+          in [SILC4].  The contents of this packet is command specific.
+          This packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.13 Command Payload
+
+
+     12   SILC_PACKET_COMMAND_REPLY
+
+          This packet is sent as reply to the SILC_PACKET_COMMAND packet.
+          The contents of this packet is command specific.  This packet
+          MAY be sent to entity that is indirectly connected to the
+          sender.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.14 Command Reply 
+                                  Payload and section 2.3.13 Command
+                                  Payload
+
+
+
+
+     13   SILC_PACKET_KEY_EXCHANGE
+
+          This packet is used to start SILC Key Exchange Protocol, 
+          described in detail in [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     14   SILC_PACKET_KEY_EXCHANGE_1
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     15   SILC_PACKET_KEY_EXCHANGE_2
+
+          This packet is used as part of the SILC Key Exchange Protocol.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Key Exchange
+                                  Protocol and its sub sections in
+                                  [SILC3].
+
+
+     16   SILC_PACKET_CONNECTION_AUTH_REQUEST
+
+          This packet is used to request the authentication method to
+          be used in the SILC Connection Authentication Protocol.  If 
+          initiator of the protocol does not know the mandatory 
+          authentication method this packet MAY be used to determine it.
+
+          The party receiving this payload MUST respond with the same
+          packet including the mandatory authentication method.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.15 Connection Auth
+                                  Request Payload
+
+
+
+
+     17   SILC_PACKET_CONNECTION_AUTH
+
+          This packet is used to start and perform the SILC Connection
+          Authentication Protocol.  This protocol is used to authenticate
+          the connecting party.  The protocol is described in detail in
+          [SILC3].
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  Payload of this packet is described
+                                  in the section SILC Authentication
+                                  Protocol and it sub sections in [SILC].
+
+
+     18   SILC_PACKET_NEW_ID
+
+          This packet is used to distribute new ID's from server to
+          router and from router to all routers in the SILC network.
+          This is used when for example new client is registered to
+          SILC network.  The newly created ID's of these operations are
+          distributed by this packet.  Only server may send this packet,
+          however, client MUST be able to receive this packet.  This
+          packet MAY be sent to entity that is indirectly connected
+          to the sender.
+
+          Payload of the packet:  See section 2.3.16 New ID Payload
+
+
+     19   SILC_PACKET_NEW_CLIENT
+
+          This packet is used by client to register itself to the   
+          SILC network.  This is sent after key exchange and  
+          authentication protocols has been completed.  Client sends
+          various information about itself in this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.17 New Client Payload
+
+
+     20   SILC_PACKET_NEW_SERVER
+
+          This packet is used by server to register itself to the
+          SILC network.  This is sent after key exchange and 
+          authentication protocols has been completed.  Server sends
+          this to the router it connected to, or, if router was
+          connecting, to the connected router.  Server sends its
+          Server ID and other information in this packet.  The client
+          MUST NOT send or receive this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.18 New Server Payload
+
+
+     21   SILC_PACKET_NEW_CHANNEL
+
+          This packet is used to notify routers about newly created
+          channel.  Channels are always created by the router and it MUST
+          notify other routers about the created channel.  Router sends
+          this packet to its primary route.  Client MUST NOT send this
+          packet.  This packet MAY be sent to entity that is indirectly
+          connected to the sender.
+
+          Payload of the packet:  See section 2.3.19 New Channel Payload
+
+
+     22   SILC_PACKET_REKEY
+
+          This packet is used to indicate that re-key must be performed
+          for session keys.  See section Session Key Regeneration in
+          [SILC1] for more information.  This packet does not have
+          a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     23   SILC_PACKET_REKEY_DONE
+
+          This packet is used to indicate that re-key is performed and
+          new keys must be used hereafter.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+     
+     24   SILC_PACKET_HEARTBEAT
+
+          This packet is used by clients, servers and routers to keep the
+          connection alive.  It is recommended that all servers implement
+          keepalive actions and perform it to both direction in a link.
+          This packet does not have a payload.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+
+     25   SILC_PACKET_KEY_AGREEMENT
+
+          This packet is used by clients to request key negotiation 
+          between another client in the SILC network.  If the negotiation
+          is started it is performed using the SKE protocol.  The result of
+          the negotiation, the secret key material, can be used for
+          example as private message key.  The server and router MUST NOT
+          send this packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.20 Key Agreement Payload
+
+
+     26   SILC_PACKET_RESUME_ROUTER
+
+          This packet is used during backup router protocol when the 
+          original primary router of the cell comes back online and wishes
+          to resume the position as being the primary router of the cell.
+
+          Payload of the packet:  See section 2.3.21 Resume Router Payload
+
+
+     27   SILC_PACKET_FTP
+
+          This packet is used to perform an file transfer protocol in the
+          SILC session with some entity in the network.  The packet is
+          multi purpose.  The packet is used to tell other entity in the
+          network that the sender wishes to perform an file transfer
+          protocol.  The packet is also used to actually tunnel the
+          file transfer protocol stream.  The file transfer protocol
+          stream is always protected with the SILC packet.
+
+          This packet MUST NOT be sent as list and the List flag MUST
+          NOT be set.
+
+          Payload of the packet:  See section 2.3.22 File Transfer Payload
+
+
+     28 - 199
+
+          Currently undefined commands.
+
+
+     200 - 254
+
+          These packet types are reserved for private use and they will
+          not be defined by this document.
+
+
+
+
+     255  SILC_PACKET_MAX
+
+          This type is reserved for future extensions and currently it 
+          MUST NOT be sent.
+.in 3
+
+
+.ti 0
+2.3.1 SILC Packet Payloads
+
+All payloads resides in the main data area of the SILC packet.  However
+all payloads MUST be at the start of the data area after the SILC
+packet header and padding.  All fields in the packet payload are always
+encrypted, as they reside in the data area of the packet which is
+always encrypted.
+
+Payloads described in this section are common payloads that MUST be
+accepted anytime during SILC session.  Most of the payloads may only
+be sent with specific packet type which is defined in the description
+of the payload.
+
+There are a lot of other payloads in the SILC as well.  However, they
+are not common in the sense that they could be sent at any time. 
+These payloads are not described in this section.  These are payloads
+such as SILC Key Exchange payloads and so on.  These are described
+in [SILC1], [SILC3] and [SILC4].
+
+
+.ti 0
+2.3.2 Generic payloads
+
+This section describes generic payloads that are not associated to any
+specific packet type.  They can be used for example inside some other
+packet payloads.
+
+
+.ti 0
+2.3.2.1 ID Payload
+
+This payload can be used to send an ID.  ID's are variable in length
+thus this payload provides a way to send variable length ID's.
+
+The following diagram represents the ID Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|             ID Type           |           ID Length           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           ID Data                             ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 3:  ID Payload
+
+
+.in 6
+o ID Type (2 bytes) - Indicates the type of the ID.  See 
+  section 2.4 SILC ID Types for list of defined ID types.
+
+o ID Length (2 bytes) - Length of the ID Data area not 
+  including the length of any other fields in the payload.
+
+o ID Data (variable length) - The actual ID data.
+.in 3
+
+
+.ti 0
+2.3.2.2 Argument Payload
+
+Argument Payload is used to set arguments for any packet payload that
+needs and supports arguments, such as commands.  Number of arguments
+associated with a packet MUST be indicated by the packet payload which
+needs the arguments.  Argument Payloads MUST always reside right after
+the packet payload needing the arguments.  Incorrect amount of argument
+payloads MUST cause rejection of the packet.
+
+
+
+
+
+
+
+The following diagram represents the Argument Payload.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | Argument Type |               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               +
+|                                                               |
+~                        Argument Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 4:  Argument Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the argument payload data 
+  area not including the length of any other fields in the 
+  payload.
+
+o Argument Type (1 byte) - Indicates the type of the argument.  
+  Every argument may have a specific type that MUST be defined
+  by the packet payload needing the argument.  For example
+  every command specify a number for each argument that maybe 
+  associated with the command.  By using this number the receiver 
+  of the packet knows what type of argument this is.  If there is
+  no specific argument type this field is set to zero (0).
+
+o Argument Data (variable length) - Argument data.
+.in 3
+
+
+.ti 0
+2.3.2.3 Channel Payload
+
+Generic Channel Payload may be used to send information about channel,
+its name, the Channel ID and a mode.
+
+The following diagram represents the Channel Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Name Length      |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                           Mode Mask                           |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  New Channel Payload
+
+
+.in 6
+o Channel Name Length (2 bytes) - Length of the channel name
+  field.
+
+o Channel Name (variable length) - The name of the channel.
+
+o Channel ID Length (2 bytes) - Length of the Channel ID field.
+
+o Channel ID (variable length) - The Channel ID.
+
+o Mode Mask (4 bytes) - A mode.  This can be the mode of the
+  channel but it can also be the mode of the client on the
+  channel.  The contents of this field is dependent of the
+  usage of this payload.  The usage is defined separately
+  when this payload is used.  This is a 32 bit MSB first value.
+.in 3
+
+
+.ti 0
+2.3.2.4 Public Key Payload
+
+Generic Public Key Payload may be used to send different types of
+public keys and certificates.
+
+The following diagram represents the Public Key Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Public Key Length       |        Public Key Type        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~            Public Key of the party (or certificate)           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  Public Key Payload
+
+
+.in 6
+o Public Key Length (2 bytes) - The length of the Public Key
+  (or certificate) field, not including any other field.
+
+o Public Key Type (2 bytes) - The public key (or certificate) 
+  type.  This field indicates the type of the public key in 
+  the packet.  See the [SILC3] for defined public key types.
+
+o Public Key (or certificate) (variable length) - The
+  public key or certificate.
+.in 3
+
+
+.ti 0
+2.3.3 Disconnect Payload
+
+Disconnect payload is sent upon disconnection.  The payload is simple;
+reason of disconnection is sent to the disconnected party.
+
+The payload may only be sent with SILC_PACKET_DISCONNECT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the Disconnect Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Disconnect Message                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 7:  Disconnect Payload
+
+
+
+
+.in 6
+o Disconnect Message (variable length) - Human readable
+  reason of the disconnection.
+.in 3
+
+
+.ti 0
+2.3.4 Success Payload
+
+Success payload is sent when some protocol execution is successfully
+completed.  The payload is simple; indication of the success is sent.
+This may be any data, including binary or human readable data.
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Success Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 8:  Success Payload
+
+
+.in 6
+o Success Indication (variable length) - Indication of
+  the success.  This may be for example some flag that
+  indicates the protocol and the success status or human
+  readable success message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+
+.ti 0
+2.3.5 Failure Payload
+
+This is opposite of Success Payload.  Indication of failure of
+some protocol is sent in the payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                      Failure Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 9:  Failure Payload
+
+
+.in 6
+o Failure Indication (variable length) - Indication of
+  the failure.  This may be for example some flag that
+  indicates the protocol and the failure status or human
+  readable failure message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.6 Reject Payload
+
+This payload is sent when some protocol is rejected to be executed.
+Other operations MAY send this as well that was rejected.  The
+indication of the rejection is sent in the payload.  The indication
+may be binary or human readable data.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Reject Indication                       ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 10:  Reject Payload
+
+
+.in 6
+o Reject Indication (variable length) - Indication of
+  the rejection.  This maybe for example some flag that
+  indicates the protocol and the rejection status or human
+  readable rejection message.  The true length of this
+  payload is available by calculating it from the SILC
+  Packet Header.
+.in 3
+
+
+.ti 0
+2.3.7 Notify Payload
+
+Notify payload is used to send notify messages.  The payload is usually
+sent from server to client, however, server MAY send it to another
+server as well.  This payload MAY also be sent to a channel.  Client
+MUST NOT send this payload.  The receiver of this payload MAY ignore
+the contents of the payload, however, notify message SHOULD be audited.
+
+The payload may only be sent with SILC_PACKET_NOTIFY packet.  It MUST
+not be sent in any other packet type.  The following diagram represents
+the Notify Payload.
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|          Notify Type          |        Payload Length         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Argument Nums |
++-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 11:  Notify Payload
+
+
+.in 6
+o Notify Type (2 bytes) - Indicates the type of the notify
+  message.
+
+o Payload Length (2 bytes) - Length of the entire Notify Payload
+  including any associated Argument Payloads.
+
+o Argument Nums (2 bytes) - Indicates the number of Argument
+  Payloads associated to this payload.  Notify types may define
+  arguments to be send along the notify message.
+.in 3
+
+The following list of currently defined notify types.  The format for
+notify arguments is same as in SILC commands described in [SILC4]. 
+Also, all ID's sent in arguments are sent inside ID Payload.
+
+.in 6
+0     SILC_NOTIFY_TYPE_NONE
+
+      If no specific notify type apply for the notify message this type
+      MAY be used.
+
+      Max Arguments:  1
+          Arguments:  (1) <message>
+
+      The <message> is implementation specific free text string.
+      Receiver MAY ignore this message.
+
+
+1     SILC_NOTIFY_TYPE_INVITE
+
+      Sent when an client is invited to a channel.  This is also sent
+      when the invite list of the channel is changed.  This notify type
+      is sent between routers and if an client was invited, to the 
+      client as well.  In this case the packet is destined to the client.
+
+      Max Arguments:  5
+          Arguments:  (1) <Channel ID>          (2) <channel name>
+                      (3) [<sender Client ID>]  (4) [<adding client>]
+                      (5) [<removing client>]
+
+      The <Channel ID> is the channel.  The <channel name> is the name
+      of the channel and is provided because the client which receives 
+      this notify packet may not have a way to resolve the name of the
+      channel from the <Channel ID>.  The <sender Client ID> is the
+      Client ID which invited the client to the channel.  The <adding
+      client> and the <removing client> indicates the added or removed
+      client from the channel's invite list.  The format of the <adding
+      client> and the <removing client> is defined in the [SILC4] with
+      SILC_COMMAND_INVITE command.
+
+      The <adding client> and <removing client> MUST NOT be sent when
+      the packet is destined to a client.
+
+
+2     SILC_NOTIFY_TYPE_JOIN
+
+      Sent when client has joined to a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) [<Client ID>]       (2) <Channel ID>
+
+      The <Client ID> is the client that joined to the channel indicated
+      by the <Channel ID>.
+
+
+3     SILC_NOTIFY_TYPE_LEAVE
+
+      Sent when client has left a channel.  The server must distribute
+      this type only to the local clients on the channel and then send
+      it to its primary router.  The router or server receiving the
+      packet distributes this type to the local clients on the channel
+      and broadcast it to the network.
+
+      Max Arguments:  1
+          Arguments:  (1) <Client ID>
+
+      The <Client ID> is the client which left the channel.
+
+
+4     SILC_NOTIFY_TYPE_SIGNOFF
+
+      Sent when client signoff from SILC network.  The server MUST
+      distribute this type only to the local clients on the channel and
+      then send it to its primary router.  The router or server receiving
+      the packet distributes this type to the local clients on the
+      channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <message>
+
+      The <Client ID> is the client which left SILC network.  The
+      <message> is free text string indicating the reason of the signoff.
+
+
+5     SILC_NOTIFY_TYPE_TOPIC_SET
+
+      Sent when topic is set/changed on a channel.  This type must be
+      sent only to the clients which is joined on the channel which
+      topic was set or changed.
+
+      Max Arguments:  2
+          Arguments:  (1) <ID Payload>  (2) <topic>
+
+      The <ID Payload> is the ID of the entity who set the topic.  It
+      usually is Client ID but it can be Server ID and Channel ID as well.
+
+
+6     SILC_NOTIFY_TYPE_NICK_CHANGE
+
+      Sent when client changes nick on a channel.  The server MUST
+      distribute this type only to the local clients on the channel
+      and then send it to its primary router.  The router or server
+      receiving the packet distributes this type to the local clients
+      on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Client ID>  (2) <New Client ID>
+
+      The <Old Client ID> is the old ID of the client which changed
+      the nickname.  The <New Client ID> is the new ID generated by
+      the change of the nickname.
+
+
+7     SILC_NOTIFY_TYPE_CMODE_CHANGE
+
+      Sent when channel mode has changed.  This type MUST be sent only
+      to the clients which is joined on the channel which mode was
+      changed.
+
+      Max Arguments:  5
+          Arguments:  (1) <ID Payload>    (2) <mode mask>
+                      (3) [<cipher>]      (4) <[hmac>]     
+                      (5) [<passphrase>]
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing channel mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The client can safely
+      ignore the <cipher> argument since the SILC_PACKET_CHANNEL_KEY
+      packet will force the new channel key change anyway.  The <hmac>
+      argument is important since the client is responsible of setting
+      the new HMAC and the hmac key into use.  The <passphrase> is
+      the passphrase of the channel, if it was now set.
+
+
+8     SILC_NOTIFY_TYPE_CUMODE_CHANGE
+
+      Sent when user mode on channel has changed.  This type MUST be
+      sent only to the clients which is joined on the channel where
+      the target client is on.
+
+      Max Arguments:  3
+          Arguments:  (1) <ID Payload>  (2) <mode mask>
+                      (3) <Target Client ID>
+
+      The <ID Payload> is the ID (usually Client ID but it can be
+      Server ID as well when the router is enforcing user's mode
+      change) of the entity which changed the mode.  The <mode mask>
+      is the new mode mask of the channel.  The <Target Client ID>
+      is the client which mode was changed.
+
+
+9     SILC_NOTIFY_TYPE_MOTD
+
+      Sent when Message of the Day (motd) is sent to a client.
+
+      Max Arguments:  1
+          Arguments:  (1) <motd>
+
+      The <motd> is the Message of the Day.
+
+
+10    SILC_NOTIFY_TYPE_CHANNEL_CHANGE
+
+      Sent when channel's ID has changed for a reason or another.
+      This is sent by normal server to the client.  This can also be
+      sent by router to other server to force the Channel ID change.
+      The Channel ID MUST be changed to use the new one.  When sent
+      to clients, this type MUST be sent only to the clients which is
+      joined on the channel.
+
+      Max Arguments:  2
+          Arguments:  (1) <Old Channel ID>  (2) <New Channel ID>
+
+      The <Old Channel ID> is the channel's old ID and the <New
+      Channel ID> is the new one that MUST replace the old one.
+
+
+11    SILC_NOTIFY_TYPE_SERVER_SIGNOFF
+
+      Sent when server quits SILC network.  Those clients from this
+      server that are on channels must be removed from the channel.
+
+      Max Arguments:  2000
+          Arguments:  (1) <Server ID>   (n) [<Client ID>]   [...]
+
+      The <Server ID> is the server's ID.  The rest of the arguments
+      are the Client ID's of the client's which are coming from this
+      server and are thus quitting the SILC network also.  If the
+      maximum number of arguments are reached another 
+      SILC_NOTIFY_TYPE_SERVER_SIGNOFF notify packet MUST be sent.
+      When this notify packet is sent between routers the Client ID's
+      MAY be omitted.  Server receiving the Client ID's in the payload
+      may use them directly to remove the client.
+
+
+12    SILC_NOTIFY_TYPE_KICKED
+
+      Sent when a client has been kicked from a channel.  This is
+      sent also to the client which was kicked from the channel.
+      The client which was kicked from the channel MUST be removed
+      from the channel.  This notify type is always destined to the
+      channel.  The router or server receiving the packet distributes
+      this type to the local clients on the channel and broadcast it
+      to the network.
+
+      Max Arguments:  3
+          Arguments:  (1) <Client ID>           (2) [<comment>]
+                      (3) <Kicker's Client ID>
+
+      The <Client ID> is the client which was kicked from the channel.
+      The kicker may have set the <comment> to indicate the reason for
+      the kicking.  The <Kicker's Client ID> is the kicker.
+
+
+13    SILC_NOTIFY_TYPE_KILLED
+
+      Sent when a client has been killed from the network.  This is sent 
+      also to the client which was killed from the network.  The client
+      which was killed from the network MUST be removed from the network.
+      This notify type is destined directly to the client which was
+      killed and to channel if the client is on any channel.  The router
+      or server receiving the packet distributes this type to the local
+      clients on the channel and broadcast it to the network.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) [<comment>]
+
+      The <Client ID> is the client which was killed from the network.
+      The killer may have set the <comment> to indicate the reason for
+      the killing.
+
+
+14    SILC_NOTIFY_TYPE_UMODE_CHANGE
+
+      Sent when user's mode in the SILC changes.  This type is sent
+      only between routers as broadcast packet.
+
+      Max Arguments:  2
+          Arguments:  (1) <Client ID>  (2) <mode mask>
+
+      The <Client ID> is the client which mode was changed.  The
+      <mode mask> is the new mode mask.
+
+
+15    SILC_NOTIFY_TYPE_BAN
+
+      Sent when the ban list of the channel is changed.  This type is
+      sent only between routers as broadcast packet.
+
+      Max Arguments:  3
+          Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                      (3) [<removing client>]
+
+      The <Channel ID> is the channel which ban list was changed.  The
+      <adding client> is used to indicate that a ban was added and the
+      <removing client> is used to indicate that a ban was removed from
+      the ban list.  The format of the <adding client> and the 
+      <removing client> is defined in the [SILC4] with SILC_COMMAND_BAN
+      command.
+
+.in 3
+
+Notify types starting from 16384 are reserved for private notify
+message types.
+
+
+.ti 0
+2.3.8 Error Payload
+
+Error payload is sent upon error.  Error may occur in various
+conditions when server sends this packet.  Client MUST NOT send this
+payload but MUST be able to accept it.  However, client MAY
+totally ignore the contents of the packet as server is going to
+take action on the error anyway.  However, it is recommended
+that the client takes error packet seriously.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Error Message                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 12:  Error Payload
+
+
+.in 6
+o Error Message (variable length) - Human readable error
+  message.
+.in 3
+
+
+.ti 0
+2.3.9 Channel Message Payload
+
+Channel messages are the most common messages sent in the SILC.
+Channel Message Payload is used to send message to channels.  These
+messages can only be sent if client has joined to some channel.
+Even though this packet is the most common in SILC it is still
+special packet.  Some special handling on sending and reception
+of channel message is required.
+
+Padding MUST be applied into this payload since the payload is
+encrypted separately from other parts of the packet with the
+channel specific key.  Hence the requirement of the padding.  
+The padding SHOULD be random data.  The packet MUST be made
+multiple by eight (8) or by the block size of the cipher, which
+ever is larger.
+
+The SILC header in this packet is encrypted with the session key
+of the next receiver of the packet.  Nothing else is encrypted
+with that key.  Thus, the actual packet and padding to be
+encrypted with the session key is SILC Header plus padding to it
+to make it multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.
+
+Receiver of the the channel message packet is able to determine
+the channel the message is destined to by checking the destination
+ID from the SILC Packet header which tells the destination channel.
+The original sender of the packet is also determined by checking
+the source ID from the header which tells the client which sent
+the message.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_MESSAGE packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Message Payload.
+
+(*) indicates that the field is not encrypted.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |         Message Length        |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                         Message Data                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Padding Length         |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                            Padding                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                              MAC                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                       Initial Vector *                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 13:  Channel Message Payload
+
+
+.in 6
+o Flags (2 bytes) - Includes the flags of the channel
+  messages.  The flags can indicate a reason or purpose
+  for the channel message.  Note that the Private Message
+  Payload use these same flags for the same purpose.  The
+  following flags are defined:
+
+  0x0000  SILC_MESSAGE_FLAG_NONE
+
+          No specific flags set.
+
+  0x0001  SILC_MESSAGE_FLAG_AUTOREPLY
+
+          This message is an automatic reply to an earlier
+          received message.
+
+  0x0002  SILC_MESSAGE_FLAG_NOREPLY
+
+          There should not be reply messages to this
+          message.
+
+  0x0004  SILC_MESSAGE_FLAG_ACTION
+
+          The sender is performing an action and the message
+          is the indication of the action.
+
+  0x0008  SILC_MESSAGE_FLAG_NOTICE
+
+          The message is for example an informational notice
+          type message.
+
+  0x0010  SILC_MESSAGE_FLAG_REQUEST
+
+          This is a generic request flag to send request
+          messages.  A separate document should define any 
+          payloads associated to this flag.
+
+  0x0020  SILC_MESSAGE_FLAG_SIGNED
+
+          This flag indicates that the message is signed
+          with sender's private key and thus can be verified
+          by the receiver using the sender's public key.  A
+          separate document should define the detailed procedure
+          of the signing process and any associated payloads
+          of this flag.
+
+  0x0040 - 0x0200 RESERVED
+
+          Reserved for future flags
+
+  0x0400 - 0x8000 PRIVATE RANGE
+
+          Private range for free use.
+
+o Message Length (2 bytes) - Indicates the length of the
+  the Message Data field in the payload, not including any 
+  other field.
+
+o Message Data (variable length) - The actual message to
+  the channel.
+
+o Padding Length (2 bytes) - Indicates the length of the
+  Padding field in the payload, not including any other
+  field.
+
+o Padding (variable length) - The padding that MUST be
+  applied because this payload is encrypted separately from
+  other parts of the packet.
+
+o MAC (variable length) - The MAC computed from the
+  Message Length, Message Data, Padding Length and Padding
+  fields.  This protects the integrity of the plaintext
+  channel message.  The receiver can verify from the MAC
+  whether the message decrypted correctly.  Also, if more than
+  one private key has been set for the channel, the receiver
+  can verify which of the keys decrypted the message 
+  correctly.  Note that, this field is encrypted and MUST
+  be added to the padding calculation.
+
+o Initial Vector (variable length) - The initial vector
+  that has been used in packet encryption.  It needs to be
+  used in the packet decryption as well.  What this field
+  includes is implementation issue.  However, it is 
+  RECOMMENDED that it would be random data or, perhaps,
+  a timestamp.  It is NOT RECOMMENDED to use zero (0) as an
+  initial vector.  This field is not encrypted.  This field
+  is not included into the padding calculation.  Length
+  of this field equals the cipher's block size.  This field
+  is, however, authenticated.
+.in 3
+
+
+.ti 0
+2.3.10 Channel Key Payload
+
+All traffic in channels are protected by channel specific keys.
+Channel Key Payload is used to distribute channel keys to all
+clients on the particular channel.  Channel keys are sent when
+the channel is created, when new user joins to the channel and
+whenever a user has left a channel.  Server creates the new
+channel key and distributes it to the clients by encrypting this
+payload with the session key shared between the server and
+the client.  After that, client starts using the key received
+in this payload to protect the traffic on the channel.
+
+The client which is joining to the channel receives its key in the
+SILC_COMMAND_JOIN command reply message thus it is not necessary to
+send this payload to the entity which sent the SILC_COMMAND_JOIN
+command.
+
+Channel keys are cell specific thus every router in the cell have
+to create a channel key and distribute it if any client in the
+cell has joined to a channel.  Channel traffic between cell's
+are not encrypted using channel keys, they are encrypted using
+normal session keys between two routers.  Inside a cell, all
+channel traffic is encrypted with the specified channel key.
+Channel key should expire periodically, say, in one hour, in
+which case new channel key is created and distributed.
+
+The payload may only be sent with SILC_PACKET_CHANNEL_KEY packet.
+It MUST NOT be sent in any other packet type.  The following diagram 
+represents the Channel Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Channel ID Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Channel ID                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Cipher Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Channel Key Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Channel Key                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 14:  Channel Key Payload
+
+
+
+.in 6
+o Channel ID Length (2 bytes) - Indicates the length of the
+  Channel ID field in the payload, not including any other
+  field.
+
+o Channel ID (variable length) - The Channel ID of the
+  channel this key is meant for.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher used
+  in the protection of channel traffic.  This name is
+  initially decided by the creator of the channel but it
+  MAY change during the life time of the channel as well.
+
+o Channel Key Length (2 bytes) - Indicates the length of the
+  Channel Key field in the payload, not including any other
+  field.
+
+o Channel Key (variable length) - The actual channel key
+  material.
+.in 3
+
+
+.ti 0
+2.3.11 Private Message Payload
+
+Private Message Payload is used to send private message between
+two clients (or users for that matter).  The messages are sent only
+to the specified user and no other user inside SILC network is
+able to see the message.  The message is protected by the session 
+key established by the SILC Key Exchange Protocol.  However,
+it is also possible to agree to use a private key to protect
+just the private messages.  See section 2.3.11 Private Message
+Key Payload for detailed description of how to agree to use
+specific key.
+
+If normal session key is used to protect the message, every server
+between the sender client and the receiving client MUST decrypt the
+packet and always re-encrypt it with the session key of the next
+receiver of the packet.  See section Client To Client in [SILC1].
+
+When private key is used to protect the message, servers between 
+the sender and the receiver needs not to decrypt/re-encrypt the 
+packet.  Section Client To Client in [SILC1] gives example of this
+scheme as well.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Payload.
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|            Flags              |      Message Data Length      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                          Message Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                             Padding                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 15:  Private Message Payload
+
+
+.in 6
+o Flags (2 bytes) - This field includes the flags of the
+  private message.  They can indicate a different reason or
+  purpose for the private message.  See the section 2.3.9
+  Channel Message Payload for defined flags.  Note that
+  the Channel Message Payload use the same flags for the
+  same purpose.
+
+o Message Data Length (2 bytes) - Indicates the length of the
+  Message Data field, not includes any other field.
+
+o Message Data (variable length) - The actual message to
+  the client.  Rest of the packet is reserved for the message
+  data.
+
+o Padding (variable length) - This field is present only
+  when the private message payload is encrypted with private
+  message key.  In this case the padding is applied to make
+  the payload multiple by eight (8), or by the block size of
+  the cipher, which ever is larger.  When encrypted with
+  normal session keys, this field MUST NOT be included.
+.in 3
+
+
+.ti 0
+2.3.12 Private Message Key Payload
+
+This payload is used to send key from client to another client that
+is going to be used to protect the private messages between these
+two clients.  If this payload is not sent normal session key 
+established by the SILC Key Exchange Protocol is used to protect
+the private messages.
+
+This payload may only be sent by client to another client.  Server
+MUST NOT send this payload at any time.  After sending this payload
+the sender of private messages must set the Private Message Key
+flag into SILC Packet Header.
+
+The payload may only be sent with SILC_PACKET_PRIVATE_MESSAGE_KEY 
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Private Message Key Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|  Private Message Key Length   |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                      Private Message Key                      ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Cipher Name Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Cipher Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 16:  Private Message Key Payload
+
+
+
+
+.in 6
+o Private Message Key Length (2 bytes) - Indicates the length 
+  of the Private Message Key field in the payload, not including 
+  any other field.
+
+o Private Message Key (variable length) - The actual private
+  message key material.
+
+o Cipher Name Length (2 bytes) - Indicates the length of the
+  Cipher Name field in the payload, not including any other
+  field.
+
+o Cipher Name (variable length) - Name of the cipher to use
+  in the private message encryption.  If this field does not
+  exist then the default cipher of the SILC protocol is used.
+  See the [SILC1] for defined ciphers.
+.in 3
+
+
+
+.ti 0
+2.3.13 Command Payload
+
+Command Payload is used to send SILC commands from client to server.
+Also server MAY send commands to other servers.  The following diagram
+represents the Command Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|         Payload Length        | SILC Command  | Arguments Num |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Command Identifier      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 17:  Command Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire command 
+  payload including any command argument payloads associated 
+  with this payload.
+
+o SILC Command (1 byte) - Indicates the SILC command.  This MUST 
+  be set to non-zero value.  If zero (0) value is found in this 
+  field the packet MUST be discarded.
+
+o Arguments Num (1 byte) - Indicates the number of arguments
+  associated with the command.  If there are no arguments this
+  field is set to zero (0).  The arguments MUST follow the 
+  command payload.  See section 2.3.2.2 for definition of the
+  Argument Payload.
+
+o Command Identifier (2 bytes) - Identifies this command at the
+  sender's end.  The entity which replies to this command MUST
+  set the value found from this field into the Command Payload
+  used to send the reply to the sender.  This way the sender
+  can identify which command reply belongs to which originally
+  sent command.  What this field includes is implementation
+  issue but it is RECOMMENDED that wrapping counter value is
+  used in the field.  Value zero (0) in this field means that
+  no specific value is set.
+.in 3
+
+See [SILC4] for detailed description of different SILC commands,
+their arguments and their reply messages.
+
+
+
+
+.ti 0
+2.3.14 Command Reply Payload
+
+Command Reply Payload is used to send replies to the commands.  The
+Command Reply Payload is identical to the Command Payload thus see
+the upper section for the Command Payload specification.
+
+The entity which sends the reply packet MUST set the Command Identifier
+field in the reply packet's Command Payload to the value it received
+in the original command packet.
+
+See SILC Commands in [SILC4] for detailed description of different
+SILC commands, their arguments and their reply messages.
+
+
+.ti 0
+2.3.15 Connection Auth Request Payload
+
+Client MAY send this payload to server to request the authentication
+method that must be used in authentication protocol.  If client knows 
+this information beforehand this payload is not necessary to be sent.
+Server performing authentication with another server MAY also send
+this payload to request the authentication method.  If the connecting
+server already knows this information this payload is not necessary
+to be sent.
+
+Server receiving this request MUST reply with same payload sending
+the mandatory authentication method.  Algorithms that may be required
+to be used by the authentication method are the ones already 
+established by the SILC Key Exchange protocol.  See section Key
+Exchange Start Payload in [SILC3] for detailed information.
+
+The payload may only be sent with SILC_PACKET_CONNECTION_AUTH_REQUEST
+packet.  It MUST NOT be sent in any other packet type.  The following 
+diagram represents the Connection Auth Request Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Connection Type        |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 18:  Connection Auth Request Payload
+
+
+.in 6
+o Connection Type (2 bytes) - Indicates the type of the
+  connection.  The following connection types are defined:
+
+
+     1    Client connection
+     2    Server connection
+     3    Router connection
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.
+
+o Authentication Method (2 bytes) - Indicates the authentication
+  method to be used in the authentication protocol.  The following
+  authentication methods are defined:
+
+     0    NONE        (mandatory)
+     1    password    (mandatory)
+     2    public key  (mandatory)
+
+  If any other type is found in this field the packet MUST be
+  discarded and the authentication MUST be failed.  If this
+  payload is sent as request to receive the mandatory 
+  authentication method this field MUST be set to zero (0),
+  indicating that receiver should send the mandatory 
+  authentication method.  The receiver sending this payload
+  to the requesting party, MAY also set this field to zero (0) 
+  to indicate that authentication is not required.  In this
+  case authentication protocol still MUST be started but
+  server is most likely to respond with SILC_PACKET_SUCCESS
+  immediately.
+.in 3
+
+
+.ti 0
+2.3.16 New ID Payload
+
+New ID Payload is a multipurpose payload.  It is used to send newly 
+created ID's from clients and servers.  When client connects to server
+and registers itself to the server by sending SILC_PACKET_NEW_CLIENT
+packet, server replies with this packet by sending the created ID for
+the client.  Server always creates the ID for the client.
+
+This payload is also used when server tells its router that new client
+has registered to the SILC network.  In this case the server sends
+the Client ID of the client to the router.  Similarly when router
+distributes information to other routers about the client in the SILC
+network this payload is used.  
+
+Also, when server connects to router, router uses this payload to inform
+other routers about new server in the SILC network.  However, every 
+server (or router) creates their own ID's thus the ID distributed by 
+this payload is not created by the distributor in this case.  Servers
+create their own ID's.  Server registers itself to the network by
+sending SILC_PACKET_NEW_SERVER to the router it connected to.  The case
+is same when router connects to another router.
+
+However, this payload MUST NOT be used to send information about new
+channels.  New channels are always distributed by sending the dedicated
+SILC_PACKET_NEW_CHANNEL packet.
+
+Thus, this payload is very important and used every time when some
+new entity is registered to the SILC network.  Client MUST NOT send this
+payload.  Both client and server (and router) MAY receive this payload.
+
+The packet uses generic ID Payload as New ID Payload.  See section
+2.3.2.1 for generic ID Payload.
+
+
+.ti 0
+2.3.17 New Client Payload
+
+When client is connected to the server, keys has been exchanged and
+connection has been authenticated client MUST register itself to the 
+server.  Client's first packet after key exchange and authentication 
+protocols must be SILC_PACKET_NEW_CLIENT.  This payload tells server all
+the relevant information about the connected user.  Server creates a new
+client ID for the client when received this payload and sends it to the
+client in New ID Payload.
+
+This payload sends username and real name of the user on the remote host
+which is connected to the SILC server with SILC client.  The server 
+creates the client ID according the information sent in this payload.
+The nickname of the user becomes the username sent in this payload.
+However, client should call NICK command after sending this payload to
+set the real nickname of the user which is then used to create new 
+client ID.
+
+The payload may only be sent with SILC_PACKET_NEW_CLIENT packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Client Payload.
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Username Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Username                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Real Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Real Name                           ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 19:  New Client Payload
+
+
+.in 6
+o Username Length (2 bytes) - Length of the Username field.
+
+o Username (variable length) - The username of the user on
+  the host where connecting to the SILC server.
+
+o Real Name Length (2 bytes) - Length of the Real Name field.
+
+o Real Name (variable length) - The real name of the user
+  on the host where connecting to the SILC server.
+.in 3
+
+
+.ti 0
+2.3.18 New Server Payload
+
+This payload is sent by server when it has completed successfully both
+key exchange and connection authentication protocols.  The server
+MUST register itself to the SILC Network by sending this payload.
+The first packet after these key exchange and authentication protocols
+is SILC_PACKET_NEW_SERVER packet.  The payload includes the Server ID
+of the server that it has created by itself.  It also includes a
+name of the server that is associated to the Server ID.
+
+The payload may only be sent with SILC_PACKET_NEW_SERVER packet.  It
+MUST NOT be sent in any other packet type.  The following diagram
+represents the New Server Payload.
+
+
+
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Server ID Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                        Server ID Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Server Name Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                          Server Name                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 20:  New Server Payload
+
+
+.in 6
+o Server ID Length (2 bytes) - Length of the Server ID Data
+  field.
+
+o Server ID Data (variable length) - The actual Server ID
+  data.
+
+o Server Name Length (2 bytes) - Length of the server name
+  field.
+
+o Server Name (variable length) - The server name.
+.in 3
+
+
+.ti 0
+2.3.19 New Channel Payload
+
+Information about newly created channel is broadcasted to all routers
+in the SILC network by sending this packet payload.  Channels are
+created by router of the cell.  Server never creates channels unless
+it is a standalone server and it does not have router connection,
+in this case server acts as router.  Normal server send JOIN command
+to the router (after it has received JOIN command from client) which
+then processes the command and creates the channel.  Client MUST NOT
+send this packet.  Server may send this packet to a router when it is
+announcing its existing channels to the router after it has connected
+to the router.
+
+The packet uses generic Channel Payload as New Channel Payload.  See
+section 2.3.2.3 for generic Channel Payload.  The Mode Mask field in the
+Channel Payload is the mode of the channel.
+
+
+.ti 0
+2.3.20 Key Agreement Payload
+
+This payload is used by clients to request key negotiation between
+another client in the SILC Network.  The key agreement protocol used
+is the SKE protocol.  The result of the protocol, the secret key
+material, can be used for example as private message key between the
+two clients.  This significantly adds security as the key agreement
+is performed outside the SILC network.  The server and router MUST NOT
+send this payload.
+
+The sender MAY tell the receiver of this payload the hostname and the
+port where the SKE protocol is running in the sender's end.  The 
+receiver MAY then initiate the SKE negotiation with the sender.  The
+sender MAY also optionally not to include the hostname and the port
+of its SKE protocol.  In this case the receiver MAY reply to the
+request by sending the same payload filled with the receiver's hostname
+and the port where the SKE protocol is running.  The sender MAY then
+initiate the SKE negotiation with the receiver.
+
+This payload may be sent with SILC_PACKET_KEY_AGREEMENT and 
+SILC_PACKET_FTP packet types.  It MUST NOT be sent in any other packet
+types.  The following diagram represents the Key Agreement Payload.
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Hostname Length        |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Hostname                            ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                             Port                              |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 21:  Key Agreement Payload
+
+
+.in 6
+o Hostname Length (2 bytes) - Indicates the length of the
+  Hostname field.
+
+o Hostname (variable length) - The hostname or IP address where
+  the SKE protocol is running.  The sender MAY fill this field
+  when sending the payload.  If the receiver sends this payload
+  as reply to the request it MUST fill this field.
+
+o Port (4 bytes) - The port where the SKE protocol is bound.
+  The sender MAY fill this field when sending the payload.  If
+  the receiver sends this payload as reply to the request it 
+  MUST fill this field.  This is a 32 bit MSB first order value.
+.in 3
+
+
+After the key material has been received from the SKE protocol it is
+processed as the [SILC3] describes.  If the key material is used as
+channel private key then the Sending Encryption Key, as defined in
+[SILC3] is used as the channel private key.  Other key material must
+be discarded.  The [SILC1] defines the way to use the key material if
+it is intended to be used as private message keys.  Any other use for
+the key material is undefined.
+
+
+.ti 0
+2.3.21 Resume Router Payload
+
+The payload may only be sent with SILC_PACKET_RESUME_ROUTER packet.  It
+MUST NOT be sent in any other packet type.  The Following diagram
+represents the Resume Router Payload.
+
+         
+.in 5
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Type     |  Session ID   |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 22:  Resume Router Payload
+
+
+.in 6
+o Type (1 byte) - Indicates the type of the backup resume
+  protocol packet.  The type values are defined in [SILC1].
+
+o Session ID (1 bytes) - Indicates the session ID for the
+  backup resume protocol.  The sender of the packet sets this
+  value and the receiver MUST set the same value in subsequent
+  reply packet.
+.in 3
+
+
+.ti 0
+2.3.22 File Transfer Payload
+
+File Transfer Payload is used to perform file transfer protocol
+between two entities in the network.  The actual file transfer
+protocol is always encapsulated inside the SILC Packet.  The actual
+data stream is also sent peer to peer outside SILC network.
+
+When an entity, usually a client wishes to perform file transfer
+protocol with another client in the network, they perform Key Agreement
+protocol as described in the section 2.3.20 Key Agreement Payload and
+in [SILC3], inside File Transfer Payload.  After the Key Agreement
+protocol has been performed the subsequent packets in the data stream
+will be protected using the new key material.  The actual file transfer
+protocol is also initialized in this stage.  All file transfer protocol
+packets are always encapsulated in the File Transfer Payload and
+protected with the negotiated key material.
+
+The payload may only be sent with SILC_PACKET_FTP packet.  It MUST NOT
+be sent in any other packet type.  The following diagram represents the
+File Transfer Payload
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Type      |                                               |
++-+-+-+-+-+-+-+-+                                               +
+|                                                               |
+~                             Data                              ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 23:  File Transfer Payload
+
+
+.in 6
+o Type (1 byte) - Indicates the type of the file transfer
+  protocol.  The following file transfer protocols has been
+  defined:
+
+    1    SSH File Transfer Protocol (SFTP) (mandatory)
+
+  If zero (0) value or any unsupported file transfer protocol
+  type is found in this field the packet must be discarded.
+  The currently mandatory file transfer protocol is SFTP.
+  The SFTP protocol is defined in [SFTP].
+
+o Data (variable length) - Arbitrary file transfer data.  The
+  contents and encoding of this field is dependent of the usage
+  of this payload and the type of the file transfer protocol.
+  When this payload is used to perform the Key Agreement 
+  protocol, this field include the Key Agreement Payload,
+  as defined in the section 2.3.20 Key Agreement Payload.
+  When this payload is used to send the actual file transfer
+  protocol data, the encoding is defined in the corresponding
+  file transfer protocol.
+.in 3
+
+
+
+
+.ti 0
+2.4 SILC ID Types
+
+ID's are extensively used in the SILC network to associate different
+entities.  The following ID's has been defined to be used in the SILC
+network.
+
+.in 6
+0    No ID
+
+     When ever specific ID cannot be used this is used.
+
+1    Server ID
+
+     Server ID to associate servers.  See the format of
+     this ID in [SILC1].
+
+2    Client ID
+
+     Client ID to associate clients.  See the format of
+     this ID in [SILC1].
+
+3    Channel ID
+
+     Channel ID to associate channels.  See the format of
+     this ID in [SILC1].
+.in 3
+
+When encoding different IDs into the ID Payload, all fields are always
+in MSB first order.  The IP address, port, and/or the random number
+are encoded in the MSB first order.
+
+
+.ti 0
+2.5 Packet Encryption And Decryption
+
+SILC packets are encrypted almost entirely.  Only small part of SILC
+header is not encrypted as described in section 5.2 SILC Packet Header.
+The SILC Packet header is the first part of a packet to be encrypted
+and it is always encrypted with the key of the next receiver of the
+packet.  The data payload area of the packet is always entirely 
+encrypted and it is usually encrypted with the next receiver's key.
+However, there are some special packet types and packet payloads
+that require special encryption process.  These special cases are
+described in the next sections.  First is described the normal packet
+encryption process.
+
+
+.ti 0
+2.5.1 Normal Packet Encryption And Decryption
+
+Normal SILC packets are encrypted with the session key of the next
+receiver of the packet.  The entire SILC Packet header and the packet
+data payload is is also encrypted with the same key.  Padding of the
+packet is also encrypted always with the session key, also in special
+cases.  Computed MAC of the packet must not be encrypted.
+
+Decryption process in these cases are straightforward.  The receiver
+of the packet MUST first decrypt the SILC Packet header, or some parts
+of it, usually first 16 bytes of it.  Then the receiver checks the
+packet type from the decrypted part of the header and can determine
+how the rest of the packet must be decrypted.  If the packet type is
+any of the special cases described in the following sections the packet
+decryption is special.  If the packet type is not among those special
+packet types rest of the packet can be decrypted with the same key.
+
+With out a doubt, this sort of decryption processing causes some
+overhead to packet decryption, but never the less, is required.
+
+
+.ti 0
+2.5.2 Channel Message Encryption And Decryption
+
+Channel Messages (Channel Message Payload) are always encrypted with
+the channel specific key.  However, the SILC Packet header is not 
+encrypted with that key.  As in normal case, the header is encrypted
+with the key of the next receiver of the packet, who ever that might
+be.  Note that in this case the encrypted data area is not touched
+at all; it MUST NOT be re-encrypted with the session key.
+
+Receiver of a channel message, who ever that is, is REQUIRED to decrypt
+the SILC Packet header to be able to even recognize the packet to be as
+channel message.  This is same procedure as for normal SILC packets.
+As the receiver founds the packet to be channel message, rest of the
+packet processing is special.  Rest of the SILC Packet header is
+decrypted with the same session key along with the padding of the
+packet.  After that the packet is protected with the channel specific
+key and thus can be decrypted only if the receiver is the client on
+the channel.  See section 2.7 Packet Padding Generation for more
+information about padding on special packets.
+
+If the receiver of the channel message is router which is routing the
+message to another router then it MUST decrypt the Channel Message
+payload.  Between routers (that is, between cells) channel messages
+are protected with session keys shared between the routers.  This
+causes another special packet processing for channel messages.  If
+the channel message is received from another router then the entire
+packet, including Channel Message payload, MUST be encrypted with the
+session key shared between the routers.  In this case the packet
+decryption process is as with normal SILC packets.  Hence, if the
+router is sending channel message to another router the Channel
+Message payload MUST have been decrypted and MUST be re-encrypted
+with the session key shared between the another router.  In this
+case the packet encryption is as with any normal SILC packet.
+
+It must be noted that this is only when the channel messages are sent
+from router to another router.  In all other cases the channel
+message encryption and decryption is as described above.  This
+different processing of channel messages with router to router
+connection is because channel keys are cell specific.  All cells have
+their own channel keys thus the channel message traveling from one
+cell to another MUST be protected as it would be any normal SILC
+packet.
+
+If the SILC_CMODE_PRIVKEY channel mode has been set for the channel
+then the router cannot decrypt the packet as it does not know the
+private key.  In this case the entire packet MUST be encrypted with
+the session key and sent to the router.  The router receiving the
+packet MUST check the channel mode and decrypt the packet accordingly.
+
+
+.ti 0
+2.5.3 Private Message Encryption And Decryption
+
+By default, private message in SILC are protected by session keys.
+In this case the private message encryption and decryption process is
+equivalent to normal packet encryption and decryption.
+
+However, private messages MAY be protected with private message key
+which causes the packet to be special packet.  The procedure in this
+case is very much alike to channel packets.  The actual private message
+is encrypted with the private message key and other parts of the
+packet is encrypted with the session key.  See 2.7 Packet Padding
+Generation for more information about padding on special packets.
+
+The difference from channel message processing is that server or router
+en route never decrypts the actual private message, as it does not
+have the key to do that.  Thus, when sending packets between router
+the processing is same as in any other case as well; the packet's header
+and padding is protected by the session key and the data area is not
+touched.
+
+The true receiver of the private message, client, that is, is able
+to decrypt the private message as it shares the key with the sender
+of the message.
+
+
+.ti 0
+2.6 Packet MAC Generation
+
+Data integrity of a packet is protected by including a message
+authentication code (MAC) at the end of the packet.  The MAC is computed
+from shared secret MAC key, that is established by the SILC Key Exchange
+protocol, from packet sequence number, and from the original contents
+of the packet.  The MAC is always computed before the packet is
+encrypted, although after it is compressed if compression is used.
+
+The MAC is computed from entire packet.  Every bit of data in the packet,
+including SILC Packet Header is used in the MAC computing.  This way
+the entire packet becomes authenticated.
+
+If the packet is special packet MAC is computed from the entire packet
+but part of the packet may be encrypted before the MAC is computed.
+This is case, for example, with channel messages where the message data
+is encrypted with key that server may not now.  In this case the MAC
+has been computed from the encrypted data.
+
+Hence, packet's MAC generation is as follows:
+
+  mac = MAC(key, sequence number | SILC packet)
+
+The MAC key is negotiated during the SKE protocol.  The sequence number
+is a 32 bit MSB first value starting from zero for first packet and
+increasing for subsequent packets, finally wrapping after 2^32 packets.
+The value is never reset, not even after rekey has been performed.  Note
+that the sequence number is incremented only when MAC is computed for a
+packet.  If packet is not encrypted and MAC is not computed then the
+sequence number is not incremented.  Hence, the sequence number is zero
+for first encrypted packet.
+
+See [SILC1] for defined and allowed MAC algorithms.
+
+
+.ti 0
+2.7 Packet Padding Generation
+
+Padding is needed in the packet because the packet is encrypted.  It
+MUST always be multiple by eight (8) or multiple by the block size
+of the cipher, which ever is larger.  The padding is always encrypted.
+
+For normal packets the padding is added after the SILC Packet Header
+and between the Data Payload area.  The padding for normal packets
+may be calculated as follows:
+
+.in 6
+padding length = 16 - (packet_length mod block_size)
+.in 3
+
+The `block_size' is the block size of the cipher.  The maximum padding
+length is 128 bytes, and minimum is 1 byte.  The above algorithm calculates
+the padding to the next block size, and always returns the padding
+length between 1 - 16 bytes.  However, implementations may add padding
+up to 128 bytes.  For example packets that include a passphrase or a
+password for authentication purposes SHOULD pad the packet up to the
+maximum padding length.
+
+For special packets the padding calculation is different as special
+packets may be encrypted differently.  In these cases the encrypted
+data area MUST already be multiple by the block size thus in this case
+the padding is calculated only for SILC Packet Header, not for any
+other area of the packet.  The same algorithm works in this case as
+well, except that the `packet length' is now the SILC Packet Header
+length. 
+
+The padding MUST be random data, preferably, generated by 
+cryptographically strong random number generator.
+
+
+.ti 0
+2.8 Packet Compression
+
+SILC Packets MAY be compressed.  In this case the data payload area
+is compressed and all other areas of the packet MUST remain as they
+are.  After compression is performed for the data area, the length
+field of Packet Header MUST be set to the compressed length of the
+data.
+
+The compression MUST always be applied before encryption.  When
+the packet is received and decrypted the data area MUST be decompressed.
+Note that the true sender of the packet MUST apply the compression and
+the true receiver of the packet MUST apply the decompression.  Any
+server or router en route MUST NOT decompress the packet.
+
+
+.ti 0
+2.9 Packet Sending
+
+The sender of the packet MUST assemble the SILC Packet Header with
+correct values.  It MUST set the Source ID of the header as its own
+ID, unless it is forwarding the packet.  It MUST also set the Destination
+ID of the header to the true destination.  If the destination is client
+it will be Client ID, if it is server it will be Server ID and if it is
+channel it will be Channel ID.
+
+If the sender wants to compress the packet it MUST apply the
+compression now.  Sender MUST also compute the padding as described
+in above sections.  Then sender MUST compute the MAC of the packet.
+
+Then sender MUST encrypt the packet as has been described in above
+sections according whether the packet is normal packet or special
+packet.  The computed MAC MUST NOT be encrypted.
+
+
+.ti 0
+2.10 Packet Reception
+
+On packet reception the receiver MUST check that all fields in the
+SILC Packet Header are valid.  It MUST check the flags of the
+header and act accordingly.  It MUST also check the MAC of the packet
+and if it is to be failed the packet MUST be discarded.  Also if the
+header of the packet includes any bad fields the packet MUST be
+discarded.
+
+See above sections on the decryption process of the received packet.
+The receiver MUST also check that the ID's in the header are valid
+ID's.  Unsupported ID types or malformed ID's MUST cause packet
+rejection.  The padding on the reception is always ignored.
+
+The receiver MUST also check the packet type and start parsing the
+packet according to the type.  However, note the above sections on
+special packet types and their parsing.
+
+
+.ti 0
+2.11 Packet Routing
+
+Routers are the primary entities in the SILC network that takes care
+of packet routing.  However, normal servers routes packets as well, for
+example, when they are routing channel message to the local clients.
+Routing is quite simple as every packet tells the true origin and the
+true destination of the packet.
+
+It is still RECOMMENDED for routers that has several routing connections
+to create route cache for those destinations that has faster route than
+the router's primary route.  This information is available for the router
+when other router connects to the router.  The connecting party then
+sends all of its locally connected clients, servers and channels.  These
+informations helps to create the route cache.  Also, when new channels
+are created to a cell its information is broadcasted to all routers
+in the network.  Channel ID's are based on router's ID thus it is easy
+to create route cache based on these informations.  If faster route for
+destination does not exist in router's route cache the packet MUST be
+routed to the primary route (default route).
+
+However, there are some issues when routing channel messages to group
+of users.  Routers are responsible of routing the channel message to
+other routers, local servers and local clients as well.  Routers MUST
+send the channel message to only one router in the network, preferrably
+to the shortest route to reach the channel users.  The message can be
+routed into either upstream or downstream.  After the message is sent
+to a router in the network it MUST NOT be sent to any other router in
+either same route or other route.  The message MUST NOT be routed to
+the router it came from.
+
+When routing for example private messages they should be routed to the
+shortest route always to reach the destination client as fast as possible.
+
+For server which receives a packet to be routed to its locally connected
+client the server MUST check whether the particular packet type is
+allowed to be routed to the client.  Not all packets may be sent by
+some odd entity to client that is indirectly connected to the sender.
+See section 2.3 SILC Packet Types and paragraph about indirectly connected
+entities and sending packets to them.  The section mentions the packets
+that may be sent to indirectly connected entities.  It is clear that
+server cannot send, for example, disconnect packet to client that is not
+directly connected to the server.
+
+Routers form a ring in the SILC network.  However, routers may have other
+direct connections to other routers in the network too.  This can cause
+interesting routing problems in the network.  Since the network is a ring,
+the packets usually should be routed into counter clock-wise direction,
+or if it cannot be used then always clock-wise (primary route) direction.
+Problems may arise when a faster direct route exists and router is routing
+a channel message.  Currently channel messages must be routed either
+in upstream or downstream, they cannot be routed to other direct routes.
+The SILC protocol should have a shortest path discovery protocol, and some
+existing routing protocol, that can handle a ring network with other
+direct routes inside the ring (so called hybrid ring-mesh topology),
+MAY be defined to be used with the SILC protocol.  Additional
+specifications MAY be written on the subject to permeate this 
+specification.
+
+
+.ti 0
+2.12 Packet Broadcasting
+
+SILC packets MAY be broadcasted in SILC network.  However, only router
+server may send or receive broadcast packets.  Client and normal server
+MUST NOT send broadcast packets and they MUST ignore broadcast packets
+if they receive them.  Broadcast packets are sent by setting Broadcast
+flag to the SILC packet header.
+
+Broadcasting packets means that the packet is sent to all routers in
+the SILC network, except to the router that sent the packet.  The router
+receiving broadcast packet MUST send the packet to its primary route.
+The fact that SILC routers may have several router connections can
+cause problems, such as race conditions inside the SILC network, if
+care is not taken when broadcasting packets.  Router MUST NOT send
+the broadcast packet to any other route except to its primary route.
+
+If the primary route of the router is the original sender of the packet
+the packet MUST NOT be sent to the primary route.  This may happen
+if router has several router connections and some other router uses
+the router as its primary route.
+
+Routers use broadcast packets to broadcast for example information
+about newly registered clients, servers, channels etc. so that all the
+routers may keep these informations up to date.
+
+
+.ti 0
+3 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for 
+symmetric and asymmetric keys must be followed in order to maintain the   
+security of this protocol.
+
+
+.ti 0
+4 References
+
+[SILC1]      Riikonen, P., "Secure Internet Live Conferencing (SILC),
+             Protocol Specification", Internet Draft, April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+[SFTP]       Ylonen T., and Lehtinen S., "Secure Shell File Transfer
+             Protocol", Internet Draft, March 2001.
+
+.ti 0
+5 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires XXX
index a37c0a5181e7fe681a09f20867b8d3ec5fbb9d5a..5bd3c04c3b9950932db90f4925fd166d02ea460c 100644 (file)
@@ -8,44 +8,45 @@
 .ds RF FORMFEED[Page %]
 .ds CF
 .ds LH Internet Draft
-.ds RH 27 June 2000
-.ds CH Secure Internet Live Conferencing
+.ds RH 13 September 2000
+.ds CH
 .na
 .hy 0
 .in 0
 .nf
 Network Working Group                                      P. Riikonen
 Internet-Draft
-draft-riikonen-silc-spec-00.txt                           27 June 2000
-Expires: 27 Jan 2001
+draft-riikonen-silc-spec-00.txt                      13 September 2000
+Expires: 13 May 2001
 
 .in 3
 
-.ce 2
+.ce 3
 Secure Internet Live Conferencing (SILC),
 Protocol Specification
+<draft-riikonen-silc-spec-00.txt>
 
 .ti 0
 Status of this Memo
 
-This document is an Internet-Draft.  Internet-Drafts are working
-documents of the Internet Engineering Task Force (IETF), its areas,
-and its working groups.  Note that other groups may also distribute
-working documents as Internet-Drafts.
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
 
-Internet-Drafts are draft documents valid for a maximum of six
-months and may be updated, replaced, or obsoleted by other 
-documents at any time. It is inappropriate to use Internet-Drafts  
-as reference material or to cite them other than as 
-``work in progress.''
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
 
-To learn the current status of any Internet-Draft, please check the
-``1id-abstracts.txt'' listing contained in the Internet-Drafts
-Shadow Directories on ftp.is.co.za (Africa), nic.nordu.net (Europe),
-munnari.oz.au (Pacific Rim), ds.internic.net (US East Coast), or
-ftp.isi.edu (US West Coast).
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
 
-The distribution of this memo is unlimited.
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
 
 
 .ti 0
@@ -80,50 +81,51 @@ Table of Contents
 3 SILC Specification ............................................  7
   3.1 Client ....................................................  7
       3.1.1 Client ID ...........................................  8
-  3.2 Server ....................................................  8
+  3.2 Server ....................................................  9
       3.2.1 Server's Local ID List ..............................  9
       3.2.2 Server ID ........................................... 10
-      3.2.3 SILC Server Ports ................................... 10
+      3.2.3 SILC Server Ports ................................... 11
   3.3 Router .................................................... 11
       3.3.1 Router's Local ID List .............................. 11
       3.3.2 Router's Global ID List ............................. 12
-      3.3.3 Router's Server ID .................................. 12
-  3.4 Channels .................................................. 12
-      3.4.1 Channel ID .......................................... 13
+      3.3.3 Router's Server ID .................................. 13
+  3.4 Channels .................................................. 13
+      3.4.1 Channel ID .......................................... 14
   3.5 Operators ................................................. 14
-  3.6 SILC Commands ............................................. 14
+  3.6 SILC Commands ............................................. 15
   3.7 SILC Packets .............................................. 15
-  3.8 Packet Encryption ......................................... 15
+  3.8 Packet Encryption ......................................... 16
       3.8.1 Determination of the Source and the Destination ..... 16
-      3.8.2 Client To Client .................................... 16
-      3.8.3 Client To Channel ................................... 17
-      3.8.4 Server To Server .................................... 18
-  3.9 Key Exchange And Authentication ........................... 18
+      3.8.2 Client To Client .................................... 17
+      3.8.3 Client To Channel ................................... 18
+      3.8.4 Server To Server .................................... 19
+  3.9 Key Exchange And Authentication ........................... 19
   3.10 Algorithms ............................................... 19
       3.10.1 Ciphers ............................................ 19
       3.10.2 Public Key Algorithms .............................. 20
       3.10.3 MAC Algorithms ..................................... 20
-      3.10.4 Compression Algorithms ............................. 20
+      3.10.4 Compression Algorithms ............................. 21
   3.11 SILC Public Key .......................................... 21
-4 SILC Procedures ............................................... 23
-  4.1 Creating Client Connection ................................ 23
-  4.2 Creating Server Connection ................................ 24
-  4.3 Joining to a Channel ...................................... 25
-  4.4 Channel Key Generation .................................... 26
+  3.12 SILC Version Detection ................................... 24
+4 SILC Procedures ............................................... 24
+  4.1 Creating Client Connection ................................ 24
+  4.2 Creating Server Connection ................................ 25
+  4.3 Joining to a Channel ...................................... 26
+  4.4 Channel Key Generation .................................... 27
   4.5 Private Message Sending and Reception ..................... 27
-  4.6 Private Message Key Generation ............................ 27
-  4.7 Channel Message Sending and Reception ..................... 28
-  4.8 Session Key Regeneration .................................. 28
+  4.6 Private Message Key Generation ............................ 28
+  4.7 Channel Message Sending and Reception ..................... 29
+  4.8 Session Key Regeneration .................................. 29
   4.9 Command Sending and Reception ............................. 29
-5 SILC Commands ................................................. 29
-  5.1 SILC Commands Syntax ...................................... 29
-  5.2 SILC Commands List ........................................ 31
+5 SILC Commands ................................................. 30
+  5.1 SILC Commands Syntax ...................................... 30
+  5.2 SILC Commands List ........................................ 32
   5.3 SILC Command Status Types ................................. 53
       5.3.1 SILC Command Status Payload ......................... 53
       5.3.2 SILC Command Status List ............................ 54
-6 Security Considerations ....................................... 58
-7 References .................................................... 58
-8 Author's Address .............................................. 59
+6 Security Considerations ....................................... 59
+7 References .................................................... 59
+8 Author's Address .............................................. 60
 
 
 .ti 0
@@ -143,11 +145,12 @@ Figure 5:  SILC Command Status Payload
 This document describes a Secure Internet Live Conferencing (SILC)
 protocol which provides secure conferencing services over insecure
 network channel.  SILC is IRC [IRC] like protocol, however, it is 
-not equivalent to IRC and does not support IRC.  Strong cryptographic
-methods are used to protect SILC packets inside SILC network.  Two
-other Internet Drafts relates very closely to this memo;  SILC Packet
-Protocol [SILC2] and SILC Key Exchange and Authentication Protocols
-[SILC3].
+not equivalent to IRC and does not support IRC.
+
+Strong cryptographic methods are used to protect SILC packets inside
+SILC network.  Two other Internet Drafts relates very closely to this
+memo; SILC Packet Protocol [SILC2] and SILC Key Exchange and
+Authentication Protocols [SILC3].
 
 The protocol uses extensively packets as conferencing protocol 
 requires message and command sending.  The SILC Packet Protocol is
@@ -189,7 +192,8 @@ clear.
 SILC network is a cellular network as opposed to tree style network 
 topology.  The rationale for this is to have servers that can perform 
 specific kind of tasks what other servers cannot perform.  This leads 
-to two kinds of servers; normal SILC servers and SILC routers.  
+to two kinds of servers; normal SILC servers and SILC routers.
+
 A difference between normal server and router server is that routers 
 knows everything about everything in the network.  They also do the 
 actual routing of the messages to the correct receiver.  Normal servers 
@@ -199,9 +203,25 @@ keep global information up to date at all time.
 
 This, on the other hand, leads to cellular like network, where routers 
 are in the centrum on the cell and servers are connected to the router.
+
 Following diagram represents SILC network topology.
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 .in 8
 .nf
   ---- ---- ----         ---- ---- ----
@@ -326,9 +346,6 @@ when clients are connected directly to the routers and the messages
 are delivered from one router to the other router.
 
 
-
-
-
 .ti 0 
 2.4 Channel Communication
 
@@ -402,7 +419,7 @@ o Server ID IP address - Indicates the server where this
   client is coming from.  The IP address hence equals the
   server IP address where to the client has connected.
 
-o Random number - Random number to further unify the
+o Random number - Random number to further randomize the
   Client ID.  This makes it possible to have 2^8 same
   nicknames from the same server IP address.
 
@@ -462,6 +479,13 @@ of creating the Client ID's for their clients.
 Normal server also keeps information about locally created channels and
 their Channel ID's.
 
+
+
+
+
+
+
+
 Hence, local list for normal server includes:
 
 .in 6
@@ -481,8 +505,6 @@ client list        - All clients in server
    o Sending key
    o Receiving key
 
-
-
 channel list       - All channels in server
    o Channel name
    o Channel ID
@@ -513,7 +535,7 @@ o IP address of the server - This is the real IP address of
 
 o Port - This is the port the server is binded to.
 
-o Random number - This is used to further unify the Server ID.
+o Random number - This is used to further randomize the Server ID.
 
 .in 3
 Collisions are not expected to happen in any conditions.  The Server ID
@@ -524,13 +546,15 @@ distributing it to the router.
 .ti 0
 3.2.3 SILC Server Ports
 
-SILC uses currently TCP port 334 on SILC network.  However, this is not
-official port assigned for SILC.  Official port has been requested by 
-the IANA.
+Following ports has been assigned by IANA for the SILC protocol:
+
+.in 10
+silc            706/tcp    SILC
+silc            706/udp    SILC
+.in 3
 
 If there are needs to create new SILC networks in the future the port
-numbers must be officially assigned by the IANA.  Most convenience case
-would be to assign port numbers upwards from 334.
+numbers must be officially assigned by the IANA.
 
 Server on network above privileged ports (>1023) should not be trusted
 as they could have been set up by untrusted party.
@@ -565,6 +589,10 @@ information about user's nickname, username and hostname and real name
 since these are not needed by the router.  Router keeps only information
 that it needs.
 
+
+
+
+
 Hence, local list for router includes:
 
 .in 6
@@ -616,6 +644,7 @@ server list        - All servers in SILC
    o Server ID
    o Router's Server ID
 
+
 client list        - All clients in SILC
    o Client ID
 
@@ -707,7 +736,7 @@ o Router's Server ID IP address - Indicates the IP address of
 o Router's Server ID port - Indicates the port of the channel on 
   the server.  This is taken from the router's Server ID.
 
-o Random number - To further unify the Channel ID.  This makes
+o Random number - To further randomize the Channel ID.  This makes
   sure that there are no collisions.  This also means that
   in a cell there can be 2^16 channels.
 .in 3
@@ -738,7 +767,10 @@ to set nickname, join to channel, change modes and many other things.
 Client usually sends the commands and server replies by sending a reply
 packet to the command.  Server may also send commands usually to serve
 the original client's request.  However, server may not send command
-to client and there are some commands that server must not send.
+to client and there are some commands that server must not send.  Server
+is also able to send the forwarded command packets.  For example, 
+SILC_COMMAND_JOIN is always forwarded packet.  See [SILC2] for more
+about packet forwarding.
 
 Note that the command reply is usually sent only after client has sent
 the command request but server is allowed to send command reply packet
@@ -770,6 +802,8 @@ in [SILC2].  This document does not define or describe details of
 SILC packets.
 
 
+
+
 .ti 0
 3.8 Packet Encryption
 
@@ -968,6 +1002,11 @@ in the SILC packets.  See [SILC2] of the actual encryption process and
 definition of how it must be done.  SILC has a mandatory algorithm that
 must be supported in order to be compliant with this protocol.
 
+
+
+
+
+
 Following ciphers are defined in SILC protocol:
 
 .in 6
@@ -993,10 +1032,6 @@ Additional ciphers may be defined to be used in SILC by using the
 same name format as above.
 
 
-
-
-
-
 .ti 0
 3.10.2 Public Key Algorithms
 
@@ -1023,6 +1058,10 @@ Data integrity is protected by computing a message authentication code
 (MAC) of the packet data.  See [SILC2] for details how to compute the
 MAC.
 
+
+
+
+
 Following MAC algorithms are defined in SILC protocol:
 
 .in 6
@@ -1074,6 +1113,11 @@ and to perform other tasks related to public key cryptography.
 The format of the SILC Public Key is as follows:
 
 
+
+
+
+
+
 .in 5
 .nf
                      1                   2                   3
@@ -1176,6 +1220,39 @@ All fields in the public key are in MSB (most significant byte first)
 order.
 
 
+.ti 0
+3.12 SILC Version Detection
+
+The version detection of both client and server is performed at the
+connection phase while executing the SILC Key Exchange protocol.  The
+version identifier is exchanged between intiator and responder.  The
+version identifier is of following format:
+
+.in 6
+SILC-<protocol version>-<software version>
+.in 3
+
+The version strings are of following format:
+
+.in 6
+protocol version = <major>.<minor>
+software version = <major>[.<minor>[.<build>]]
+.in 3
+
+Protocol version may provide both major and minor version.  Currently
+implementations must set the protocol version and accept the protocol
+version as SILC-1.0-<sotware version>. 
+
+Software version may provide major, minor and build version.  The
+software version may be freely set and accepted.
+
+Thus, the version string could be, for example:
+
+.in 6
+SILC-1.0-1.2
+.in 3
+
+
 .ti 0
 4 SILC Procedures
 
@@ -1298,7 +1375,7 @@ newly joined channel is sent to the router.  The new channel key is
 also distributed to the router and to all clients on the channel.
 
 If the channel does not exist in the local list the command must be
-sent to the router which will then perform the actual joining
+fowarded to the router which will then perform the actual joining
 procedure.  When server receives the reply to the command from the
 router it must be distributed to the client who sent the command
 originally.  Server will also receive the channel key from the server
@@ -1421,6 +1498,8 @@ case by default in SILC, the private messages are secured by using
 normal session keys established by SILC Key Exchange protocol.
 
 
+
+
 .ti 0
 4.7 Channel Message Sending and Reception
 
@@ -1519,6 +1598,10 @@ possible to have multiple optional arguments in commands and in
 command replies.  The number of argument is marked in parentheses
 before the actual argument.
 
+
+
+
+
 .in 6
 Example:  Arguments:  (1) <nickname> (2) <username@host>
 .in 3
@@ -1570,6 +1653,8 @@ Every command reply with <Status Payload>, it is mandatory
 argument for all command replies and for this reason it is not
 described in the command reply descriptions.
 
+
+
 Status messages:
 
     SILC_STATUS_OK
@@ -1600,19 +1685,23 @@ List of all defined commands in SILC follows.
         None.  This is reserved command and must not be sent.
 
 
-   2    SILC_COMMAND_WHOIS
+   1    SILC_COMMAND_WHOIS
 
-        Max Arguments:  2
-            Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
+        Max Arguments:  3
+            Arguments:  (1) <nickname>[@<server>]  (2) [<Client ID>]
+                        (3) [<count>]
+
+        Whois command is used to query various information about specific
+        user.  The user maybe requested by their nickname and server name.
+        The query may find multiple matching users as there are no unique
+        nicknames in the SILC.  The <count> option maybe given to narrow
+        down the number of accepted results.  If this is not defined there
+        are no limit of accepted results.  The query may also be narrowed
+        down by defining the server name of the nickname.
 
-        Whois.  Whois command is used to query various information about
-        specific user.  The user maybe requested by their nickname and
-        server name.  The query may find multiple matching users as
-        there are no unique nicknames in the SILC.  The <count> option
-        maybe given to narrow down the number of accepted results.  If
-        this is not defined there are no limit of accepted results.
-        The query may also be narrowed down by defining the server name
-        of the nickname.
+        It is also possible to search the user by Client ID.  If <Client ID>
+        is provided server must use it as the search value instead of
+        the <nickname>.
 
         To prevent miss-use of this service wildcards in the nickname
         or in the servername are not permitted.  It is not allowed
@@ -1658,7 +1747,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_TOO_MANY_PARAMS
 
 
-   3    SILC_COMMAND_WHOWAS
+   2    SILC_COMMAND_WHOWAS
 
         Max Arguments:  2
             Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
@@ -1707,7 +1796,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_TOO_MANY_PARAMS
 
 
-   4    SILC_COMMAND_IDENTIFY
+   3    SILC_COMMAND_IDENTIFY
 
         Max Arguments:  2
             Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
@@ -1730,7 +1819,7 @@ List of all defined commands in SILC follows.
         be based on specific nickname request.
 
         Implementations may not want to give interface access to this
-        commands as it is hardly a command that would be used a end user.
+        command as it is hardly a command that would be used a end user.
         However, it must be implemented as it is used with private message
         sending.
 
@@ -1766,7 +1855,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_TOO_MANY_PARAMS
 
 
-   5    SILC_COMMAND_NICK
+   4    SILC_COMMAND_NICK
 
         Max Arguments:  1
             Arguments:  (1) <nickname>
@@ -1800,7 +1889,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_TOO_MANY_PARAMS
 
 
-   6    SILC_COMMAND_LIST
+   5    SILC_COMMAND_LIST
 
         Max Arguments:  2
             Arguments:  (1) [<Channel ID>] [<server>]
@@ -1817,9 +1906,9 @@ List of all defined commands in SILC follows.
 
         Reply messages to the command:
 
-        Max Arguments:  3
-            Arguments:  (1) <Status Payload>  (2) <channel>
-                        (3) <topic>
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel>         (4) <topic>
 
         This command may reply with several command reply messages to form
         a list of results.  In this case the status payload will include
@@ -1827,7 +1916,7 @@ List of all defined commands in SILC follows.
         the last reply to indicate the end of the list.  If there are only 
         one reply the status is set to normal STATUS_OK.
 
-        This command replies with channel name and the topic of the
+        This command replies with Channel ID, name and the topic of the
         channel.  If the channel is private channel the <topic> includes
         "*private*" string.
 
@@ -1844,7 +1933,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_SUCH_SERVER
 
 
-   7    SILC_COMMAND_TOPIC
+   6    SILC_COMMAND_TOPIC
 
         Max Arguments:  2
             Arguments:  (1) <Channel ID>  (2) [<server>]]
@@ -1878,16 +1967,19 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CHANNEL_PRIV
 
 
-   8    SILC_COMMAND_INVITE
+   7    SILC_COMMAND_INVITE
 
         Max Arguments:  2
-            Arguments:  (1) <Client ID>  (2) <channel>
+            Arguments:  (1) <Client ID>  (2) <Channel ID>
 
         This command is used to invite other clients to join to the
-        channel.  There is no requirement that the channel the target
-        client is being invited to must exist or be a valid channel.
-        The <Client ID> argument is the target client's ID that is being
-        invited.
+        channel.  The <Client ID> argument is the target client's ID that
+        is being invited.  The <Channel ID> is the Channel ID of the
+        requested channel.  The sender of this command must be on the
+        channel.  This command must fail if the requested channel does
+        not exist, the requested client is already on the channel or if
+        the channel is invite only channel and the caller of this command
+        does not have at least channel operator privileges.
 
         Reply messages to the command:
 
@@ -1899,18 +1991,18 @@ List of all defined commands in SILC follows.
         Status messages:
 
             SILC_STATUS_OK
-            SILC_STATUS_ERR_NOT_ON_CHANNEL
-            SILC_STATUS_ERR_WILDCARDS
             SILC_STATUS_ERR_NOT_REGISTERED
             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
-            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
             SILC_STATUS_ERR_TOO_MANY_PARAMS
-            SILC_STATUS_ERR_NO_RECIPIENT
-            SILC_STATUS_ERR_USER_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
             SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
 
 
-   9    SILC_COMMAND_QUIT
+   8    SILC_COMMAND_QUIT
 
         Max Arguments:  1
             Arguments:  (1) [<quit message>]
@@ -1925,7 +2017,7 @@ List of all defined commands in SILC follows.
         This command does not reply anything.
 
 
-   10   SILC_COMMAND_KILL
+    9   SILC_COMMAND_KILL
 
         Max Arguments:  2
             Arguments:  (1) <Client ID>  (2) [<comment>]
@@ -1955,7 +2047,10 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CLIENT_ID
 
 
-   11   SILC_COMMAND_INFO
+
+
+
+   10   SILC_COMMAND_INFO
 
         Max Arguments:  1
             Arguments:  (1) [<server>]
@@ -1978,14 +2073,12 @@ List of all defined commands in SILC follows.
             SILC_STATUS_OK
             SILC_STATUS_ERR_WILDCARDS
             SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
             SILC_STATUS_ERR_TOO_MANY_PARAMS
-            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
             SILC_STATUS_ERR_NO_SUCH_SERVER
 
 
-
-
-   12   SILC_COMMAND_CONNECT
+   11   SILC_COMMAND_CONNECT
 
         Max Arguments:  2
             Arguments:  (1) <Server ID>  
@@ -2004,6 +2097,8 @@ List of all defined commands in SILC follows.
 
         This command replies only with Status Payload.
 
+
+
         Status messages:
 
             SILC_STATUS_OK
@@ -2016,15 +2111,15 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_ROUTER_PRIV
 
 
-   13   SILC_COMMAND_PING
+   12   SILC_COMMAND_PING
 
         Max Arguments:  1
             Arguments:  (1) <Server ID>
 
-        This command is used by clients to test the communication
-        channel to its server if client suspects that the communication
-        is not working correctly.  The <Server ID> is the ID of the
-        server the client is connected to.
+        This command is used by client and server to test the communication
+        channel to its server if one suspects that the communication is not
+        working correctly.  The <Server ID> is the ID of the server the
+        sender is connected to.
 
         Reply messages to the command:
 
@@ -2039,11 +2134,12 @@ List of all defined commands in SILC follows.
             SILC_STATUS_OK
             SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
             SILC_STATUS_ERR_TOO_MANY_PARAMS
-            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
             SILC_STATUS_ERR_NOT_REGISTERED
 
 
-   14   SILC_COMMAND_OPER
+   13   SILC_COMMAND_OPER
 
         Max Arguments:  2
             Arguments:  (1) <username>  (2) <authentication data>
@@ -2078,7 +2174,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_AUTH_FAILED
 
 
-   15   SILC_COMMAND_JOIN
+   14   SILC_COMMAND_JOIN
 
         Max Arguments:  3
             Arguments:  (1) <channel>  (2) [<passphrase>] 
@@ -2086,9 +2182,10 @@ List of all defined commands in SILC follows.
 
         Join to channel/create new channel.  This command is used to
         join to a channel.  If the channel does not exist the channel is
-        created on the server receiving the join request.  The channel 
-        may be protected with passphrase.  If this is the case the 
-        passphrase must be sent along the join command.
+        created.  If server is normal server this command must be forwarded
+        to router who will create the channel.  The channel may be protected
+        with passphrase.  If this is the case the passphrase must be sent
+        along the join command.
 
         The name of the <channel> must not include any spaces (` '),
         non-printable characters, commas (`,') or any wildcard characters.
@@ -2148,7 +2245,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_USER_ON_CHANNEL
 
 
-   16   SILC_COMMAND_MOTD
+   15   SILC_COMMAND_MOTD
 
         Max Arguments:  1
             Arguments:  (1) <server>
@@ -2171,7 +2268,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_SUCH_SERVER
 
 
-   17   SILC_COMMAND_UMODE
+   16   SILC_COMMAND_UMODE
 
         Max Arguments:  2
             Arguments:  (1) <Client ID>  (2) <client mode mask>
@@ -2218,6 +2315,7 @@ List of all defined commands in SILC follows.
         This command replies with the changed client mode mask that
         the client is required to keep locally.
 
+
         Status messages:
 
             SILC_STATUS_OK
@@ -2232,12 +2330,13 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CLIENT_ID
 
 
-   18   SILC_COMMAND_CMODE
+   17   SILC_COMMAND_CMODE
 
-        Max Arguments:  6
+        Max Arguments:  8
             Arguments:  (1) <Channel ID>    (2) <channel mode mask>
                         (3) [<user limit>]  (4) [<passphrase>]
-                        (5) [<Client ID>]   (6) [<cipher>[:<key len>]]
+                        (5) [<ban mask>]    (6) [<invite list>]
+                        (7) [<Client ID>]   (8) [<cipher>[:<key len>]]
 
         This command is used by client to set or change channel flags on
         a channel.  Channel has several modes that set various properties
@@ -2375,8 +2474,22 @@ List of all defined commands in SILC follows.
               Typical implementation would use [+|-]b on user interface
               to set/unset this mode.
 
+
+           0x0100    SILC_CMODE_INVITE
+
+              Invite list has been set to the channel.  The invite list
+              can be used to mark the clients that is able to join
+              channel without being invited when the channel is set to
+              be invite-only channel.  The <invite list> argument is the
+              set invite mask.  When unsetting entry from the invite list
+              the entry must be provided as argument.  Channel founder and
+              channel operator may set/unset this mode.
+
+              Typical implementation would use [+|-]I on user interface
+              to set/unset this mode.
+
         
-           0x0100    SILC_CMODE_OPERATOR
+           0x0200    SILC_CMODE_OPERATOR
 
               Sets channel operator privileges on the channel for a
               client on the channel.  The <Client ID> argument is the
@@ -2388,7 +2501,7 @@ List of all defined commands in SILC follows.
               to set/unset this mode.
 
 
-           0x0200    SILC_CMODE_CIPHER
+           0x0400    SILC_CMODE_CIPHER
 
               Sets specific cipher to be used to protect channel
               traffic.  The <cipher> argument is the requested cipher.
@@ -2409,6 +2522,7 @@ List of all defined commands in SILC follows.
         all clients on the channel by sending SILC_COMMAND_CMODE command
         reply packet.
 
+
         Reply messages to the command:
 
         Max Arguments:  2
@@ -2434,7 +2548,9 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CLIENT_ID
 
 
-   19   SILC_COMMAND_KICK
+
+
+   18   SILC_COMMAND_KICK
 
         Max Arguments:  3
             Arguments:  (1) <channel>  (2) <Client ID>  
@@ -2465,7 +2581,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CLIENT_ID
 
 
-   20   SILC_COMMAND_RESTART
+   19   SILC_COMMAND_RESTART
 
         Max Arguments:  0
             Arguments:  None
@@ -2480,6 +2596,8 @@ List of all defined commands in SILC follows.
 
         This command replies only with Status Payload.
 
+
+
         Status messages:
 
             SILC_STATUS_OK
@@ -2487,7 +2605,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_SERVER_PRIV
 
 
-   21   SILC_COMMAND_CLOSE
+   20   SILC_COMMAND_CLOSE
 
         Max Arguments:  1
             Arguments:  (1) <Server ID>
@@ -2503,6 +2621,8 @@ List of all defined commands in SILC follows.
 
         This command replies only with Status Payload.
 
+
+
         Status messages:
 
             SILC_STATUS_OK
@@ -2514,7 +2634,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_SUCH_SERVER_ID
 
 
-   22   SILC_COMMAND_DIE
+   21   SILC_COMMAND_DIE
 
         Max Arguments:  0
             Arguments:  None
@@ -2525,6 +2645,8 @@ List of all defined commands in SILC follows.
 
         Reply messages to the command:
 
+
+
         Max Arguments:  1
             Arguments:  (1) <Status Payload>
 
@@ -2537,7 +2659,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_SERVER_PRIV
 
 
-   23   SILC_COMMAND_SILCOPER
+   22   SILC_COMMAND_SILCOPER
 
         Max Arguments:  2
             Arguments:  (1) <username>  (2) <authentication data>
@@ -2577,7 +2699,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_AUTH_FAILED
 
 
-   24   SILC_COMMAND_LEAVE
+   23   SILC_COMMAND_LEAVE
 
         Max Arguments:  1
             Arguments:  (1) <Channel ID>
@@ -2605,7 +2727,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NO_CHANNEL_ID
 
 
-   25   SILC_COMMAND_NAMES
+   24   SILC_COMMAND_NAMES
 
         Max Arguments:  1
             Arguments:  (1) <Channel ID>
@@ -2624,11 +2746,17 @@ List of all defined commands in SILC follows.
 
         Reply messages to the command:
 
-        Max Arguments:  2
-            Arguments:  (1) <Status Payload>  (2) <name list>
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <name list>       (4) <Client ID list>
 
-        This command replies with the comma separated list of users on
-        the channel.
+        This command replies with the Channel ID of the requested channel,
+        comma separated list of users on the channel and Client ID list
+        of the users on the list.  The Client ID list has Client ID's
+        of all users in the list.  First Client ID in the list must be
+        the Client ID of the first user in <name list>.  The Client ID
+        List is formed by adding Client ID's each after each.  Note that
+        the Client ID list is binary data.
 
         Status messages:
 
@@ -2642,7 +2770,7 @@ List of all defined commands in SILC follows.
             SILC_STATUS_ERR_NOT_ON_CHANNEL
 
 
-   26 - 254
+   25 - 254
 
         Currently undefined commands.
 
@@ -2767,125 +2895,129 @@ List of all defined command status messages following.
         "No Channel ID given".  Channel ID were expected as command
         parameter but were not found.
 
-   19   SILC_STATUS_ERR_BAD_CLIENT_ID
+   19   SILC_STATUS_ERR_NO_SERVER_ID
+
+        "No Serve ID given".  Server ID were expected as command
+        parameter but were not found.
+
+   20   SILC_STATUS_ERR_BAD_CLIENT_ID
 
         "Bad Client ID".  Client ID provided were erroneous.
 
-   20   SILC_STATUS_ERR_BAD_CHANNEL_ID
+   21   SILC_STATUS_ERR_BAD_CHANNEL_ID
 
         "Bad Channel ID".  Channel ID provided were erroneous.
 
-   21   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+   22   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
 
         "No such Client ID".  Client ID provided does not exist.
 
 
-
-   22   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+   23   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
 
         "No such Channel ID".  Channel ID provided does not exist.
 
-   23   SILC_STATUS_ERR_NICKNAME_IN_USE
+   24   SILC_STATUS_ERR_NICKNAME_IN_USE
 
         "Nickname already exists".  Nickname created could not be 
         registered because number of same nicknames were already set to
         maximum.  This is not expected to happen in real life but is
         possible to occur.
 
-   24   SILC_STATUS_ERR_NOT_ON_CHANNEL
+   25   SILC_STATUS_ERR_NOT_ON_CHANNEL
 
         "You are not on that channel".  The command were specified for
         client user is not currently on.
 
-   25   SILC_STATUS_ERR_USER_ON_CHANNEL
+   26   SILC_STATUS_ERR_USER_ON_CHANNEL
 
         "User already on channel".  User were invited on channel they
         already are on.
 
-   26   SILC_STATUS_ERR_NOT_REGISTERED
+   27   SILC_STATUS_ERR_NOT_REGISTERED
 
         "You have not registered".  User executed command that requires
         the client to be registered on the server before it may be
         executed.
 
-   27   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+   28   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
 
         "Not enough parameters".  Command requires more parameters
         than provided.
 
-   28   SILC_STATUS_ERR_TOO_MANY_PARAMS
+   29   SILC_STATUS_ERR_TOO_MANY_PARAMS
 
         "Too many parameters".  Too many parameters were provided
         for the command.
 
-   29   SILC_STATUS_ERR_PERM_DENIED
+   30   SILC_STATUS_ERR_PERM_DENIED
 
         "Your host is not among the privileged".  The client tried to
         register on server that does not allow this host to connect.
 
-   30   SILC_STATUS_ERR_BANNED_FROM_SERVER
+   31   SILC_STATUS_ERR_BANNED_FROM_SERVER
 
         "You are banned from this server".  The client tried to register
         on server that has explicitly denied this host to connect.
 
 
 
-   31   SILC_STATUS_ERR_BAD_PASSWORD
+   32   SILC_STATUS_ERR_BAD_PASSWORD
 
         "Cannot join channel. Incorrect password".  Password provided for 
         channel were not accepted.
 
-   32   SILC_STATUS_ERR_CHANNEL_IS_FULL
+   33   SILC_STATUS_ERR_CHANNEL_IS_FULL
 
         "Cannot join channel. Channel is full".  The channel is full
         and client cannot be joined to it.
 
-   33   SILC_STATUS_ERR_NOT_INVITED
+   34   SILC_STATUS_ERR_NOT_INVITED
 
         "Cannot join channel. You have not been invited".  The channel
         is invite only channel and client has not been invited.
 
-   34   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+   35   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
 
         "Cannot join channel. You have been banned".  The client has
         been banned from the channel.
 
-   35   SILC_STATUS_ERR_UNKNOWN_MODE
+   36   SILC_STATUS_ERR_UNKNOWN_MODE
 
         "Unknown mode".  Mode provided by the client were unknown to
         the server.
 
-   36   SILC_STATUS_ERR_NOT_YOU
+   37   SILC_STATUS_ERR_NOT_YOU
 
         "Cannot change mode for other users".  User tried to change
         someone else's mode.
 
-   37   SILC_STATUS_ERR_NO_CHANNEL_PRIV
+   38   SILC_STATUS_ERR_NO_CHANNEL_PRIV
 
         "Permission denied. You are not channel operator".  Command may 
         be executed only by channel operator.
 
-   38   SILC_STATUS_ERR_NO_SERVER_PRIV
+   39   SILC_STATUS_ERR_NO_SERVER_PRIV
 
         "Permission denied. You are not server operator".  Command may
         be executed only by server operator.
 
-   39   SILC_STATUS_ERR_NO_ROUTER_PRIV
+   40   SILC_STATUS_ERR_NO_ROUTER_PRIV
 
         "Permission denied. You are not SILC operator".  Command may be
         executed only by router (SILC) operator.
 
-   40   SILC_STATUS_ERR_BAD_NICKNAME
+   41   SILC_STATUS_ERR_BAD_NICKNAME
 
         "Bad nickname".  Nickname requested contained illegal characters
         or were malformed.
 
-   41   SILC_STATUS_ERR_BAD_CHANNEL
+   42   SILC_STATUS_ERR_BAD_CHANNEL
 
         "Bad channel name".  Channel requested contained illegal characters
         or were malformed.
 
-   42   SILC_STATUS_ERR_AUTH_FAILED
+   43   SILC_STATUS_ERR_AUTH_FAILED
 
         "Authentication failed".  The authentication data sent as 
         argument were wrong and thus authentication failed.
@@ -2899,6 +3031,9 @@ Security is central to the design of this protocol, and these security
 considerations permeate the specification.
 
 
+
+
+
 .ti 0
 7 References
 
@@ -2944,6 +3079,7 @@ considerations permeate the specification.
              Authentication", RFC 2104, February 1997.
 
 
+
 .ti 0
 8 Author's Address
 
@@ -2954,3 +3090,5 @@ Kasarmikatu 11 A4
 Finland
 
 EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 13 May 2001 
diff --git a/doc/draft-riikonen-silc-spec-01.nroff b/doc/draft-riikonen-silc-spec-01.nroff
new file mode 100644 (file)
index 0000000..326c79c
--- /dev/null
@@ -0,0 +1,3581 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 6 October 2000
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-spec-01.txt                         6 October 2000
+Expires: 6 Jun 2001
+
+.in 3
+
+.ce 3
+Secure Internet Live Conferencing (SILC),
+Protocol Specification
+<draft-riikonen-silc-spec-01.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside SILC network.  Two
+other Internet Drafts relates very closely to this memo;  SILC Packet
+Protocol [SILC2] and SILC Key Exchange and Authentication Protocols
+[SILC3].
+
+
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+2 SILC Concepts .................................................  3
+  2.1 SILC Network Topology .....................................  4
+  2.2 Communication Inside a Cell ...............................  5
+  2.3 Communication in the Network ..............................  6
+  2.4 Channel Communication .....................................  7
+  2.5 Router Connections ........................................  7
+  2.6 Backup Routers ............................................ XX
+3 SILC Specification ............................................  8
+  3.1 Client ....................................................  8
+      3.1.1 Client ID ...........................................  9
+  3.2 Server .................................................... 10
+      3.2.1 Server's Local ID List .............................. 10
+      3.2.2 Server ID ........................................... 11 
+      3.2.3 SILC Server Ports ................................... 11
+  3.3 Router .................................................... 12
+      3.3.1 Router's Local ID List .............................. 12
+      3.3.2 Router's Global ID List ............................. 13
+      3.3.3 Router's Server ID .................................. 13
+  3.4 Channels .................................................. 14
+      3.4.1 Channel ID .......................................... 15
+  3.5 Operators ................................................. 15
+  3.6 SILC Commands ............................................. 15
+  3.7 SILC Packets .............................................. 16
+  3.8 Packet Encryption ......................................... 16
+      3.8.1 Determination of the Source and the Destination ..... 17
+      3.8.2 Client To Client .................................... 17
+      3.8.3 Client To Channel ................................... 19
+      3.8.4 Server To Server .................................... 19
+  3.9 Key Exchange And Authentication ........................... 20
+  3.10 Algorithms ............................................... 20
+      3.10.1 Ciphers ............................................ 20
+      3.10.2 Public Key Algorithms .............................. 21
+      3.10.3 Hash Functions ..................................... XXX
+      3.10.4 MAC Algorithms ..................................... XXX
+      3.10.5 Compression Algorithms ............................. XXX
+  3.11 SILC Public Key .......................................... 22
+  3.12 SILC Version Detection ................................... 24
+4 SILC Procedures ............................................... 25
+  4.1 Creating Client Connection ................................ 25
+  4.2 Creating Server Connection ................................ 26
+  4.3 Joining to a Channel ...................................... 27
+  4.4 Channel Key Generation .................................... 28
+  4.5 Private Message Sending and Reception ..................... 29
+  4.6 Private Message Key Generation ............................ 29
+  4.7 Channel Message Sending and Reception ..................... 30
+  4.8 Session Key Regeneration .................................. 30
+  4.9 Command Sending and Reception ............................. 30
+5 SILC Commands ................................................. 31
+  5.1 SILC Commands Syntax ...................................... 31
+  5.2 SILC Commands List ........................................ 33
+  5.3 SILC Command Status Types ................................. 56
+      5.3.1 SILC Command Status Payload ......................... 56
+      5.3.2 SILC Command Status List ............................ 57
+6 Security Considerations ....................................... 61
+7 References .................................................... 61
+8 Author's Address .............................................. 62
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  SILC Network Topology
+Figure 2:  Communication Inside cell
+Figure 3:  Communication Between Cells
+Figure 4:  Router Connections
+Figure 5:  SILC Public Key
+Figure 6:  SILC Command Status Payload
+
+
+.ti 0
+1. Introduction
+
+This document describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.
+
+Strong cryptographic methods are used to protect SILC packets inside
+SILC network.  Two other Internet Drafts relates very closely to this
+memo; SILC Packet Protocol [SILC2] and SILC Key Exchange and
+Authentication Protocols [SILC3].
+
+The protocol uses extensively packets as conferencing protocol 
+requires message and command sending.  The SILC Packet Protocol is
+described in [SILC2] and should be read to fully comprehend this
+document and protocol.  [SILC2] also describes the packet encryption
+and decryption in detail.
+
+The security of SILC protocol and for any security protocol for that
+matter is based on strong and secure key exchange protocol.  The SILC
+Key Exchange protocol is described in [SILC3] along with connection
+authentication protocol and should be read to fully comprehend this
+document and protocol.
+
+The SILC protocol has been developed to work on TCP/IP network
+protocol, although it could be made to work on other network protocols
+with only minor changes.  However, it is recommended that TCP/IP
+protocol is used under SILC protocol.  Typical implementation would
+be made in client-server model.
+
+
+.ti 0
+2. SILC Concepts
+
+This section describes various SILC protocol concepts that forms the 
+actual protocol, and in the end, the actual SILC network.  The mission
+of the protocol is to deliver messages from clients to other clients 
+through routers and servers in secure manner.  The messages may also 
+be delivered from one client to many clients forming a group, also 
+known as a channel.
+
+This section does not focus to security issues, instead basic network 
+concepts are introduced to make the topology of the SILC network 
+clear.
+
+
+.ti 0
+2.1 SILC Network Topology
+
+SILC network is a cellular network as opposed to tree style network 
+topology.  The rationale for this is to have servers that can perform 
+specific kind of tasks what other servers cannot perform.  This leads 
+to two kinds of servers; normal SILC servers and SILC routers.
+
+A difference between normal server and router server is that routers 
+knows everything about everything in the network.  They also do the 
+actual routing of the messages to the correct receiver.  Normal servers 
+knows only about local information and nothing about global information.
+This makes the network faster as there are less servers that needs to 
+keep global information up to date at all time.
+
+This, on the other hand, leads to cellular like network, where routers 
+are in the center of the cell and servers are connected to the router.
+
+The following diagram represents SILC network topology.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.in 8
+.nf
+  ---- ---- ----         ---- ---- ----
+ | S8 | S5 | S4 |       | S7 | S5 | S6 |
+ ----- ---- -----       ----- ---- -----
+| S7 | S/R1 | S2 | --- | S8 | S/R2 | S4 |
+ ---- ------ ----       ---- ------ ----
+ | S6 | S3 | S1 |       | S1 | S3 | S2 |         ---- ----
+  ---- ---- ----         ---- ---- ----         | S3 | S1 |
+     Cell 1.   \\             Cell 2.  | \\____  ----- -----
+                |                     |        | S4 | S/R4 |
+    ---- ---- ----         ---- ---- ----       ---- ------
+   | S7 | S4 | S2 |       | S1 | S3 | S2 |      | S2 | S5 |
+   ----- ---- -----       ----- ---- -----       ---- ----
+  | S6 | S/R3 | S1 | --- | S4 | S/R5 | S5 | ____/ Cell 4.
+   ---- ------ ----       ---- ------ ----
+   | S8 | S5 | S3 |       | S6 | S7 | S8 |     ... etc ...
+    ---- ---- ----         ---- ---- ----
+       Cell 3.                Cell 5.
+.in 3
+
+.ce
+Figure 1:  SILC Network Topology
+
+
+A cell is formed when a server or servers connect to one router.  In
+SILC network normal server cannot directly connect to other normal
+server.  Normal server may only connect to SILC router which then
+routes the messages to the other servers in the cell.  Router servers
+on the other hand may connect to other routers to form the actual SILC 
+network, as seen in above figure.  However, router is also normal SILC 
+server; clients may connect to it the same way as to normal SILC 
+servers.  Normal server also cannot have active connections to more 
+than one router.  Normal server cannot be connected to two different 
+cells.  Router servers, on the other hand, may have as many router to 
+router connections as needed.
+
+There are many issues in this network topology that needs to be careful
+about.  Issues like the size of the cells, the number of the routers in 
+the SILC network and the capacity requirements of the routers.  These
+issues should be discussed in the Internet Community and additional
+documents on the issue will be written.
+
+
+.ti 0
+2.2 Communication Inside a Cell
+
+It is always guaranteed that inside a cell message is delivered to the 
+recipient with at most two server hops.  Client who is connected to
+server in the cell and is talking on channel to other client connected 
+to other server in the same cell, will have its messages delivered from 
+its local server first to the router of the cell, and from the router 
+to the other server in the cell.
+
+The following diagram represents this scenario:
+
+
+.in 25
+.nf
+1 --- S1     S4 --- 5
+         S/R
+ 2 -- S2     S3
+     /        |
+    4         3
+.in 3
+
+
+.ce
+Figure 2:  Communication Inside cell
+
+
+Example:  Client 1. connected to Server 1. message sent to
+          Client 4. connected to Server 2. travels from Server 1.
+          first to Router which routes the message to Server 2.
+          which then sends it to the Client 4.  All the other
+          servers in the cell will not see the routed message.
+
+
+If client is connected directly to the router, as router is also normal 
+SILC server, the messages inside the cell are always delivered only with 
+one server hop.  If clients communicating with each other are connected 
+to the same server, no router interaction is needed.  This is the optimal
+situation of message delivery in the SILC network.
+
+
+.ti 0
+2.3 Communication in the Network
+
+If the message is destined to server that does not belong to local cell 
+the message is routed to the router server to which the destination 
+server belongs, if the local router is connected to destination router.
+If there is no direct connection to the destination router, the local
+router routes the message to its primary route.  The following diagram
+represents message sending between cells.
+
+
+.in 16
+.nf
+1 --- S1     S4 --- 5            S2 --- 1
+         S/R - - - - - - - - S/R
+ 2 -- S2     S3           S1
+     /        |             \\
+    4         3              2
+
+   Cell 1.               Cell 2.
+.in 3
+
+
+.ce
+Figure 3:  Communication Between Cells
+
+
+Example:  Client 5. connected to Server 4. in Cell 1. message sent
+          to Client 2. connected to Server 1. in Cell 2. travels
+          from Server 4. to Router which routes the message to
+          Router in Cell 2, which then routes the message to 
+          Server 1.  All the other servers and routers in the
+          network will not see the routed message.
+
+
+The optimal case of message delivery from client point of view is
+when clients are connected directly to the routers and the messages
+are delivered from one router to the other router.
+
+
+.ti 0 
+2.4 Channel Communication
+
+Messages may be sent to group of clients as well.  Sending messages to
+many clients works the same way as sending messages point to point, from
+message delivery point of view.  Security issues are another matter
+which are not discussed in this section.
+
+Router server handles the message routing to multiple recipients.  If 
+any recipient is not in the same cell as the sender the messages are 
+routed further.
+
+Server distributes the channel message to its local clients who are 
+joined to the channel.  Also, router distributes the message to its 
+local clients on the channel.
+
+
+.ti 0
+2.5 Router Connections
+
+Router connections play very important role in making the SILC like
+network topology to work.  For example, sending broadcast packets in
+SILC network require special connections between routers; routers must
+be connected in specific way.
+
+Every router has their primary route which is a connection to another
+router in the network.  Unless there is only two routers in the network
+must not routers use each other as their primary routes.  The router
+connections in the network must form a circular.
+
+Example with three routers in the network:
+
+
+
+
+
+
+
+.in 16
+.nf
+    S/R1 - > - > - > - > - > - > - S/R2
+     \\                               /
+      ^                             v
+       \\ - < -  < - S/R3 - < - < - /
+.in 3
+
+
+.ce
+Figure 4:  Router Connections
+
+
+Example:  Network with three routers.  Router 1. uses Router 2. as its
+          primary router.  Router 2. uses Router 3. as its primary router,
+          and Router 3. uses Router 1. as its primary router.  There may
+          be other direct connections between the routers but they must
+          not be used as primary routes.
+
+The above example is applicable to any amount of routers in the network
+except for two routers.  If there are only two routers in the network both
+routers must be able to handle situation where they use each other as their
+primary routes.
+
+The issue of router connections are very important especially with SILC
+broadcast packets.  Usually all router wide information in the network is
+distributed by SILC broadcast packets.
+
+
+.ti 0
+2.6 Backup Routers
+
+Backup routers may exist in the cell in addition of the primary router.
+However, they must not be active routers and act as routers in the cell.
+Only one router may be acting as primary router in the cell.  In the case
+of failure of the primary router may one of the backup routers become
+active.  The purpose of backup routers are in case of failure of the
+primary router to maintain working connections inside the cell and outside
+the cell and to avoid netsplits.
+
+Backup routers are normal servers in the cell that are prepared to take
+over the tasks of primary router if needed.  They need to have at least
+one direct and active connection to the primary router of the cell.
+This communication channel is used to send the router information to
+the backup router.  Backup router must know everything that the primary
+router knows to be able to take over the tasks of the primary router.
+It is the primary router's responsibility to feed the data to the backup
+router.  If the backup router does not know all the data in the case of
+failure some connections may be lost.  The primary router of the cell
+must consider the backup router being normal router server and feed the
+data accordingly.
+
+In addition of having direct connection to the primary router of the
+cell the backup router must also have connection to the same router
+the primary router of the cell has connected.  However, it must not be
+active router connection meaning that the backup router must not use
+that channel as its primary route and it must not notify the router
+about having connected servers, channels and clients behind it.  It
+merely connects to the router.  This sort of connection is later
+referred as being passive connection.  Some keepalive actions may be
+needed by the router to keep the connection alive.
+
+The primary router notifies its primary router about having backup
+routers in the cell by sending SILC_PACKET_CELL_ROUTERS packet.  If
+and when the primary router of the cell becomes unresponsive, its
+primary router knows that there exists backup routers in the cell.  
+After that it will start using the first backup router sent in the
+packet as router of that cell.  In this case the backup router must
+notify its new primary router about the servers, channels and clients
+it has connected to it.  The primary router knows that this server
+has become a router of the cell because of failure of the primary
+router in the cell.  It must also cope with the fact that the servers,
+channels and clients that the new backup router announces are not
+really new, since they used to exist in the primary router of the
+cell.
+
+It is required that other normal servers has passive connections to
+the backup router(s) in the cell.  Some keepalive actions may be needed
+by the server to keep the connection alive.  After they notice the
+failure of the primary router they must start using the connection to
+the first backup router as their primary route.
+
+It is recommended that there would be at least one backup router in
+the cell.  It is not recommended to have all servers in the cell acting
+as backup routers as it requires establishing several connections to
+several servers in the cell.  Large cells can easily have several
+backup routers in the cell.  The order of the backup routers are decided
+at the primary router of the cell and servers and backup servers in the
+cell must be configured accordingly.  It is not required that the backup
+server is actually active server in the cell.  Backup router may be spare
+server in the cell that does not accept normal client connections at all.
+It maybe reserved purely for the backup purposes.  These, however, are
+cell management issues.
+
+If the first backup router is down as well and there is another backup
+router in the cell then it will start acting as the primary router as
+described above.
+
+
+.ti 0
+3. SILC Specification
+
+This section describes the SILC protocol.  However, [SILC2] and
+[SILC3] describes other important protocols that are part of this SILC
+specification and must be read.
+
+
+.ti 0
+3.1 Client
+
+A client is a piece of software connecting to SILC server.  SILC client 
+cannot be SILC server.  Purpose of clients is to provide the user 
+interface of the SILC services for end user.  Clients are distinguished
+from other clients by unique Client ID.  Client ID is a 128 bit ID that
+is used in the communication in the SILC network.  The client ID is 
+based on the nickname selected by the user.  User uses logical nicknames
+in communication which are then mapped to the corresponding Client ID.
+Client ID's are low level identifications and must not be seen by the
+end user.
+
+Clients provide other information about the end user as well. Information
+such as the nickname of the user, username and the hostname of the end 
+user and user's real name.  See section 3.2 Server for information of 
+the requirements of keeping this information.
+
+The nickname selected by the user is not unique in the SILC network.
+There can be 2^8 same nicknames for one IP address.  As for comparison
+to IRC [IRC] where nicknames are unique this is a fundamental difference
+between SILC and IRC.  This causes the server names to be used along
+with the nicknames to identify specific users when sending messages.
+This feature of SILC makes IRC style nickname-wars obsolete as no one
+owns their nickname; there can always be someone else with the same
+nickname.  The maximum length of nickname is 128 characters.
+
+
+.ti 0
+3.1.1 Client ID
+
+Client ID is used to identify users in the SILC network.  The Client ID
+is unique to the extent that there can be 2^128 different Client ID's,
+and ID's based on IPv6 addresses extends this to 2^224 different Client
+ID's.  Collisions are not expected to happen.  The Client ID is defined
+as follows.
+
+.in 6
+128 bit Client ID based on IPv4 addresses:
+
+32 bit  Server ID IP address (bits 1-32)
+ 8 bit  Random number or counter
+88 bit  Truncated MD5 hash value of the nickname
+
+224 bit Client ID based on IPv6 addresses:
+
+128 bit  Server ID IP address (bits 1-128)
+  8 bit  Random number or counter
+ 88 bit  Truncated MD5 hash value of the nickname
+
+o Server ID IP address - Indicates the server where this
+  client is coming from.  The IP address hence equals the
+  server IP address where to the client has connected.
+
+o Random number or counter - Random number to further 
+  randomize the Client ID.  Another choice is to use
+  a counter starting from the zero (0).  This makes it
+  possible to have 2^8 same nicknames from the same
+  server IP address.
+
+o MD5 hash - MD5 hash value of the nickname is truncated
+  taking 88 bits from the start of the hash value.  This
+  hash value is used to search the user's Client ID from
+  the ID lists.
+
+.in 3
+Collisions could occur when more than 2^8 clients using same nickname
+from the same server IP address is connected to the SILC network.  
+Server must be able to handle this situation by refusing to accept 
+anymore of that nickname.
+
+Another possible collision may happen with the truncated hash value of
+the nickname.  It could be possible to have same truncated hash value for
+two different nicknames.  However, this is not expected to happen nor
+cause any problems if it would occur.  Nicknames are usually logical and
+it is unlikely to have two distinct logical nicknames produce same
+truncated hash value.
+
+
+.ti 0
+3.2 Server
+
+Servers are the most important parts of the SILC network.  They form the
+basis of the SILC, providing a point to which clients may connect to.
+There are two kinds of servers in SILC; normal servers and router servers.
+This section focus on the normal server and router server is described
+in the section 3.3 Router.
+
+Normal servers may not directly connect to other normal server.  Normal
+servers may only directly connect to router server.  If the message sent
+by the client is destined outside the local server it is always sent to
+the router server for further routing.  Server may only have one active
+connection to router on same port.  Normal server may not connect to other
+cell's router except in situations where its cell's router is unavailable.
+
+Servers and routers in the SILC network are considered to be trusted.
+With out a doubt, servers that are set to work on ports above 1023 are
+not considered to be trusted.  Also, the service provider acts important
+role in the server's trustworthy.
+
+
+.ti 0
+3.2.1 Server's Local ID List
+
+Normal server keeps various information about the clients and their end
+users connected to it.  Every normal server must keep list of all locally
+connected clients, Client ID's, nicknames, usernames and hostnames and
+user's real name.  Normal servers only keeps local information and it
+does not keep any global information.  Hence, normal servers knows only
+about their locally connected clients.  This makes servers efficient as
+they don't have to worry about global clients.  Server is also responsible
+of creating the Client ID's for their clients.
+
+Normal server also keeps information about locally created channels and
+their Channel ID's.
+
+
+Hence, local list for normal server includes:
+
+.in 6
+server list        - Router connection
+   o Server name
+   o Server IP address
+   o Server ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+
+
+
+client list        - All clients in server
+   o Nickname
+   o Username@host
+   o Real name
+   o Client ID
+   o Sending key
+   o Receiving key
+
+channel list       - All channels in server
+   o Channel name
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+
+.ti 0
+3.2.2 Server ID
+
+Servers are distinguished from other servers by unique 64 bit Server ID 
+(for IPv4) or 160 bit Server ID (for IPv6).  The Server ID is used in
+the SILC to route messages to correct servers.  Server ID's also provide
+information for Client ID's, see section 3.1.1 Client ID.  Server ID is
+defined as follows.
+
+.in 6
+64 bit Server ID based on IPv4 addresses:
+
+32 bit  IP address of the server
+16 bit  Port
+16 bit  Random number
+
+160 bit Server ID based on IPv6 addresses:
+
+128 bit  IP address of the server
+ 16 bit  Port
+ 16 bit  Random number
+
+o IP address of the server - This is the real IP address of
+  the server.
+
+o Port - This is the port the server is bound to.
+
+o Random number - This is used to further randomize the Server ID.
+
+.in 3
+Collisions are not expected to happen in any conditions.  The Server ID
+is always created by the server itself and server is responsible of
+distributing it to the router.
+
+
+.ti 0
+3.2.3 SILC Server Ports
+
+The following ports has been assigned by IANA for the SILC protocol:
+
+.in 10
+silc            706/tcp    SILC
+silc            706/udp    SILC
+.in 3
+
+If there are needs to create new SILC networks in the future the port
+numbers must be officially assigned by the IANA.
+
+Server on network above privileged ports (>1023) should not be trusted
+as they could have been set up by untrusted party.
+
+
+.ti 0
+3.3 Router
+
+Router server in SILC network is responsible for keeping the cell together
+and routing messages to other servers and to other routers.  Router server
+is also a normal server thus clients may connect to it as it would be
+just normal SILC server.
+
+However, router servers has a lot of important tasks that normal servers
+do not have.  Router server knows everything about everything in the SILC.
+They know all clients currently on SILC, all servers and routers and all
+channels in SILC.  Routers are the only servers in SILC that care about
+global information and keeping them up to date at all time.  And, this
+is what they must do.
+
+
+.ti 0
+3.3.1 Router's Local ID List
+
+Router server as well must keep local list of connected clients and
+locally created channels.  However, this list is extended to include all
+the informations of the entire cell, not just the server itself as for
+normal servers.
+
+However, on router this list is a lot smaller since routers do not keep
+information about user's nickname, username and hostname and real name
+since these are not needed by the router.  Router keeps only information
+that it needs.
+
+
+Hence, local list for router includes:
+
+.in 6
+server list        - All servers in the cell
+   o Server name
+   o Server ID
+   o Router's Server ID
+   o Sending key
+   o Receiving key
+
+client list        - All clients in the cell
+   o Client ID
+
+
+channel list       - All channels in the cell
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+Note that locally connected clients and other information include all the
+same information as defined in section section 3.2.1 Server's Local ID
+List.
+
+
+.ti 0
+3.3.2 Router's Global ID List
+
+Router server must also keep global list.  Normal servers do not have
+global list as they know only about local information.  Global list
+includes all the clients on SILC, their Client ID's, all created channels
+and their Channel ID's and all servers and routers on SILC and their
+Server ID's.  That is said, global list is for global information and the
+list must not include the local information already on the router's local
+list.
+
+Note that the global list does not include information like nicknames,
+usernames and hostnames or user's real names.  Router does not keep
+these informations as they are not needed by the router.  This 
+information is available from the client's server which maybe queried
+when needed.
+
+Hence, global list includes:
+
+.in 6
+server list        - All servers in SILC
+   o Server name
+   o Server ID
+   o Router's Server ID
+
+
+client list        - All clients in SILC
+   o Client ID
+
+channel list       - All channels in SILC
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+.in 3
+
+
+.ti 0
+3.3.3 Router's Server ID
+
+Router's Server ID's are equivalent to normal Server ID's.  As routers
+are normal servers as well same types of ID's applies for routers as well.
+Thus, see section 3.2.2 Server ID.  Server ID's for routers are always
+created by the remote router where the router is connected to.
+
+
+.ti 0
+3.4 Channels
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client requests JOIN command to the channel, and the channel ceases to
+exist when the last client has left it.  When channel exists, any client
+can reference it using the name of the channel.
+
+Channel names are unique although the real uniqueness comes from 64 bit
+Channel ID that unifies each channel.  However, channel names are still
+unique and no two global channels with same name may exist.  The Channel
+name is a string of maximum length of 256 characters.  Channel names may
+not contain any spaces (`  '), any non-printable ASCII characters,
+commas (`,') and wildcard characters.
+
+Channels can have operators that can administrate the channel and
+operate all of its modes.  The following operators on channel exist on SILC
+network.
+
+.in 6
+o Channel founder - When channel is created the joining client becomes
+  channel founder.  Channel founder is channel operator with some more
+  privileges.  Basically, channel founder can fully operate the channel
+  and all of its modes.  The privileges are limited only to the particular
+  channel.  There can be only one channel founder per channel.  Channel
+  founder supersedes channel operator's privileges.
+
+  Channel founder privileges cannot be removed by any other operator on
+  channel.  When channel founder leaves the channel there is no channel
+  founder on the channel.  Channel founder also cannot be removed by
+  force from the channel.
+
+o Channel operator - When client joins to channel that has not existed
+  previously it will become automatically channel operator (and channel
+  founder discussed above).  Channel operator is able administrate the
+  channel, set some modes on channel, remove a badly behaving client from
+  the channel and promote other clients to become channel operator.
+  The privileges are limited only to the particular channel.
+
+  Normal channel user may be promoted (opped) to channel operator
+  gaining channel operator privileges.  Channel founder or other channel
+  operator may also demote (deop) channel operator to normal channel
+  user.
+.in 3
+
+
+.ti 0
+3.4.1 Channel ID
+
+Channels are distinguished from other channels by unique Channel ID.
+The Channel ID is a 64 bit ID (for IPv4) or 160 bit ID (for IPv6), and
+collisions are not expected to happen in any conditions.  Channel names
+are just for logical use of channels.  The Channel ID is created by the
+server where the channel is created.  The Channel ID is defined as
+follows.
+
+.in 6
+64 bit Channel ID based on IPv4 addresses:
+
+32 bit  Router's Server ID IP address (bits 1-32)
+16 bit  Router's Server ID port (bits 33-48)
+16 bit  Random number
+
+160 bit Channel ID based on IPv6 addresses:
+
+128 bit  Router's Server ID IP address (bits 1-128)
+ 16 bit  Router's Server ID port (bits 129-144)
+ 16 bit  Random number
+
+o Router's Server ID IP address - Indicates the IP address of 
+  the router of the cell where this channel is created.  This is 
+  taken from the router's Server ID.  This way SILC router knows 
+  where this channel resides in the SILC network.
+
+o Router's Server ID port - Indicates the port of the channel on 
+  the server.  This is taken from the router's Server ID.
+
+o Random number - To further randomize the Channel ID.  This makes
+  sure that there are no collisions.  This also means that
+  in a cell there can be 2^16 channels.
+.in 3
+
+
+.ti 0
+3.5 Operators
+
+Operators are normal users with extra privileges to their server or
+router.  Usually these people are SILC server and router administrators
+that take care of their own server and clients on them.  The purpose of
+operators is to administrate the SILC server or router.  However, even
+an operator with highest privileges is not able to enter invite-only
+channel, to gain access to the contents of a encrypted and authenticated
+packets traveling in the SILC network or to gain channel operator
+privileges on public channels without being promoted.  They have the
+same privileges as everyone else except they are able to administrate
+their server or router.
+
+
+.ti 0
+3.6 SILC Commands
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+Client usually sends the commands and server replies by sending a reply
+packet to the command.  Server may also send commands usually to serve
+the original client's request.  However, server may not send command
+to client and there are some commands that server must not send.
+
+Note that the command reply is usually sent only after client has sent
+the command request but server is allowed to send command reply packet
+to client even if client has not requested the command.  Client may,
+however, choose ignore the command reply, but should not.
+
+It is expected that some of the commands may be miss-used by clients
+resulting various problems on the server side.  Every implementation
+should assure that commands may not be executed more than once, say,
+in two (2) seconds.  However, to keep response rate up, allowing for
+example five (5) commands before limiting is allowed.  It is recommended
+that commands such as SILC_COMMAND_NICK, SILC_COMMAND_JOIN and 
+SILC_COMMAND_LEAVE should be limited in all cases as they require
+heavy operations.  This should be sufficient to prevent the miss-use of
+commands.
+
+SILC commands are described in section 5 SILC Commands.
+
+
+.ti 0
+3.7 SILC Packets
+
+Packets are naturally the most important part of the protocol and the
+packets are what actually makes the protocol.  Packets in SILC network
+are always encrypted using, usually, the shared secret session key
+or some other key, for example, channel key, when encrypting channel
+messages.  The SILC Packet Protocol is a wide protocol and is described
+in [SILC2].  This document does not define or describe details of
+SILC packets.
+
+
+
+.ti 0
+3.8 Packet Encryption
+
+All packets passed in SILC network must be encrypted.  This section
+defines how packets must be encrypted in the SILC network.  The detailed
+description of the actual encryption process of the packets are
+described in [SILC2].
+
+Client and its server shares secret symmetric session key which is
+established by the SILC Key Exchange Protocol, described in [SILC3]. 
+Every packet sent from client to server, with exception of packets for
+channels, are encrypted with this session key.
+
+Channels has their own key that are shared by every client on the channel.
+However, the channel keys are cell specific thus one cell does not know
+the channel key of the other cell, even if that key is for same channel.
+Channel key is also known by the routers and all servers that has clients
+on the channel.  However, channels may have channel private keys that
+are entirely local setting for client.  All clients on the channel must
+know the channel private key before hand to be able to talk on the
+channel.  In this case, no server or router knows the key for channel.
+
+Server shares secret symmetric session key with router which is
+established by the SILC Key Exchange Protocol.  Every packet passed from
+server to router, with exception of packets for channels, are encrypted
+with the shared session key.  Same way, router server shares secret
+symmetric key with its primary route.  However, every packet passed
+from router to other router, including packets for channels, are
+encrypted with the shared session key.  Every router connection has
+their own session keys.
+
+
+.ti 0
+3.8.1 Determination of the Source and the Destination
+
+The source and the destination of the packet needs to be determined
+to be able to route the packets to correct receiver.  This information
+is available in the SILC Packet Header which is included in all packets
+sent in SILC network.  The SILC Packet Header is described in [SILC2].
+
+The header is always encrypted with the session key who is next receiver
+of the packet along the route.  The receiver of the packet, for example
+a router along the route, is able to determine the sender and the
+destination of the packet by decrypting the SILC Packet Header and
+checking the ID's attached to the header.  The ID's in the header will
+tell to where the packet needs to be sent and where it is coming from.
+
+The header in the packet does not change during the routing of the
+packet.  The original sender, for example client, assembles the packet
+and the packet header and server or router between the sender and the
+receiver must not change the packet header.
+
+Note that the packet and the packet header may be encrypted with
+different keys.  For example, packets to channels are encrypted with
+the channel key, however, the header is encrypted with the session key
+as described above.  However, the header and the packet may be encrypted
+with same key.  This is case, for example, with command packets.
+
+
+.ti 0
+3.8.2 Client To Client
+
+Process of message delivery and encryption from client to another
+client is as follows.
+
+Example:  Private message from client to another client on different
+          servers.  Clients do not share private message delivery
+          keys; normal session keys are used.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the session key shared between client and its
+  server.
+
+o Server determines the destination of the packet and decrypts
+  the packet.  Server encrypts the packet with session key shared
+  between the server and its router, and sends the packet to the
+  router.
+
+o Router determines the destination of the packet and decrypts
+  the packet.  Router encrypts the packet with session key 
+  shared between the router and the destination server, and sends
+  the packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and decrypts the packet.  Server encrypts the packet with
+  session key shared between the server and the destination client,
+  and sends the packet to the client.
+
+o Client 2. decrypts the packet.
+
+
+Example:  Private message from client to another client on different
+          servers.  Clients has established secret shared private
+          message delivery key with each other and that is used in 
+          the message encryption.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the private message delivery key shared between
+  clients.
+
+o Server determines the destination of the packet and sends the 
+  packet to the router.
+
+o Router determines the destination of the packet and sends the
+  packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and sends the packet to the client.
+
+o Client 2. decrypts the packet with the secret shared key.
+
+
+If clients share secret key with each other the private message
+delivery is much simpler since servers and routers between the
+clients do not need to decrypt and re-encrypt the packet.
+
+The process for clients on same server is much simpler as there are
+no need to send the packet to the router.  The process for clients 
+on different cells is same as above except that the packet is routed 
+outside the cell.  The router of the destination cell routes the 
+packet to the destination same way as described above.
+
+
+.ti 0
+3.8.3 Client To Channel
+
+Process of message delivery from client on channel to all the clients
+on the channel.
+
+Example:  Channel of four users; two on same server, other two on
+          different cells.  Client sends message to the channel.
+
+o Client 1. encrypts the packet with channel key and sends the
+  packet to its server.
+
+o Server determines local clients on the channel and sends the
+  packet to the Client on the same server.  Server then sends
+  the packet to its router for further routing.
+
+o Router determines local clients on the channel, if found
+  sends packet to the local clients.  Router determines global
+  clients on the channel and sends the packet to its primary
+  router or fastest route.
+
+o (Other router(s) do the same thing and sends the packet to
+   the server(s))
+
+o Server determines local clients on the channel and sends the
+  packet to the client.
+
+o All clients receiving the packet decrypts the packet.
+
+
+.ti 0
+3.8.4 Server To Server
+
+Server to server packet delivery and encryption is described in above
+examples. Router to router packet delivery is analogous to server to
+server.  However, some packets, such as channel packets, are processed
+differently.  These cases are described later in this document and
+more in detail in [SILC2].
+
+
+.ti 0
+3.9 Key Exchange And Authentication
+
+Key exchange is done always when for example client connects to server
+but also when server and router and router and router connects to each
+other.  The purpose of key exchange protocol is to provide secure key
+material to be used in the communication.  The key material is used to
+derive various security parameters used to secure SILC packets.  The
+SILC Key Exchange protocol is described in detail in [SILC3].
+
+Authentication is done after key exchange protocol has been successfully
+completed.  The purpose of authentication is to authenticate for example
+client connecting to the server.  However, Usually clients are accepted
+to connect to server without explicit authentication.  Servers are
+required use authentication protocol when connecting.  The authentication
+may be based on passphrase (pre-shared-secret) or public key.  The
+connection authentication protocol is described in detail in [SILC3].
+
+
+.ti 0
+3.9.1 Authentication Payload
+
+Authentication payload is used separately from the SKE and the Connection
+authentication protocol.  It is used during the session to authenticate
+with the remote.  For example, the client can authenticate itself to the
+server to be server operator.  In this case, Authentication Payload is
+used.
+
+The format of the Authentication Payload is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Public Data Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Authentication Data Length  |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                       Authentication Data                     ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|
+.in 3
+.ce
+Figure 5:  Authentication Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire payload.
+
+o Authentication Type (2) - The method of the authentication.
+  The authentication methods are defined in [SILC2] in the
+  Connection Auth Request Payload.  The NONE authentication
+  method is not recommended.
+
+o Public Data Length (2 bytes) - Indicates the length of
+  the Public Data field.
+
+o Public Data (variable length) - This is defined only if
+  the authentication method is public key.  If it is any other
+  this field does not exist and the Public Data Length field
+  is set to zero (0).
+
+  When the authentication method is public key this includes
+  128 to 4096 bytes of non-zero random data that is used in
+  the signature process, described subsequently.
+
+o Authentication Data Length (2 bytes) - Indicates the
+  length of the Authentication Data field.
+
+o Authentication Data (variable length) - Authentication 
+  method dependent authentication data.
+.in 3
+
+
+If the authentication method is password based, the Authentication
+Data field includes the plaintext password.  It is safe to send
+plaintext password since the entire payload is encrypted.  In this
+case the Public Data Lenght is set to zero (0).
+
+If the authentication method is public key based (or certificate)
+the Authentication Data is computed as follows:
+
+  HASH = hash(random bytes | ID | public key (or certificate));
+  Authentication Data = sign(HASH);
+
+The hash() and the sign() are the hash funtion and the public key
+cryptography function selected in the SKE protocol.  The public key
+is SILC style public key unless certificates are used.  The ID is the
+entity's ID (Client or Server ID) who is authenticating itself.  The ID
+is raw ID data.  The random bytes are non-zero random bytes of length
+between 128 and 4096 bytes, and will be included into the Public Data
+field as is.
+
+The receiver will compute the signature using the random data received
+in the payload, the ID associated to the connection and the public key
+(or certificate) received in the SKE protocol.  After computing the
+receiver must verify the signature.  In this case also, the entire
+payload is encrypted.
+
+
+.ti 0
+3.10 Algorithms
+
+This section defines all the allowed algorithms that can be used in
+the SILC protocol.  This includes mandatory cipher, mandatory public
+key algorithm and MAC algorithms.
+
+
+.ti 0
+3.10.1 Ciphers
+
+Cipher is the encryption algorithm that is used to protect the data
+in the SILC packets.  See [SILC2] of the actual encryption process and
+definition of how it must be done.  SILC has a mandatory algorithm that
+must be supported in order to be compliant with this protocol.
+
+The following ciphers are defined in SILC protocol:
+
+.in 6
+aes-256-cbc         AES in CBC mode, 256 bit key       (mandatory)
+aes-192-cbc         AES in CBC mode, 192 bit key       (optional)
+aes-128-cbc         AES in CBC mode, 128 bit key       (optional)
+twofish-256-cbc     Twofish in CBC mode, 256 bit key   (optional)
+twofish-192-cbc     Twofish in CBC mode, 192 bit key   (optional)
+twofish-128-cbc     Twofish in CBC mode, 128 bit key   (optional)
+blowfish-128-cbc    Blowfish in CBC mode, 128 bit key  (optional)
+cast-256-cbc        CAST-256 in CBC mode, 256 bit key  (optional)
+cast-192-cbc        CAST-256 in CBC mode, 192 bit key  (optional)
+cast-128-cbc        CAST-256 in CBC mode, 128 bit key  (optional)
+rc6-256-cbc         RC6 in CBC mode, 256 bit key       (optional)
+rc6-192-cbc         RC6 in CBC mode, 192 bit key       (optional)
+rc6-128-cbc         RC6 in CBC mode, 128 bit key       (optional)
+mars-256-cbc        Mars in CBC mode, 256 bit key      (optional)
+mars-192-cbc        Mars in CBC mode, 192 bit key      (optional)
+mars-128-cbc        Mars in CBC mode, 128 bit key      (optional)
+none                No encryption         (optional)
+.in 3
+
+
+Algorithm none does not perform any encryption process at all and 
+thus is not recommended to be used.  It is recommended that no client
+or server implementation would accept none algorithms except in special
+debugging mode.
+
+Additional ciphers may be defined to be used in SILC by using the
+same name format as above.
+
+
+.ti 0
+3.10.2 Public Key Algorithms
+
+Public keys are used in SILC to authenticate entities in SILC network
+and to perform other tasks related to public key cryptography.  The 
+public keys are also used in the SILC Key Exchange protocol [SILC3].
+
+The following public key algorithms are defined in SILC protocol:
+
+.in 6
+rsa        RSA  (mandatory)
+dss        DSS  (optional)
+.in 3
+
+DSS is described in [Menezes].  The RSA must be implemented according
+PKCS #1 [PKCS1].  The mandatory PKCS #1 implementation in SILC must be
+compliant to either PKCS #1 version 1.5 or newer with the the following
+notes: The signature encoding is always in same format as the encryption
+encoding regardles of the PKCS #1 version.  The signature with appendix
+(with hash algorithm OID in the data) must not be used in the SILC.  The
+rationale for this is that there is no binding between the PKCS #1 OIDs
+and the hash algorithms used in the SILC protocol.  Hence, the encoding
+is always in PKCS #1 version 1.5 format.
+
+Additional public key algorithms may be defined to be used in SILC.
+
+
+.ti 0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section.  They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+The following Hash algorithm are defined in SILC protocol:
+
+sha1             SHA-1, length = 20      (mandatory)
+md5              MD5, length = 16        (optional)
+
+
+.ti 0
+3.10.4 MAC Algorithms
+
+Data integrity is protected by computing a message authentication code
+(MAC) of the packet data.  See [SILC2] for details how to compute the
+MAC.
+
+The following MAC algorithms are defined in SILC protocol:
+
+.in 6
+hmac-sha1-96     HMAC-SHA1, length = 12  (mandatory)
+hmac-md5-96      HMAC-MD5, length = 12   (optional)
+hmac-sha1        HMAC-SHA1, length = 20  (optional)
+hmac-md5         HMAC-MD5, length = 16   (optional)
+none             No MAC                  (optional)
+.in 3
+
+The none MAC is not recommended to be used as the packet is not
+authenticated when MAC is not computed.  It is recommended that no
+client or server would accept none MAC except in special debugging
+mode.
+
+The HMAC algorithm is described in [HMAC] and hash algorithms that
+are used as part of the HMACs are described in [Scheneir] and in
+[Menezes]
+
+Additional MAC algorithms may be defined to be used in SILC.
+
+
+.ti 0
+3.10.5 Compression Algorithms
+
+SILC protocol supports compression that may be applied to unencrypted
+data.  It is recommended to use compression on slow links as it may
+significantly speed up the data transmission.  By default, SILC does not
+use compression which is the mode that must be supported by all SILC
+implementations.
+
+The following compression algorithms are defined:
+
+.in 6
+none        No compression               (mandatory)
+zlib        GNU ZLIB (LZ77) compression  (optional)
+.in 3
+
+Additional compression algorithms may be defined to be used in SILC.
+
+
+.ti 0
+3.11 SILC Public Key
+
+This section defines the type and format of the SILC public key.  All
+implementations must support this public key type.  See [SILC3] for
+other optional public key and certificate types allowed in SILC
+protocol.  Public keys in SILC may be used to authenticate entities
+and to perform other tasks related to public key cryptography.
+
+The format of the SILC Public Key is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Public Key Length                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Algorithm Name Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Algorithm Name                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Identifier Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Identifier                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  SILC Public Key
+
+
+.in 6
+o Public Key Length (4 bytes) - Indicates the full length
+  of the public key, not including this field.
+
+o Algorithm Name Length (2 bytes) - Indicates the length
+  of the Algorithm Length field, not including this field.
+
+o Algorithm name (variable length) - Indicates the name
+  of the public key algorithm that the key is.  See the
+  section 3.10.2 Public Key Algorithms for defined names.
+
+o Identifier Length (2 bytes) - Indicates the length of
+  the Identifier field, not including this field.
+
+o Identifier (variable length) - Indicates the identifier
+  of the public key.  This data can be used to identify
+  the owner of the key.  The identifier is of the following
+  format:
+
+     UN   User name
+     HN   Host name or IP address
+     RN   Real name
+     E    EMail address
+     O    Organization
+     C    Country
+
+
+  Examples of an identifier:
+
+    `UN=priikone, HN=poseidon.pspt.fi, E=priikone@poseidon.pspt.fi'
+
+    `UN=sam, HN=dummy.fi, RN=Sammy Sam, O=Company XYZ, C=Finland'
+
+  At least user name (UN) and host name (HN) must be provided as
+  identifier.  The fields are separated by commas (`,').  If
+  comma is in the identifier string it must be written as `\\,',
+  for example, `O=Company XYZ\\, Inc.'.
+
+o Public Data (variable length) - Includes the actual
+  public data of the public key.
+
+  The format of this field for RSA algorithm is
+  as follows:
+
+     4 bytes            Length of e
+     variable length    e
+     4 bytes            Length of n
+     variable length    n
+
+
+  The format of this field for DSS algorithm is
+  as follows:
+
+     4 bytes            Length of p
+     variable length    p
+     4 bytes            Length of q
+     variable length    q
+     4 bytes            Length of g
+     variable length    g
+     4 bytes            Length of y
+     variable length    y
+
+  The variable length fields are multiple precession
+  integers encoded as strings in both examples.
+
+  Other algorithms must define their own type of this
+  field if they are used.
+.in 3
+
+All fields in the public key are in MSB (most significant byte first)
+order.
+
+
+.ti 0
+3.12 SILC Version Detection
+
+The version detection of both client and server is performed at the
+connection phase while executing the SILC Key Exchange protocol.  The
+version identifier is exchanged between initiator and responder.  The
+version identifier is of the following format:
+
+.in 6
+SILC-<protocol version>-<software version>
+.in 3
+
+The version strings are of the following format:
+
+.in 6
+protocol version = <major>.<minor>
+software version = <major>[.<minor>[.<build>]]
+.in 3
+
+Protocol version may provide both major and minor version.  Currently
+implementations must set the protocol version and accept the protocol
+version as SILC-1.0-<sotware version>. 
+
+Software version may provide major, minor and build version.  The
+software version may be freely set and accepted.
+
+
+Thus, the version string could be, for example:
+
+.in 6
+SILC-1.0-1.2
+.in 3
+
+
+.ti 0
+4 SILC Procedures
+
+This section describes various SILC procedures such as how the 
+connections are created and registered, how channels are created and
+so on.  The section describes the procedures only generally as details
+are described in [SILC2] and [SILC3].
+
+
+.ti 0
+4.1 Creating Client Connection
+
+This section describes the procedure when client connects to SILC server.
+When client connects to server the server must perform IP address lookup
+and reverse IP address lookup to assure that the origin host really is
+who it claims to be.  Client, host, connecting to server must have 
+both valid IP address and fully qualified domain name (FQDN).
+
+After that the client and server performs SILC Key Exchange protocol
+which will provide the key material used later in the communication.
+The key exchange protocol must be completed successfully before the
+connection registration may continue.  The SILC Key Exchange protocol
+is described in [SILC3].
+
+Typical server implementation would keep a list of connections that it
+allows to connect to the server.  The implementation would check, for
+example, the connecting client's IP address from the connection list
+before the SILC Key Exchange protocol has been started.  Reason for
+this is that if the host is not allowed to connect to the server there
+is no reason to perform a key exchange protocol.
+
+After successful key exchange protocol the client and server performs
+connection authentication protocol.  The purpose of the protocol is to
+authenticate the client connecting to the server.  Flexible
+implementation could also accept the client to connect to the server
+without explicit authentication.  However, if authentication is
+desired for a specific client it may be based on passphrase or
+public key authentication.  If authentication fails the connection
+must be terminated.  The connection authentication protocol is described
+in [SILC3].
+
+After successful key exchange and authentication protocol the client
+registers itself by sending SILC_PACKET_NEW_CLIENT packet to the
+server.  This packet includes various information about the client
+that the server uses to create the client.  Server creates the client
+and sends SILC_PACKET_NEW_ID to the client which includes the created
+Client ID that the client must start using after that.  After that
+all SILC packets from the client must have the Client ID as the
+Source ID in the SILC Packet Header, described in [SILC2].
+
+Client must also get the server's Server ID that is to be used as
+Destination ID in the SILC Packet Header when communicating with
+the server (for example when sending commands to the server).  The
+ID may be resolved in two ways.  Client can take the ID from an
+previously received packet from server that must include the ID,
+or to send SILC_COMMAND_INFO command and receive the Server ID as
+command reply.
+
+Server may choose not to use the information received in the
+SILC_PACKET_NEW_CLIENT packet.  For example, if public key or 
+certificate were used in the authentication, server may use those
+informations rather than what it received from client.  This is suitable
+way to get the true information about client if it is available.
+
+The nickname of client is initially set to the username sent in the
+SILC_PACKET_NEW_CLIENT packet.  User should set the nickname to more
+suitable by sending SILC_COMMAND_NICK command.  However, this is not
+required as part of registration process.
+
+Server must also distribute the information about newly registered
+client to its router (or if the server is router, to all routers in
+the SILC network).  More information about this in [SILC2].
+
+
+.ti 0
+4.2 Creating Server Connection
+
+This section descibres the procedure when server connects to its
+router (or when router connects to other router, the cases are
+equivalent).  The procedure is very much alike when client connects
+to the server thus it is not repeated here.
+
+One difference is that server must perform connection authentication
+protocol with proper authentication.  Proper authentication is based
+on passphrase or public key authentication.
+
+After server and router has successfully performed the key exchange
+and connection authentication protocol, the server register itself
+to the router by sending SILC_PACKET_NEW_SERVER packet.  This packet
+includes the server's Server ID that it has created by itself and
+other relevant information about the server.
+
+After router has received the SILC_PACKET_NEW_SERVER packet it
+distributes the information about newly registered server to all routers
+in the SILC network.  More information about this in [SILC2].
+
+As client needed to resolve the destination ID this must be done by the
+server that connected to the router, as well.  The way to resolve it is
+to get the ID from previously received packet.  Server must also start
+using its own Server ID as Source ID in SILC Packet Header and the
+router's Server ID as Destination when communicating with the router.
+
+If the server has already connected clients and locally created
+channels the server must distribute these informations to the router.
+The distribution is done by sending packet SILC_PACKET_NEW_CHANNEL.
+See [SILC2] for more information on this.
+
+
+.ti 0
+4.3 Joining to a Channel
+
+This section describes the procedure when client joins to a channel.
+Client may join to channel by sending command SILC_COMMAND_JOIN to the
+server.  If the receiver receiving join command is normal server the
+server must check its local list whether this channel already exists
+locally.  This would indicate that some client connected to the server
+has already joined to the channel.  If this is case the client is
+joined to the client, new channel key is created and information about
+newly joined channel is sent to the router.  The router is informed
+by sending SILC_NOTIFY_TYPE_JOIN notify type.  The notify type must
+also be sent to the local clients on the channel.  The new channel key
+is also sent to the router and to local clients on the channel.
+
+If the channel does not exist in the local list the client's command
+must be sent to the router which will then perform the actual joining
+procedure.  When server receives the reply to the command from the
+router it must be sent to the client who sent the command originally.
+Server will also receive the channel key from the server that it must
+send to the client who originally requested the join command.  The server
+must also save the channel key.
+
+If the receiver of the join command is router it must first check its
+local list whether anyone in the cell has already joined to the channel.
+If this is the case the client is joined to the channel and reply is
+sent to the client.  If the command was sent by server the command reply
+is sent to the server who sent it.  Then the router must also create
+new channel key and distribute it to all clients on the channel and
+all servers that has clients on the channel.  Router must also send
+the SILC_NOTIFY_TYPE_JOIN notify type to local clients on the channel
+and to local servers that has clients on the channel.
+
+If the channel does not exist on the router's local list it must
+check the global list whether the channel exists at all.  If it does
+the client is joined to the channel as described previously.  If
+the channel does not exist the channel is created and the client
+is joined to the channel.  The channel key is also created and
+distributed as previously described.  The client joining to the created
+channel is made automatically channel founder and both channel founder
+and channel operator privileges is set for the client.
+
+If the router created the channel in the process, information about the
+new channel must be broadcasted to all routers.  This is done by 
+broadcasting SILC_PACKET_NEW_CHANNEL packet to the router's primary
+route.  When the router joins the client to the channel it must also
+send information about newly joined client to all routers in the SILC
+network.  This is done by broadcasting the SILC_NOTIFY_TYPE_JOIN notify
+type to the router's primary route. 
+
+It is important to note that new channel key is created always when
+new client joins to channel, whether the channel has existed previously
+or not.  This way the new client on the channel is not able to decrypt
+any of the old traffic on the channel.  Client who receives the reply to
+the join command must start using the received Channel ID in the channel
+message communication thereafter.  Client also receives the key for the
+channel in the command reply.
+
+
+.ti 0
+4.4 Channel Key Generation
+
+Channel keys are created by router who creates the channel by taking
+enough randomness from cryptographically strong random number generator.
+The key is generated always when channel is created, when new client
+joins a channel and after the key has expired.  Key could expire for
+example in an hour.
+
+The key must also be re-generated whenever some client leaves a channel.
+In this case the key is created from scratch by taking enough randomness
+from the random number generator.  After that the key is distributed to
+all clients on the channel.  However, channel keys are cell specific thus
+the key is created only on the cell where the client, who left the
+channel, exists.  While the server or router is creating the new channel
+key, no other client may join to the channel.  Messages that are sent
+while creating the new key are still processed with the old key.  After
+server has sent the SILC_PACKET_CHANNEL_KEY packet must client start
+using the new key.  If server creates the new key the server must also
+send the new key to its router.  See [SILC2] on more information about
+how channel messages must be encrypted and decrypted when router is
+processing them.
+
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it must process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages.  The processing is as follows:
+
+  channel_key  = raw key data
+  HMAC key     = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+
+
+.ti 0
+4.5 Private Message Sending and Reception
+
+Private messages are sent point to point.  Client explicitly destines
+a private message to specific client that is delivered to only to that
+client.  No other client may receive the private message.  The receiver
+of the private message is destined in the SILC Packet Header as any
+other packet as well.
+
+If the sender of a private message does not know the receiver's Client
+ID, it must resolve it from server.  There are two ways to resolve the
+client ID from server; it is recommended that client implementations
+send SILC_COMMAND_IDENTIFY command to receive the Client ID.  Client
+may also send SILC_COMMAND_WHOIS command to receive the Client ID.
+If the sender has received earlier a private message from the receiver
+it should have cached the Client ID from the SILC Packet Header.
+
+Receiver of a private message should not explicitly trust the nickname
+that it receives in the Private Message Payload, described in [SILC2].
+Implementations could resolve the nickname from server, as described
+previously, and compare the received Client ID and the SILC Packet
+Header's Client ID.  The nickname in the payload is merely provided
+to be displayed for end user.
+
+See [SILC2] for description of private message encryption and decryption
+process.
+
+
+.ti 0
+4.6 Private Message Key Generation
+
+Private message may be protected by key generated by client.  The key
+may be generated and sent to the other client by sending packet
+SILC_PACKET_PRIVATE_MESSAGE_KEY which travels through the network
+and is secured by session keys.  After that the private message key
+is used in the private message communication between those clients.
+
+Other choice is to entirely use keys that are not sent through
+the SILC network at all.  This significantly adds security.  This key
+would be pre-shared-key that is known by both of the clients.  Both
+agree about using the key and starts sending packets that indicate
+that the private message is secured using private message key.
+
+The key material used as private message key is implementation issue.
+However, SILC_PACKET_KEY_AGREEMENT packet may be used to negotiate
+the key material.  If the key is normal pre-shared-key or randomly
+generated key, and the SILC_PACKET_KEY_AGREEMENT was not used, then
+the key material should be processed as defined in the [SILC3].  In
+the processing, however, the HASH, as defined in [SILC3] must be 
+ignored.  After processing the key material it is employed as defined
+in [SILC3], however, the HMAC key material must be discarded.
+
+If the key is pre-shared-key or randomly generated the implementations
+should use the SILC protocol's mandatory cipher as the cipher.  If the
+SKE was used to negotiate key material the cipher was negotiated as well.
+
+.ti 0
+4.7 Channel Message Sending and Reception
+
+Channel messages are delivered to group of users.  The group forms a
+channel and all clients on the channel receives messages sent to the
+channel.
+
+Channel messages are destined to channel by specifying the Channel ID
+as Destination ID in the SILC Packet Header.  The server must then
+distribute the message to all clients on the channel by sending the
+channel message destined explicitly to a client on the channel.
+
+See [SILC2] for description of channel message encryption and decryption
+process.
+
+
+.ti 0
+4.8 Session Key Regeneration
+
+Session keys should be regenerated periodically, say, once in an hour.
+The re-key process is started by sending SILC_PACKET_REKEY packet to
+other end, to indicate that re-key must be performed.
+
+If perfect forward secrecy (PFS) flag was selected in the SILC Key
+Exchange protocol [SILC3] the re-key must cause new key exchange with
+SKE protocol.  In this case the protocol is secured with the old key
+and the protocol results to new key material.  See [SILC3] for more
+information.  After the SILC_PACKET_REKEY packet is sent the sender
+will perform the SKE protocol.
+
+If PFS flag was not set, which is the default case, then re-key is done
+without executing SKE protocol.  In this case, the new key is created by
+hashing the old key with hash function selected earlier in the SKE
+protocol.  If the digest length of the hash function is too short for the
+key, then the key is distributed as described in section Processing the
+Key Material in [SILC3].
+
+After both parties has regenerated the session key, both send
+SILC_PACKET_REKEY_DONE packet to each other.  These packets are still
+secured with the old key.  After these packets, the following packets
+must be protected with the new key.  After sending the REKEY_DONE packet
+all subsequent sent packets must be encrypted with the new key.  After
+receiving the REKEY_DONE packet all subsequent packets must be
+decrypted with the new key.
+
+
+.ti 0
+4.9 Command Sending and Reception
+
+Client usually sends the commands in the SILC network.  In this case
+the client simply sends the command packet to server and the server
+processes it and replies with command reply packet.
+
+However, if the server is not able to process the command, it is sent 
+to the server's router.  This is case for example with commands such
+as, SILC_COMMAND_JOIN and SILC_COMMAND_WHOIS commands.  However, there
+are other commands as well.  For example, if client sends the WHOIS
+command requesting specific information about some client the server must
+send the WHOIS command to router so that all clients in SILC network
+are searched.  The router, on the other hand, sends the WHOIS command
+further to receive the exact information about the requested client.
+The WHOIS command travels all the way to the server who owns the client
+and it replies with command reply packet.  Finally, the server who
+sent the command receives the command reply and it must be able to
+determine which client sent the original command.  The server then
+sends command reply to the client.  Implementations should have some
+kind of cache to handle, for example, WHOIS information.  Servers
+and routers along the route could all cache the information for faster
+referencing in the future.
+
+The commands sent by server may be sent hop by hop until someone is able
+to process the command.  However, it is preferred to destine the command
+as precisely as it is possible.  In this case, other routers en route
+must route the command packet by checking the true sender and true
+destination of the packet.  However, servers and routers must not route
+command reply packets to clients coming from other server.  Client
+must not accept command reply packet originated from anyone else but
+from its own server.
+
+
+.ti 0
+5 SILC Commands
+
+.ti 0
+5.1 SILC Commands Syntax
+
+This section briefly describes the syntax of the command notions
+in this document.  Every field in command is separated from each
+other by whitespaces (` ') indicating that each field is independent
+argument and each argument must have own Command Argument Payload.
+The number of maximum arguments are defined with each command
+separately.  The Command Argument Payload is described in [SILC2].
+
+Every command defines specific number for each argument.  Currently,
+they are defined in ascending order; first argument has number one 
+(1), second has number two (2) and so on.  This number is set into the
+Argument Type field in the Command Argument Payload.  This makes it
+possible to send the arguments in free order as the number must be
+used to identify the type of the argument.  This makes is it also
+possible to have multiple optional arguments in commands and in
+command replies.  The number of argument is marked in parentheses
+before the actual argument.
+
+
+
+.in 6
+Example:  Arguments:  (1) <nickname> (2) <username@host>
+.in 3
+   
+
+Every command replies with Status Payload.  This payload tells the
+sender of the command whether the command was completed successfully or
+whether there was an error.  If error occured the payload includes the
+error type.  In the next section the Status Payload is not described 
+as it is common to all commands and has been described here.  Commands 
+may reply with other arguments as well.  These arguments are command 
+specific and are described in the next section.
+
+Example command:
+.in 6
+
+EXAMPLE_COMMAND
+
+.in 8
+Max Arguments:  3
+    Arguments:  (1) <nickname>[@<server>]  (2) <message>
+                (3) [<count>]
+
+The command has maximum of 3 arguments.  However, only first
+and second arguments are mandatory.
+
+First argument <nickname> is mandatory but may have optional
+<nickname@server> format as well.  Second argument is mandatory
+<message> argument.  Third argument is optional <count> argument.
+
+The numbers in parentheses are the argument specific numbers
+that specify the type of the argument in Command Argument Payload.
+The receiver always knows that, say, argument number two (2) is
+<message> argument, regardless of the ordering of the arguments in
+the Command Payload.
+
+Reply messages to the command:
+
+Max Arguments:  4
+    Arguments:  (1) <Status Payload>  (2) [<channel list>]
+                (3) <idle time>       (4) [<away message>]
+
+This command may reply with maximum of 4 arguments.  However,
+only the first and third arguments are mandatory.  The numbers
+in the parentheses have the same meaning as in the upper
+command sending specification.
+
+Every command reply with <Status Payload>, it is mandatory 
+argument for all command replies and for this reason it is not
+described in the command reply descriptions.
+
+
+
+Status messages:
+
+    SILC_STATUS_OK
+    SILC_STATUS_ERR_TOO_MANY_TARGETS
+    SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+    SILC_STATUS_ERR_NO_SUCH_NICK
+
+Every command reply also defines set of status message that it
+may return inside the <Status Payload>.  All status messages
+are defined in the section 5.3 SILC Command Status Types.
+
+.in 3
+Every command that has some kind of ID as argument (for example
+<Client ID>) are actually ID Payloads, defined in [SILC2] that includes
+the type of the ID, length of the ID and the actual ID data.  This
+way variable length ID's can be sent as arguments.
+
+
+.ti 0
+5.2 SILC Commands List
+
+This section lists all SILC commands, however, it is expected that a
+implementation and especially client implementation has many more
+commands that has only local affect.  These commands are official
+SILC commands that has both client and server sides and cannot be
+characterized as local commands.
+
+List of all defined commands in SILC follows.
+
+.in 0
+   0    SILC_COMMAND_NONE
+
+        None.  This is reserved command and must not be sent.
+
+
+   1    SILC_COMMAND_WHOIS
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Whois command is used to query various information about specific
+        user.  The user maybe requested by their nickname and server name.
+        The query may find multiple matching users as there are no unique
+        nicknames in the SILC.  The <count> option maybe given to narrow
+        down the number of accepted results.  If this is not defined there
+        are no limit of accepted results.  The query may also be narrowed
+        down by defining the server name of the nickname.
+
+        It is also possible to search the user by Client ID.  If <Client ID>
+        is provided server must use it as the search value instead of
+        the <nickname>.  One of the arguments must be given.  It is also
+        possible to define multiple Client ID's to search multiple users
+        sending only one WHOIS command.  In this case the Client ID's are
+        appended as normal arguments.  The server replies in this case
+        with only one reply message for all requested users.
+
+        To prevent miss-use of this service wildcards in the nickname
+        or in the servername are not permitted.  It is not allowed
+        to request all users on some server.  The WHOIS requests must 
+        be based on specific nickname request.
+
+        The WHOIS request must be always sent to the router by server
+        so that all users are searched.  However, the server still must
+        search its locally connected clients.  The router must send
+        this command to the server who owns the requested client.  That
+        server must reply to the command.  Server must not send whois
+       replies to the client until it has received the reply from its
+       router.
+
+        Reply messages to the command:
+
+        Max Arguments:  8
+            Arguments:  (1) <Status Payload>       (2) <Client ID> 
+                        (3) <nickname>[@<server>]  (4) <username@host> 
+                        (5) <real name>            (6) [<Channel Payload 
+                                                         list>] 
+                        (7) [<user mode>]          (8) [<idle time>]
+
+
+        This command may reply with several command reply messages to
+        form a list of results.  In this case the status payload will
+        include STATUS_LIST_START status in the first reply and
+        STATUS_LIST_END in the last reply to indicate the end of the
+        list.  If there are only one reply the status is set to normal
+        STATUS_OK.
+
+        The command replies include the Client ID of the nickname,
+        nickname and servername, username and hostname and users real
+        name.  Client should process these replies only after the last
+        reply has been received with the STATUS_LIST_END status.  If the
+        <count> option were defined in the query there will be only
+        <count> many replies from the server.
+
+        The server may return the list of channel the client has joined.
+        In this case the list is list of Channel Payloads.  The Mode Mask
+        in the Channel Payload (see [SILC2] and section 2.3.2.3 for the
+        Channel Payload) is the client's mode on the channel.  The list
+        is encoded by adding the Channel Payloads one after the other.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   2    SILC_COMMAND_WHOWAS
+
+        Max Arguments:  2
+            Arguments:  (1) <nickname>[@<server>]  (2) [<count>]
+
+        Whowas.  This command is used to query history information about
+        specific user.  The user maybe requested by their nickname and 
+        server name.  The query may find multiple matching users as there
+        are no unique nicknames in the SILC.  The <count> option maybe
+        given to narrow down the number of accepted results.  If this
+        is not defined there are no limit of accepted results.  The query
+        may also be narrowed down by defining the server name of the 
+        nickname.
+
+        To prevent miss-use of this service wildcards in the nickname
+        or in the servername are not permitted.  The WHOWAS requests must 
+        be based on specific nickname request.
+
+        The WHOWAS request must be always sent to the router by server
+        so that all users are searched.  However, the server still must
+        search its locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>        (2) <Client ID>
+                        (3) <nickname>[@<server>]   (4) <username@host>
+                        (5) [<real name>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with nickname and username and hostname.
+        Every server must keep history for some period of time of its
+        locally connected clients.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   3    SILC_COMMAND_IDENTIFY
+
+        Max Arguments:  3328
+            Arguments:  (1) [<nickname>[@<server>]]  (2) [<count>]
+                        (3) [<Client ID>]            (n) [...]
+
+        Identify.  Identify command is almost analogous to WHOIS command,
+        except that it does not return as much information.  Only relevant
+        information such as Client ID is returned.  This is usually used
+        to get the Client ID of a client used in the communication with
+        the client.
+
+        The query may find multiple matching users as there are no unique 
+        nicknames in the SILC.  The <count> option maybe given to narrow 
+        down the number of accepted results.  If this is not defined there 
+        are no limit of accepted results.  The query may also be narrowed 
+        down by defining the server name of the nickname.
+
+        It is also possible to search the user by Client ID.  If <Client ID>
+        is provided server must use it as the search value instead of
+        the <nickname>.  One of the arguments must be given.  It is also
+        possible to define multiple Client ID's to search multiple users
+        sending only one IDENTIFY command.  In this case the Client ID's are
+        appended as normal arguments.  The server replies in this case
+        with only one reply message for all requested users.
+
+        To prevent miss-use of this service wildcards in the nickname
+        or in the servername are not permitted.  It is not allowed
+        to request all users on some server.  The IDENTIFY requests must 
+        be based on specific nickname request.
+
+        Implementations may not want to give interface access to this
+        command as it is hardly a command that would be used by an end user.
+        However, it must be implemented as it is used with private message
+        sending.
+
+        The IDENTIFY must be always sent to the router by server so that
+        all users are searched.  However, server must still search its
+        locally connected clients.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>         (2) <Client ID>
+                        (3) [<nickname>[@<server>]]  (4) [<username@host>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        The command replies with Client ID of the nickname and if more
+        information is available it may reply with nickname and username
+        and hostname.  If the <count> option were defined in the query
+        there will be only <count> many replies from the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_NO_SUCH_NICK
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   4    SILC_COMMAND_NICK
+
+        Max Arguments:  1
+            Arguments:  (1) <nickname>
+
+        Set/change nickname.  This command is used to set nickname for
+        user.  There is no limit of the length of the nickname in SILC.
+        Nickname must not include any spaces (` '), non-printable
+        characters, commas (`,') and any wildcard characters.  Note:
+        nicknames in SILC are case-sensitive which must be taken into
+        account when searching clients by nickname.
+
+        When nickname is changed new Client ID is generated.  Server must
+        distribute SILC_NOTIFY_TYPE_NICK_CHANGE to local clients on the
+        channels (if any) the client is joined on.  Then it must send
+        SILC_PACKET_REPLACE_ID to its primary route to replace the old
+        Client ID with the new one.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <New ID Payload>
+
+        This command is replied always with New ID Payload that is
+        generated by the server every time user changes their nickname.
+        Client receiving this payload must start using the received
+        Client ID as its current valid Client ID.  The New ID Payload
+        is described in [SILC2].
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NICKNAME_IN_USE
+            SILC_STATUS_ERR_BAD_NICKNAME
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+
+   5    SILC_COMMAND_LIST
+
+        Max Arguments:  1
+            Arguments:  (1) [<Channel ID>]
+
+        The list command is used to list channels and their topics on the
+        current server.  If the <Channel ID> parameter is used, only the
+        status of that channel is displayed.  Secret channels are not
+        listed at all.  Private channels are listed with status indicating
+        that the channel is private.  Router may reply with all channels
+        it knows about.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <channel>         (4) [<topic>]
+                        (5) [<user count>]
+
+        This command may reply with several command reply messages to form
+        a list of results.  In this case the status payload will include
+        STATUS_LIST_START status in the first reply and STATUS_LIST_END in 
+        the last reply to indicate the end of the list.  If there are only 
+        one reply the status is set to normal STATUS_OK.
+
+        This command replies with Channel ID, name and the topic of the
+        channel.  If the channel is private channel the <topic> includes
+        "*private*" string.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_LIST_START
+            SILC_STATUS_LIST_END
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   6    SILC_COMMAND_TOPIC
+
+        Max Arguments:  2
+            Arguments:  (1) <Channel ID>  (2) [<topic>]]
+
+        This command is used to change or view the topic of a channel.
+        The topic for channel <Channel ID> is returned if there is no
+        <topic> given.  If the <topic> parameter is present, the topic
+        for that channel will be changed, if the channel modes permit
+        this action.
+
+        After setting the topic the server must send the notify type
+        SILC_NOTIFY_TYPE_TOPIC_SET to its primary router and then to
+        the channel which topic was changed.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <Channel ID> 
+                        (3) [<topic>]
+
+        The command may reply with the topic of the channel if it is
+        set.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   7    SILC_COMMAND_INVITE
+
+        Max Arguments:  4
+            Arguments:  (1) <Channel ID>       (2) [<Client ID>]
+                        (3) [<adding client>]  (4) [<removing client>]
+
+        This command is used to invite other clients to join to the
+        channel.  The <Client ID> argument is the target client's ID that
+        is being invited.  The <Channel ID> is the Channel ID of the
+        requested channel.  The sender of this command must be on the
+        channel.  The server must also send the notify type
+        SILC_NOTIFY_TYPE_INVITE to its primary router and then to the
+        client indicated by the <Client ID>.
+
+        The <adding client> and <removing client> can be used to add to
+        and remove from the invite list.  The format of the <adding client>
+        and <removing client> is as follows:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        When adding to or removing from the invite list the server must
+        send the notify type SILC_NOTIFY_TYPE_INVITE to its primary router
+        and must not send it to the client which was added to the list.
+        The client which executes this command must have at least channel
+        operator privileges to be able to add to or remove from the invite
+        list.  The wildcards may be used with this command.  If adding or
+        removing from than one clients then the lists are an comma (`,')
+        separated list.
+
+        Note that the <Client ID> provided must be resolved into correct
+        nickname and hostname and add to the invite list before sending
+        the notify packet.
+        
+        When this command is given with only <Channel ID> argument then
+        the command merely returns the invite list of the channel.   This
+        command must fail if the requested channel does not exist, the
+        requested <Client ID> is already on the channel or if the channel
+        is invite only channel and the caller of this command does not
+        have at least channel operator privileges.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<invite list>]
+
+       This command replies with the invite list of the channel if it
+       exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   8    SILC_COMMAND_QUIT
+
+        Max Arguments:  1
+            Arguments:  (1) [<quit message>]
+
+        This command is used by client to end SILC session.  The server
+        must close the connection to a client which sends this command.
+        if <quit message> is given it will be sent to other clients on
+        channel if the client is on channel when quitting.
+
+        Reply messages to the command:
+
+        This command does not reply anything.
+
+
+    9   SILC_COMMAND_KILL
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) [<comment>]
+
+        This command is used by SILC operators to remove a client from
+        SILC network.  The removing has temporary effects and client may
+        reconnect to SILC network.  The <Client ID> is the client to be
+        removed from SILC.  The <comment> argument may be provided to 
+        give to the removed client some information why it was removed
+        from the network.
+
+        When killing a client the router must first send notify type
+        SILC_NOTIFY_TYPE_KILLED to all channels the client has joined.
+        The packet must not be sent to the killed client on the channel.
+        Then, the router must send the same notify type to its primary
+        router.  Finally, the router must send the same notify type to
+        the client who was killed.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CLIENT_ID
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   10   SILC_COMMAND_INFO
+
+        Max Arguments:  2
+            Arguments:  (1) [<server>]  (2) [<Server ID>]
+
+        This command is used to fetch various information about a server.
+        If <server> argument is specified the command must be sent to
+        the requested server.
+
+        If the <Server ID> is specified the server information if fetched
+        by the provided Server ID.
+
+        Reply messages to the command:
+
+        Max Arguments:  4
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) <server name>     (4) <string>
+
+        This command replies with the Server ID of the server and a
+        string which tells the information about the server.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+            SILC_STATUS_ERR_NO_SERVER_ID
+
+
+   11   SILC_COMMAND_CONNECT
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used by operators to force a server to try to
+        establish a new connection to remote server or router. The
+        Operator must specify the server/router to be connected by
+        setting <remote server> argument.  The port is 32 bit MSB value.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+
+   12   SILC_COMMAND_PING
+
+        Max Arguments:  1
+            Arguments:  (1) <Server ID>
+
+        This command is used by client and server to test the communication
+        channel to its server if one suspects that the communication is not
+        working correctly.  The <Server ID> is the ID of the server the
+        sender is connected to.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.  Server returns
+        SILC_STATUS_OK in Status Payload if pinging was successful.
+
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SERVER_ID
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NOT_REGISTERED
+
+
+   13   SILC_COMMAND_OPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain server operator
+        privileges on some server or router.  Note that router operator
+        has router privileges that supersedes the server operator
+        privileges and this does not obtain those privileges.  Client
+        must use SILCOPER command to obtain router level privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key or certificate
+        authentication data (data signed with private key).
+
+        After changing the mode server must send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   14   SILC_COMMAND_JOIN
+
+        Max Arguments:  5
+            Arguments:  (1) <channel>       (2) <Client ID>
+                        (3) [<passphrase>]  (4) [<cipher>]
+                        (5) [<hmac>]
+
+        Join to channel/create new channel.  This command is used to
+        join to a channel.  If the channel does not exist the channel is
+        created.  If server is normal server this command must be sent
+        to router who will create the channel.  The channel may be
+        protected with passphrase.  If this is the case the passphrase
+        must be sent along the join command.
+
+        The name of the <channel> must not include any spaces (` '),
+        non-printable characters, commas (`,') or any wildcard characters.
+
+        The second argument <Client ID> is the Client ID of the client who
+        is joining to the client.  When client sends this command to the
+        server the <Client ID> must be the client's own ID.
+
+        Cipher to be used to secure the traffic on the channel may be
+        requested by sending the name of the requested <cipher>.  This
+        is used only if the channel does not exist and is created.  If
+        the channel already exists the cipher set previously for the
+        channel will be used to secure the traffic.  The computed MACs
+        of the channel message are produced by the default HMAC or by
+        the <hmac> provided for the command.
+
+        The server must check whether the user is allowed to join to
+        the requested channel.  Various modes set to the channel affect
+        the ability of the user to join the channel.  These conditions
+        are:
+
+            o  The user must be invited to the channel if the channel
+               is invite-only channel.
+
+            o  The Client ID/nickname/username/hostname must not match
+               any active bans.
+
+            o  The correct passphrase must be provided if passphrase 
+               is set to the channel.
+
+            o  The user count limit, if set, must not be reached.
+
+        Reply messages to the command:
+
+        Max Arguments:  14
+            Arguments:  (1) <Status Payload>        (2) <channel> 
+                        (3) <Channel ID>            (4) <Client ID>
+                        (5) <channel mode mask>     (6) <created>
+                        (7) [<Channel Key Payload>] (8) [<ban list>]
+                        (9) [<invite list>]         (10) [<topic>]
+                        (11) [<hmac>]               (12) <list count>
+                        (13) <Client ID list>       (14) <client mode list>
+
+        This command replies with the channel name requested by the
+        client, channel ID of the channel and topic of the channel
+        if it exists.  The <Client ID> is the Client ID which was joined
+        to the channel.  It also replies with the channel mode mask
+        which tells all the modes set on the channel.  If the
+        channel is created the mode mask is zero (0).  If ban mask
+        and/or invite list is set they are sent as well.
+
+        The <list count>, <Client ID list> and <client mode list> are
+        the clients currently on the channel and their modes on the
+        channel.  The <Client ID list> is formed by adding the ID Payloads
+        one after the other.  The <client mode list> is formed by adding
+        32 bit MSB first order values one after the other.
+
+        Client receives the channel key in the reply message as well
+        inside <Channel Key Payload>.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_WILDCARDS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_BAD_PASSWORD
+            SILC_STATUS_ERR_CHANNEL_IS_FULL
+            SILC_STATUS_ERR_NOT_INVITED
+            SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+            SILC_STATUS_ERR_BAD_CHANNEL
+            SILC_STATUS_ERR_USER_ON_CHANNEL
+
+
+   15   SILC_COMMAND_MOTD
+
+        Max Arguments:  1
+            Arguments:  (1) <server>
+
+        This command is used to query the Message of the Day of the server.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Server ID>
+                        (3) [<motd>]
+
+        This command replies with the motd message if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+
+
+   16   SILC_COMMAND_UMODE
+
+        Max Arguments:  2
+            Arguments:  (1) <Client ID>  (2) <client mode mask>
+
+        This command is used by client to set/unset modes for itself.
+        However, there are some modes that the client may not set itself,
+        but they will be set by server.  However, client may unset any
+        mode.  Modes may be masked together ORing them thus having
+        several modes set.  Client must keep its client mode mask
+        locally so that the mode setting/unsetting would work without
+        problems.  Client may change only its own modes.
+
+        After changing the mode server must send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        The following client modes are defined:
+
+           0x0000    SILC_UMODE_NONE
+
+              No specific mode for client.  This is the initial
+              setting when new client is created.  The client is
+              normal client now.
+
+
+           0x0001    SILC_UMODE_SERVER_OPERATOR
+
+              Marks the user as server operator.  Client cannot
+              set this mode itself.  Server sets this mode to the
+              client when client attains the server operator
+              privileges by SILC_COMMAND_OPER command.  Client
+              may unset the mode itself.
+
+
+           0x0002    SILC_UMODE_ROUTER_OPERATOR
+
+              Marks the user as router (SILC) operator.  Client
+              cannot this mode itself.  Router sets this mode to
+              the client when client attains the router operator
+              privileges by SILC_COMMAND_SILCOPER command.  Client
+              may unset the mode itself.
+
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <client mode mask>
+
+        This command replies with the changed client mode mask that
+        the client is required to keep locally.
+
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_BAD_CLIENT_ID
+            SILC_STATUS_ERR_NOT_YOU
+            SILC_STATUS_ERR_PERM_DENIED
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   17   SILC_COMMAND_CMODE
+
+        Max Arguments:  6
+            Arguments:  (1) <Channel ID>    (2) <channel mode mask>
+                        (3) [<user limit>]  (4) [<passphrase>]
+                        (5) [<cipher>]      (6) [<hmac>]
+
+        This command is used by client to set or change channel flags on
+        a channel.  Channel has several modes that set various properties
+        of a channel.  Modes may be masked together by ORing them thus
+        having several modes set.  The <Channel ID> is the ID of the
+        target channel.  The client changing channel mode must be on
+        the same channel and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CMODE_CHANGE notify
+        type is distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CMODE_NONE
+
+              No specific mode on channel.  This is the default when
+              channel is created.  This means that channel is just plain
+              normal channel.
+
+
+           0x0001    SILC_CMODE_PRIVATE
+
+              Channel is private channel.  Private channels are shown
+              in the channel list listed with SILC_COMMAND_LIST command
+              with indication that the channel is private.  Also,
+              client on private channel will no be detected to be on
+              the channel as the channel is not shown in the client's
+              currently joined channel list.  Channel founder and 
+              channel operator may set/unset this mode.
+
+              Typical implementation would use [+|-]p on user interface
+              to set/unset this mode.
+
+
+           0x0002    SILC_CMODE_SECRET
+
+              Channel is secret channel.  Secret channels are not shown
+              in the list listed with SILC_COMMAND_LIST command.  Secret
+              channels can be considered to be invisible channels.
+              Channel founder and channel operator may set/unset this
+              mode.
+
+              Typical implementation would use [+|-]s on user interface
+              to set/unset this mode.
+
+
+           0x0004    SILC_CMODE_PRIVKEY
+
+              Channel uses private channel key to protect the traffic
+              on the channel.  When this mode is set the client will be
+              responsible to set the key it wants to use to encrypt and
+              decrypt the traffic on channel.  Server generated channel
+              keys are not used at all.  This mode provides additional
+              security as clients on channel may agree to use private
+              channel key that even servers do not know.  Naturally,
+              this requires that every client on the channel knows
+              the key before hand (it is considered to be pre-shared-
+              key).  This specification does not define how the private
+              channel key is set as it is entirely local setting on
+              the client end.
+
+              As it is local setting it is possible to have several
+              private channel keys on one channel.  In this case several
+              clients can talk on same channel but only those clients
+              that share the key with the message sender will be able
+              to hear the talking.  Client should not display those
+              message for the end user that it is not able to decrypt
+              when this mode is set.
+
+              Only channel founder may set/unset this mode.  If this
+              mode is unset the server will distribute new channel
+              key to all clients on the channel which will be used
+              thereafter.
+
+              Typical implementation would use [+|-]k on user interface
+              to set/unset this mode.
+
+
+           0x0008    SILC_CMODE_INVITE
+
+              Channel is invite only channel.  Client may join to this
+              channel only if it is invited to the channel.  Channel
+              founder and channel operator may set/unset this mode.
+
+              Typical implementation would use [+|-]i on user interface
+              to set/unset this mode.
+
+
+           0x0010    SILC_CMODE_TOPIC
+
+              The topic of the channel may only be set by client that
+              is channel founder or channel operator.  Normal clients
+              on channel will not be able to set topic when this mode
+              is set.  Channel founder and channel operator may set/
+              unset this mode.
+
+              Typical implementation would use [+|-]t on user interface
+              to set/unset this mode.
+
+
+           0x0020    SILC_CMODE_ULIMIT
+
+              User limit has been set to the channel.  New clients
+              may not join to the channel when the limit set is
+              reached.  Channel founder and channel operator may set/
+              unset the limit.  The <user limit> argument is the
+              number of limited users.
+
+              Typical implementation would use [+|-]l on user interface
+              to set/unset this mode.
+
+
+           0x0040    SILC_CMODE_PASSPHRASE
+
+              Passphrase has been set to the channel.  Client may
+              join to the channel only if it is able to provide the
+              correct passphrase.  Setting passphrases to channel
+              is entirely safe as all commands are protected in the
+              SILC network.  Only channel founder may set/unset
+              the passphrase.  The <passphrase> argument is the
+              set passphrase.
+
+              Typical implementation would use [+|-]a on user interface
+              to set/unset this mode.
+
+
+           0x0080    SILC_CMODE_CIPHER
+
+              Sets specific cipher to be used to protect channel
+              traffic.  The <cipher> argument is the requested cipher.
+              When set or unset the server must re-generate new
+              channel key.  Only channel founder may set the cipher of 
+              the channel.  When unset the new key is generated using
+              default cipher for the channel.
+
+              Typical implementation would use [+|-]c on user interface
+              to set/unset this mode.
+
+
+           0x0100    SILC_CMODE_HMAC
+
+              Sets specific hmac to be used to compute the MACs of the
+              channel message.  The <hmac> argument is the requested hmac.
+              Only channel founder may set the hmac of the channel.
+
+              Typical implementation would use [+|-]h on user interface
+              to set/unset this mode.
+
+
+        To make the mode system work, client must keep the channel mode
+        mask locally so that the mode setting and unsetting would work
+        without problems.  The client receives the initial channel mode
+        mask when it joins to the channel.  When the mode changes on
+        channel the servers distributes the changed channel mode mask to
+        all clients on the channel by sending SILC_NOTIFY_TYPE_CMODE_CHANGE
+        notify type.
+
+        Reply messages to the command:
+
+        Max Arguments:  2
+            Arguments:  (1) <Status Payload>  (2) <channel mode mask>
+
+        This command replies with the changed channel mode mask that
+        client is required to keep locally.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+
+   18   SILC_COMMAND_CUMODE
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>  (2) <mode mask>
+                        (3) <Client ID>
+
+        This command is used by client to change channel user modes on
+        channel.  Users on channel may have some special modes and this
+        command is used by channel operators to set or change these modes.
+        The <Channel ID> is the ID of the target channel.  The <mode mask>
+        is OR'ed mask of modes.  The <Client ID> is the target client.
+        The client changing channel user modes must be on the same channel
+        as the target client and poses sufficient privileges to be able to
+        change the mode.
+
+        When the mode is changed SILC_NOTIFY_TYPE_CUMODE_CHANGE notify
+        type is distributed to the channel.
+
+        The following channel modes are defined:
+
+           0x0000    SILC_CUMODE_NONE
+
+              No specific mode.  This is the normal situation for client.
+              Also, this is the mode set when removing all modes from client.
+
+
+           0x0001    SILC_CUMODE_FOUNDER
+
+              The client is channel founder of the channel.  This mode
+              cannot be set by other client, it is set by the server when
+              the channel was founded (created).  The mode is provided 
+              because client may remove the founder rights from itself.
+
+
+           0x0002    SILC_CUMODE_OPERATOR
+
+              Sets channel operator privileges on the channel for a
+              client on the channel.  Channel founder and channel operator
+              may set/unset (promote/demote) this mode.
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <channel user mode mask>
+                        (3) <Client ID>
+
+        This command replies with the changed channel user mode mask that
+        client is required to keep locally.  The <Client ID> is the target
+        client.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_UNKNOWN_MODE
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+
+   19   SILC_COMMAND_KICK
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>  (2) <Client ID>  
+                        (3) [<comment>]
+
+        This command is used by channel operators to remove a client from
+        channel.  The <channel> argument is the channel the client to be
+        removed is on currently.  Note that the "kicker" must be on the same
+        channel.  If <comment> is provided it will be sent to the removed
+        client.
+
+        After kicking the client the server must send the notify type
+        SILC_NOTIFY_TYPE_KICKED to the channel and to its primary router.
+        The channel key must also be re-generated after kicking.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL
+            SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+            SILC_STATUS_ERR_NO_CLIENT_ID
+
+
+   20   SILC_COMMAND_RESTART
+
+        Max Arguments:  0
+            Arguments:  None
+
+        This command may only be used by server operator to force a
+        server to restart itself.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+
+
+   21   SILC_COMMAND_CLOSE
+
+        Max Arguments:  2
+            Arguments:  (1) <remote server/router>  (2) [<port>]
+
+        This command is used only by operator to close connection to a
+        remote site.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SUCH_SERVER
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+            SILC_STATUS_ERR_NO_SUCH_SERVER_ID
+
+
+   22   SILC_COMMAND_SHUTDOWN
+
+        Max Arguments:  0
+            Arguments:  None
+
+        This command is used only by operator to shutdown the server.
+        All connections to the server will be closed and the server is
+        shutdown.
+
+        Reply messages to the command:
+
+
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NO_SERVER_PRIV
+
+
+   23   SILC_COMMAND_SILCOPER
+
+        Max Arguments:  2
+            Arguments:  (1) <username>  (2) <authentication payload>
+
+        This command is used by normal client to obtain router operator
+        privileges (also known as SILC operator) on some router.  Note
+        that router operator has router privileges that supersedes the
+        server operator privileges.
+
+        The <username> is the username set in the server configurations
+        as operator.  The <authentication payload> is the data that the
+        client is authenticated against.  It may be passphrase prompted
+        for user on client's screen or it may be public key
+        authentication data (data signed with private key), or 
+        certificate.
+
+        Difference between router operator and server operator is that
+        router operator is able to handle cell level properties while
+        server operator (even on router server) is able to handle only
+        local properties, such as, local connections and normal server
+        administration.
+
+        After changing the mode server must send the notify type
+        SILC_NOTIFY_TYPE_UMODE_CHANGE to its primary router.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_AUTH_FAILED
+
+
+   24   SILC_COMMAND_LEAVE
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used by client to leave a channel the client is
+        joined to. 
+
+        When leaving the channel the server must send the notify type
+        SILC_NOTIFY_TYPE_LEAVE to its primary router and to the channel.
+        The channel key must also be re-generated when leaving the channel
+        and distribute it to all clients still currently on the channel.
+
+        Reply messages to the command:
+
+        Max Arguments:  1
+            Arguments:  (1) <Status Payload>
+
+        This command replies only with Status Payload.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+
+
+   25   SILC_COMMAND_USERS
+
+        Max Arguments:  1
+            Arguments:  (1) <Channel ID>
+
+        This command is used to list user names currently on the requested
+        channel; argument <Channel ID>.  The server must resolve the
+        user names and send a comma (`,') separated list of user names
+        on the channel.  Server or router may resolve the names by sending
+        SILC_COMMAND_WHOIS commands.
+
+        If the requested channel is a private or secret channel, this
+        command must not send the list of users, as private and secret
+        channels cannot be seen by outside.  In this case the returned
+        name list may include a indication that the server could not 
+        resolve the names of the users on the channel.  Also, in this case
+        Client ID's or client modes are not sent either.
+
+        Reply messages to the command:
+
+        Max Arguments:  5
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) <list count>      (4) <Client ID list>
+                        (5) <client mode list>
+
+        This command replies with the Channel ID of the requested channel
+        Client ID list of the users on the channel and list of their modes.
+        The Client ID list has Client ID's of all users in the list.  The 
+        <Client ID list> is formed by adding Client ID's one after another.
+        The <client mode list> is formed by adding client's user modes on
+        the channel one after another (4 bytes (32 bits) each).  The <list 
+        count> of length of 4 bytes (32 bits), tells the number of entries
+        in the lists.  Both lists must have equal number of entries.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_BAD_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+
+   26   SILC_COMMAND_BAN
+
+        Max Arguments:  3
+            Arguments:  (1) <Channel ID>         (2) [<adding client>]
+                        (3) [<removing client>]
+
+        This command is used to manage the ban list of the channel
+        indicated by the <Channel ID>.  A client that is banned from
+        channel is no longer able to join the channel.  The client which
+        is executing this command must have at least channel operator
+        privileges on the channel.
+
+        The <adding client> and <removing client> are used to add to and
+        remove from the ban list.  The format of the <adding client> and
+        the <removing client> is of following format:
+
+            [<nickname>[@<server>]!][<username>]@[<hostname>]
+
+        The server must send the notify type SILC_NOTIFY_TYPE_BAN to its
+        primary router after adding to or removing from the ban list.
+        The wildcards may be used with this command.  If adding or removing
+        from than one clients then the lists are an comma (`,') separated
+        list.
+
+        If this command is executed without the ban arguments the command
+        merely replies with the current ban list.
+
+
+        Reply messages to the command:
+
+        Max Arguments:  3
+            Arguments:  (1) <Status Payload>  (2) <Channel ID>
+                        (3) [<ban list>]
+
+        This command replies with the <Channel ID> of the channel and
+        the current <ban list> of the channel if it exists.
+
+        Status messages:
+
+            SILC_STATUS_OK
+            SILC_STATUS_ERR_NOT_REGISTERED
+            SILC_STATUS_ERR_TOO_MANY_PARAMS
+            SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+            SILC_STATUS_ERR_NO_CHANNEL_ID
+            SILC_STATUS_ERR_NOT_ON_CHANNEL
+            SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+
+   27 - 199
+
+        Currently undefined commands.
+
+
+   200 - 254
+
+        These commands are reserved for private use and will not be defined
+        in this document.
+
+
+   255  SILC_COMMAND_MAX   
+
+        Reserved command.  This must not be sent.
+.in 3
+
+
+.ti 0
+5.3 SILC Command Status Types
+
+.ti 0
+5.3.1 SILC Command Status Payload
+
+Command Status Payload is sent in command reply messages to indicate
+the status of the command.  The payload is one of argument in the
+command thus this is the data area in Command Argument Payload described
+in [SILC2].  The payload is only 2 bytes of length.  The following diagram
+represents the Command Status Payload (field is always in MSB order).
+
+
+
+
+
+.in 21
+.nf
+                     1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Status Message         |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 6:  SILC Command Status Payload
+
+
+.in 6
+o Status Message (2 bytes) - Indicates the status message.
+  All Status messages are described in the next section.
+.in 3
+
+
+.ti 0
+5.3.2 SILC Command Status List
+
+Command Status messages are returned in the command reply messages
+to indicate whether the command were executed without errors.  If error
+has occured the status tells which error occured.  Status payload only
+sends numeric reply about the status.  Receiver of the payload must
+convert the numeric values into human readable error messages.  The
+list of status messages below has an example human readable error
+messages that client may display for the user.
+
+List of all defined command status messages following.
+
+.in 0
+   Generic status messages:
+
+   0    SILC_STATUS_OK
+
+        Ok status.  Everything went Ok.  The status payload maybe
+        safely ignored in this case.
+
+   1    SILC_STATUS_LIST_START
+
+        Start of the list.  There will be several command replies and
+        this reply is the start of the list.
+
+   2    SILC_STATUS_LIST_ITEM
+
+        Item in the list.  This is one of the item in the list but not the
+        first or last one.
+
+   3    SILC_STATUS_LIST_END
+
+        End of the list.  There were several command replies and this
+        reply is the last of the list.  There won't be other replies
+        belonging to this list after this one.
+
+   4 - 9
+
+        Currently undefined and has been reserved for the future.
+
+
+   Error status message:
+
+   10   SILC_STATUS_ERR_NO_SUCH_NICK
+
+        "No such nickname".  Requested nickname does not exist.
+
+   11   SILC_STATUS_ERR_NO_SUCH_CHANNEL
+
+        "No such channel".  Requested channel name does not exist.
+
+   12   SILC_STATUS_ERR_NO_SUCH_SERVER
+
+        "No such server".  Requested server name does not exist.
+
+   13   SILC_STATUS_ERR_TOO_MANY_TARGETS
+
+        "Duplicate recipients. No message delivered".  Message were
+        tried to be sent to recipient which has several occurrences in 
+        the recipient list.
+
+   14   SILC_STATUS_ERR_NO_RECIPIENT
+
+        "No recipient given".  Command required recipient which was
+        not provided.
+
+   15   SILC_STATUS_ERR_UNKNOWN_COMMAND
+
+        "Unknown command".  Command sent to server is unknown by the
+        server.
+
+   16   SILC_STATUS_ERR_WILDCARDS
+
+        "Wildcards cannot be used".  Wildcards were provided but they
+        weren't permitted.
+
+   17   SILC_STATUS_ERR_NO_CLIENT_ID
+
+        "No Client ID given".  Client ID were expected as command
+        parameter but were not found.
+
+   18   SILC_STATUS_ERR_NO_CHANNEL_ID
+
+        "No Channel ID given".  Channel ID were expected as command
+        parameter but were not found.
+
+   19   SILC_STATUS_ERR_NO_SERVER_ID
+
+        "No Serve ID given".  Server ID were expected as command
+        parameter but were not found.
+
+   20   SILC_STATUS_ERR_BAD_CLIENT_ID
+
+        "Bad Client ID".  Client ID provided were erroneous.
+
+   21   SILC_STATUS_ERR_BAD_CHANNEL_ID
+
+        "Bad Channel ID".  Channel ID provided were erroneous.
+
+   22   SILC_STATUS_ERR_NO_SUCH_CLIENT_ID
+
+        "No such Client ID".  Client ID provided does not exist.
+
+   23   SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID
+
+        "No such Channel ID".  Channel ID provided does not exist.
+
+   24   SILC_STATUS_ERR_NICKNAME_IN_USE
+
+        "Nickname already exists".  Nickname created could not be 
+        registered because number of same nicknames were already set to
+        maximum.  This is not expected to happen in real life but is
+        possible to occur.
+
+   25   SILC_STATUS_ERR_NOT_ON_CHANNEL
+
+        "You are not on that channel".  The command were specified for
+        channel user is not currently on.
+
+   26   SILC_STATUS_ERR_USER_NOT_ON_CHANNEL
+
+        "They are not on channel".  The requested target client is not
+        on requested channel.
+
+   27   SILC_STATUS_ERR_USER_ON_CHANNEL
+
+        "User already on channel".  User were invited on channel they
+        already are on.
+
+   28   SILC_STATUS_ERR_NOT_REGISTERED
+
+        "You have not registered".  User executed command that requires
+        the client to be registered on the server before it may be
+        executed.
+
+   29   SILC_STATUS_ERR_NOT_ENOUGH_PARAMS
+
+        "Not enough parameters".  Command requires more parameters
+        than provided.
+
+   30   SILC_STATUS_ERR_TOO_MANY_PARAMS
+
+        "Too many parameters".  Too many parameters were provided
+        for the command.
+
+   31   SILC_STATUS_ERR_PERM_DENIED
+
+        "Permission denied".  Generic permission denied error status
+        to indicate disallowed access.
+
+   32   SILC_STATUS_ERR_BANNED_FROM_SERVER
+
+        "You are banned from this server".  The client tried to register
+        on server that has explicitly denied this host to connect.
+
+   33   SILC_STATUS_ERR_BAD_PASSWORD
+
+        "Cannot join channel. Incorrect password".  Password provided for 
+        channel were not accepted.
+
+   34   SILC_STATUS_ERR_CHANNEL_IS_FULL
+
+        "Cannot join channel. Channel is full".  The channel is full
+        and client cannot be joined to it.
+
+   35   SILC_STATUS_ERR_NOT_INVITED
+
+        "Cannot join channel. You have not been invited".  The channel
+        is invite only channel and client has not been invited.
+
+   36   SILC_STATUS_ERR_BANNED_FROM_CHANNEL
+
+        "Cannot join channel. You have been banned".  The client has
+        been banned from the channel.
+
+   37   SILC_STATUS_ERR_UNKNOWN_MODE
+
+        "Unknown mode".  Mode provided by the client were unknown to
+        the server.
+
+   38   SILC_STATUS_ERR_NOT_YOU
+
+        "Cannot change mode for other users".  User tried to change
+        someone else's mode.
+
+   39   SILC_STATUS_ERR_NO_CHANNEL_PRIV
+
+        "Permission denied. You are not channel operator".  Command may 
+        be executed only by channel operator.
+
+   40   SILC_STATUS_ERR_NO_CHANNEL_FOPRIV
+
+        "Permission denied. You are not channel founder".  Command may 
+        be executed only by channel operator.
+
+   41   SILC_STATUS_ERR_NO_SERVER_PRIV
+
+        "Permission denied. You are not server operator".  Command may
+        be executed only by server operator.
+
+   42   SILC_STATUS_ERR_NO_ROUTER_PRIV
+
+        "Permission denied. You are not SILC operator".  Command may be
+        executed only by router (SILC) operator.
+
+   43   SILC_STATUS_ERR_BAD_NICKNAME
+
+        "Bad nickname".  Nickname requested contained illegal characters
+        or were malformed.
+
+   44   SILC_STATUS_ERR_BAD_CHANNEL
+
+        "Bad channel name".  Channel requested contained illegal characters
+        or were malformed.
+
+   45   SILC_STATUS_ERR_AUTH_FAILED
+
+        "Authentication failed".  The authentication data sent as 
+        argument were wrong and thus authentication failed.
+
+   46   SILC_STATUS_ERR_UNKOWN_ALGORITHM
+
+        "The algorithm was not supported."  The server does not support the
+        requested algorithm.
+.in 3
+
+
+.ti 0
+6 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+
+.ti 0
+7 References
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             June 2000.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, June 2000.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+
+.ti 0
+8 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 6 Jun 2001 
diff --git a/doc/draft-riikonen-silc-spec-02.nroff b/doc/draft-riikonen-silc-spec-02.nroff
new file mode 100644 (file)
index 0000000..9359563
--- /dev/null
@@ -0,0 +1,2027 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 26 April 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-spec-02.txt                          26 April 2001
+Expires: 26 October 2001
+
+.in 3
+
+.ce 3
+Secure Internet Live Conferencing (SILC),
+Protocol Specification
+<draft-riikonen-silc-spec-02.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside the SILC network.
+Three other Internet Drafts relates very closely to this memo;
+SILC Packet Protocol [SILC2], SILC Key Exchange and Authentication
+Protocols [SILC3] and SILC Commands [SILC4].
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Concepts .................................................  4
+  2.1 SILC Network Topology .....................................  4
+  2.2 Communication Inside a Cell ...............................  5
+  2.3 Communication in the Network ..............................  6
+  2.4 Channel Communication .....................................  7
+  2.5 Router Connections ........................................  7
+  2.6 Backup Routers ............................................  8
+3 SILC Specification ............................................ 10
+  3.1 Client .................................................... 10
+      3.1.1 Client ID ........................................... 10
+  3.2 Server .................................................... 11
+      3.2.1 Server's Local ID List .............................. 12
+      3.2.2 Server ID ........................................... 13
+      3.2.3 SILC Server Ports ................................... 14
+  3.3 Router .................................................... 14
+      3.3.1 Router's Local ID List .............................. 14
+      3.3.2 Router's Global ID List ............................. 15
+      3.3.3 Router's Server ID .................................. 15
+  3.4 Channels .................................................. 16
+      3.4.1 Channel ID .......................................... 17
+  3.5 Operators ................................................. 17
+  3.6 SILC Commands ............................................. 18
+  3.7 SILC Packets .............................................. 18
+  3.8 Packet Encryption ......................................... 19
+      3.8.1 Determination of the Source and the Destination ..... 19
+      3.8.2 Client To Client .................................... 20
+      3.8.3 Client To Channel ................................... 21
+      3.8.4 Server To Server .................................... 22
+  3.9 Key Exchange And Authentication ........................... 22
+      3.9.1 Authentication Payload .............................. 22
+  3.10 Algorithms ............................................... 24
+      3.10.1 Ciphers ............................................ 24
+      3.10.2 Public Key Algorithms .............................. 25
+      3.10.3 Hash Functions ..................................... 26
+      3.10.4 MAC Algorithms ..................................... 26
+      3.10.5 Compression Algorithms ............................. 26
+  3.11 SILC Public Key .......................................... 27
+  3.12 SILC Version Detection ................................... 29
+4 SILC Procedures ............................................... 30
+  4.1 Creating Client Connection ................................ 30
+  4.2 Creating Server Connection ................................ 31
+      4.2.1 Announcing Clients, Channels and Servers ............ 32
+  4.3 Joining to a Channel ...................................... 33
+  4.4 Channel Key Generation .................................... 34
+  4.5 Private Message Sending and Reception ..................... 34
+  4.6 Private Message Key Generation ............................ 35
+  4.7 Channel Message Sending and Reception ..................... 35
+  4.8 Session Key Regeneration .................................. 36
+  4.9 Command Sending and Reception ............................. 37
+  4.10 Closing Connection ....................................... 37
+5 Security Considerations ....................................... 38
+6 References .................................................... 38
+7 Author's Address .............................................. 39
+
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  SILC Network Topology
+Figure 2:  Communication Inside cell
+Figure 3:  Communication Between Cells
+Figure 4:  Router Connections
+Figure 5:  SILC Public Key
+
+
+.ti 0
+1. Introduction
+
+This document describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.
+
+Strong cryptographic methods are used to protect SILC packets inside
+the SILC network.  Three other Internet Drafts relates very closely
+to this memo; SILC Packet Protocol [SILC2], SILC Key Exchange and
+Authentication Protocols [SILC3] and SILC Commands [SILC4].
+
+The protocol uses extensively packets as conferencing protocol 
+requires message and command sending.  The SILC Packet Protocol is
+described in [SILC2] and should be read to fully comprehend this
+document and protocol.  [SILC2] also describes the packet encryption
+and decryption in detail.
+
+The security of SILC protocol, and for any security protocol for that
+matter, is based on strong and secure key exchange protocol.  The SILC
+Key Exchange protocol is described in [SILC3] along with connection
+authentication protocol and should be read to fully comprehend this
+document and protocol.
+
+The SILC protocol has been developed to work on TCP/IP network
+protocol, although it could be made to work on other network protocols
+with only minor changes.  However, it is recommended that TCP/IP
+protocol is used under SILC protocol.  Typical implementation would
+be made in client-server model.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2. SILC Concepts
+
+This section describes various SILC protocol concepts that forms the 
+actual protocol, and in the end, the actual SILC network.  The mission
+of the protocol is to deliver messages from clients to other clients 
+through routers and servers in secure manner.  The messages may also 
+be delivered from one client to many clients forming a group, also 
+known as a channel.
+
+This section does not focus to security issues.  Instead, basic network 
+concepts are introduced to make the topology of the SILC network 
+clear.
+
+
+.ti 0
+2.1 SILC Network Topology
+
+SILC network is a cellular network as opposed to tree style network 
+topology.  The rationale for this is to have servers that can perform 
+specific kind of tasks what other servers cannot perform.  This leads 
+to two kinds of servers; normal SILC servers and SILC routers.
+
+A difference between normal server and router server is that routers 
+knows everything about everything in the network.  They also do the 
+actual routing of the messages to the correct receiver.  Normal servers 
+knows only about local information and nothing about global information.
+This makes the network faster as there are less servers that needs to 
+keep global information up to date at all time.
+
+This, on the other hand, leads to cellular like network, where routers 
+are in the center of the cell and servers are connected to the router.
+
+
+
+
+
+
+
+The following diagram represents SILC network topology.
+
+.in 8
+.nf
+  ---- ---- ----         ---- ---- ----
+ | S8 | S5 | S4 |       | S7 | S5 | S6 |
+ ----- ---- -----       ----- ---- -----
+| S7 | S/R1 | S2 | --- | S8 | S/R2 | S4 |
+ ---- ------ ----       ---- ------ ----
+ | S6 | S3 | S1 |       | S1 | S3 | S2 |         ---- ----
+  ---- ---- ----         ---- ---- ----         | S3 | S1 |
+     Cell 1.   \\             Cell 2.  | \\____  ----- -----
+                |                     |        | S4 | S/R4 |
+    ---- ---- ----         ---- ---- ----       ---- ------
+   | S7 | S4 | S2 |       | S1 | S3 | S2 |      | S2 | S5 |
+   ----- ---- -----       ----- ---- -----       ---- ----
+  | S6 | S/R3 | S1 | --- | S4 | S/R5 | S5 | ____/ Cell 4.
+   ---- ------ ----       ---- ------ ----
+   | S8 | S5 | S3 |       | S6 | S7 | S8 |     ... etc ...
+    ---- ---- ----         ---- ---- ----
+       Cell 3.                Cell 5.
+.in 3
+
+.ce
+Figure 1:  SILC Network Topology
+
+
+A cell is formed when a server or servers connect to one router.  In
+SILC network normal server cannot directly connect to other normal
+server.  Normal server may only connect to SILC router which then
+routes the messages to the other servers in the cell.  Router servers
+on the other hand may connect to other routers to form the actual SILC 
+network, as seen in above figure.  However, router is also normal SILC 
+server; clients may connect to it the same way as to normal SILC 
+server.  Normal server also cannot have active connections to more 
+than one router.  Normal server cannot be connected to two different 
+cells.  Router servers, on the other hand, may have as many router to 
+router connections as needed.
+
+There are many issues in this network topology that needs to be careful
+about.  Issues like the size of the cells, the number of the routers in 
+the SILC network and the capacity requirements of the routers.  These
+issues should be discussed in the Internet Community and additional
+documents on the issue may be written.
+
+
+.ti 0
+2.2 Communication Inside a Cell
+
+It is always guaranteed that inside a cell message is delivered to the 
+recipient with at most two server hops.  A client which is connected to
+server in the cell and is talking on channel to other client connected 
+to other server in the same cell, will have its messages delivered from 
+its local server first to the router of the cell, and from the router 
+to the other server in the cell.
+
+The following diagram represents this scenario:
+
+
+.in 25
+.nf
+1 --- S1     S4 --- 5
+         S/R
+ 2 -- S2     S3
+     /        |
+    4         3
+.in 3
+
+
+.ce
+Figure 2:  Communication Inside cell
+
+
+Example:  Client 1. connected to Server 1. send message to
+          Client 4. connected to Server 2. travels from Server 1.
+          first to Router which routes the message to Server 2.
+          which then sends it to the Client 4.  All the other
+          servers in the cell will not see the routed message.
+
+
+If the client is connected directly to the router, as router is also normal
+SILC server, the messages inside the cell are always delivered only with 
+one server hop.  If clients communicating with each other are connected 
+to the same server, no router interaction is needed.  This is the optimal
+situation of message delivery in the SILC network.
+
+
+.ti 0
+2.3 Communication in the Network
+
+If the message is destined to server that does not belong to local cell 
+the message is routed to the router server to which the destination 
+server belongs, if the local router is connected to destination router.
+If there is no direct connection to the destination router, the local
+router routes the message to its primary route.  The following diagram
+represents message sending between cells.
+
+
+.in 16
+.nf
+1 --- S1     S4 --- 5            S2 --- 1
+         S/R - - - - - - - - S/R
+ 2 -- S2     S3           S1
+     /        |             \\
+    4         3              2
+
+   Cell 1.               Cell 2.
+.in 3
+
+
+.ce
+Figure 3:  Communication Between Cells
+
+
+Example:  Client 5. connected to Server 4. in Cell 1. sends message
+          to Client 2. connected to Server 1. in Cell 2. travels
+          from Server 4. to Router which routes the message to
+          Router in Cell 2, which then routes the message to 
+          Server 1.  All the other servers and routers in the
+          network will not see the routed message.
+
+
+The optimal case of message delivery from the client point of view is
+when clients are connected directly to the routers and the messages
+are delivered from one router to the other.
+
+
+.ti 0 
+2.4 Channel Communication
+
+Messages may be sent to group of clients as well.  Sending messages to
+many clients works the same way as sending messages point to point, from
+message delivery point of view.  Security issues are another matter
+which are not discussed in this section.
+
+Router server handles the message routing to multiple recipients.  If 
+any recipient is not in the same cell as the sender the messages are 
+routed further.
+
+Server distributes the channel message to its local clients which are 
+joined to the channel.  Router also distributes the message to its 
+local clients on the channel.
+
+
+.ti 0
+2.5 Router Connections
+
+Router connections play very important role in making the SILC like
+network topology to work.  For example, sending broadcast packets in
+SILC network require special connections between routers; routers must
+be connected in a specific way.
+
+Every router has their primary route which is a connection to another
+router in the network.  Unless there is only two routers in the network
+must not routers use each other as their primary routes.  The router
+connections in the network must form a circular.
+
+
+
+
+
+
+
+Example with three routers in the network:
+
+
+.in 16
+.nf
+    S/R1 - > - > - > - > - > - > - S/R2
+     \\                               /
+      ^                             v
+       \\ - < -  < - S/R3 - < - < - /
+.in 3
+
+
+.ce
+Figure 4:  Router Connections
+
+
+Example:  Network with three routers.  Router 1. uses Router 2. as its
+          primary router.  Router 2. uses Router 3. as its primary router,
+          and Router 3. uses Router 1. as its primary router.  There may
+          be other direct connections between the routers but they must
+          not be used as primary routes.
+
+The above example is applicable to any amount of routers in the network
+except for two routers.  If there are only two routers in the network both
+routers must be able to handle situation where they use each other as their
+primary routes.
+
+The issue of router connections are very important especially with SILC
+broadcast packets.  Usually all router wide information in the network is
+distributed by SILC broadcast packets.
+
+
+.ti 0
+2.6 Backup Routers
+
+Backup routers may exist in the cell in addition of the primary router.
+However, they must not be active routers and act as routers in the cell.
+Only one router may be acting as primary router in the cell.  In the case
+of failure of the primary router may one of the backup routers become
+active.  The purpose of backup routers are in case of failure of the
+primary router to maintain working connections inside the cell and outside
+the cell and to avoid netsplits.
+
+Backup routers are normal servers in the cell that are prepared to take
+over the tasks of the primary router if needed.  They need to have at
+least one direct and active connection to the primary router of the cell.
+This communication channel is used to send the router information to
+the backup router.
+
+Backup router must know everything that the primary router knows to be
+able to take over the tasks of the primary router.  It is the primary
+router's responsibility to feed the data to the backup router.  If the
+backup router does not know all the data in the case of failure some
+connections may be lost.  The primary router of the cell must consider
+the backup router being normal router server and feed the data
+accordingly.
+
+In addition of having direct connection to the primary router of the
+cell the backup router must also have connection to the same router
+the primary router of the cell is connected.  However, it must not be
+active router connection meaning that the backup router must not use
+that channel as its primary route and it must not notify the router
+about having connected servers, channels and clients behind it.  It
+merely connects to the router.  This sort of connection is later
+referred as being passive connection.  Some keepalive actions may be
+needed by the router to keep the connection alive.
+
+The primary router notifies its primary router about having backup
+routers in the cell by sending SILC_PACKET_CELL_ROUTERS packet.  If
+and when the primary router of the cell becomes unresponsive, its
+primary router knows that there exists backup routers in the cell.  
+After that it will start using the first backup router sent in the
+packet as router of that cell.
+
+In this case the backup router must notify its new primary router about
+the servers, channels and clients it has connected to it.  The primary
+router knows that this server has become a router of the cell because
+of failure of the primary router in the cell.  It must also cope with
+the fact that the servers, channels and clients that the new backup
+router announces are not really new, since they used to exist in the
+primary router of the cell.
+
+It is required that other normal servers has passive connections to
+the backup router(s) in the cell.  Some keepalive actions may be needed
+by the server to keep the connection alive.  After they notice the
+failure of the primary router they must start using the connection to
+the first backup router as their primary route.
+
+It is RECOMMENDED that there would be at least one backup router in
+the cell.  It is NOT RECOMMENDED to have all servers in the cell acting
+as backup routers as it requires establishing several connections to
+several servers in the cell.  Large cells can easily have several
+backup routers in the cell.
+
+The order of the backup routers are decided at the primary router of the
+cell and servers and backup routers in the cell must be configured
+accordingly.  It is not required that the backup server is actually
+active server in the cell.  Backup router may be a spare server in the
+cell that does not accept normal client connections at all.  It may be
+reserved purely for the backup purposes.  These, however, are cell
+management issues.
+
+If also the first backup router is down as well and there is another
+backup router in the cell then it will start acting as the primary
+router as described above.
+
+
+.ti 0
+3. SILC Specification
+
+This section describes the SILC protocol.  However, [SILC2] and
+[SILC3] describes other important protocols that are part of this SILC
+specification and must be read.
+
+
+.ti 0
+3.1 Client
+
+A client is a piece of software connecting to SILC server.  SILC client 
+cannot be SILC server.  Purpose of clients is to provide the user 
+interface of the SILC services for end user.  Clients are distinguished
+from other clients by unique Client ID.  Client ID is a 128 bit ID that
+is used in the communication in the SILC network.  The client ID is 
+based on the nickname selected by the user.  User uses logical nicknames
+in communication which are then mapped to the corresponding Client ID.
+Client ID's are low level identifications and must not be seen by the
+end user.
+
+Clients provide other information about the end user as well. Information
+such as the nickname of the user, username and the host name of the end 
+user and user's real name.  See section 3.2 Server for information of 
+the requirements of keeping this information.
+
+The nickname selected by the user is not unique in the SILC network.
+There can be 2^8 same nicknames for one IP address.  As for comparison
+to IRC [IRC] where nicknames are unique this is a fundamental difference
+between SILC and IRC.  This causes the server names or client's host names
+to be used along with the nicknames to identify specific users when sending
+messages.  This feature of SILC makes IRC style nickname-wars obsolete as
+no one owns their nickname; there can always be someone else with the same
+nickname.  The maximum length of nickname is 128 characters.
+
+
+.ti 0
+3.1.1 Client ID
+
+Client ID is used to identify users in the SILC network.  The Client ID
+is unique to the extent that there can be 2^128 different Client ID's,
+and ID's based on IPv6 addresses extends this to 2^224 different Client
+ID's.  Collisions are not expected to happen.  The Client ID is defined
+as follows.
+
+
+
+.in 6
+128 bit Client ID based on IPv4 addresses:
+
+32 bit  Server ID IP address (bits 1-32)
+ 8 bit  Random number or counter
+88 bit  Truncated MD5 hash value of the nickname
+
+224 bit Client ID based on IPv6 addresses:
+
+128 bit  Server ID IP address (bits 1-128)
+  8 bit  Random number or counter
+ 88 bit  Truncated MD5 hash value of the nickname
+
+o Server ID IP address - Indicates the server where this
+  client is coming from.  The IP address hence equals the
+  server IP address where to the client has connected.
+
+o Random number or counter - Random number to further 
+  randomize the Client ID.  Another choice is to use
+  a counter starting from the zero (0).  This makes it
+  possible to have 2^8 same nicknames from the same
+  server IP address.
+
+o MD5 hash - MD5 hash value of the nickname is truncated
+  taking 88 bits from the start of the hash value.  This
+  hash value is used to search the user's Client ID from
+  the ID lists.
+
+.in 3
+Collisions could occur when more than 2^8 clients using same nickname
+from the same server IP address is connected to the SILC network.  
+Server MUST be able to handle this situation by refusing to accept 
+anymore of that nickname.
+
+Another possible collision may happen with the truncated hash value of
+the nickname.  It could be possible to have same truncated hash value for
+two different nicknames.  However, this is not expected to happen nor
+cause any problems if it would occur.  Nicknames are usually logical and
+it is unlikely to have two distinct logical nicknames produce same
+truncated hash value.
+
+
+.ti 0
+3.2 Server
+
+Servers are the most important parts of the SILC network.  They form the
+basis of the SILC, providing a point to which clients may connect to.
+There are two kinds of servers in SILC; normal servers and router servers.
+This section focus on the normal server and router server is described
+in the section 3.3 Router.
+
+Normal servers MUST NOT directly connect to other normal server.  Normal
+servers may only directly connect to router server.  If the message sent
+by the client is destined outside the local server it is always sent to
+the router server for further routing.  Server may only have one active
+connection to router on same port.  Normal server MUST NOT connect to other
+cell's router except in situations where its cell's router is unavailable.
+
+Servers and routers in the SILC network are considered to be trusted.
+With out a doubt, servers that are set to work on ports above 1023 are
+not considered to be trusted.  Also, the service provider acts important
+role in the server's trustworthy.
+
+
+.ti 0
+3.2.1 Server's Local ID List
+
+Normal server keeps various information about the clients and their end
+users connected to it.  Every normal server MUST keep list of all locally
+connected clients, Client ID's, nicknames, usernames and host names and
+user's real name.  Normal servers only keeps local information and it
+does not keep any global information.  Hence, normal servers knows only
+about their locally connected clients.  This makes servers efficient as
+they don't have to worry about global clients.  Server is also responsible
+of creating the Client ID's for their clients.
+
+Normal server also keeps information about locally created channels and
+their Channel ID's.
+
+
+Hence, local list for normal server includes:
+
+.in 6
+server list        - Router connection
+   o Server name
+   o Server IP address
+   o Server ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+client list        - All clients in server
+   o Nickname
+   o Username@host
+   o Real name
+   o Client ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+
+channel list       - All channels in server
+   o Channel name
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+.ti 0
+3.2.2 Server ID
+
+Servers are distinguished from other servers by unique 64 bit Server ID 
+(for IPv4) or 160 bit Server ID (for IPv6).  The Server ID is used in
+the SILC to route messages to correct servers.  Server ID's also provide
+information for Client ID's, see section 3.1.1 Client ID.  Server ID is
+defined as follows.
+
+.in 6
+64 bit Server ID based on IPv4 addresses:
+
+32 bit  IP address of the server
+16 bit  Port
+16 bit  Random number
+
+160 bit Server ID based on IPv6 addresses:
+
+128 bit  IP address of the server
+ 16 bit  Port
+ 16 bit  Random number
+
+o IP address of the server - This is the real IP address of
+  the server.
+
+o Port - This is the port the server is bound to.
+
+o Random number - This is used to further randomize the Server ID.
+
+.in 3
+Collisions are not expected to happen in any conditions.  The Server ID
+is always created by the server itself and server is responsible of
+distributing it to the router.
+
+
+.ti 0
+3.2.3 SILC Server Ports
+
+The following ports has been assigned by IANA for the SILC protocol:
+
+.in 10
+silc            706/tcp    SILC
+silc            706/udp    SILC
+.in 3
+
+
+If there are needs to create new SILC networks in the future the port
+numbers must be officially assigned by the IANA.
+
+Server on network above privileged ports (>1023) SHOULD NOT be trusted
+as they could have been set up by untrusted party.
+
+
+.ti 0
+3.3 Router
+
+Router server in SILC network is responsible for keeping the cell together
+and routing messages to other servers and to other routers.  Router server
+is also a normal server thus clients may connect to it as it would be
+just normal SILC server.
+
+However, router servers has a lot of important tasks that normal servers
+do not have.  Router server knows everything about everything in the SILC.
+They know all clients currently on SILC, all servers and routers and all
+channels in SILC.  Routers are the only servers in SILC that care about
+global information and keeping them up to date at all time.  And, this
+is what they must do.
+
+
+.ti 0
+3.3.1 Router's Local ID List
+
+Router server as well MUST keep local list of connected clients and
+locally created channels.  However, this list is extended to include all
+the informations of the entire cell, not just the server itself as for
+normal servers.
+
+However, on router this list is a lot smaller since routers do not need
+to keep information about user's nickname, username and host name and real
+name since these are not needed by the router.  The router keeps only
+information that it needs.
+
+
+Hence, local list for router includes:
+
+.in 6
+server list        - All servers in the cell
+   o Server name
+   o Server ID
+   o Router's Server ID
+   o Sending key
+   o Receiving key
+
+client list        - All clients in the cell
+   o Client ID
+
+
+channel list       - All channels in the cell
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+Note that locally connected clients and other information include all the
+same information as defined in section section 3.2.1 Server's Local ID
+List.
+
+
+.ti 0
+3.3.2 Router's Global ID List
+
+Router server MUST also keep global list.  Normal servers do not have
+global list as they know only about local information.  Global list
+includes all the clients on SILC, their Client ID's, all created channels
+and their Channel ID's and all servers and routers on SILC and their
+Server ID's.  That is said, global list is for global information and the
+list must not include the local information already on the router's local
+list.
+
+Note that the global list does not include information like nicknames,
+usernames and host names or user's real names.  Router does not need to
+keep these informations as they are not needed by the router.  This 
+information is available from the client's server which maybe queried
+when needed.
+
+Hence, global list includes:
+
+.in 6
+server list        - All servers in SILC
+   o Server name
+   o Server ID
+   o Router's Server ID
+
+client list        - All clients in SILC
+   o Client ID
+
+channel list       - All channels in SILC
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+.in 3
+
+
+
+
+
+
+
+
+.ti 0
+3.3.3 Router's Server ID
+
+Router's Server ID's are equivalent to normal Server ID's.  As routers
+are normal servers as well same types of ID's applies for routers as well.
+Thus, see section 3.2.2 Server ID.
+
+
+.ti 0
+3.4 Channels
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client requests JOIN command to the channel, and the channel ceases to
+exist when the last client has left it.  When channel exists, any client
+can reference it using the name of the channel.
+
+Channel names are unique although the real uniqueness comes from 64 bit
+Channel ID.  However, channel names are still unique and no two global
+channels with same name may exist.  The Channel name is a string of
+maximum length of 256 characters.  Channel names MUST NOT contain any
+spaces (`  '), any non-printable ASCII characters, commas (`,') and
+wildcard characters.
+
+Channels can have operators that can administrate the channel and
+operate all of its modes.  The following operators on channel exist on
+the SILC network.
+
+.in 6
+o Channel founder - When channel is created the joining client becomes
+  channel founder.  Channel founder is channel operator with some more
+  privileges.  Basically, channel founder can fully operate the channel
+  and all of its modes.  The privileges are limited only to the
+  particular channel.  There can be only one channel founder per
+  channel. Channel founder supersedes channel operator's privileges.
+
+  Channel founder privileges cannot be removed by any other operator on
+  channel.  When channel founder leaves the channel there is no channel
+  founder on the channel.  However, it is possible to set a mode for
+  the channel which allows the original channel founder to regain the
+  founder privileges even after leaving the channel.  Channel founder
+  also cannot be removed by force from the channel.
+
+o Channel operator - When client joins to channel that has not existed
+  previously it will become automatically channel operator (and channel
+  founder discussed above).  Channel operator is able administrate the
+  channel, set some modes on channel, remove a badly behaving client
+  from the channel and promote other clients to become channel
+  operator.  The privileges are limited only to the particular channel.
+
+  Normal channel user may be promoted (opped) to channel operator
+  gaining channel operator privileges.  Channel founder or other
+  channel operator may also demote (deop) channel operator to normal
+  channel user.
+.in 3
+
+
+.ti 0
+3.4.1 Channel ID
+
+Channels are distinguished from other channels by unique Channel ID.
+The Channel ID is a 64 bit ID (for IPv4) or 160 bit ID (for IPv6), and
+collisions are not expected to happen in any conditions.  Channel names
+are just for logical use of channels.  The Channel ID is created by the
+server where the channel is created.  The Channel ID is defined as
+follows.
+
+.in 6
+64 bit Channel ID based on IPv4 addresses:
+
+32 bit  Router's Server ID IP address (bits 1-32)
+16 bit  Router's Server ID port (bits 33-48)
+16 bit  Random number
+
+160 bit Channel ID based on IPv6 addresses:
+
+128 bit  Router's Server ID IP address (bits 1-128)
+ 16 bit  Router's Server ID port (bits 129-144)
+ 16 bit  Random number
+
+o Router's Server ID IP address - Indicates the IP address of 
+  the router of the cell where this channel is created.  This is 
+  taken from the router's Server ID.  This way SILC router knows 
+  where this channel resides in the SILC network.
+
+o Router's Server ID port - Indicates the port of the channel on 
+  the server.  This is taken from the router's Server ID.
+
+o Random number - To further randomize the Channel ID.  This makes
+  sure that there are no collisions.  This also means that
+  in a cell there can be 2^16 channels.
+.in 3
+
+
+.ti 0
+3.5 Operators
+
+Operators are normal users with extra privileges to their server or
+router.  Usually these people are SILC server and router administrators
+that take care of their own server and clients on them.  The purpose of
+operators is to administrate the SILC server or router.  However, even
+an operator with highest privileges is not able to enter invite-only
+channel, to gain access to the contents of a encrypted and authenticated
+packets traveling in the SILC network or to gain channel operator
+privileges on public channels without being promoted.  They have the
+same privileges as everyone else except they are able to administrate
+their server or router.
+
+
+.ti 0
+3.6 SILC Commands
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+Client usually sends the commands and server replies by sending a reply
+packet to the command.  Server MAY also send commands usually to serve
+the original client's request.  However, server MUST NOT send commands
+to client and there are some commands that server must not send.
+
+Note that the command reply is usually sent only after client has sent
+the command request but server is allowed to send command reply packet
+to client even if client has not requested the command.  Client MAY,
+choose to ignore the command reply.
+
+It is expected that some of the commands may be miss-used by clients
+resulting various problems on the server side.  Every implementation
+SHOULD assure that commands may not be executed more than once, say,
+in two (2) seconds.  However, to keep response rate up, allowing for
+example five (5) commands before limiting is allowed.  It is RECOMMENDED
+that commands such as SILC_COMMAND_NICK, SILC_COMMAND_JOIN, 
+SILC_COMMAND_LEAVE and SILC_COMMAND_KILL SHOULD be limited in all cases
+as they require heavy operations.  This should be sufficient to prevent
+the miss-use of commands.
+
+SILC commands are described in [SILC4].
+
+
+.ti 0
+3.7 SILC Packets
+
+Packets are naturally the most important part of the protocol and the
+packets are what actually makes the protocol.  Packets in SILC network
+are always encrypted using, usually the shared secret session key
+or some other key, for example, channel key, when encrypting channel
+messages.  The SILC Packet Protocol is a wide protocol and is described
+in [SILC2].  This document does not define or describe details of
+SILC packets.
+
+
+
+
+
+.ti 0
+3.8 Packet Encryption
+
+All packets passed in SILC network MUST be encrypted.  This section
+defines how packets must be encrypted in the SILC network.  The detailed
+description of the actual encryption process of the packets are
+described in [SILC2].
+
+Client and its server shares secret symmetric session key which is
+established by the SILC Key Exchange Protocol, described in [SILC3]. 
+Every packet sent from client to server, with exception of packets for
+channels, are encrypted with this session key.
+
+Channels has their own key that are shared by every client on the channel.
+However, the channel keys are cell specific thus one cell does not know
+the channel key of the other cell, even if that key is for same channel.
+Channel key is also known by the routers and all servers that has clients
+on the channel.  However, channels MAY have channel private keys that
+are entirely local setting for the client.  All clients on the channel
+MUST know the channel private key before hand to be able to talk on the
+channel.  In this case, no server or router know the key for channel.
+
+Server shares secret symmetric session key with router which is
+established by the SILC Key Exchange Protocol.  Every packet passed from
+server to router, with exception of packets for channels, are encrypted
+with the shared session key.  Same way, router server shares secret
+symmetric key with its primary route.  However, every packet passed
+from router to other router, including packets for channels, are
+encrypted with the shared session key.  Every router connection has
+their own session keys.
+
+
+.ti 0
+3.8.1 Determination of the Source and the Destination
+
+The source and the destination of the packet needs to be determined
+to be able to route the packets to correct receiver.  This information
+is available in the SILC Packet Header which is included in all packets
+sent in SILC network.  The SILC Packet Header is described in [SILC2].
+
+The header MUST be encrypted with the session key who is next receiver
+of the packet along the route.  The receiver of the packet, for example
+a router along the route, is able to determine the sender and the
+destination of the packet by decrypting the SILC Packet Header and
+checking the ID's attached to the header.  The ID's in the header will
+tell to where the packet needs to be sent and where it is coming from.
+
+The header in the packet MUST NOT change during the routing of the
+packet.  The original sender, for example client, assembles the packet
+and the packet header and server or router between the sender and the
+receiver MUST NOT change the packet header.
+
+Note that the packet and the packet header may be encrypted with
+different keys.  For example, packets to channels are encrypted with
+the channel key, however, the header is encrypted with the session key
+as described above.  However, the header and the packet may be encrypted
+with same key.  This is the case, for example, with command packets.
+
+
+.ti 0
+3.8.2 Client To Client
+
+The process of message delivery and encryption from client to another
+client is as follows.
+
+Example:  Private message from client to another client on different
+          servers.  Clients do not share private message delivery
+          keys; normal session keys are used.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the session key shared between client and its
+  server.
+
+o Server determines the destination of the packet and decrypts
+  the packet.  Server encrypts the packet with session key shared
+  between the server and its router, and sends the packet to the
+  router.
+
+o Router determines the destination of the packet and decrypts
+  the packet.  Router encrypts the packet with session key 
+  shared between the router and the destination server, and sends
+  the packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and decrypts the packet.  Server encrypts the packet with
+  session key shared between the server and the destination client,
+  and sends the packet to the client.
+
+o Client 2. decrypts the packet.
+
+
+Example:  Private message from client to another client on different
+          servers.  Clients has established secret shared private
+          message delivery key with each other and that is used in 
+          the message encryption.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the private message delivery key shared between
+  clients.
+
+o Server determines the destination of the packet and sends the 
+  packet to the router.
+
+o Router determines the destination of the packet and sends the
+  packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and sends the packet to the client.
+
+o Client 2. decrypts the packet with the secret shared key.
+
+
+If clients share secret key with each other the private message
+delivery is much simpler since servers and routers between the
+clients do not need to decrypt and re-encrypt the packet.
+
+The process for clients on same server is much simpler as there are
+no need to send the packet to the router.  The process for clients 
+on different cells is same as above except that the packet is routed 
+outside the cell.  The router of the destination cell routes the 
+packet to the destination same way as described above.
+
+
+.ti 0
+3.8.3 Client To Channel
+
+Process of message delivery from client on channel to all the clients
+on the channel.
+
+Example:  Channel of four users; two on same server, other two on
+          different cells.  Client sends message to the channel.
+
+o Client 1. encrypts the packet with channel key and sends the
+  packet to its server.
+
+o Server determines local clients on the channel and sends the
+  packet to the Client on the same server.  Server then sends
+  the packet to its router for further routing.
+
+o Router determines local clients on the channel, if found
+  sends packet to the local clients.  Router determines global
+  clients on the channel and sends the packet to its primary
+  router or fastest route.
+
+o (Other router(s) do the same thing and sends the packet to
+   the server(s))
+
+o Server determines local clients on the channel and sends the
+  packet to the client.
+
+o All clients receiving the packet decrypts the packet.
+
+
+.ti 0
+3.8.4 Server To Server
+
+Server to server packet delivery and encryption is described in above
+examples. Router to router packet delivery is analogous to server to
+server.  However, some packets, such as channel packets, are processed
+differently.  These cases are described later in this document and
+more in detail in [SILC2].
+
+
+.ti 0
+3.9 Key Exchange And Authentication
+
+Key exchange is done always when for example client connects to server
+but also when server and router, and router and router connects to each
+other.  The purpose of key exchange protocol is to provide secure key
+material to be used in the communication.  The key material is used to
+derive various security parameters used to secure SILC packets.  The
+SILC Key Exchange protocol is described in detail in [SILC3].
+
+Authentication is done after key exchange protocol has been successfully
+completed.  The purpose of authentication is to authenticate for example
+client connecting to the server.  However, usually clients are accepted
+to connect to server without explicit authentication.  Servers are
+required use authentication protocol when connecting.  The authentication
+may be based on passphrase (pre-shared-secret) or public key.  The
+connection authentication protocol is described in detail in [SILC3].
+
+
+.ti 0
+3.9.1 Authentication Payload
+
+Authentication payload is used separately from the SKE and the Connection
+Authentication protocol.  It is used during the session to authenticate
+with the remote.  For example, the client can authenticate itself to the
+server to become server operator.  In this case, Authentication Payload is
+used.
+
+
+
+
+
+
+
+
+
+
+
+The format of the Authentication Payload is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Public Data Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Authentication Data Length  |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                       Authentication Data                     ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 5:  Authentication Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire payload.
+
+o Authentication Method (2) - The method of the authentication.
+  The authentication methods are defined in [SILC2] in the
+  Connection Auth Request Payload.  The NONE authentication
+  method SHOULD NOT be used.
+
+o Public Data Length (2 bytes) - Indicates the length of
+  the Public Data field.
+
+o Public Data (variable length) - This is defined only if
+  the authentication method is public key.  If it is any other
+  this field does not exist and the Public Data Length field
+  is set to zero (0).
+
+  When the authentication method is public key this includes
+  128 to 4096 bytes of non-zero random data that is used in
+  the signature process, described subsequently.
+
+o Authentication Data Length (2 bytes) - Indicates the
+  length of the Authentication Data field.
+
+o Authentication Data (variable length) - Authentication 
+  method dependent authentication data.
+.in 3
+
+
+If the authentication method is password based, the Authentication
+Data field includes the plaintext password.  It is safe to send
+plaintext password since the entire payload is encrypted.  In this
+case the Public Data Length is set to zero (0).
+
+If the authentication method is public key based (or certificate)
+the Authentication Data is computed as follows:
+
+  HASH = hash(random bytes | ID | public key (or certificate));
+  Authentication Data = sign(HASH);
+
+The hash() and the sign() are the hash function and the public key
+cryptography function selected in the SKE protocol.  The public key
+is SILC style public key unless certificates are used.  The ID is the
+entity's ID (Client or Server ID) which is authenticating itself.  The
+ID is raw ID data.  The random bytes are non-zero random bytes of
+length between 128 and 4096 bytes, and will be included into the
+Public Data field as is.
+
+The receiver will compute the signature using the random data received
+in the payload, the ID associated to the connection and the public key
+(or certificate) received in the SKE protocol.  After computing the
+receiver MUST verify the signature.  In this case also, the entire
+payload is encrypted.
+
+
+.ti 0
+3.10 Algorithms
+
+This section defines all the allowed algorithms that can be used in
+the SILC protocol.  This includes mandatory cipher, mandatory public
+key algorithm and MAC algorithms.
+
+
+.ti 0
+3.10.1 Ciphers
+
+Cipher is the encryption algorithm that is used to protect the data
+in the SILC packets.  See [SILC2] of the actual encryption process and
+definition of how it must be done.  SILC has a mandatory algorithm that
+must be supported in order to be compliant with this protocol.
+
+The following ciphers are defined in SILC protocol:
+
+.in 6
+aes-256-cbc         AES in CBC mode, 256 bit key       (REQUIRED)
+aes-192-cbc         AES in CBC mode, 192 bit key       (OPTIONAL)
+aes-128-cbc         AES in CBC mode, 128 bit key       (OPTIONAL)
+twofish-256-cbc     Twofish in CBC mode, 256 bit key   (OPTIONAL)
+twofish-192-cbc     Twofish in CBC mode, 192 bit key   (OPTIONAL)
+twofish-128-cbc     Twofish in CBC mode, 128 bit key   (OPTIONAL)
+blowfish-128-cbc    Blowfish in CBC mode, 128 bit key  (OPTIONAL)
+cast-256-cbc        CAST-256 in CBC mode, 256 bit key  (OPTIONAL)
+cast-192-cbc        CAST-256 in CBC mode, 192 bit key  (OPTIONAL)
+cast-128-cbc        CAST-256 in CBC mode, 128 bit key  (OPTIONAL)
+rc6-256-cbc         RC6 in CBC mode, 256 bit key       (OPTIONAL)
+rc6-192-cbc         RC6 in CBC mode, 192 bit key       (OPTIONAL)
+rc6-128-cbc         RC6 in CBC mode, 128 bit key       (OPTIONAL)
+mars-256-cbc        Mars in CBC mode, 256 bit key      (OPTIONAL)
+mars-192-cbc        Mars in CBC mode, 192 bit key      (OPTIONAL)
+mars-128-cbc        Mars in CBC mode, 128 bit key      (OPTIONAL)
+none                No encryption                      (OPTIONAL)
+.in 3
+
+
+Algorithm none does not perform any encryption process at all and 
+thus is not recommended to be used.  It is recommended that no client
+or server implementation would accept none algorithms except in special
+debugging mode.
+
+Additional ciphers MAY be defined to be used in SILC by using the
+same name format as above.
+
+
+.ti 0
+3.10.2 Public Key Algorithms
+
+Public keys are used in SILC to authenticate entities in SILC network
+and to perform other tasks related to public key cryptography.  The 
+public keys are also used in the SILC Key Exchange protocol [SILC3].
+
+The following public key algorithms are defined in SILC protocol:
+
+.in 6
+rsa        RSA  (REQUIRED)
+dss        DSS  (OPTIONAL)
+.in 3
+
+DSS is described in [Menezes].  The RSA MUST be implemented according
+PKCS #1 [PKCS1].  The mandatory PKCS #1 implementation in SILC MUST be
+compliant to either PKCS #1 version 1.5 or newer with the following
+notes: The signature encoding is always in same format as the encryption
+encoding regardless of the PKCS #1 version.  The signature with appendix
+(with hash algorithm OID in the data) MUST NOT be used in the SILC.  The
+rationale for this is that there is no binding between the PKCS #1 OIDs
+and the hash algorithms used in the SILC protocol.  Hence, the encoding
+is always in PKCS #1 version 1.5 format.
+
+Additional public key algorithms MAY be defined to be used in SILC.
+
+
+
+
+.ti 0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section.  They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+The following Hash algorithm are defined in SILC protocol:
+
+.in 6
+sha1             SHA-1, length = 20      (REQUIRED)
+md5              MD5, length = 16        (OPTIONAL)
+.in 3
+
+
+.ti 0
+3.10.4 MAC Algorithms
+
+Data integrity is protected by computing a message authentication code
+(MAC) of the packet data.  See [SILC2] for details how to compute the
+MAC.
+
+The following MAC algorithms are defined in SILC protocol:
+
+.in 6
+hmac-sha1-96     HMAC-SHA1, length = 12  (REQUIRED)
+hmac-md5-96      HMAC-MD5, length = 12   (OPTIONAL)
+hmac-sha1        HMAC-SHA1, length = 20  (OPTIONAL)
+hmac-md5         HMAC-MD5, length = 16   (OPTIONAL)
+none             No MAC                  (OPTIONAL)
+.in 3
+
+The none MAC is not recommended to be used as the packet is not
+authenticated when MAC is not computed.  It is recommended that no
+client or server would accept none MAC except in special debugging
+mode.
+
+The HMAC algorithm is described in [HMAC] and hash algorithms that
+are used as part of the HMACs are described in [Scheneir] and in
+[Menezes]
+
+Additional MAC algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.10.5 Compression Algorithms
+
+SILC protocol supports compression that may be applied to unencrypted
+data.  It is recommended to use compression on slow links as it may
+significantly speed up the data transmission.  By default, SILC does not
+use compression which is the mode that must be supported by all SILC
+implementations.
+
+
+
+The following compression algorithms are defined:
+
+.in 6
+none        No compression               (REQUIRED)
+zlib        GNU ZLIB (LZ77) compression  (OPTIONAL)
+.in 3
+
+Additional compression algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.11 SILC Public Key
+
+This section defines the type and format of the SILC public key.  All
+implementations MUST support this public key type.  See [SILC3] for
+other optional public key and certificate types allowed in the SILC
+protocol.  Public keys in SILC may be used to authenticate entities
+and to perform other tasks related to public key cryptography.
+
+The format of the SILC Public Key is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Public Key Length                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Algorithm Name Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Algorithm Name                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Identifier Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Identifier                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  SILC Public Key
+
+
+.in 6
+o Public Key Length (4 bytes) - Indicates the full length
+  of the public key, not including this field.
+
+o Algorithm Name Length (2 bytes) - Indicates the length
+  of the Algorithm Length field, not including this field.
+
+o Algorithm name (variable length) - Indicates the name
+  of the public key algorithm that the key is.  See the
+  section 3.10.2 Public Key Algorithms for defined names.
+
+o Identifier Length (2 bytes) - Indicates the length of
+  the Identifier field, not including this field.
+
+o Identifier (variable length) - Indicates the identifier
+  of the public key.  This data can be used to identify
+  the owner of the key.  The identifier is of the following
+  format:
+
+     UN   User name
+     HN   Host name or IP address
+     RN   Real name
+     E    EMail address
+     O    Organization
+     C    Country
+
+
+  Examples of an identifier:
+
+    `UN=priikone, HN=poseidon.pspt.fi, E=priikone@poseidon.pspt.fi'
+
+    `UN=sam, HN=dummy.fi, RN=Sammy Sam, O=Company XYZ, C=Finland'
+
+  At least user name (UN) and host name (HN) MUST be provided as
+  identifier.  The fields are separated by commas (`,').  If
+  comma is in the identifier string it must be written as `\\,',
+  for example, `O=Company XYZ\\, Inc.'.
+
+o Public Data (variable length) - Includes the actual
+  public data of the public key.
+
+  The format of this field for RSA algorithm is
+  as follows:
+
+     4 bytes            Length of e
+     variable length    e
+     4 bytes            Length of n
+     variable length    n
+
+
+  The format of this field for DSS algorithm is
+  as follows:
+
+     4 bytes            Length of p
+     variable length    p
+     4 bytes            Length of q
+     variable length    q
+     4 bytes            Length of g
+     variable length    g
+     4 bytes            Length of y
+     variable length    y
+
+  The variable length fields are multiple precession
+  integers encoded as strings in both examples.
+
+  Other algorithms must define their own type of this
+  field if they are used.
+.in 3
+
+All fields in the public key are in MSB (most significant byte first)
+order.
+
+
+.ti 0
+3.12 SILC Version Detection
+
+The version detection of both client and server is performed at the
+connection phase while executing the SILC Key Exchange protocol.  The
+version identifier is exchanged between initiator and responder.  The
+version identifier is of the following format:
+
+.in 6
+SILC-<protocol version>-<software version>
+.in 3
+
+The version strings are of the following format:
+
+.in 6
+protocol version = <major>.<minor>
+software version = <major>[.<minor>[.<build>]]
+.in 3
+
+Protocol version MAY provide both major and minor version.  Currently
+implementations MUST set the protocol version and accept the protocol
+version as SILC-1.0-<software version>. 
+
+Software version MAY provide major, minor and build version.  The
+software version MAY be freely set and accepted.
+
+
+Thus, the version string could be, for example:
+
+.in 6
+SILC-1.0-1.2
+.in 3
+
+
+
+
+.ti 0
+4 SILC Procedures
+
+This section describes various SILC procedures such as how the 
+connections are created and registered, how channels are created and
+so on.  The section describes the procedures only generally as details
+are described in [SILC2] and [SILC3].
+
+
+.ti 0
+4.1 Creating Client Connection
+
+This section describes the procedure when client connects to SILC server.
+When client connects to server the server MUST perform IP address lookup
+and reverse IP address lookup to assure that the origin host really is
+who it claims to be.  Client, host, connecting to server SHOULD have 
+both valid IP address and fully qualified domain name (FQDN).
+
+After that the client and server performs SILC Key Exchange protocol
+which will provide the key material used later in the communication.
+The key exchange protocol MUST be completed successfully before the
+connection registration may continue.  The SILC Key Exchange protocol
+is described in [SILC3].
+
+Typical server implementation would keep a list of connections that it
+allows to connect to the server.  The implementation would check, for
+example, the connecting client's IP address from the connection list
+before the SILC Key Exchange protocol has been started.  Reason for
+this is that if the host is not allowed to connect to the server there
+is no reason to perform the key exchange protocol.
+
+After successful key exchange protocol the client and server performs
+connection authentication protocol.  The purpose of the protocol is to
+authenticate the client connecting to the server.  Flexible
+implementation could also accept the client to connect to the server
+without explicit authentication.  However, if authentication is
+desired for a specific client it may be based on passphrase or
+public key authentication.  If authentication fails the connection
+MUST be terminated.  The connection authentication protocol is described
+in [SILC3].
+
+After successful key exchange and authentication protocol the client
+registers itself by sending SILC_PACKET_NEW_CLIENT packet to the
+server.  This packet includes various information about the client
+that the server uses to create the client.  Server creates the client
+and sends SILC_PACKET_NEW_ID to the client which includes the created
+Client ID that the client MUST start using after that.  After that
+all SILC packets from the client MUST have the Client ID as the
+Source ID in the SILC Packet Header, described in [SILC2].
+
+Client MUST also get the server's Server ID that is to be used as
+Destination ID in the SILC Packet Header when communicating with
+the server (for example when sending commands to the server).  The
+ID may be resolved in two ways.  Client can take the ID from an
+previously received packet from server that MUST include the ID,
+or to send SILC_COMMAND_INFO command and receive the Server ID as
+command reply.
+
+Server MAY choose not to use the information received in the
+SILC_PACKET_NEW_CLIENT packet.  For example, if public key or 
+certificate were used in the authentication, server MAY use those
+informations rather than what it received from client.  This is suitable
+way to get the true information about client if it is available.
+
+The nickname of client is initially set to the username sent in the
+SILC_PACKET_NEW_CLIENT packet.  User should set the nickname to more
+suitable by sending SILC_COMMAND_NICK command.  However, this is not
+required as part of registration process.
+
+Server MUST also distribute the information about newly registered
+client to its router (or if the server is router, to all routers in
+the SILC network).  More information about this in [SILC2].
+
+
+.ti 0
+4.2 Creating Server Connection
+
+This section describes the procedure when server connects to its
+router (or when router connects to other router, the cases are
+equivalent).  The procedure is very much alike when client connects
+to the server thus it is not repeated here.
+
+One difference is that server MUST perform connection authentication
+protocol with proper authentication.  A proper authentication is based
+on passphrase or public key authentication.
+
+After server and router has successfully performed the key exchange
+and connection authentication protocol, the server register itself
+to the router by sending SILC_PACKET_NEW_SERVER packet.  This packet
+includes the server's Server ID that it has created by itself and
+other relevant information about the server.
+
+After router has received the SILC_PACKET_NEW_SERVER packet it
+distributes the information about newly registered server to all routers
+in the SILC network.  More information about this in [SILC2].
+
+As client needed to resolve the destination ID this MUST be done by the
+server that connected to the router, as well.  The way to resolve it is
+to get the ID from previously received packet.  The server MAY also 
+use SILC_COMMAND_INFO command to resolve the ID.  Server MUST also start
+using its own Server ID as Source ID in SILC Packet Header and the
+router's Server ID as Destination when communicating with the router.
+
+
+.ti 0
+4.2.1 Announcing Clients, Channels and Servers
+
+After server or router has connected to the remote router, and it already
+has connected clients and channels it MUST announce them to the router.
+If the server is router server, also all the local servers in the cell
+MUST be announced.
+
+All clients are announced by compiling a list of ID Payloads into the
+SILC_PACKET_NEW_ID packet.  All channels are announced by compiling a
+list of Channel Payloads into the SILC_PACKET_NEW_CHANNEL packet.  Also, 
+the channel users on the channels must be announced by compiling a
+list of Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into
+the SILC_PACKET_NOTIFY packet.
+
+The router MUST also announce the local servers by compiling list of
+ID Payloads into the SILC_PACKET_NEW_ID packet.
+
+The router which receives these lists MUST process them and broadcast
+the packets to its primary route.
+
+When processing the announced channels and channel users the router MUST
+check whether a channel exists already with the same name.  If channel
+exists with the same name it MUST check whether the Channel ID is
+different.  If the Channel ID is different the router MUST send the notify
+type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to the server to force the channel ID
+change to the ID the router has.  If the mode of the channel is different
+the router MUST send the notify type SILC_NOTIFY_TYPE_CMODE_CHANGE to the
+server to force the mode change to the mode that the router has.
+
+The router MUST also generate new channel key and distribute it to the
+channel.  The key MUST NOT be generated if the SILC_CMODE_PRIVKEY mode
+is set.
+
+If the channel has channel founder on the router the router MUST send
+the notify type SILC_NOTIFY_TYPE_CUMODE_CHANGE to the server to force
+the mode change for the channel founder on the server.  The channel 
+founder privileges MUST be removed.
+
+The router processing the channels MUST also compile a list of
+Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into the
+SILC_PACKET_NOTIFY and send the packet to the server.  This way the
+server (or router) will receive the clients on the channel that
+the router has.
+
+
+.ti 0
+4.3 Joining to a Channel
+
+This section describes the procedure when client joins to a channel.
+Client joins to channel by sending command SILC_COMMAND_JOIN to the
+server.  If the receiver receiving join command is normal server the
+server MUST check its local list whether this channel already exists
+locally.  This would indicate that some client connected to the server
+has already joined to the channel.  If this is case the client is
+joined to the channel, new channel key is created and information about
+newly joined channel is sent to the router.  The router is informed
+by sending SILC_NOTIFY_TYPE_JOIN notify type.  The notify type MUST
+also be sent to the local clients on the channel.  The new channel key
+is also sent to the router and to local clients on the channel.
+
+If the channel does not exist in the local list the client's command
+MUST be sent to the router which will then perform the actual joining
+procedure.  When server receives the reply to the command from the
+router it MUST be sent to the client which sent the command originally.
+Server will also receive the channel key from the server that it MUST
+send to the client which originally requested the join command.  The
+server MUST also save the channel key.
+
+If the receiver of the join command is router it MUST first check its
+local list whether anyone in the cell has already joined to the channel.
+If this is the case the client is joined to the channel and reply is
+sent to the client.  If the command was sent by server the command reply
+is sent to the server which sent it.  Then the router MUST also create
+new channel key and distribute it to all clients on the channel and
+all servers that has clients on the channel.  Router MUST also send
+the SILC_NOTIFY_TYPE_JOIN notify type to local clients on the channel
+and to local servers that has clients on the channel.
+
+If the channel does not exist on the router's local list it MUST
+check the global list whether the channel exists at all.  If it does
+the client is joined to the channel as described previously.  If
+the channel does not exist the channel is created and the client
+is joined to the channel.  The channel key is also created and
+distributed as previously described.  The client joining to the created
+channel is made automatically channel founder and both channel founder
+and channel operator privileges is set for the client.
+
+If the router created the channel in the process, information about the
+new channel MUST be broadcasted to all routers.  This is done by 
+broadcasting SILC_PACKET_NEW_CHANNEL packet to the router's primary
+route.  When the router joins the client to the channel it MUST also
+send information about newly joined client to all routers in the SILC
+network.  This is done by broadcasting the SILC_NOTIFY_TYPE_JOIN notify
+type to the router's primary route. 
+
+It is important to note that new channel key is created always when
+new client joins to channel, whether the channel has existed previously
+or not.  This way the new client on the channel is not able to decrypt
+any of the old traffic on the channel.  Client which receives the reply to
+the join command MUST start using the received Channel ID in the channel
+message communication thereafter.  Client also receives the key for the
+channel in the command reply.  Note that the channel key is never
+generated if the SILC_CMODE_PRIVKEY mode is set.
+
+
+.ti 0
+4.4 Channel Key Generation
+
+Channel keys are created by router which creates the channel by taking
+enough randomness from cryptographically strong random number generator.
+The key is generated always when channel is created, when new client
+joins a channel and after the key has expired.  Key could expire for
+example in an hour.
+
+The key MUST also be re-generated whenever some client leaves a channel.
+In this case the key is created from scratch by taking enough randomness
+from the random number generator.  After that the key is distributed to
+all clients on the channel.  However, channel keys are cell specific thus
+the key is created only on the cell where the client, which left the
+channel, exists.  While the server or router is creating the new channel
+key, no other client may join to the channel.  Messages that are sent
+while creating the new key are still processed with the old key.  After
+server has sent the SILC_PACKET_CHANNEL_KEY packet MUST client start
+using the new key.  If server creates the new key the server MUST also
+send the new key to its router.  See [SILC2] on more information about
+how channel messages must be encrypted and decrypted when router is
+processing them.
+
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it MUST process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages.  The processing is as follows:
+
+  channel_key  = raw key data
+  HMAC key     = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+Note that the server MUST also save the channel key.
+
+
+.ti 0
+4.5 Private Message Sending and Reception
+
+Private messages are sent point to point.  Client explicitly destines
+a private message to specific client that is delivered to only to that
+client.  No other client may receive the private message.  The receiver
+of the private message is destined in the SILC Packet Header as any
+other packet as well.
+
+If the sender of a private message does not know the receiver's Client
+ID, it MUST resolve it from server.  There are two ways to resolve the
+client ID from server; it is RECOMMENDED that client implementations
+send SILC_COMMAND_IDENTIFY command to receive the Client ID.  Client
+MAY also send SILC_COMMAND_WHOIS command to receive the Client ID.
+If the sender has received earlier a private message from the receiver
+it should have cached the Client ID from the SILC Packet Header.
+
+See [SILC2] for description of private message encryption and decryption
+process.
+
+
+.ti 0
+4.6 Private Message Key Generation
+
+Private message MAY be protected by the key generated by the client.
+The key may be generated and sent to the other client by sending packet
+SILC_PACKET_PRIVATE_MESSAGE_KEY which travels through the network
+and is secured by session keys.  After that the private message key
+is used in the private message communication between those clients.
+
+Other choice is to entirely use keys that are not sent through
+the SILC network at all.  This significantly adds security.  This key
+would be pre-shared-key that is known by both of the clients.  Both
+agree about using the key and starts sending packets that indicate
+that the private message is secured using private message key.
+
+The key material used as private message key is implementation issue.
+However, SILC_PACKET_KEY_AGREEMENT packet MAY be used to negotiate
+the key material.  If the key is normal pre-shared-key or randomly
+generated key, and the SILC_PACKET_KEY_AGREEMENT was not used, then
+the key material SHOULD be processed as defined in the [SILC3].  In
+the processing, however, the HASH, as defined in [SILC3] MUST be 
+ignored.  After processing the key material it is employed as defined
+in [SILC3], however, the HMAC key material MUST be discarded.
+
+If the key is pre-shared-key or randomly generated the implementations
+should use the SILC protocol's mandatory cipher as the cipher.  If the
+SKE was used to negotiate key material the cipher was negotiated as well.
+
+.ti 0
+4.7 Channel Message Sending and Reception
+
+Channel messages are delivered to group of users.  The group forms a
+channel and all clients on the channel receives messages sent to the
+channel.
+
+Channel messages are destined to channel by specifying the Channel ID
+as Destination ID in the SILC Packet Header.  The server MUST then
+distribute the message to all clients on the channel by sending the
+channel message destined explicitly to a client on the channel.
+
+See [SILC2] for description of channel message encryption and decryption
+process.
+
+
+.ti 0
+4.8 Session Key Regeneration
+
+Session keys MUST be regenerated periodically, say, once in an hour.
+The re-key process is started by sending SILC_PACKET_REKEY packet to
+other end, to indicate that re-key must be performed.  The initiator
+of the connection SHOULD initiate the re-key.
+
+If perfect forward secrecy (PFS) flag was selected in the SILC Key
+Exchange protocol [SILC3] the re-key MUST cause new key exchange with
+SKE protocol.  In this case the protocol is secured with the old key
+and the protocol results to new key material.  See [SILC3] for more
+information.  After the SILC_PACKET_REKEY packet is sent the sender
+will perform the SKE protocol.
+
+If PFS flag was set the resulted key material is processed as described
+in the section Processing the Key Material in [SILC3].  The difference
+with re-key in the processing is that the initial data for the hash 
+function is just the resulted key material and not the HASH as it
+is not computed at all with re-key.  Other than that, the key processing
+it equivalent to normal SKE negotiation.
+
+If PFS flag was not set, which is the default case, then re-key is done
+without executing SKE protocol.  In this case, the new key is created by
+providing the current sending encryption key to the SKE protocol's key
+processing function.  The process is described in the section Processing
+the Key Material in [SILC3].  The difference in the processing is that
+the initial data for the hash function is the current sending encryption
+key and not the SKE's KEY and HASH values.  Other than that, the key
+processing is equivalent to normal SKE negotiation.
+
+After both parties has regenerated the session key, both MUST send
+SILC_PACKET_REKEY_DONE packet to each other.  These packets are still
+secured with the old key.  After these packets, the subsequent packets
+MUST be protected with the new key.
+
+
+
+
+.ti 0
+4.9 Command Sending and Reception
+
+Client usually sends the commands in the SILC network.  In this case
+the client simply sends the command packet to server and the server
+processes it and replies with command reply packet.
+
+However, if the server is not able to process the command, it is sent 
+to the server's router.  This is case for example with commands such
+as, SILC_COMMAND_JOIN and SILC_COMMAND_WHOIS commands.  However, there
+are other commands as well.  For example, if client sends the WHOIS
+command requesting specific information about some client the server must
+send the WHOIS command to router so that all clients in SILC network
+are searched.  The router, on the other hand, sends the WHOIS command
+further to receive the exact information about the requested client.
+The WHOIS command travels all the way to the server which owns the client
+and it replies with command reply packet.  Finally, the server which
+sent the command receives the command reply and it must be able to
+determine which client sent the original command.  The server then
+sends command reply to the client.  Implementations should have some
+kind of cache to handle, for example, WHOIS information.  Servers
+and routers along the route could all cache the information for faster
+referencing in the future.
+
+The commands sent by server may be sent hop by hop until someone is able
+to process the command.  However, it is preferred to destine the command
+as precisely as it is possible.  In this case, other routers en route
+MUST route the command packet by checking the true sender and true
+destination of the packet.  However, servers and routers MUST NOT route
+command reply packets to clients coming from other server.  Client
+MUST NOT accept command reply packet originated from anyone else but
+from its own server.
+
+
+.ti 0
+4.10 Closing Connection
+
+When remote client connection is closed the server MUST send the notify
+type SILC_NOTIFY_TYPE_SIGNOFF to its primary router and to all channels
+the client was joined.  The server MUST also save the client's information
+for a period of time for history purposes.
+
+When remote server or router connection is closed the server or router
+MUST also remove all the clients that was behind the server or router
+from the SILC Network.  The server or router MUST also send the notify
+type SILC_NOTIFY_TYPE_SERVER_SIGNOFF to its primary router and to all
+local clients that are joined on the same channels with the remote 
+server's or router's clients.
+
+
+.ti 0
+5 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+Special attention must also be paid on the servers and routers that are
+running the SILC service.  The SILC protocol's security depends greatly
+on the security and the integrity of the servers and administrators that
+are running the service.  It is recommended that some form of registration
+is required by the server and router administrator prior acceptance to
+the SILC Network.  The clients must be able to trust the servers they
+are using.
+
+It is also recommended that router operators in the SILC Network would
+form a joint forum to discuss the router and SILC Network management
+issues.  Also, router operators along with the cell's server operators
+should have a forum to discuss the cell management issues.
+
+
+.ti 0
+6 References
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+7 Author's Address
+
+.nf
+Pekka Riikonen
+Kasarmikatu 11 A4
+70110 Kuopio
+Finland
+
+EMail: priikone@poseidon.pspt.fi
+
+This Internet-Draft expires 26 October 2001 
diff --git a/doc/draft-riikonen-silc-spec-03.nroff b/doc/draft-riikonen-silc-spec-03.nroff
new file mode 100644 (file)
index 0000000..ecb58e4
--- /dev/null
@@ -0,0 +1,2046 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 21 August 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                      P. Riikonen
+Internet-Draft
+draft-riikonen-silc-spec-03.txt                         21 August 2001
+Expires: 21 February 2002
+
+.in 3
+
+.ce 3
+Secure Internet Live Conferencing (SILC),
+Protocol Specification
+<draft-riikonen-silc-spec-03.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside the SILC network.
+Three other Internet Drafts relates very closely to this memo;
+SILC Packet Protocol [SILC2], SILC Key Exchange and Authentication
+Protocols [SILC3] and SILC Commands [SILC4].
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Concepts .................................................  4
+  2.1 SILC Network Topology .....................................  4
+  2.2 Communication Inside a Cell ...............................  5
+  2.3 Communication in the Network ..............................  6
+  2.4 Channel Communication .....................................  7
+  2.5 Router Connections ........................................  7
+  2.6 Backup Routers ............................................  8
+3 SILC Specification ............................................ 10
+  3.1 Client .................................................... 10
+      3.1.1 Client ID ........................................... 10
+  3.2 Server .................................................... 11
+      3.2.1 Server's Local ID List .............................. 12
+      3.2.2 Server ID ........................................... 13
+      3.2.3 SILC Server Ports ................................... 14
+  3.3 Router .................................................... 14
+      3.3.1 Router's Local ID List .............................. 14
+      3.3.2 Router's Global ID List ............................. 15
+      3.3.3 Router's Server ID .................................. 15
+  3.4 Channels .................................................. 16
+      3.4.1 Channel ID .......................................... 17
+  3.5 Operators ................................................. 17
+  3.6 SILC Commands ............................................. 18
+  3.7 SILC Packets .............................................. 18
+  3.8 Packet Encryption ......................................... 19
+      3.8.1 Determination of the Source and the Destination ..... 19
+      3.8.2 Client To Client .................................... 20
+      3.8.3 Client To Channel ................................... 21
+      3.8.4 Server To Server .................................... 22
+  3.9 Key Exchange And Authentication ........................... 22
+      3.9.1 Authentication Payload .............................. 22
+  3.10 Algorithms ............................................... 24
+      3.10.1 Ciphers ............................................ 24
+      3.10.2 Public Key Algorithms .............................. 25
+      3.10.3 Hash Functions ..................................... 26
+      3.10.4 MAC Algorithms ..................................... 26
+      3.10.5 Compression Algorithms ............................. 26
+  3.11 SILC Public Key .......................................... 27
+  3.12 SILC Version Detection ................................... 29
+4 SILC Procedures ............................................... 30
+  4.1 Creating Client Connection ................................ 30
+  4.2 Creating Server Connection ................................ 31
+      4.2.1 Announcing Clients, Channels and Servers ............ 32
+  4.3 Joining to a Channel ...................................... 33
+  4.4 Channel Key Generation .................................... 34
+  4.5 Private Message Sending and Reception ..................... 34
+  4.6 Private Message Key Generation ............................ 35
+  4.7 Channel Message Sending and Reception ..................... 35
+  4.8 Session Key Regeneration .................................. 36
+  4.9 Command Sending and Reception ............................. 37
+  4.10 Closing Connection ....................................... 37
+5 Security Considerations ....................................... 38
+6 References .................................................... 38
+7 Author's Address .............................................. 40
+
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  SILC Network Topology
+Figure 2:  Communication Inside cell
+Figure 3:  Communication Between Cells
+Figure 4:  Router Connections
+Figure 5:  SILC Public Key
+
+
+.ti 0
+1. Introduction
+
+This document describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.
+
+Strong cryptographic methods are used to protect SILC packets inside
+the SILC network.  Three other Internet Drafts relates very closely
+to this memo; SILC Packet Protocol [SILC2], SILC Key Exchange and
+Authentication Protocols [SILC3] and SILC Commands [SILC4].
+
+The protocol uses extensively packets as conferencing protocol 
+requires message and command sending.  The SILC Packet Protocol is
+described in [SILC2] and should be read to fully comprehend this
+document and protocol.  [SILC2] also describes the packet encryption
+and decryption in detail.
+
+The security of SILC protocol, and for any security protocol for that
+matter, is based on strong and secure key exchange protocol.  The SILC
+Key Exchange protocol is described in [SILC3] along with connection
+authentication protocol and should be read to fully comprehend this
+document and protocol.
+
+The SILC protocol has been developed to work on TCP/IP network
+protocol, although it could be made to work on other network protocols
+with only minor changes.  However, it is recommended that TCP/IP
+protocol is used under SILC protocol.  Typical implementation would
+be made in client-server model.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2. SILC Concepts
+
+This section describes various SILC protocol concepts that forms the 
+actual protocol, and in the end, the actual SILC network.  The mission
+of the protocol is to deliver messages from clients to other clients 
+through routers and servers in secure manner.  The messages may also 
+be delivered from one client to many clients forming a group, also 
+known as a channel.
+
+This section does not focus to security issues.  Instead, basic network 
+concepts are introduced to make the topology of the SILC network 
+clear.
+
+
+.ti 0
+2.1 SILC Network Topology
+
+SILC network is a cellular network as opposed to tree style network 
+topology.  The rationale for this is to have servers that can perform 
+specific kind of tasks what other servers cannot perform.  This leads 
+to two kinds of servers; normal SILC servers and SILC routers.
+
+A difference between normal server and router server is that routers 
+knows everything about everything in the network.  They also do the 
+actual routing of the messages to the correct receiver.  Normal servers 
+knows only about local information and nothing about global information.
+This makes the network faster as there are less servers that needs to 
+keep global information up to date at all time.
+
+This, on the other hand, leads to cellular like network, where routers 
+are in the center of the cell and servers are connected to the router.
+
+
+
+
+
+
+
+The following diagram represents SILC network topology.
+
+.in 8
+.nf
+  ---- ---- ----         ---- ---- ----
+ | S8 | S5 | S4 |       | S7 | S5 | S6 |
+ ----- ---- -----       ----- ---- -----
+| S7 | S/R1 | S2 | --- | S8 | S/R2 | S4 |
+ ---- ------ ----       ---- ------ ----
+ | S6 | S3 | S1 |       | S1 | S3 | S2 |         ---- ----
+  ---- ---- ----         ---- ---- ----         | S3 | S1 |
+     Cell 1.   \\             Cell 2.  | \\____  ----- -----
+                |                     |        | S4 | S/R4 |
+    ---- ---- ----         ---- ---- ----       ---- ------
+   | S7 | S4 | S2 |       | S1 | S3 | S2 |      | S2 | S5 |
+   ----- ---- -----       ----- ---- -----       ---- ----
+  | S6 | S/R3 | S1 | --- | S4 | S/R5 | S5 | ____/ Cell 4.
+   ---- ------ ----       ---- ------ ----
+   | S8 | S5 | S3 |       | S6 | S7 | S8 |     ... etc ...
+    ---- ---- ----         ---- ---- ----
+       Cell 3.                Cell 5.
+.in 3
+
+.ce
+Figure 1:  SILC Network Topology
+
+
+A cell is formed when a server or servers connect to one router.  In
+SILC network normal server cannot directly connect to other normal
+server.  Normal server may only connect to SILC router which then
+routes the messages to the other servers in the cell.  Router servers
+on the other hand may connect to other routers to form the actual SILC 
+network, as seen in above figure.  However, router is also normal SILC 
+server; clients may connect to it the same way as to normal SILC 
+server.  Normal server also cannot have active connections to more 
+than one router.  Normal server cannot be connected to two different 
+cells.  Router servers, on the other hand, may have as many router to 
+router connections as needed.
+
+There are many issues in this network topology that needs to be careful
+about.  Issues like the size of the cells, the number of the routers in 
+the SILC network and the capacity requirements of the routers.  These
+issues should be discussed in the Internet Community and additional
+documents on the issue may be written.
+
+
+.ti 0
+2.2 Communication Inside a Cell
+
+It is always guaranteed that inside a cell message is delivered to the 
+recipient with at most two server hops.  A client which is connected to
+server in the cell and is talking on channel to other client connected 
+to other server in the same cell, will have its messages delivered from 
+its local server first to the router of the cell, and from the router 
+to the other server in the cell.
+
+The following diagram represents this scenario:
+
+
+.in 25
+.nf
+1 --- S1     S4 --- 5
+         S/R
+ 2 -- S2     S3
+     /        |
+    4         3
+.in 3
+
+
+.ce
+Figure 2:  Communication Inside cell
+
+
+Example:  Client 1. connected to Server 1. send message to
+          Client 4. connected to Server 2. travels from Server 1.
+          first to Router which routes the message to Server 2.
+          which then sends it to the Client 4.  All the other
+          servers in the cell will not see the routed message.
+
+
+If the client is connected directly to the router, as router is also normal
+SILC server, the messages inside the cell are always delivered only with 
+one server hop.  If clients communicating with each other are connected 
+to the same server, no router interaction is needed.  This is the optimal
+situation of message delivery in the SILC network.
+
+
+.ti 0
+2.3 Communication in the Network
+
+If the message is destined to server that does not belong to local cell 
+the message is routed to the router server to which the destination 
+server belongs, if the local router is connected to destination router.
+If there is no direct connection to the destination router, the local
+router routes the message to its primary route.  The following diagram
+represents message sending between cells.
+
+
+.in 16
+.nf
+1 --- S1     S4 --- 5            S2 --- 1
+         S/R - - - - - - - - S/R
+ 2 -- S2     S3           S1
+     /        |             \\
+    4         3              2
+
+   Cell 1.               Cell 2.
+.in 3
+
+
+.ce
+Figure 3:  Communication Between Cells
+
+
+Example:  Client 5. connected to Server 4. in Cell 1. sends message
+          to Client 2. connected to Server 1. in Cell 2. travels
+          from Server 4. to Router which routes the message to
+          Router in Cell 2, which then routes the message to 
+          Server 1.  All the other servers and routers in the
+          network will not see the routed message.
+
+
+The optimal case of message delivery from the client point of view is
+when clients are connected directly to the routers and the messages
+are delivered from one router to the other.
+
+
+.ti 0 
+2.4 Channel Communication
+
+Messages may be sent to group of clients as well.  Sending messages to
+many clients works the same way as sending messages point to point, from
+message delivery point of view.  Security issues are another matter
+which are not discussed in this section.
+
+Router server handles the message routing to multiple recipients.  If 
+any recipient is not in the same cell as the sender the messages are 
+routed further.
+
+Server distributes the channel message to its local clients which are 
+joined to the channel.  Router also distributes the message to its 
+local clients on the channel.
+
+
+.ti 0
+2.5 Router Connections
+
+Router connections play very important role in making the SILC like
+network topology to work.  For example, sending broadcast packets in
+SILC network require special connections between routers; routers must
+be connected in a specific way.
+
+Every router has their primary route which is a connection to another
+router in the network.  Unless there is only two routers in the network
+must not routers use each other as their primary routes.  The router
+connections in the network must form a circular.
+
+
+
+
+
+
+
+Example with three routers in the network:
+
+
+.in 16
+.nf
+    S/R1 - > - > - > - > - > - > - S/R2
+     \\                               /
+      ^                             v
+       \\ - < -  < - S/R3 - < - < - /
+.in 3
+
+
+.ce
+Figure 4:  Router Connections
+
+
+Example:  Network with three routers.  Router 1. uses Router 2. as its
+          primary router.  Router 2. uses Router 3. as its primary router,
+          and Router 3. uses Router 1. as its primary router.  There may
+          be other direct connections between the routers but they must
+          not be used as primary routes.
+
+The above example is applicable to any amount of routers in the network
+except for two routers.  If there are only two routers in the network both
+routers must be able to handle situation where they use each other as their
+primary routes.
+
+The issue of router connections are very important especially with SILC
+broadcast packets.  Usually all router wide information in the network is
+distributed by SILC broadcast packets.
+
+
+.ti 0
+2.6 Backup Routers
+
+Backup routers may exist in the cell in addition of the primary router.
+However, they must not be active routers and act as routers in the cell.
+Only one router may be acting as primary router in the cell.  In the case
+of failure of the primary router may one of the backup routers become
+active.  The purpose of backup routers are in case of failure of the
+primary router to maintain working connections inside the cell and outside
+the cell and to avoid netsplits.
+
+Backup routers are normal servers in the cell that are prepared to take
+over the tasks of the primary router if needed.  They need to have at
+least one direct and active connection to the primary router of the cell.
+This communication channel is used to send the router information to
+the backup router.
+
+Backup router must know everything that the primary router knows to be
+able to take over the tasks of the primary router.  It is the primary
+router's responsibility to feed the data to the backup router.  If the
+backup router does not know all the data in the case of failure some
+connections may be lost.  The primary router of the cell must consider
+the backup router being normal router server and feed the data
+accordingly.
+
+In addition of having direct connection to the primary router of the
+cell the backup router must also have connection to the same router
+the primary router of the cell is connected.  However, it must not be
+active router connection meaning that the backup router must not use
+that channel as its primary route and it must not notify the router
+about having connected servers, channels and clients behind it.  It
+merely connects to the router.  This sort of connection is later
+referred as being passive connection.  Some keepalive actions may be
+needed by the router to keep the connection alive.
+
+The primary router notifies its primary router about having backup
+routers in the cell by sending SILC_PACKET_CELL_ROUTERS packet.  If
+and when the primary router of the cell becomes unresponsive, its
+primary router knows that there exists backup routers in the cell.  
+After that it will start using the first backup router sent in the
+packet as router of that cell.
+
+In this case the backup router must notify its new primary router about
+the servers, channels and clients it has connected to it.  The primary
+router knows that this server has become a router of the cell because
+of failure of the primary router in the cell.  It must also cope with
+the fact that the servers, channels and clients that the new backup
+router announces are not really new, since they used to exist in the
+primary router of the cell.
+
+It is required that other normal servers has passive connections to
+the backup router(s) in the cell.  Some keepalive actions may be needed
+by the server to keep the connection alive.  After they notice the
+failure of the primary router they must start using the connection to
+the first backup router as their primary route.
+
+It is RECOMMENDED that there would be at least one backup router in
+the cell.  It is NOT RECOMMENDED to have all servers in the cell acting
+as backup routers as it requires establishing several connections to
+several servers in the cell.  Large cells can easily have several
+backup routers in the cell.
+
+The order of the backup routers are decided at the primary router of the
+cell and servers and backup routers in the cell must be configured
+accordingly.  It is not required that the backup server is actually
+active server in the cell.  Backup router may be a spare server in the
+cell that does not accept normal client connections at all.  It may be
+reserved purely for the backup purposes.  These, however, are cell
+management issues.
+
+If also the first backup router is down as well and there is another
+backup router in the cell then it will start acting as the primary
+router as described above.
+
+
+.ti 0
+3. SILC Specification
+
+This section describes the SILC protocol.  However, [SILC2] and
+[SILC3] describes other important protocols that are part of this SILC
+specification and must be read.
+
+
+.ti 0
+3.1 Client
+
+A client is a piece of software connecting to SILC server.  SILC client 
+cannot be SILC server.  Purpose of clients is to provide the user 
+interface of the SILC services for end user.  Clients are distinguished
+from other clients by unique Client ID.  Client ID is a 128 bit ID that
+is used in the communication in the SILC network.  The client ID is 
+based on the nickname selected by the user.  User uses logical nicknames
+in communication which are then mapped to the corresponding Client ID.
+Client ID's are low level identifications and must not be seen by the
+end user.
+
+Clients provide other information about the end user as well. Information
+such as the nickname of the user, username and the host name of the end 
+user and user's real name.  See section 3.2 Server for information of 
+the requirements of keeping this information.
+
+The nickname selected by the user is not unique in the SILC network.
+There can be 2^8 same nicknames for one IP address.  As for comparison
+to IRC [IRC] where nicknames are unique this is a fundamental difference
+between SILC and IRC.  This causes the server names or client's host names
+to be used along with the nicknames to identify specific users when sending
+messages.  This feature of SILC makes IRC style nickname-wars obsolete as
+no one owns their nickname; there can always be someone else with the same
+nickname.  The maximum length of nickname is 128 characters.
+
+
+.ti 0
+3.1.1 Client ID
+
+Client ID is used to identify users in the SILC network.  The Client ID
+is unique to the extent that there can be 2^128 different Client ID's,
+and ID's based on IPv6 addresses extends this to 2^224 different Client
+ID's.  Collisions are not expected to happen.  The Client ID is defined
+as follows.
+
+
+
+.in 6
+128 bit Client ID based on IPv4 addresses:
+
+32 bit  Server ID IP address (bits 1-32)
+ 8 bit  Random number or counter
+88 bit  Truncated MD5 hash value of the nickname
+
+224 bit Client ID based on IPv6 addresses:
+
+128 bit  Server ID IP address (bits 1-128)
+  8 bit  Random number or counter
+ 88 bit  Truncated MD5 hash value of the nickname
+
+o Server ID IP address - Indicates the server where this
+  client is coming from.  The IP address hence equals the
+  server IP address where to the client has connected.
+
+o Random number or counter - Random number to further 
+  randomize the Client ID.  Another choice is to use
+  a counter starting from the zero (0).  This makes it
+  possible to have 2^8 same nicknames from the same
+  server IP address.
+
+o MD5 hash - MD5 hash value of the nickname is truncated
+  taking 88 bits from the start of the hash value.  This
+  hash value is used to search the user's Client ID from
+  the ID lists.
+
+.in 3
+Collisions could occur when more than 2^8 clients using same nickname
+from the same server IP address is connected to the SILC network.  
+Server MUST be able to handle this situation by refusing to accept 
+anymore of that nickname.
+
+Another possible collision may happen with the truncated hash value of
+the nickname.  It could be possible to have same truncated hash value for
+two different nicknames.  However, this is not expected to happen nor
+cause any problems if it would occur.  Nicknames are usually logical and
+it is unlikely to have two distinct logical nicknames produce same
+truncated hash value.
+
+
+.ti 0
+3.2 Server
+
+Servers are the most important parts of the SILC network.  They form the
+basis of the SILC, providing a point to which clients may connect to.
+There are two kinds of servers in SILC; normal servers and router servers.
+This section focus on the normal server and router server is described
+in the section 3.3 Router.
+
+Normal servers MUST NOT directly connect to other normal server.  Normal
+servers may only directly connect to router server.  If the message sent
+by the client is destined outside the local server it is always sent to
+the router server for further routing.  Server may only have one active
+connection to router on same port.  Normal server MUST NOT connect to other
+cell's router except in situations where its cell's router is unavailable.
+
+Servers and routers in the SILC network are considered to be trusted.
+With out a doubt, servers that are set to work on ports above 1023 are
+not considered to be trusted.  Also, the service provider acts important
+role in the server's trustworthy.
+
+
+.ti 0
+3.2.1 Server's Local ID List
+
+Normal server keeps various information about the clients and their end
+users connected to it.  Every normal server MUST keep list of all locally
+connected clients, Client ID's, nicknames, usernames and host names and
+user's real name.  Normal servers only keeps local information and it
+does not keep any global information.  Hence, normal servers knows only
+about their locally connected clients.  This makes servers efficient as
+they don't have to worry about global clients.  Server is also responsible
+of creating the Client ID's for their clients.
+
+Normal server also keeps information about locally created channels and
+their Channel ID's.
+
+
+Hence, local list for normal server includes:
+
+.in 6
+server list        - Router connection
+   o Server name
+   o Server IP address
+   o Server ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+client list        - All clients in server
+   o Nickname
+   o Username@host
+   o Real name
+   o Client ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+
+channel list       - All channels in server
+   o Channel name
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+.ti 0
+3.2.2 Server ID
+
+Servers are distinguished from other servers by unique 64 bit Server ID 
+(for IPv4) or 160 bit Server ID (for IPv6).  The Server ID is used in
+the SILC to route messages to correct servers.  Server ID's also provide
+information for Client ID's, see section 3.1.1 Client ID.  Server ID is
+defined as follows.
+
+.in 6
+64 bit Server ID based on IPv4 addresses:
+
+32 bit  IP address of the server
+16 bit  Port
+16 bit  Random number
+
+160 bit Server ID based on IPv6 addresses:
+
+128 bit  IP address of the server
+ 16 bit  Port
+ 16 bit  Random number
+
+o IP address of the server - This is the real IP address of
+  the server.
+
+o Port - This is the port the server is bound to.
+
+o Random number - This is used to further randomize the Server ID.
+
+.in 3
+Collisions are not expected to happen in any conditions.  The Server ID
+is always created by the server itself and server is responsible of
+distributing it to the router.
+
+
+.ti 0
+3.2.3 SILC Server Ports
+
+The following ports has been assigned by IANA for the SILC protocol:
+
+.in 10
+silc            706/tcp    SILC
+silc            706/udp    SILC
+.in 3
+
+
+If there are needs to create new SILC networks in the future the port
+numbers must be officially assigned by the IANA.
+
+Server on network above privileged ports (>1023) SHOULD NOT be trusted
+as they could have been set up by untrusted party.
+
+
+.ti 0
+3.3 Router
+
+Router server in SILC network is responsible for keeping the cell together
+and routing messages to other servers and to other routers.  Router server
+is also a normal server thus clients may connect to it as it would be
+just normal SILC server.
+
+However, router servers has a lot of important tasks that normal servers
+do not have.  Router server knows everything about everything in the SILC.
+They know all clients currently on SILC, all servers and routers and all
+channels in SILC.  Routers are the only servers in SILC that care about
+global information and keeping them up to date at all time.  And, this
+is what they must do.
+
+
+.ti 0
+3.3.1 Router's Local ID List
+
+Router server as well MUST keep local list of connected clients and
+locally created channels.  However, this list is extended to include all
+the informations of the entire cell, not just the server itself as for
+normal servers.
+
+However, on router this list is a lot smaller since routers do not need
+to keep information about user's nickname, username and host name and real
+name since these are not needed by the router.  The router keeps only
+information that it needs.
+
+
+Hence, local list for router includes:
+
+.in 6
+server list        - All servers in the cell
+   o Server name
+   o Server ID
+   o Router's Server ID
+   o Sending key
+   o Receiving key
+
+client list        - All clients in the cell
+   o Client ID
+
+
+channel list       - All channels in the cell
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+Note that locally connected clients and other information include all the
+same information as defined in section section 3.2.1 Server's Local ID
+List.
+
+
+.ti 0
+3.3.2 Router's Global ID List
+
+Router server MUST also keep global list.  Normal servers do not have
+global list as they know only about local information.  Global list
+includes all the clients on SILC, their Client ID's, all created channels
+and their Channel ID's and all servers and routers on SILC and their
+Server ID's.  That is said, global list is for global information and the
+list must not include the local information already on the router's local
+list.
+
+Note that the global list does not include information like nicknames,
+usernames and host names or user's real names.  Router does not need to
+keep these informations as they are not needed by the router.  This 
+information is available from the client's server which maybe queried
+when needed.
+
+Hence, global list includes:
+
+.in 6
+server list        - All servers in SILC
+   o Server name
+   o Server ID
+   o Router's Server ID
+
+client list        - All clients in SILC
+   o Client ID
+
+channel list       - All channels in SILC
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+.in 3
+
+
+
+
+
+
+
+
+.ti 0
+3.3.3 Router's Server ID
+
+Router's Server ID's are equivalent to normal Server ID's.  As routers
+are normal servers as well same types of ID's applies for routers as well.
+Thus, see section 3.2.2 Server ID.
+
+
+.ti 0
+3.4 Channels
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client requests JOIN command to the channel, and the channel ceases to
+exist when the last client has left it.  When channel exists, any client
+can reference it using the name of the channel.
+
+Channel names are unique although the real uniqueness comes from 64 bit
+Channel ID.  However, channel names are still unique and no two global
+channels with same name may exist.  The Channel name is a string of
+maximum length of 256 characters.  Channel names MUST NOT contain any
+spaces (`  '), any non-printable ASCII characters, commas (`,') and
+wildcard characters.
+
+Channels can have operators that can administrate the channel and
+operate all of its modes.  The following operators on channel exist on
+the SILC network.
+
+.in 6
+o Channel founder - When channel is created the joining client becomes
+  channel founder.  Channel founder is channel operator with some more
+  privileges.  Basically, channel founder can fully operate the channel
+  and all of its modes.  The privileges are limited only to the
+  particular channel.  There can be only one channel founder per
+  channel. Channel founder supersedes channel operator's privileges.
+
+  Channel founder privileges cannot be removed by any other operator on
+  channel.  When channel founder leaves the channel there is no channel
+  founder on the channel.  However, it is possible to set a mode for
+  the channel which allows the original channel founder to regain the
+  founder privileges even after leaving the channel.  Channel founder
+  also cannot be removed by force from the channel.
+
+o Channel operator - When client joins to channel that has not existed
+  previously it will become automatically channel operator (and channel
+  founder discussed above).  Channel operator is able administrate the
+  channel, set some modes on channel, remove a badly behaving client
+  from the channel and promote other clients to become channel
+  operator.  The privileges are limited only to the particular channel.
+
+  Normal channel user may be promoted (opped) to channel operator
+  gaining channel operator privileges.  Channel founder or other
+  channel operator may also demote (deop) channel operator to normal
+  channel user.
+.in 3
+
+
+.ti 0
+3.4.1 Channel ID
+
+Channels are distinguished from other channels by unique Channel ID.
+The Channel ID is a 64 bit ID (for IPv4) or 160 bit ID (for IPv6), and
+collisions are not expected to happen in any conditions.  Channel names
+are just for logical use of channels.  The Channel ID is created by the
+server where the channel is created.  The Channel ID is defined as
+follows.
+
+.in 6
+64 bit Channel ID based on IPv4 addresses:
+
+32 bit  Router's Server ID IP address (bits 1-32)
+16 bit  Router's Server ID port (bits 33-48)
+16 bit  Random number
+
+160 bit Channel ID based on IPv6 addresses:
+
+128 bit  Router's Server ID IP address (bits 1-128)
+ 16 bit  Router's Server ID port (bits 129-144)
+ 16 bit  Random number
+
+o Router's Server ID IP address - Indicates the IP address of 
+  the router of the cell where this channel is created.  This is 
+  taken from the router's Server ID.  This way SILC router knows 
+  where this channel resides in the SILC network.
+
+o Router's Server ID port - Indicates the port of the channel on 
+  the server.  This is taken from the router's Server ID.
+
+o Random number - To further randomize the Channel ID.  This makes
+  sure that there are no collisions.  This also means that
+  in a cell there can be 2^16 channels.
+.in 3
+
+
+.ti 0
+3.5 Operators
+
+Operators are normal users with extra privileges to their server or
+router.  Usually these people are SILC server and router administrators
+that take care of their own server and clients on them.  The purpose of
+operators is to administrate the SILC server or router.  However, even
+an operator with highest privileges is not able to enter invite-only
+channel, to gain access to the contents of a encrypted and authenticated
+packets traveling in the SILC network or to gain channel operator
+privileges on public channels without being promoted.  They have the
+same privileges as everyone else except they are able to administrate
+their server or router.
+
+
+.ti 0
+3.6 SILC Commands
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+Client usually sends the commands and server replies by sending a reply
+packet to the command.  Server MAY also send commands usually to serve
+the original client's request.  However, server MUST NOT send commands
+to client and there are some commands that server must not send.
+
+Note that the command reply is usually sent only after client has sent
+the command request but server is allowed to send command reply packet
+to client even if client has not requested the command.  Client MAY,
+choose to ignore the command reply.
+
+It is expected that some of the commands may be miss-used by clients
+resulting various problems on the server side.  Every implementation
+SHOULD assure that commands may not be executed more than once, say,
+in two (2) seconds.  However, to keep response rate up, allowing for
+example five (5) commands before limiting is allowed.  It is RECOMMENDED
+that commands such as SILC_COMMAND_NICK, SILC_COMMAND_JOIN, 
+SILC_COMMAND_LEAVE and SILC_COMMAND_KILL SHOULD be limited in all cases
+as they require heavy operations.  This should be sufficient to prevent
+the miss-use of commands.
+
+SILC commands are described in [SILC4].
+
+
+.ti 0
+3.7 SILC Packets
+
+Packets are naturally the most important part of the protocol and the
+packets are what actually makes the protocol.  Packets in SILC network
+are always encrypted using, usually the shared secret session key
+or some other key, for example, channel key, when encrypting channel
+messages.  The SILC Packet Protocol is a wide protocol and is described
+in [SILC2].  This document does not define or describe details of
+SILC packets.
+
+
+
+
+
+.ti 0
+3.8 Packet Encryption
+
+All packets passed in SILC network MUST be encrypted.  This section
+defines how packets must be encrypted in the SILC network.  The detailed
+description of the actual encryption process of the packets are
+described in [SILC2].
+
+Client and its server shares secret symmetric session key which is
+established by the SILC Key Exchange Protocol, described in [SILC3]. 
+Every packet sent from client to server, with exception of packets for
+channels, are encrypted with this session key.
+
+Channels has their own key that are shared by every client on the channel.
+However, the channel keys are cell specific thus one cell does not know
+the channel key of the other cell, even if that key is for same channel.
+Channel key is also known by the routers and all servers that has clients
+on the channel.  However, channels MAY have channel private keys that
+are entirely local setting for the client.  All clients on the channel
+MUST know the channel private key before hand to be able to talk on the
+channel.  In this case, no server or router know the key for channel.
+
+Server shares secret symmetric session key with router which is
+established by the SILC Key Exchange Protocol.  Every packet passed from
+server to router, with exception of packets for channels, are encrypted
+with the shared session key.  Same way, router server shares secret
+symmetric key with its primary route.  However, every packet passed
+from router to other router, including packets for channels, are
+encrypted with the shared session key.  Every router connection has
+their own session keys.
+
+
+.ti 0
+3.8.1 Determination of the Source and the Destination
+
+The source and the destination of the packet needs to be determined
+to be able to route the packets to correct receiver.  This information
+is available in the SILC Packet Header which is included in all packets
+sent in SILC network.  The SILC Packet Header is described in [SILC2].
+
+The header MUST be encrypted with the session key who is next receiver
+of the packet along the route.  The receiver of the packet, for example
+a router along the route, is able to determine the sender and the
+destination of the packet by decrypting the SILC Packet Header and
+checking the ID's attached to the header.  The ID's in the header will
+tell to where the packet needs to be sent and where it is coming from.
+
+The header in the packet MUST NOT change during the routing of the
+packet.  The original sender, for example client, assembles the packet
+and the packet header and server or router between the sender and the
+receiver MUST NOT change the packet header.
+
+Note that the packet and the packet header may be encrypted with
+different keys.  For example, packets to channels are encrypted with
+the channel key, however, the header is encrypted with the session key
+as described above.  However, the header and the packet may be encrypted
+with same key.  This is the case, for example, with command packets.
+
+
+.ti 0
+3.8.2 Client To Client
+
+The process of message delivery and encryption from client to another
+client is as follows.
+
+Example:  Private message from client to another client on different
+          servers.  Clients do not share private message delivery
+          keys; normal session keys are used.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the session key shared between client and its
+  server.
+
+o Server determines the destination of the packet and decrypts
+  the packet.  Server encrypts the packet with session key shared
+  between the server and its router, and sends the packet to the
+  router.
+
+o Router determines the destination of the packet and decrypts
+  the packet.  Router encrypts the packet with session key 
+  shared between the router and the destination server, and sends
+  the packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and decrypts the packet.  Server encrypts the packet with
+  session key shared between the server and the destination client,
+  and sends the packet to the client.
+
+o Client 2. decrypts the packet.
+
+
+Example:  Private message from client to another client on different
+          servers.  Clients has established secret shared private
+          message delivery key with each other and that is used in 
+          the message encryption.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the private message delivery key shared between
+  clients.
+
+o Server determines the destination of the packet and sends the 
+  packet to the router.
+
+o Router determines the destination of the packet and sends the
+  packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and sends the packet to the client.
+
+o Client 2. decrypts the packet with the secret shared key.
+
+
+If clients share secret key with each other the private message
+delivery is much simpler since servers and routers between the
+clients do not need to decrypt and re-encrypt the packet.
+
+The process for clients on same server is much simpler as there are
+no need to send the packet to the router.  The process for clients 
+on different cells is same as above except that the packet is routed 
+outside the cell.  The router of the destination cell routes the 
+packet to the destination same way as described above.
+
+
+.ti 0
+3.8.3 Client To Channel
+
+Process of message delivery from client on channel to all the clients
+on the channel.
+
+Example:  Channel of four users; two on same server, other two on
+          different cells.  Client sends message to the channel.
+
+o Client 1. encrypts the packet with channel key and sends the
+  packet to its server.
+
+o Server determines local clients on the channel and sends the
+  packet to the Client on the same server.  Server then sends
+  the packet to its router for further routing.
+
+o Router determines local clients on the channel, if found
+  sends packet to the local clients.  Router determines global
+  clients on the channel and sends the packet to its primary
+  router or fastest route.
+
+o (Other router(s) do the same thing and sends the packet to
+   the server(s))
+
+o Server determines local clients on the channel and sends the
+  packet to the client.
+
+o All clients receiving the packet decrypts the packet.
+
+
+.ti 0
+3.8.4 Server To Server
+
+Server to server packet delivery and encryption is described in above
+examples. Router to router packet delivery is analogous to server to
+server.  However, some packets, such as channel packets, are processed
+differently.  These cases are described later in this document and
+more in detail in [SILC2].
+
+
+.ti 0
+3.9 Key Exchange And Authentication
+
+Key exchange is done always when for example client connects to server
+but also when server and router, and router and router connects to each
+other.  The purpose of key exchange protocol is to provide secure key
+material to be used in the communication.  The key material is used to
+derive various security parameters used to secure SILC packets.  The
+SILC Key Exchange protocol is described in detail in [SILC3].
+
+Authentication is done after key exchange protocol has been successfully
+completed.  The purpose of authentication is to authenticate for example
+client connecting to the server.  However, usually clients are accepted
+to connect to server without explicit authentication.  Servers are
+required use authentication protocol when connecting.  The authentication
+may be based on passphrase (pre-shared-secret) or public key.  The
+connection authentication protocol is described in detail in [SILC3].
+
+
+.ti 0
+3.9.1 Authentication Payload
+
+Authentication payload is used separately from the SKE and the Connection
+Authentication protocol.  It is used during the session to authenticate
+with the remote.  For example, the client can authenticate itself to the
+server to become server operator.  In this case, Authentication Payload is
+used.
+
+
+
+
+
+
+
+
+
+
+
+The format of the Authentication Payload is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Public Data Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Authentication Data Length  |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                       Authentication Data                     ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 5:  Authentication Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire payload.
+
+o Authentication Method (2) - The method of the authentication.
+  The authentication methods are defined in [SILC2] in the
+  Connection Auth Request Payload.  The NONE authentication
+  method SHOULD NOT be used.
+
+o Public Data Length (2 bytes) - Indicates the length of
+  the Public Data field.
+
+o Public Data (variable length) - This is defined only if
+  the authentication method is public key.  If it is any other
+  this field does not exist and the Public Data Length field
+  is set to zero (0).
+
+  When the authentication method is public key this includes
+  128 to 4096 bytes of non-zero random data that is used in
+  the signature process, described subsequently.
+
+o Authentication Data Length (2 bytes) - Indicates the
+  length of the Authentication Data field.
+
+o Authentication Data (variable length) - Authentication 
+  method dependent authentication data.
+.in 3
+
+
+If the authentication method is password based, the Authentication
+Data field includes the plaintext password.  It is safe to send
+plaintext password since the entire payload is encrypted.  In this
+case the Public Data Length is set to zero (0).
+
+If the authentication method is public key based (or certificate)
+the Authentication Data is computed as follows:
+
+  HASH = hash(random bytes | ID | public key (or certificate));
+  Authentication Data = sign(HASH);
+
+The hash() and the sign() are the hash function and the public key
+cryptography function selected in the SKE protocol.  The public key
+is SILC style public key unless certificates are used.  The ID is the
+entity's ID (Client or Server ID) which is authenticating itself.  The
+ID is raw ID data.  The random bytes are non-zero random bytes of
+length between 128 and 4096 bytes, and will be included into the
+Public Data field as is.
+
+The receiver will compute the signature using the random data received
+in the payload, the ID associated to the connection and the public key
+(or certificate) received in the SKE protocol.  After computing the
+receiver MUST verify the signature.  In this case also, the entire
+payload is encrypted.
+
+
+.ti 0
+3.10 Algorithms
+
+This section defines all the allowed algorithms that can be used in
+the SILC protocol.  This includes mandatory cipher, mandatory public
+key algorithm and MAC algorithms.
+
+
+.ti 0
+3.10.1 Ciphers
+
+Cipher is the encryption algorithm that is used to protect the data
+in the SILC packets.  See [SILC2] of the actual encryption process and
+definition of how it must be done.  SILC has a mandatory algorithm that
+must be supported in order to be compliant with this protocol.
+
+The following ciphers are defined in SILC protocol:
+
+.in 6
+aes-256-cbc         AES in CBC mode, 256 bit key       (REQUIRED)
+aes-192-cbc         AES in CBC mode, 192 bit key       (OPTIONAL)
+aes-128-cbc         AES in CBC mode, 128 bit key       (OPTIONAL)
+twofish-256-cbc     Twofish in CBC mode, 256 bit key   (OPTIONAL)
+twofish-192-cbc     Twofish in CBC mode, 192 bit key   (OPTIONAL)
+twofish-128-cbc     Twofish in CBC mode, 128 bit key   (OPTIONAL)
+blowfish-128-cbc    Blowfish in CBC mode, 128 bit key  (OPTIONAL)
+cast-256-cbc        CAST-256 in CBC mode, 256 bit key  (OPTIONAL)
+cast-192-cbc        CAST-256 in CBC mode, 192 bit key  (OPTIONAL)
+cast-128-cbc        CAST-256 in CBC mode, 128 bit key  (OPTIONAL)
+rc6-256-cbc         RC6 in CBC mode, 256 bit key       (OPTIONAL)
+rc6-192-cbc         RC6 in CBC mode, 192 bit key       (OPTIONAL)
+rc6-128-cbc         RC6 in CBC mode, 128 bit key       (OPTIONAL)
+mars-256-cbc        Mars in CBC mode, 256 bit key      (OPTIONAL)
+mars-192-cbc        Mars in CBC mode, 192 bit key      (OPTIONAL)
+mars-128-cbc        Mars in CBC mode, 128 bit key      (OPTIONAL)
+none                No encryption                      (OPTIONAL)
+.in 3
+
+
+Algorithm none does not perform any encryption process at all and 
+thus is not recommended to be used.  It is recommended that no client
+or server implementation would accept none algorithms except in special
+debugging mode.
+
+Additional ciphers MAY be defined to be used in SILC by using the
+same name format as above.
+
+
+.ti 0
+3.10.2 Public Key Algorithms
+
+Public keys are used in SILC to authenticate entities in SILC network
+and to perform other tasks related to public key cryptography.  The 
+public keys are also used in the SILC Key Exchange protocol [SILC3].
+
+The following public key algorithms are defined in SILC protocol:
+
+.in 6
+rsa        RSA  (REQUIRED)
+dss        DSS  (OPTIONAL)
+.in 3
+
+DSS is described in [Menezes].  The RSA MUST be implemented according
+PKCS #1 [PKCS1].  The mandatory PKCS #1 implementation in SILC MUST be
+compliant to either PKCS #1 version 1.5 or newer with the following
+notes: The signature encoding is always in same format as the encryption
+encoding regardless of the PKCS #1 version.  The signature with appendix
+(with hash algorithm OID in the data) MUST NOT be used in the SILC.  The
+rationale for this is that there is no binding between the PKCS #1 OIDs
+and the hash algorithms used in the SILC protocol.  Hence, the encoding
+is always in PKCS #1 version 1.5 format.
+
+Additional public key algorithms MAY be defined to be used in SILC.
+
+
+
+
+.ti 0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section.  They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+The following Hash algorithm are defined in SILC protocol:
+
+.in 6
+sha1             SHA-1, length = 20      (REQUIRED)
+md5              MD5, length = 16        (OPTIONAL)
+.in 3
+
+
+.ti 0
+3.10.4 MAC Algorithms
+
+Data integrity is protected by computing a message authentication code
+(MAC) of the packet data.  See [SILC2] for details how to compute the
+MAC.
+
+The following MAC algorithms are defined in SILC protocol:
+
+.in 6
+hmac-sha1-96     HMAC-SHA1, length = 12  (REQUIRED)
+hmac-md5-96      HMAC-MD5, length = 12   (OPTIONAL)
+hmac-sha1        HMAC-SHA1, length = 20  (OPTIONAL)
+hmac-md5         HMAC-MD5, length = 16   (OPTIONAL)
+none             No MAC                  (OPTIONAL)
+.in 3
+
+The none MAC is not recommended to be used as the packet is not
+authenticated when MAC is not computed.  It is recommended that no
+client or server would accept none MAC except in special debugging
+mode.
+
+The HMAC algorithm is described in [HMAC] and hash algorithms that
+are used as part of the HMACs are described in [Scheneir] and in
+[Menezes]
+
+Additional MAC algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.10.5 Compression Algorithms
+
+SILC protocol supports compression that may be applied to unencrypted
+data.  It is recommended to use compression on slow links as it may
+significantly speed up the data transmission.  By default, SILC does not
+use compression which is the mode that must be supported by all SILC
+implementations.
+
+
+
+The following compression algorithms are defined:
+
+.in 6
+none        No compression               (REQUIRED)
+zlib        GNU ZLIB (LZ77) compression  (OPTIONAL)
+.in 3
+
+Additional compression algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.11 SILC Public Key
+
+This section defines the type and format of the SILC public key.  All
+implementations MUST support this public key type.  See [SILC3] for
+other optional public key and certificate types allowed in the SILC
+protocol.  Public keys in SILC may be used to authenticate entities
+and to perform other tasks related to public key cryptography.
+
+The format of the SILC Public Key is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Public Key Length                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Algorithm Name Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Algorithm Name                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Identifier Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Identifier                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  SILC Public Key
+
+
+.in 6
+o Public Key Length (4 bytes) - Indicates the full length
+  of the public key, not including this field.
+
+o Algorithm Name Length (2 bytes) - Indicates the length
+  of the Algorithm Length field, not including this field.
+
+o Algorithm name (variable length) - Indicates the name
+  of the public key algorithm that the key is.  See the
+  section 3.10.2 Public Key Algorithms for defined names.
+
+o Identifier Length (2 bytes) - Indicates the length of
+  the Identifier field, not including this field.
+
+o Identifier (variable length) - Indicates the identifier
+  of the public key.  This data can be used to identify
+  the owner of the key.  The identifier is of the following
+  format:
+
+     UN   User name
+     HN   Host name or IP address
+     RN   Real name
+     E    EMail address
+     O    Organization
+     C    Country
+
+
+  Examples of an identifier:
+
+    `UN=priikone, HN=poseidon.pspt.fi, E=priikone@poseidon.pspt.fi'
+
+    `UN=sam, HN=dummy.fi, RN=Sammy Sam, O=Company XYZ, C=Finland'
+
+  At least user name (UN) and host name (HN) MUST be provided as
+  identifier.  The fields are separated by commas (`,').  If
+  comma is in the identifier string it must be written as `\\,',
+  for example, `O=Company XYZ\\, Inc.'.
+
+o Public Data (variable length) - Includes the actual
+  public data of the public key.
+
+  The format of this field for RSA algorithm is
+  as follows:
+
+     4 bytes            Length of e
+     variable length    e
+     4 bytes            Length of n
+     variable length    n
+
+
+  The format of this field for DSS algorithm is
+  as follows:
+
+     4 bytes            Length of p
+     variable length    p
+     4 bytes            Length of q
+     variable length    q
+     4 bytes            Length of g
+     variable length    g
+     4 bytes            Length of y
+     variable length    y
+
+  The variable length fields are multiple precession
+  integers encoded as strings in both examples.
+
+  Other algorithms must define their own type of this
+  field if they are used.
+.in 3
+
+All fields in the public key are in MSB (most significant byte first)
+order.
+
+
+.ti 0
+3.12 SILC Version Detection
+
+The version detection of both client and server is performed at the
+connection phase while executing the SILC Key Exchange protocol.  The
+version identifier is exchanged between initiator and responder.  The
+version identifier is of the following format:
+
+.in 6
+SILC-<protocol version>-<software version>
+.in 3
+
+The version strings are of the following format:
+
+.in 6
+protocol version = <major>.<minor>
+software version = <major>[.<minor>[.<build>]]
+.in 3
+
+Protocol version MAY provide both major and minor version.  Currently
+implementations MUST set the protocol version and accept the protocol
+version as SILC-1.0-<software version>. 
+
+Software version MAY provide major, minor and build version.  The
+software version MAY be freely set and accepted.
+
+
+Thus, the version string could be, for example:
+
+.in 6
+SILC-1.0-1.2
+.in 3
+
+
+
+
+.ti 0
+4 SILC Procedures
+
+This section describes various SILC procedures such as how the 
+connections are created and registered, how channels are created and
+so on.  The section describes the procedures only generally as details
+are described in [SILC2] and [SILC3].
+
+
+.ti 0
+4.1 Creating Client Connection
+
+This section describes the procedure when client connects to SILC server.
+When client connects to server the server MUST perform IP address lookup
+and reverse IP address lookup to assure that the origin host really is
+who it claims to be.  Client, host, connecting to server SHOULD have 
+both valid IP address and fully qualified domain name (FQDN).
+
+After that the client and server performs SILC Key Exchange protocol
+which will provide the key material used later in the communication.
+The key exchange protocol MUST be completed successfully before the
+connection registration may continue.  The SILC Key Exchange protocol
+is described in [SILC3].
+
+Typical server implementation would keep a list of connections that it
+allows to connect to the server.  The implementation would check, for
+example, the connecting client's IP address from the connection list
+before the SILC Key Exchange protocol has been started.  Reason for
+this is that if the host is not allowed to connect to the server there
+is no reason to perform the key exchange protocol.
+
+After successful key exchange protocol the client and server performs
+connection authentication protocol.  The purpose of the protocol is to
+authenticate the client connecting to the server.  Flexible
+implementation could also accept the client to connect to the server
+without explicit authentication.  However, if authentication is
+desired for a specific client it may be based on passphrase or
+public key authentication.  If authentication fails the connection
+MUST be terminated.  The connection authentication protocol is described
+in [SILC3].
+
+After successful key exchange and authentication protocol the client
+registers itself by sending SILC_PACKET_NEW_CLIENT packet to the
+server.  This packet includes various information about the client
+that the server uses to create the client.  Server creates the client
+and sends SILC_PACKET_NEW_ID to the client which includes the created
+Client ID that the client MUST start using after that.  After that
+all SILC packets from the client MUST have the Client ID as the
+Source ID in the SILC Packet Header, described in [SILC2].
+
+Client MUST also get the server's Server ID that is to be used as
+Destination ID in the SILC Packet Header when communicating with
+the server (for example when sending commands to the server).  The
+ID may be resolved in two ways.  Client can take the ID from an
+previously received packet from server that MUST include the ID,
+or to send SILC_COMMAND_INFO command and receive the Server ID as
+command reply.
+
+Server MAY choose not to use the information received in the
+SILC_PACKET_NEW_CLIENT packet.  For example, if public key or 
+certificate were used in the authentication, server MAY use those
+informations rather than what it received from client.  This is suitable
+way to get the true information about client if it is available.
+
+The nickname of client is initially set to the username sent in the
+SILC_PACKET_NEW_CLIENT packet.  User should set the nickname to more
+suitable by sending SILC_COMMAND_NICK command.  However, this is not
+required as part of registration process.
+
+Server MUST also distribute the information about newly registered
+client to its router (or if the server is router, to all routers in
+the SILC network).  More information about this in [SILC2].
+
+
+.ti 0
+4.2 Creating Server Connection
+
+This section describes the procedure when server connects to its
+router (or when router connects to other router, the cases are
+equivalent).  The procedure is very much alike when client connects
+to the server thus it is not repeated here.
+
+One difference is that server MUST perform connection authentication
+protocol with proper authentication.  A proper authentication is based
+on passphrase or public key authentication.
+
+After server and router has successfully performed the key exchange
+and connection authentication protocol, the server register itself
+to the router by sending SILC_PACKET_NEW_SERVER packet.  This packet
+includes the server's Server ID that it has created by itself and
+other relevant information about the server.
+
+After router has received the SILC_PACKET_NEW_SERVER packet it
+distributes the information about newly registered server to all routers
+in the SILC network.  More information about this in [SILC2].
+
+As client needed to resolve the destination ID this MUST be done by the
+server that connected to the router, as well.  The way to resolve it is
+to get the ID from previously received packet.  The server MAY also 
+use SILC_COMMAND_INFO command to resolve the ID.  Server MUST also start
+using its own Server ID as Source ID in SILC Packet Header and the
+router's Server ID as Destination when communicating with the router.
+
+
+.ti 0
+4.2.1 Announcing Clients, Channels and Servers
+
+After server or router has connected to the remote router, and it already
+has connected clients and channels it MUST announce them to the router.
+If the server is router server, also all the local servers in the cell
+MUST be announced.
+
+All clients are announced by compiling a list of ID Payloads into the
+SILC_PACKET_NEW_ID packet.  All channels are announced by compiling a
+list of Channel Payloads into the SILC_PACKET_NEW_CHANNEL packet.  Also, 
+the channel users on the channels must be announced by compiling a
+list of Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into
+the SILC_PACKET_NOTIFY packet.  The users' modes on the channel must 
+also be announced by compiling list of Notify Payloads with the 
+SILC_NOTIFY_TYPE_CUMODE_CHANGE notify type into the SILC_PACKET_NOTIFY
+packet.
+
+The router MUST also announce the local servers by compiling list of
+ID Payloads into the SILC_PACKET_NEW_ID packet.
+
+The router which receives these lists MUST process them and broadcast
+the packets to its primary route.
+
+When processing the announced channels and channel users the router MUST
+check whether a channel exists already with the same name.  If channel
+exists with the same name it MUST check whether the Channel ID is
+different.  If the Channel ID is different the router MUST send the notify
+type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to the server to force the channel ID
+change to the ID the router has.  If the mode of the channel is different
+the router MUST send the notify type SILC_NOTIFY_TYPE_CMODE_CHANGE to the
+server to force the mode change to the mode that the router has.
+
+The router MUST also generate new channel key and distribute it to the
+channel.  The key MUST NOT be generated if the SILC_CMODE_PRIVKEY mode
+is set.
+
+If the channel has channel founder on the router the router MUST send
+the notify type SILC_NOTIFY_TYPE_CUMODE_CHANGE to the server to force
+the mode change for the channel founder on the server.  The channel 
+founder privileges MUST be removed.
+
+The router processing the channels MUST also compile a list of
+Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into the
+SILC_PACKET_NOTIFY and send the packet to the server.  This way the
+server (or router) will receive the clients on the channel that
+the router has.
+
+
+.ti 0
+4.3 Joining to a Channel
+
+This section describes the procedure when client joins to a channel.
+Client joins to channel by sending command SILC_COMMAND_JOIN to the
+server.  If the receiver receiving join command is normal server the
+server MUST check its local list whether this channel already exists
+locally.  This would indicate that some client connected to the server
+has already joined to the channel.  If this is case the client is
+joined to the channel, new channel key is created and information about
+newly joined channel is sent to the router.  The router is informed
+by sending SILC_NOTIFY_TYPE_JOIN notify type.  The notify type MUST
+also be sent to the local clients on the channel.  The new channel key
+is also sent to the router and to local clients on the channel.
+
+If the channel does not exist in the local list the client's command
+MUST be sent to the router which will then perform the actual joining
+procedure.  When server receives the reply to the command from the
+router it MUST be sent to the client which sent the command originally.
+Server will also receive the channel key from the server that it MUST
+send to the client which originally requested the join command.  The
+server MUST also save the channel key.
+
+If the receiver of the join command is router it MUST first check its
+local list whether anyone in the cell has already joined to the channel.
+If this is the case the client is joined to the channel and reply is
+sent to the client.  If the command was sent by server the command reply
+is sent to the server which sent it.  Then the router MUST also create
+new channel key and distribute it to all clients on the channel and
+all servers that has clients on the channel.  Router MUST also send
+the SILC_NOTIFY_TYPE_JOIN notify type to local clients on the channel
+and to local servers that has clients on the channel.
+
+If the channel does not exist on the router's local list it MUST
+check the global list whether the channel exists at all.  If it does
+the client is joined to the channel as described previously.  If
+the channel does not exist the channel is created and the client
+is joined to the channel.  The channel key is also created and
+distributed as previously described.  The client joining to the created
+channel is made automatically channel founder and both channel founder
+and channel operator privileges is set for the client.
+
+If the router created the channel in the process, information about the
+new channel MUST be broadcasted to all routers.  This is done by 
+broadcasting SILC_PACKET_NEW_CHANNEL packet to the router's primary
+route.  When the router joins the client to the channel it MUST also
+send information about newly joined client to all routers in the SILC
+network.  This is done by broadcasting the SILC_NOTIFY_TYPE_JOIN notify
+type to the router's primary route. 
+
+It is important to note that new channel key is created always when
+new client joins to channel, whether the channel has existed previously
+or not.  This way the new client on the channel is not able to decrypt
+any of the old traffic on the channel.  Client which receives the reply to
+the join command MUST start using the received Channel ID in the channel
+message communication thereafter.  Client also receives the key for the
+channel in the command reply.  Note that the channel key is never
+generated if the SILC_CMODE_PRIVKEY mode is set.
+
+
+.ti 0
+4.4 Channel Key Generation
+
+Channel keys are created by router which creates the channel by taking
+enough randomness from cryptographically strong random number generator.
+The key is generated always when channel is created, when new client
+joins a channel and after the key has expired.  Key could expire for
+example in an hour.
+
+The key MUST also be re-generated whenever some client leaves a channel.
+In this case the key is created from scratch by taking enough randomness
+from the random number generator.  After that the key is distributed to
+all clients on the channel.  However, channel keys are cell specific thus
+the key is created only on the cell where the client, which left the
+channel, exists.  While the server or router is creating the new channel
+key, no other client may join to the channel.  Messages that are sent
+while creating the new key are still processed with the old key.  After
+server has sent the SILC_PACKET_CHANNEL_KEY packet MUST client start
+using the new key.  If server creates the new key the server MUST also
+send the new key to its router.  See [SILC2] on more information about
+how channel messages must be encrypted and decrypted when router is
+processing them.
+
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it MUST process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages.  The processing is as follows:
+
+  channel_key  = raw key data
+  HMAC key     = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+Note that the server MUST also save the channel key.
+
+
+.ti 0
+4.5 Private Message Sending and Reception
+
+Private messages are sent point to point.  Client explicitly destines
+a private message to specific client that is delivered to only to that
+client.  No other client may receive the private message.  The receiver
+of the private message is destined in the SILC Packet Header as any
+other packet as well.
+
+If the sender of a private message does not know the receiver's Client
+ID, it MUST resolve it from server.  There are two ways to resolve the
+client ID from server; it is RECOMMENDED that client implementations
+send SILC_COMMAND_IDENTIFY command to receive the Client ID.  Client
+MAY also send SILC_COMMAND_WHOIS command to receive the Client ID.
+If the sender has received earlier a private message from the receiver
+it should have cached the Client ID from the SILC Packet Header.
+
+See [SILC2] for description of private message encryption and decryption
+process.
+
+
+.ti 0
+4.6 Private Message Key Generation
+
+Private message MAY be protected by the key generated by the client.
+The key may be generated and sent to the other client by sending packet
+SILC_PACKET_PRIVATE_MESSAGE_KEY which travels through the network
+and is secured by session keys.  After that the private message key
+is used in the private message communication between those clients.
+
+Other choice is to entirely use keys that are not sent through
+the SILC network at all.  This significantly adds security.  This key
+would be pre-shared-key that is known by both of the clients.  Both
+agree about using the key and starts sending packets that indicate
+that the private message is secured using private message key.
+
+The key material used as private message key is implementation issue.
+However, SILC_PACKET_KEY_AGREEMENT packet MAY be used to negotiate
+the key material.  If the key is normal pre-shared-key or randomly
+generated key, and the SILC_PACKET_KEY_AGREEMENT was not used, then
+the key material SHOULD be processed as defined in the [SILC3].  In
+the processing, however, the HASH, as defined in [SILC3] MUST be 
+ignored.  After processing the key material it is employed as defined
+in [SILC3], however, the HMAC key material MUST be discarded.
+
+If the key is pre-shared-key or randomly generated the implementations
+should use the SILC protocol's mandatory cipher as the cipher.  If the
+SKE was used to negotiate key material the cipher was negotiated as well.
+
+.ti 0
+4.7 Channel Message Sending and Reception
+
+Channel messages are delivered to group of users.  The group forms a
+channel and all clients on the channel receives messages sent to the
+channel.
+
+Channel messages are destined to channel by specifying the Channel ID
+as Destination ID in the SILC Packet Header.  The server MUST then
+distribute the message to all clients on the channel by sending the
+channel message destined explicitly to a client on the channel.
+
+See [SILC2] for description of channel message encryption and decryption
+process.
+
+
+.ti 0
+4.8 Session Key Regeneration
+
+Session keys MUST be regenerated periodically, say, once in an hour.
+The re-key process is started by sending SILC_PACKET_REKEY packet to
+other end, to indicate that re-key must be performed.  The initiator
+of the connection SHOULD initiate the re-key.
+
+If perfect forward secrecy (PFS) flag was selected in the SILC Key
+Exchange protocol [SILC3] the re-key MUST cause new key exchange with
+SKE protocol.  In this case the protocol is secured with the old key
+and the protocol results to new key material.  See [SILC3] for more
+information.  After the SILC_PACKET_REKEY packet is sent the sender
+will perform the SKE protocol.
+
+If PFS flag was set the resulted key material is processed as described
+in the section Processing the Key Material in [SILC3].  The difference
+with re-key in the processing is that the initial data for the hash 
+function is just the resulted key material and not the HASH as it
+is not computed at all with re-key.  Other than that, the key processing
+it equivalent to normal SKE negotiation.
+
+If PFS flag was not set, which is the default case, then re-key is done
+without executing SKE protocol.  In this case, the new key is created by
+providing the current sending encryption key to the SKE protocol's key
+processing function.  The process is described in the section Processing
+the Key Material in [SILC3].  The difference in the processing is that
+the initial data for the hash function is the current sending encryption
+key and not the SKE's KEY and HASH values.  Other than that, the key
+processing is equivalent to normal SKE negotiation.
+
+After both parties has regenerated the session key, both MUST send
+SILC_PACKET_REKEY_DONE packet to each other.  These packets are still
+secured with the old key.  After these packets, the subsequent packets
+MUST be protected with the new key.
+
+
+
+
+.ti 0
+4.9 Command Sending and Reception
+
+Client usually sends the commands in the SILC network.  In this case
+the client simply sends the command packet to server and the server
+processes it and replies with command reply packet.
+
+However, if the server is not able to process the command, it is sent 
+to the server's router.  This is case for example with commands such
+as, SILC_COMMAND_JOIN and SILC_COMMAND_WHOIS commands.  However, there
+are other commands as well.  For example, if client sends the WHOIS
+command requesting specific information about some client the server must
+send the WHOIS command to router so that all clients in SILC network
+are searched.  The router, on the other hand, sends the WHOIS command
+further to receive the exact information about the requested client.
+The WHOIS command travels all the way to the server which owns the client
+and it replies with command reply packet.  Finally, the server which
+sent the command receives the command reply and it must be able to
+determine which client sent the original command.  The server then
+sends command reply to the client.  Implementations should have some
+kind of cache to handle, for example, WHOIS information.  Servers
+and routers along the route could all cache the information for faster
+referencing in the future.
+
+The commands sent by server may be sent hop by hop until someone is able
+to process the command.  However, it is preferred to destine the command
+as precisely as it is possible.  In this case, other routers en route
+MUST route the command packet by checking the true sender and true
+destination of the packet.  However, servers and routers MUST NOT route
+command reply packets to clients coming from other server.  Client
+MUST NOT accept command reply packet originated from anyone else but
+from its own server.
+
+
+.ti 0
+4.10 Closing Connection
+
+When remote client connection is closed the server MUST send the notify
+type SILC_NOTIFY_TYPE_SIGNOFF to its primary router and to all channels
+the client was joined.  The server MUST also save the client's information
+for a period of time for history purposes.
+
+When remote server or router connection is closed the server or router
+MUST also remove all the clients that was behind the server or router
+from the SILC Network.  The server or router MUST also send the notify
+type SILC_NOTIFY_TYPE_SERVER_SIGNOFF to its primary router and to all
+local clients that are joined on the same channels with the remote 
+server's or router's clients.
+
+
+.ti 0
+5 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+Special attention must also be paid on the servers and routers that are
+running the SILC service.  The SILC protocol's security depends greatly
+on the security and the integrity of the servers and administrators that
+are running the service.  It is recommended that some form of registration
+is required by the server and router administrator prior acceptance to
+the SILC Network.  The clients must be able to trust the servers they
+are using.
+
+It must also be noted that if the client requires absolute security by
+not trusting any of the servers or routers in the SILC Network, this can
+be accomplished by negotiating private keys outside the SILC Network,
+either using SKE or some other key negotiation protocol, or to use some
+other external means for distributing the keys.  This applies for all 
+messages, private messages and channel messages.  It is important to note
+that SILC, like any other security protocol is not full proof system and
+cannot secure from insecure environment; the SILC servers and routers could
+very well be compromised.  However, to provide acceptable level of security
+and usability for end user the protocol uses many times session keys or
+other keys generated by the servers to secure the messages.  If this is
+unacceptable for the client or end user, the private keys negotiatied 
+outside the SILC Network should always be used.  In the end it is always
+implementor's choice whether to negotiate private keys by default or
+whether to use the keys generated by the servers.
+
+It is also recommended that router operators in the SILC Network would
+form a joint forum to discuss the router and SILC Network management
+issues.  Also, router operators along with the cell's server operators
+should have a forum to discuss the cell management issues.
+
+
+.ti 0
+6 References
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+.ti 0
+7 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 21 February 2002
diff --git a/doc/draft-riikonen-silc-spec-04.nroff b/doc/draft-riikonen-silc-spec-04.nroff
new file mode 100644 (file)
index 0000000..3ba65ff
--- /dev/null
@@ -0,0 +1,2233 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH 13 November 2001
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-spec-04.txt                         13 November 2001
+Expires: 13 May 2002
+
+.in 3
+
+.ce 3
+Secure Internet Live Conferencing (SILC),
+Protocol Specification
+<draft-riikonen-silc-spec-04.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside the SILC network.
+Three other Internet Drafts relates very closely to this memo;
+SILC Packet Protocol [SILC2], SILC Key Exchange and Authentication
+Protocols [SILC3] and SILC Commands [SILC4].
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Concepts .................................................  4
+  2.1 SILC Network Topology .....................................  4
+  2.2 Communication Inside a Cell ...............................  5
+  2.3 Communication in the Network ..............................  6
+  2.4 Channel Communication .....................................  7
+  2.5 Router Connections ........................................  7
+3 SILC Specification ............................................  8
+  3.1 Client ....................................................  8
+      3.1.1 Client ID ...........................................  9
+  3.2 Server .................................................... 10
+      3.2.1 Server's Local ID List .............................. 10
+      3.2.2 Server ID ........................................... 11
+      3.2.3 SILC Server Ports ................................... 12
+  3.3 Router .................................................... 12
+      3.3.1 Router's Local ID List .............................. 12
+      3.3.2 Router's Global ID List ............................. 13
+      3.3.3 Router's Server ID .................................. 14
+  3.4 Channels .................................................. 14
+      3.4.1 Channel ID .......................................... 16
+  3.5 Operators ................................................. 16
+  3.6 SILC Commands ............................................. 16
+  3.7 SILC Packets .............................................. 17
+  3.8 Packet Encryption ......................................... 17
+      3.8.1 Determination of the Source and the Destination ..... 17
+      3.8.2 Client To Client .................................... 18
+      3.8.3 Client To Channel ................................... 19
+      3.8.4 Server To Server .................................... 20
+  3.9 Key Exchange And Authentication ........................... 20
+      3.9.1 Authentication Payload .............................. 20
+  3.10 Algorithms ............................................... 22
+      3.10.1 Ciphers ............................................ 22
+      3.10.2 Public Key Algorithms .............................. 23
+      3.10.3 Hash Functions ..................................... 24
+      3.10.4 MAC Algorithms ..................................... 24
+      3.10.5 Compression Algorithms ............................. 25
+  3.11 SILC Public Key .......................................... 25
+  3.12 SILC Version Detection ................................... 27
+  3.13 Backup Routers ........................................... 28
+      3.13.1 Switching to Backup Router ......................... 29
+      3.13.2 Resuming Primary Router ............................ 30
+      3.13.3 Discussion on Backup Router Scheme ................. 32
+4 SILC Procedures ............................................... 33
+  4.1 Creating Client Connection ................................ 33
+  4.2 Creating Server Connection ................................ 34
+      4.2.1 Announcing Clients, Channels and Servers ............ 35
+  4.3 Joining to a Channel ...................................... 36
+  4.4 Channel Key Generation .................................... 37
+  4.5 Private Message Sending and Reception ..................... 38
+  4.6 Private Message Key Generation ............................ 38
+  4.7 Channel Message Sending and Reception ..................... 39
+  4.8 Session Key Regeneration .................................. 39
+  4.9 Command Sending and Reception ............................. 40
+  4.10 Closing Connection ....................................... 41
+5 Security Considerations ....................................... 41
+6 References .................................................... 42
+7 Author's Address .............................................. 44
+
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  SILC Network Topology
+Figure 2:  Communication Inside cell
+Figure 3:  Communication Between Cells
+Figure 4:  Router Connections
+Figure 5:  SILC Public Key
+
+
+.ti 0
+1. Introduction
+
+This document describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.
+
+Strong cryptographic methods are used to protect SILC packets inside
+the SILC network.  Three other Internet Drafts relates very closely
+to this memo; SILC Packet Protocol [SILC2], SILC Key Exchange and
+Authentication Protocols [SILC3] and SILC Commands [SILC4].
+
+The protocol uses extensively packets as conferencing protocol 
+requires message and command sending.  The SILC Packet Protocol is
+described in [SILC2] and should be read to fully comprehend this
+document and protocol.  [SILC2] also describes the packet encryption
+and decryption in detail.
+
+The security of SILC protocol, and for any security protocol for that
+matter, is based on strong and secure key exchange protocol.  The SILC
+Key Exchange protocol is described in [SILC3] along with connection
+authentication protocol and should be read to fully comprehend this
+document and protocol.
+
+The SILC protocol has been developed to work on TCP/IP network
+protocol, although it could be made to work on other network protocols
+with only minor changes.  However, it is recommended that TCP/IP
+protocol is used under SILC protocol.  Typical implementation would
+be made in client-server model.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2. SILC Concepts
+
+This section describes various SILC protocol concepts that forms the 
+actual protocol, and in the end, the actual SILC network.  The mission
+of the protocol is to deliver messages from clients to other clients 
+through routers and servers in secure manner.  The messages may also 
+be delivered from one client to many clients forming a group, also 
+known as a channel.
+
+This section does not focus to security issues.  Instead, basic network 
+concepts are introduced to make the topology of the SILC network 
+clear.
+
+
+.ti 0
+2.1 SILC Network Topology
+
+SILC network is a cellular network as opposed to tree style network 
+topology.  The rationale for this is to have servers that can perform 
+specific kind of tasks what other servers cannot perform.  This leads 
+to two kinds of servers; normal SILC servers and SILC routers.
+
+A difference between normal server and router server is that routers 
+knows everything about everything in the network.  They also do the 
+actual routing of the messages to the correct receiver.  Normal servers 
+knows only about local information and nothing about global information.
+This makes the network faster as there are less servers that needs to 
+keep global information up to date at all time.
+
+This, on the other hand, leads to cellular like network, where routers 
+are in the center of the cell and servers are connected to the router.
+
+
+
+
+
+
+
+The following diagram represents SILC network topology.
+
+.in 8
+.nf
+  ---- ---- ----         ---- ---- ----
+ | S8 | S5 | S4 |       | S7 | S5 | S6 |
+ ----- ---- -----       ----- ---- -----
+| S7 | S/R1 | S2 | --- | S8 | S/R2 | S4 |
+ ---- ------ ----       ---- ------ ----
+ | S6 | S3 | S1 |       | S1 | S3 | S2 |         ---- ----
+  ---- ---- ----         ---- ---- ----         | S3 | S1 |
+     Cell 1.   \\             Cell 2.  | \\____  ----- -----
+                |                     |        | S4 | S/R4 |
+    ---- ---- ----         ---- ---- ----       ---- ------
+   | S7 | S4 | S2 |       | S1 | S3 | S2 |      | S2 | S5 |
+   ----- ---- -----       ----- ---- -----       ---- ----
+  | S6 | S/R3 | S1 | --- | S4 | S/R5 | S5 | ____/ Cell 4.
+   ---- ------ ----       ---- ------ ----
+   | S8 | S5 | S3 |       | S6 | S7 | S8 |     ... etc ...
+    ---- ---- ----         ---- ---- ----
+       Cell 3.                Cell 5.
+.in 3
+
+.ce
+Figure 1:  SILC Network Topology
+
+
+A cell is formed when a server or servers connect to one router.  In
+SILC network normal server cannot directly connect to other normal
+server.  Normal server may only connect to SILC router which then
+routes the messages to the other servers in the cell.  Router servers
+on the other hand may connect to other routers to form the actual SILC 
+network, as seen in above figure.  However, router is also normal SILC 
+server; clients may connect to it the same way as to normal SILC 
+server.  Normal server also cannot have active connections to more 
+than one router.  Normal server cannot be connected to two different 
+cells.  Router servers, on the other hand, may have as many router to 
+router connections as needed.
+
+There are many issues in this network topology that needs to be careful
+about.  Issues like the size of the cells, the number of the routers in 
+the SILC network and the capacity requirements of the routers.  These
+issues should be discussed in the Internet Community and additional
+documents on the issue may be written.
+
+
+.ti 0
+2.2 Communication Inside a Cell
+
+It is always guaranteed that inside a cell message is delivered to the 
+recipient with at most two server hops.  A client which is connected to
+server in the cell and is talking on channel to other client connected 
+to other server in the same cell, will have its messages delivered from 
+its local server first to the router of the cell, and from the router 
+to the other server in the cell.
+
+The following diagram represents this scenario:
+
+
+.in 25
+.nf
+1 --- S1     S4 --- 5
+         S/R
+ 2 -- S2     S3
+     /        |
+    4         3
+.in 3
+
+
+.ce
+Figure 2:  Communication Inside cell
+
+
+Example:  Client 1. connected to Server 1. send message to
+          Client 4. connected to Server 2. travels from Server 1.
+          first to Router which routes the message to Server 2.
+          which then sends it to the Client 4.  All the other
+          servers in the cell will not see the routed message.
+
+
+If the client is connected directly to the router, as router is also normal
+SILC server, the messages inside the cell are always delivered only with 
+one server hop.  If clients communicating with each other are connected 
+to the same server, no router interaction is needed.  This is the optimal
+situation of message delivery in the SILC network.
+
+
+.ti 0
+2.3 Communication in the Network
+
+If the message is destined to server that does not belong to local cell 
+the message is routed to the router server to which the destination 
+server belongs, if the local router is connected to destination router.
+If there is no direct connection to the destination router, the local
+router routes the message to its primary route.  The following diagram
+represents message sending between cells.
+
+
+.in 16
+.nf
+1 --- S1     S4 --- 5            S2 --- 1
+         S/R - - - - - - - - S/R
+ 2 -- S2     S3           S1
+     /        |             \\
+    4         3              2
+
+   Cell 1.               Cell 2.
+.in 3
+
+
+.ce
+Figure 3:  Communication Between Cells
+
+
+Example:  Client 5. connected to Server 4. in Cell 1. sends message
+          to Client 2. connected to Server 1. in Cell 2. travels
+          from Server 4. to Router which routes the message to
+          Router in Cell 2, which then routes the message to 
+          Server 1.  All the other servers and routers in the
+          network will not see the routed message.
+
+
+The optimal case of message delivery from the client point of view is
+when clients are connected directly to the routers and the messages
+are delivered from one router to the other.
+
+
+.ti 0 
+2.4 Channel Communication
+
+Messages may be sent to group of clients as well.  Sending messages to
+many clients works the same way as sending messages point to point, from
+message delivery point of view.  Security issues are another matter
+which are not discussed in this section.
+
+Router server handles the message routing to multiple recipients.  If 
+any recipient is not in the same cell as the sender the messages are 
+routed further.
+
+Server distributes the channel message to its local clients which are 
+joined to the channel.  Router also distributes the message to its 
+local clients on the channel.
+
+
+.ti 0
+2.5 Router Connections
+
+Router connections play very important role in making the SILC like
+network topology to work.  For example, sending broadcast packets in
+SILC network require special connections between routers; routers must
+be connected in a specific way.
+
+Every router has their primary route which is a connection to another
+router in the network.  Unless there is only two routers in the network
+must not routers use each other as their primary routes.  The router
+connections in the network must form a ring.
+
+
+
+
+
+
+
+Example with three routers in the network:
+
+
+.in 16
+.nf
+    S/R1 - > - > - > - > - > - > - S/R2
+     \\                               /
+      ^                             v
+       \\ - < -  < - S/R3 - < - < - /
+.in 3
+
+
+.ce
+Figure 4:  Router Connections
+
+
+Example:  Network with three routers.  Router 1. uses Router 2. as its
+          primary router.  Router 2. uses Router 3. as its primary router,
+          and Router 3. uses Router 1. as its primary router.  There may
+          be other direct connections between the routers but they must
+          not be used as primary routes.
+
+The above example is applicable to any amount of routers in the network
+except for two routers.  If there are only two routers in the network both
+routers must be able to handle situation where they use each other as their
+primary routes.
+
+The issue of router connections are very important especially with SILC
+broadcast packets.  Usually all router wide information in the network is
+distributed by SILC broadcast packets.  This sort of ring network, with
+ability to have other direct routes in the network cause interesting
+routing problems.  The [SILC2] discusses the routing of packets in this
+sort of network in more detail.
+
+
+.ti 0
+3. SILC Specification
+
+This section describes the SILC protocol.  However, [SILC2] and
+[SILC3] describes other important protocols that are part of this SILC
+specification and must be read.
+
+
+.ti 0
+3.1 Client
+
+A client is a piece of software connecting to SILC server.  SILC client 
+cannot be SILC server.  Purpose of clients is to provide the user 
+interface of the SILC services for end user.  Clients are distinguished
+from other clients by unique Client ID.  Client ID is a 128 bit ID that
+is used in the communication in the SILC network.  The client ID is 
+based on the nickname selected by the user.  User uses logical nicknames
+in communication which are then mapped to the corresponding Client ID.
+Client ID's are low level identifications and must not be seen by the
+end user.
+
+Clients provide other information about the end user as well. Information
+such as the nickname of the user, username and the host name of the end 
+user and user's real name.  See section 3.2 Server for information of 
+the requirements of keeping this information.
+
+The nickname selected by the user is not unique in the SILC network.
+There can be 2^8 same nicknames for one IP address.  As for comparison
+to IRC [IRC] where nicknames are unique this is a fundamental difference
+between SILC and IRC.  This causes the server names or client's host names
+to be used along with the nicknames to identify specific users when sending
+messages.  This feature of SILC makes IRC style nickname-wars obsolete as
+no one owns their nickname; there can always be someone else with the same
+nickname.  The maximum length of nickname is 128 characters.
+
+
+.ti 0
+3.1.1 Client ID
+
+Client ID is used to identify users in the SILC network.  The Client ID
+is unique to the extent that there can be 2^128 different Client ID's,
+and ID's based on IPv6 addresses extends this to 2^224 different Client
+ID's.  Collisions are not expected to happen.  The Client ID is defined
+as follows.
+
+
+
+.in 6
+128 bit Client ID based on IPv4 addresses:
+
+32 bit  Server ID IP address (bits 1-32)
+ 8 bit  Random number or counter
+88 bit  Truncated MD5 hash value of the nickname
+
+224 bit Client ID based on IPv6 addresses:
+
+128 bit  Server ID IP address (bits 1-128)
+  8 bit  Random number or counter
+ 88 bit  Truncated MD5 hash value of the nickname
+
+o Server ID IP address - Indicates the server where this
+  client is coming from.  The IP address hence equals the
+  server IP address where to the client has connected.
+
+o Random number or counter - Random number to further 
+  randomize the Client ID.  Another choice is to use
+  a counter starting from the zero (0).  This makes it
+  possible to have 2^8 same nicknames from the same
+  server IP address.
+
+o MD5 hash - MD5 hash value of the nickname is truncated
+  taking 88 bits from the start of the hash value.  This
+  hash value is used to search the user's Client ID from
+  the ID lists.
+
+.in 3
+Collisions could occur when more than 2^8 clients using same nickname
+from the same server IP address is connected to the SILC network.  
+Server MUST be able to handle this situation by refusing to accept 
+anymore of that nickname.
+
+Another possible collision may happen with the truncated hash value of
+the nickname.  It could be possible to have same truncated hash value for
+two different nicknames.  However, this is not expected to happen nor
+cause any problems if it would occur.  Nicknames are usually logical and
+it is unlikely to have two distinct logical nicknames produce same
+truncated hash value.
+
+
+.ti 0
+3.2 Server
+
+Servers are the most important parts of the SILC network.  They form the
+basis of the SILC, providing a point to which clients may connect to.
+There are two kinds of servers in SILC; normal servers and router servers.
+This section focus on the normal server and router server is described
+in the section 3.3 Router.
+
+Normal servers MUST NOT directly connect to other normal server.  Normal
+servers may only directly connect to router server.  If the message sent
+by the client is destined outside the local server it is always sent to
+the router server for further routing.  Server may only have one active
+connection to router on same port.  Normal server MUST NOT connect to other
+cell's router except in situations where its cell's router is unavailable.
+
+
+.ti 0
+3.2.1 Server's Local ID List
+
+Normal server keeps various information about the clients and their end
+users connected to it.  Every normal server MUST keep list of all locally
+connected clients, Client ID's, nicknames, usernames and host names and
+user's real name.  Normal servers only keeps local information and it
+does not keep any global information.  Hence, normal servers knows only
+about their locally connected clients.  This makes servers efficient as
+they don't have to worry about global clients.  Server is also responsible
+of creating the Client ID's for their clients.
+
+Normal server also keeps information about locally created channels and
+their Channel ID's.
+
+
+Hence, local list for normal server includes:
+
+.in 6
+server list        - Router connection
+   o Server name
+   o Server IP address
+   o Server ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+client list        - All clients in server
+   o Nickname
+   o Username@host
+   o Real name
+   o Client ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+
+channel list       - All channels in server
+   o Channel name
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+.ti 0
+3.2.2 Server ID
+
+Servers are distinguished from other servers by unique 64 bit Server ID 
+(for IPv4) or 160 bit Server ID (for IPv6).  The Server ID is used in
+the SILC to route messages to correct servers.  Server ID's also provide
+information for Client ID's, see section 3.1.1 Client ID.  Server ID is
+defined as follows.
+
+.in 6
+64 bit Server ID based on IPv4 addresses:
+
+32 bit  IP address of the server
+16 bit  Port
+16 bit  Random number
+
+160 bit Server ID based on IPv6 addresses:
+
+128 bit  IP address of the server
+ 16 bit  Port
+ 16 bit  Random number
+
+o IP address of the server - This is the real IP address of
+  the server.
+
+o Port - This is the port the server is bound to.
+
+o Random number - This is used to further randomize the Server ID.
+
+.in 3
+Collisions are not expected to happen in any conditions.  The Server ID
+is always created by the server itself and server is responsible of
+distributing it to the router.
+
+
+.ti 0
+3.2.3 SILC Server Ports
+
+The following ports has been assigned by IANA for the SILC protocol:
+
+.in 10
+silc            706/tcp    SILC
+silc            706/udp    SILC
+.in 3
+
+
+If there are needs to create new SILC networks in the future the port
+numbers must be officially assigned by the IANA.
+
+Server on network above privileged ports (>1023) SHOULD NOT be trusted
+as they could have been set up by untrusted party.
+
+
+.ti 0
+3.3 Router
+
+Router server in SILC network is responsible for keeping the cell together
+and routing messages to other servers and to other routers.  Router server
+is also a normal server thus clients may connect to it as it would be
+just normal SILC server.
+
+However, router servers has a lot of important tasks that normal servers
+do not have.  Router server knows everything about everything in the SILC.
+They know all clients currently on SILC, all servers and routers and all
+channels in SILC.  Routers are the only servers in SILC that care about
+global information and keeping them up to date at all time.  And, this
+is what they must do.
+
+
+.ti 0
+3.3.1 Router's Local ID List
+
+Router server as well MUST keep local list of connected clients and
+locally created channels.  However, this list is extended to include all
+the informations of the entire cell, not just the server itself as for
+normal servers.
+
+However, on router this list is a lot smaller since routers do not need
+to keep information about user's nickname, username and host name and real
+name since these are not needed by the router.  The router keeps only
+information that it needs.
+
+
+Hence, local list for router includes:
+
+.in 6
+server list        - All servers in the cell
+   o Server name
+   o Server ID
+   o Router's Server ID
+   o Sending key
+   o Receiving key
+
+client list        - All clients in the cell
+   o Client ID
+
+
+channel list       - All channels in the cell
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+Note that locally connected clients and other information include all the
+same information as defined in section section 3.2.1 Server's Local ID
+List.
+
+
+.ti 0
+3.3.2 Router's Global ID List
+
+Router server MUST also keep global list.  Normal servers do not have
+global list as they know only about local information.  Global list
+includes all the clients on SILC, their Client ID's, all created channels
+and their Channel ID's and all servers and routers on SILC and their
+Server ID's.  That is said, global list is for global information and the
+list must not include the local information already on the router's local
+list.
+
+Note that the global list does not include information like nicknames,
+usernames and host names or user's real names.  Router does not need to
+keep these informations as they are not needed by the router.  This 
+information is available from the client's server which maybe queried
+when needed.
+
+Hence, global list includes:
+
+.in 6
+server list        - All servers in SILC
+   o Server name
+   o Server ID
+   o Router's Server ID
+
+client list        - All clients in SILC
+   o Client ID
+
+channel list       - All channels in SILC
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+.in 3
+
+
+
+.ti 0
+3.3.3 Router's Server ID
+
+Router's Server ID's are equivalent to normal Server ID's.  As routers
+are normal servers as well same types of ID's applies for routers as well.
+Thus, see section 3.2.2 Server ID.
+
+
+.ti 0
+3.4 Channels
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client requests JOIN command to the channel, and the channel ceases to
+exist when the last client has left it.  When channel exists, any client
+can reference it using the name of the channel.
+
+Channel names are unique although the real uniqueness comes from 64 bit
+Channel ID.  However, channel names are still unique and no two global
+channels with same name may exist.  The Channel name is a string of
+maximum length of 256 characters.  Channel names MUST NOT contain any
+spaces (`  '), any non-printable ASCII characters, commas (`,') and
+wildcard characters.
+
+Channels can have operators that can administrate the channel and
+operate all of its modes.  The following operators on channel exist on
+the SILC network.
+
+.in 6
+o Channel founder - When channel is created the joining client becomes
+  channel founder.  Channel founder is channel operator with some more
+  privileges.  Basically, channel founder can fully operate the channel
+  and all of its modes.  The privileges are limited only to the
+  particular channel.  There can be only one channel founder per
+  channel. Channel founder supersedes channel operator's privileges.
+
+  Channel founder privileges cannot be removed by any other operator on
+  channel.  When channel founder leaves the channel there is no channel
+  founder on the channel.  However, it is possible to set a mode for
+  the channel which allows the original channel founder to regain the
+  founder privileges even after leaving the channel.  Channel founder
+  also cannot be removed by force from the channel.
+
+o Channel operator - When client joins to channel that has not existed
+  previously it will become automatically channel operator (and channel
+  founder discussed above).  Channel operator is able administrate the
+  channel, set some modes on channel, remove a badly behaving client
+  from the channel and promote other clients to become channel
+  operator.  The privileges are limited only to the particular channel.
+
+  Normal channel user may be promoted (opped) to channel operator
+  gaining channel operator privileges.  Channel founder or other
+  channel operator may also demote (deop) channel operator to normal
+  channel user.
+.in 3
+
+
+.ti 0
+3.4.1 Channel ID
+
+Channels are distinguished from other channels by unique Channel ID.
+The Channel ID is a 64 bit ID (for IPv4) or 160 bit ID (for IPv6), and
+collisions are not expected to happen in any conditions.  Channel names
+are just for logical use of channels.  The Channel ID is created by the
+server where the channel is created.  The Channel ID is defined as
+follows.
+
+.in 6
+64 bit Channel ID based on IPv4 addresses:
+
+32 bit  Router's Server ID IP address (bits 1-32)
+16 bit  Router's Server ID port (bits 33-48)
+16 bit  Random number
+
+160 bit Channel ID based on IPv6 addresses:
+
+128 bit  Router's Server ID IP address (bits 1-128)
+ 16 bit  Router's Server ID port (bits 129-144)
+ 16 bit  Random number
+
+o Router's Server ID IP address - Indicates the IP address of 
+  the router of the cell where this channel is created.  This is 
+  taken from the router's Server ID.  This way SILC router knows 
+  where this channel resides in the SILC network.
+
+o Router's Server ID port - Indicates the port of the channel on 
+  the server.  This is taken from the router's Server ID.
+
+o Random number - To further randomize the Channel ID.  This makes
+  sure that there are no collisions.  This also means that
+  in a cell there can be 2^16 channels.
+.in 3
+
+
+.ti 0
+3.5 Operators
+
+Operators are normal users with extra privileges to their server or
+router.  Usually these people are SILC server and router administrators
+that take care of their own server and clients on them.  The purpose of
+operators is to administrate the SILC server or router.  However, even
+an operator with highest privileges is not able to enter invite-only
+channel, to gain access to the contents of a encrypted and authenticated
+packets traveling in the SILC network or to gain channel operator
+privileges on public channels without being promoted.  They have the
+same privileges as everyone else except they are able to administrate
+their server or router.
+
+
+.ti 0
+3.6 SILC Commands
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+Client usually sends the commands and server replies by sending a reply
+packet to the command.  Server MAY also send commands usually to serve
+the original client's request.  However, server MUST NOT send commands
+to client and there are some commands that server must not send.
+
+Note that the command reply is usually sent only after client has sent
+the command request but server is allowed to send command reply packet
+to client even if client has not requested the command.  Client MAY,
+choose to ignore the command reply.
+
+It is expected that some of the commands may be miss-used by clients
+resulting various problems on the server side.  Every implementation
+SHOULD assure that commands may not be executed more than once, say,
+in two (2) seconds.  However, to keep response rate up, allowing for
+example five (5) commands before limiting is allowed.  It is RECOMMENDED
+that commands such as SILC_COMMAND_NICK, SILC_COMMAND_JOIN, 
+SILC_COMMAND_LEAVE and SILC_COMMAND_KILL SHOULD be limited in all cases
+as they require heavy operations.  This should be sufficient to prevent
+the miss-use of commands.
+
+SILC commands are described in [SILC4].
+
+
+.ti 0
+3.7 SILC Packets
+
+Packets are naturally the most important part of the protocol and the
+packets are what actually makes the protocol.  Packets in SILC network
+are always encrypted using, usually the shared secret session key
+or some other key, for example, channel key, when encrypting channel
+messages.  It is not possible to send packet in SILC network without
+encryption.  The SILC Packet Protocol is a wide protocol and is described
+in [SILC2].  This document does not define or describe details of
+SILC packets.
+
+
+.ti 0
+3.8 Packet Encryption
+
+All packets passed in SILC network MUST be encrypted.  This section
+defines how packets must be encrypted in the SILC network.  The detailed
+description of the actual encryption process of the packets are
+described in [SILC2].
+
+Client and its server shares secret symmetric session key which is
+established by the SILC Key Exchange Protocol, described in [SILC3]. 
+Every packet sent from client to server, with exception of packets for
+channels, are encrypted with this session key.
+
+Channels has their own key that are shared by every client on the channel.
+However, the channel keys are cell specific thus one cell does not know
+the channel key of the other cell, even if that key is for same channel.
+Channel key is also known by the routers and all servers that has clients
+on the channel.  However, channels MAY have channel private keys that
+are entirely local setting for the client.  All clients on the channel
+MUST know the channel private key before hand to be able to talk on the
+channel.  In this case, no server or router know the key for channel.
+
+Server shares secret symmetric session key with router which is
+established by the SILC Key Exchange Protocol.  Every packet passed from
+server to router, with exception of packets for channels, are encrypted
+with the shared session key.  Same way, router server shares secret
+symmetric key with its primary route.  However, every packet passed
+from router to other router, including packets for channels, are
+encrypted with the shared session key.  Every router connection has
+their own session keys.
+
+
+.ti 0
+3.8.1 Determination of the Source and the Destination
+
+The source and the destination of the packet needs to be determined
+to be able to route the packets to correct receiver.  This information
+is available in the SILC Packet Header which is included in all packets
+sent in SILC network.  The SILC Packet Header is described in [SILC2].
+
+The header MUST be encrypted with the session key who is next receiver
+of the packet along the route.  The receiver of the packet, for example
+a router along the route, is able to determine the sender and the
+destination of the packet by decrypting the SILC Packet Header and
+checking the ID's attached to the header.  The ID's in the header will
+tell to where the packet needs to be sent and where it is coming from.
+
+The header in the packet MUST NOT change during the routing of the
+packet.  The original sender, for example client, assembles the packet
+and the packet header and server or router between the sender and the
+receiver MUST NOT change the packet header.
+
+Note that the packet and the packet header may be encrypted with
+different keys.  For example, packets to channels are encrypted with
+the channel key, however, the header is encrypted with the session key
+as described above.  However, the header and the packet may be encrypted
+with same key.  This is the case, for example, with command packets.
+
+
+.ti 0
+3.8.2 Client To Client
+
+The process of message delivery and encryption from client to another
+client is as follows.
+
+Example:  Private message from client to another client on different
+          servers.  Clients do not share private message delivery
+          keys; normal session keys are used.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the session key shared between client and its
+  server.
+
+o Server determines the destination of the packet and decrypts
+  the packet.  Server encrypts the packet with session key shared
+  between the server and its router, and sends the packet to the
+  router.
+
+o Router determines the destination of the packet and decrypts
+  the packet.  Router encrypts the packet with session key 
+  shared between the router and the destination server, and sends
+  the packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and decrypts the packet.  Server encrypts the packet with
+  session key shared between the server and the destination client,
+  and sends the packet to the client.
+
+o Client 2. decrypts the packet.
+
+
+Example:  Private message from client to another client on different
+          servers.  Clients has established secret shared private
+          message delivery key with each other and that is used in 
+          the message encryption.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the private message delivery key shared between
+  clients.
+
+o Server determines the destination of the packet and sends the 
+  packet to the router.
+
+o Router determines the destination of the packet and sends the
+  packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and sends the packet to the client.
+
+o Client 2. decrypts the packet with the secret shared key.
+
+
+If clients share secret key with each other the private message
+delivery is much simpler since servers and routers between the
+clients do not need to decrypt and re-encrypt the packet.
+
+The process for clients on same server is much simpler as there are
+no need to send the packet to the router.  The process for clients 
+on different cells is same as above except that the packet is routed 
+outside the cell.  The router of the destination cell routes the 
+packet to the destination same way as described above.
+
+
+.ti 0
+3.8.3 Client To Channel
+
+Process of message delivery from client on channel to all the clients
+on the channel.
+
+Example:  Channel of four users; two on same server, other two on
+          different cells.  Client sends message to the channel.
+
+o Client 1. encrypts the packet with channel key and sends the
+  packet to its server.
+
+o Server determines local clients on the channel and sends the
+  packet to the Client on the same server.  Server then sends
+  the packet to its router for further routing.
+
+o Router determines local clients on the channel, if found
+  sends packet to the local clients.  Router determines global
+  clients on the channel and sends the packet to its primary
+  router or fastest route.
+
+o (Other router(s) do the same thing and sends the packet to
+   the server(s))
+
+o Server determines local clients on the channel and sends the
+  packet to the client.
+
+o All clients receiving the packet decrypts the packet.
+
+
+.ti 0
+3.8.4 Server To Server
+
+Server to server packet delivery and encryption is described in above
+examples. Router to router packet delivery is analogous to server to
+server.  However, some packets, such as channel packets, are processed
+differently.  These cases are described later in this document and
+more in detail in [SILC2].
+
+
+.ti 0
+3.9 Key Exchange And Authentication
+
+Key exchange is done always when for example client connects to server
+but also when server and router, and router and router connects to each
+other.  The purpose of key exchange protocol is to provide secure key
+material to be used in the communication.  The key material is used to
+derive various security parameters used to secure SILC packets.  The
+SILC Key Exchange protocol is described in detail in [SILC3].
+
+Authentication is done after key exchange protocol has been successfully
+completed.  The purpose of authentication is to authenticate for example
+client connecting to the server.  However, usually clients are accepted
+to connect to server without explicit authentication.  Servers are
+required use authentication protocol when connecting.  The authentication
+may be based on passphrase (pre-shared-secret) or public key.  The
+connection authentication protocol is described in detail in [SILC3].
+
+
+.ti 0
+3.9.1 Authentication Payload
+
+Authentication payload is used separately from the SKE and the Connection
+Authentication protocol.  It is used during the session to authenticate
+with the remote.  For example, the client can authenticate itself to the
+server to become server operator.  In this case, Authentication Payload is
+used.
+
+The format of the Authentication Payload is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Public Data Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Authentication Data Length  |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                       Authentication Data                     ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 5:  Authentication Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire payload.
+
+o Authentication Method (2) - The method of the authentication.
+  The authentication methods are defined in [SILC2] in the
+  Connection Auth Request Payload.  The NONE authentication
+  method SHOULD NOT be used.
+
+o Public Data Length (2 bytes) - Indicates the length of
+  the Public Data field.
+
+o Public Data (variable length) - This is defined only if
+  the authentication method is public key.  If it is any other
+  this field MAY include a random data for padding purposes.
+  However, in this case the field MUST be ignored by the
+  receiver.
+
+  When the authentication method is public key this includes
+  128 to 4096 bytes of non-zero random data that is used in
+  the signature process, described subsequently.
+
+o Authentication Data Length (2 bytes) - Indicates the
+  length of the Authentication Data field.
+
+o Authentication Data (variable length) - Authentication 
+  method dependent authentication data.
+.in 3
+
+
+If the authentication method is password based, the Authentication
+Data field includes the plaintext password.  It is safe to send
+plaintext password since the entire payload is encrypted.  In this
+case the Public Data Length is set to zero (0), but MAY also include
+random data for padding purposes.  It is also RECOMMENDED that maximum
+amount of padding is applied to SILC packet when using password based
+authentication.  This way it is not possible to approximate the length
+of the password from the encrypted packet.
+
+If the authentication method is public key based (or certificate)
+the Authentication Data is computed as follows:
+
+  HASH = hash(random bytes | ID | public key (or certificate));
+  Authentication Data = sign(HASH);
+
+The hash() and the sign() are the hash function and the public key
+cryptography function selected in the SKE protocol.  The public key
+is SILC style public key unless certificates are used.  The ID is the
+entity's ID (Client or Server ID) which is authenticating itself.  The
+ID is raw ID data.  The random bytes are non-zero random bytes of
+length between 128 and 4096 bytes, and will be included into the
+Public Data field as is.
+
+The receiver will compute the signature using the random data received
+in the payload, the ID associated to the connection and the public key
+(or certificate) received in the SKE protocol.  After computing the
+receiver MUST verify the signature.  In this case also, the entire
+payload is encrypted.
+
+
+.ti 0
+3.10 Algorithms
+
+This section defines all the allowed algorithms that can be used in
+the SILC protocol.  This includes mandatory cipher, mandatory public
+key algorithm and MAC algorithms.
+
+
+.ti 0
+3.10.1 Ciphers
+
+Cipher is the encryption algorithm that is used to protect the data
+in the SILC packets.  See [SILC2] of the actual encryption process and
+definition of how it must be done.  SILC has a mandatory algorithm that
+must be supported in order to be compliant with this protocol.
+
+The following ciphers are defined in SILC protocol:
+
+.in 6
+aes-256-cbc         AES in CBC mode, 256 bit key       (REQUIRED)
+aes-192-cbc         AES in CBC mode, 192 bit key       (OPTIONAL)
+aes-128-cbc         AES in CBC mode, 128 bit key       (OPTIONAL)
+twofish-256-cbc     Twofish in CBC mode, 256 bit key   (OPTIONAL)
+twofish-192-cbc     Twofish in CBC mode, 192 bit key   (OPTIONAL)
+twofish-128-cbc     Twofish in CBC mode, 128 bit key   (OPTIONAL)
+blowfish-128-cbc    Blowfish in CBC mode, 128 bit key  (OPTIONAL)
+cast-256-cbc        CAST-256 in CBC mode, 256 bit key  (OPTIONAL)
+cast-192-cbc        CAST-256 in CBC mode, 192 bit key  (OPTIONAL)
+cast-128-cbc        CAST-256 in CBC mode, 128 bit key  (OPTIONAL)
+rc6-256-cbc         RC6 in CBC mode, 256 bit key       (OPTIONAL)
+rc6-192-cbc         RC6 in CBC mode, 192 bit key       (OPTIONAL)
+rc6-128-cbc         RC6 in CBC mode, 128 bit key       (OPTIONAL)
+mars-256-cbc        Mars in CBC mode, 256 bit key      (OPTIONAL)
+mars-192-cbc        Mars in CBC mode, 192 bit key      (OPTIONAL)
+mars-128-cbc        Mars in CBC mode, 128 bit key      (OPTIONAL)
+none                No encryption                      (OPTIONAL)
+.in 3
+
+
+Algorithm none does not perform any encryption process at all and 
+thus is not recommended to be used.  It is recommended that no client
+or server implementation would accept none algorithms except in special
+debugging mode.
+
+Additional ciphers MAY be defined to be used in SILC by using the
+same name format as above.
+
+
+.ti 0
+3.10.2 Public Key Algorithms
+
+Public keys are used in SILC to authenticate entities in SILC network
+and to perform other tasks related to public key cryptography.  The 
+public keys are also used in the SILC Key Exchange protocol [SILC3].
+
+The following public key algorithms are defined in SILC protocol:
+
+.in 6
+rsa        RSA  (REQUIRED)
+dss        DSS  (OPTIONAL)
+.in 3
+
+DSS is described in [Menezes].  The RSA MUST be implemented according
+PKCS #1 [PKCS1].  The mandatory PKCS #1 implementation in SILC MUST be
+compliant to either PKCS #1 version 1.5 or newer with the following
+notes: The signature encoding is always in same format as the encryption
+encoding regardless of the PKCS #1 version.  The signature with appendix
+(with hash algorithm OID in the data) MUST NOT be used in the SILC.  The
+rationale for this is that there is no binding between the PKCS #1 OIDs
+and the hash algorithms used in the SILC protocol.  Hence, the encoding
+is always in PKCS #1 version 1.5 format.
+
+Additional public key algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section.  They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+The following Hash algorithm are defined in SILC protocol:
+
+.in 6
+sha1             SHA-1, length = 20      (REQUIRED)
+md5              MD5, length = 16        (OPTIONAL)
+.in 3
+
+
+.ti 0
+3.10.4 MAC Algorithms
+
+Data integrity is protected by computing a message authentication code
+(MAC) of the packet data.  See [SILC2] for details how to compute the
+MAC.
+
+The following MAC algorithms are defined in SILC protocol:
+
+.in 6
+hmac-sha1-96     HMAC-SHA1, length = 12  (REQUIRED)
+hmac-md5-96      HMAC-MD5, length = 12   (OPTIONAL)
+hmac-sha1        HMAC-SHA1, length = 20  (OPTIONAL)
+hmac-md5         HMAC-MD5, length = 16   (OPTIONAL)
+none             No MAC                  (OPTIONAL)
+.in 3
+
+The none MAC is not recommended to be used as the packet is not
+authenticated when MAC is not computed.  It is recommended that no
+client or server would accept none MAC except in special debugging
+mode.
+
+The HMAC algorithm is described in [HMAC] and hash algorithms that
+are used as part of the HMACs are described in [Scheneir] and in
+[Menezes]
+
+Additional MAC algorithms MAY be defined to be used in SILC.
+
+
+
+
+.ti 0
+3.10.5 Compression Algorithms
+
+SILC protocol supports compression that may be applied to unencrypted
+data.  It is recommended to use compression on slow links as it may
+significantly speed up the data transmission.  By default, SILC does not
+use compression which is the mode that must be supported by all SILC
+implementations.
+
+The following compression algorithms are defined:
+
+.in 6
+none        No compression               (REQUIRED)
+zlib        GNU ZLIB (LZ77) compression  (OPTIONAL)
+.in 3
+
+Additional compression algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.11 SILC Public Key
+
+This section defines the type and format of the SILC public key.  All
+implementations MUST support this public key type.  See [SILC3] for
+other optional public key and certificate types allowed in the SILC
+protocol.  Public keys in SILC may be used to authenticate entities
+and to perform other tasks related to public key cryptography.
+
+The format of the SILC Public Key is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Public Key Length                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Algorithm Name Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Algorithm Name                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Identifier Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Identifier                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  SILC Public Key
+
+
+.in 6
+o Public Key Length (4 bytes) - Indicates the full length
+  of the public key, not including this field.
+
+o Algorithm Name Length (2 bytes) - Indicates the length
+  of the Algorithm Length field, not including this field.
+
+o Algorithm name (variable length) - Indicates the name
+  of the public key algorithm that the key is.  See the
+  section 3.10.2 Public Key Algorithms for defined names.
+
+o Identifier Length (2 bytes) - Indicates the length of
+  the Identifier field, not including this field.
+
+o Identifier (variable length) - Indicates the identifier
+  of the public key.  This data can be used to identify
+  the owner of the key.  The identifier is of the following
+  format:
+
+     UN   User name
+     HN   Host name or IP address
+     RN   Real name
+     E    EMail address
+     O    Organization
+     C    Country
+
+
+  Examples of an identifier:
+
+    `UN=priikone, HN=poseidon.pspt.fi, E=priikone@poseidon.pspt.fi'
+
+    `UN=sam, HN=dummy.fi, RN=Sammy Sam, O=Company XYZ, C=Finland'
+
+  At least user name (UN) and host name (HN) MUST be provided as
+  identifier.  The fields are separated by commas (`,').  If
+  comma is in the identifier string it must be written as `\\,',
+  for example, `O=Company XYZ\\, Inc.'.
+
+o Public Data (variable length) - Includes the actual
+  public data of the public key.
+
+  The format of this field for RSA algorithm is
+  as follows:
+
+     4 bytes            Length of e
+     variable length    e
+     4 bytes            Length of n
+     variable length    n
+
+
+  The format of this field for DSS algorithm is
+  as follows:
+
+     4 bytes            Length of p
+     variable length    p
+     4 bytes            Length of q
+     variable length    q
+     4 bytes            Length of g
+     variable length    g
+     4 bytes            Length of y
+     variable length    y
+
+  The variable length fields are multiple precession
+  integers encoded as strings in both examples.
+
+  Other algorithms must define their own type of this
+  field if they are used.
+.in 3
+
+All fields in the public key are in MSB (most significant byte first)
+order.
+
+
+.ti 0
+3.12 SILC Version Detection
+
+The version detection of both client and server is performed at the
+connection phase while executing the SILC Key Exchange protocol.  The
+version identifier is exchanged between initiator and responder.  The
+version identifier is of the following format:
+
+.in 6
+SILC-<protocol version>-<software version>
+.in 3
+
+The version strings are of the following format:
+
+.in 6
+protocol version = <major>.<minor>
+software version = <major>[.<minor>[.<build>]]
+.in 3
+
+Protocol version MAY provide both major and minor version.  Currently
+implementations MUST set the protocol version and accept the protocol
+version as SILC-1.0-<software version>.  If new protocol version causes
+in compatibilities with older version the the <minor> versio number MUST
+be incremented.  The <major> is incremented if new protocol version is
+fully incompatible.
+
+Software version MAY provide major, minor and build version.  The
+software version MAY be freely set and accepted.
+
+
+Thus, the version string could be, for example:
+
+.in 6
+SILC-1.0-1.2
+.in 3
+
+
+.ti 0
+3.13 Backup Routers
+
+Backup routers may exist in the cell in addition of the primary router.
+However, they must not be active routers and act as routers in the cell.
+Only one router may be acting as primary router in the cell.  In the case
+of failure of the primary router may one of the backup routers become
+active.  The purpose of backup routers are in case of failure of the
+primary router to maintain working connections inside the cell and outside
+the cell and to avoid netsplits.
+
+Backup routers are normal servers in the cell that are prepared to take
+over the tasks of the primary router if needed.  They need to have at
+least one direct and active connection to the primary router of the cell.
+This communication channel is used to send the router information to
+the backup router.  When the backup router connects to the primary router
+of the cell it MUST present itself as router server in the Connection
+Authentication protocol, even though it is normal server as long as the
+primary router is available.  Reason for this is that the configuration
+needed in the responder end requires usually router connection level
+configuration.  The responder, however must understand and treat the
+connection as normal server (except when feeding router level data to
+the backup router).
+
+Backup router must know everything that the primary router knows to be
+able to take over the tasks of the primary router.  It is the primary
+router's responsibility to feed the data to the backup router.  If the
+backup router does not know all the data in the case of failure some
+connections may be lost.  The primary router of the cell must consider
+the backup router being actual router server when it feeds the data to
+it.
+
+In addition of having direct connection to the primary router of the
+cell, the backup router must also have connection to the same router
+the primary router of the cell is connected.  However, it must not be
+active router connection meaning that the backup router must not use
+that channel as its primary route and it must not notify the router
+about having connected servers, channels and clients behind it.  It
+merely connects to the router.  This sort of connection is later
+referred as being passive connection.  Some keepalive actions may be
+needed by the router to keep the connection alive.
+
+It is required that other normal servers have passive connections to
+the backup router(s) in the cell.  Some keepalive actions may be needed
+by the server to keep the connection alive.  After they notice the
+failure of the primary router they must start using the connection to
+the first backup router as their primary route.
+
+Also, if any other router in the network is using the cell's primary
+router as its own primary router, it must also have passive connection
+to the cell's backup router.  It too is prepared to switch to use the
+backup router as its new primary router as soon as the orignal primary
+router becomes unresponsive.
+
+All of the parties of this protocol knows which one is the backup router
+of the cell from their local configuration.  Each of the entity must
+be configured accordingly and care must be taken when configuring the
+backup routers, servers and other routers in the network.
+
+It must be noted that some of the channel messages and private messages
+may be lost during the switch to the backup router.  The announcements
+assures that the state of the network is not lost during the switch.
+
+It is RECOMMENDED that there would be at least one backup router in
+the cell.  It is NOT RECOMMENDED to have all servers in the cell acting
+as backup routers as it requires establishing several connections to
+several servers in the cell.  Large cells can easily have several
+backup routers in the cell.
+
+The order of the backup routers are decided at the configuration phase.
+All the parties of this protocol must be configured accordingly to 
+understand the order of the backup routers.  It is not required that
+the backup server is actually active server in the cell.  Backup router
+may be a spare server in the cell that does not accept normal client
+connections at all.  It may be reserved purely for the backup purposes.
+These, however, are cell management issues.
+
+If also the first backup router is down as well and there is another
+backup router in the cell then it will start acting as the primary
+router as described above.
+
+
+.ti 0
+3.13.1 Switching to Backup Router
+
+When the primary router of the cell becomes unresponsive, for example
+by sending EOF to the connection, all the parties of this protocol MUST
+replace the old connection to the primary router with first configured
+backup router.  The backup router usually needs to do local modifications
+to its database in order to update all the information needed to maintain
+working routes.  The backup router must understand that clients that
+were orignated from the primary router are now originated from some of
+the existing server connections and must update them accordingly.  It
+must also remove those clients that were owned by the primary router
+since those connections were lost when the primary router became
+unresponsive.
+
+All the other parties of the protocol must also update their local
+database to understand that the route to the primary router will now go
+to the backup router.
+
+The servers connected to the backup router must announce their clients,
+channels, channel users, channel user modes and channel modes to the
+backup router.  This is to assure that none of the important notify 
+packets were lost during the switch to the backup router.  The backup
+router must check which of these announced entities it already have
+and distribute the new ones to the primary route.
+
+The backup router too must announce its servers, clients, channels
+and other information to the new primary router.  The primary router
+of the backup router too must announce its informations to the backup
+router.  Both must process only the ones they do not know about.  If
+any of the announced modes does not match then they are enforced in
+normal manner defined later in this specification.
+
+
+.ti 0
+3.13.2 Resuming Primary Router
+
+Usually the primary router is unresponsive only a short period of time
+and it is intended that the original router of the cell will reassume
+its position as primary router when it comes back online.  The backup
+router that is now acting as primary router of the cell must constantly
+try to connect to the original primary router of the cell.  It is
+RECOMMENDED that it would try to reconnect in 30 second intervals to
+the primary router.
+
+When the connection is established to the primary router the backup
+resuming protocol is executed.  The protocol is advanced as follows:
+
+  1. Backup router sends SILC_PACKET_RESUME_ROUTER packet with type
+     value 1 the primary router that came back online.  The packet
+     will indicate the primary router has been replaced by the backup
+     router.  After sending the packet the backup router will announce
+     all of its channels, channel users, modes etc. to the primary
+     router.
+
+  2. Backup router sends SILC_PACKET_RESUME_ROUTER packet with type
+     value 2 to its current primary router to indicate that it will
+     resign as being primary router.  Then, backup router sends the
+     SILC_PACKET_RESUME_ROUTER packet with type value 1 to all
+     connected servers to also indicate that it will resign as being
+     primary router.
+
+  3. Backup router also send SILC_PACKET_RESUME_ROUTER packet with
+     type value 2 to the router that is using the backup router
+     currently as its primary router.
+
+  4. Any server and router that receives the SILC_PACKET_RESUME_ROUTER
+     with type value 1 or 2 must reconnect immediately to the
+     primary router of the cell that came back online.  After they
+     have created the connection they MUST NOT use that connection
+     as active primary route but still route all packets to the
+     backup router.  After the connection is created they MUST send
+     SILC_PACKET_RESUME_ROUTER with type value 3 back to the
+     backup router.  The session ID value found in the first packet
+     MUST be set in this packet.
+
+  5. Backup router MUST wait for all packets with type value 3 before
+     it continues with the protocol.  It knows from the session ID values
+     set in the packet when it have received all packets.  The session
+     value should be different in all packets it have send earlier.
+     After the packets is received the backup router sends the
+     SILC_PACKET_RESUME_ROUTER packet with type value 4 to the
+     primary router that came back online.  This packet will indicate 
+     that the backup router is now ready to resign as being primary
+     router.  The session ID value in this packet MUST be the same as
+     in first packet sent to the primary router.  During this time
+     the backup router should still route all packets it is receiving
+     from server connections.
+
+  6. The primary router receives the packet and send the
+     SILC_PACKET_RESUME_ROUTER with type value 5 to all connected servers
+     including the backup router.  It also sends the packet with type
+     value 6 to its primary router, and to the router that is using
+     it as its primary router.  The Session ID value in this packet
+     SHOULD be zero (0).
+
+  7. Any server and router that receives the SILC_PACKET_RESUME_ROUTER
+     with type value 5 or 6 must switch their primary route to the
+     new primary router and remove the route for the backup router, since
+     it is not anymore the primary router of the cell.  They must also
+     update their local database to understand that the clients are
+     not originated from the backup router but from the locally connected
+     servers.  After that they MUST announce their channels, channel
+     users, modes etc. to the primary router.  They must not use the
+     backup router connection after this and the connection is considered
+     to be passive connection.  The implementations SHOULD be able
+     to disable the connection without closing the actual link.
+
+After this protocol is executed the backup router is now again normal
+server in the cell that has the backup link to the primary router.  The
+primary router feeds the router specific data again to the backup router.
+All server connections in the backup router are considered passive
+connections.
+
+When the primary router of the cell comes back online and connects
+to its primary router, the remote primary router must send the 
+SILC_PACKET_RESUME_ROUTER with type value 20 indicating that the
+connection is not allowed since the router has been replaced by an
+backup router.  The session ID value in this packet SHOULD be zero (0).
+When the router receives this packet it must not use the connection
+as active connection but to understand that it cannot act as primary
+router in the cell.  It must wait that the backup router connects to
+it, and the backup resuming protocol is executed.
+
+The following type values has been defined for SILC_PACKET_RESUME_ROUTER
+packet:
+
+  1    SILC_SERVER_BACKUP_START
+  2    SILC_SERVER_BACKUP_START_GLOBAL
+  3    SILC_SERVER_BACKUP_START_CONNECTED
+  4    SILC_SERVER_BACKUP_START_ENDING
+  5    SILC_SERVER_BACKUP_START_RESUMED
+  6    SILC_SERVER_BACKUP_START_GLOBAL
+  20   SILC_SERVER_BACKUP_START_REPLACED
+
+If any other value is found in the type field the packet must be 
+discarded.  The SILC_PACKET_RESUME_ROUTER packet and its payload
+is defined in [SILC2].
+
+
+.ti 0
+3.13.3 Discussion on Backup Router Scheme
+
+It is clear that this backup router support is not able to handle all
+possible situations arrising in unreliable network environment.  This
+scheme for example does not handle situation when the router actually
+does not go offline but the network link goes down temporarily.  It would
+require some intelligence to figure out when it is best time to switch
+to the backup router.  To make it even more complicated it is possible
+that the backup router may have not lost the network link to the primary
+router.
+
+Other possible situation is when the network link is lost temporarily
+between two primary routers in the SILC network.  Unless the routers
+notice the link going down they cannot perhaps find alternative routes.
+Worst situation is when the link goes down only for a short period of
+time, thus causing lag.  Should the routers or servers find alternative
+routes if they cannot get response from the router during the lag?
+When alternative routes are being found it must be careful not to
+mess up existing primary routes between routers in the network.
+
+It is suggested that the current backup router scheme is only temporary
+solution and existing backup router protocols are studied further.  It
+is also suggested that the backup router specification will be separated
+from this SILC specification Internet-Draft and additional specification
+is written on the subject.
+
+
+.ti 0
+4 SILC Procedures
+
+This section describes various SILC procedures such as how the 
+connections are created and registered, how channels are created and
+so on.  The section describes the procedures only generally as details
+are described in [SILC2] and [SILC3].
+
+
+.ti 0
+4.1 Creating Client Connection
+
+This section describes the procedure when client connects to SILC server.
+When client connects to server the server MUST perform IP address lookup
+and reverse IP address lookup to assure that the origin host really is
+who it claims to be.  Client, host, connecting to server SHOULD have 
+both valid IP address and fully qualified domain name (FQDN).
+
+After that the client and server performs SILC Key Exchange protocol
+which will provide the key material used later in the communication.
+The key exchange protocol MUST be completed successfully before the
+connection registration may continue.  The SILC Key Exchange protocol
+is described in [SILC3].
+
+Typical server implementation would keep a list of connections that it
+allows to connect to the server.  The implementation would check, for
+example, the connecting client's IP address from the connection list
+before the SILC Key Exchange protocol has been started.  Reason for
+this is that if the host is not allowed to connect to the server there
+is no reason to perform the key exchange protocol.
+
+After successful key exchange protocol the client and server performs
+connection authentication protocol.  The purpose of the protocol is to
+authenticate the client connecting to the server.  Flexible
+implementation could also accept the client to connect to the server
+without explicit authentication.  However, if authentication is
+desired for a specific client it may be based on passphrase or
+public key authentication.  If authentication fails the connection
+MUST be terminated.  The connection authentication protocol is described
+in [SILC3].
+
+After successful key exchange and authentication protocol the client
+registers itself by sending SILC_PACKET_NEW_CLIENT packet to the
+server.  This packet includes various information about the client
+that the server uses to create the client.  Server creates the client
+and sends SILC_PACKET_NEW_ID to the client which includes the created
+Client ID that the client MUST start using after that.  After that
+all SILC packets from the client MUST have the Client ID as the
+Source ID in the SILC Packet Header, described in [SILC2].
+
+Client MUST also get the server's Server ID that is to be used as
+Destination ID in the SILC Packet Header when communicating with
+the server (for example when sending commands to the server).  The
+ID may be resolved in two ways.  Client can take the ID from an
+previously received packet from server that MUST include the ID,
+or to send SILC_COMMAND_INFO command and receive the Server ID as
+command reply.
+
+Server MAY choose not to use the information received in the
+SILC_PACKET_NEW_CLIENT packet.  For example, if public key or 
+certificate were used in the authentication, server MAY use those
+informations rather than what it received from client.  This is suitable
+way to get the true information about client if it is available.
+
+The nickname of client is initially set to the username sent in the
+SILC_PACKET_NEW_CLIENT packet.  User should set the nickname to more
+suitable by sending SILC_COMMAND_NICK command.  However, this is not
+required as part of registration process.
+
+Server MUST also distribute the information about newly registered
+client to its router (or if the server is router, to all routers in
+the SILC network).  More information about this in [SILC2].
+
+
+.ti 0
+4.2 Creating Server Connection
+
+This section describes the procedure when server connects to its
+router (or when router connects to other router, the cases are
+equivalent).  The procedure is very much alike when client connects
+to the server thus it is not repeated here.
+
+One difference is that server MUST perform connection authentication
+protocol with proper authentication.  A proper authentication is based
+on passphrase or public key authentication.
+
+After server and router has successfully performed the key exchange
+and connection authentication protocol, the server register itself
+to the router by sending SILC_PACKET_NEW_SERVER packet.  This packet
+includes the server's Server ID that it has created by itself and
+other relevant information about the server.
+
+After router has received the SILC_PACKET_NEW_SERVER packet it
+distributes the information about newly registered server to all routers
+in the SILC network.  More information about this in [SILC2].
+
+As client needed to resolve the destination ID this MUST be done by the
+server that connected to the router, as well.  The way to resolve it is
+to get the ID from previously received packet.  The server MAY also 
+use SILC_COMMAND_INFO command to resolve the ID.  Server MUST also start
+using its own Server ID as Source ID in SILC Packet Header and the
+router's Server ID as Destination when communicating with the router.
+
+
+.ti 0
+4.2.1 Announcing Clients, Channels and Servers
+
+After server or router has connected to the remote router, and it already
+has connected clients and channels it MUST announce them to the router.
+If the server is router server, also all the local servers in the cell
+MUST be announced.
+
+All clients are announced by compiling a list of ID Payloads into the
+SILC_PACKET_NEW_ID packet.  All channels are announced by compiling a
+list of Channel Payloads into the SILC_PACKET_NEW_CHANNEL packet.  Also, 
+the channel users on the channels must be announced by compiling a
+list of Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into
+the SILC_PACKET_NOTIFY packet.  The users' modes on the channel must 
+also be announced by compiling list of Notify Payloads with the 
+SILC_NOTIFY_TYPE_CUMODE_CHANGE notify type into the SILC_PACKET_NOTIFY
+packet.
+
+The router MUST also announce the local servers by compiling list of
+ID Payloads into the SILC_PACKET_NEW_ID packet.
+
+Also, clients' modes (user modes in SILC) MUST be announced.  This is
+done by compiling a list of Notify Payloads with the 
+SILC_NOTIFY_UMODE_CHANGE nofity type into the SILC_PACKET_NOTIFY packet.
+
+Also, channel's topics MUST be announced by compiling a list of Notify
+Payloads with the SILC_NOTIFY_TOPIC_SET notify type into the
+SILC_PACKET_NOTIFY packet.
+
+The router which receives these lists MUST process them and broadcast
+the packets to its primary route.
+
+When processing the announced channels and channel users the router MUST
+check whether a channel exists already with the same name.  If channel
+exists with the same name it MUST check whether the Channel ID is
+different.  If the Channel ID is different the router MUST send the notify
+type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to the server to force the channel ID
+change to the ID the router has.  If the mode of the channel is different
+the router MUST send the notify type SILC_NOTIFY_TYPE_CMODE_CHANGE to the
+server to force the mode change to the mode that the router has.
+
+The router MUST also generate new channel key and distribute it to the
+channel.  The key MUST NOT be generated if the SILC_CMODE_PRIVKEY mode
+is set.
+
+If the channel has channel founder on the router the router MUST send
+the notify type SILC_NOTIFY_TYPE_CUMODE_CHANGE to the server to force
+the mode change for the channel founder on the server.  The channel 
+founder privileges MUST be removed.
+
+The router processing the channels MUST also compile a list of
+Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into the
+SILC_PACKET_NOTIFY and send the packet to the server.  This way the
+server (or router) will receive the clients on the channel that
+the router has.
+
+
+.ti 0
+4.3 Joining to a Channel
+
+This section describes the procedure when client joins to a channel.
+Client joins to channel by sending command SILC_COMMAND_JOIN to the
+server.  If the receiver receiving join command is normal server the
+server MUST check its local list whether this channel already exists
+locally.  This would indicate that some client connected to the server
+has already joined to the channel.  If this is case the client is
+joined to the channel, new channel key is created and information about
+newly joined channel is sent to the router.  The router is informed
+by sending SILC_NOTIFY_TYPE_JOIN notify type.  The notify type MUST
+also be sent to the local clients on the channel.  The new channel key
+is also sent to the router and to local clients on the channel.
+
+If the channel does not exist in the local list the client's command
+MUST be sent to the router which will then perform the actual joining
+procedure.  When server receives the reply to the command from the
+router it MUST be sent to the client which sent the command originally.
+Server will also receive the channel key from the server that it MUST
+send to the client which originally requested the join command.  The
+server MUST also save the channel key.
+
+If the receiver of the join command is router it MUST first check its
+local list whether anyone in the cell has already joined to the channel.
+If this is the case the client is joined to the channel and reply is
+sent to the client.  If the command was sent by server the command reply
+is sent to the server which sent it.  Then the router MUST also create
+new channel key and distribute it to all clients on the channel and
+all servers that has clients on the channel.  Router MUST also send
+the SILC_NOTIFY_TYPE_JOIN notify type to local clients on the channel
+and to local servers that has clients on the channel.
+
+If the channel does not exist on the router's local list it MUST
+check the global list whether the channel exists at all.  If it does
+the client is joined to the channel as described previously.  If
+the channel does not exist the channel is created and the client
+is joined to the channel.  The channel key is also created and
+distributed as previously described.  The client joining to the created
+channel is made automatically channel founder and both channel founder
+and channel operator privileges is set for the client.
+
+If the router created the channel in the process, information about the
+new channel MUST be broadcasted to all routers.  This is done by 
+broadcasting SILC_PACKET_NEW_CHANNEL packet to the router's primary
+route.  When the router joins the client to the channel it MUST also
+send information about newly joined client to all routers in the SILC
+network.  This is done by broadcasting the SILC_NOTIFY_TYPE_JOIN notify
+type to the router's primary route. 
+
+It is important to note that new channel key is created always when
+new client joins to channel, whether the channel has existed previously
+or not.  This way the new client on the channel is not able to decrypt
+any of the old traffic on the channel.  Client which receives the reply to
+the join command MUST start using the received Channel ID in the channel
+message communication thereafter.  Client also receives the key for the
+channel in the command reply.  Note that the channel key is never
+generated if the SILC_CMODE_PRIVKEY mode is set.
+
+
+.ti 0
+4.4 Channel Key Generation
+
+Channel keys are created by router which creates the channel by taking
+enough randomness from cryptographically strong random number generator.
+The key is generated always when channel is created, when new client
+joins a channel and after the key has expired.  Key could expire for
+example in an hour.
+
+The key MUST also be re-generated whenever some client leaves a channel.
+In this case the key is created from scratch by taking enough randomness
+from the random number generator.  After that the key is distributed to
+all clients on the channel.  However, channel keys are cell specific thus
+the key is created only on the cell where the client, which left the
+channel, exists.  While the server or router is creating the new channel
+key, no other client may join to the channel.  Messages that are sent
+while creating the new key are still processed with the old key.  After
+server has sent the SILC_PACKET_CHANNEL_KEY packet MUST client start
+using the new key.  If server creates the new key the server MUST also
+send the new key to its router.  See [SILC2] on more information about
+how channel messages must be encrypted and decrypted when router is
+processing them.
+
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it MUST process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages.  The processing is as follows:
+
+  channel_key  = raw key data
+  HMAC key     = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+Note that the server MUST also save the channel key.
+
+
+.ti 0
+4.5 Private Message Sending and Reception
+
+Private messages are sent point to point.  Client explicitly destines
+a private message to specific client that is delivered to only to that
+client.  No other client may receive the private message.  The receiver
+of the private message is destined in the SILC Packet Header as any
+other packet as well.
+
+If the sender of a private message does not know the receiver's Client
+ID, it MUST resolve it from server.  There are two ways to resolve the
+client ID from server; it is RECOMMENDED that client implementations
+send SILC_COMMAND_IDENTIFY command to receive the Client ID.  Client
+MAY also send SILC_COMMAND_WHOIS command to receive the Client ID.
+If the sender has received earlier a private message from the receiver
+it should have cached the Client ID from the SILC Packet Header.
+
+See [SILC2] for description of private message encryption and decryption
+process.
+
+
+.ti 0
+4.6 Private Message Key Generation
+
+Private message MAY be protected by the key generated by the client.
+The key may be generated and sent to the other client by sending packet
+SILC_PACKET_PRIVATE_MESSAGE_KEY which travels through the network
+and is secured by session keys.  After that the private message key
+is used in the private message communication between those clients.
+
+Other choice is to entirely use keys that are not sent through
+the SILC network at all.  This significantly adds security.  This key
+would be pre-shared-key that is known by both of the clients.  Both
+agree about using the key and starts sending packets that indicate
+that the private message is secured using private message key.
+
+The key material used as private message key is implementation issue.
+However, SILC_PACKET_KEY_AGREEMENT packet MAY be used to negotiate
+the key material.  If the key is normal pre-shared-key or randomly
+generated key, and the SILC_PACKET_KEY_AGREEMENT was not used, then
+the key material SHOULD be processed as defined in the [SILC3].  In
+the processing, however, the HASH, as defined in [SILC3] MUST be 
+ignored.  After processing the key material it is employed as defined
+in [SILC3], however, the HMAC key material MUST be discarded.
+
+If the key is pre-shared-key or randomly generated the implementations
+should use the SILC protocol's mandatory cipher as the cipher.  If the
+SKE was used to negotiate key material the cipher was negotiated as well.
+
+.ti 0
+4.7 Channel Message Sending and Reception
+
+Channel messages are delivered to group of users.  The group forms a
+channel and all clients on the channel receives messages sent to the
+channel.
+
+Channel messages are destined to channel by specifying the Channel ID
+as Destination ID in the SILC Packet Header.  The server MUST then
+distribute the message to all clients on the channel by sending the
+channel message destined explicitly to a client on the channel.
+
+See the [SILC2] for description of channel messege routing for router
+servers.
+
+See [SILC2] for description of channel message encryption and decryption
+process.
+
+
+.ti 0
+4.8 Session Key Regeneration
+
+Session keys MUST be regenerated periodically, say, once in an hour.
+The re-key process is started by sending SILC_PACKET_REKEY packet to
+other end, to indicate that re-key must be performed.  The initiator
+of the connection SHOULD initiate the re-key.
+
+If perfect forward secrecy (PFS) flag was selected in the SILC Key
+Exchange protocol [SILC3] the re-key MUST cause new key exchange with
+SKE protocol.  In this case the protocol is secured with the old key
+and the protocol results to new key material.  See [SILC3] for more
+information.  After the SILC_PACKET_REKEY packet is sent the sender
+will perform the SKE protocol.
+
+If PFS flag was set the resulted key material is processed as described
+in the section Processing the Key Material in [SILC3].  The difference
+with re-key in the processing is that the initial data for the hash 
+function is just the resulted key material and not the HASH as it
+is not computed at all with re-key.  Other than that, the key processing
+it equivalent to normal SKE negotiation.
+
+If PFS flag was not set, which is the default case, then re-key is done
+without executing SKE protocol.  In this case, the new key is created by
+providing the current sending encryption key to the SKE protocol's key
+processing function.  The process is described in the section Processing
+the Key Material in [SILC3].  The difference in the processing is that
+the initial data for the hash function is the current sending encryption
+key and not the SKE's KEY and HASH values.  Other than that, the key
+processing is equivalent to normal SKE negotiation.
+
+After both parties has regenerated the session key, both MUST send
+SILC_PACKET_REKEY_DONE packet to each other.  These packets are still
+secured with the old key.  After these packets, the subsequent packets
+MUST be protected with the new key.
+
+
+.ti 0
+4.9 Command Sending and Reception
+
+Client usually sends the commands in the SILC network.  In this case
+the client simply sends the command packet to server and the server
+processes it and replies with command reply packet.
+
+However, if the server is not able to process the command, it is sent 
+to the server's router.  This is case for example with commands such
+as, SILC_COMMAND_JOIN and SILC_COMMAND_WHOIS commands.  However, there
+are other commands as well.  For example, if client sends the WHOIS
+command requesting specific information about some client the server must
+send the WHOIS command to router so that all clients in SILC network
+are searched.  The router, on the other hand, sends the WHOIS command
+further to receive the exact information about the requested client.
+The WHOIS command travels all the way to the server which owns the client
+and it replies with command reply packet.  Finally, the server which
+sent the command receives the command reply and it must be able to
+determine which client sent the original command.  The server then
+sends command reply to the client.  Implementations should have some
+kind of cache to handle, for example, WHOIS information.  Servers
+and routers along the route could all cache the information for faster
+referencing in the future.
+
+The commands sent by server may be sent hop by hop until someone is able
+to process the command.  However, it is preferred to destine the command
+as precisely as it is possible.  In this case, other routers en route
+MUST route the command packet by checking the true sender and true
+destination of the packet.  However, servers and routers MUST NOT route
+command reply packets to clients coming from other server.  Client
+MUST NOT accept command reply packet originated from anyone else but
+from its own server.
+
+
+.ti 0
+4.10 Closing Connection
+
+When remote client connection is closed the server MUST send the notify
+type SILC_NOTIFY_TYPE_SIGNOFF to its primary router and to all channels
+the client was joined.  The server MUST also save the client's information
+for a period of time for history purposes.
+
+When remote server or router connection is closed the server or router
+MUST also remove all the clients that was behind the server or router
+from the SILC Network.  The server or router MUST also send the notify
+type SILC_NOTIFY_TYPE_SERVER_SIGNOFF to its primary router and to all
+local clients that are joined on the same channels with the remote 
+server's or router's clients.
+
+
+.ti 0
+5 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+Special attention must also be paid on the servers and routers that are
+running the SILC service.  The SILC protocol's security depends greatly
+on the security and the integrity of the servers and administrators that
+are running the service.  It is recommended that some form of registration
+is required by the server and router administrator prior acceptance to
+the SILC Network.  Even though, the SILC protocol is secure in a network
+of mutual distrust between clients, servers, routers and adminstrators
+of the servers, the client should be able to trust the servers they are
+using if they whish to do so.
+
+It however must be noted that if the client requires absolute security
+by not trusting any of the servers or routers in the SILC Network, it can
+be accomplished by negotiating private keys outside the SILC Network,
+either using SKE or some other key exchange protocol, or to use some
+other external means for distributing the keys.  This applies for all 
+messages, private messages and channel messages.
+
+It is important to note that SILC, like any other security protocol is
+not full proof system and cannot secure from insecure environment; the
+SILC servers and routers could very well be compromised.  However, to
+provide acceptable level of security and usability for end user the
+protocol use many times session keys or other keys generated by the
+servers to secure the messages.  This is intentional design feature to
+allow ease of use for end user.  This way the network is still usable,
+and remains encrypted even if the external means of distributing the
+keys is not working.  The implementation, however, may like to not
+follow this design feature, and always negotiate the keys outside SILC
+network.  This is acceptable solution and many times recommended.  The
+implementation still must be able to work with the server generated keys.
+
+If this is unacceptable for the client or end user, the private keys
+negotiatied outside the SILC Network should always be used.  In the end
+it is always implementor's choice whether to negotiate private keys by
+default or whether to use the keys generated by the servers.
+
+It is also recommended that router operators in the SILC Network would
+form a joint forum to discuss the router and SILC Network management
+issues.  Also, router operators along with the cell's server operators
+should have a forum to discuss the cell management issues.
+
+
+.ti 0
+6 References
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+
+.ti 0
+7 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires 13 Nay 2002
diff --git a/doc/draft-riikonen-silc-spec-05.nroff b/doc/draft-riikonen-silc-spec-05.nroff
new file mode 100644 (file)
index 0000000..c80d650
--- /dev/null
@@ -0,0 +1,2237 @@
+.pl 10.0i
+.po 0
+.ll 7.2i
+.lt 7.2i
+.nr LL 7.2i
+.nr LT 7.2i
+.ds LF Riikonen
+.ds RF FORMFEED[Page %]
+.ds CF
+.ds LH Internet Draft
+.ds RH XXX
+.ds CH
+.na
+.hy 0
+.in 0
+.nf
+Network Working Group                                        P. Riikonen
+Internet-Draft
+draft-riikonen-silc-spec-05.txt                        XXX
+Expires: XXX
+
+.in 3
+
+.ce 3
+Secure Internet Live Conferencing (SILC),
+Protocol Specification
+<draft-riikonen-silc-spec-05.txt>
+
+.ti 0
+Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with   
+all provisions of Section 10 of RFC 2026.  Internet-Drafts are   
+working documents of the Internet Engineering Task Force (IETF), its   
+areas, and its working groups.  Note that other groups may also   
+distribute working documents as Internet-Drafts.   
+
+Internet-Drafts are draft documents valid for a maximum of six months   
+and may be updated, replaced, or obsoleted by other documents at any   
+time.  It is inappropriate to use Internet-Drafts as reference   
+material or to cite them other than as "work in progress."   
+
+The list of current Internet-Drafts can be accessed at   
+http://www.ietf.org/ietf/1id-abstracts.txt   
+
+The list of Internet-Draft Shadow Directories can be accessed at   
+http://www.ietf.org/shadow.html   
+
+The distribution of this memo is unlimited.  
+
+
+.ti 0
+Abstract
+
+This memo describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside the SILC network.
+Three other Internet Drafts relates very closely to this memo;
+SILC Packet Protocol [SILC2], SILC Key Exchange and Authentication
+Protocols [SILC3] and SILC Commands [SILC4].
+
+
+
+
+
+
+.ti 0
+Table of Contents
+
+.nf
+1 Introduction ..................................................  3
+  1.1 Requirements Terminology ..................................  4
+2 SILC Concepts .................................................  4
+  2.1 SILC Network Topology .....................................  4
+  2.2 Communication Inside a Cell ...............................  5
+  2.3 Communication in the Network ..............................  6
+  2.4 Channel Communication .....................................  7
+  2.5 Router Connections ........................................  7
+3 SILC Specification ............................................  8
+  3.1 Client ....................................................  8
+      3.1.1 Client ID ...........................................  9
+  3.2 Server .................................................... 10
+      3.2.1 Server's Local ID List .............................. 10
+      3.2.2 Server ID ........................................... 11
+      3.2.3 SILC Server Ports ................................... 12
+  3.3 Router .................................................... 12
+      3.3.1 Router's Local ID List .............................. 12
+      3.3.2 Router's Global ID List ............................. 13
+      3.3.3 Router's Server ID .................................. 14
+  3.4 Channels .................................................. 14
+      3.4.1 Channel ID .......................................... 16
+  3.5 Operators ................................................. 16
+  3.6 SILC Commands ............................................. 16
+  3.7 SILC Packets .............................................. 17
+  3.8 Packet Encryption ......................................... 17
+      3.8.1 Determination of the Source and the Destination ..... 17
+      3.8.2 Client To Client .................................... 18
+      3.8.3 Client To Channel ................................... 19
+      3.8.4 Server To Server .................................... 20
+  3.9 Key Exchange And Authentication ........................... 20
+      3.9.1 Authentication Payload .............................. 20
+  3.10 Algorithms ............................................... 22
+      3.10.1 Ciphers ............................................ 22
+      3.10.2 Public Key Algorithms .............................. 23
+      3.10.3 Hash Functions ..................................... 24
+      3.10.4 MAC Algorithms ..................................... 24
+      3.10.5 Compression Algorithms ............................. 25
+  3.11 SILC Public Key .......................................... 25
+  3.12 SILC Version Detection ................................... 27
+  3.13 Backup Routers ........................................... 28
+      3.13.1 Switching to Backup Router ......................... 29
+      3.13.2 Resuming Primary Router ............................ 30
+      3.13.3 Discussion on Backup Router Scheme ................. 32
+4 SILC Procedures ............................................... 33
+  4.1 Creating Client Connection ................................ 33
+  4.2 Creating Server Connection ................................ 34
+      4.2.1 Announcing Clients, Channels and Servers ............ 35
+  4.3 Joining to a Channel ...................................... 36
+  4.4 Channel Key Generation .................................... 37
+  4.5 Private Message Sending and Reception ..................... 38
+  4.6 Private Message Key Generation ............................ 38
+  4.7 Channel Message Sending and Reception ..................... 39
+  4.8 Session Key Regeneration .................................. 39
+  4.9 Command Sending and Reception ............................. 40
+  4.10 Closing Connection ....................................... 41
+5 Security Considerations ....................................... 41
+6 References .................................................... 42
+7 Author's Address .............................................. 44
+
+
+
+.ti 0
+List of Figures
+
+.nf
+Figure 1:  SILC Network Topology
+Figure 2:  Communication Inside cell
+Figure 3:  Communication Between Cells
+Figure 4:  Router Connections
+Figure 5:  SILC Public Key
+
+
+.ti 0
+1. Introduction
+
+This document describes a Secure Internet Live Conferencing (SILC)
+protocol which provides secure conferencing services over insecure
+network channel.  SILC is IRC [IRC] like protocol, however, it is 
+not equivalent to IRC and does not support IRC.
+
+Strong cryptographic methods are used to protect SILC packets inside
+the SILC network.  Three other Internet Drafts relates very closely
+to this memo; SILC Packet Protocol [SILC2], SILC Key Exchange and
+Authentication Protocols [SILC3] and SILC Commands [SILC4].
+
+The protocol uses extensively packets as conferencing protocol 
+requires message and command sending.  The SILC Packet Protocol is
+described in [SILC2] and should be read to fully comprehend this
+document and protocol.  [SILC2] also describes the packet encryption
+and decryption in detail.
+
+The security of SILC protocol, and for any security protocol for that
+matter, is based on strong and secure key exchange protocol.  The SILC
+Key Exchange protocol is described in [SILC3] along with connection
+authentication protocol and should be read to fully comprehend this
+document and protocol.
+
+The SILC protocol has been developed to work on TCP/IP network
+protocol, although it could be made to work on other network protocols
+with only minor changes.  However, it is recommended that TCP/IP
+protocol is used under SILC protocol.  Typical implementation would
+be made in client-server model.
+
+
+.ti 0
+1.1 Requirements Terminology
+
+The keywords MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, 
+MAY, and OPTIONAL, when they appear in this document, are to be
+interpreted as described in [RFC2119].
+
+
+.ti 0
+2. SILC Concepts
+
+This section describes various SILC protocol concepts that forms the 
+actual protocol, and in the end, the actual SILC network.  The mission
+of the protocol is to deliver messages from clients to other clients 
+through routers and servers in secure manner.  The messages may also 
+be delivered from one client to many clients forming a group, also 
+known as a channel.
+
+This section does not focus to security issues.  Instead, basic network 
+concepts are introduced to make the topology of the SILC network 
+clear.
+
+
+.ti 0
+2.1 SILC Network Topology
+
+SILC network is a cellular network as opposed to tree style network 
+topology.  The rationale for this is to have servers that can perform 
+specific kind of tasks what other servers cannot perform.  This leads 
+to two kinds of servers; normal SILC servers and SILC routers.
+
+A difference between normal server and router server is that routers 
+knows everything about everything in the network.  They also do the 
+actual routing of the messages to the correct receiver.  Normal servers 
+knows only about local information and nothing about global information.
+This makes the network faster as there are less servers that needs to 
+keep global information up to date at all time.
+
+This, on the other hand, leads to cellular like network, where routers 
+are in the center of the cell and servers are connected to the router.
+
+
+
+
+
+
+
+The following diagram represents SILC network topology.
+
+.in 8
+.nf
+  ---- ---- ----         ---- ---- ----
+ | S8 | S5 | S4 |       | S7 | S5 | S6 |
+ ----- ---- -----       ----- ---- -----
+| S7 | S/R1 | S2 | --- | S8 | S/R2 | S4 |
+ ---- ------ ----       ---- ------ ----
+ | S6 | S3 | S1 |       | S1 | S3 | S2 |         ---- ----
+  ---- ---- ----         ---- ---- ----         | S3 | S1 |
+     Cell 1.   \\             Cell 2.  | \\____  ----- -----
+                |                     |        | S4 | S/R4 |
+    ---- ---- ----         ---- ---- ----       ---- ------
+   | S7 | S4 | S2 |       | S1 | S3 | S2 |      | S2 | S5 |
+   ----- ---- -----       ----- ---- -----       ---- ----
+  | S6 | S/R3 | S1 | --- | S4 | S/R5 | S5 | ____/ Cell 4.
+   ---- ------ ----       ---- ------ ----
+   | S8 | S5 | S3 |       | S6 | S7 | S8 |     ... etc ...
+    ---- ---- ----         ---- ---- ----
+       Cell 3.                Cell 5.
+.in 3
+
+.ce
+Figure 1:  SILC Network Topology
+
+
+A cell is formed when a server or servers connect to one router.  In
+SILC network normal server cannot directly connect to other normal
+server.  Normal server may only connect to SILC router which then
+routes the messages to the other servers in the cell.  Router servers
+on the other hand may connect to other routers to form the actual SILC 
+network, as seen in above figure.  However, router is also normal SILC 
+server; clients may connect to it the same way as to normal SILC 
+server.  Normal server also cannot have active connections to more 
+than one router.  Normal server cannot be connected to two different 
+cells.  Router servers, on the other hand, may have as many router to 
+router connections as needed.
+
+There are many issues in this network topology that needs to be careful
+about.  Issues like the size of the cells, the number of the routers in 
+the SILC network and the capacity requirements of the routers.  These
+issues should be discussed in the Internet Community and additional
+documents on the issue may be written.
+
+
+.ti 0
+2.2 Communication Inside a Cell
+
+It is always guaranteed that inside a cell message is delivered to the 
+recipient with at most two server hops.  A client which is connected to
+server in the cell and is talking on channel to other client connected 
+to other server in the same cell, will have its messages delivered from 
+its local server first to the router of the cell, and from the router 
+to the other server in the cell.
+
+The following diagram represents this scenario:
+
+
+.in 25
+.nf
+1 --- S1     S4 --- 5
+         S/R
+ 2 -- S2     S3
+     /        |
+    4         3
+.in 3
+
+
+.ce
+Figure 2:  Communication Inside cell
+
+
+Example:  Client 1. connected to Server 1. send message to
+          Client 4. connected to Server 2. travels from Server 1.
+          first to Router which routes the message to Server 2.
+          which then sends it to the Client 4.  All the other
+          servers in the cell will not see the routed message.
+
+
+If the client is connected directly to the router, as router is also normal
+SILC server, the messages inside the cell are always delivered only with 
+one server hop.  If clients communicating with each other are connected 
+to the same server, no router interaction is needed.  This is the optimal
+situation of message delivery in the SILC network.
+
+
+.ti 0
+2.3 Communication in the Network
+
+If the message is destined to server that does not belong to local cell 
+the message is routed to the router server to which the destination 
+server belongs, if the local router is connected to destination router.
+If there is no direct connection to the destination router, the local
+router routes the message to its primary route.  The following diagram
+represents message sending between cells.
+
+
+.in 16
+.nf
+1 --- S1     S4 --- 5            S2 --- 1
+         S/R - - - - - - - - S/R
+ 2 -- S2     S3           S1
+     /        |             \\
+    4         3              2
+
+   Cell 1.               Cell 2.
+.in 3
+
+
+.ce
+Figure 3:  Communication Between Cells
+
+
+Example:  Client 5. connected to Server 4. in Cell 1. sends message
+          to Client 2. connected to Server 1. in Cell 2. travels
+          from Server 4. to Router which routes the message to
+          Router in Cell 2, which then routes the message to 
+          Server 1.  All the other servers and routers in the
+          network will not see the routed message.
+
+
+The optimal case of message delivery from the client point of view is
+when clients are connected directly to the routers and the messages
+are delivered from one router to the other.
+
+
+.ti 0 
+2.4 Channel Communication
+
+Messages may be sent to group of clients as well.  Sending messages to
+many clients works the same way as sending messages point to point, from
+message delivery point of view.  Security issues are another matter
+which are not discussed in this section.
+
+Router server handles the message routing to multiple recipients.  If 
+any recipient is not in the same cell as the sender the messages are 
+routed further.
+
+Server distributes the channel message to its local clients which are 
+joined to the channel.  Router also distributes the message to its 
+local clients on the channel.
+
+
+.ti 0
+2.5 Router Connections
+
+Router connections play very important role in making the SILC like
+network topology to work.  For example, sending broadcast packets in
+SILC network require special connections between routers; routers must
+be connected in a specific way.
+
+Every router has their primary route which is a connection to another
+router in the network.  Unless there is only two routers in the network
+must not routers use each other as their primary routes.  The router
+connections in the network must form a ring.
+
+
+
+
+
+
+
+Example with three routers in the network:
+
+
+.in 16
+.nf
+    S/R1 - > - > - > - > - > - > - S/R2
+     \\                               /
+      ^                             v
+       \\ - < -  < - S/R3 - < - < - /
+.in 3
+
+
+.ce
+Figure 4:  Router Connections
+
+
+Example:  Network with three routers.  Router 1. uses Router 2. as its
+          primary router.  Router 2. uses Router 3. as its primary router,
+          and Router 3. uses Router 1. as its primary router.  There may
+          be other direct connections between the routers but they must
+          not be used as primary routes.
+
+The above example is applicable to any amount of routers in the network
+except for two routers.  If there are only two routers in the network both
+routers must be able to handle situation where they use each other as their
+primary routes.
+
+The issue of router connections are very important especially with SILC
+broadcast packets.  Usually all router wide information in the network is
+distributed by SILC broadcast packets.  This sort of ring network, with
+ability to have other direct routes in the network cause interesting
+routing problems.  The [SILC2] discusses the routing of packets in this
+sort of network in more detail.
+
+
+.ti 0
+3. SILC Specification
+
+This section describes the SILC protocol.  However, [SILC2] and
+[SILC3] describes other important protocols that are part of this SILC
+specification and must be read.
+
+
+.ti 0
+3.1 Client
+
+A client is a piece of software connecting to SILC server.  SILC client 
+cannot be SILC server.  Purpose of clients is to provide the user 
+interface of the SILC services for end user.  Clients are distinguished
+from other clients by unique Client ID.  Client ID is a 128 bit ID that
+is used in the communication in the SILC network.  The client ID is 
+based on the nickname selected by the user.  User uses logical nicknames
+in communication which are then mapped to the corresponding Client ID.
+Client ID's are low level identifications and must not be seen by the
+end user.
+
+Clients provide other information about the end user as well. Information
+such as the nickname of the user, username and the host name of the end 
+user and user's real name.  See section 3.2 Server for information of 
+the requirements of keeping this information.
+
+The nickname selected by the user is not unique in the SILC network.
+There can be 2^8 same nicknames for one IP address.  As for comparison
+to IRC [IRC] where nicknames are unique this is a fundamental difference
+between SILC and IRC.  This causes the server names or client's host names
+to be used along with the nicknames to identify specific users when sending
+messages.  This feature of SILC makes IRC style nickname-wars obsolete as
+no one owns their nickname; there can always be someone else with the same
+nickname.  The maximum length of nickname is 128 characters.
+
+
+.ti 0
+3.1.1 Client ID
+
+Client ID is used to identify users in the SILC network.  The Client ID
+is unique to the extent that there can be 2^128 different Client ID's,
+and ID's based on IPv6 addresses extends this to 2^224 different Client
+ID's.  Collisions are not expected to happen.  The Client ID is defined
+as follows.
+
+
+
+.in 6
+128 bit Client ID based on IPv4 addresses:
+
+32 bit  Server ID IP address (bits 1-32)
+ 8 bit  Random number or counter
+88 bit  Truncated MD5 hash value of the nickname
+
+224 bit Client ID based on IPv6 addresses:
+
+128 bit  Server ID IP address (bits 1-128)
+  8 bit  Random number or counter
+ 88 bit  Truncated MD5 hash value of the nickname
+
+o Server ID IP address - Indicates the server where this
+  client is coming from.  The IP address hence equals the
+  server IP address where to the client has connected.
+
+o Random number or counter - Random number to further 
+  randomize the Client ID.  Another choice is to use
+  a counter starting from the zero (0).  This makes it
+  possible to have 2^8 same nicknames from the same
+  server IP address.
+
+o MD5 hash - MD5 hash value of the nickname is truncated
+  taking 88 bits from the start of the hash value.  This
+  hash value is used to search the user's Client ID from
+  the ID lists.
+
+.in 3
+Collisions could occur when more than 2^8 clients using same nickname
+from the same server IP address is connected to the SILC network.  
+Server MUST be able to handle this situation by refusing to accept 
+anymore of that nickname.
+
+Another possible collision may happen with the truncated hash value of
+the nickname.  It could be possible to have same truncated hash value for
+two different nicknames.  However, this is not expected to happen nor
+cause any problems if it would occur.  Nicknames are usually logical and
+it is unlikely to have two distinct logical nicknames produce same
+truncated hash value.
+
+
+.ti 0
+3.2 Server
+
+Servers are the most important parts of the SILC network.  They form the
+basis of the SILC, providing a point to which clients may connect to.
+There are two kinds of servers in SILC; normal servers and router servers.
+This section focus on the normal server and router server is described
+in the section 3.3 Router.
+
+Normal servers MUST NOT directly connect to other normal server.  Normal
+servers may only directly connect to router server.  If the message sent
+by the client is destined outside the local server it is always sent to
+the router server for further routing.  Server may only have one active
+connection to router on same port.  Normal server MUST NOT connect to other
+cell's router except in situations where its cell's router is unavailable.
+
+
+.ti 0
+3.2.1 Server's Local ID List
+
+Normal server keeps various information about the clients and their end
+users connected to it.  Every normal server MUST keep list of all locally
+connected clients, Client ID's, nicknames, usernames and host names and
+user's real name.  Normal servers only keeps local information and it
+does not keep any global information.  Hence, normal servers knows only
+about their locally connected clients.  This makes servers efficient as
+they don't have to worry about global clients.  Server is also responsible
+of creating the Client ID's for their clients.
+
+Normal server also keeps information about locally created channels and
+their Channel ID's.
+
+
+Hence, local list for normal server includes:
+
+.in 6
+server list        - Router connection
+   o Server name
+   o Server IP address
+   o Server ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+client list        - All clients in server
+   o Nickname
+   o Username@host
+   o Real name
+   o Client ID
+   o Sending key
+   o Receiving key
+   o Public key
+
+
+channel list       - All channels in server
+   o Channel name
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+.ti 0
+3.2.2 Server ID
+
+Servers are distinguished from other servers by unique 64 bit Server ID 
+(for IPv4) or 160 bit Server ID (for IPv6).  The Server ID is used in
+the SILC to route messages to correct servers.  Server ID's also provide
+information for Client ID's, see section 3.1.1 Client ID.  Server ID is
+defined as follows.
+
+.in 6
+64 bit Server ID based on IPv4 addresses:
+
+32 bit  IP address of the server
+16 bit  Port
+16 bit  Random number
+
+160 bit Server ID based on IPv6 addresses:
+
+128 bit  IP address of the server
+ 16 bit  Port
+ 16 bit  Random number
+
+o IP address of the server - This is the real IP address of
+  the server.
+
+o Port - This is the port the server is bound to.
+
+o Random number - This is used to further randomize the Server ID.
+
+.in 3
+Collisions are not expected to happen in any conditions.  The Server ID
+is always created by the server itself and server is responsible of
+distributing it to the router.
+
+
+.ti 0
+3.2.3 SILC Server Ports
+
+The following ports has been assigned by IANA for the SILC protocol:
+
+.in 10
+silc            706/tcp    SILC
+silc            706/udp    SILC
+.in 3
+
+
+If there are needs to create new SILC networks in the future the port
+numbers must be officially assigned by the IANA.
+
+Server on network above privileged ports (>1023) SHOULD NOT be trusted
+as they could have been set up by untrusted party.
+
+
+.ti 0
+3.3 Router
+
+Router server in SILC network is responsible for keeping the cell together
+and routing messages to other servers and to other routers.  Router server
+is also a normal server thus clients may connect to it as it would be
+just normal SILC server.
+
+However, router servers has a lot of important tasks that normal servers
+do not have.  Router server knows everything about everything in the SILC.
+They know all clients currently on SILC, all servers and routers and all
+channels in SILC.  Routers are the only servers in SILC that care about
+global information and keeping them up to date at all time.  And, this
+is what they must do.
+
+
+.ti 0
+3.3.1 Router's Local ID List
+
+Router server as well MUST keep local list of connected clients and
+locally created channels.  However, this list is extended to include all
+the informations of the entire cell, not just the server itself as for
+normal servers.
+
+However, on router this list is a lot smaller since routers do not need
+to keep information about user's nickname, username and host name and real
+name since these are not needed by the router.  The router keeps only
+information that it needs.
+
+
+Hence, local list for router includes:
+
+.in 6
+server list        - All servers in the cell
+   o Server name
+   o Server ID
+   o Router's Server ID
+   o Sending key
+   o Receiving key
+
+client list        - All clients in the cell
+   o Client ID
+
+
+channel list       - All channels in the cell
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+   o Channel key
+.in 3
+
+
+Note that locally connected clients and other information include all the
+same information as defined in section section 3.2.1 Server's Local ID
+List.
+
+
+.ti 0
+3.3.2 Router's Global ID List
+
+Router server MUST also keep global list.  Normal servers do not have
+global list as they know only about local information.  Global list
+includes all the clients on SILC, their Client ID's, all created channels
+and their Channel ID's and all servers and routers on SILC and their
+Server ID's.  That is said, global list is for global information and the
+list must not include the local information already on the router's local
+list.
+
+Note that the global list does not include information like nicknames,
+usernames and host names or user's real names.  Router does not need to
+keep these informations as they are not needed by the router.  This 
+information is available from the client's server which maybe queried
+when needed.
+
+Hence, global list includes:
+
+.in 6
+server list        - All servers in SILC
+   o Server name
+   o Server ID
+   o Router's Server ID
+
+client list        - All clients in SILC
+   o Client ID
+
+channel list       - All channels in SILC
+   o Channel ID
+   o Client ID's on channel
+   o Client ID modes on channel
+.in 3
+
+
+
+.ti 0
+3.3.3 Router's Server ID
+
+Router's Server ID's are equivalent to normal Server ID's.  As routers
+are normal servers as well same types of ID's applies for routers as well.
+Thus, see section 3.2.2 Server ID.
+
+
+.ti 0
+3.4 Channels
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client requests JOIN command to the channel, and the channel ceases to
+exist when the last client has left it.  When channel exists, any client
+can reference it using the name of the channel.
+
+Channel names are unique although the real uniqueness comes from 64 bit
+Channel ID.  However, channel names are still unique and no two global
+channels with same name may exist.  The Channel name is a string of
+maximum length of 256 characters.  Channel names MUST NOT contain any
+spaces (`  '), any non-printable ASCII characters, commas (`,') and
+wildcard characters.
+
+Channels can have operators that can administrate the channel and
+operate all of its modes.  The following operators on channel exist on
+the SILC network.
+
+.in 6
+o Channel founder - When channel is created the joining client becomes
+  channel founder.  Channel founder is channel operator with some more
+  privileges.  Basically, channel founder can fully operate the channel
+  and all of its modes.  The privileges are limited only to the
+  particular channel.  There can be only one channel founder per
+  channel. Channel founder supersedes channel operator's privileges.
+
+  Channel founder privileges cannot be removed by any other operator on
+  channel.  When channel founder leaves the channel there is no channel
+  founder on the channel.  However, it is possible to set a mode for
+  the channel which allows the original channel founder to regain the
+  founder privileges even after leaving the channel.  Channel founder
+  also cannot be removed by force from the channel.
+
+o Channel operator - When client joins to channel that has not existed
+  previously it will become automatically channel operator (and channel
+  founder discussed above).  Channel operator is able administrate the
+  channel, set some modes on channel, remove a badly behaving client
+  from the channel and promote other clients to become channel
+  operator.  The privileges are limited only to the particular channel.
+
+  Normal channel user may be promoted (opped) to channel operator
+  gaining channel operator privileges.  Channel founder or other
+  channel operator may also demote (deop) channel operator to normal
+  channel user.
+.in 3
+
+
+.ti 0
+3.4.1 Channel ID
+
+Channels are distinguished from other channels by unique Channel ID.
+The Channel ID is a 64 bit ID (for IPv4) or 160 bit ID (for IPv6), and
+collisions are not expected to happen in any conditions.  Channel names
+are just for logical use of channels.  The Channel ID is created by the
+server where the channel is created.  The Channel ID is defined as
+follows.
+
+.in 6
+64 bit Channel ID based on IPv4 addresses:
+
+32 bit  Router's Server ID IP address (bits 1-32)
+16 bit  Router's Server ID port (bits 33-48)
+16 bit  Random number
+
+160 bit Channel ID based on IPv6 addresses:
+
+128 bit  Router's Server ID IP address (bits 1-128)
+ 16 bit  Router's Server ID port (bits 129-144)
+ 16 bit  Random number
+
+o Router's Server ID IP address - Indicates the IP address of 
+  the router of the cell where this channel is created.  This is 
+  taken from the router's Server ID.  This way SILC router knows 
+  where this channel resides in the SILC network.
+
+o Router's Server ID port - Indicates the port of the channel on 
+  the server.  This is taken from the router's Server ID.
+
+o Random number - To further randomize the Channel ID.  This makes
+  sure that there are no collisions.  This also means that
+  in a cell there can be 2^16 channels.
+.in 3
+
+
+.ti 0
+3.5 Operators
+
+Operators are normal users with extra privileges to their server or
+router.  Usually these people are SILC server and router administrators
+that take care of their own server and clients on them.  The purpose of
+operators is to administrate the SILC server or router.  However, even
+an operator with highest privileges is not able to enter invite-only
+channel, to gain access to the contents of a encrypted and authenticated
+packets traveling in the SILC network or to gain channel operator
+privileges on public channels without being promoted.  They have the
+same privileges as everyone else except they are able to administrate
+their server or router.
+
+
+.ti 0
+3.6 SILC Commands
+
+Commands are very important part on SILC network especially for client
+which uses commands to operate on the SILC network.  Commands are used
+to set nickname, join to channel, change modes and many other things.
+
+Client usually sends the commands and server replies by sending a reply
+packet to the command.  Server MAY also send commands usually to serve
+the original client's request.  However, server MUST NOT send commands
+to client and there are some commands that server must not send.
+
+Note that the command reply is usually sent only after client has sent
+the command request but server is allowed to send command reply packet
+to client even if client has not requested the command.  Client MAY,
+choose to ignore the command reply.
+
+It is expected that some of the commands may be miss-used by clients
+resulting various problems on the server side.  Every implementation
+SHOULD assure that commands may not be executed more than once, say,
+in two (2) seconds.  However, to keep response rate up, allowing for
+example five (5) commands before limiting is allowed.  It is RECOMMENDED
+that commands such as SILC_COMMAND_NICK, SILC_COMMAND_JOIN, 
+SILC_COMMAND_LEAVE and SILC_COMMAND_KILL SHOULD be limited in all cases
+as they require heavy operations.  This should be sufficient to prevent
+the miss-use of commands.
+
+SILC commands are described in [SILC4].
+
+
+.ti 0
+3.7 SILC Packets
+
+Packets are naturally the most important part of the protocol and the
+packets are what actually makes the protocol.  Packets in SILC network
+are always encrypted using, usually the shared secret session key
+or some other key, for example, channel key, when encrypting channel
+messages.  It is not possible to send packet in SILC network without
+encryption.  The SILC Packet Protocol is a wide protocol and is described
+in [SILC2].  This document does not define or describe details of
+SILC packets.
+
+
+.ti 0
+3.8 Packet Encryption
+
+All packets passed in SILC network MUST be encrypted.  This section
+defines how packets must be encrypted in the SILC network.  The detailed
+description of the actual encryption process of the packets are
+described in [SILC2].
+
+Client and its server shares secret symmetric session key which is
+established by the SILC Key Exchange Protocol, described in [SILC3]. 
+Every packet sent from client to server, with exception of packets for
+channels, are encrypted with this session key.
+
+Channels has their own key that are shared by every client on the channel.
+However, the channel keys are cell specific thus one cell does not know
+the channel key of the other cell, even if that key is for same channel.
+Channel key is also known by the routers and all servers that has clients
+on the channel.  However, channels MAY have channel private keys that
+are entirely local setting for the client.  All clients on the channel
+MUST know the channel private key before hand to be able to talk on the
+channel.  In this case, no server or router know the key for channel.
+
+Server shares secret symmetric session key with router which is
+established by the SILC Key Exchange Protocol.  Every packet passed from
+server to router, with exception of packets for channels, are encrypted
+with the shared session key.  Same way, router server shares secret
+symmetric key with its primary route.  However, every packet passed
+from router to other router, including packets for channels, are
+encrypted with the shared session key.  Every router connection has
+their own session keys.
+
+
+.ti 0
+3.8.1 Determination of the Source and the Destination
+
+The source and the destination of the packet needs to be determined
+to be able to route the packets to correct receiver.  This information
+is available in the SILC Packet Header which is included in all packets
+sent in SILC network.  The SILC Packet Header is described in [SILC2].
+
+The header MUST be encrypted with the session key who is next receiver
+of the packet along the route.  The receiver of the packet, for example
+a router along the route, is able to determine the sender and the
+destination of the packet by decrypting the SILC Packet Header and
+checking the ID's attached to the header.  The ID's in the header will
+tell to where the packet needs to be sent and where it is coming from.
+
+The header in the packet MUST NOT change during the routing of the
+packet.  The original sender, for example client, assembles the packet
+and the packet header and server or router between the sender and the
+receiver MUST NOT change the packet header.
+
+Note that the packet and the packet header may be encrypted with
+different keys.  For example, packets to channels are encrypted with
+the channel key, however, the header is encrypted with the session key
+as described above.  However, the header and the packet may be encrypted
+with same key.  This is the case, for example, with command packets.
+
+
+.ti 0
+3.8.2 Client To Client
+
+The process of message delivery and encryption from client to another
+client is as follows.
+
+Example:  Private message from client to another client on different
+          servers.  Clients do not share private message delivery
+          keys; normal session keys are used.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the session key shared between client and its
+  server.
+
+o Server determines the destination of the packet and decrypts
+  the packet.  Server encrypts the packet with session key shared
+  between the server and its router, and sends the packet to the
+  router.
+
+o Router determines the destination of the packet and decrypts
+  the packet.  Router encrypts the packet with session key 
+  shared between the router and the destination server, and sends
+  the packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and decrypts the packet.  Server encrypts the packet with
+  session key shared between the server and the destination client,
+  and sends the packet to the client.
+
+o Client 2. decrypts the packet.
+
+
+Example:  Private message from client to another client on different
+          servers.  Clients has established secret shared private
+          message delivery key with each other and that is used in 
+          the message encryption.
+
+o Client 1. sends encrypted packet to its server.  The packet is
+  encrypted with the private message delivery key shared between
+  clients.
+
+o Server determines the destination of the packet and sends the 
+  packet to the router.
+
+o Router determines the destination of the packet and sends the
+  packet to the server.
+
+o Server determines the client to which the packet is destined
+  to and sends the packet to the client.
+
+o Client 2. decrypts the packet with the secret shared key.
+
+
+If clients share secret key with each other the private message
+delivery is much simpler since servers and routers between the
+clients do not need to decrypt and re-encrypt the packet.
+
+The process for clients on same server is much simpler as there are
+no need to send the packet to the router.  The process for clients 
+on different cells is same as above except that the packet is routed 
+outside the cell.  The router of the destination cell routes the 
+packet to the destination same way as described above.
+
+
+.ti 0
+3.8.3 Client To Channel
+
+Process of message delivery from client on channel to all the clients
+on the channel.
+
+Example:  Channel of four users; two on same server, other two on
+          different cells.  Client sends message to the channel.
+
+o Client 1. encrypts the packet with channel key and sends the
+  packet to its server.
+
+o Server determines local clients on the channel and sends the
+  packet to the Client on the same server.  Server then sends
+  the packet to its router for further routing.
+
+o Router determines local clients on the channel, if found
+  sends packet to the local clients.  Router determines global
+  clients on the channel and sends the packet to its primary
+  router or fastest route.
+
+o (Other router(s) do the same thing and sends the packet to
+   the server(s))
+
+o Server determines local clients on the channel and sends the
+  packet to the client.
+
+o All clients receiving the packet decrypts the packet.
+
+
+.ti 0
+3.8.4 Server To Server
+
+Server to server packet delivery and encryption is described in above
+examples. Router to router packet delivery is analogous to server to
+server.  However, some packets, such as channel packets, are processed
+differently.  These cases are described later in this document and
+more in detail in [SILC2].
+
+
+.ti 0
+3.9 Key Exchange And Authentication
+
+Key exchange is done always when for example client connects to server
+but also when server and router, and router and router connects to each
+other.  The purpose of key exchange protocol is to provide secure key
+material to be used in the communication.  The key material is used to
+derive various security parameters used to secure SILC packets.  The
+SILC Key Exchange protocol is described in detail in [SILC3].
+
+Authentication is done after key exchange protocol has been successfully
+completed.  The purpose of authentication is to authenticate for example
+client connecting to the server.  However, usually clients are accepted
+to connect to server without explicit authentication.  Servers are
+required use authentication protocol when connecting.  The authentication
+may be based on passphrase (pre-shared-secret) or public key.  The
+connection authentication protocol is described in detail in [SILC3].
+
+
+.ti 0
+3.9.1 Authentication Payload
+
+Authentication payload is used separately from the SKE and the Connection
+Authentication protocol.  It is used during the session to authenticate
+with the remote.  For example, the client can authenticate itself to the
+server to become server operator.  In this case, Authentication Payload is
+used.
+
+The format of the Authentication Payload is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|        Payload Length         |     Authentication Method     |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|      Public Data Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|   Authentication Data Length  |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                       Authentication Data                     ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+.ce
+Figure 5:  Authentication Payload
+
+
+.in 6
+o Payload Length (2 bytes) - Length of the entire payload.
+
+o Authentication Method (2) - The method of the authentication.
+  The authentication methods are defined in [SILC2] in the
+  Connection Auth Request Payload.  The NONE authentication
+  method SHOULD NOT be used.
+
+o Public Data Length (2 bytes) - Indicates the length of
+  the Public Data field.
+
+o Public Data (variable length) - This is defined only if
+  the authentication method is public key.  If it is any other
+  this field MAY include a random data for padding purposes.
+  However, in this case the field MUST be ignored by the
+  receiver.
+
+  When the authentication method is public key this includes
+  128 to 4096 bytes of non-zero random data that is used in
+  the signature process, described subsequently.
+
+o Authentication Data Length (2 bytes) - Indicates the
+  length of the Authentication Data field.
+
+o Authentication Data (variable length) - Authentication 
+  method dependent authentication data.
+.in 3
+
+
+If the authentication method is password based, the Authentication
+Data field includes the plaintext password.  It is safe to send
+plaintext password since the entire payload is encrypted.  In this
+case the Public Data Length is set to zero (0), but MAY also include
+random data for padding purposes.  It is also RECOMMENDED that maximum
+amount of padding is applied to SILC packet when using password based
+authentication.  This way it is not possible to approximate the length
+of the password from the encrypted packet.
+
+If the authentication method is public key based (or certificate)
+the Authentication Data is computed as follows:
+
+  HASH = hash(random bytes | ID | public key (or certificate));
+  Authentication Data = sign(HASH);
+
+The hash() and the sign() are the hash function and the public key
+cryptography function selected in the SKE protocol.  The public key
+is SILC style public key unless certificates are used.  The ID is the
+entity's ID (Client or Server ID) which is authenticating itself.  The
+ID is raw ID data.  The random bytes are non-zero random bytes of
+length between 128 and 4096 bytes, and will be included into the
+Public Data field as is.
+
+The receiver will compute the signature using the random data received
+in the payload, the ID associated to the connection and the public key
+(or certificate) received in the SKE protocol.  After computing the
+receiver MUST verify the signature.  In this case also, the entire
+payload is encrypted.
+
+
+.ti 0
+3.10 Algorithms
+
+This section defines all the allowed algorithms that can be used in
+the SILC protocol.  This includes mandatory cipher, mandatory public
+key algorithm and MAC algorithms.
+
+
+.ti 0
+3.10.1 Ciphers
+
+Cipher is the encryption algorithm that is used to protect the data
+in the SILC packets.  See [SILC2] of the actual encryption process and
+definition of how it must be done.  SILC has a mandatory algorithm that
+must be supported in order to be compliant with this protocol.
+
+The following ciphers are defined in SILC protocol:
+
+.in 6
+aes-256-cbc         AES in CBC mode, 256 bit key       (REQUIRED)
+aes-192-cbc         AES in CBC mode, 192 bit key       (OPTIONAL)
+aes-128-cbc         AES in CBC mode, 128 bit key       (OPTIONAL)
+twofish-256-cbc     Twofish in CBC mode, 256 bit key   (OPTIONAL)
+twofish-192-cbc     Twofish in CBC mode, 192 bit key   (OPTIONAL)
+twofish-128-cbc     Twofish in CBC mode, 128 bit key   (OPTIONAL)
+blowfish-128-cbc    Blowfish in CBC mode, 128 bit key  (OPTIONAL)
+cast-256-cbc        CAST-256 in CBC mode, 256 bit key  (OPTIONAL)
+cast-192-cbc        CAST-256 in CBC mode, 192 bit key  (OPTIONAL)
+cast-128-cbc        CAST-256 in CBC mode, 128 bit key  (OPTIONAL)
+rc6-256-cbc         RC6 in CBC mode, 256 bit key       (OPTIONAL)
+rc6-192-cbc         RC6 in CBC mode, 192 bit key       (OPTIONAL)
+rc6-128-cbc         RC6 in CBC mode, 128 bit key       (OPTIONAL)
+mars-256-cbc        Mars in CBC mode, 256 bit key      (OPTIONAL)
+mars-192-cbc        Mars in CBC mode, 192 bit key      (OPTIONAL)
+mars-128-cbc        Mars in CBC mode, 128 bit key      (OPTIONAL)
+none                No encryption                      (OPTIONAL)
+.in 3
+
+
+Algorithm none does not perform any encryption process at all and 
+thus is not recommended to be used.  It is recommended that no client
+or server implementation would accept none algorithms except in special
+debugging mode.
+
+Additional ciphers MAY be defined to be used in SILC by using the
+same name format as above.
+
+
+.ti 0
+3.10.2 Public Key Algorithms
+
+Public keys are used in SILC to authenticate entities in SILC network
+and to perform other tasks related to public key cryptography.  The 
+public keys are also used in the SILC Key Exchange protocol [SILC3].
+
+The following public key algorithms are defined in SILC protocol:
+
+.in 6
+rsa        RSA  (REQUIRED)
+dss        DSS  (OPTIONAL)
+.in 3
+
+DSS is described in [Menezes].  The RSA MUST be implemented according
+PKCS #1 [PKCS1].  The mandatory PKCS #1 implementation in SILC MUST be
+compliant to either PKCS #1 version 1.5 or newer with the following
+notes: The signature encoding is always in same format as the encryption
+encoding regardless of the PKCS #1 version.  The signature with appendix
+(with hash algorithm OID in the data) MUST NOT be used in the SILC.  The
+rationale for this is that there is no binding between the PKCS #1 OIDs
+and the hash algorithms used in the SILC protocol.  Hence, the encoding
+is always in PKCS #1 version 1.5 format.
+
+Additional public key algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.10.3 Hash Functions
+
+Hash functions are used as part of MAC algorithms defined in the next
+section.  They are also used in the SILC Key Exchange protocol defined
+in the [SILC3].
+
+The following Hash algorithm are defined in SILC protocol:
+
+.in 6
+sha1             SHA-1, length = 20      (REQUIRED)
+md5              MD5, length = 16        (OPTIONAL)
+.in 3
+
+
+.ti 0
+3.10.4 MAC Algorithms
+
+Data integrity is protected by computing a message authentication code
+(MAC) of the packet data.  See [SILC2] for details how to compute the
+MAC.
+
+The following MAC algorithms are defined in SILC protocol:
+
+.in 6
+hmac-sha1-96     HMAC-SHA1, length = 12  (REQUIRED)
+hmac-md5-96      HMAC-MD5, length = 12   (OPTIONAL)
+hmac-sha1        HMAC-SHA1, length = 20  (OPTIONAL)
+hmac-md5         HMAC-MD5, length = 16   (OPTIONAL)
+none             No MAC                  (OPTIONAL)
+.in 3
+
+The none MAC is not recommended to be used as the packet is not
+authenticated when MAC is not computed.  It is recommended that no
+client or server would accept none MAC except in special debugging
+mode.
+
+The HMAC algorithm is described in [HMAC] and hash algorithms that
+are used as part of the HMACs are described in [Scheneir] and in
+[Menezes]
+
+Additional MAC algorithms MAY be defined to be used in SILC.
+
+
+
+
+.ti 0
+3.10.5 Compression Algorithms
+
+SILC protocol supports compression that may be applied to unencrypted
+data.  It is recommended to use compression on slow links as it may
+significantly speed up the data transmission.  By default, SILC does not
+use compression which is the mode that must be supported by all SILC
+implementations.
+
+The following compression algorithms are defined:
+
+.in 6
+none        No compression               (REQUIRED)
+zlib        GNU ZLIB (LZ77) compression  (OPTIONAL)
+.in 3
+
+Additional compression algorithms MAY be defined to be used in SILC.
+
+
+.ti 0
+3.11 SILC Public Key
+
+This section defines the type and format of the SILC public key.  All
+implementations MUST support this public key type.  See [SILC3] for
+other optional public key and certificate types allowed in the SILC
+protocol.  Public keys in SILC may be used to authenticate entities
+and to perform other tasks related to public key cryptography.
+
+The format of the SILC Public Key is as follows:
+
+
+.in 5
+.nf
+                     1                   2                   3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                        Public Key Length                      |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|     Algorithm Name Length     |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                         Algorithm Name                        ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|       Identifier Length       |                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
+|                                                               |
+~                           Identifier                          ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+|                                                               |
+~                           Public Data                         ~
+|                                                               |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+.in 3
+
+.ce
+Figure 5:  SILC Public Key
+
+
+.in 6
+o Public Key Length (4 bytes) - Indicates the full length
+  of the public key, not including this field.
+
+o Algorithm Name Length (2 bytes) - Indicates the length
+  of the Algorithm Length field, not including this field.
+
+o Algorithm name (variable length) - Indicates the name
+  of the public key algorithm that the key is.  See the
+  section 3.10.2 Public Key Algorithms for defined names.
+
+o Identifier Length (2 bytes) - Indicates the length of
+  the Identifier field, not including this field.
+
+o Identifier (variable length) - Indicates the identifier
+  of the public key.  This data can be used to identify
+  the owner of the key.  The identifier is of the following
+  format:
+
+     UN   User name
+     HN   Host name or IP address
+     RN   Real name
+     E    EMail address
+     O    Organization
+     C    Country
+
+
+  Examples of an identifier:
+
+    `UN=priikone, HN=poseidon.pspt.fi, E=priikone@poseidon.pspt.fi'
+
+    `UN=sam, HN=dummy.fi, RN=Sammy Sam, O=Company XYZ, C=Finland'
+
+  At least user name (UN) and host name (HN) MUST be provided as
+  identifier.  The fields are separated by commas (`,').  If
+  comma is in the identifier string it must be written as `\\,',
+  for example, `O=Company XYZ\\, Inc.'.
+
+o Public Data (variable length) - Includes the actual
+  public data of the public key.
+
+  The format of this field for RSA algorithm is
+  as follows:
+
+     4 bytes            Length of e
+     variable length    e
+     4 bytes            Length of n
+     variable length    n
+
+
+  The format of this field for DSS algorithm is
+  as follows:
+
+     4 bytes            Length of p
+     variable length    p
+     4 bytes            Length of q
+     variable length    q
+     4 bytes            Length of g
+     variable length    g
+     4 bytes            Length of y
+     variable length    y
+
+  The variable length fields are multiple precession
+  integers encoded as strings in both examples.
+
+  Other algorithms must define their own type of this
+  field if they are used.
+.in 3
+
+All fields in the public key are in MSB (most significant byte first)
+order.
+
+
+.ti 0
+3.12 SILC Version Detection
+
+The version detection of both client and server is performed at the
+connection phase while executing the SILC Key Exchange protocol.  The
+version identifier is exchanged between initiator and responder.  The
+version identifier is of the following format:
+
+.in 6
+SILC-<protocol version>-<software version>
+.in 3
+
+The version strings are of the following format:
+
+.in 6
+protocol version = <major>.<minor>
+software version = <major>[.<minor>[.<build>]]
+.in 3
+
+Protocol version MAY provide both major and minor version.  Currently
+implementations MUST set the protocol version and accept the protocol
+version as SILC-1.0-<software version>.  If new protocol version causes
+in compatibilities with older version the the <minor> versio number MUST
+be incremented.  The <major> is incremented if new protocol version is
+fully incompatible.
+
+Software version MAY provide major, minor and build version.  The
+software version MAY be freely set and accepted.
+
+
+Thus, the version string could be, for example:
+
+.in 6
+SILC-1.0-1.2
+.in 3
+
+
+.ti 0
+3.13 Backup Routers
+
+Backup routers may exist in the cell in addition of the primary router.
+However, they must not be active routers and act as routers in the cell.
+Only one router may be acting as primary router in the cell.  In the case
+of failure of the primary router may one of the backup routers become
+active.  The purpose of backup routers are in case of failure of the
+primary router to maintain working connections inside the cell and outside
+the cell and to avoid netsplits.
+
+Backup routers are normal servers in the cell that are prepared to take
+over the tasks of the primary router if needed.  They need to have at
+least one direct and active connection to the primary router of the cell.
+This communication channel is used to send the router information to
+the backup router.  When the backup router connects to the primary router
+of the cell it MUST present itself as router server in the Connection
+Authentication protocol, even though it is normal server as long as the
+primary router is available.  Reason for this is that the configuration
+needed in the responder end requires usually router connection level
+configuration.  The responder, however must understand and treat the
+connection as normal server (except when feeding router level data to
+the backup router).
+
+Backup router must know everything that the primary router knows to be
+able to take over the tasks of the primary router.  It is the primary
+router's responsibility to feed the data to the backup router.  If the
+backup router does not know all the data in the case of failure some
+connections may be lost.  The primary router of the cell must consider
+the backup router being actual router server when it feeds the data to
+it.
+
+In addition of having direct connection to the primary router of the
+cell, the backup router must also have connection to the same router
+the primary router of the cell is connected.  However, it must not be
+active router connection meaning that the backup router must not use
+that channel as its primary route and it must not notify the router
+about having connected servers, channels and clients behind it.  It
+merely connects to the router.  This sort of connection is later
+referred as being passive connection.  Some keepalive actions may be
+needed by the router to keep the connection alive.
+
+It is required that other normal servers have passive connections to
+the backup router(s) in the cell.  Some keepalive actions may be needed
+by the server to keep the connection alive.  After they notice the
+failure of the primary router they must start using the connection to
+the first backup router as their primary route.
+
+Also, if any other router in the network is using the cell's primary
+router as its own primary router, it must also have passive connection
+to the cell's backup router.  It too is prepared to switch to use the
+backup router as its new primary router as soon as the orignal primary
+router becomes unresponsive.
+
+All of the parties of this protocol knows which one is the backup router
+of the cell from their local configuration.  Each of the entity must
+be configured accordingly and care must be taken when configuring the
+backup routers, servers and other routers in the network.
+
+It must be noted that some of the channel messages and private messages
+may be lost during the switch to the backup router.  The announcements
+assures that the state of the network is not lost during the switch.
+
+It is RECOMMENDED that there would be at least one backup router in
+the cell.  It is NOT RECOMMENDED to have all servers in the cell acting
+as backup routers as it requires establishing several connections to
+several servers in the cell.  Large cells can easily have several
+backup routers in the cell.
+
+The order of the backup routers are decided at the configuration phase.
+All the parties of this protocol must be configured accordingly to 
+understand the order of the backup routers.  It is not required that
+the backup server is actually active server in the cell.  Backup router
+may be a spare server in the cell that does not accept normal client
+connections at all.  It may be reserved purely for the backup purposes.
+These, however, are cell management issues.
+
+If also the first backup router is down as well and there is another
+backup router in the cell then it will start acting as the primary
+router as described above.
+
+
+.ti 0
+3.13.1 Switching to Backup Router
+
+When the primary router of the cell becomes unresponsive, for example
+by sending EOF to the connection, all the parties of this protocol MUST
+replace the old connection to the primary router with first configured
+backup router.  The backup router usually needs to do local modifications
+to its database in order to update all the information needed to maintain
+working routes.  The backup router must understand that clients that
+were orignated from the primary router are now originated from some of
+the existing server connections and must update them accordingly.  It
+must also remove those clients that were owned by the primary router
+since those connections were lost when the primary router became
+unresponsive.
+
+All the other parties of the protocol must also update their local
+database to understand that the route to the primary router will now go
+to the backup router.
+
+The servers connected to the backup router must announce their clients,
+channels, channel users, channel user modes and channel modes to the
+backup router.  This is to assure that none of the important notify 
+packets were lost during the switch to the backup router.  The backup
+router must check which of these announced entities it already have
+and distribute the new ones to the primary route.
+
+The backup router too must announce its servers, clients, channels
+and other information to the new primary router.  The primary router
+of the backup router too must announce its informations to the backup
+router.  Both must process only the ones they do not know about.  If
+any of the announced modes does not match then they are enforced in
+normal manner defined later in this specification.
+
+
+.ti 0
+3.13.2 Resuming Primary Router
+
+Usually the primary router is unresponsive only a short period of time
+and it is intended that the original router of the cell will reassume
+its position as primary router when it comes back online.  The backup
+router that is now acting as primary router of the cell must constantly
+try to connect to the original primary router of the cell.  It is
+RECOMMENDED that it would try to reconnect in 30 second intervals to
+the primary router.
+
+When the connection is established to the primary router the backup
+resuming protocol is executed.  The protocol is advanced as follows:
+
+  1. Backup router sends SILC_PACKET_RESUME_ROUTER packet with type
+     value 1 the primary router that came back online.  The packet
+     will indicate the primary router has been replaced by the backup
+     router.  After sending the packet the backup router will announce
+     all of its channels, channel users, modes etc. to the primary
+     router.
+
+  2. Backup router sends SILC_PACKET_RESUME_ROUTER packet with type
+     value 2 to its current primary router to indicate that it will
+     resign as being primary router.  Then, backup router sends the
+     SILC_PACKET_RESUME_ROUTER packet with type value 1 to all
+     connected servers to also indicate that it will resign as being
+     primary router.
+
+  3. Backup router also send SILC_PACKET_RESUME_ROUTER packet with
+     type value 2 to the router that is using the backup router
+     currently as its primary router.
+
+  4. Any server and router that receives the SILC_PACKET_RESUME_ROUTER
+     with type value 1 or 2 must reconnect immediately to the
+     primary router of the cell that came back online.  After they
+     have created the connection they MUST NOT use that connection
+     as active primary route but still route all packets to the
+     backup router.  After the connection is created they MUST send
+     SILC_PACKET_RESUME_ROUTER with type value 3 back to the
+     backup router.  The session ID value found in the first packet
+     MUST be set in this packet.
+
+  5. Backup router MUST wait for all packets with type value 3 before
+     it continues with the protocol.  It knows from the session ID values
+     set in the packet when it have received all packets.  The session
+     value should be different in all packets it have send earlier.
+     After the packets is received the backup router sends the
+     SILC_PACKET_RESUME_ROUTER packet with type value 4 to the
+     primary router that came back online.  This packet will indicate 
+     that the backup router is now ready to resign as being primary
+     router.  The session ID value in this packet MUST be the same as
+     in first packet sent to the primary router.  During this time
+     the backup router should still route all packets it is receiving
+     from server connections.
+
+  6. The primary router receives the packet and send the
+     SILC_PACKET_RESUME_ROUTER with type value 5 to all connected servers
+     including the backup router.  It also sends the packet with type
+     value 6 to its primary router, and to the router that is using
+     it as its primary router.  The Session ID value in this packet
+     SHOULD be zero (0).
+
+  7. Any server and router that receives the SILC_PACKET_RESUME_ROUTER
+     with type value 5 or 6 must switch their primary route to the
+     new primary router and remove the route for the backup router, since
+     it is not anymore the primary router of the cell.  They must also
+     update their local database to understand that the clients are
+     not originated from the backup router but from the locally connected
+     servers.  After that they MUST announce their channels, channel
+     users, modes etc. to the primary router.  They must not use the
+     backup router connection after this and the connection is considered
+     to be passive connection.  The implementations SHOULD be able
+     to disable the connection without closing the actual link.
+
+After this protocol is executed the backup router is now again normal
+server in the cell that has the backup link to the primary router.  The
+primary router feeds the router specific data again to the backup router.
+All server connections in the backup router are considered passive
+connections.
+
+When the primary router of the cell comes back online and connects
+to its primary router, the remote primary router must send the 
+SILC_PACKET_RESUME_ROUTER with type value 20 indicating that the
+connection is not allowed since the router has been replaced by an
+backup router.  The session ID value in this packet SHOULD be zero (0).
+When the router receives this packet it must not use the connection
+as active connection but to understand that it cannot act as primary
+router in the cell.  It must wait that the backup router connects to
+it, and the backup resuming protocol is executed.
+
+The following type values has been defined for SILC_PACKET_RESUME_ROUTER
+packet:
+
+  1    SILC_SERVER_BACKUP_START
+  2    SILC_SERVER_BACKUP_START_GLOBAL
+  3    SILC_SERVER_BACKUP_START_CONNECTED
+  4    SILC_SERVER_BACKUP_START_ENDING
+  5    SILC_SERVER_BACKUP_START_RESUMED
+  6    SILC_SERVER_BACKUP_START_GLOBAL
+  20   SILC_SERVER_BACKUP_START_REPLACED
+
+If any other value is found in the type field the packet must be 
+discarded.  The SILC_PACKET_RESUME_ROUTER packet and its payload
+is defined in [SILC2].
+
+
+.ti 0
+3.13.3 Discussion on Backup Router Scheme
+
+It is clear that this backup router support is not able to handle all
+possible situations arrising in unreliable network environment.  This
+scheme for example does not handle situation when the router actually
+does not go offline but the network link goes down temporarily.  It would
+require some intelligence to figure out when it is best time to switch
+to the backup router.  To make it even more complicated it is possible
+that the backup router may have not lost the network link to the primary
+router.
+
+Other possible situation is when the network link is lost temporarily
+between two primary routers in the SILC network.  Unless the routers
+notice the link going down they cannot perhaps find alternative routes.
+Worst situation is when the link goes down only for a short period of
+time, thus causing lag.  Should the routers or servers find alternative
+routes if they cannot get response from the router during the lag?
+When alternative routes are being found it must be careful not to
+mess up existing primary routes between routers in the network.
+
+It is suggested that the current backup router scheme is only temporary
+solution and existing backup router protocols are studied further.  It
+is also suggested that the backup router specification will be separated
+from this SILC specification Internet-Draft and additional specification
+is written on the subject.
+
+
+.ti 0
+4 SILC Procedures
+
+This section describes various SILC procedures such as how the 
+connections are created and registered, how channels are created and
+so on.  The section describes the procedures only generally as details
+are described in [SILC2] and [SILC3].
+
+
+.ti 0
+4.1 Creating Client Connection
+
+This section describes the procedure when client connects to SILC server.
+When client connects to server the server MUST perform IP address lookup
+and reverse IP address lookup to assure that the origin host really is
+who it claims to be.  Client, host, connecting to server SHOULD have 
+both valid IP address and fully qualified domain name (FQDN).
+
+After that the client and server performs SILC Key Exchange protocol
+which will provide the key material used later in the communication.
+The key exchange protocol MUST be completed successfully before the
+connection registration may continue.  The SILC Key Exchange protocol
+is described in [SILC3].
+
+Typical server implementation would keep a list of connections that it
+allows to connect to the server.  The implementation would check, for
+example, the connecting client's IP address from the connection list
+before the SILC Key Exchange protocol has been started.  Reason for
+this is that if the host is not allowed to connect to the server there
+is no reason to perform the key exchange protocol.
+
+After successful key exchange protocol the client and server performs
+connection authentication protocol.  The purpose of the protocol is to
+authenticate the client connecting to the server.  Flexible
+implementation could also accept the client to connect to the server
+without explicit authentication.  However, if authentication is
+desired for a specific client it may be based on passphrase or
+public key authentication.  If authentication fails the connection
+MUST be terminated.  The connection authentication protocol is described
+in [SILC3].
+
+After successful key exchange and authentication protocol the client
+registers itself by sending SILC_PACKET_NEW_CLIENT packet to the
+server.  This packet includes various information about the client
+that the server uses to create the client.  Server creates the client
+and sends SILC_PACKET_NEW_ID to the client which includes the created
+Client ID that the client MUST start using after that.  After that
+all SILC packets from the client MUST have the Client ID as the
+Source ID in the SILC Packet Header, described in [SILC2].
+
+Client MUST also get the server's Server ID that is to be used as
+Destination ID in the SILC Packet Header when communicating with
+the server (for example when sending commands to the server).  The
+ID may be resolved in two ways.  Client can take the ID from an
+previously received packet from server that MUST include the ID,
+or to send SILC_COMMAND_INFO command and receive the Server ID as
+command reply.
+
+Server MAY choose not to use the information received in the
+SILC_PACKET_NEW_CLIENT packet.  For example, if public key or 
+certificate were used in the authentication, server MAY use those
+informations rather than what it received from client.  This is suitable
+way to get the true information about client if it is available.
+
+The nickname of client is initially set to the username sent in the
+SILC_PACKET_NEW_CLIENT packet.  User should set the nickname to more
+suitable by sending SILC_COMMAND_NICK command.  However, this is not
+required as part of registration process.
+
+Server MUST also distribute the information about newly registered
+client to its router (or if the server is router, to all routers in
+the SILC network).  More information about this in [SILC2].
+
+
+.ti 0
+4.2 Creating Server Connection
+
+This section describes the procedure when server connects to its
+router (or when router connects to other router, the cases are
+equivalent).  The procedure is very much alike when client connects
+to the server thus it is not repeated here.
+
+One difference is that server MUST perform connection authentication
+protocol with proper authentication.  A proper authentication is based
+on passphrase or public key authentication.
+
+After server and router has successfully performed the key exchange
+and connection authentication protocol, the server register itself
+to the router by sending SILC_PACKET_NEW_SERVER packet.  This packet
+includes the server's Server ID that it has created by itself and
+other relevant information about the server.
+
+After router has received the SILC_PACKET_NEW_SERVER packet it
+distributes the information about newly registered server to all routers
+in the SILC network.  More information about this in [SILC2].
+
+As client needed to resolve the destination ID this MUST be done by the
+server that connected to the router, as well.  The way to resolve it is
+to get the ID from previously received packet.  The server MAY also 
+use SILC_COMMAND_INFO command to resolve the ID.  Server MUST also start
+using its own Server ID as Source ID in SILC Packet Header and the
+router's Server ID as Destination when communicating with the router.
+
+
+.ti 0
+4.2.1 Announcing Clients, Channels and Servers
+
+After server or router has connected to the remote router, and it already
+has connected clients and channels it MUST announce them to the router.
+If the server is router server, also all the local servers in the cell
+MUST be announced.
+
+All clients are announced by compiling a list of ID Payloads into the
+SILC_PACKET_NEW_ID packet.  All channels are announced by compiling a
+list of Channel Payloads into the SILC_PACKET_NEW_CHANNEL packet.  Also, 
+the channel users on the channels must be announced by compiling a
+list of Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into
+the SILC_PACKET_NOTIFY packet.  The users' modes on the channel must 
+also be announced by compiling list of Notify Payloads with the 
+SILC_NOTIFY_TYPE_CUMODE_CHANGE notify type into the SILC_PACKET_NOTIFY
+packet.
+
+The router MUST also announce the local servers by compiling list of
+ID Payloads into the SILC_PACKET_NEW_ID packet.
+
+Also, clients' modes (user modes in SILC) MUST be announced.  This is
+done by compiling a list of Notify Payloads with the 
+SILC_NOTIFY_UMODE_CHANGE nofity type into the SILC_PACKET_NOTIFY packet.
+
+Also, channel's topics MUST be announced by compiling a list of Notify
+Payloads with the SILC_NOTIFY_TOPIC_SET notify type into the
+SILC_PACKET_NOTIFY packet.
+
+The router which receives these lists MUST process them and broadcast
+the packets to its primary route.
+
+When processing the announced channels and channel users the router MUST
+check whether a channel exists already with the same name.  If channel
+exists with the same name it MUST check whether the Channel ID is
+different.  If the Channel ID is different the router MUST send the notify
+type SILC_NOTIFY_TYPE_CHANNEL_CHANGE to the server to force the channel ID
+change to the ID the router has.  If the mode of the channel is different
+the router MUST send the notify type SILC_NOTIFY_TYPE_CMODE_CHANGE to the
+server to force the mode change to the mode that the router has.
+
+The router MUST also generate new channel key and distribute it to the
+channel.  The key MUST NOT be generated if the SILC_CMODE_PRIVKEY mode
+is set.
+
+If the channel has channel founder on the router the router MUST send
+the notify type SILC_NOTIFY_TYPE_CUMODE_CHANGE to the server to force
+the mode change for the channel founder on the server.  The channel 
+founder privileges MUST be removed.
+
+The router processing the channels MUST also compile a list of
+Notify Payloads with the SILC_NOTIFY_TYPE_JOIN notify type into the
+SILC_PACKET_NOTIFY and send the packet to the server.  This way the
+server (or router) will receive the clients on the channel that
+the router has.
+
+
+.ti 0
+4.3 Joining to a Channel
+
+This section describes the procedure when client joins to a channel.
+Client joins to channel by sending command SILC_COMMAND_JOIN to the
+server.  If the receiver receiving join command is normal server the
+server MUST check its local list whether this channel already exists
+locally.  This would indicate that some client connected to the server
+has already joined to the channel.  If this is case the client is
+joined to the channel, new channel key is created and information about
+newly joined channel is sent to the router.  The router is informed
+by sending SILC_NOTIFY_TYPE_JOIN notify type.  The notify type MUST
+also be sent to the local clients on the channel.  The new channel key
+is also sent to the router and to local clients on the channel.
+
+If the channel does not exist in the local list the client's command
+MUST be sent to the router which will then perform the actual joining
+procedure.  When server receives the reply to the command from the
+router it MUST be sent to the client which sent the command originally.
+Server will also receive the channel key from the server that it MUST
+send to the client which originally requested the join command.  The
+server MUST also save the channel key.
+
+If the receiver of the join command is router it MUST first check its
+local list whether anyone in the cell has already joined to the channel.
+If this is the case the client is joined to the channel and reply is
+sent to the client.  If the command was sent by server the command reply
+is sent to the server which sent it.  Then the router MUST also create
+new channel key and distribute it to all clients on the channel and
+all servers that has clients on the channel.  Router MUST also send
+the SILC_NOTIFY_TYPE_JOIN notify type to local clients on the channel
+and to local servers that has clients on the channel.
+
+If the channel does not exist on the router's local list it MUST
+check the global list whether the channel exists at all.  If it does
+the client is joined to the channel as described previously.  If
+the channel does not exist the channel is created and the client
+is joined to the channel.  The channel key is also created and
+distributed as previously described.  The client joining to the created
+channel is made automatically channel founder and both channel founder
+and channel operator privileges is set for the client.
+
+If the router created the channel in the process, information about the
+new channel MUST be broadcasted to all routers.  This is done by 
+broadcasting SILC_PACKET_NEW_CHANNEL packet to the router's primary
+route.  When the router joins the client to the channel it MUST also
+send information about newly joined client to all routers in the SILC
+network.  This is done by broadcasting the SILC_NOTIFY_TYPE_JOIN notify
+type to the router's primary route. 
+
+It is important to note that new channel key is created always when
+new client joins to channel, whether the channel has existed previously
+or not.  This way the new client on the channel is not able to decrypt
+any of the old traffic on the channel.  Client which receives the reply to
+the join command MUST start using the received Channel ID in the channel
+message communication thereafter.  Client also receives the key for the
+channel in the command reply.  Note that the channel key is never
+generated if the SILC_CMODE_PRIVKEY mode is set.
+
+
+.ti 0
+4.4 Channel Key Generation
+
+Channel keys are created by router which creates the channel by taking
+enough randomness from cryptographically strong random number generator.
+The key is generated always when channel is created, when new client
+joins a channel and after the key has expired.  Key could expire for
+example in an hour.
+
+The key MUST also be re-generated whenever some client leaves a channel.
+In this case the key is created from scratch by taking enough randomness
+from the random number generator.  After that the key is distributed to
+all clients on the channel.  However, channel keys are cell specific thus
+the key is created only on the cell where the client, which left the
+channel, exists.  While the server or router is creating the new channel
+key, no other client may join to the channel.  Messages that are sent
+while creating the new key are still processed with the old key.  After
+server has sent the SILC_PACKET_CHANNEL_KEY packet MUST client start
+using the new key.  If server creates the new key the server MUST also
+send the new key to its router.  See [SILC2] on more information about
+how channel messages must be encrypted and decrypted when router is
+processing them.
+
+When client receives the SILC_PACKET_CHANNEL_KEY packet with the
+Channel Key Payload it MUST process the key data to create encryption
+and decryption key, and to create the HMAC key that is used to compute
+the MACs of the channel messages.  The processing is as follows:
+
+  channel_key  = raw key data
+  HMAC key     = hash(raw key data)
+
+The raw key data is the key data received in the Channel Key Payload.
+The hash() function is the hash function used in the HMAC of the channel.
+Note that the server MUST also save the channel key.
+
+
+.ti 0
+4.5 Private Message Sending and Reception
+
+Private messages are sent point to point.  Client explicitly destines
+a private message to specific client that is delivered to only to that
+client.  No other client may receive the private message.  The receiver
+of the private message is destined in the SILC Packet Header as any
+other packet as well.
+
+If the sender of a private message does not know the receiver's Client
+ID, it MUST resolve it from server.  There are two ways to resolve the
+client ID from server; it is RECOMMENDED that client implementations
+send SILC_COMMAND_IDENTIFY command to receive the Client ID.  Client
+MAY also send SILC_COMMAND_WHOIS command to receive the Client ID.
+If the sender has received earlier a private message from the receiver
+it should have cached the Client ID from the SILC Packet Header.
+
+If server receives a private message packet which includes invalid
+destionation Client ID the server MUST send SILC_COMMAND_IDENTIFY
+command reply packet destined to the client with error status.
+
+See [SILC2] for description of private message encryption and decryption
+process.
+
+
+.ti 0
+4.6 Private Message Key Generation
+
+Private message MAY be protected by the key generated by the client.
+The key may be generated and sent to the other client by sending packet
+SILC_PACKET_PRIVATE_MESSAGE_KEY which travels through the network
+and is secured by session keys.  After that the private message key
+is used in the private message communication between those clients.
+
+Other choice is to entirely use keys that are not sent through
+the SILC network at all.  This significantly adds security.  This key
+would be pre-shared-key that is known by both of the clients.  Both
+agree about using the key and starts sending packets that indicate
+that the private message is secured using private message key.
+
+The key material used as private message key is implementation issue.
+However, SILC_PACKET_KEY_AGREEMENT packet MAY be used to negotiate
+the key material.  If the key is normal pre-shared-key or randomly
+generated key, and the SILC_PACKET_KEY_AGREEMENT was not used, then
+the key material SHOULD be processed as defined in the [SILC3].  In
+the processing, however, the HASH, as defined in [SILC3] MUST be 
+ignored.  After processing the key material it is employed as defined
+in [SILC3], however, the HMAC key material MUST be discarded.
+
+If the key is pre-shared-key or randomly generated the implementations
+should use the SILC protocol's mandatory cipher as the cipher.  If the
+SKE was used to negotiate key material the cipher was negotiated as well.
+
+.ti 0
+4.7 Channel Message Sending and Reception
+
+Channel messages are delivered to group of users.  The group forms a
+channel and all clients on the channel receives messages sent to the
+channel.
+
+Channel messages are destined to channel by specifying the Channel ID
+as Destination ID in the SILC Packet Header.  The server MUST then
+distribute the message to all clients on the channel by sending the
+channel message destined explicitly to a client on the channel.
+
+See the [SILC2] for description of channel messege routing for router
+servers.
+
+See [SILC2] for description of channel message encryption and decryption
+process.
+
+
+.ti 0
+4.8 Session Key Regeneration
+
+Session keys MUST be regenerated periodically, say, once in an hour.
+The re-key process is started by sending SILC_PACKET_REKEY packet to
+other end, to indicate that re-key must be performed.  The initiator
+of the connection SHOULD initiate the re-key.
+
+If perfect forward secrecy (PFS) flag was selected in the SILC Key
+Exchange protocol [SILC3] the re-key MUST cause new key exchange with
+SKE protocol.  In this case the protocol is secured with the old key
+and the protocol results to new key material.  See [SILC3] for more
+information.  After the SILC_PACKET_REKEY packet is sent the sender
+will perform the SKE protocol.
+
+If PFS flag was set the resulted key material is processed as described
+in the section Processing the Key Material in [SILC3].  The difference
+with re-key in the processing is that the initial data for the hash 
+function is just the resulted key material and not the HASH as it
+is not computed at all with re-key.  Other than that, the key processing
+it equivalent to normal SKE negotiation.
+
+If PFS flag was not set, which is the default case, then re-key is done
+without executing SKE protocol.  In this case, the new key is created by
+providing the current sending encryption key to the SKE protocol's key
+processing function.  The process is described in the section Processing
+the Key Material in [SILC3].  The difference in the processing is that
+the initial data for the hash function is the current sending encryption
+key and not the SKE's KEY and HASH values.  Other than that, the key
+processing is equivalent to normal SKE negotiation.
+
+After both parties has regenerated the session key, both MUST send
+SILC_PACKET_REKEY_DONE packet to each other.  These packets are still
+secured with the old key.  After these packets, the subsequent packets
+MUST be protected with the new key.
+
+
+.ti 0
+4.9 Command Sending and Reception
+
+Client usually sends the commands in the SILC network.  In this case
+the client simply sends the command packet to server and the server
+processes it and replies with command reply packet.
+
+However, if the server is not able to process the command, it is sent 
+to the server's router.  This is case for example with commands such
+as, SILC_COMMAND_JOIN and SILC_COMMAND_WHOIS commands.  However, there
+are other commands as well.  For example, if client sends the WHOIS
+command requesting specific information about some client the server must
+send the WHOIS command to router so that all clients in SILC network
+are searched.  The router, on the other hand, sends the WHOIS command
+further to receive the exact information about the requested client.
+The WHOIS command travels all the way to the server which owns the client
+and it replies with command reply packet.  Finally, the server which
+sent the command receives the command reply and it must be able to
+determine which client sent the original command.  The server then
+sends command reply to the client.  Implementations should have some
+kind of cache to handle, for example, WHOIS information.  Servers
+and routers along the route could all cache the information for faster
+referencing in the future.
+
+The commands sent by server may be sent hop by hop until someone is able
+to process the command.  However, it is preferred to destine the command
+as precisely as it is possible.  In this case, other routers en route
+MUST route the command packet by checking the true sender and true
+destination of the packet.  However, servers and routers MUST NOT route
+command reply packets to clients coming from other server.  Client
+MUST NOT accept command reply packet originated from anyone else but
+from its own server.
+
+
+.ti 0
+4.10 Closing Connection
+
+When remote client connection is closed the server MUST send the notify
+type SILC_NOTIFY_TYPE_SIGNOFF to its primary router and to all channels
+the client was joined.  The server MUST also save the client's information
+for a period of time for history purposes.
+
+When remote server or router connection is closed the server or router
+MUST also remove all the clients that was behind the server or router
+from the SILC Network.  The server or router MUST also send the notify
+type SILC_NOTIFY_TYPE_SERVER_SIGNOFF to its primary router and to all
+local clients that are joined on the same channels with the remote 
+server's or router's clients.
+
+
+.ti 0
+5 Security Considerations
+
+Security is central to the design of this protocol, and these security
+considerations permeate the specification.  Common security considerations
+such as keeping private keys truly private and using adequate lengths for
+symmetric and asymmetric keys must be followed in order to maintain the
+security of this protocol.
+
+Special attention must also be paid on the servers and routers that are
+running the SILC service.  The SILC protocol's security depends greatly
+on the security and the integrity of the servers and administrators that
+are running the service.  It is recommended that some form of registration
+is required by the server and router administrator prior acceptance to
+the SILC Network.  Even though, the SILC protocol is secure in a network
+of mutual distrust between clients, servers, routers and adminstrators
+of the servers, the client should be able to trust the servers they are
+using if they whish to do so.
+
+It however must be noted that if the client requires absolute security
+by not trusting any of the servers or routers in the SILC Network, it can
+be accomplished by negotiating private keys outside the SILC Network,
+either using SKE or some other key exchange protocol, or to use some
+other external means for distributing the keys.  This applies for all 
+messages, private messages and channel messages.
+
+It is important to note that SILC, like any other security protocol is
+not full proof system and cannot secure from insecure environment; the
+SILC servers and routers could very well be compromised.  However, to
+provide acceptable level of security and usability for end user the
+protocol use many times session keys or other keys generated by the
+servers to secure the messages.  This is intentional design feature to
+allow ease of use for end user.  This way the network is still usable,
+and remains encrypted even if the external means of distributing the
+keys is not working.  The implementation, however, may like to not
+follow this design feature, and always negotiate the keys outside SILC
+network.  This is acceptable solution and many times recommended.  The
+implementation still must be able to work with the server generated keys.
+
+If this is unacceptable for the client or end user, the private keys
+negotiatied outside the SILC Network should always be used.  In the end
+it is always implementor's choice whether to negotiate private keys by
+default or whether to use the keys generated by the servers.
+
+It is also recommended that router operators in the SILC Network would
+form a joint forum to discuss the router and SILC Network management
+issues.  Also, router operators along with the cell's server operators
+should have a forum to discuss the cell management issues.
+
+
+.ti 0
+6 References
+
+[SILC2]      Riikonen, P., "SILC Packet Protocol", Internet Draft,
+             April 2001.
+
+[SILC3]      Riikonen, P., "SILC Key Exchange and Authentication 
+             Protocols", Internet Draft, April 2001.
+
+[SILC4]      Riikonen, P., "SILC Commands", Internet Draft, April 2001.
+
+[IRC]        Oikarinen, J., and Reed D., "Internet Relay Chat Protocol",
+             RFC 1459, May 1993.
+
+[IRC-ARCH]   Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
+             April 2000.
+
+[IRC-CHAN]   Kalt, C., "Internet Relay Chat: Channel Management", RFC
+             2811, April 2000.
+
+[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
+             2812, April 2000.
+
+[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
+             2813, April 2000.
+
+[SSH-TRANS]  Ylonen, T., et al, "SSH Transport Layer Protocol", 
+             Internet Draft.
+
+[PGP]        Callas, J., et al, "OpenPGP Message Format", RFC 2440,
+             November 1998.
+
+[SPKI]       Ellison C., et al, "SPKI Certificate Theory", RFC 2693,
+             September 1999.
+
+[PKIX-Part1] Housley, R., et al, "Internet X.509 Public Key 
+             Infrastructure, Certificate and CRL Profile", RFC 2459,
+             January 1999.
+
+[Schneier]   Schneier, B., "Applied Cryptography Second Edition",
+             John Wiley & Sons, New York, NY, 1996.
+
+[Menezes]    Menezes, A., et al, "Handbook of Applied Cryptography",
+             CRC Press 1997.
+
+[OAKLEY]     Orman, H., "The OAKLEY Key Determination Protocol",
+             RFC 2412, November 1998.
+
+[ISAKMP]     Maughan D., et al, "Internet Security Association and
+             Key Management Protocol (ISAKMP)", RFC 2408, November
+             1998.
+
+[IKE]        Harkins D., and Carrel D., "The Internet Key Exchange
+             (IKE)", RFC 2409, November 1998.
+
+[HMAC]       Krawczyk, H., "HMAC: Keyed-Hashing for Message
+             Authentication", RFC 2104, February 1997.
+
+[PKCS1]      Kalinski, B., and Staddon, J., "PKCS #1 RSA Cryptography
+             Specifications, Version 2.0", RFC 2437, October 1998.
+
+[RFC2119]    Bradner, S., "Key Words for use in RFCs to Indicate
+             Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+
+.ti 0
+7 Author's Address
+
+.nf
+Pekka Riikonen
+Snellmanninkatu 34 A 15
+70100 Kuopio
+Finland
+
+EMail: priikone@silcnet.org
+
+This Internet-Draft expires XXX
diff --git a/doc/example_silc.conf b/doc/example_silc.conf
deleted file mode 100644 (file)
index 64574a9..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# Configured ciphers.
-#
-# Format: <name>:<module path>:<key length>:<block length>
-#
-# If the cipher is builtin the <module path> maybe omitted.
-#
-[cipher]
-twofish-cbc:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-rc6-cbc:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-mars-cbc:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-#
-# Configured hash functions.
-#
-# Format: <name>:<module path>:<block length>:<digest length>
-#
-# If the hash function is builtin the <module path> maybe omitted.
-#
-[hash]
-md5::64:16
-sha1::64:20
-
-#
-# Configured PKCS.
-#
-# Format: <name>:<module path>:<key length>
-#
-# NOTE: <module path> must be omitted as PKCS cannot be modules currently.
-#
-#[pkcs]
-#rsa::1024
-#dss::1024
-
-#
-# Configured connections to servers.
-#
-# Format: <remote host>:<auth type>:<auth data>:<port>
-#
-# <auth type> maybe `passwd' or `pubkey'.
-#
-[connection]
-#lassi.kuo.fi.ssh.com:passwd::1333
-
-#
-# Commands.  These are executed when SILC client is run.  Normal
-# SILC commands may be executed here.
-#
-# Format: <command>
-#
-[commands]
-/server silc.pspt.fi
diff --git a/doc/example_silc.conf.in b/doc/example_silc.conf.in
new file mode 100644 (file)
index 0000000..0820d9e
--- /dev/null
@@ -0,0 +1,68 @@
+#
+# Configured ciphers.
+#
+# Format: <name>:<module path>:<key length>:<block length>
+#
+# If the cipher is builtin the <module path> maybe omitted.
+#
+[cipher]
+aes-256-cbc:@MODULESDIR@/aes.sim.so:32:16
+aes-192-cbc:@MODULESDIR@/aes.sim.so:24:16
+aes-128-cbc:@MODULESDIR@/aes.sim.so:16:16
+twofish-256-cbc:@MODULESDIR@/twofish.sim.so:32:16
+twofish-192-cbc:@MODULESDIR@/twofish.sim.so:24:16
+twofish-128-cbc:@MODULESDIR@/twofish.sim.so:16:16
+mars-256-cbc:@MODULESDIR@/mars.sim.so:32:16
+mars-192-cbc:@MODULESDIR@/mars.sim.so:24:16
+mars-128-cbc:@MODULESDIR@/mars.sim.so:16:16
+none:@MODULESDIR@/none.sim.so:0:0
+
+#
+# Configured hash functions.
+#
+# Format: <name>:<module path>:<block length>:<digest length>
+#
+# If the hash function is builtin the <module path> maybe omitted.
+#
+[hash]
+sha1::64:20
+md5::64:16
+
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# configured to the [hash] section.
+#
+# Format: <name>:<hash name>:<mac length>
+#
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20
+hmac-md5:md5:16
+
+#
+# Configured PKCS.
+#
+# Format: <name>
+#
+[pkcs]
+rsa
+
+#
+# Configured connections to servers.
+#
+# Format: <remote host>:<auth type>:<auth data>:<port>
+#
+# <auth type> maybe `passwd' or `pubkey'.
+#
+[connection]
+#lassi.kuo.fi.ssh.com:passwd::706
+
+#
+# Commands.  These are executed when SILC client is run.  Normal
+# SILC commands may be executed here.
+#
+# Format: <command>
+#
+[commands]
+/server silc.pspt.fi
diff --git a/doc/example_silcd.conf b/doc/example_silcd.conf
deleted file mode 100644 (file)
index fd67a4c..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-#
-# Configured ciphers.
-#
-# Format: <name>:<module path>:<key length>:<block length>
-#
-# If the cipher is builtin the <module path> maybe omitted.
-#
-[cipher]
-twofish-cbc:/home/priikone/silc/lib/silcsim/modules/twofish.sim.so:16:16
-rc6-cbc:/home/priikone/silc/lib/silcsim/modules/rc6.sim.so:16:16
-mars-cbc:/home/priikone/silc/lib/silcsim/modules/mars.sim.so:16:16
-none:/home/priikone/silc/lib/silcsim/modules/none.sim.so:0:0
-
-#
-# Configured hash functions.
-#
-# Format: <name>:<module path>:<block length>:<digest length>
-#
-# If the hash function is builtin the <module path> maybe omitted.
-#
-[hash]
-md5::64:16
-sha1::64:20
-
-#
-# Configured PKCS.
-#
-# Format: <name>:<module path>:<key length>
-#
-# NOTE: <module path> must be omitted as PKCS cannot be modules currently.
-#
-#[pkcs]
-#rsa::1024
-#dss::1024
-
-#
-# Server's administrative information.
-#
-# Format: <location>:<server type>:<admin's name>:<admin's email address>
-#
-[AdminInfo]
-Kuopio, Finland:Test Server:Pekka Riikonen:priikone@poseidon.pspt.fi
-
-#
-# Server information.
-#
-# Format: +<server FQDN>:<server IP>:<geographic location>:<port>
-#
-[ServerInfo]
-lassi.kuo.fi.ssh.com:10.2.1.6:Kuopio, Finland:1333
-
-#
-# Listenning ports.
-#
-# Format: <local IP/UNIX socket path>:<remote IP>:<port>
-#
-[ListenPort]
-10.2.1.6:10.2.1.6:1333
-
-#
-# Log files.
-#
-# This section is used to set various logging files, their paths
-# and maximum sizes. All the other directives except those defined
-# below are ignored in this section. Log files are purged after they
-# reach the maximum set byte size.
-#
-# Format: infologfile:<path>:<max byte size>
-#         warninglogile:<path>:<max byte size>
-#         errorlogile:<path>:<max byte size>
-#         fatallogile:<path>:<max byte size>
-#
-[Logging]
-infologfile:silcd.log:10000
-#warninglogfile:/var/log/silcd_warning.log:10000
-#errorlogfile:ERROR.log:10000
-#fatallogfile:/var/log/silcd_error.log:
-
-#
-# Connection classes.
-#
-# This section is used to define connection classes. These can be
-# used to optimize the server and the connections.#
-#
-# Format: <class number>:<ping freq>:<connect freq>:<max links>
-#
-[ConnectionClass]
-1:100:100:100
-2:200:300:400
-
-#
-# Configured client connections.
-#
-# Format: <remote host>:<auth method>:<auth data>:<port>:<class>
-#
-[ClientConnection]
-:::1333:1
-
-#
-# Configured server administrator connections
-#
-# Format: <host>:<auth method>:<auth data>:<nickname hash>:<class>
-#
-[AdminConnection]
-10.2.1.199:passwd:veryscret:XXX:1
-
-#
-# Configured server connections.
-#
-# If server connections are configured it means that our server is
-# router server.  Normal server must not configure server connections.
-# Thus, if your server is not router do not configure this section.  If
-# your server is router, this must be configured.
-#
-# Format: <remote host>:<auth method>:<auth data>:<port>:<version ID>:<vlass>
-#
-[ServerConnection]
-10.2.1.7:passwd:veryscret:1333:1:1
-
-#
-# Configured router connections.
-#
-# For normal server only one entry maybe configured to this section.  It
-# must be the router this server will be connected to.  For router server,
-# this sections includes all configured router connections.  The first
-# configured connection is the primary route.
-#
-# Format: <remote host>:<auth method>:<auth data>:<port>:<version ID>:<class>
-#
-[RouterConnection]
-10.2.1.100:passwd:veryverysecret:1333:1:1
-
-#
-# Denied connections.
-#
-# These connections are denied to connect our server.
-#
-# Format: <remote host/nickname>:<time interval>:<comment>:<port>
-#
-[DenyConnection]
-
-#
-# Redirected client connections.
-#
-# Clients will be redirected to these servers when our server is full.
-#
-# Format: <remote host>:<port>
-#
-[RedirectClient]
diff --git a/doc/example_silcd.conf.in b/doc/example_silcd.conf.in
new file mode 100644 (file)
index 0000000..afe74b3
--- /dev/null
@@ -0,0 +1,348 @@
+#
+# Example configuration file.  Note that this attempts to present various
+# configuration possibilities and may not actually give any sensible
+# configuration.  For real life example see the examples/ directory.
+#
+
+#
+# General configuration options
+#
+General {
+       # This is the default path where to search modules
+       # You can comment it out to use builtin modules globally.
+       ModulePath = "@MODULESDIR@";
+};
+
+#
+# Configured ciphers
+#
+# The "Module" option can be either absolute or relative to the "ModulePath"
+# option.
+# If commented out forces using of built-in modules.
+#
+cipher {
+       name = "aes-256-cbc";
+       module = "aes.sim.so";
+       key_length = 32;
+       block_length = 16;
+};
+cipher {
+       name = "aes-192-cbc";
+       module = "aes.sim.so";
+       key_length = 24;
+       block_length = 16;
+};
+cipher {
+       name = "aes-128-cbc";
+       module = "aes.sim.so";
+       key_length = 16;
+       block_length = 16;
+};
+cipher {
+       name = "twofish-256-cbc";
+       module = "twofish.sim.so";
+       key_length = 32;
+       block_length = 16;
+};
+cipher {
+       name = "twofish-192-cbc";
+       module = "twofish.sim.so";
+       key_length = 24;
+       block_length = 16;
+};
+cipher {
+       name = "twofish-128-cbc";
+       module = "twofish.sim.so";
+       key_length = 16;
+       block_length = 16;
+};
+cipher {
+       name = "mars-256-cbc";
+       module = "mars.sim.so";
+       key_length = 32;
+       block_length = 16;
+};
+cipher {
+       name = "mars-192-cbc";
+       module = "mars.sim.so";
+       key_length = 24;
+       block_length = 16;
+};
+cipher {
+       name = "mars-128-cbc";
+       module = "mars.sim.so";
+       key_length = 16;
+       block_length = 16;
+};
+cipher {
+       name = "none";
+       module = "none.sim.so";
+};
+
+#
+# Configured hash functions
+#
+hash {
+       name = "sha1";
+       block_length = 64;
+       digest_length = 20;
+};
+hash {
+       name = "md5";
+       block_length = 64;
+       digest_length = 16;
+};
+
+#
+# Configured HMAC functions. The hash function used in the HMAC must
+# be configured in the hash section.
+#
+hmac {
+       name = "hmac-sha1-96";
+       hash = "sha1";
+       mac_length = 12;
+};
+hmac {
+       name = "hmac-md5-96";
+       hash = "md5";
+       mac_length = 12;
+};
+hmac {
+       name = "hmac-sha1";
+       hash = "sha1";
+       mac_length = 20;
+};
+hmac {
+       name = "hmac-md5";
+       hash = "md5";
+       mac_length = 16;
+};
+
+#
+# Configured PKCS
+#
+PKCS { name = "rsa"; };
+
+#
+# Server information
+#
+ServerInfo {
+       #
+       # Server FQDN and IP address
+       #
+       hostname = "lassi.kuo.fi.ssh.com";
+       ip = "10.2.1.6";
+       port = 706;
+
+       #
+       # ServerType field specifies the purpose of this server
+       # This is only a descriptive field.
+       #
+       ServerType = "Test Server";
+
+       #
+       # Geographic location
+       #
+       Location = "Kuopio, Finland";
+
+       #
+       # Full admin name
+       #
+       Admin = "Pekka Riikonen";
+
+       #
+       # Admin's email address
+       #
+       EMail = "priikone@poseidon.pspt.fi";
+
+       #
+       # Run SILC server as specific user and group. The server must be initially
+       # run as root.
+       #
+       User = "nobody";
+       Group = "nobody";
+
+       #
+       # Public and private keys
+       #
+       PublicKey = "@ETCDIR@/silcd.pub";
+       PrivateKey = "@ETCDIR@/silcd.prv";
+
+       #
+       # Motd file
+       #
+       # Specifies the text file displayed on client connection
+       #
+       #MotdFile = "@ETCDIR@/motd.txt";
+
+       #
+       # Pid file
+       #
+       PidFile = "@PIDFILE@";
+};
+
+#
+# Log files.
+#
+# This section is used to set various logging files, their paths, maximum
+# sizes and logging options.
+# There are only four defined channels allowed for defining (see below).
+# The log channels have an importance value, and most important channels
+# are redirected on the less important ones, thus setting a valid logging
+# file for "infologfile" will ensure logging for all channels, while setting
+# logging file for "errorlogfile" will ensure logging for channels "error"
+# and "fatal"
+#
+Logging {
+       #
+       # If QuickLogs is true, then the logging files will be updated
+       # real-time. This causes a bit more CPU and HDD activity, but
+       # reduces memory usage. (if unsure say true).
+       #
+       QuickLogs = false;
+
+       #
+       # (Only if QuickLogs is false)
+       # FlushDelay tells log files update delay in case you have chosen
+       # buffering output.
+       #
+       FlushDelay = 180;
+
+       Info {
+               File = "@LOGSDIR@/silcd.log";
+               Size = "50k";
+       };
+       Warnings {
+               File = "@LOGSDIR@/silcd_warnings.log";
+               Size = "50k";
+       };
+       Errors {
+               File = "@LOGSDIR@/silcd_errors.log";
+               Size = "50k";
+       };
+       Fatals {
+               File = "@LOGSDIR@/silcd_fatals.log";
+               Size = "50k";
+       };
+};
+
+#
+# Connection classes (UNSUPPORTED)
+#
+# This section is used to define connection classes. These can be
+# used to optimize the server and the connections.
+#
+#Class {
+#      Name = "norm";
+#      Ping = 100;
+#      Connect = 100;
+#      Links = 100;
+#};
+
+#
+# Configured client connections.
+#
+# All fields except Class are optional.  Omitted fields are assumed
+# to be generic (e.g. if the "Host" field is omitted all hosts will match
+# this client class).
+#
+#Client {
+#      Host = "127.0.0.1";
+#      Port = 706;
+#      Class = "local";
+#};
+Client {
+       Port = 706;
+       Class = "norm";
+};
+
+#
+# Configured server administrator connections
+#
+# The fields "Host", "User", and "Nick", are optional but you are encouraged
+# in using them to better identify your admins.
+# "AuthMethod" and "AuthData" fields are mandatory.  The "AuthMethod" field
+# can be either the special string "passwd" or "pubkey" to identify the type
+# of data specified by "AuthData".
+#
+Admin {
+       Host = "10.2.1.199";
+       User = "priikone";
+       Nick = "pekka";
+       AuthMethod = "passwd";
+       AuthData = "verysecret";
+};
+
+#
+# Denied connections
+#
+# These connections are denied to connect to our server.
+#
+# The "Reason" field is mandatory, while the "Host" and "Port" fields can be
+# omitted to match everything.
+#
+#Deny {
+#      Host = "10.2.1.99";
+#      Port = 706;
+#      Reason = "Go away spammer";
+#};
+#Deny {
+#      Host = "10.3.*";
+#      Reason = "You are not welcome.";
+#};
+
+#
+# Configured server connections.
+#
+# If server connections are configured it means that this server is
+# router server.  Normal servers must not configure server connections.
+# Thus, if this server is not router do not configure this section.  If
+# your server is router, this must be configured.
+#
+# The "AuthData" option is either passphrase or file path to the public key
+# file. If the connection is backup connection then set the "Backup" option
+# to true. For normal connections set it false. If it is
+# set to true then this server will be backup router.
+#
+ServerConnection {
+       Host = "10.2.1.7";
+       AuthMethod = passwd;
+       AuthData = "verysecret";
+       Port = 706;
+       VersionID = 1;
+       Class = "norm";
+       Backup = false;
+};
+
+#
+# Configured router connections
+#
+# For normal servers only one entry maybe configured to this section.  It
+# must be the router this server will be connected to.  For router servers,
+# this section includes all configured router connections.  The first
+# configured connection is the primary route.
+#
+# The "AuthData" option is either passphrase or file path to the public key
+# file. If you are the initiator of the connection then set the "Initiator"
+# option to true.  If you are the responder of the connection (waiting for
+# incoming connection) then set it to false.
+#
+# If the connection is backup router connection then set the "BackupHost"
+# option to the IP address of the router that the backup router will
+# replace if it becomes unavailable.  Set also the router's port to the
+# "BackupPort" option.  For normal connection leave both commented. If this
+# backup router is in our cell then set the "LocalBackup" option to true.
+# If the backup router is in other cell then set it to false.
+#
+RouterConnection {
+       Host = "10.2.1.100";
+       AuthMethod = passwd;
+       AuthData = "verysecret";
+       Port = 706;
+       VersionID = 1;
+       Class = "norm";
+       Initiator = true;
+       #BackupHost = "10.2.1.6";
+       #BackupPort = 706;
+       #LocalBackup = true;
+};
diff --git a/doc/examples/README b/doc/examples/README
new file mode 100644 (file)
index 0000000..fa0b552
--- /dev/null
@@ -0,0 +1,34 @@
+This directory includes example files for a small SILC network.  The
+network consists of three (3) routers, and seven (7) servers.  One of
+the cell also has a backup router too.  The topology of the network is
+as follows:
+
+Cell 1:
+Router                212.146.42.250   cell1_router.conf
+ Backup router        212.146.42.100   cell1_backup.conf
+ Server1              212.146.42.101   cell1_server1.conf
+ Server2              212.146.42.102   cell1_server2.conf
+
+Cell 2:
+Router                212.146.42.251   cell2_router.conf
+ Server1              212.146.42.130   cell2_server1.conf
+ Server2              212.146.42.131   cell2_server2.conf
+
+Cell 3:
+Router                212.146.42.252   cell3_router.conf
+ Server1              212.146.42.150   cell3_server1.conf
+ Server2              212.146.42.151   cell3_server2.conf
+
+To make it simple all servers and routers use the same public and private
+keys.  They are the silcd.pub and silcd.prv in this directory.  Also, to
+make authentication simple all servers and routers authenticate themselves
+to other routers by simple password.
+
+Also note that the format in the configuration file is very raw so if you 
+need any help to understand what some setting means please check the
+documented example configuration files in doc/ directory.  Mainly, the
+example_silcd.conf.
+
+If you want to test this network you should change the IP addresses
+in the configuration file or perhaps set IP aliases for you local
+machine.
diff --git a/doc/examples/cell1_backup.conf b/doc/examples/cell1_backup.conf
new file mode 100644 (file)
index 0000000..00a076e
--- /dev/null
@@ -0,0 +1,65 @@
+#
+# CELL 1 Backup Router 212.146.42.100 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell1:Backup:Administrator:admin@cell1backup.com
+
+[ServerInfo]
+backup.cell1.com:212.146.42.100:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.100:212.146.42.100:706
+
+[Logging]
+infologfile:cell1_backup.log:
+warninglogfile:cell1_backup.log:
+errorlogfile:cell1_backup.log:
+fatallogfile:cell1_backup.log:
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+# backup connections
+212.146.42.101:passwd:priikone:706:1:1:1
+212.146.42.102:passwd:priikone:706:1:1:1
+
+[RouterConnection]
+# my primary
+212.146.42.250:passwd:priikone:706:1:1:1
+# backup connection to my primary's primary
+212.146.42.251:passwd:priikone:706:1:1:1:212.146.42.250:706
+# this use my primary as it's primary and me as backup
+212.146.42.252:passwd:priikone:706:1:1:0:212.146.42.250:706
+
+[DenyConnection]
diff --git a/doc/examples/cell1_router.conf b/doc/examples/cell1_router.conf
new file mode 100644 (file)
index 0000000..1c9e41a
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# CELL 1 Router 212.146.42.250 on port 706
+#
+# CELL 1 is:
+#
+# Router               212.146.42.250
+#  Backup router       212.146.42.100
+#  Server1             212.146.42.101
+#  Server2             212.146.42.102
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell1:Router:Administrator:admin@cell1router.com
+
+[ServerInfo]
+router.cell1.com:212.146.42.250:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.250:212.146.42.250:706
+
+[Logging]
+infologfile:cell1_router.log:
+warninglogfile:cell1_router.log:
+errorlogfile:cell1_router.log:
+fatallogfile:cell1_router.log:
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+212.146.42.101:passwd:priikone:706:1:1
+212.146.42.102:passwd:priikone:706:1:1
+
+[RouterConnection]
+# my primary
+212.146.42.251:passwd:priikone:706:1:1:1
+# this use me as primary
+212.146.42.252:passwd:priikone:706:1:1:0
+# our backup router (it will replace me if I'll go down)
+212.146.42.100:passwd:priikone:706:1:1:0:212.146.42.250:706:1
+
+[DenyConnection]
diff --git a/doc/examples/cell1_server1.conf b/doc/examples/cell1_server1.conf
new file mode 100644 (file)
index 0000000..7f0019b
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# CELL 1 Server 212.146.42.101 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell1:Server1:Administrator:admin@cell1server1.com
+
+[ServerInfo]
+server1.cell1.com:212.146.42.101:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.101:212.146.42.101:706
+
+[Logging]
+infologfile:cell1_server1.log:
+warninglogfile:cell1_server1.log:
+errorlogfile:cell1_server1.log:
+fatallogfile:cell1_server1.log:
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+
+[RouterConnection]
+# my primary
+212.146.42.250:passwd:priikone:706:1:1:1
+# our backup router in the cell
+212.146.42.100:passwd:priikone:706:1:1:1:212.146.42.250:706:1
+
+[DenyConnection]
diff --git a/doc/examples/cell1_server2.conf b/doc/examples/cell1_server2.conf
new file mode 100644 (file)
index 0000000..6037c84
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# CELL 1 Server 212.146.42.102 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell1:Server2:Administrator:admin@cell1server2.com
+
+[ServerInfo]
+server2.cell1.com:212.146.42.102:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.102:212.146.42.102:706
+
+[Logging]
+infologfile:cell1_server2.log:
+warninglogfile:cell1_server2.log:              
+errorlogfile:cell1_server2.log:
+fatallogfile:cell1_server2.log:       
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+
+[RouterConnection]
+# my primary
+212.146.42.250:passwd:priikone:706:1:1:1
+# our backup router in the cell
+212.146.42.100:passwd:priikone:706:1:1:1:212.146.42.250:706:1
+
+[DenyConnection]
diff --git a/doc/examples/cell2_router.conf b/doc/examples/cell2_router.conf
new file mode 100644 (file)
index 0000000..46168b9
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# CELL 2 Router 212.146.42.251 on port 706
+#
+# CELL 2 is:
+#
+# Router               212.146.42.251
+#  Server1             212.146.42.130
+#  Server2             212.146.42.131
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell2:Router:Administrator:admin@cell2router.com
+
+[ServerInfo]
+router.cell2.com:212.146.42.251:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.251:212.146.42.251:706
+
+[Logging]
+infologfile:cell2_router.log:
+warninglogfile:cell2_router.log:
+errorlogfile:cell2_router.log:
+fatallogfile:cell2_router.log:
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+212.146.42.130:passwd:priikone:706:1:1
+212.146.42.131:passwd:priikone:706:1:1
+
+[RouterConnection]
+# my primary
+212.146.42.252:passwd:priikone:706:1:1:1
+# this use me as primary
+212.146.42.250:passwd:priikone:706:1:1:0
+# this is the 212.146.42.250 router's backup router
+212.146.42.100:passwd:priikone:706:1:1:0:212.146.42.250:706:0
+
+[DenyConnection]
diff --git a/doc/examples/cell2_server1.conf b/doc/examples/cell2_server1.conf
new file mode 100644 (file)
index 0000000..76726f0
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# CELL 2 Server 212.146.42.130 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell2:Server1:Administrator:admin@cell2server1.com
+
+[ServerInfo]
+server1.cell2.com:212.146.42.130:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.130:212.146.42.130:706
+
+[Logging]
+infologfile:cell2_server1.log:
+warninglogfile:cell2_server1.log:              
+errorlogfile:cell2_server1.log:
+fatallogfile:cell2_server1.log:       
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+
+[RouterConnection]
+# my primary
+212.146.42.251:passwd:priikone:706:1:1:1
+
+[DenyConnection]
diff --git a/doc/examples/cell2_server2.conf b/doc/examples/cell2_server2.conf
new file mode 100644 (file)
index 0000000..9c01326
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# CELL 2 Server 212.146.42.131 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell2:Server2:Administrator:admin@cell2server2.com
+
+[ServerInfo]
+server2.cell2.com:212.146.42.131:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.131:212.146.42.131:706
+
+[Logging]
+infologfile:cell2_server2.log:
+warninglogfile:cell2_server2.log:              
+errorlogfile:cell2_server2.log:
+fatallogfile:cell2_server2.log:       
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+
+[RouterConnection]
+# my primary
+212.146.42.251:passwd:priikone:706:1:1:1
+
+[DenyConnection]
diff --git a/doc/examples/cell3_router.conf b/doc/examples/cell3_router.conf
new file mode 100644 (file)
index 0000000..d215f63
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# CELL 3 Router 212.146.42.252 on port 706
+#
+# CELL 3 is:
+#
+# Router               212.146.42.252
+#  Server1             212.146.42.150
+#  Server2             212.146.42.151
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell3:Router:Administrator:admin@cell3router.com
+
+[ServerInfo]
+router.cell3.com:212.146.42.252:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.252:212.146.42.252:706
+
+[Logging]
+infologfile:cell3_router.log:
+warninglogfile:cell3_router.log:
+errorlogfile:cell3_router.log:  
+fatallogfile:cell3_router.log:  
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+212.146.42.150:passwd:priikone:706:1:1
+212.146.42.151:passwd:priikone:706:1:1
+
+[RouterConnection]
+# my primary
+212.146.42.250:passwd:priikone:706:1:1:1
+# this use me as primary
+212.146.42.251:passwd:priikone:706:1:1:0
+# this is 212.146.42.250 router's (my primary's) backup router
+212.146.42.100:passwd:priikone:706:1:1:1:212.146.42.250:706:0
+
+
+[DenyConnection]
diff --git a/doc/examples/cell3_server1.conf b/doc/examples/cell3_server1.conf
new file mode 100644 (file)
index 0000000..a0b6953
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# CELL 3 Server 212.146.42.150 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell3:Server1:Administrator:admin@cell3server1.com
+
+[ServerInfo]
+server1.cell3.com:212.146.42.150:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.150:212.146.42.150:706
+
+[Logging]
+infologfile:cell3_server1.log:
+warninglogfile:cell3_server1.log:              
+errorlogfile:cell3_server1.log:
+fatallogfile:cell3_server1.log:       
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+
+[RouterConnection]
+# my primary
+212.146.42.252:passwd:priikone:706:1:1:1
+
+[DenyConnection]
diff --git a/doc/examples/cell3_server2.conf b/doc/examples/cell3_server2.conf
new file mode 100644 (file)
index 0000000..7f21928
--- /dev/null
@@ -0,0 +1,58 @@
+#
+# CELL 3 Server 212.146.42.151 on port 706
+#
+
+[Cipher]
+aes-256-cbc::32:16
+
+[Hash]
+md5::64:16
+sha1::64:20
+
+[hmac]
+hmac-sha1-96:sha1:12
+hmac-md5-96:md5:12
+hmac-sha1:sha1:20   
+hmac-md5:md5:16
+
+[PKCS]
+rsa
+
+[serverkeys]
+./silcd.pub:./silcd.prv
+
+[Identity]
+nobody:nobody
+
+[AdminInfo]
+Cell3:Server2:Administrator:admin@cell3server2.com
+
+[ServerInfo]
+server2.cell3.com:212.146.42.151:Kuopio, Finland:706
+
+[ListenPort]
+212.146.42.151:212.146.42.151:706
+
+[Logging]
+infologfile:cell3_server2.log:
+warninglogfile:cell3_server2.log:              
+errorlogfile:cell3_server2.log:
+fatallogfile:cell3_server2.log:       
+
+[ConnectionClass]
+1:100:100:100
+2:200:300:400
+
+[ClientConnection]
+:::706:1
+
+[AdminConnection]
+*:priikone:*:passwd:testi
+
+[ServerConnection]
+
+[RouterConnection]
+# my primary
+212.146.42.252:passwd:priikone:706:1:1:1
+
+[DenyConnection]
diff --git a/doc/examples/silcd.prv b/doc/examples/silcd.prv
new file mode 100644 (file)
index 0000000..71cadd0
Binary files /dev/null and b/doc/examples/silcd.prv differ
diff --git a/doc/examples/silcd.pub b/doc/examples/silcd.pub
new file mode 100644 (file)
index 0000000..057cf1e
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN SILC PUBLIC KEY-----
+AAAA5wADcnNhAFBVTj1wcmlpa29uZSwgSE49c2lsYy5yYWtldHRpLm5ldCwgUk49UGVra2E
+gUmlpa29uZW4sIEU9cHJpaWtvbmVAc2lsYy5yYWtldHRpLm5ldAAAAAQAAAB/AAAAgCVP8Q
+JzCCCC3DUhJlTNABfFqvBIe+BheiAtpHc5D5+2dHqzoAQy99gTFlRGfnnqLvcz1YzYPjVEn
++mbVEL6jddJZ2C1YwqzCGa5lG6dr4Un5QSU/uSgFoMY8wRjmfB1Cp/7/CgEFb20JeD/cS6s
+Tl86ElyTwi+NIwPMFePjlBkx
+-----END SILC PUBLIC KEY-----
diff --git a/doc/whitepaper/Makefile.am b/doc/whitepaper/Makefile.am
new file mode 100644 (file)
index 0000000..05fa94b
--- /dev/null
@@ -0,0 +1,33 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+HTML2PS = ../../scripts/html2ps -f ../../scripts/html2psrc
+
+all:   make-html2ps make-ps2pdf zip
+
+make-html2ps:
+       $(HTML2PS) -o silc_protocol.ps silc_protocol.html
+
+make-ps2pdf:
+       ps2pdf silc_protocol.ps
+
+zip:
+       gzip silc_protocol.ps
+       gzip silc_protocol.pdf
diff --git a/doc/whitepaper/silc_channel.edg b/doc/whitepaper/silc_channel.edg
new file mode 100644 (file)
index 0000000..0d948d9
--- /dev/null
@@ -0,0 +1,2685 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -56\r
+Y 203\r
+Scale 100\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 128,0,128\r
+Color9 238,232,238\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 255,255,0\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block short"\r
+LastEndLen 21\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 64\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A000000000000000000000000000000000000000000DC455D00\r
+}\r
+\r
+Preview 3292\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF44FF3F827FFF7FFF13FF03820204EEE8EE1E820205EEE8EE03827FFF7FFF\r
+13FF03820213EEE8EE03824EFF6CC057FF3F824BFF03FC03C066FC03C00FFF7F\r
+82538203FC03C030FC128203FC098218FC03C00CFF06827FFF4DFF038203FC03\r
+C066FC03C07FFF62FF03FC03C030FC0C8203FC068203FC038203FC038215FC03\r
+C07FFF62FF03FC03C006FC03821BFC03823FFC03C07FFF62FF03FC03C006FC0F\r
+8206FC0C8209FC098203FC038206FC128203FC068206FC03C07FFF62FF03FC03\r
+C066FC03C030FF12826CFF128221FF03FC03C033FC068203FC068203FC03821E\r
+FC03C030FF12826CFF128221FF03FC03C006FC1E8242FC03C030FF03820CDB03\r
+826CFF03820CDB038221FF03FC03C006FC1E820CFC128203FC098203FC06820F\r
+FC03C030FF12826CFF128221FF03FC03C066FC03C05AFF278260FF03FC03C033\r
+FC068203FC068203FC03821EFC03C05AFF038221F4038260FF03FC03C006FC1E\r
+8242FC03C02AFF278209FF0C8203F4068203F4038203F4038203F4038209FF27\r
+8230FF03FC03C006FC1E820CFC128203FC098203FC06820FFC03C00FFF158206\r
+FF038221F4038209FF068203F403820CF4038209F4038209FF038221F4038230\r
+FF03FC03C066FC03C00FFF0F8203DB038206FF0C8203F4068203F4038203F403\r
+8203F4038209FF038221F4038209FF0C8203F4068203F4038203F4038203F403\r
+820CFF15C003FF06C006FF03FC03C066FC03C00FFF03820FDB038206FF068203\r
+F403820CF4038209F4038209FF12820300128209FF068203F403820CF4038209\r
+F403820CFF03C006FF0CC003FF06C006FF03FC03C006FC12824EFC03C00FFF15\r
+8206FF038221F4038218FF06001BFF038221F403820CFF06C009FF06C003FF06\r
+C006FF03FC03C006FC03820CDB038206FC098206FC068206FC068203FC128203\r
+FC098206FC03C02AFF218206001BFF03001BFF0300248230FF03FC03C006FC03\r
+820CDB03824EFC03C04BFF090018FF030018FF060054FF03FC03C006FC12824E\r
+FC03C054FF030012FF060012FF06005AFF03FC03C006FC0FDB51FC03C024FF12\r
+821BFF0382030015820300128206005AFF03FC03C066FC03C024FF03820CDB03\r
+821BFF33825DFF03FC03C066FC03C024FF12821BFF03820207E6F2FF03820207\r
+E6F2FF038212FF278224FF03FC03C066FC03C024FF03820CDB03821BFF0C82FD\r
+E6F2FF1582FDE6F2FF0C8203000CFF0300038221F4038224FF03FC03C066FC03\r
+C024FF12821BFF03820207E6F2FF03820207E6F2FF0382150003F4098203F406\r
+8203F4038203F4068209FF158206FF03FC03C066FC03C051FF338212FF038203\r
+F4038203F403820CF4038206F4038209FF0F8203DB038206FF03FC6CC051FF03\r
+82020FE6F2FF038212FF038221F4038209FF03820FDB03827FFF47FF03820204\r
+E6F2FF0F820206E6F2FF038212FF278209FF15827FFF47FF2782060006827FFF\r
+72FF0204E1E3FFFEE1E3038207FF0206E1E3FF11FF068215FF09007FFF20FF12\r
+823DFFFEE1E30205FFF0F0FDFFE1E30382FDFFE1E30206FFF0F0FDFFE1E30382\r
+09FF098218FF06007EFF15820CFF12823AFFFEE1E30210FFF0F0FDFFE1E30382\r
+09FF068215FF248212FF12823FFF0F8203DB03820CFF03820CDB038237FFFEE1\r
+E30212FFF0F0FDFFE1E3038221FF03821EF4038212FF12823FFF03820FDB0382\r
+0CFF128234FFFEE1E30214FFF0F0FDFFE1E306821BFF0C8203F4068203F40382\r
+03F4068212FF03820CDB03823FFF158252FFFEE1E30214FFF0F0FDFFE1E30382\r
+1EFF068203F403820CF4038206F4038212FF12827BFF248207FFFEE1E30214FF\r
+F0F0FDFFE1E303821EFF03821EF403827FFF1DFF03F403821EF4038207FFFEE1\r
+E30215FFF0F00F8212FF24826CFF278209FF03F403821EF4038207FFFEE1E302\r
+15FFF0F00205FFE1E303827FFF20FF038221F4038209FF03F40C8203F4068203\r
+F4038203F406820AFFFEE1E30212FFF0F003820206FFF0F0FDFFE1E3038239FF\r
+128227FF15C003FF06C00CFF038221F4038209FF03F4068203F403820CF40382\r
+06F403820DFFFEE1E30219FFF0F0FDFFE1E3038236FF03820CDB038227FF03C0\r
+06FF0CC003FF06C00CFF038203F4098203F4068203F4038203F4068209FF03F4\r
+03821EF4038210FFFEE1E30205FFF0F009C0FDFFF0F012C003820209FFF0F0FD\r
+FFE1E3038233FF128227FF06C009FF06C006FF03C00CFF038203F4038203F403\r
+820CF4038206F4038209FF03F40F8206000F8213FFFEE1E30204FFF0F003C0FD\r
+FFF0F003C0FDFFF0F003C00204FFF0F003C0038203C00208FFF0F0FDFFE1E303\r
+8233FF03820CDB038251FF038221F403821EFF030022FFFEE1E3020EFFF0F003\r
+820209FFF0F0FDFFE1E3038233FF128251FF248206001BFF03001FFFFEE1E302\r
+0DFFF0F00382020BFFF0F0FDFFE1E303827FFF3BFF030003FF060012FF06001F\r
+FFFEE1E30208FFF0F00CC0038206C0020AFFF0F0FDFFE1E37FFF47FF060012FF\r
+03001FFFFEE1E30208FFF0F003C0FDFFF0F0038203C0038203C0020BFFF0F0FD\r
+FFE1E37FFF0EFF27820FFF338206FF06820204FFF0F00682FDFFF0F00982020E\r
+FFF0F0FDFFE1E37FFF11FF038221F403820FFF3982030009820215FFF0F0FDFF\r
+E1E37FFF14FF0C8203F4068203F4038203F4038203F4038203FF030009FF0300\r
+0208E6F2FF03820206E6F2FF038206FF0682FDFFE1E30214FFF0F0FDFFE1E303\r
+827FFF17FF068203F403820CF4038209F403821200FDE6F2FF1282FDE6F2FF09\r
+82FDE6F2FF0C820DFFFEE1E30214FFF0F0FDFFE1E3038203FF33C060FF038221\r
+F403820FFF03820208E6F2FF03820206E6F2FF038210FFFEE1E30213FFF0F0FD\r
+FFE1E306FF03C02DF403C060FF27820FFF338213FFFEE1E30211FFF0F0FDFFE1\r
+E309FF03C02DF403C07FFF17FF03820205E6F2FF0F820205E6F2FF038216FFFB\r
+E1E3FFE1E30206FFF0F0FDFFE1E30207FFF0F0FDFFE1E30CFF33C07FFF14FF06\r
+001582030018821CFF0206E1E3FF03FFFEE1E30205FFF0F0FDFFE1E30FFF03C0\r
+2DF403C04BFF128230FF060003FF030015FF060049FF0205E1E3FF11FF03C012\r
+F40C820FF403C04BFF03820CDB038209FF278206001BFF03006CFF33C04BFF12\r
+8209FF038221F4038221FF060069FF03C02DF403C04BFF03820CDB038209FF03\r
+8221F4038221FF03006CFF03C012F40C820FF403C04BFF128209FF038203F409\r
+8203F4068203F4038203F406820CFF27825DFF03C02DF403C006FF038203FF03\r
+8209FF0C8203FF098203FF038230FF038203F4038203F403820CF4038206F403\r
+820CFF038221F403825DFF33C066FF038221F403820CFF038203F4098203F406\r
+8203F4038203F406825DFF03C02DF403C006FF038203FF068203FF0C8203FF06\r
+8206FF038233FF27820CFF038203F4038203F403820CF4038206F403825DFF03\r
+C012F40C820FF403C07FFF1AFF038221F4038209FF15823FFF33C006FF098209\r
+FF038206FF068203FF12825DFF278209FF038203DB0F823FFF03C02DF403C060\r
+FF158254FF03820FDB03823FFF03C012F40C820FF403C006FF098203FF038206\r
+FF038203FF188227FF0F8203DB038254FF15823FFF03C02DF403C060FF03820F\r
+DB03827FFF29FF33C006FF098203FF0C8203FF038203FF0C822DFF15820FFF12\r
+827FFF08FF03C02DF403C07FFF05FF12827FFF08FF03C009F41E8206F403C006\r
+FF038203FF068203FF12825DFF03820CDB03827FFF08FF33C07FFF05FF12827F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF29FF03827FFF35FF03827FFF1AFF7F8238827F\r
+FF7FFF7FFF7FFF26FF3F827FFF7FFF13FF03820204F0FFF00382FDF0FFF01B82\r
+0204F0FFF003827FFF7FFF13FF03820213F0FFF003827FFF7FFF13FF3F827FFF\r
+7FFF7FFF7FFF7FFF5EFF03C003FF03C03FFF06C07FFF17FF06C003FF03C003FF\r
+06C006FF03C003FF06C003FF06C006FF03C003FF03C003FF09C006FF03C006FF\r
+03C006FF03C006FF0CC009FF09C003FF03C006FF06C003FF03C003FF03C006FF\r
+03C006FF03C07FFF14FF03C009FF03C003FF03C003FF03C003FF03C003FF06C0\r
+03FF0FC003FF1BC003FF06C006FF03C003FF03C003FF0CC003FF03C009FF12C0\r
+06FF0CC003FF03C07FFF17FF03C009FF06C006FF03C003FF06C003FF06C006FF\r
+03C003FF03C003FF0CC003FF03C006FF03C006FF03C006FF03C006FF06C003FF\r
+03C006FF03C00CFF03C003FF09C003FF03C003FF03C003FF09C003FF03C07FFF\r
+14FF06C003FF03C02AFF03C003FF03C006FF03C039FF06C00FFF06C07FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF63FFFE000000\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+FigureSymbol "box layer 6"\r
+{\r
+  FixedAspect FALSE\r
+  Height 768\r
+  Width 256\r
+  TextBox >50,16,<950,150\r
+  TextBox >50,182,<950,316\r
+  TextBox >50,348,<950,482\r
+  TextBox >50,516,<950,648\r
+  TextBox >50,682,<950,814\r
+  TextBox >50,848,<950,980\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Line 0,166 1000,166\r
+    Line 0,332 1000,332\r
+    Line 0,500 1000,500\r
+    Line 0,666 1000,666\r
+    Line 0,831 1000,831\r
+  }\r
+}\r
+\r
+FigureSymbol "bracket3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 48\r
+  Width 192\r
+  Outline\r
+  {\r
+    Polyline 23 0,1000 0,<957 >16,<871 >46,<789 >90,<713 >146,<646 >213,<590 >289,<546-\r
+     >371,<516 >457,500 475,500 500,0 525,500 <543,500 <629,<516 <711,<546-\r
+     <787,<590 <854,<646 <910,<713 <954,<789 <984,<871 1000,<957 1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "bracket4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 48\r
+  Width 192\r
+  Outline\r
+  {\r
+    Polyline 23 0,0 0,>43 >16,>129 >46,>211 >90,>287 >146,>354 >213,>410 >289,>454-\r
+     >371,>484 >457,500 475,500 500,1000 525,500 <543,500 <629,>484 <711,>434-\r
+     <787,>410 <854,>354 <910,>287 <954,>211 <984,>129 1000,>43 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "box divided 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,33,<950,300\r
+  TextBox >50,366,500,966\r
+  TextBox 500,366,<950,966\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Line 0,333 1000,333\r
+    Line 500,333 500,1000\r
+  }\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 192,1328,736,1744\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Text "Channel Xyz\sect Client\sect Client\sect Client\sect Client\sect -\r
+. . ."\r
+  Bounds 1280,672,1536,992\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "box layer 6"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 864,736,960,800\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 512,656,608,720\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 1600,1584,1696,1648\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 976,1584,1072,1648\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "SILC Network Cloud"\r
+  Text "SILC\line Network"\r
+  Bounds 864,864,1312,1312\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 14\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 368,800,560,896\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 624,768,816,864\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 336,944,528,1040\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 400,1072,592,1168\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 640,1104,832,1200\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 13\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 1424,1456,1616,1552\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 14\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 1344,1168,1536,1264\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 15\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 944,1456,1136,1552\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 16\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 1184,1488,1376,1584\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 17\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 1488,1312,1680,1408\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 18\r
+{\r
+  Style "SILC Server"\r
+  Text "Channel Key"\r
+  Bounds 512,528,832,592\r
+  BorderColor 130,130,130\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 19\r
+{\r
+  Style "SILC Server"\r
+  Text "Channel Key"\r
+  Bounds 1168,1728,1488,1792\r
+  BorderColor 130,130,130\r
+  FillColor 238,232,238\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 20\r
+{\r
+  Text ""\r
+  Bounds 208,592,1136,624\r
+  BorderColor 130,130,130\r
+  FillColor 130,130,130\r
+  BorderWidth 4\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "bracket3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 21\r
+{\r
+  Text ""\r
+  Bounds 800,1696,1856,1728\r
+  BorderColor 130,130,130\r
+  BorderWidth 4\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "bracket4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 22\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 912,1360,1008,1424\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 23\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 816,1472,912,1536\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 1568,1088,1664,1152\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 25\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 1616,1200,1712,1264\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 26\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 1728,1312,1824,1376\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 27\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 336,704,432,768\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 28\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 224,848,320,912\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 29\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 512,1216,608,1280\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 30\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Cell A"\r
+  Bounds 186,1088,358,1161\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 18\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 31\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Cell B"\r
+  Bounds 1675,1463,1847,1536\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 18\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 32\r
+{\r
+  Style "SILC Client"\r
+  Text ""\r
+  Bounds 240,1424,336,1488\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 33\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Client on the Channel Xyz"\r
+  Bounds 368,1447,698,1479\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 34\r
+{\r
+  Style "SILC Server"\r
+  Text ""\r
+  Bounds 240,1520,400,1552\r
+  BorderColor 130,130,130\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 35\r
+{\r
+  Style "SILC Server"\r
+  Text ""\r
+  Bounds 240,1584,400,1616\r
+  BorderColor 130,130,130\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 36\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Channel key on \line the Cell A\line Channel key on\line the Cell-\r
+ B\line Router connection\line secured with \line session key"\r
+  Bounds 459,1509,691,1727\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00A24052\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 37\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 240,1648\r
+  EndPoint2 400,1648\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 21\r
+  End2Length 21\r
+  PenStyle 21\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Figure 38\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Channel Message Delivery"\r
+  Bounds 591,421,1481,503\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 39\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 352,1200,448,1264\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 40\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "SILC channel\line containing all joined \line clients. A message\-\r
+line sent to the channel\line will be delivered to\line all of th-\r
+ese clients"\r
+  Bounds 1559,677,1817,864\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 41\r
+{\r
+  Text "Router\sect Decypt\line message\sect Encrypt\line message"\r
+  Bounds 608,912,864,1040\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "box divided 3"\r
+  TypeSize 7\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 42\r
+{\r
+  Text "Router\sect Encrypt\line message\sect Decrypt\line message"\r
+  Bounds 1136,1296,1392,1424\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "box divided 3"\r
+  TypeSize 7\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 43\r
+{\r
+  Text ""\r
+  Bounds 1097,1049,1112,1064\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 44\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 41\r
+  Figure2 43\r
+  EndPoint1 864,1004\r
+  EndPoint2 1104,1056\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 45\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 43\r
+  Figure2 42\r
+  EndPoint1 1104,1056\r
+  EndPoint2 1230,1296\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 46\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 9\r
+  Figure2 41\r
+  EndPoint1 725,864\r
+  EndPoint2 729,912\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 47\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 8\r
+  Figure2 41\r
+  EndPoint1 560,894\r
+  EndPoint2 608,915\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 48\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 10\r
+  Figure2 41\r
+  EndPoint1 528,986\r
+  EndPoint2 608,983\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 49\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 11\r
+  Figure2 41\r
+  EndPoint1 576,1072\r
+  EndPoint2 629,1040\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 50\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 12\r
+  Figure2 41\r
+  EndPoint1 736,1104\r
+  EndPoint2 736,1040\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 51\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 15\r
+  Figure2 42\r
+  EndPoint1 1115,1456\r
+  EndPoint2 1164,1424\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 52\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 16\r
+  Figure2 42\r
+  EndPoint1 1275,1488\r
+  EndPoint2 1270,1424\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 53\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 13\r
+  Figure2 42\r
+  EndPoint1 1434,1456\r
+  EndPoint2 1378,1424\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 54\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 17\r
+  Figure2 42\r
+  EndPoint1 1488,1360\r
+  EndPoint2 1392,1360\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 55\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 14\r
+  Figure2 42\r
+  EndPoint1 1381,1264\r
+  EndPoint2 1343,1296\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_network.edg b/doc/whitepaper/silc_network.edg
new file mode 100644 (file)
index 0000000..3198926
--- /dev/null
@@ -0,0 +1,2946 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -296\r
+Y 40\r
+Scale 100\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 128,0,128\r
+Color9 243,237,243\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 255,255,0\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block"\r
+LastEndLen 25\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 56\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A0000000000000000000000000000000000\r
+}\r
+\r
+Preview 4388\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF73FF7FC044C07FFF0EFF03C018FC338215FC098254FC\r
+03C07FFF0EFF03C018FC338272FC03C07FFF0EFF03C018FC338215FC068203FC\r
+06824EFC03C07FFF0EFF03C018FC338215FC068203FC068206FC128203FC0982\r
+03FC038224FC03C07FFF0EFF03C018FC338272FC03C07FFF0EFF03C018FC3382\r
+15FC098203FC03824EFC03C07FFF0EFF03C018FC338272FC03C07FFF0EFF03C0\r
+18FC0210E6F2FF75FC03C07FFF0EFF03C018FC36006FFC03C07FFF0EFF03C01B\r
+FC03002AFC030015FC068203FC068209FC038203FC098203FC038206FC0F8203\r
+FC068212FC03C07FFF0EFF03C018FC0C8203FC128203FC068203FC0C820FFC09\r
+8203FC038209FC038203FC098203FC038206FC0F8203FC068212FC03C07FFF0E\r
+FF03C01EFC038227FC038272FC03C07FFF0EFF03C07FFC3EFC03C07FFF0EFF7F\r
+C044C07FFF0EFF7FC044C07FFF05FF09C0FAF5F1F5F5F1F57FFF38FFFAF5F1F5\r
+F5F1F509C075FF06C00203F5F1F506820242F5F1F506C06CFF03C00206F5F1F5\r
+06820243F5F1F503C066FF03C00207F5F1F50382FDF5F1F50382021BF5F1F50F\r
+820223F5F1F503C063FF03C00225F5F1F50F820204F5F1F512820219F5F1F503\r
+C060FF0982FDF5F1F50F82FDF5F1F50382FDF5F1F50F82FDF5F1F50682FDF5F1\r
+F503820211F5F1F5038209DB03820204F5F1F51282020AF5F1F51282020AF5F1\r
+F503C05DFF03C00203F5F1F50382FDF5F1F50382FDF5F1F503820203F5F1F509\r
+820203F5F1F506820213F5F1F50F820204F5F1F503820CDB0382020AF5F1F512\r
+82FAF5F1F5F5F1F51282FAF5F1F5F5F1F503C05DFF03C0020CF5F1F503820222\r
+F5F1F51282020AF5F1F503820CDB0382FAF5F1F5F5F1F512820203F5F1F55DFF\r
+03C0020EF5F1F50382020EF5F1F51282021CF5F1F51282FAF5F1F5F5F1F50382\r
+0CDB03820203F5F1F503C05AFF03C0020FF5F1F50382020DF5F1F512820224F5\r
+F1F512820203F5F1F503C05AFF03C00210F5F1F50382020CF5F1F503820CDB03\r
+82022CF5F1F503FF03C05AFF03C00211F5F1F50682020AF5F1F512820203F5F1\r
+F521820207F5F1F52182020CF5F1F503FF03C05AFF03C00213F5F1F503820212\r
+F5F1F503820209F0FFF003820207F5F1F503821BF40382020CF5F1F503FF03C0\r
+5AFF03C00215F5F1F503820210F5F1F50982FDF0FFF00682FDF0FFF00382FDF0\r
+FFF006820207F5F1F50C8203F4098203F406820203F5F1F512820203F5F1F503\r
+FF03C05AFF03C00218F5F1F50382020DF5F1F50382FDF0FFF003820204F0FFF0\r
+0382FAF0FFF0F0FFF003820207F5F1F5068203F4038209F4038206F403820203\r
+F5F1F512820203F5F1F503FF03C05AFF03C0021AF5F1F50382020BF5F1F52182\r
+0207F5F1F521820203F5F1F512820203F5F1F503FF03C05AFF03C0021DF5F1F5\r
+0382FDF5F1F506820205F5F1F50208F0FFF006000208F5F1F506F4030015F402\r
+04F5F1F50FDB0204F5F1F503FF03C05AFF03C00222F5F1F50C820209F5F1F506\r
+000207F5F1F506000215F5F1F503FF03C05AFF03C00226F5F1F50682FDF5F1F5\r
+39820213F5F1F503FF03C05AFF03C0021DF5F1F52182FAF5F1F5F5F1F5098202\r
+0EE6F2FF03820203F5F1F524820204F5F1F503FF03C05AFF03C0021DF5F1F503\r
+821BF403820204F5F1F50382020EE6F2FF03820203F5F1F503821EF403820204\r
+F5F1F503FF03C05AFF03C0021DF5F1F50C8203F4068203F4038203F403820F00\r
+FAE6F2FFE6F2FF0F82FDE6F2FF0682FDE6F2FF0382FAE6F2FFE6F2FF03820C00\r
+098203F4068203F4038203F406820204F5F1F503FF03C05AFF03C0021DF5F1F5\r
+068203F403820CF4038203F40382FDF5F1F50300FAF5F1F5F5F1F50300FAE6F2\r
+FFE6F2FF09820203E6F2FF06820204E6F2FF03820300FAF5F1F5F5F1F5030003\r
+8203F403820CF4038206F403820204F5F1F503FF03C05AFF03C0021DF5F1F503\r
+821BF403820204F5F1F50382020EE6F2FF03820203F5F1F503821EF403820204\r
+F5F1F503FF03C05AFF03C00206F5F1F50F82FDF5F1F50682020FF5F1F52182FA\r
+F5F1F5F5F1F50982020EE6F2FF03820203F5F1F524820204F5F1F503FF03C05A\r
+FF03C00206F5F1F50F82021AF5F1F50382FDF5F1F50382FDF5F1F50682FDF5F1\r
+F5098206001582030009820213F5F1F503FF03C05AFF03C00222F5F1F50382FD\r
+F5F1F503820209F5F1F50300FDF5F1F503000207F5F1F50300FDF5F1F5030002\r
+14F5F1F503FF03C05AFF03C0021FF5F1F50682020DF5F1F506000209F5F1F506\r
+000214F5F1F503FF03C05AFF03C0021DF5F1F503820208F5F1F521820206F5F1\r
+F52182020DF5F1F503FF03C05AFF03C0020AF5F1F512820216F5F1F503821BF4\r
+03820206F5F1F503820209F0FFF00382020DF5F1F503FF03C05AFF03C0020AF5\r
+F1F512820216F5F1F5098203F4068203F4038203F406820206F5F1F50C82FDF0\r
+FFF00982FDF0FFF00682020DF5F1F503FF03C05AFF03C0020AF5F1F503820CDB\r
+0382020CF5F1F503820209F5F1F5038203F403820CF4038206F403820206F5F1\r
+F50682FDF0FFF003820203F0FFF00382FAF0FFF0F0FFF00382020DF5F1F503FF\r
+03C05AFF03C0020AF5F1F51282020CF5F1F503820209F5F1F503821BF4038202\r
+06F5F1F503820209F0FFF00382020DF5F1F503FF03C05AFF03C0021CF5F1F503\r
+820209F5F1F521820206F5F1F52182FAF5F1F5F5F1F512820205F5F1F503FF03\r
+C05AFF03C00211F5F1F50F820205F5F1F503820228F5F1F512820205F5F1F503\r
+FF03C05AFF03C00206F5F1F512820205F5F1F50F820205F5F1F503820228F5F1\r
+F503820CDB03820205F5F1F503FF03C05AFF03C00206F5F1F512820205F5F1F5\r
+038209DB03820205F5F1F50382021FF5F1F50F820204F5F1F512820205F5F1F5\r
+03FF03C05AFF03C00206F5F1F512820205F5F1F50F820225F5F1F50F82020FF5\r
+F1F503FF03C05AFF03C00206F5F1F50FDB0210F5F1F50382021FF5F1F50F8202\r
+0FF5F1F503FF03C05AFF03C0021BF5F1F50382021FF5F1F50CDB0207F5F1F512\r
+820203F5F1F503FF03C05AFF03C0020DF5F1F521820207F5F1F52182021CF5F1\r
+F512820203F5F1F503FF03C05AFF03C00203F5F1F512820204F5F1F503820209\r
+F0FFF003820203F5F1F503820203F5F1F503821BF40382021CF5F1F503820CDB\r
+03820203F5F1F503FF03C05AFF03C00203F5F1F512820204F5F1F51282FDF0FF\r
+F00382FDF0FFF006820204F5F1F50382FAF5F1F5F5F1F50C8203F4068203F403\r
+8203F403820215F5F1F50F82FAF5F1F5F5F1F512820203F5F1F503FF03C05AFF\r
+03C00203F5F1F503820CDB03820204F5F1F50682FDF0FFF003820203F0FFF003\r
+82FAF0FFF0F0FFF003820207F5F1F5068203F403820CF4038203F403820214F5\r
+F1F503DB0F82020BF5F1F503FF03C05AFF03C00203F5F1F512820204F5F1F503\r
+820209F0FFF003820204F5F1F50382FAF5F1F5F5F1F503821BF403820214F5F1\r
+F503DB038209DB0382020BF5F1F503FF03C05AFF03C0020DF5F1F51882060003\r
+820203F5F1F50682FAF5F1F5F5F1F50682030018820214F5F1F503DB0F82020B\r
+F5F1F503FF03C05AFF03C00215F5F1F509000203F5F1F50682FAF5F1F5F5F1F5\r
+0300FDF5F1F50300022DF5F1F503FF03C05AFF03C00216F5F1F506000203F5F1\r
+F503820203F5F1F50600022EF5F1F503FF03C05AFF03C00213F5F1F53082021F\r
+F5F1F50982FDF5F1F50382FDF5F1F506820205F5F1F503FF03C05AFF03C00204\r
+F5F1F524820203F5F1F50382020EE6F2FF03820204F5F1F521820210F5F1F509\r
+82FDF5F1F50382FDF5F1F506820205F5F1F503FF03C05AFF03C00204F5F1F503\r
+821EF403820203F5F1F50382020EE6F2FF03820204F5F1F503821BF40382021D\r
+F5F1F503FF03C05AFF03C00204F5F1F50C8203F4068203F4038203F406820900\r
+0382FAE6F2FFE6F2FF0F82FDE6F2FF0682FDE6F2FF0382FAE6F2FFE6F2FF0382\r
+FDF5F1F50900098203F4068203F4038203F40682021DF5F1F503FF03C05AFF03\r
+C00204F5F1F5068203F403820CF4038206F403820203F5F1F50382FAE6F2FFE6\r
+F2FF09820203E6F2FF06820204E6F2FF03820204F5F1F5038203F403820CF403\r
+8206F40382021DF5F1F503FF03C05AFF03C00204F5F1F503821EF403820203F5\r
+F1F50382020EE6F2FF03820204F5F1F503821BF40382021DF5F1F503FF03C05A\r
+FF03C00204F5F1F524820203F5F1F50382020EE6F2FF03820204F5F1F5218202\r
+03F5F1F512820214F5F1F503FF03C05AFF03C00213F5F1F50982060015820600\r
+06820212F5F1F512820214F5F1F503FF03C05AFF03C00215F5F1F509000204F5\r
+F1F50682FDF5F1F509000213F5F1F503820CDB03820214F5F1F503FF03C05AFF\r
+03C00215F5F1F506000204F5F1F50982FAF5F1F5F5F1F506000213F5F1F51282\r
+0214F5F1F503FF03C05AFF03C0020DF5F1F521820204F5F1F50382FDF5F1F521\r
+820226F5F1F503FF03C05AFF03C0020DF5F1F503821BF403820206F5F1F50382\r
+1BF403820226F5F1F503FF03C05AFF03C00203F5F1F512820204F5F1F5128203\r
+F4038203F406820203F5F1F50682FDF5F1F50C8203F4068203F4038203F40382\r
+0203F5F1F512820205F5F1F512820212F5F1F503FF03C05AFF03C00203F5F1F5\r
+12820204F5F1F5068203F4038209F4038206F403820203F5F1F50382FAF5F1F5\r
+F5F1F5068203F403820CF4038203F403820203F5F1F512820205F5F1F5128202\r
+12F5F1F503FF03C05AFF03C00203F5F1F512820204F5F1F521820203F5F1F503\r
+82FAF5F1F5F5F1F521820203F5F1F512820205F5F1F512820212F5F1F503FF03\r
+C05AFF03C0021BF5F1F503820233F5F1F503FF03C05AFF03C0021AF5F1F50382\r
+0217F5F1F512820217F5F1F503FF03C05AFF03C00205F5F1F51282021DF5F1F5\r
+0F820205F5F1F512820217F5F1F503FF03C05AFF03C00205F5F1F512820210F5\r
+F1F50382020CF5F1F50F820205F5F1F503820CDB03820217F5F1F503FF03C05A\r
+FF03C00205F5F1F503820CDB0382FAF5F1F5F5F1F512820208F5F1F50382020C\r
+F5F1F5038209DB03820205F5F1F512820217F5F1F503FF03C05AFF03C00205F5\r
+F1F51282FAF5F1F5F5F1F50C8203DB03820208F5F1F50382020CF5F1F50F8202\r
+22F5F1F503FF03C05AFF03C0020DF5F1F503820CDB03820209F5F1F503820232\r
+F5F1F503FF03C05AFF03C0020DF5F1F51282020BF5F1F50682022FF5F1F503FF\r
+03C05AFF03C00221F5F1F50982022BF5F1F503FF03C05AFF03C00224F5F1F509\r
+82FDF5F1F506820211F5F1F50982FDF5F1F50F82FDF5F1F50382FDF5F1F50F82\r
+FDF5F1F5068203FF03825AFF03C0022BF5F1F50382FDF5F1F50682FDF5F1F503\r
+82FDF5F1F50382020CF5F1F50382FDF5F1F50382FDF5F1F503820203F5F1F509\r
+820203F5F1F50682FDF5F1F503C05AFF03C00233F5F1F50382FDF5F1F50682FD\r
+F5F1F50982FDF5F1F503820212F5F1F506C05AFF03C0023DF5F1F50382FDF5F1\r
+F50382FAF5F1F5F5F1F50382FDF5F1F50682FDF5F1F50382FDF5F1F509820203\r
+F5F1F503C05DFF03C00249F5F1F50C82FAF5F1F5F5F1F503C05DFF03C0021BF5\r
+F1F503C0FAF5F1F5F5F1F506C0FDF5F1F503C0FDF5F1F503C0FDF5F1F503C0FD\r
+F5F1F50CC0FAF5F1F5F5F1F506C0FDF5F1F503C0FDF5F1F506C00216F5F1F503\r
+820204F5F1F503C060FF03C0021BF5F1F503C0FDF5F1F506C0FDF5F1F503C002\r
+03F5F1F51EC0FDF5F1F503C0FDF5F1F506C0021AF5F1F503C066FF03C00219F5\r
+F1F506C0FDF5F1F506C0FAF5F1F5F5F1F503C0FAF5F1F5F5F1F503C0FDF5F1F5\r
+03C0FAF5F1F5F5F1F503C00207F5F1F503C0021AF5F1F503C069FF03C0024BF5\r
+F1F503C06CFF06C00247F5F1F506C075FF7FC04DC0FDF5F1F506C07FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF23FFFE0000\r
+00000000\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Router to Router Prime"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 448,1536,1536,1792\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 2\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 608,1616\r
+  EndPoint2 880,1616\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Figure 3\r
+{\r
+  Style "SILC Background"\r
+  Text ""\r
+  Bounds 320,144,1664,1520\r
+  BorderColor 192,192,192\r
+  FillColor 245,241,245\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x01024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+  HasConnectionPoints TRUE\r
+  ConnectionPoint 1 99,71 ""\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 544,752,736,848\r
+  BorderColor 130,130,130\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 656,592,912,720\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 848,752,1040,848\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 976,608,1168,704\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 832,464,1024,560\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Server"\r
+  Text "Server"\r
+  Bounds 544,464,736,560\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 400,608,592,704\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 960,1248,1152,1344\r
+  BorderColor 130,130,130\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Style "Rectangle"\r
+  Text "Router"\r
+  Bounds 1072,1088,1328,1216\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 13\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 1264,1248,1456,1344\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 14\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 1392,1104,1584,1200\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 15\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 1248,960,1440,1056\r
+  BorderColor 130,130,130\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 16\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 960,960,1152,1056\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 17\r
+{\r
+  Style "Rectangle"\r
+  Text "Server"\r
+  Bounds 816,1104,1008,1200\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 18\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 416,368,512,432\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 19\r
+{\r
+  Text ""\r
+  Bounds 809,1017,824,1032\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 20\r
+{\r
+  Figure1 5\r
+  Figure2 19\r
+  EndPoint1 790,720\r
+  EndPoint2 816,1024\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  BindToStyle FALSE\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 21\r
+{\r
+  Style "Flow Closed 2"\r
+  Figure1 19\r
+  Figure2 12\r
+  EndPoint1 816,1024\r
+  EndPoint2 1072,1109\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 22\r
+{\r
+  Text ""\r
+  Bounds 665,1289,680,1304\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 23\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 22\r
+  Figure2 -1\r
+  EndPoint1 672,1296\r
+  EndPoint2 448,1488\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 24\r
+{\r
+  Style "Flow Closed 2"\r
+  Figure1 12\r
+  Figure2 22\r
+  EndPoint1 1072,1187\r
+  EndPoint2 672,1296\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 25\r
+{\r
+  Text ""\r
+  Bounds 841,329,856,344\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 26\r
+{\r
+  Style "Flow Closed 2"\r
+  Figure1 5\r
+  Figure2 25\r
+  EndPoint1 797,592\r
+  EndPoint2 848,336\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 27\r
+{\r
+  Style "Flow Closed 2"\r
+  Figure1 25\r
+  Figure2 -1\r
+  EndPoint1 848,336\r
+  EndPoint2 1600,240\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 28\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Another Router\line \line \line \line \line \line \line \line \li-\r
+ne Cell B"\r
+  Bounds 1359,265,1644,736\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 29\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 10\r
+  Figure2 5\r
+  EndPoint1 592,656\r
+  EndPoint2 656,656\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 30\r
+{\r
+  Style "SILC Server To Router"\r
+  Figure1 9\r
+  Figure2 5\r
+  EndPoint1 688,560\r
+  EndPoint2 720,592\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 31\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 8\r
+  Figure2 5\r
+  EndPoint1 880,560\r
+  EndPoint2 848,592\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 32\r
+{\r
+  Figure1 7\r
+  Figure2 5\r
+  EndPoint1 976,656\r
+  EndPoint2 912,656\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Figure 33\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "SILC Network"\r
+  Bounds 758,174,1219,256\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 34\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 544,336,640,400\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x01024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+  HasConnectionPoints TRUE\r
+  ConnectionPoint 1 659,984 ""\r
+}\r
+\r
+Figure 35\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 384,464,480,528\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 36\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 992,368,1088,432\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 37\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1072,464,1168,528\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 38\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1168,384,1264,448\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 39\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1216,560,1312,624\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 40\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1376,752,1472,816\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 41\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1248,464,1344,528\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 42\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 384,768,480,832\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 43\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 432,880,528,944\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 44\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 608,896,704,960\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 45\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1312,864,1408,928\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 46\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1504,800,1600,864\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 47\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1472,912,1568,976\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 48\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1520,1360,1616,1424\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 49\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1392,1376,1488,1440\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 50\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1504,1248,1600,1312\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 51\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 816,1328,912,1392\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 52\r
+{\r
+  Style "SILC Client"\r
+  Text "Client"\r
+  Bounds 496,976,592,1040\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 53\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 1120,1392,1216,1456\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 54\r
+{\r
+  Style "Rectangle"\r
+  Text "Client"\r
+  Bounds 960,1408,1056,1472\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 55\r
+{\r
+  Figure1 4\r
+  Figure2 5\r
+  EndPoint1 688,752\r
+  EndPoint2 720,720\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 56\r
+{\r
+  Figure1 6\r
+  Figure2 5\r
+  EndPoint1 890,752\r
+  EndPoint2 856,720\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 57\r
+{\r
+  Figure1 16\r
+  Figure2 12\r
+  EndPoint1 1104,1056\r
+  EndPoint2 1136,1088\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 58\r
+{\r
+  Figure1 15\r
+  Figure2 12\r
+  EndPoint1 1296,1056\r
+  EndPoint2 1264,1088\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 59\r
+{\r
+  Figure1 14\r
+  Figure2 12\r
+  EndPoint1 1392,1152\r
+  EndPoint2 1328,1152\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 60\r
+{\r
+  Figure1 13\r
+  Figure2 12\r
+  EndPoint1 1306,1248\r
+  EndPoint2 1272,1216\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 61\r
+{\r
+  Figure1 11\r
+  Figure2 12\r
+  EndPoint1 1104,1248\r
+  EndPoint2 1136,1216\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 62\r
+{\r
+  Figure1 17\r
+  Figure2 12\r
+  EndPoint1 1008,1152\r
+  EndPoint2 1072,1152\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  BindToStyle FALSE\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 63\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 592,1584\r
+  EndPoint2 896,1584\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 64\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Router to Router Connection\line Server to Router Connection"\r
+  Bounds 1005,1568,1368,1631\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 65\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 592,1744,880,1776\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x01024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+  HasConnectionPoints TRUE\r
+  ConnectionPoint 1 704,250 ""\r
+}\r
+\r
+Figure 66\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 592,1712,880,1744\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x01024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+  HasConnectionPoints TRUE\r
+  ConnectionPoint 1 663,93 ""\r
+}\r
+\r
+Figure 67\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 592,1648,880,1680\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x01024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+  HasConnectionPoints TRUE\r
+  ConnectionPoint 1 611,312 ""\r
+}\r
+\r
+Figure 68\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 592,1680,880,1712\r
+  BorderColor 130,130,130\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x01024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+  HasConnectionPoints TRUE\r
+  ConnectionPoint 1 642,375 ""\r
+}\r
+\r
+Figure 69\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Router\line Server/Backup Router\line Server\line Client"\r
+  Bounds 1005,1651,1288,1776\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 70\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "\line \line \line \line \line \line Cell A\line \line \line \line-\r
+ \line \line \line Another Router"\r
+  Bounds 355,792,640,1451\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_packet.edg b/doc/whitepaper/silc_packet.edg
new file mode 100644 (file)
index 0000000..ddc4ab0
--- /dev/null
@@ -0,0 +1,2025 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -60\r
+Y 740\r
+Scale 120\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 128,0,128\r
+Color9 239,231,239\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 255,255,0\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block shorter"\r
+LastEndLen 36\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 56\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A0000000000000000000000000000000000\r
+}\r
+\r
+Preview 1780\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF22FF7FC06BC066FF03C07FFC65FC03C066FF03C015FC\r
+248212FC0C8203FC0C8203FC038203FC068209FC098203FC098203FC068203FC\r
+068203FC038203FC038203FC098203FC068203FC068215FC03C066FF03C015FC\r
+24827FFC2CFC03C066FF03C07FFC65FC03C066FF03C04BFC038206FC0F8203FC\r
+038203FC068206FC098206FC1B8206FC068203FC068203FC038203FC15820FFC\r
+03C066FF03C07FFC65FC03C066FF03C015FC248215FC0C8206FC068203FC0382\r
+03FC038203FC068203FC0F8203FC0F8203FC068203FC038203FC068206FC0382\r
+03FC038203FC128203FC098266FF03C015FC24827FFC2CFC03C066FF03C07FFC\r
+65FC03C066FF03C015FC248212FC038203FC068206FC0C8206FC0F8206FC0F82\r
+03FC0F8203FC068203FC038203FC038203FC128203FC098209FC03C066FF03C0\r
+7FFC65FC03C066FF03C015FC24827FFC2CFC03C066FF03C015FC248212FC0F82\r
+03FC068203FC068203FC038203FC068269FC03C066FF03C07FFC65FC03C066FF\r
+03C07FFC65FC03C066FF7FC06BC07FFF7FFF7FFF7FFF56FF030003FF060006FF\r
+09007FFF7FFF7FFF7FFF7FFF16FF09827FFF59FF6F8209FF6F8269FF03827FFF\r
+7FFF7FFF7FFF7FFF73FF12007FFF7FFF37FF060003FF0F0003FF06007FFF7FFF\r
+7FFF7FFF7FFF13FF06827FFF74FF578206FF54827FFF20FF03827FFF7FFF5EFF\r
+120003FF0C0021FF120003FF090003FF030006FF030003FF060006FF0F007FFF\r
+7FFF7FFF7FFF17FF090003FF060003FF06003FFF090003FF060003FF06007FFF\r
+7FFF7FFF7FFF3EFFFDA0000054FFFD8000807FFF59FF0214A000000213800080\r
+03FF02128000807FFF7FFF7FFF7FFF7FFF5EFF7F827F824F8203FF038230F403\r
+8233F403820213EFE7EF03820226E6F2FF0382020FCCFCCC038203FF098206F4\r
+098203F4038203F4098203F4038203F4038203F4098206F4098203F4038203F4\r
+098203F41282FDEFE7EF0682FDEFE7EF0982FDEFE7EF0382FDEFE7EF0982FDEF\r
+E7EF06820209E6F2FF0382FAE6F2FFE6F2FF0382FDE6F2FF0982FDE6F2FF0382\r
+FDE6F2FF0C82FAE6F2FFE6F2FF0382020BE6F2FF03820204CCFCCC0682FDCCFC\r
+CC0C820204CCFCCC038203FF0C8203F4098203F4038203F4098203F4038203F4\r
+038203F4038203F4068203F4098203F4038203F4098203F40982FDEFE7EF0682\r
+FDEFE7EF0382FAEFE7EFEFE7EF0982FDEFE7EF0382FDEFE7EF0982FDEFE7EF06\r
+820209E6F2FF0682FDE6F2FF0382FDE6F2FF0382FDE6F2FF0982FDE6F2FF0682\r
+FDE6F2FF0382FAE6F2FFE6F2FF0382020BE6F2FF03820204CCFCCC0682FACCFC\r
+CCCCFCCC06820205CCFCCC038203FF7F827F824F827FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF75FF06C003FF03C075FF15C003FF06C006FF03C006FF03C006\r
+FF0CC006FF03C006FF06C003FF03C006FF03C003FF06C003FF03C003FF03C006\r
+FF03C006FF0CC006FF03C006FF06C003FF0CC003FF0FC006FF03C003FF03C003\r
+FF18C003FF03C05AFF09C006FF03C00CFF09C003FF03C003FF03C006FF06C003\r
+FF0CC009FF03C003FF03C003FF06C003FF03C003FF09C003FF03C003FF03C006\r
+FF06C003FF0CC006FF03C006FF03C003FF06C006FF03C003FF03C006FF03C003\r
+FF06C003FF06C003FF06C003FF03C054FF03C006FF06C006FF03C00CFF03C003\r
+FF03C003FF03C006FF03C003FF03C003FF03C003FF03C003FF06C006FF03C003\r
+FF06C006FF06C003FF03C003FF03C003FF03C006FF03C003FF03C003FF03C003\r
+FF03C003FF06C003FF03C006FF06C006FF03C003FF09C003FF09C003FF09C003\r
+FF03C003FF06C05AFF03C003FF06C009FF06C006FF06C015FF03C030FF03C003\r
+FF06C015FF03C018FF09C030FF03C07FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF5BFFFE00000000\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+FigureSymbol "bracket4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 48\r
+  Width 192\r
+  Outline\r
+  {\r
+    Polyline 23 0,0 0,>43 >16,>129 >46,>211 >90,>287 >146,>354 >213,>410 >289,>454-\r
+     >371,>484 >457,500 475,500 500,1000 525,500 <543,500 <629,>484 <711,>434-\r
+     <787,>410 <854,>354 <910,>287 <954,>211 <984,>129 1000,>43 1000,0\r
+  }\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 400,1488,1680,1776\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Style "Rectangle"\r
+  Text "IP Header"\r
+  Bounds 144,1056,432,1152\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Style "Rectangle"\r
+  Text "TCP Header"\r
+  Bounds 416,1056,736,1152\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "Rectangle"\r
+  Text "SILC Header"\r
+  Bounds 720,1056,1072,1152\r
+  BorderColor 130,130,130\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "Rectangle"\r
+  Text "Packet Data"\r
+  Bounds 1040,1056,1712,1152\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Style "Rectangle"\r
+  Text "MAC"\r
+  Bounds 1680,1056,1968,1152\r
+  BorderColor 130,130,130\r
+  FillColor 204,252,204\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Text ""\r
+  Bounds 720,1280,1680,1312\r
+  BorderColor 130,130,130\r
+  BorderWidth 4\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "bracket4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Text ""\r
+  Bounds 720,1168,1040,1200\r
+  BorderColor 0,0,160\r
+  BorderWidth 4\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "bracket4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Text ""\r
+  Bounds 1040,1168,1680,1200\r
+  BorderColor 128,0,128\r
+  FillColor 192,192,192\r
+  BorderWidth 4\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "bracket4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Encrypted\line (session key)"\r
+  Bounds 797,1211,964,1274\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Encrypted\line (session key or other keys)"\r
+  Bounds 1192,1213,1531,1276\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text ""\r
+  Bounds 1281,1310,1297,1326\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 13\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Authenticated\line (HMAC)"\r
+  Bounds 1115,1322,1292,1385\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 14\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "SILC Packet and Packet Encryption"\r
+  Bounds 345,914,1777,1012\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 24\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 15\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 544,1584,736,1616\r
+  BorderColor 130,130,130\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 16\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 544,1632,736,1664\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 17\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 544,1536,736,1568\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 18\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 544,1712,736,1744\r
+  BorderColor 130,130,130\r
+  FillColor 204,252,204\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 19\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Unencrypted area"\r
+  Bounds 835,1535,1062,1567\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 20\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "SILC Packet Header always encrypted with session key"\r
+  Bounds 835,1583,1536,1615\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 21\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Packet data area always encrypted either with session key\line or-\r
+ some other key (depends on the data payload)"\r
+  Bounds 835,1630,1573,1693\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 22\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "MAC generated from plaintext before encryption"\r
+  Bounds 835,1710,1440,1742\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 23\r
+{\r
+  Text ""\r
+  Bounds 720,1392,1968,1424\r
+  BorderColor 130,130,130\r
+  BorderWidth 4\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "bracket4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "SILC Packet"\r
+  Bounds 1264,1439,1423,1471\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_priv1.edg b/doc/whitepaper/silc_priv1.edg
new file mode 100644 (file)
index 0000000..2986b55
--- /dev/null
@@ -0,0 +1,2393 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -56\r
+Y 0\r
+Scale 109\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 253,254,194\r
+Color9 243,237,243\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 241,240,255\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block short"\r
+LastEndLen 25\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 68\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A00000000000000000000000000000000000000000000000000\r
+F0BA5900\r
+}\r
+\r
+Preview 3856\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF3BFF7FC05FC072FF03C01BFC24DB15FC0F8203FC0982\r
+03FC068203FC0C8203FC068203FC06820CFC098206FC038206FC098212FC03C0\r
+72FF03C01BFC24DB7FFC1AFC03C072FF03C07FFC59FC03C072FF03C01BFC24DB\r
+15FC0F8203FC098203FC068203FC0C8203FC068203FC068206FC038203FC0982\r
+06FC038206FC098212FC03C072FF03C01BFC24DB7FFC1AFC03C072FF03C07FFC\r
+59FC03C072FF03C01BFC24DB15FC0F8203FC098203FC068203FC0C8203FC0982\r
+06FC038203FC098203FC068203FC06821EFC03C072FF03C01BFC24DB7FFC1AFC\r
+03C072FF03C07FFC59FC03C072FF03C01BFC24DB15FC0F8203FC098203FC0682\r
+03FC0C8203FC098206FC038203FC098203FC068203FC068203FC038218FC03C0\r
+72FF03C01BFC24DB7FFC1AFC03C072FF03C07FFC59FC03C072FF7FC05FC03FFF\r
+21C012FF42C05AFF42C012FF21C006FF06C0FDF3EDF31BFFFDF3EDF306C009FF\r
+03C006F439FF03F406C04EFF06C003F439FF06F403C009FF06C0FDF3EDF31BFF\r
+FDF3EDF309C0020EF3EDF306FF03C04BF403C048FF03C04BF403C006FF03C002\r
+0EF3EDF303C027DB03C006FF03C048DB06F448FF03C04BDB03F406FF03C027DB\r
+FDF3EDF303C003DB020BF0FFF003DB03C006FF03C003DB020AF0FFF006DB020A\r
+FFFFC603DB03F403C048FF03C003DB020BC2FEFD06DB020AFFDFDF03DB03F403\r
+C003FF03C003DB020BFFDFDF03DB03FF03C003DB1282FDF0FFF00982FDF0FFF0\r
+03DB03C006FF03C003DB1282FDF0FFF0098206DB1282FDFFFFC6098203DB03F4\r
+03C048FF03C003DBFDC2FEFD1B82FDC2FEFD06DB1282FDFFDFDF098203DB03F4\r
+03C003FF03C003DBFDFFDFDF0F82FDFFDFDF0982FDFFDFDF03DB03FF03C027DB\r
+03C006FF03C048DB03F403C048FF03C04BDB03FF03C003FF03C027DB03FF03C0\r
+020CF0FFF0FDF3EDF303C006FF03C0020BF0FFF003F4020CFFFFC603F403C012\r
+FF068209FF09821EFF03C0020CC2FEFD03F4020BFFDFDF03F403FF03C003FF03\r
+C0020CFFDFDFFDF3EDF303FF03C027DB03C006FF03C048DB03F403C010FF0204\r
+E1E3FF03FF0203E1E3FFFEE1E303821BFF03C04BDB03FF03C003FF03C027DB03\r
+FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03\r
+F403C00DFFFEE1E30204FFF0F0FDFFE1E30204FFF0F0FDFFE1E3038218FF03C0\r
+03DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF\r
+03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F4\r
+03C009FF0382FDFFE1E30209FFF0F0FDFFE1E3068215FF03C003DB020BF3EDF3\r
+06DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF03C003DB21F406\r
+C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C00AFFFEE1E3020B\r
+FFF0F0FDFFE1E3038215FF06C0020BF3EDF306DB020AF3EDF303DB03FF03C003\r
+FF03C003DB03C01EF403DB03FF03C003DB1EF412C003DB020AF3EDF306DB020A\r
+F3EDF303DB12C0020BFFF0F0FDFFE1E303820CFF0FC00382020AF3EDF306DB02\r
+0AF3EDF303DB03FF0FC003821BF403DB03FF03C003DB1EF403C00FF403DB020A\r
+F3EDF306DB020AF3EDF303DB03C00FF4020BFFF0F0FDFFE1E3038209FF12F403\r
+C0020AF3EDF306DB020AF3EDF303DB12F403C01BF403DB03FF03C003DB06F409\r
+8203F4098203C012F403DBFDF3EDF30982FDF3EDF30982FAF3EDF3F3EDF306DB\r
+FAF3EDF3F3EDF30982FDF3EDF30982FDF3EDF303C012F4020CFFF0F00203FFE1\r
+E303FF12F403C0FDF3EDF30982FDF3EDF30982FAF3EDF3F3EDF306DBFAF3EDF3\r
+F3EDF31282FAF3EDF3F3EDF303DB12F403C0098203F4098206F403DB03FF03C0\r
+03DB1EF403C00FF403DB020AF3EDF306DB020AF3EDF303DB03C00FF4020FFFF0\r
+F0FDFFE1E312F403C0020AF3EDF306DB020AF3EDF303DB12F403C01BF403DB03\r
+FF03C003DB1EF415C0020AF3EDF306DB020AF3EDF303DB15C0020EFFF0F0FDFF\r
+E1E312C0020BF3EDF306DB020AF3EDF303DB12C01EF403DB03FF03C003DB21F4\r
+12C0020AF3EDF306DB020AF3EDF303DB03F412C0FDFFF0F009C0FDFFF0F015C0\r
+0203FFF0F0FDFFE1E30FC00382020AF3EDF306DB020AF3EDF303DB12C01EF403\r
+DB03FF03C003DB06F4098203F4098215F403C0FDF3EDF30982FDF3EDF30982FA\r
+F3EDF3F3EDF306DBFAF3EDF3F3EDF30982FDF3EDF30982FDF3EDF303DB12F403\r
+C0FDFFE1E303C0FDFFF0F003C0FDFFF0F003C00204FFF0F009C00202FFF0F0FD\r
+FFE1E303C00CF40382FDF3EDF30982FDF3EDF30982FAF3EDF3F3EDF306DBFAF3\r
+EDF3F3EDF31282FAF3EDF3F3EDF303DB03C00CF4038203F4098203F4098206F4\r
+03DB03FF03C003DB33F403C00209F3EDF306DB020AF3EDF303DB15F403C0020E\r
+FFF0F003C00FF40382020AF3EDF306DB020AF3EDF303C00FF403821EF403DB03\r
+FF03C003DB30F403C0020AF3EDF306DB020AF3EDF303DB12F403C0020FFFF0F0\r
+FDFFE1E303C00CF40382020AF3EDF306DB020AF3EDF303DB03C00CF403821EF4\r
+03DB03FF03C003DB1EF415C0020AF3EDF306DB020AF3EDF303DB15C00203FFF0\r
+F00CC0FDFFF0F006C00205FFF0F0FDFFE1E312C0020AF3EDF306DB020AF3EDF3\r
+03DB12C01EF403DB03FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306\r
+DB020AF3EDF303DB03F403C009FF03C0FDFFE1E30203FFF0F003C0FDFFF0F006\r
+C0FDFFF0F003C00206FFF0F0FDFFE1E303FF03C003FF03C003DB020BF3EDF306\r
+DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF03C003DB21F403DB\r
+03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C00DFFFEE1E302\r
+0FFFF0F00CFF03C003DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C0\r
+03DB21F403DB03FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB02\r
+0AF3EDF303DB03F403C00DFFFEE1E3020DFFF0F0FDFFE1E30FFF03C003DB020B\r
+F3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF03C00CDB\r
+03C006DB06C00CDB03C006FF03C048DB03F403C00DFFFEE1E3020DFFF0F0FDFF\r
+E1E30FFF03C04BDB03FF03C003FF03C00CDB06C003DB06C00CDB03FF03C00203\r
+F3EDF309C003FF06C00204F3EDF303C006FF03C04BF403C010FFFEE1E3020CFF\r
+F0F0FDFFE1E30FFF03C04BF403FF03C003FF03C00203F3EDF312C00204F3EDF3\r
+03FF03C00204F3EDF306C003FF06C00204F3EDF303C006FF03C04BF403C010FF\r
+FEE1E3020CFFF0F0FDFFE1E30FFF03C04BF403FF03C003FF03C00204F3EDF306\r
+C0FDF3EDF306C00204F3EDF303FF03C00204F3EDF306C003FF06C00204F3EDF3\r
+03C006FF03C04BF403C013FFFBE1E3FFE1E30203FFF0F00202FFE1E30204FFF0\r
+F0FDFFE1E312FF03C04BF403FF03C003FF03C00204F3EDF306C0FDF3EDF306C0\r
+0204F3EDF303FF03C00204F3EDF306C003FF06C00204F3EDF303C006FF03C04B\r
+F403C019FF0202E1E3FFFEE1E303820204FFF0F0FDFFE1E315FF03C04BF403FF\r
+03C003FF03C00204F3EDF306C0FDF3EDF306C00204F3EDF303FF03C00204F3ED\r
+F306C003FF06C00204F3EDF303C006FF03C04BF403C028FF0203E1E3FF17FF03\r
+C04BF403FF03C003FF03C00204F3EDF306C0FDF3EDF306C00204F3EDF303FF03\r
+C00204F3EDF30FC00204F3EDF303C006FF03C04BF403C048FF03C04BF403FF03\r
+C003FF03C00204F3EDF306C0FDF3EDF309C00203F3EDF303FF03C027DB03C006\r
+FF03C04BF403C048FF03C04BF403FF03C003FF03C027DB03FF03C003DBFDE6F2\r
+FF1882FAE6F2FFE6F2FF03DB03C006FF03C04BF403C048FF03C04BF403FF03C0\r
+03FF03C003DBFAE6F2FFE6F2FF1582FAE6F2FFE6F2FF03DB03FF03C003DB020B\r
+E6F2FF03DB03C006FF03C04BF403C048FF03C04BF403FF03C003FF03C003DB02\r
+0BE6F2FF03DB03FF03C027DB03C006FF03C04BF403C048FF03C04BF403FF03C0\r
+03FF03C027DB03FF03C0020DF3EDF303C006FF03C04BF403C048FF03C04BF403\r
+FF03C003FF03C0020DF3EDF303FF03C0020DF3EDF303C006FF03C012F4098203\r
+F4068203F4038203F4098215F403C048FF03C012F4098203F4068203F4038203\r
+F403821BF403FF03C003FF03C0020DF3EDF303FF03C00F82FDF3EDF30982FDF3\r
+EDF30682FDF3EDF303C006FF03C012F4038203F4038203F4038203F4068203F4\r
+098215F403C048FF03C012F4038203F4038203F4038203F4068203F4038203F4\r
+068215F403C003FF03C0FDF3EDF30C82FDF3EDF30682FDF3EDF30682FAF3EDF3\r
+F3EDF303FF03C00382FDF3EDF30982FDF3EDF30382FDF3EDF30382FAF3EDF3F3\r
+EDF30382FDF3EDF303C006FF03C04BF403C048FF03C04BF406C003FF03C0FDF3\r
+EDF30382FDF3EDF30682FDF3EDF30382FAF3EDF3F3EDF30982FDF3EDF303FF03\r
+C0020DF3EDF303C006FF03C04BF403C048FF03C04BF403C006FF03C0020DF3ED\r
+F306FFFDF3EDF321C00FFF45C003F403C04EFF48C003F40FFF21C07FFF7FFF7F\r
+FF7FFF7FFF7FFF5DFF03C003FF03C03FFF06C07FFF1DFF03C006FF03C003FF03\r
+C003FF03C003FF03C006FF09C009FF03C003FF09C006FF03C006FF03C006FF03\r
+C006FF0CC009FF09C003FF03C006FF06C003FF03C003FF03C006FF03C006FF03\r
+C07FFF1DFF0CC003FF06C009FF03C003FF03C003FF09C003FF1BC003FF06C006\r
+FF03C003FF03C003FF0CC003FF03C009FF12C006FF0CC003FF03C07FFF20FF03\r
+C003FF0FC003FF03C003FF03C003FF06C003FF03C006FF0CC003FF03C006FF03\r
+C006FF03C006FF03C006FF06C003FF03C006FF03C00CFF03C003FF09C003FF03\r
+C003FF03C003FF09C003FF03C07FFF1AFF06C009FF03C024FF03C006FF03C039\r
+FF06C00FFF06C07FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF1FFFFE0000007FFF7F\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 256\r
+  Width 128\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 256\r
+  Width 128\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+FigureSymbol "arrow right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 96\r
+  Width 256\r
+  TextBox 0,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 7 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 96\r
+  Width 256\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 7 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC Background"\r
+  Text "Server X"\r
+  Bounds 448,576,864,1216\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Style "SILC Background"\r
+  Text "Client A"\r
+  Bounds 192,576,416,1216\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Private Message Delivery"\r
+  Bounds 624,460,1472,542\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 208,832,400,1120\r
+  BorderColor 219,219,219\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "Rectangle"\r
+  Text "Session Key"\r
+  Bounds 208,1120,400,1184\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Text ""\r
+  Bounds 256,736,288,832\r
+  BorderColor 192,192,192\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "Arrow up"\r
+  Text ""\r
+  Bounds 304,736,336,832\r
+  BorderColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "Rectangle"\r
+  Text "Message"\r
+  Bounds 208,672,400,736\r
+  BorderColor 219,219,219\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Background"\r
+  Text "Client B"\r
+  Bounds 1696,576,1920,1216\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 1712,832,1904,1120\r
+  BorderColor 219,219,219\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Style "Rectangle"\r
+  Text "Session Key"\r
+  Bounds 1712,1120,1904,1184\r
+  BorderColor 219,219,219\r
+  FillColor 223,223,255\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Text ""\r
+  Bounds 1760,736,1792,832\r
+  BorderColor 192,192,192\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 13\r
+{\r
+  Style "Arrow up"\r
+  Text ""\r
+  Bounds 1808,736,1840,832\r
+  BorderColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 14\r
+{\r
+  Style "Rectangle"\r
+  Text "Message"\r
+  Bounds 1712,672,1904,736\r
+  BorderColor 219,219,219\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 15\r
+{\r
+  Style "Rectangle"\r
+  Text "Decrypt\line \line Encrypt"\r
+  Bounds 464,832,656,1120\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 16\r
+{\r
+  Style "Rectangle"\r
+  Text "Session Key"\r
+  Bounds 464,1120,656,1184\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 17\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 656,832,848,1120\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 18\r
+{\r
+  Style "Rectangle"\r
+  Text "Session Key"\r
+  Bounds 656,1120,848,1184\r
+  BorderColor 219,219,219\r
+  FillColor 198,255,255\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 19\r
+{\r
+  Style "SILC Background"\r
+  Text "Server Y"\r
+  Bounds 1248,576,1664,1216\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 20\r
+{\r
+  Style "Rectangle"\r
+  Text "Session Key"\r
+  Bounds 1264,1120,1456,1184\r
+  BorderColor 219,219,219\r
+  FillColor 253,254,194\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 21\r
+{\r
+  Style "Rectangle"\r
+  Text "Session Key"\r
+  Bounds 1456,1120,1648,1184\r
+  BorderColor 219,219,219\r
+  FillColor 223,223,255\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 22\r
+{\r
+  Style "Rectangle"\r
+  Text "Decrypt\line \line Encrypt"\r
+  Bounds 1456,832,1648,1120\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 23\r
+{\r
+  Style "SILC Network Cloud"\r
+  Text "SILC\line Network"\r
+  Bounds 912,720,1200,1136\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 480,1232,1632,1456\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 25\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 640,1264,832,1296\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 26\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 640,1312,832,1344\r
+  BorderColor 219,219,219\r
+  FillColor 223,223,255\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 27\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 1264,832,1456,1120\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 28\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Session key between Client A and Server X"\r
+  Bounds 926,1264,1480,1296\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 29\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Session key between Client B and Server Y"\r
+  Bounds 926,1312,1479,1344\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 30\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 640,1360,832,1392\r
+  BorderColor 219,219,219\r
+  FillColor 198,255,255\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 31\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 640,1408,832,1440\r
+  BorderColor 219,219,219\r
+  FillColor 253,254,194\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 32\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Session key between Server X and its router"\r
+  Bounds 926,1359,1491,1391\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 33\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Session key between Server Y and its router"\r
+  Bounds 926,1403,1491,1435\r
+  TextColor 130,130,130\r
+  TextFormat 0x0024\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 34\r
+{\r
+  Text ""\r
+  Bounds 384,880,480,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 35\r
+{\r
+  Text ""\r
+  Bounds 368,960,464,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 36\r
+{\r
+  Text ""\r
+  Bounds 848,880,944,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 37\r
+{\r
+  Text ""\r
+  Bounds 832,960,928,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 38\r
+{\r
+  Text ""\r
+  Bounds 1184,880,1280,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 39\r
+{\r
+  Text ""\r
+  Bounds 1184,960,1280,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 40\r
+{\r
+  Text ""\r
+  Bounds 1632,880,1728,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 41\r
+{\r
+  Text ""\r
+  Bounds 1648,960,1744,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_priv2.edg b/doc/whitepaper/silc_priv2.edg
new file mode 100644 (file)
index 0000000..15fa493
--- /dev/null
@@ -0,0 +1,2237 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -56\r
+Y 0\r
+Scale 109\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 253,254,194\r
+Color9 243,237,243\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 241,240,255\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block short"\r
+LastEndLen 25\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 76\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A00000000000000000000000000000000000000000000000000\r
+FDD3EBBF0000000078385900\r
+}\r
+\r
+Preview 3880\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF5AFF7FC06BC066FF03C015FC\r
+24DB18FC098203FC188203FC098203FC068203FC0C8203FC098206FC038203FC\r
+098203FC098203FC038218FC03C066FF03C015FC24DB7FFC2CFC03C066FF7FC0\r
+6BC07FFF7FFF7FFF0CFF21C012FF42C05AFF42C012FF21C006FF06C0FDF3EDF3\r
+1BFFFDF3EDF306C009FF03C006F439FF03F406C04EFF06C003F439FF06F403C0\r
+09FF06C0FDF3EDF31BFFFDF3EDF309C0020EF3EDF306FF03C04BF403C048FF03\r
+C04BF403C006FF03C0020EF3EDF303C027DB03C006FF03C04EF448FF03C04EF4\r
+06FF03C027DBFDF3EDF303C003DB0204F0FFF009820204F0FFF003DB03C006FF\r
+03C04BF403C048FF03C04EF403C003FF03C003DB0204F0FFF009820204F0FFF0\r
+03DB03FF03C003DB020BF0FFF003DB03C006FF03C04BF403C048FF03C04EF403\r
+C003FF03C003DB020BF0FFF003DB03FF03C027DB03C006FF03C04BF403C048FF\r
+03C04BF403FF03C003FF03C027DB03FF03C027DB03C006FF03C048DB03F403C0\r
+12FF068209FF09821EFF03C04BF403FF03C003FF03C027DB03FF03C003DB21F4\r
+03DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C010FF0204\r
+E1E3FF03FF0203E1E3FFFEE1E303821BFF03C04BDB03FF03C003FF03C003DB21\r
+F403DB03FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020AF3ED\r
+F303DB03F403C00DFFFEE1E30204FFF0F0FDFFE1E30204FFF0F0FDFFE1E30382\r
+18FF03C003DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F4\r
+03DB03FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF3\r
+03DB03F403C009FF0382FDFFE1E30209FFF0F0FDFFE1E3068215FF03C003DB02\r
+0BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF03C003\r
+DB21F406C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C00AFFFE\r
+E1E3020BFFF0F0FDFFE1E3038215FF06C0020BF3EDF306DB020AF3EDF303DB03\r
+FF03C003FF06C021F403DB03FF03C003DB1EF412C003DB020AF3EDF306DB020A\r
+F3EDF303DB12C0020BFFF0F0FDFFE1E303820CFF0FC00382020AF3EDF306DB02\r
+0AF3EDF303DB0FC003821EF403DB03FF03C003DB1EF403C00FF403DB020AF3ED\r
+F306DB020AF3EDF303DB03C00FF4020CFFF0F0098203FF12F403C0020AF3EDF3\r
+06DB020AF3EDF312F403C01EF403DB03FF03C003DB06F4098203F4098203C012\r
+F403DBFDF3EDF30682FDF3EDF30C82FAF3EDF3F3EDF306DBFAF3EDF3F3EDF306\r
+82FDF3EDF30C82FDF3EDF303C012F4020CFFF0F00203FFE1E3038212F403C002\r
+0AF3EDF306DB020AF3EDF312F403C003F4098203F4098206F403DB03FF03C003\r
+DB1EF415C0020AF3EDF306DB020AF3EDF303DB15C0020EFFF0F0FDFFE1E315C0\r
+FDF3EDF30682FDF3EDF30C82FAF3EDF3F3EDF306DBFAF3EDF3F3EDF31282FAF3\r
+EDF3F3EDF315C01EF403DB03FF03C003DB1EF409C006FF03C00382FDF3EDF318\r
+82FDF3EDF306DBFDF3EDF31882FDF3EDF303DB06C009FF03C00382020EFFF0F0\r
+FDFFE1E3038203C006FF06C0020BF3EDF306DB020AF3EDF303DB03FF03C003FF\r
+06C021F403DB03FF03C003DB21F412C0020AF3EDF306DB020AF3EDF303DB03F4\r
+12C0FDFFF0F009C0FDFFF0F015C00203FFF0F012C0FAF3EDF3F3EDF31882FDF3\r
+EDF306DBFDF3EDF31882FDF3EDF303DB0FC003821EF403DB03FF03C003DB06F4\r
+098203F4098215F403C0FAF3EDF3F3EDF30982FDF3EDF30982FDF3EDF306DBFA\r
+F3EDF3F3EDF30982FDF3EDF30982FDF3EDF303DB12F403C0FDFFE1E303C0FDFF\r
+F0F003C0FDFFF0F003C00204FFF0F009C0FAFFF0F0FFF0F003C00CF40382020B\r
+F3EDF306DB020AF3EDF303DB03C00CF4038203F4098203F4098206F403DB03FF\r
+03C003DB33F403C00209F3EDF306DB020AF3EDF303DB15F403C0020DFFF0F003\r
+C00FF403820203F3EDF31282FAF3EDF3F3EDF306DBFAF3EDF3F3EDF30982FDF3\r
+EDF30982FDF3EDF303C00FF403821EF403DB03FF03C003DB30F403C0020AF3ED\r
+F306DB020AF3EDF303DB12F403C0020FFFF0F003C00CF40382020BF3EDF306DB\r
+020AF3EDF303DB03C00CF403821EF403DB03FF03C003DB1EF415C0020AF3EDF3\r
+06DB020AF3EDF303DB15C00203FFF0F00CC0FDFFF0F006C00205FFF0F012C002\r
+0BF3EDF306DB020AF3EDF303DB12C01EF403DB03FF03C003DB21F403DB03C006\r
+FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C009FF03C0FDFFE1E302\r
+03FFF0F003C0FDFFF0F006C0FDFFF0F003C00206FFF0F0FDFFE1E303C006FF03\r
+C003DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03\r
+FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03\r
+F403C00DFFFEE1E3020FFFF0F00CFF03C003DB020BF3EDF306DB020AF3EDF303\r
+DB03FF03C003FF03C003DB21F403DB03FF03C003DB21F403DB03C006FF03C003\r
+DB020AF3EDF306DB020AF3EDF303DB03F403C00DFFFEE1E3020DFFF0F0FDFFE1\r
+E30FFF03C003DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21\r
+F403DB03FF03C00CDB03C006DB06C00CDB03C006FF03C048DB03F403C00DFFFE\r
+E1E3020DFFF0F0FDFFE1E30FFF03C003DB020BF3EDF306DB020AF3EDF303DB03\r
+FF03C003FF03C00CDB06C003DB06C00CDB03FF03C00203F3EDF309C0FDF3EDF3\r
+06C00204F3EDF303C006FF03C04BF403C010FFFEE1E3020CFFF0F0FDFFE1E30F\r
+FF03C04BDB03FF03C003FF03C00203F3EDF312C00204F3EDF303FF03C00204F3\r
+EDF306C0FDF3EDF306C00204F3EDF303C006FF03C04BF403C010FFFEE1E3020C\r
+FFF0F0FDFFE1E30FFF03C04BF403FF03C003FF03C00204F3EDF306C0FDF3EDF3\r
+06C00204F3EDF303FF03C00204F3EDF306C0FDF3EDF306C00204F3EDF303C006\r
+FF03C04BF403C013FFFBE1E3FFE1E30203FFF0F00202FFE1E30204FFF0F0FDFF\r
+E1E312FF03C04BF403FF03C003FF03C00204F3EDF306C0FDF3EDF306C00204F3\r
+EDF303FF03C00204F3EDF306C0FDF3EDF306C00204F3EDF303C006FF03C04BF4\r
+03C019FF0203E1E3FF03FF0204F0F0FFFEE1E315FF03C04BF403FF03C003FF03\r
+C00204F3EDF306C0FDF3EDF306C00204F3EDF303FF03C00204F3EDF30FC00204\r
+F3EDF303C006FF03C04BF403C028FF0203E1E3FF17FF03C04BF403FF03C003FF\r
+03C00204F3EDF306C0FDF3EDF309C00203F3EDF303FF03C00204F3EDF306C0FD\r
+F3EDF303C00205F3EDF303C006FF03C04BF403C048FF03C04BF403FF03C003FF\r
+03C00204F3EDF306C0FAF3EDF3F3EDF303C00204F3EDF303FF03C027DB03C006\r
+FF03C04BF403C048FF03C04BF403FF03C003FF03C027DB03FF03C003DBFDE6F2\r
+FF1882FAE6F2FFE6F2FF03DB03C006FF03C04BF403C048FF03C04BF403FF03C0\r
+03FF03C003DBFAE6F2FFE6F2FF1582FAE6F2FFE6F2FF03DB03FF03C003DB020B\r
+E6F2FF03DB03C006FF03C04BF403C048FF03C04BF403FF03C003FF03C003DB02\r
+0BE6F2FF03DB03FF03C027DB03C006FF03C04BF403C048FF03C04BF403FF03C0\r
+03FF03C027DB03FF03C0020DF3EDF303C006FF03C04BF403C048FF03C04BF403\r
+FF03C003FF03C0020DF3EDF303FF03C0020DF3EDF303C006FF03C012F4098203\r
+F4068203F4038203F4098215F403C048FF03C012F4098203F4068203F4038203\r
+F403821BF403FF03C003FF03C0020DF3EDF303FF03C00F82FDF3EDF30982FDF3\r
+EDF30682FDF3EDF303C006FF03C012F4038203F4038203F4038203F4068203F4\r
+098215F403C048FF03C012F4038203F4038203F4038203F4068203F4038203F4\r
+068215F403C003FF03C0FDF3EDF30C82FDF3EDF30682FDF3EDF30682FAF3EDF3\r
+F3EDF303FF03C00382FDF3EDF30982FDF3EDF30382FDF3EDF30382FAF3EDF3F3\r
+EDF30382FDF3EDF303C006FF03C04BF403C048FF03C04BF406C003FF03C0FDF3\r
+EDF30382FDF3EDF30682FDF3EDF30382FAF3EDF3F3EDF30982FDF3EDF303FF03\r
+C0020DF3EDF303C006FF03C04BF403C048FF03C04BF403C006FF03C00210F3ED\r
+F321C0FAF3EDF3F3EDF306FF03F445C003F403C04EFF48C006F406FFFAF3EDF3\r
+F3EDF321C00211F3EDF309FF48F454FF48F409FF020FF3EDF37FFF7FFF7FFF7F\r
+FF7FFF7FFF78FF03C003FF03C01EFF06C07FFF1DFF03C003FF03C003FF0CC003\r
+FF03C003FF03C006FF03C003FF03C003FF03C003FF03C006FF09C009FF03C003\r
+FF09C006FF03C006FF03C006FF03C006FF0CC006FF03C003FF03C006FF03C006\r
+FF03C07FFF1DFF03C009FF06C003FF03C003FF03C003FF0CC003FF06C009FF03\r
+C003FF03C003FF09C003FF1BC003FF06C006FF03C003FF03C003FF15C006FF09\r
+C07FFF1DFF03C003FF03C006FF0FC006FF03C003FF0FC003FF03C003FF03C003\r
+FF06C003FF03C006FF0CC003FF03C006FF03C006FF03C006FF03C006FF06C003\r
+FF03C003FF03C00FFF06C003FF03C07FFF17FF03C003FF03C003FF06C006FF03\r
+C009FF06C009FF03C024FF03C006FF03C003FF03C003FF03C02AFF03C003FF03\r
+C009FF06C07FFF20FF03C006FF03C003FF03C003FF03C003FF03C006FF09C009\r
+FF03C003FF09C006FF03C006FF03C006FF03C006FF0CC009FF09C003FF03C006\r
+FF09C003FF03C006FF03C006FF03C07FFF20FF0CC003FF06C009FF03C003FF03\r
+C003FF09C003FF1BC003FF06C006FF03C003FF03C003FF0CC003FF03C009FF0F\r
+C006FF0CC003FF03C07FFF23FF03C003FF0FC003FF03C003FF03C003FF06C003\r
+FF03C006FF0CC003FF03C006FF03C006FF03C006FF03C006FF06C003FF03C006\r
+FF03C00CFF03C003FF06C003FF03C003FF03C003FF09C003FF03C07FFF1DFF06\r
+C009FF03C024FF03C006FF03C039FF06C00FFF06C07FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF44\r
+FFFE000000FFFFFF\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 256\r
+  Width 128\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 256\r
+  Width 128\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+FigureSymbol "arrow right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 96\r
+  Width 256\r
+  TextBox 0,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 7 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 96\r
+  Width 256\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 7 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC Background"\r
+  Text "Server X"\r
+  Bounds 448,576,864,1216\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Style "SILC Background"\r
+  Text "Client A"\r
+  Bounds 192,576,416,1216\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Private Message Delivery\line With Private Message Key"\r
+  Bounds 607,384,1483,547\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 208,832,400,1120\r
+  BorderColor 219,219,219\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "Rectangle"\r
+  Text "Key"\r
+  Bounds 208,1120,400,1184\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Text ""\r
+  Bounds 256,736,288,832\r
+  BorderColor 192,192,192\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "Arrow up"\r
+  Text ""\r
+  Bounds 304,736,336,832\r
+  BorderColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "Rectangle"\r
+  Text "Message"\r
+  Bounds 208,672,400,736\r
+  BorderColor 219,219,219\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Background"\r
+  Text "Client B"\r
+  Bounds 1696,576,1920,1216\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 1712,832,1904,1120\r
+  BorderColor 219,219,219\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Text ""\r
+  Bounds 1760,736,1792,832\r
+  BorderColor 192,192,192\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Style "Arrow up"\r
+  Text ""\r
+  Bounds 1808,736,1840,832\r
+  BorderColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 13\r
+{\r
+  Style "Rectangle"\r
+  Text "Message"\r
+  Bounds 1712,672,1904,736\r
+  BorderColor 219,219,219\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 14\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 464,832,656,1120\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 15\r
+{\r
+  Style "SILC Background"\r
+  Text "Server Y"\r
+  Bounds 1248,576,1664,1216\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 16\r
+{\r
+  Style "SILC Network Cloud"\r
+  Text "SILC\line Network"\r
+  Bounds 912,720,1200,1136\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 17\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 448,1248,1664,1312\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 18\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 576,1264,768,1296\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 19\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Private Message Key between Client A and Client B"\r
+  Bounds 861,1266,1516,1298\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 20\r
+{\r
+  Style "Rectangle"\r
+  Text "Key"\r
+  Bounds 1712,1120,1904,1184\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 21\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 656,832,848,1120\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 22\r
+{\r
+  Text ""\r
+  Bounds 368,960,464,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 23\r
+{\r
+  Text ""\r
+  Bounds 384,880,480,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Text ""\r
+  Bounds 848,880,944,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 25\r
+{\r
+  Text ""\r
+  Bounds 832,960,928,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 26\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 1264,816,1456,1104\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 27\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 1456,816,1648,1104\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 28\r
+{\r
+  Text ""\r
+  Bounds 1168,880,1264,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 29\r
+{\r
+  Text ""\r
+  Bounds 1184,960,1280,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 30\r
+{\r
+  Text ""\r
+  Bounds 1632,880,1728,976\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 31\r
+{\r
+  Text ""\r
+  Bounds 1632,960,1728,1056\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_priv3.edg b/doc/whitepaper/silc_priv3.edg
new file mode 100644 (file)
index 0000000..cc5a2d0
--- /dev/null
@@ -0,0 +1,2302 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -152\r
+Y -27\r
+Scale 100\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 253,254,194\r
+Color9 243,237,243\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 241,240,255\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block short"\r
+LastEndLen 25\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 80\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A00000000000000000000000000000000000000000000000000\r
+40465D000C005900D01F5900D0F0FF00\r
+}\r
+\r
+Preview 3956\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF35FF7FC06BC066FF03C018FC24DB12FC098206FC0682\r
+06FC098203FC098203FC098209FC068203FC0C8203FC068233FC03C066FF03C0\r
+18FC24DB24FC03827FFCFEFCFC03C066FF03C07FFC65FC03C066FF03C018FC24\r
+DB12FC098206FC068206FC098203FC098203FC098209FC068203FC038203FC06\r
+8203FC068233FC03C066FF03C018FC24DB24FC03827FFCFEFCFC03C066FF03C0\r
+7FFC65FC03C066FF7FC06BC039FF21C012FF42C05AFF42C012FF21C006FF06C0\r
+FDF3EDF31BFFFDF3EDF306C009FF03C006F439FF03F406C04EFF06C003F439FF\r
+06F403C009FF06C0FDF3EDF31BFFFDF3EDF309C027DBFDF3EDF306FF03C04BF4\r
+03C048FF03C04BF403C006FF03C027DBFDF3EDF303C003DBFAC2FEFDC2FEFD0C\r
+82FDC2FEFD0982FDC2FEFD03DB03C006FF03C04EF448FF03C04EF406FF03C003\r
+DB0203F0FFF00982FDF0FFF00982FDF0FFF003DBFDF3EDF303C003DB020BC2FE\r
+FD03DB03C006FF03C04BF403C048FF03C04EF403C003FF03C003DB020BF0FFF0\r
+03DB03FF03C027DB03C006FF03C04BF403C048FF03C04EF403C003FF03C027DB\r
+03FF03C027DB03C006FF03C04BF403C048FF03C04BF403FF03C003FF03C027DB\r
+03FF03C003DB020BF0FFF003DB03C006FF03C04BF403C048FF03C04BF403FF03\r
+C003FF03C003DB020BC2FEFD03DB03FF03C003DBFAF0FFF0F0FFF00982FDF0FF\r
+F00982FAF0FFF0F0FFF003DB03C006FF03C04BF403C048FF03C04BF403FF03C0\r
+03FF03C003DB0203C2FEFD1282FAC2FEFDC2FEFD03DB03FF03C027DB03C006FF\r
+03C04BF403C012FF068209FF09821EFF03C04BF403FF03C003FF03C027DB03FF\r
+03C0020CF0FFF0FDF3EDF303C006FF03C04BF403C010FF0204E1E3FF03FF0203\r
+E1E3FFFEE1E303821BFF03C04BF403FF03C003FF03C0020CC2FEFDFDF3EDF303\r
+FF03C027DB03C006FF03C048DB03F403C00DFFFEE1E30204FFF0F0FDFFE1E302\r
+04FFF0F0FDFFE1E3038218FF03C04BF403FF03C003FF03C027DB03FF03C003DB\r
+21F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C009FF\r
+0382FDFFE1E30209FFF0F0FDFFE1E3068215FF03C04BDB03FF03C003FF03C003\r
+DB21F403DB03FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020A\r
+F3EDF303DB03F403C00AFFFEE1E3020BFFF0F0FDFFE1E3038215FF03C003DB02\r
+0BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF03C003\r
+DB21F406C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C00AFFFE\r
+E1E3020BFFF0F0FDFFE1E3038215FF06C0020BF3EDF306DB020AF3EDF303DB03\r
+FF03C003FF06C021F403DB03FF03C003DB1EF412C003DB020AF3EDF306DB020A\r
+F3EDF303DB12C0020BFFF0F0FDFFE1E303820CFF0FC00382020AF3EDF306DB02\r
+0AF3EDF303DB0FC003821EF403DB03FF03C003DB1EF403C00FF403DB020AF3ED\r
+F306DB020AF3EDF303DB03C00FF4020CFFF0F00203FFE1E303FF12F403C0020A\r
+F3EDF306DB020AF3EDF312F403C01EF403DB03FF03C003DB06F4098203F40982\r
+03C012F403DBFDF3EDF30682FDF3EDF30C82FAF3EDF3F3EDF306DBFAF3EDF3F3\r
+EDF30682FDF3EDF30C82FDF3EDF303C012F4020FFFF0F0FDFFE1E312F403C002\r
+0AF3EDF306DB020AF3EDF312F403C003F4098203F4098206F403DB03FF03C003\r
+DB1EF403C00FF403DB020AF3EDF306DB020AF3EDF303DB03C00FF4FDFFE1E302\r
+0EFFF0F0FDFFE1E312F403C0FDF3EDF30682FDF3EDF30C82FAF3EDF3F3EDF306\r
+DBFAF3EDF3F3EDF31282FAF3EDF3F3EDF312F403C01EF403DB03FF03C003DB1E\r
+F412C00382FDF3EDF31882FDF3EDF306DBFDF3EDF31882FDF3EDF303DB15C0FD\r
+FFF0F009C0FDFFF0F015C00203FFF0F012C0020BF3EDF306DB020AF3EDF312C0\r
+21F403DB03FF03C003DB21F412C0020AF3EDF306DB020AF3EDF303DB03F412C0\r
+FDFFE1E303C0FDFFF0F003C0FDFFF0F003C00204FFF0F009C00202FFF0F0FDFF\r
+E1E30FC00382FDF3EDF31882FDF3EDF306DBFDF3EDF31882FDF3EDF303DB0FC0\r
+03821EF403DB03FF03C003DB06F4098203F4098215F403C0FAF3EDF3F3EDF309\r
+82FDF3EDF30982FDF3EDF306DBFAF3EDF3F3EDF30982FDF3EDF30982FDF3EDF3\r
+03DB12F403C0020FFFF0F0FDFFE1E303C00CF40382020AF3EDF306DB020AF3ED\r
+F303DB03C00CF4038203F4098203F4098206F403DB03FF03C003DB33F403C002\r
+09F3EDF306DB020AF3EDF303DB15F403C0020EFFF0F003C00FF40382FAF3EDF3\r
+F3EDF31282FAF3EDF3F3EDF306DBFAF3EDF3F3EDF30982FDF3EDF30982FDF3ED\r
+F303C00FF403821EF403DB03FF03C003DB30F403C0020AF3EDF306DB020AF3ED\r
+F303DB12F403C00203FFF0F00CC0FDFFF0F006C00205FFF0F0FDFFE1E303C00C\r
+F40382020AF3EDF306DB020AF3EDF303DB03C00CF403821EF403DB03FF03C003\r
+DB1EF415C0020AF3EDF306DB020AF3EDF303DB15C00203FFF0F003C0FDFFF0F0\r
+06C0FDFFF0F003C00206FFF0F0FDFFE1E312C0020AF3EDF306DB020AF3EDF303\r
+DB12C01EF403DB03FF03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB\r
+020AF3EDF303DB03F403C009FF03C0FDFFE1E3020FFFF0F006FF03C003FF03C0\r
+03DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C003DB21F403DB03FF\r
+03C003DB21F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F4\r
+03C00DFFFEE1E3020DFFF0F0FDFFE1E30FFF03C003DB020BF3EDF306DB020AF3\r
+EDF303DB03FF03C003FF03C003DB21F403DB03FF03C003DB09F403C006F406C0\r
+09F403DB03C006FF03C003DB020AF3EDF306DB020AF3EDF303DB03F403C00DFF\r
+FEE1E3020DFFF0F0FDFFE1E30FFF03C003DB020BF3EDF306DB020AF3EDF303DB\r
+03FF03C003FF03C003DB09F406C003F406C009F403DB03FF03C009DB09C003FF\r
+06C00CDB03C006FF03C048DB03F403C010FFFEE1E3020CFFF0F0FDFFE1E30FFF\r
+03C003DB020BF3EDF306DB020AF3EDF303DB03FF03C003FF03C009DB12C00CDB\r
+03FF03C00204F3EDF306C003FF06C00204F3EDF303C006FF03C04BF403C010FF\r
+FEE1E3020CFFF0F0FDFFE1E30FFF03C04BDB03FF03C003FF03C00204F3EDF306\r
+C0FDF3EDF306C00204F3EDF303FF03C00204F3EDF306C003FF06C00204F3EDF3\r
+03C006FF03C04BF403C013FFFBE1E3FFE1E30203FFF0F00202FFE1E30204FFF0\r
+F0FDFFE1E312FF03C04BF403FF03C003FF03C00204F3EDF306C0FDF3EDF306C0\r
+0204F3EDF303FF03C00204F3EDF30FC00204F3EDF303C006FF03C04BF403C019\r
+FF0202E1E3FFFEE1E303820204FFF0F0FDFFE1E315FF03C04BF403FF03C003FF\r
+03C00204F3EDF306C0FDF3EDF309C00203F3EDF303FF03C027DB03C006FF03C0\r
+4BF403C028FF0203E1E3FF17FF03C04BF403FF03C003FF03C027DB03FF03C003\r
+DBFDE6F2FF1882FAE6F2FFE6F2FF03DB03C006FF03C04BF403C048FF03C04BF4\r
+03FF03C003FF03C003DBFAE6F2FFE6F2FF1582FAE6F2FFE6F2FF03DB03FF03C0\r
+03DB020BE6F2FF03DB03C006FF03C04BF403C048FF03C04BF403FF03C003FF03\r
+C003DB020BE6F2FF03DB03FF03C027DB03C006FF03C04BF403C048FF03C04BF4\r
+03FF03C003FF03C027DB03FF03C0020DF3EDF303C006FF03C04BF403C048FF03\r
+C04BF403FF03C003FF03C0020DF3EDF303FF03C0020DF3EDF303C006FF03C012\r
+F4098203F4068203F4038203F4098215F403C048FF03C012F4098203F4068203\r
+F4038203F403821BF403FF03C003FF03C0020DF3EDF303FF03C00F82FDF3EDF3\r
+0982FDF3EDF30682FDF3EDF303C006FF03C012F4038203F4038203F4038203F4\r
+068203F4098215F403C048FF03C012F4038203F4038203F4038203F4068203F4\r
+038203F4068215F403C003FF03C0FDF3EDF30C82FDF3EDF30682FDF3EDF30682\r
+FAF3EDF3F3EDF303FF03C00382FDF3EDF30982FDF3EDF30382FDF3EDF30382FA\r
+F3EDF3F3EDF30382FDF3EDF303C006FF03C04BF403C048FF03C04BF406C003FF\r
+03C0FDF3EDF30382FDF3EDF30682FDF3EDF30382FAF3EDF3F3EDF30982FDF3ED\r
+F303FF03C0020DF3EDF303C006FF03C04BF403C048FF03C04BF403C006FF03C0\r
+0210F3EDF321C0FAF3EDF3F3EDF306FF03F445C003F403C04EFF48C006F406FF\r
+FAF3EDF3F3EDF321C00211F3EDF309FF48F454FF48F409FF020FF3EDF37FFF7F\r
+FF7FFF72FF06C018FF06C003FF03C01BFF06C07FFF23FF03C003FF03C003FF0C\r
+C003FF03C003FF03C006FF03C003FF06C003FF12C003FF03C003FF03C003FF03\r
+C009FF03C009FF06C003FF03C006FF03C003FF03C003FF12C009FF03C003FF03\r
+C006FF09C006FF03C003FF06C07BFF03C009FF06C003FF03C003FF03C003FF0C\r
+C003FF06C003FF0CC009FF09C003FF0CC009FF03C009FF03C003FF03C006FF03\r
+C003FF03C003FF03C003FF09C003FF03C006FF06C003FF03C003FF0CC003FF06\r
+C078FF03C003FF03C006FF0FC006FF03C003FF06C003FF09C003FF06C003FF03\r
+C006FF03C00CFF03C003FF03C003FF03C003FF03C009FF09C003FF0FC003FF03\r
+C006FF03C003FF03C003FF03C003FF03C003FF06C003FF03C003FF06C003FF03\r
+C078FF03C003FF03C003FF06C006FF03C009FF06C00CFF03C006FF06C00CFF03\r
+C003FF03C01BFF06C07FFF7FFF4FFF03C003FF03C03CFF06C07FFF20FF03C006\r
+FF03C003FF03C003FF03C003FF03C006FF09C009FF03C003FF09C006FF03C006\r
+FF03C006FF03C006FF0CC009FF09C003FF03C006FF09C003FF03C006FF03C006\r
+FF03C07FFF20FF0CC003FF06C009FF03C003FF03C003FF09C003FF1BC003FF06\r
+C006FF03C003FF03C003FF0CC003FF03C009FF0FC006FF0CC003FF03C07FFF23\r
+FF03C003FF0FC003FF03C003FF03C003FF06C003FF03C006FF0CC003FF03C006\r
+FF03C006FF03C006FF03C006FF06C003FF03C006FF03C00CFF03C003FF06C003\r
+FF03C003FF03C003FF09C003FF03C07FFF1DFF06C009FF03C024FF03C006FF03\r
+C039FF06C00FFF06C07FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7F\r
+FF7FFF7FFF7FFF7FFF7FFF7FFF71FFFE000000FF\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 256\r
+  Width 128\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 256\r
+  Width 128\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+FigureSymbol "arrow right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 96\r
+  Width 256\r
+  TextBox 0,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 7 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 96\r
+  Width 256\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 7 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC Background"\r
+  Text "Server X"\r
+  Bounds 448,576,864,1216\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Style "SILC Background"\r
+  Text "Client A"\r
+  Bounds 192,576,416,1216\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Private Message Delivery\line With Public Key Cryptosystem"\r
+  Bounds 533,384,1557,547\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 208,784,400,1072\r
+  BorderColor 219,219,219\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "Rectangle"\r
+  Text "Public Key"\r
+  Bounds 208,1072,400,1136\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Text ""\r
+  Bounds 256,736,288,800\r
+  BorderColor 192,192,192\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "Arrow up"\r
+  Text ""\r
+  Bounds 304,736,336,800\r
+  BorderColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "Rectangle"\r
+  Text "Message"\r
+  Bounds 208,672,400,736\r
+  BorderColor 219,219,219\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Background"\r
+  Text "Client B"\r
+  Bounds 1696,576,1920,1216\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "Rectangle"\r
+  Text "Encrypt\line \line Decrypt"\r
+  Bounds 1712,784,1904,1072\r
+  BorderColor 219,219,219\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Text ""\r
+  Bounds 1760,736,1792,800\r
+  BorderColor 192,192,192\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Style "Arrow up"\r
+  Text ""\r
+  Bounds 1808,736,1840,800\r
+  BorderColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 13\r
+{\r
+  Style "Rectangle"\r
+  Text "Message"\r
+  Bounds 1712,672,1904,736\r
+  BorderColor 219,219,219\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 14\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 464,784,656,1072\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 15\r
+{\r
+  Style "SILC Background"\r
+  Text "Server Y"\r
+  Bounds 1248,576,1664,1216\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 16\r
+{\r
+  Style "SILC Network Cloud"\r
+  Text "SILC\line Network"\r
+  Bounds 912,688,1200,1104\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 12\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 17\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 448,1232,1664,1360\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 18\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 592,1264,784,1296\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 19\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Client B's Public key and Private key pair"\r
+  Bounds 881,1266,1399,1298\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 20\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 656,784,848,1072\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 21\r
+{\r
+  Text ""\r
+  Bounds 368,912,464,1008\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 22\r
+{\r
+  Text ""\r
+  Bounds 384,832,480,928\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 23\r
+{\r
+  Text ""\r
+  Bounds 848,832,944,928\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Text ""\r
+  Bounds 832,912,928,1008\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 25\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 1264,768,1456,1056\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 26\r
+{\r
+  Style "Rectangle"\r
+  Text "Pass the\line message\line through"\r
+  Bounds 1456,768,1648,1056\r
+  BorderColor 219,219,219\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 10\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 27\r
+{\r
+  Text ""\r
+  Bounds 1184,832,1280,928\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 28\r
+{\r
+  Text ""\r
+  Bounds 1184,912,1280,1008\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 29\r
+{\r
+  Text ""\r
+  Bounds 1632,832,1728,928\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 30\r
+{\r
+  Text ""\r
+  Bounds 1632,912,1728,1008\r
+  BorderColor 192,192,192\r
+  FillColor 244,244,244\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 31\r
+{\r
+  Style "Rectangle"\r
+  Text "Private Key"\r
+  Bounds 208,1136,400,1200\r
+  BorderColor 219,219,219\r
+  FillColor 253,254,194\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 32\r
+{\r
+  Style "Rectangle"\r
+  Text "Private Key"\r
+  Bounds 1712,1136,1904,1200\r
+  BorderColor 219,219,219\r
+  FillColor 240,255,240\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 33\r
+{\r
+  Style "Rectangle"\r
+  Text "Public Key"\r
+  Bounds 1712,1072,1904,1136\r
+  BorderColor 219,219,219\r
+  FillColor 253,254,194\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 34\r
+{\r
+  Style "Rectangle"\r
+  Text ""\r
+  Bounds 592,1312,784,1344\r
+  BorderColor 219,219,219\r
+  FillColor 253,254,194\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 35\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Client A's Public key and Private key pair"\r
+  Bounds 880,1309,1398,1341\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_protocol.html b/doc/whitepaper/silc_protocol.html
new file mode 100644 (file)
index 0000000..9b607a0
--- /dev/null
@@ -0,0 +1,947 @@
+<!-- This file is processed with html2ps to generate PostiScript. 
+     Do not edit the HTML syntax unless you know what you are doing. -->
+<html>
+<head>
+<title>SILC Protocol White Paper</title>
+<link rev=made href="mailto:priikone@silcnet.org">
+<meta name="Author" content="Pekka Riikonen - SILC Project">
+<meta name="Description"
+ content="SILC - Secure Internet Live Conferencing Protocol">
+<meta name="Created" content="Version 1.0.5 / 15 Jab 2002">
+</head>
+<body bgcolor="#ffffff">
+
+<font face="Helvetica">
+
+<font size="6"><b>SILC Protocol White Paper</b></font><br>
+<font size="2">Version 1.0.5 / 15 Jan 2002</font>
+
+<p>
+<h1>Introduction</h1>
+
+Chat protocols are very popular on the Internet.  They have actually
+been very popular since the very first chat protocols appeared on the net.
+The Internet Relay Chat (IRC) was one of the first chat protocols, and quickly
+gained the status of being the most popular chat on the net.  Today, IRC
+has several competitors from various other so called Instant Messaging (IM)
+protocols, such as ICQ.  However, all of these different chat protocols
+have something in common; they are all insecure.
+<p>
+
+The security is important feature in applications and protocols in 
+contemporary network environment.  The older chat protocols, however have
+failed to meet the growing security requirements on the Internet.
+It is not anymore enough to just provide services, like for example
+chat services. Now, they need to be secure services.
+<p>
+
+The Secure Internet Live Conferencing (SILC) protocol is a new generation
+chat protocol which provides full featured conferencing services, just
+like any other contemporary chat protocol provides.  In addition, it
+provides security by encrypting and authenticating the messages in
+the network.  The security has been the primary goal of the SILC protocol
+and the protocol has been designed from the day one security in mind.
+All packets and messages travelling in the SILC Network are always
+encrypted and authenticated.  The network topology is also different
+from for example IRC network.  The SILC network topology attempts to be
+more powerful and scalable than the IRC network.  The basic purpose
+of the SILC protocol is to provide secure conferencing services.
+<p>
+
+The SILC Protocol have been developed as Open Source project.  The
+protocol specifications are freely available and they have been submitted to
+the IETF.  The very first implementations of the protocol are also already
+available.
+
+<p><br>
+<h1>About This White Paper</h1>
+<p>
+The purpose of this white paper is to give short but deep enough introduction
+to the SILC Protocol.  The document describes the purpose of the protocol
+and how the protocol works in practice.  This document is intended for all
+audience.  This document should be easy to understand for non-technical
+person and still be detailed enough for technically oriented person.  See
+the section <a href="#terms">Terms and Abbreviations</a> for terms used
+in this document.
+<p>
+
+<p>
+(c) Copyright 2001 - 2002 Pekka Riikonen 
+(<a href="mailto:priikone at silcnet.org">priikone at silcnet.org</a>)
+<p>
+This document is free document; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.  This document is distributed in
+the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+
+<p><br>
+<h1>SILC Protocol</h1>
+<p>
+
+The Secure Internet Live Conferencing (SILC) protocol provides secure
+conferencing services over insecure network channel.  The SILC is IRC
+like protocol, however it does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside the SILC network.  SILC
+provides all the common conferencing services like channels, channel
+messages, private messages, nicknames, various commands, and secure
+file transfer.  Difference to other chat protocol is in the design of
+the protocol.  The SILC protocol has been designed from the day one
+security in mind and it shows in the protocol design.
+<p>
+
+The packets and messages in the SILC network are always encrypted and
+authenticated.  It is not possible to send unencrypted messages in SILC
+at all.  This assures that end user cannot even accidently send unencrypted
+messages while thinking that it is encrypted.  This is one of the problems 
+of most of the other chat protocols that provide so called plugin encryption.
+They are not secure by default but try to provide security by applying
+external security protocol such as PGP or SSL over the insecure chat
+protocol.  In these cases the security is achieved usually by encrypting the
+data while key management, message authentication and other security issues
+may be left out, leaving the implementation vulnerable to various security
+problems.  The other problem is also that the external protocols tend to
+leave the network only partly secured; usually only two points in the
+network are secured with for example SSL.  While SSL does provide provable
+security it is not enough to provide security for a chat network as a whole.
+<p>
+
+SILC is secure in environment of mutual distrust between entities
+in the network.  It is possible to encrypt messages end to end, so that only
+the sender and the receiver is able to encrypt and decrypt messages.  It
+is also possible to send messages to group of users, so that only the
+specified group of users is able to encrypt and decrypt messages.  Many
+times the protocol use keys that are generated by the servers, so that
+if other external key exchange methods fail the network still remains
+encrypted.  However, it is always possible to negotiate and use locally
+generated keys to secure messages, so that the servers do not know the
+key.
+<p>
+
+Like so many other contemporary chat protocols, SILC too provides
+file transfer.  It is possible to transfer files securely between users
+in the SILC Network.  The actual file transfer stream is always sent
+outside the network peer to peer.  Before the file transfer is started
+a key exchange protocol is executed to negotiate file transfer session
+key.
+<p>
+
+The network topology is also different to various other chat protocols,
+like for example IRC.  IRC has tree style network, but SILC network can be
+described more as an hybrid ring-mesh network.  The routers in the network
+form a ring, but they can also have other direct routers (secondary routes)
+to other routers.  A router in the network is also called a cell, when it
+has multiple servers and clients connected to it.  The cell can also have
+backup routers in case the primary router becomes unresponsive.
+
+<p><br>
+<object data="silc_network.jpg" type="application/postscript">
+<img src="silc_network.png" alt="SILC Network" align="center" border"0">
+</object>
+<p><br>
+
+The diagram above illustrates a portion of the SILC network.  It shows
+two cells that both have several servers, and backup routers and several
+clients.  Clients can connect to server and routers if they want to.
+The following sections will describe the entities of the SILC Network
+in greater detail.
+<p>
+
+
+<p><br>
+<h2>Clients</h2>
+<p>
+
+A client is a piece of software connecting to SILC server.  The software
+is usually run by the end user, a real person that is.  The purpose of the
+clients is to provide the end user an interface to the SILC services.
+They are used to actually engage the conversations on the SILC Network,
+and they can be used to execute various SILC commands.
+<p>
+
+The clients are distinquished from other clients by unique Client ID.
+There cannot be multiple same Client IDs in the SILC Network at the same time.
+The end user, however does not use Client IDs.  The end users usually selects
+a preferred nickname they want to use, and identifies themself with that
+nickname to other users on the network.  The nicknames are not unique in
+the SILC Network.  There can be multiple same nicknames at the same time
+on the network.  The maximum length for the nickname is 128 characters.
+<p>
+
+Most of the other chat protocols have unique nicknames.  This is where SILC
+differs from most of the other chat protocols.  The purpose of this
+feature is to make IRC style nickname wars obsolete, as no one owns their
+nickname; there can always be somene else with the same nickname.
+<p>
+
+When client connects to the server the SILC Key Exchange (SKE) protocol and
+SILC Connection Authentication protocol are executed.  The result of the
+SKE protocol is the session key that the client and server use to secure
+their communication.  All commands, for example, that the client sends
+to the server are secured with the session key.  The session key expires
+periodically and the rekey process can be executed with or without the
+Perfect Forward Secrecy (PFS).  The connection authentication protocol is
+used to authenticate the client to the server.  The server may allow the
+client to connect without authentication, or it may require a passphrase or
+public key based (or certificates) authentication.
+
+
+<p><br>
+<h2>Servers</h2>
+<p>
+
+Servers forms the basis for the SILC Network, by providing a point to which
+clients may connect.  There are two kinds of servers in SILC; normal servers
+and router servers.  The next section describes the function of router
+server.
+<p>
+
+Normal servers connect to router server.  Normal servers cannot directly
+connect to other normal servers.  Messages that are destined outside the
+local server are always sent to the router for further routing.
+The clients usually connect to the normal server, however, clients may
+connect to router servers as well.  The SILC Network diagram above
+illustrates how normal servers connects to the router server.
+<p>
+
+The servers are distinquished by other servers in the network by unique
+Server ID.  There cannot be multiple same Server IDs in the SILC Network
+at the same time.  The servers keep track of local information.  It knows
+all locally connected clients and it knows all channels that its clients
+have joined.  However, it does not know any global information.  It
+usually does not keep track of global clients, however, it may cache
+that information if it was queried.  The reason for this is that the
+server does not need to keep global information up to date and thus
+makes the server faster (and in the end the entire network faster).
+They can always query the information from the router.
+<p>
+
+When server connects to its router the SILC Key Exchange (SKE) protocol
+and the SILC Connection Authentication protocol are executed, just like
+when client connects to server.  The SKE results in to the session key
+that is used to secure the communication between the server and the
+router.  The connection authentication protocol is used to authenticate
+the server to the router.  The authentication is always based in either 
+passphrase or public key (or certificates).
+
+
+<p><br>
+<h2>Routers</h2>
+<p>
+
+The router servers are servers that actually handles the message routing
+in the network.  They are, however also normal servers and they do accept
+client connections.  Each of the router in the network is called a cell.
+A cell can have only one active router and it may have several servers
+and several clients.  The cell, however may have backup routers that can
+take over the tasks of the primary router if it becomes unresponsive.
+The switch to the backup router should be transparent and only local
+connections to the primary router are lost.  Other connections in the
+cell are intact, and clients and servers merely experience some lag in
+the network connection during the switch to the backup router.
+<p>
+
+The normal server knows only local information.  Router server on the
+other hand knows local information and global information.  It considers
+the cell as local and outside cells as global.  It knows all the clients
+connected to the network, all created channels, and all routers and servers
+in the network.  The server may query the global information if it is needed.
+For example, when client sends WHOIS command, the server may query the
+information from the router.  If the router does not know all the details
+that the WHOIS command requires it can query the information from a router
+or a server which knows all the details.  It may then cache that information.
+<p>
+
+The primary purpose of the router server is to route the messages to
+local servers and local clients, and messages that are destined to outside
+the cell are routed to the primary route or some other secondary
+route if it is a faster route.  The routers in the network forms a ring.
+Each router has a primary route to other router in the network.  Finally
+the ring is closed by the last router using the first router in the
+network as its primary route.
+
+<p><br>
+<object data="silc_routers.jpg" type="application/postscript">
+<img src="silc_routers.png" alt="SILC Routers" align="center" border"0">
+</object>
+<p><br>
+
+The diagram above illustrates how the routers forms a ring in the network.
+A router may have several secondary routes which it may use when it
+routes the packets.
+<p>
+
+When routers connect to its primary router the SKE and the SILC Connection
+Authentication protocols are executed just like when normal server connects
+to its router.  The session key is used to secure the communication between
+the routers.  All the secondary routes also have their own session keys.
+
+
+<p><br>
+<h1>SILC Packet Protocol</h1>
+<p>
+
+The basis of SILC protocol relies in the SILC packets and they are with
+out a doubt the most important part of the protocol.  The SILC Packet 
+protocol is a binary packet protocol.  The protocol provides secure
+binary packets and assures that the contents of the packets are secured
+and authenticated.
+<p>
+
+Packets are used in the SILC protocol all the time to send for example
+channel messages, private messages, commands and other information.  All
+packets in SILC network are always encrypted and their integrity is
+assured by computed Message Authentication Codes (MAC).  The protocol
+defines several packet types and packet payloads.  Each packet type
+usually has a specific packet payload that actually defines the contents
+of the packet.  Hence, the actual data in the packet is the packet payload 
+defined in the protocol.
+
+<p><br>
+<object data="silc_packet.jpg" type="application/postscript">
+<img src="silc_packet.png" alt="Typical SILC Packet" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above illustrates the SILC packet is constructed from the
+SILC Packet Header that is included in all SILC packets, data area that
+includes the packet payloads, and MAC area which assures the integrity of the
+packet.  Entire SILC packet is always encrypted, except for the MAC area
+which is never encrypted.  The encryption process and the key used,
+however depends on the packet payload.  Some of the payloads are encrypted
+with the session key and some are encrypted with other keys, for example
+with channel message keys.  The SILC Packet Header is always encrypted with
+the session key.  The MAC is computed from the SILC Packet Header and the
+data area before encrypting the packet.
+
+
+<p><br>
+<h1>SILC Key Exchange Protocol</h1>
+<p>
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key material
+used to secure the communication channel.  This protocol is executed when,
+for example client connects to server.  It is also executed when server
+connects to router.  And, there is no reason why it could not be executed
+between two clients too, if two clients would need to create secret key.
+The purpose of the SKE protocol is to create session keys to be used
+in current SILC session.  The SKE is based on the Diffie-Hellman key
+exchange algorithm, and is immune to for example man-in-the-middle attacks 
+by using digital signatures.
+<p>
+
+This is the first protocol that is executed when creating connection to,
+for example SILC server.  All the other protocols are always executed
+after this protocol.  This way all the other protocols are secured since
+the SKE creates the session key that is used to secure all subsequent
+packets.  The session keys created in the SKE are valid only for some
+period of time (usually an hour) or at most until the session ends.
+The rekey process can be executed with or without the Perfect Forward
+Secrecy (PFS).
+<p>
+
+The security properties that are used in the SILC session are also
+negotiated during the SKE.  The protocol has initiator and responder.
+The initator is the one who starts the SKE negotiation and responder is
+the one who receives the SKE negotiation.  When the protocol is started
+initiator sends a list of security properties that it supports.  The
+responder then selects the security properties it supports and sends
+its reply to the initiator.  The security properties includes ciphers,
+hash functions, public key algorithms, HMAC functions and other
+security properties.  The responder can always choose the properties
+it supports.
+<p>
+
+After the security properties are selected the protocol continues
+by performing the Diffie-Hellman key exchange algorithm.  At the same
+time the intiator and responder also sends their public keys or
+certificates to each other.  The initiator and responder also computes a
+signature that the other party will verify.  By default the protocol is
+executed in so called mutual authentication mode, where both of the
+parties computes a signature which are verified by each other
+independently.  This way both of the parties will have prove the
+posession of the private key to the public key they are providing
+in the protocol.  If any of the phases of the protocol are to fail the
+connection is closed immeadiately.
+<p>
+
+The public key or certificate that is received during the SKE protocol
+must be verified.  If it is not verified it would be possible to 
+execute a man-in-the-middle attack against the SKE protocol.  If
+certificates are used they can be verified by a third party Certification
+Authority (CA).  Verifying a public key requires either confirming
+a fingerprint of the public key over phone or email, or the server
+can for example publish the fingerprint (and the public key) on some 
+website.  In real life systems accepting the public key without
+verification, however is often desired.  In many security protocols,
+such as in SSH2, the public key is accepted without verification
+in the first time when the connection is created.  The public key is
+then cached on local hard disk.  When connecting next time to the
+server the public key on local cache is verified against the public
+key server sent.  In real life this works most of the time.  However,
+if client (or server) cannot trust this, it must find some other way
+to verify the received public key or certificate.
+
+
+<p><br>
+<h1>SILC Connection Authentication Protocol</h1>
+<p>
+
+Purpose of SILC Connection Authentication protocol is to authenticate the
+connecting party with server or router.  This protocol is executed when
+for example client connects to server.  It is also executed when server
+connects to router.  Its other purpose is to provide information for the
+server about which type of connection it is.  The type of the connection
+defines whether it is client, server or router.  If it is client then
+the server will create a new Client ID for the client.  If it is server
+then it will except the server to send its Server ID.  Server IDs are
+created by the servers and routers itself.
+<p>
+
+Since the SILC Connection Authentication protocol is always executed after
+the SKE protocol, session keys has been established already.  This means
+that all packets sent in the connection authentication protocol are encrypted 
+and authenticated.
+<p>
+
+The authentication may be based either in passphrase or public key
+encryption.  It is also possible to not require authentication at all.
+If the authentication is based to passphrase the passphrase is sent
+to the server.  As the packet sent by, for example client, is entirely
+encrypted it is safe to send the passphrase inside the packet.
+<p>
+
+If the authentication is based to public key then, for example the client, 
+signs data with its private key and sends it to the server.  The server
+then verifies this signature by using the client's public key.  The
+packet is also encrypted in the case of public key authentication.
+<p>
+
+If the authentication is to fail the connection to the server or router
+will be refused.  If it is successful the connection is granted.  After
+this the client is ready to communicate in the SILC Network.
+
+
+<p><br>
+<h1>Channels</h1>
+<p>
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client joins to it, and the channel ceases to exist when the last client
+leaves it.  When channel exists, any client can reference it using the 
+name of the channel.  Channel is a place where group of people can engage
+conversation.
+<p>
+
+Channel names are unique in the SILC Network.  There cannot be multiple
+same channels in the network at the same time.  However, channel has also
+a Channel ID which is actually used to reference the channel in the
+SILC Network.  The maximum length for the channel name is 256 characters.
+<p>
+
+Channels can have operators that can administrate the channel and operate
+all of its modes.  There are two types of operators on the channel:
+channel founder and channel operator.
+<p>
+
+The channel founder is the client which created the channel.  Channel
+founder is channel operator with some more privileges.  Channel founder
+can operate all of the channel's modes.  Furthermore, channel founder
+privileges cannot be removed by any other operator on channel and channel
+founder cannot be removed from the channel by force.  It is also possible
+for the channel founder to regain its privileges at later time, even if
+they have left the channel.
+<p>
+
+Channel operator is operator that can operate most of the channel's
+modes and administrate the channel.  However, it cannot operate all
+modes which are strictly reserved for channel founder.  Channel operator
+is, however able to adminstrate the channel, set some modes on the
+channel, remove a badly behaving client from the channel, and promote
+other clients to become channel operator.
+
+
+<p><br>
+<h2>Channel Message Delivery</h2>
+<p>
+
+All clients that have joined the channel can send messages to the channel.
+All channel messages are secured and authenticated by channel key.  The
+channel key is generated by the server when the channel is created,
+a client joins the channel, or a client leaves the channel.  The channel
+key is also regenerated periodically.  The reason for the regeneration
+of channel key everytime someone joins or leaves the channel is that
+it prevents new clients joining the channel, and old clients leaving the
+channel, to encrypt or decrypt old or new messages.  They can encrypt
+and decrypt channel messages only when they have joined on the channel.
+<p>
+
+Channel keys are cell specific in the SILC Network.  Each cell that
+have clients joined on a particular channel have also own key for the
+channel.  That key is not shared by other cells in the network.  Inside
+the cell the channel key is known by the router and all servers that
+have clients on the channel and all clients that have joined the channel.
+
+<p><br>
+<object data="silc_channel.jpg" type="application/postscript">
+<img src="silc_channel.png" alt="Channel Message Delivery" align="center" border"0">
+</object>
+<p><br>
+
+The diagram above illustrates typical delivery of channel messages inside
+a cell and between two cells.  Both of the cells have their own channel
+key.  Both cells knows all clients joined on the channel.  When message
+is sent to the channel by an client, it is encrypted with the current
+channel key in that cell.  The servers and the router in the local cell
+then routes the message to all local clients who have joined the channel.
+If the channel has clients that belong to other cell in the network the
+router will route the channel message to that cell.  When channel
+messages are sent between routers they are first decrypted with the
+current channel key, and then re-encrypted with the session key shared
+between the two routers.  The router who receives the channel message
+then decrypts it with the session and re-encrypts it with the
+current channel key in that cell.  It then distributes the channel message
+to all clients on the channel.  The clients who have joined the channel
+always knows the current channel key and can decrypt all channel messages
+they receive.  Note that normal servers in the SILC network never decrypt
+the channel messages even though the have the key.  There is no reason
+for servers to decrypt the message.  The router decrypts the message
+only when sending it between two routers.
+<p>
+
+This method of channel message delivery is the default way to send
+channel messages in the SILC Network.  However, this is not perfect
+solution on all circumstances.  If the clients joined on a particular
+channel cannot trust, or do not want to trust the servers and routers
+in the SILC Network they can consider the fact, that servers and routers
+knows the channel key is actually a breach of security.
+<p>
+
+If the clients on the other hand can trust their servers and routers
+in the SILC Network this is the recommended way of sending channel
+messages.  This method is the simplest method for end user since it
+does not require any special settings before engaging the conversation
+on the channel.  The client merely joins the channel, receives the
+channel key from the server and can start the conversation on the
+channel.
+<p>
+
+In addition of encrypting channel messages it also possible to digitally
+sign all sent channel messages.  The receiver could then verify the
+signature of each of the message using the sender's public key.
+
+
+<p><br>
+<h2>Channel Message Delivery With Channel Private Key</h2>
+<p>
+
+If the clients cannot trust the servers and routers in the SILC Network
+they should not use the default way of sending the channel messages.
+Instead, they should use channel private keys to encrypt and decrypt
+the channel messages.  Channel private keys are keys that are known
+only by the clients who have joined the channel.  Servers and
+routers do not know the key and cannot decrypt the messages.  When
+message is sent between two routers they are merely re-encrypted with
+the session key but not decrypted since the router do not have the
+key to do that.
+<p>
+
+The clients who have joined the channel must first agree on the channel
+private key they are going to use.  The key may generally be anything.
+It may be a passphrase or a random string, or the key may negotiated
+using some key exchange protocol which provides negotiating the
+key for multiple clients at the same time.
+<p>
+
+As the channel private key is actually entirely local setting in the
+client, it is possible to set several channel private keys for one
+channel.  It is possible to have multiple channel private keys that
+are not known by all channel members.  When encrypting messages with
+one channel private key only the clients who have that key can decrypt
+the message.  The other key could be shared for example by all clients
+on the channel and thus all clients can decrypt messages encrypted with
+that key.  In this way it is actually possible to have a private group
+conversation inside the channel while having global conversation at the
+same time.
+
+
+<p><br>
+<h1>Private Messages</h1>
+<p>
+Private messages are messages that are sent from one client to another 
+through the SILC Network.  They are private because they are not sent to
+anyone else except to the true receiver of the message.  Private messages
+can be used to engage private conversation with another client if channels
+are not desired.
+<p>
+
+As all messages in SILC the private message are also encrypted and
+authenticated.  There are several ways to secure private messages.  By
+default private messages are encrypted using the session keys established
+in the SKE protocol.  It is also possible to negotiate a private message
+key between the two clients and encrypt the messages with that key.  It
+is even possible to encrypt the messages with public key cryptosystem,
+if desired.  The next sections will describe all these private message
+delivery methods.
+
+<p>
+The SILC protocol provides these three methods of delivering private messages
+because none of the methods alone can satisfy the security requirements
+of all people.  The end user should decide the acceptable level of risk,
+the required level of security and other security and usability aspects when
+deciding what way of sending private message suites for them.
+<p>
+
+In addition of encrypting private messages it also possible to digitally
+sign all sent private messages.  The receiver could then verify the
+signature of each of the message using the sender's public key.
+
+
+<p><br>
+<h2>Private Message Delivery With Session Keys</h2>
+<p>
+Sending private messages are by default secured with session keys established
+in the SKE protocol.  This means that the private message is always encrypted
+with the session key of the next receiver of the message enroute to the 
+receiving client.  This also means that the message is decrypted and
+re-encrypted everytime it is sent further to the receiving client.
+
+<p><br>
+<object data="silc_priv1.jpg" type="application/postscript">
+<img src="silc_priv1.png" alt="Basic Private Message Delivery" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above shows the private messages sent by Client A to the
+Client B travels through the SILC Network and is always decrypted and
+re-encrypted with the session key of the next receiver.  The Client B then
+finally decrypts the private messages that is encrypted with the session
+key shared between the Client B and the Server Y.
+<p>
+
+This way of securing private messages is not perfect and cannot be used
+in all circumstances.  If the clients having the conversation cannot trust
+the servers and routers in the SILC Network they should not send private
+messages that are secured in this manner.  Messages secured in this manner
+can be decrypted by the servers and routers that the clients may consider
+to be untrusted.
+<p>
+
+If the clients on the other hand trust the servers and routers in their 
+SILC Network, or they do not care that servers can decrypt their messages,
+sending private messages in this way is very simple from client's point
+of view.  For servers and routers this of course means that they need
+to decrypt and re-encrypt each private message.  Since this way of securing
+private message cannot be used at all times the SILC protocol provides
+other ways of securing private messages.
+
+
+<p><br>
+<h2>Private Message Delivery With Private Message Key</h2>
+<p>
+Private messages can be secured with private message key as well.  This
+key is known only by the sender of the message and the receiver of the
+message.  This way no one else except the sender and the receiver can encrypt
+and decrypt the private messages.  The message is encrypted by the sender
+with the private message key and all the servers and routers pass the message
+through enroute to the receiver.  They cannot decrypt the message since
+they do not have the key.  When sending private messages in this way it
+does not matter whether the clients trust or do not trust the servers and
+routers in the SILC network.
+
+<p><br>
+<object data="silc_priv2.jpg" type="application/postscript">
+<img src="silc_priv2.png" alt="Private Messages with Private Message Key" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above shows the Client A encrypts the message with private
+message key and sends the message to the SILC Network.  All servers and
+routers merely pass the message through since they cannot decrypt it.
+The Client B then receives the message and decrypts it with the private
+message key.
+<p>
+
+Sending private messages in this manner is always secure since the key is
+shared only by the sender and the receiver.  The problem of this method
+is that the sender and the receiver must somehow agree about the key
+they are going to use.  The private message key can generally be anything.
+It can be a passphrase that only the sender and the receiver knows.  They
+could have been agreed to use some word or phrase as the key sometime
+earlier before they started the conversation.  Or the key maybe from some
+random string from a code book that only the sender and the receiver poses.
+Or it can be a key that is negotiated using some key exchange protocol.
+<p>
+
+The problem however is fundamental.  How to agree to use some key when
+you cannot reach the other person over secure channel?  The SILC protocol
+solves this problem by providing a possiblity to negotiate the key
+between two clients using the SKE protocol.  One or both of the clients
+can set up the SKE server running in their host and ask the other client
+to connect to it.  In this case the SKE is executed outside the SILC
+Network.  As a result of the SKE protocol the clients have now shared
+secret that they can use as private message key.  The key is known only
+by the two clients that executed the SKE protocol.  They can then use
+that key to secure all subsequent private messages.
+<p>
+
+Using this method of private messages delivery is recommended if the
+clients cannot trust the servers and routers in the SILC Network.  The 
+drawback is the extra phase of setting the private message key before
+starting the conversation.  However, using the SKE protocol is the
+recommended way to negotiate the private message key since it can be
+automatized and does not cause any extra tasks for end user.
+
+
+<p><br>
+<h2>Private Message Delivery With Public Key Encryption</h2>
+<p>
+If the clients cannot trust the servers and routers in the SILC Network
+they can use the private message key.  As described in the previous section
+it is easy to set up with the SKE protocol.  However, sometimes the two
+clients do not want to use any passphrases as private message key or
+negotiate the key with SKE, or perhaps they are unable to negotiate the
+key because of some other external problem.  The SILC protocol provides
+yet another way of securing the private messages.  This way does not
+require setting or negotiating private message key.  And, in this method
+also it does not matter whether the clients trust or do not trust the
+servers and routers in the SILC Network.  The method is public key
+encryption.  The clients can encrypt the private messages with the
+receiver's public key and send the message to the network.  The servers
+and routers cannot decrypt the messages since they do not have the
+receiver's private key.  The receiver on the other hand has the private
+key which it uses to decrypt the message.
+<p>
+
+Even this method of private message delivery is not perfect.  The drawbacks
+of this method is that the sender must first assure that the public key
+it is using in the encryption is actually the receiver's public key.
+This is a absolute requirement in this method.  If the sender cannot
+authenticate the receiver's public key this method of private message
+delivery should not be used.  In SILC protocol clients can fetch other
+clients public keys from servers, but the client still must verify the
+key.  Use of certificates can solve the problem.  The receiver's certificate
+could be authenticated by a third party Certification Authority (CA).
+
+<p>
+Usually verifying the public key is not a problem since the receiver
+can provide the public key on some website, or verify the fingerprint of
+the key over email, or phone call.  The clients can also fetch the
+public keys from SILC servers.  If both of the clients trust that the
+public keys are authentic using this method of private message delivery
+is very simple and recommended.
+
+
+<p><br>
+<h1>Secure File Transfers</h1>
+
+The file transfer support in chat protocols are a absolute requirement
+nowadays, and chat protocol without one is no chat protocol at all.  SILC
+also supports file transfer with the addition that the file transfer
+stream is secured.  When a user wants to transfer a file to another
+user, the SILC Key Exchange (SKE) protocol is first executed to negotiate
+a session key for the file transfer stream.  This key is then used to
+protect the peer to peer stream between users.
+<p>
+
+The file transfer protocol used in SILC protocol is the SSH File Transfer
+protocol (SFTP).  Even though the name of the protocol relates to SSH,
+the actual file transfer protocol has nothing to do with Secure Shell.
+The SFTP is totally independent file transfer protocol and its stream
+is secured using SILC.  The SFTP is very good protocol because in addition
+of providing simple file transfer support, it can also support complex
+file and directory manipulation.
+<p>
+
+The support for file transfer in SILC has been designed so that using
+practically any file transfer protocol is possible.  The mandatory protocol
+is SFTP but in the future adding support for other protocols is also
+possible.
+
+
+<p><br>
+<h1>Future of the Protocol</h1>
+
+The current protocol version is 1.0.  This does not mean that the protocol
+is perfect and does not need further development.  There is still features
+that are missing from the protocol, and it is clear that the protocol needs
+to mature a bit more.  There has been a talk about adding features like
+permanent channels, more wide channel founder privileges, and other similar
+features.  The network model of the protocol allows powerful routing
+capabilities, however the routing is not fully defined yet in the protocol
+and requires more in depth work.  The protocol is still in draft phase
+and is open for new features.  However, it is our intention that the
+protocol will be standardized in the future.
+
+
+<p><br>
+<h1>Conclusion</h1>
+
+The Secure Internet Live Conferencing (SILC) protocol is a new generation
+chat protocol that provides all the common conferencing services with
+strong support for security.  It has wide range of security properties
+that should meet the highest levels of security requirements, while not
+forgetting easy of use.  The network topology offers new architectural
+solution with better scalability over traditional chat protocols.
+
+
+<p><br>
+<h1>Further Information</h1>
+<p>
+More detailed information about the SILC protocol is available in the
+SILC protocol specification documents.  There exists currently four
+Internet Drafts that defines the protocol in great detail.  The Internet
+Drafts are available from the following sources but also from the
+<a href="http://www.ietf.org">IETF website</a>.
+<p>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-spec-04.txt">
+Secure Internet Live Conferencing (SILC), Protocol Specification</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-pp-04.txt">
+SILC Packet Protocol</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-ke-auth-04.txt">
+SILC Key Exchange and Authentication Protocols</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-commands-02.txt">
+SILC Commands</a>
+<p>
+
+For comprehensive introduction to cryptography refer to the
+<a href="http://www.ssh.com/tech/crypto/">Cryptography A-2-Z document</a>.
+
+<p><br>
+<a name="terms"></a>
+<h1>Terms and Abbreviations</h1>
+<p>
+
+- Asymmetric cryptosystem
+<p>
+Asymmetric cryptosystem provides public encryption.  It has two keys,
+one public key and one private key (also called as secret key).  The public
+key is publicly available allowing anyone to encrypt messages with the
+public key.  Only the posessor of the private key can decrypt those messages.
+Difference to symmetric cryptosystem is that symmetric cryptosystem use only
+one key, and the key is usually used to both encryption and decryption.  The
+asymmetric cryptosystem is also called as public key encryption, public key
+cryptosystem or public key algorithm.  SILC supports RSA and DSS asymmetric
+cryptosystems.
+<p>
+
+- Authentication
+<p>
+The verification of the identity of a person, host or process in order
+to gain access to a service or prove identity.  In data communications
+it also means verifying the origin of a message.
+<p>
+
+- Certificate
+<p>
+Certificate is a digital document which can be used to verify the 
+identity of a person or host.  In SILC, certificates can be used to prove
+identity of clients, servers and routers.  Basically certificate is a public
+key with subject name.  SILC supports X.509, OpenPGP and SPKI certificates.
+Supported public keys are SILC style public key and SSH2 style public
+key.
+<p>
+
+- Certification Authority (CA)
+<p>
+A third party entity that can verify identity of a person or host.  CA
+is usually external company that provides certificates and their
+verification services.
+<p>
+
+- Diffie-Hellman key exchange
+<p>
+First public key algorithm ever invented.  It is used to generate a secret
+key between two or more parties.  It gets its security from the difficulty
+of calculating discrete logarithms.
+<p>
+
+- Encryption
+<p>
+A mechanism (usually mathematical) to transfer plaintext (or cleartext)
+to ciphertext to provide confidentiality.  A process to transfer
+the ciphertext back to plaintext is called decryption.
+<p>
+
+- Integrity
+<p>
+The verification of data to detect any modifications.  If data is
+modified enroute from the sender to the receiver, the modification will
+be detected.
+<p>
+
+- HMAC
+<p>
+Hash Message Authentication Code.  Also called as keyed hash function.
+It is a secret key authentication algorithm which proves that the message
+is not modified and that the HMAC was computed by the sender of the
+message.
+<p>
+
+- Key management
+<p>
+Key management is a set of processes and mechanisms which support key
+exchange and maintainance of current keying relationships between parties,
+including replacing older keys with new keys as necessary, by executing
+rekey.
+<p>
+
+- Man-in-the-middle attack
+<p>
+An attack against two connecting entities where the attacker executes
+key exchange protocol with both of the parties indepently without
+their knowledge.  Both of the connecting entities will end up having secret
+key with the attacker, and the attacker can encrypt and decrypt all the
+messages that goes between the two entities.
+<p>
+
+- Message Authentication Code (MAC)
+<p>
+MAC provides message integrity by computing the MAC using a secret
+key authentication algorithm (HMAC).
+<p>
+
+- Perfect Forward Secrecy (PFS)
+<p>
+A property of rekey (or key regeneration) which defines whether the
+new key is derived from the old key.  If Perfect Forward Secrecy is
+selected the new key is never dependent of the old key which means
+that if the old key would get compromised at later time it will not
+compromise the new key.  In SILC setting PFS in the SKE protocol means
+executing the SKE protocol again.  If PFS is not selected the new key
+is always derived from the old key.
+<p>
+
+- Rekey
+<p>
+A key regeneration process where the old key has expired or is not
+secure anymore to use.  In this case rekey is performed and new key
+is generated.
+<p>
+
+- Symmetric cryptosystem
+<p>
+Symmetric cryptosystem is one key cryptosystem where one key is used
+usually to both encryption and decryption process.  The symmetric
+cryptosystems are usually significantly faster than asymmetric cryptosystems.
+DES, AES, Twofish and Blowfish are examples of symmetric cryptosystems.
+SILC supports all the common symmetric cryptosystems including AES.
+SILC does not support DES as it is insecure and 3DES as it is too slow.
+
+
+</font>
+
+</body>
+</html>
diff --git a/doc/whitepaper/silc_routers.edg b/doc/whitepaper/silc_routers.edg
new file mode 100644 (file)
index 0000000..2425871
--- /dev/null
@@ -0,0 +1,2445 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -296\r
+Y 61\r
+Scale 100\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 128,0,128\r
+Color9 243,237,243\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 255,255,0\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block"\r
+LastEndLen 25\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 68\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A00000000000000000000000000000000000000000000000000\r
+94395900\r
+}\r
+\r
+Preview 4864\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7DFF7FC07FC01FC033FF03C07FFC7FFC19FC03C033FF03C07FFC7FFC\r
+19FC03C033FF03C07FFC7FFC19FC03C033FF03C069FC06827FFC29FC03C033FF\r
+03C024FC0C8203FC3F821EFC0F8203FC068206FC038203FC038206FC038206FC\r
+038203FC098203FC0C8233FC03C033FF03C069FC038224FC038215FC038206FC\r
+038206FC098203FC068206FC09823CFC03C033FF03C07FFC7FFC19FC03C033FF\r
+03C07FFC7FFC19FC03C033FF03C069FC068224FC03820CFC038203FC038206FC\r
+038206FC038203FC098203FC098242FC03C033FF03C021FC518221FC0F820CFC\r
+098203FC068206FC098248FC03C033FF03C003FC0C8203FC038254FC03827FFC\r
+2CFC03C033FF03C003FC068203FC038203FC03827FFC7FFC04FC03C033FF03C0\r
+7FFC7FFC19FC03C033FF7FC07FC01FC07FFF7FFF7FFF0203F3EDF37FC07FC010\r
+C00203F3EDF32AFFFAF3EDF3F3EDF309C0025AF3EDF309C0FAF3EDF3F3EDF321\r
+FFFDF3EDF306C00260F3EDF306C0FDF3EDF31BFF06C00264F3EDF306C015FF03\r
+C00268F3EDF303C00FFFFDF3EDF303C0022AF3EDF345820227F3EDF303C0FDF3\r
+EDF30CFF03C0022BF3EDF303820215E6F2FF03820228F3EDF303C009FFFDF3ED\r
+F303C0022BF3EDF303820215E6F2FF03820228F3EDF303C0FDF3EDF306FF03C0\r
+021DF3EDF31E82FAF3EDF3F3EDF30682FDF3EDF303820215E6F2FF03820205F3\r
+EDF32A820216F3EDF303C006FF03C0021CF3EDF30382020AF3EDF312820215E6\r
+F2FF1282020EF3EDF303820216F3EDF306FF03C0021CF3EDF30382020CF3EDF3\r
+0382FAF3EDF3F3EDF303820205E6F2FF0382FDE6F2FF1B820205E6F2FF038202\r
+14F3EDF303820215F3EDF303C003FF03C0021BF3EDF303820210F3EDF3038202\r
+05E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF03820214F3EDF30382\r
+0215F3EDF303C003FF03C0021BF3EDF303820210F3EDF303820205E6F2FF0682\r
+0205E6F2FF03820208E6F2FF03820214F3EDF303820215F3EDF303C003FF03C0\r
+021BF3EDF303820210F3EDF303820215E6F2FF03820214F3EDF303820214F3ED\r
+F303FF03C003FF03C0021BF3EDF303820210F3EDF303820215E6F2FF03820214\r
+F3EDF303820214F3EDF303FF03C003FF03C0021BF3EDF303820210F3EDF30382\r
+0215E6F2FF03820213F3EDF303820215F3EDF303FF03C003FF03C0021BF3EDF3\r
+03820210F3EDF345820213F3EDF303820215F3EDF303FF03C003FF03C0021BF3\r
+EDF303820239F3EDF309820214F3EDF303FF03C003FF03C0021BF3EDF3038202\r
+3AF3EDF306820214F3EDF303FF03C003FF03C0021BF3EDF30382023AF3EDF303\r
+820215F3EDF303FF03C003FF03C0020EF3EDF348820225F3EDF34582020AF3ED\r
+F303FF03C003FF03C0020EF3EDF303820216E6F2FF03820225F3EDF303820215\r
+E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820216E6F2FF0382\r
+0225F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF3\r
+03820216E6F2FF03820225F3EDF303820215E6F2FF0382020AF3EDF303FF03C0\r
+03FF03C0020EF3EDF303820205E6F2FF0382FDE6F2FF1B820206E6F2FF038202\r
+25F3EDF303820205E6F2FF0382FDE6F2FF1B820205E6F2FF0382020AF3EDF303\r
+FF03C003FF03C0020EF3EDF303820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C\r
+820206E6F2FF0382FDF3EDF306820204F3EDF30382FDF3EDF30F82FAF3EDF3F3\r
+EDF303820214F3EDF303820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205\r
+E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820205E6F2FF0682\r
+0205E6F2FF03820209E6F2FF0F82020FF3EDF306820210F3EDF303820205E6F2\r
+FF06820205E6F2FF03820208E6F2FF0382020AF3EDF303FF03C003FF03C0020E\r
+F3EDF303820216E6F2FF0382FAF3EDF3F3EDF303820212F3EDF30382020FF3ED\r
+F303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820216\r
+E6F2FF03820216F3EDF30382020EF3EDF303820215E6F2FF0382020AF3EDF303\r
+FF03C003FF03C0020EF3EDF303820216E6F2FF03820217F3EDF30382020DF3ED\r
+F303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020EF3EDF303820216\r
+E6F2FF03820219F3EDF30382020BF3EDF303820215E6F2FF0382020AF3EDF303\r
+FF03C003FF03C0020EF3EDF348820219F3EDF30382020BF3EDF34582020AF3ED\r
+F303FF03C003FF03C00214F3EDF30982020AF3EDF30982021CF3EDF303820218\r
+F3EDF303820212F3EDF303FF03C003FF03C00216F3EDF30382020BF3EDF30982\r
+0234F3EDF303820212F3EDF303FF03C003FF03C00215F3EDF30382020FF3EDF3\r
+0382021CF3EDF303820217F3EDF306820210F3EDF303FF03C003FF03C00214F3\r
+EDF303820211F3EDF30382021CF3EDF303820215F3EDF30382030003820210F3\r
+EDF303FF03C003FF03C00214F3EDF303820212F3EDF30382021CF3EDF3038202\r
+15F3EDF303820211F3EDF303FF03C003FF03C00213F3EDF303820214F3EDF303\r
+82FDF3EDF30382021AF3EDF303820214F3EDF303820211F3EDF303FF03C003FF\r
+03C00204F3EDF345820210F3EDF30382021AF3EDF30382020AF3EDF345820204\r
+F3EDF303FF03C003FF03C00204F3EDF303820215E6F2FF03820211F3EDF30382\r
+0219F3EDF30382020AF3EDF303820215E6F2FF03820204F3EDF303FF03C003FF\r
+03C00204F3EDF303820215E6F2FF03820212F3EDF306820218F3EDF303820209\r
+F3EDF303820215E6F2FF03820204F3EDF303FF03C003FF03C00204F3EDF30382\r
+0215E6F2FF03820214F3EDF303820221F3EDF303820215E6F2FF03820204F3ED\r
+F303FF03C003FF03C00204F3EDF303820205E6F2FF0382FDE6F2FF1B820205E6\r
+F2FF03820214F3EDF306820218F3EDF303820207F3EDF303820205E6F2FF0382\r
+FDE6F2FF1B820205E6F2FF03820204F3EDF303FF03C003FF03C00204F3EDF303\r
+820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF0382022FF3EDF3\r
+03820206F3EDF303820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2\r
+FF03820204F3EDF303FF03C003FF03C00204F3EDF303820205E6F2FF06820205\r
+E6F2FF03820208E6F2FF03820225F3EDF312820203F3EDF31B820205E6F2FF06\r
+820205E6F2FF03820208E6F2FF03820204F3EDF303FF03C003FF03C00204F3ED\r
+F303820215E6F2FF03820217F3EDF303820209F3EDF303820211F3EDF30682FD\r
+F3EDF303820215E6F2FF03820204F3EDF303FF03C003FF03C00204F3EDF30382\r
+0215E6F2FF03820217F3EDF303820208F3EDF303820212F3EDF30382FAF3EDF3\r
+F3EDF303820215E6F2FF03820204F3EDF303FF03C003FF03C00204F3EDF30382\r
+0215E6F2FF03820218F3EDF303820218F3EDF303820204F3EDF303820215E6F2\r
+FF03820204F3EDF303FF03C003FF03C00204F3EDF303820215E6F2FF03820218\r
+F3EDF303820206F3EDF303820212F3EDF303820203F3EDF303820215E6F2FF03\r
+820204F3EDF303FF03C003FF03C00204F3EDF345820219F3EDF303820205F3ED\r
+F303820216F3EDF345820204F3EDF303FF03C003FF03C00213F3EDF30682021F\r
+F3EDF303820204F3EDF303820214F3EDF30382020BF3EDF303820211F3EDF303\r
+FF03C003FF03C00213F3EDF303820220F3EDF303820204F3EDF303820215F3ED\r
+F303820209F3EDF303820212F3EDF303FF03C003FF03C00214F3EDF30382021F\r
+F3EDF303820203F3EDF303820216F3EDF303820208F3EDF306820212F3EDF303\r
+FF03C003FF03C00214F3EDF30382021FF3EDF303820203F3EDF303820218F3ED\r
+F303820206F3EDF30382030003820211F3EDF303FF03C003FF03C00215F3EDF3\r
+03820222F3EDF30382021FF3EDF306820212F3EDF303FF03C003FF03C00215F3\r
+EDF30382023CF3EDF303820205F3EDF303820213F3EDF303FF03C003FF03C002\r
+0DF3EDF345820227F3EDF34582020AF3EDF303FF03C003FF03C0020CF3EDF3FD\r
+E6F2FF03820215E6F2FF0382020FF3EDF303820217F3EDF303820215E6F2FF03\r
+82020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03820215E6F2FF0382\r
+0214F3EDF303820212F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF\r
+03C0020CF3EDF3FDE6F2FF03820215E6F2FF0382020EF3EDF303820218F3EDF3\r
+03820215E6F2FF0382020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03\r
+820205E6F2FF0382FDE6F2FF1B820205E6F2FF0382020EF3EDF303820205F3ED\r
+F303820212F3EDF303820205E6F2FF0382FDE6F2FF1B820205E6F2FF0382020A\r
+F3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03820205E6F2FF0682FDE6F2\r
+FF0982FDE6F2FF0C820205E6F2FF0382020EF3EDF303820205F3EDF303820212\r
+F3EDF303820205E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF038202\r
+0AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF03820205E6F2FF06820205\r
+E6F2FF03820208E6F2FF0382020DF3EDF303820206F3EDF303820212F3EDF303\r
+820205E6F2FF06820205E6F2FF03820208E6F2FF0382020AF3EDF303FF03C003\r
+FF03C0020CF3EDF3FDE6F2FF03820215E6F2FF0382020DF3EDF303820206F3ED\r
+F303820212F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020C\r
+F3EDF3FDE6F2FF03820215E6F2FF0382020DF3EDF303820206F3EDF303820212\r
+F3EDF303820215E6F2FF0382020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6\r
+F2FF03820215E6F2FF0382020DF3EDF303820219F3EDF303820215E6F2FF0382\r
+020AF3EDF303FF03C003FF03C0020CF3EDF3FDE6F2FF4582020CF3EDF3038202\r
+1AF3EDF34582020AF3EDF303FF03C003FF03C0020CF3EDF3020CE6F2FF038202\r
+0AE6F2FF020DF3EDF303820207F3EDF303820212F3EDF3020BE6F2FF0382020A\r
+E6F2FF020BF3EDF303FF03C003FF03C00218F3EDF30682023CF3EDF303820215\r
+F3EDF303FF03C003FF03C00217F3EDF30982023DF3EDF303820214F3EDF303FF\r
+03C003FF03C00218F3EDF303820214F3EDF342820214F3EDF303820214F3EDF3\r
+03FF03C003FF03C00217F3EDF303820214F3EDF345820214F3EDF303820214F3\r
+EDF303FF03C003FF03C00217F3EDF303820214F3EDF303820215E6F2FF038202\r
+14F3EDF303820214F3EDF303FF03C003FF03C00217F3EDF303820214F3EDF303\r
+820215E6F2FF03820213F3EDF303820215F3EDF303FF03C003FF03C00218F3ED\r
+F30F82020FF3EDF303820215E6F2FF0382FDF3EDF30682020EF3EDF309820215\r
+F3EDF303FF03C003FF03C0021DF3EDF330820205E6F2FF0382FDE6F2FF1B8202\r
+05E6F2FF0C82FDF3EDF327820218F3EDF303FF03C003FF03C0022CF3EDF30382\r
+0205E6F2FF0682FDE6F2FF0982FDE6F2FF0C820205E6F2FF0382FAF3EDF3F3ED\r
+F303820226F3EDF303FF03C003FF03C0022CF3EDF303820205E6F2FF06820205\r
+E6F2FF03820208E6F2FF03820229F3EDF303FF03C003FF03C0022CF3EDF30382\r
+0215E6F2FF03820229F3EDF303FF03C003FF03C0022CF3EDF303820215E6F2FF\r
+03820229F3EDF303FF03C003FF03C0022CF3EDF303820215E6F2FF03820229F3\r
+EDF303FF03C003FF03C0022CF3EDF345820229F3EDF303FF03C003FF03C0026C\r
+F3EDF303FF03C003FF03C0026CF3EDF303FF03C003FF03C0026DF3EDF303C003\r
+FF03C0026CF3EDF306C003FF03C00217F3EDF303C0FDF3EDF306C0FDF3EDF309\r
+C0FDF3EDF30CC0FAF3EDF3F3EDF306C0FDF3EDF303C00205F3EDF309C0FAF3ED\r
+F3F3EDF30CC0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF303C0FDF3EDF306C0FDF3\r
+EDF306C0FAF3EDF3F3EDF306C00203F3EDF309C0FAF3EDF3F3EDF30CC00217F3\r
+EDF303C006FF03C00217F3EDF303C0FDF3EDF303C0FDF3EDF303C0FAF3EDF3F3\r
+EDF306C0FAF3EDF3F3EDF306C0FDF3EDF303C00203F3EDF303C00204F3EDF306\r
+C0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF306C0FAF3EDF3\r
+F3EDF306C0FAF3EDF3F3EDF303C00204F3EDF306C0FAF3EDF3F3EDF303C0FAF3\r
+EDF3F3EDF303C0FAF3EDF3F3EDF303C0FDF3EDF309C00216F3EDF303C006FF03\r
+C00217F3EDF309C0FDF3EDF303C0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF306C0\r
+FDF3EDF303C0FAF3EDF3F3EDF309C00203F3EDF303C00204F3EDF303C0FAF3ED\r
+F3F3EDF309C0FDF3EDF309C0FDF3EDF306C0FAF3EDF3F3EDF303C00204F3EDF3\r
+06C0FAF3EDF3F3EDF303C0FAF3EDF3F3EDF306C0FDF3EDF306C00219F3EDF303\r
+C009FF03C00216F3EDF303C0FAF3EDF3F3EDF303C0FDF3EDF306C0FDF3EDF303\r
+C0FAF3EDF3F3EDF306C0FAF3EDF3F3EDF306C0FDF3EDF303C0FDF3EDF303C0FA\r
+F3EDF3F3EDF306C00204F3EDF306C0FDF3EDF303C0FDF3EDF303C0FDF3EDF303\r
+C0FDF3EDF303C0FAF3EDF3F3EDF306C0FDF3EDF306C0FDF3EDF309C00203F3ED\r
+F309C0FDF3EDF303C0FAF3EDF3F3EDF306C00216F3EDF303C00CFF03C00216F3\r
+EDF309C00209F3EDF303C0020BF3EDF309C00215F3EDF306C00222F3EDF303C0\r
+0FFF03C00268F3EDF303C012FF03C00268F3EDF303C015FF06C00264F3EDF306\r
+C01EFF06C00260F3EDF306C02AFF7FC07FC019C0FDF3EDF306C018FFFE000000\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Router to Router Prime"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC Background"\r
+  Text ""\r
+  Bounds 400,416,1584,1440\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Router Connections"\r
+  Bounds 656,455,1332,537\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 544,720,800,848\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 448,912,704,1040\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 560,1104,816,1232\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 880,1264,1136,1392\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 1216,1104,1472,1232\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 1280,912,1536,1040\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 1216,720,1472,848\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 880,560,1136,688\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 496,1456,1520,1616\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Text ""\r
+  Bounds 1033,921,1048,936\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 13\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 12\r
+  Figure2 8\r
+  EndPoint1 1040,928\r
+  EndPoint2 1280,959\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 14\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 10\r
+  Figure2 12\r
+  EndPoint1 1015,688\r
+  EndPoint2 1040,928\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 15\r
+{\r
+  Text ""\r
+  Bounds 953,921,968,936\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 16\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 -1\r
+  Figure2 15\r
+  EndPoint1 923,704\r
+  EndPoint2 960,928\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 17\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 15\r
+  Figure2 5\r
+  EndPoint1 960,928\r
+  EndPoint2 761,1104\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 18\r
+{\r
+  Text ""\r
+  Bounds 1049,1129,1064,1144\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 19\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 9\r
+  Figure2 18\r
+  EndPoint1 1291,848\r
+  EndPoint2 1056,1136\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 20\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 18\r
+  Figure2 5\r
+  EndPoint1 1056,1136\r
+  EndPoint2 816,1156\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 21\r
+{\r
+  Style "Router to Router Prime"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 624,1504\r
+  EndPoint2 912,1504\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 22\r
+{\r
+  Style "Router to Router Prime"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 624,1552\r
+  EndPoint2 912,1552\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 21\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 23\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Primary Route"\r
+  Bounds 1064,1490,1244,1522\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Secondary Routes"\r
+  Bounds 1064,1536,1299,1568\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 25\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 8\r
+  Figure2 9\r
+  EndPoint1 1386,912\r
+  EndPoint2 1366,848\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 26\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 7\r
+  Figure2 8\r
+  EndPoint1 1366,1104\r
+  EndPoint2 1386,1040\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 27\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 4\r
+  Figure2 5\r
+  EndPoint1 614,1040\r
+  EndPoint2 650,1104\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 28\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 3\r
+  Figure2 4\r
+  EndPoint1 640,848\r
+  EndPoint2 608,912\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Figure 29\r
+{\r
+  Text ""\r
+  Bounds 1321,633,1336,648\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 30\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 29\r
+  Figure2 10\r
+  EndPoint1 1328,640\r
+  EndPoint2 1136,631\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 31\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 9\r
+  Figure2 29\r
+  EndPoint1 1336,720\r
+  EndPoint2 1328,640\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 32\r
+{\r
+  Text ""\r
+  Bounds 665,633,680,648\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 33\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 32\r
+  Figure2 3\r
+  EndPoint1 672,640\r
+  EndPoint2 672,720\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 34\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 10\r
+  Figure2 32\r
+  EndPoint1 880,631\r
+  EndPoint2 672,640\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 35\r
+{\r
+  Text ""\r
+  Bounds 1321,1337,1336,1352\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 36\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 35\r
+  Figure2 7\r
+  EndPoint1 1328,1344\r
+  EndPoint2 1338,1232\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 25\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 37\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 6\r
+  Figure2 35\r
+  EndPoint1 1136,1335\r
+  EndPoint2 1328,1344\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 25\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 38\r
+{\r
+  Text ""\r
+  Bounds 713,1337,728,1352\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 39\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 38\r
+  Figure2 6\r
+  EndPoint1 720,1344\r
+  EndPoint2 880,1336\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 40\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 5\r
+  Figure2 38\r
+  EndPoint1 700,1232\r
+  EndPoint2 720,1344\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+## Staples Section:\r
diff --git a/doc/whitepaper/silc_template.edg b/doc/whitepaper/silc_template.edg
new file mode 100644 (file)
index 0000000..aabc817
--- /dev/null
@@ -0,0 +1,2315 @@
+EDGE Diagram File\r
+Version 3.50\r
+\r
+## Globals Section:\r
+\r
+X -296\r
+Y 40\r
+Scale 100\r
+PosterRows 1\r
+PosterCols 1\r
+Color1 221,208,221\r
+Color2 192,192,192\r
+Color3 130,130,130\r
+Color4 0,0,0\r
+Color5 0,255,255\r
+Color6 0,0,255\r
+Color7 0,0,160\r
+Color8 128,0,128\r
+Color9 243,237,243\r
+Color10 219,219,219\r
+Color11 244,244,244\r
+Color12 255,242,230\r
+Color13 240,255,240\r
+Color14 0,128,0\r
+Color15 223,223,255\r
+Color16 255,255,0\r
+GridX 32\r
+GridY 32\r
+SnapX 16\r
+SnapY 16\r
+SnapConPtsCentersEdges TRUE\r
+ShadowColor 130,130,130\r
+ShadowX 11\r
+ShadowY 11\r
+ShowGrid TRUE\r
+AlignToGrid TRUE\r
+AlignToGridConPts TRUE\r
+AttachMode 1\r
+SBarWidth 164\r
+SBarFigCols 3\r
+SBarLblCols 1\r
+SBarConCols 2\r
+SBarFigHeight 32\r
+SBarLblHeight 32\r
+SBarConHeight 24\r
+Parent ""\r
+LargeDropMenus FALSE\r
+LastEnd "block"\r
+LastEndLen 25\r
+ConPointMarks 0\r
+CornerRadius 18\r
+Template "silc_template.edg"\r
+\r
+DevMode 580\r
+{\r
+4850204C617365724A6574203131303000000000000000000000000000000000\r
+000400049400B0010F77010401000900990B3408640001000700580201000100\r
+580202000000323130207820323937206D6D0000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000001000000\r
+010000000100000001000000000000000000000000004850204C617365724A65\r
+742031313030000000000000000000000000000000004C5054313A0000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000044021005100500004402540300000300\r
+8403DC0501000000000000000000000000000000000000000000000064000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000001000100000002000000000000000000000000000000\r
+0100000002000100000000000000480002000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000100000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+0000000000000000000000000000000000000000000000000000000000000000\r
+00000100\r
+}\r
+\r
+DevNames 68\r
+{\r
+080011002200010077696E73706F6F6C004850204C617365724A657420313130\r
+30004C5054313A00000000000000000000000000000000000000000000000000\r
+94395900\r
+}\r
+\r
+Preview 700\r
+{\r
+FC424DF6A20600FF360300FF280300FF700300FF7C0300FD0100180900FAC30E\r
+0000C30E0A007FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF\r
+7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF08FFFE000000\r
+}\r
+\r
+## Graphics Section:\r
+\r
+## Figure Symbols Section:\r
+\r
+FigureSymbol "data"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+}\r
+\r
+FigureSymbol "rounded box 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >150,0,<850,1000\r
+    Rect 0,>150,1000,<850\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<700,1000,1000\r
+    Ellipse 0,<700,>300,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<850\r
+    Arc <700,<700,1000,1000 <850,1000 1000,<850\r
+    Line >150,1000 <850,1000\r
+    Arc 0,<700,>300,1000 0,<850 >150,1000\r
+    Line 0,>150 0,<850\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 <874,1000 <919,<985 <957,<957 <985,<919 1000,<874 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<874-\r
+   >16,<919 >44,<957 >82,<985 >127,1000 <874,1000\r
+}\r
+\r
+FigureSymbol "connector"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 100,100,900,900\r
+  Fill\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Ellipse 0,0,1000,1000\r
+  }\r
+  Hot 17 599,1000 783,924 924,783 1000,599 1000,402 924,218 783,77 599,1-\r
+   402,1 218,77 77,218 1,402 1,599 77,783 218,924 402,1000-\r
+   599,1000\r
+}\r
+\r
+FigureSymbol "decision"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,255,800,750\r
+  TextBox 800,375,950,625\r
+  TextBox 425,750,575,1000\r
+  TextBox 50,375,200,625\r
+  TextBox 425,0,575,250\r
+  Fill\r
+  {\r
+    Polygon 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,0 1000,500 500,1000 0,500 500,0\r
+  }\r
+  Hot 5 500,0 1000,500 500,1000 0,500 500,0\r
+}\r
+\r
+FigureSymbol "box 3d"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<850,<850\r
+  Fill\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Rect >100,>100,1000,1000\r
+    Polygon 4 <900,0 1000,>100 <900,>100 <900,0\r
+    Polygon 4 0,<900 >100,1000 >100,<900 0,<900\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,<900,<900\r
+    Polyline 5 <900,0 1000,>100 1000,1000 >100,1000 0,<900\r
+    Line <900,<900 1000,1000\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,<800,<800\r
+    Polyline 5 <800,0 1000,>200 1000,1000 >200,1000 0,<800\r
+    Line <800,<800 1000,1000\r
+  }\r
+  Hot 7 0,0 <900,0 1000,>100 1000,1000 >100,1000 0,<900 0,0\r
+}\r
+\r
+FigureSymbol "octagon"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+     0,>333\r
+  }\r
+  Hot 9 0,>333 >333,0 <666,0 1000,>333 1000,<666 <666,1000 >333,1000 0,<666-\r
+   0,>333\r
+}\r
+\r
+FigureSymbol "dog ear"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>150,<850,<850\r
+  Fill\r
+  {\r
+    Polygon 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+    Polyline 3 <850,0 <850,>150 1000,>150\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 6 0,0 <700,0 1000,>300 1000,1000 0,1000 0,0\r
+    Polyline 3 <700,0 <700,>300 1000,>300\r
+  }\r
+  Hot 6 0,0 <850,0 1000,>150 1000,1000 0,1000 0,0\r
+}\r
+\r
+FigureSymbol "disk simple"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>250,<900,<900\r
+  Fill\r
+  {\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Ellipse 0,<800,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line 0,>100 0,<900\r
+    Line 1000,>100 1000,<900\r
+    Ellipse 0,0,1000,>200\r
+    Arc 0,<800,1000,1000 0,<900 1000,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Line 0,200 0,800\r
+    Line 1000,100 1000,900\r
+    Ellipse 0,0,1000,400\r
+    Arc 0,600,1000,1000 0,800 1000,800\r
+  }\r
+  Hot 17 1,>81 77,>44 218,>16 402,>1 599,>1 783,>16 924,>44 1000,>81-\r
+   1000,<920 924,<957 783,<985 599,<1000 402,<1000 218,<985 77,<957 1,<920-\r
+   1,>81\r
+}\r
+\r
+FigureSymbol "input/ouput"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Polygon 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+  }\r
+  Hot 5 >200,0 1000,0 <800,1000 0,1000 >200,0\r
+}\r
+\r
+FigureSymbol "terminal"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>100,<800,<900\r
+  Fill\r
+  {\r
+    Rect >500,0,<500,1000\r
+    Ellipse 0,0,>999,1000\r
+    Ellipse <1,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Line >500,0 <500,0\r
+    Line >500,1000 <500,1000\r
+    Arc 0,0,>999,1000 >500,0 >500,1000\r
+    Arc <1,0,1000,1000 <500,1000 <500,0\r
+  }\r
+  Hot 17 >401,1 >217,77 >77,218 >1,402 >1,599 >77,783 >217,924 >401,1000-\r
+   <599,1000 <783,924 <923,783 <999,599 <999,402 <923,218 <783,77 <599,1-\r
+   >401,1\r
+}\r
+\r
+FigureSymbol "preparation"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >150,>100,<850,<900\r
+  Fill\r
+  {\r
+    Polygon 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+  }\r
+  Hot 7 >200,0 <800,0 1000,500 <800,1000 >200,1000 0,500 >200,0\r
+}\r
+\r
+FigureSymbol "cloud"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox 200,200,800,800\r
+  Fill\r
+  {\r
+    Ellipse 84,114,672,702\r
+    Ellipse 421,89,859,527\r
+    Ellipse 573,286,995,708\r
+    Ellipse 263,492,721,950\r
+    Ellipse 0,528,423,952\r
+  }\r
+  Outline\r
+  {\r
+    Arc 84,114,672,702 500,140 120,550\r
+    Arc 421,89,859,527 860,300 500,140\r
+    Arc 573,286,995,708 720,700 860,300\r
+    Arc 263,492,721,950 350,900 720,700\r
+    Arc 0,528,423,952 120,550 350,900\r
+  }\r
+  Hot 40 350,900 363,915 447,950 538,950 622,915 686,851 721,767 720,700-\r
+   743,708 826,708 904,676 963,617 995,539 995,456 963,378 904,319-\r
+   860,300 859,265 826,185 764,123 684,90 597,90 517,123 500,140-\r
+   436,115 321,115 212,160 130,242 85,351 85,466 120,550 92,561-\r
+   33,621 1,699 1,782 33,860 92,920 170,952 253,952 350,900\r
+}\r
+\r
+FigureSymbol "document"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<750\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,<900\r
+    Ellipse 0,<800,500,1000\r
+    Chord 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 0,<900 0,0 1000,0 1000,<900\r
+    Arc 0,<800,500,1000 0,<900 500,<900\r
+    Arc 500,<800,1000,1000 1000,<900 500,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Polyline 4 0,900 0,0 1000,0 1000,900\r
+    Arc 0,600,550,1000 0,750 550,750\r
+    Arc 480,700,1000,1000 1000,850 480,850\r
+  }\r
+  Hot 11 1,<941 147,<1000 354,<1000 500,<941 501,<860 647,<801 854,<801 1000,<860-\r
+   1000,0 0,0 1,<941\r
+}\r
+\r
+FigureSymbol "bevel"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >125,>125,<875,<875\r
+  Fill\r
+  {\r
+    Rect 0,0,1000,1000\r
+  }\r
+  Outline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect >75,>75,<925,<925\r
+    Line 0,0 >75,>75\r
+    Line 0,1000 >75,<925\r
+    Line <925,<925 1000,1000\r
+    Line <925,>75 1000,0\r
+  }\r
+  MiniOutline\r
+  {\r
+    Rect 0,0,1000,1000\r
+    Rect 150,150,850,850\r
+    Line 0,0 150,150\r
+    Line 0,1000 150,850\r
+    Line 850,850 1000,1000\r
+    Line 850,150 1000,0\r
+  }\r
+}\r
+\r
+FigureSymbol "arrow right 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >50,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+  }\r
+  Hot 8 0,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800 0,<800 0,>200\r
+}\r
+\r
+FigureSymbol "arrow left 2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<950,<750\r
+  Fill\r
+  {\r
+    Polygon 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+  }\r
+  Hot 8 1000,>200 >300,>200 >300,0 0,500 >300,1000 >300,<800 1000,<800 1000,>200\r
+}\r
+\r
+FigureSymbol "arrow up"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<950\r
+  Fill\r
+  {\r
+    Polygon 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+  }\r
+  Hot 8 500,0 1000,>350 <850,>350 <850,1000 >150,1000 >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow up/down 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>350,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+     >150,>350 0,>350 500,0\r
+  }\r
+  Hot 11 500,0 1000,>350 <850,>350 <850,<650 1000,<650 500,1000 0,<650 >150,<650-\r
+   >150,>350 0,>350 500,0\r
+}\r
+\r
+FigureSymbol "arrow left/right 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >300,>250,<700,<750\r
+  Fill\r
+  {\r
+    Polygon 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+     >300,<800 >300,1000 0,500\r
+  }\r
+  Hot 11 0,500 >300,0 >300,>200 <700,>200 <700,0 1000,500 <700,1000 <700,<800-\r
+   >300,<800 >300,1000 0,500\r
+}\r
+\r
+FigureSymbol "arrow down"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >200,>50,<800,<650\r
+  Fill\r
+  {\r
+    Polygon 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+  }\r
+  Hot 8 500,1000 1000,<650 <850,<650 <850,0 >150,0 >150,<650 0,<650 500,1000\r
+}\r
+\r
+FigureSymbol "callout 3"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 0,1000 >400,<800 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 0,1000 >400,<800 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 >150,<800 0,1000 >400,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "callout 4"\r
+{\r
+  FixedAspect FALSE\r
+  Height 0\r
+  Width 0\r
+  TextBox >100,>100,<900,<700\r
+  Fill\r
+  {\r
+    Polygon 11 0,>150 0,<650 >150,<800 <600,<800 1000,1000 <850,<800 1000,<650 1000,>150-\r
+     <900,0 >150,0 0,>150\r
+    Rect >300,0,<700,<800\r
+    Rect 0,>300,1000,<500\r
+    Ellipse 0,0,>300,>300\r
+    Ellipse <700,0,1000,>300\r
+    Ellipse <700,<500,1000,<800\r
+    Ellipse 0,<500,>300,<800\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>300,>300 >150,0 0,>150\r
+    Line >150,0 <850,0\r
+    Arc <700,0,1000,>300 1000,>150 <850,0\r
+    Line 1000,>150 1000,<650\r
+    Arc <700,<500,1000,<800 <850,<800 1000,<650\r
+    Polyline 4 >150,<800 <600,<800 1000,1000 <850,<800\r
+    Arc 0,<500,>300,<800 0,<650 >150,<800\r
+    Line 0,>150 0,<650\r
+  }\r
+  Hot 24 <874,<800 <919,<785 <957,<757 <985,<719 1000,<674 1000,>127 <985,>82 <957,>44-\r
+   <919,>16 <874,0 >127,0 >82,>16 >44,>44 >16,>82 0,>127 0,<674-\r
+   >16,<719 >44,<757 >82,<785 >127,<800 <600,<800 1000,1000 <850,<800 <874,<800\r
+}\r
+\r
+FigureSymbol "banner2"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 512\r
+  TextBox >325,>25,<725,<855\r
+  Fill\r
+  {\r
+    Polygon 23 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<880 <600,<880 <600,<910-\r
+     <600,<964 <600,<975 <605,<986 <613,<994 <630,1000 1000,1000 <890,560 1000,>120-\r
+     <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 43 0,1000 >370,1000 >387,<994 >395,<986 >400,<975 >400,<964 >395,<953 >387,<945-\r
+     >370,<940 >330,<940 >313,<934 >305,<926 >300,<915 >300,<904 >305,<893 >313,<880-\r
+     >330,<880 <670,<880 <687,<880 <695,<893 <700,<904 <700,<915 <695,<926 <687,<934-\r
+     <670,<940 <630,<940 <613,<945 <605,<953 <600,<964 <600,<975 <605,<986 <613,<994-\r
+     <630,1000 1000,1000 <890,560 1000,>120 <700,>120 <700,0 >300,0 >300,>120-\r
+     0,>120 >110,560 0,1000\r
+    Line >300,<910 >300,>120\r
+    Line >400,<880 >400,<970\r
+    Line <700,<910 <700,>120\r
+    Line <600,<880 <600,<970\r
+  }\r
+  Hot 15 0,1000 >400,1000 >400,<880 <600,<880 <600,1000 1000,1000 <890,560 1000,>120-\r
+   <700,>120 <700,0 >300,0 >300,>120 0,>120 >110,560 0,1000\r
+}\r
+\r
+FigureSymbol "scroll1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox >170,>170,<830,<950\r
+  Fill\r
+  {\r
+    Polygon 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 33 >120,<880 >51,<880 >33,<887 >18,<898 >7,<913 0,<931 0,<950 >7,<968-\r
+     >18,<983 >33,<994 >51,1000 <787,1000 <821,<994 <849,<983 <869,<968 <880,<950-\r
+     <880,>120 <950,>120 <968,>114 <983,>103 <994,>88 1000,>70 1000,>51 <994,>33-\r
+     <983,>18 <968,>7 <950,0 >171,0 >153,7 >138,>18 >127,>33 >120,>51-\r
+     >120,<880\r
+    Polyline 6 >120,<880 >120,<950 >114,<968 >103,<983 >88,<994 >70,1000\r
+    Polyline 11 >190,0 >208,7 >223,>18 >234,>33 >240,>51 >240,>70 >234,>88 >223,>103-\r
+     >208,>114 >190,>120 <880,>120\r
+    Polyline 6 >66,<880 >77,<886 >85,<894 >77,<935 >66,<940 >120,940\r
+    Polyline 10 >240,>60 >163,>60 >158,>66 >153,>74 >151,>85 >151,>96 >153,>107 >158,>115-\r
+     >163,>120 >190,>120\r
+  }\r
+  Hot 9 >120,<880 0,<880 0,1000 <880,1000 <880,>120 1000,>120 1000,0 >120,0-\r
+   >120,<880\r
+}\r
+\r
+FigureSymbol "explosion"\r
+{\r
+  FixedAspect FALSE\r
+  Height 192\r
+  Width 192\r
+  TextBox 200,300,800,700\r
+  Fill\r
+  {\r
+    Polygon 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+     975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+     400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+     0,100\r
+  }\r
+  Hot 25 0,100 330,300 400,100 500,280 775,0 750,260 850,200 780,325-\r
+   975,385 820,480 1000,600 780,585 840,830 665,675 605,900 485,680-\r
+   400,1000 360,725 215,805 270,630 0,680 190,550 0,400 200,340-\r
+   0,100\r
+}\r
+\r
+FigureSymbol "rounded box 1"\r
+{\r
+  FixedAspect FALSE\r
+  Height 128\r
+  Width 192\r
+  TextBox >100,>100,<900,<900\r
+  Fill\r
+  {\r
+    Rect >100,0,<900,1000\r
+    Rect 0,>100,1000,<900\r
+    Ellipse 0,0,>200,>200\r
+    Ellipse <800,0,1000,>200\r
+    Ellipse <800,<800,1000,1000\r
+    Ellipse 0,<800,>200,1000\r
+  }\r
+  Outline\r
+  {\r
+    Arc 0,0,>200,>200 >100,0 0,>100\r
+    Line >100,0 <900,0\r
+    Arc <800,0,1000,>200 1000,>100 <900,0\r
+    Line 1000,>100 1000,<900\r
+    Arc <800,<800,1000,1000 <900,1000 1000,<900\r
+    Line >100,1000 <900,1000\r
+    Arc 0,<800,>200,1000 0,<900 >100,1000\r
+    Line 0,>100 0,<900\r
+  }\r
+  MiniOutline\r
+  {\r
+    Arc 0,0,>500,>500 >250,0 0,>250\r
+    Line >250,0 <750,0\r
+    Arc <500,0,1000,>500 1000,>250 <750,0\r
+    Line 1000,>250 1000,<750\r
+    Arc <500,<500,1000,1000 <750,1000 1000,<750\r
+    Line >250,1000 <750,1000\r
+    Arc 0,<500,>500,1000 0,<750 >250,1000\r
+    Line 0,>250 0,<750\r
+  }\r
+  Hot 21 0,>85 >11,>55 >29,>29 >55,>11 >85,0 <916,0 <946,>11 <972,>29-\r
+   <990,>55 1000,>85 <1000,<916 <990,<946 <972,<972 <946,<990 <916,1000 >85,1000-\r
+   >55,<990 >29,<972 >11,<946 0,<916 0,>85\r
+}\r
+\r
+## End Symbols Section:\r
+\r
+EndSymbol "block"\r
+{\r
+  LineTo 100\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 1000,300 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "block short"\r
+{\r
+  LineTo 60\r
+  Fill\r
+  {\r
+    Polygon 4 500,500 800,300 800,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 4 500,500 800,300 800,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "taper"\r
+{\r
+  LineTo 80\r
+  Fill\r
+  {\r
+    Polygon 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+  Outline\r
+  {\r
+    Polyline 5 500,500 1000,300 900,500 1000,700 500,500\r
+  }\r
+}\r
+\r
+EndSymbol "stick"\r
+{\r
+  Outline\r
+  {\r
+    Line 500,500 1000,300\r
+    Line 500,500 1000,700\r
+  }\r
+}\r
+\r
+## Figure Styles Section:\r
+\r
+FigureStyle "Title"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "A diagram title"\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Title Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0022\r
+  Behavior 0x00A241D2\r
+  Symbol "lbl"\r
+  TypeSize 12\r
+  TypeWeight 400\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label"\r
+{\r
+  Label TRUE\r
+  Height 64\r
+  Width 128\r
+  Description "An annotation, comment, or title"\r
+  TextFormat 0x0044\r
+  Behavior 0x008241A1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Label Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241C9\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Note Serif"\r
+{\r
+  Label TRUE\r
+  Height 0\r
+  Width 0\r
+  TextColor 0,0,160\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00A241E1\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rectangle 3D"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "box 3d"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Paper"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "dog ear"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Rounded"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "rounded box 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Ellipse"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Terminal"\r
+{\r
+  Height 64\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "terminal"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Square"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 161\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Circle"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 160\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "connector"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Stop"\r
+{\r
+  Height 128\r
+  Width 128\r
+  DefaultText "STOP"\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "octagon"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Diamond"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "decision"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Frame"\r
+{\r
+  Height 160\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "bevel"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Disk"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "disk simple"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Slanted"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "input/ouput"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Hexagon"\r
+{\r
+  HasButton TRUE\r
+  Height 161\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "preparation"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Inverse"\r
+{\r
+  Height 160\r
+  Width 288\r
+  FillColor 0,0,0\r
+  TextColor 255,255,255\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Shadow"\r
+{\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Idea"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Document"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "document"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow right 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left 2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow right/left"\r
+{\r
+  Height 128\r
+  Width 289\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow left/right 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow down"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Arrow up/down"\r
+{\r
+  Height 161\r
+  Width 128\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "arrow up/down 1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Left"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 3"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Callout Right"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 288\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "callout 4"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Banner"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 512\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "banner2"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Scroll"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "scroll1"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "Explosion"\r
+{\r
+  HasButton TRUE\r
+  Height 192\r
+  Width 192\r
+  BorderWidth 3\r
+  TextFormat 0x0022\r
+  Behavior 0x00024E12\r
+  Symbol "explosion"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC INFO"\r
+{\r
+  HasButton TRUE\r
+  Height 160\r
+  Width 1472\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Background"\r
+{\r
+  HasButton TRUE\r
+  Height 1440\r
+  Width 1344\r
+  BorderColor 192,192,192\r
+  FillColor 239,231,239\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 10\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Server"\r
+{\r
+  HasButton TRUE\r
+  Height 96\r
+  Width 192\r
+  BorderColor 130,130,130\r
+  FillColor 244,244,244\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Router"\r
+{\r
+  HasButton TRUE\r
+  Height 128\r
+  Width 256\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Client"\r
+{\r
+  HasButton TRUE\r
+  Height 64\r
+  Width 96\r
+  BorderColor 130,130,130\r
+  FillColor 219,219,219\r
+  TextColor 130,130,130\r
+  BorderWidth 2\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+FigureStyle "SILC Network Cloud"\r
+{\r
+  HasButton TRUE\r
+  Height 480\r
+  Width 672\r
+  BorderColor 227,225,255\r
+  FillColor 240,240,255\r
+  TextColor 192,192,192\r
+  BorderWidth 3\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "cloud"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+## Connector Styles Section:\r
+\r
+ConnectorStyle "Plain"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Dashed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Open 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Flow Closed 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Stick 2"\r
+{\r
+  HasButton TRUE\r
+  End1 "stick"\r
+  End2 "stick"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open"\r
+{\r
+  End1 "null"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Taper Open 2"\r
+{\r
+  End1 "taper"\r
+  End2 "taper"\r
+  End1Length 32\r
+  End2Length 32\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End1FillColor 255,255,255\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Plain Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Dashed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "null"\r
+  End1Length 18\r
+  End2Length 18\r
+  PenStyle 2\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Closed Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "Flow Open Smoothed"\r
+{\r
+  HasButton TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  End2FillColor 255,255,255\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Router to Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+ConnectorStyle "SILC Server To Router"\r
+{\r
+  HasButton TRUE\r
+  End1 "block short"\r
+  End2 "block short"\r
+  End1Length 18\r
+  End2Length 18\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Behavior 0x00000000\r
+}\r
+\r
+ConnectorStyle "Router to Router Prime"\r
+{\r
+  HasButton TRUE\r
+  End1 "block"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 36\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+## Figures & Connectors Section:\r
+\r
+Figure 1\r
+{\r
+  Style "SILC Background"\r
+  Text ""\r
+  Bounds 400,416,1584,1440\r
+  BorderColor 192,192,192\r
+  FillColor 243,237,243\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  TextFormat 0x0042\r
+  Behavior 0x00024E22\r
+  Symbol "rounded box 1"\r
+  TypeSize 18\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 2\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Router Connections"\r
+  Bounds 656,455,1332,537\r
+  TextColor 192,192,192\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 20\r
+  TypeWeight 700\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 3\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 544,720,800,848\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 4\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 448,912,704,1040\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 5\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 560,1104,816,1232\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 6\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 880,1264,1136,1392\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 7\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 1216,1104,1472,1232\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 8\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 1280,912,1536,1040\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 9\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 1216,720,1472,848\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 10\r
+{\r
+  Style "SILC Router"\r
+  Text "Router"\r
+  Bounds 880,560,1136,688\r
+  BorderColor 130,130,130\r
+  FillColor 255,242,230\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  TextFormat 0x0A22\r
+  Behavior 0x00024E12\r
+  Symbol "data"\r
+  TypeSize 12\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 11\r
+{\r
+  Style "SILC INFO"\r
+  Text "INFO"\r
+  Bounds 496,1456,1520,1616\r
+  BorderColor 192,192,192\r
+  FillColor 252,252,252\r
+  TextColor 130,130,130\r
+  BorderWidth 3\r
+  Shadow TRUE\r
+  BindToStyle FALSE\r
+  TextFormat 0x0044\r
+  Behavior 0x00024E11\r
+  Symbol "data"\r
+  TypeSize 8\r
+  TypeWeight 700\r
+  TypeOutPrec 3\r
+  TypeClpPrec 2\r
+  TypeQuality 1\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 12\r
+{\r
+  Text ""\r
+  Bounds 1033,921,1048,936\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 13\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 12\r
+  Figure2 8\r
+  EndPoint1 1040,928\r
+  EndPoint2 1280,959\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 14\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 10\r
+  Figure2 12\r
+  EndPoint1 1015,688\r
+  EndPoint2 1040,928\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 15\r
+{\r
+  Text ""\r
+  Bounds 953,921,968,936\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 16\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 -1\r
+  Figure2 15\r
+  EndPoint1 923,704\r
+  EndPoint2 960,928\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 17\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 15\r
+  Figure2 5\r
+  EndPoint1 960,928\r
+  EndPoint2 761,1104\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 18\r
+{\r
+  Text ""\r
+  Bounds 1049,1129,1064,1144\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 19\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 9\r
+  Figure2 18\r
+  EndPoint1 1291,848\r
+  EndPoint2 1056,1136\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 20\r
+{\r
+  Style "SILC Router to Router"\r
+  Figure1 18\r
+  Figure2 5\r
+  EndPoint1 1056,1136\r
+  EndPoint2 816,1156\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 4\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 21\r
+{\r
+  Style "Router to Router Prime"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 624,1504\r
+  EndPoint2 912,1504\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 22\r
+{\r
+  Style "Router to Router Prime"\r
+  Figure1 -1\r
+  Figure2 -1\r
+  EndPoint1 624,1552\r
+  EndPoint2 912,1552\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 36\r
+  End2Length 25\r
+  PenStyle 21\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 23\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Primary Route"\r
+  Bounds 1064,1490,1244,1522\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Figure 24\r
+{\r
+  Label TRUE\r
+  Style "Title"\r
+  Text "Secondary Routes"\r
+  Bounds 1064,1536,1299,1568\r
+  TextColor 130,130,130\r
+  TextFormat 0x0022\r
+  Behavior 0x00824192\r
+  Symbol "lbl"\r
+  TypeSize 8\r
+  TypeItalic TRUE\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 25\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 9\r
+  Figure2 8\r
+  EndPoint1 1366,848\r
+  EndPoint2 1386,912\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 26\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 8\r
+  Figure2 7\r
+  EndPoint1 1386,1040\r
+  EndPoint2 1366,1104\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 27\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 5\r
+  Figure2 4\r
+  EndPoint1 650,1104\r
+  EndPoint2 614,1040\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Connector 28\r
+{\r
+  Style "Flow Closed"\r
+  Figure1 4\r
+  Figure2 3\r
+  EndPoint1 608,912\r
+  EndPoint2 640,848\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000000\r
+}\r
+\r
+Figure 29\r
+{\r
+  Text ""\r
+  Bounds 1321,633,1336,648\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 30\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 10\r
+  Figure2 29\r
+  EndPoint1 1136,631\r
+  EndPoint2 1328,640\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 31\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 29\r
+  Figure2 9\r
+  EndPoint1 1328,640\r
+  EndPoint2 1336,720\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 32\r
+{\r
+  Text ""\r
+  Bounds 665,633,680,648\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 33\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 3\r
+  Figure2 32\r
+  EndPoint1 672,720\r
+  EndPoint2 672,640\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 34\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 32\r
+  Figure2 10\r
+  EndPoint1 672,640\r
+  EndPoint2 880,631\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 35\r
+{\r
+  Text ""\r
+  Bounds 1321,1337,1336,1352\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 36\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 7\r
+  Figure2 35\r
+  EndPoint1 1338,1232\r
+  EndPoint2 1328,1344\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 37\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 35\r
+  Figure2 6\r
+  EndPoint1 1328,1344\r
+  EndPoint2 1136,1335\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Figure 38\r
+{\r
+  Text ""\r
+  Bounds 713,1337,728,1352\r
+  FillColor 0,0,0\r
+  BorderWidth 0\r
+  BindToStyle FALSE\r
+  TextFormat 0x0000\r
+  Behavior 0x00327A12\r
+  Symbol "null"\r
+  TypeSize 8\r
+  TypeFace "Arial"\r
+}\r
+\r
+Connector 39\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 6\r
+  Figure2 38\r
+  EndPoint1 880,1336\r
+  EndPoint2 720,1344\r
+  SuppressEnd1 FALSE\r
+  SuppressEnd2 TRUE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+Connector 40\r
+{\r
+  Style "Flow Closed Smoothed"\r
+  Figure1 38\r
+  Figure2 5\r
+  EndPoint1 720,1344\r
+  EndPoint2 700,1232\r
+  SuppressEnd1 TRUE\r
+  SuppressEnd2 FALSE\r
+  End1 "null"\r
+  End2 "block"\r
+  End1Length 18\r
+  End2Length 25\r
+  LineWidth 3\r
+  EndBorderWidth 2\r
+  Color 130,130,130\r
+  Behavior 0x00000008\r
+}\r
+\r
+## Staples Section:\r
index 6702d837fdf668f93eb363d13e337e2ac27cfecc..71f796e576d4e8ff25239166e34c6c07ee8fc64a 100644 (file)
@@ -21,4 +21,22 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 all:
        -cd ..
 
-EXTRA_DIST = *.h
\ No newline at end of file
+if SILC_DIST_TOOLKIT
+include_HEADERS = \
+       bitmove.h \
+       clientlibincludes.h \
+       silcincludes.h \
+       silcwin32.h \
+       version.h \
+       version_internal.h \
+       silcdefs.h
+endif
+
+EXTRA_DIST = \
+       bitmove.h \
+       clientlibincludes.h \
+       silcincludes.h \
+       silcwin32.h \
+       version.h \
+       version_internal.h \
+       silcdefs.h.in
diff --git a/includes/Makefile.in b/includes/Makefile.in
deleted file mode 100644 (file)
index 0b8bc33..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-# Makefile.in generated automatically by automake 1.3 from Makefile.am
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-#
-#  Makefile.am
-#
-#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-#
-#  Copyright (C) 2000 Pekka Riikonen
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-
-
-SHELL = /bin/sh
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-prefix = @prefix@
-exec_prefix = @exec_prefix@
-
-bindir = @bindir@
-sbindir = @sbindir@
-libexecdir = @libexecdir@
-datadir = @datadir@
-sysconfdir = @sysconfdir@
-sharedstatedir = @sharedstatedir@
-localstatedir = @localstatedir@
-libdir = @libdir@
-infodir = @infodir@
-mandir = @mandir@
-includedir = @includedir@
-oldincludedir = /usr/include
-
-DISTDIR =
-
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-
-top_builddir = ..
-
-ACLOCAL = @ACLOCAL@
-AUTOCONF = @AUTOCONF@
-AUTOMAKE = @AUTOMAKE@
-AUTOHEADER = @AUTOHEADER@
-
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-transform = @program_transform_name@
-
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_alias = @build_alias@
-build_triplet = @build@
-host_alias = @host_alias@
-host_triplet = @host@
-target_alias = @target_alias@
-target_triplet = @target@
-CC = @CC@
-LN_S = @LN_S@
-MAKEINFO = @MAKEINFO@
-PACKAGE = @PACKAGE@
-RANLIB = @RANLIB@
-VERSION = @VERSION@
-
-AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
-
-EXTRA_DIST = *.h
-mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
-CONFIG_HEADER = silcdefs.h
-CONFIG_CLEAN_FILES = 
-DIST_COMMON =  Makefile.am Makefile.in silcdefs.h.in stamp-h.in
-
-
-DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
-
-TAR = tar
-GZIP = --best
-all: Makefile silcdefs.h
-
-.SUFFIXES:
-$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
-       cd $(top_srcdir) && $(AUTOMAKE) --foreign includes/Makefile
-
-Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
-       cd $(top_builddir) \
-         && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
-
-
-silcdefs.h: stamp-h
-       @:
-stamp-h: $(srcdir)/silcdefs.h.in $(top_builddir)/config.status
-       cd $(top_builddir) \
-         && CONFIG_FILES= CONFIG_HEADERS=includes/silcdefs.h \
-            $(SHELL) ./config.status
-       @echo timestamp > stamp-h
-$(srcdir)/silcdefs.h.in: $(srcdir)/stamp-h.in
-$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) 
-       cd $(top_srcdir) && $(AUTOHEADER)
-       @echo timestamp > $(srcdir)/stamp-h.in
-
-mostlyclean-hdr:
-
-clean-hdr:
-
-distclean-hdr:
-       -rm -f silcdefs.h
-
-maintainer-clean-hdr:
-tags: TAGS
-TAGS:
-
-
-distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
-
-subdir = includes
-
-distdir: $(DISTFILES)
-       @for file in $(DISTFILES); do \
-         d=$(srcdir); \
-         test -f $(distdir)/$$file \
-         || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
-         || cp -p $$d/$$file $(distdir)/$$file; \
-       done
-info:
-dvi:
-check: all
-       $(MAKE)
-installcheck:
-install-exec: 
-       @$(NORMAL_INSTALL)
-
-install-data: 
-       @$(NORMAL_INSTALL)
-
-install: install-exec install-data all
-       @:
-
-uninstall: 
-
-install-strip:
-       $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install
-installdirs:
-
-
-mostlyclean-generic:
-       -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
-
-clean-generic:
-       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
-       -rm -f Makefile $(DISTCLEANFILES)
-       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
-       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-
-maintainer-clean-generic:
-       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-mostlyclean:  mostlyclean-hdr mostlyclean-generic
-
-clean:  clean-hdr clean-generic mostlyclean
-
-distclean:  distclean-hdr distclean-generic clean
-       -rm -f config.status
-
-maintainer-clean:  maintainer-clean-hdr maintainer-clean-generic \
-               distclean
-       @echo "This command is intended for maintainers to use;"
-       @echo "it deletes files that may require special tools to rebuild."
-
-.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
-tags distdir info dvi installcheck install-exec install-data install \
-uninstall all installdirs mostlyclean-generic distclean-generic \
-clean-generic maintainer-clean-generic clean mostlyclean distclean \
-maintainer-clean
-
-
-all:
-       -cd ..
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
index aca83a4abdaf5cf650b2f660e0414ed504387d44..5ada690b3728de6819add7b436382c14809de56e 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  bitmove.h
+  bitmove.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #ifndef BITMOVE_H
 #define BITMOVE_H
 
+#define GET_WORD(cp) ((uint32)(uint8)(cp)[0]) << 24    \
+                   | ((uint32)(uint8)(cp)[1] << 16)    \
+                   | ((uint32)(uint8)(cp)[2] << 8)     \
+                   | ((uint32)(uint8)(cp)[3])
+
+/* Returns eight 8-bit bytes, most significant bytes first. */
+#define SILC_GET64_MSB(l, cp)                  \
+       (l) = ((((uint64)GET_WORD((cp))) << 32) |       \
+             ((uint64)GET_WORD((cp) + 4)))
+#define SILC_PUT64_MSB(l, cp)                          \
+do {                                                   \
+  SILC_PUT32_MSB((uint32)((uint64)(l) >> 32), (cp));   \
+  SILC_PUT32_MSB((uint32)(l), (cp) + 4);               \
+} while(0)
+
+
 /* Returns four 8-bit bytes, most significant bytes first. */
-#define SILC_GET32_MSB(l, cp) \
-       (l) = ((unsigned long)(unsigned char)(cp)[0]) << 24 \
-           | ((unsigned long)(unsigned char)(cp)[1] << 16) \
-           | ((unsigned long)(unsigned char)(cp)[2] << 8) \
-           | ((unsigned long)(unsigned char)(cp)[3])
-#define SILC_PUT32_MSB(l, cp) \
-       (cp)[0] = l >> 24; \
-       (cp)[1] = l >> 16; \
-       (cp)[2] = l >> 8; \
+#define SILC_GET32_MSB(l, cp)                  \
+       (l) = ((uint32)(uint8)(cp)[0]) << 24    \
+           | ((uint32)(uint8)(cp)[1] << 16)    \
+           | ((uint32)(uint8)(cp)[2] << 8)     \
+           | ((uint32)(uint8)(cp)[3])
+#define SILC_PUT32_MSB(l, cp)                  \
+       (cp)[0] = l >> 24;                      \
+       (cp)[1] = l >> 16;                      \
+       (cp)[2] = l >> 8;                       \
        (cp)[3] = l;
 
 
 /* Returns four 8-bit bytes, less significant bytes first. */
-#define SILC_GET32_LSB(l, cp) \
-       (l) = ((unsigned long)(unsigned char)(cp)[0]) \
-           | ((unsigned long)(unsigned char)(cp)[1] << 8) \
-           | ((unsigned long)(unsigned char)(cp)[2] << 16) \
-           | ((unsigned long)(unsigned char)(cp)[3] << 24)
+#define SILC_GET32_LSB(l, cp)                  \
+       (l) = ((uint32)(uint8)(cp)[0])          \
+           | ((uint32)(uint8)(cp)[1] << 8)     \
+           | ((uint32)(uint8)(cp)[2] << 16)    \
+           | ((uint32)(uint8)(cp)[3] << 24)
 /* same as upper but XOR the result always */
-#define SILC_GET32_X_LSB(l, cp) \
-       (l) ^= ((unsigned long)(unsigned char)(cp)[0]) \
-           | ((unsigned long)(unsigned char)(cp)[1] << 8) \
-           | ((unsigned long)(unsigned char)(cp)[2] << 16) \
-           | ((unsigned long)(unsigned char)(cp)[3] << 24)
-#define SILC_PUT32_LSB(l, cp) \
-       (cp)[0] = l; \
-       (cp)[1] = l >> 8; \
-       (cp)[2] = l >> 16; \
+#define SILC_GET32_X_LSB(l, cp)                        \
+       (l) ^= ((uint32)(uint8)(cp)[0])         \
+           | ((uint32)(uint8)(cp)[1] << 8)     \
+           | ((uint32)(uint8)(cp)[2] << 16)    \
+           | ((uint32)(uint8)(cp)[3] << 24)
+#define SILC_PUT32_LSB(l, cp)                  \
+       (cp)[0] = l;                            \
+       (cp)[1] = l >> 8;                       \
+       (cp)[2] = l >> 16;                      \
        (cp)[3] = l >> 24;
 
 
 /* Returns two 8-bit bytes, most significant bytes first. */
-#define SILC_GET16_MSB(l, cp) \
-       (l) = ((unsigned long)(unsigned char)(cp)[0] << 8) \
-           | ((unsigned long)(unsigned char)(cp)[1])
-#define SILC_PUT16_MSB(l, cp) \
-       (cp)[0] = l >> 8; \
+#define SILC_GET16_MSB(l, cp)                  \
+       (l) = ((uint32)(uint8)(cp)[0] << 8)     \
+           | ((uint32)(uint8)(cp)[1])
+#define SILC_PUT16_MSB(l, cp)                  \
+       (cp)[0] = l >> 8;                       \
        (cp)[1] = l;
 
 /* Returns two 8-bit bytes, less significant bytes first. */
-#define SILC_GET16_LSB(l, cp) \
-       (l) = ((unsigned long)(unsigned char)(cp)[0]) \
-           | ((unsigned long)(unsigned char)(cp)[1] << 8)
-#define SILC_PUT16_LSB(l, cp) \
-       (cp)[0] = l; \
+#define SILC_GET16_LSB(l, cp)                  \
+       (l) = ((uint32)(uint8)(cp)[0])          \
+           | ((uint32)(uint8)(cp)[1] << 8)
+#define SILC_PUT16_LSB(l, cp)                  \
+       (cp)[0] = l;                            \
        (cp)[1] = l >> 8;
 
 #endif
diff --git a/includes/clientlibincludes.h b/includes/clientlibincludes.h
new file mode 100644 (file)
index 0000000..8e02600
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+
+  clientlibincludes.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENTLIBINCLUDES_H
+#define CLIENTLIBINCLUDES_H
+
+#include "silcdefs.h"
+
+/* Generic includes */
+#include "silcincludes.h"
+
+/* SILC Client includes */
+#include "client.h"
+#include "command.h"
+#include "command_reply.h"
+#include "idlist.h"
+#include "protocol.h"
+#include "silcapi.h"
+
+#endif
diff --git a/includes/silcdefs.h.in b/includes/silcdefs.h.in
deleted file mode 100644 (file)
index 61be749..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/* includes/silcdefs.h.in.  Generated automatically from configure.in by autoheader.  */
-
-/* Define to empty if the keyword does not work.  */
-#undef const
-
-/* Define to `int' if <sys/types.h> doesn't define.  */
-#undef gid_t
-
-/* Define as __inline if that's what the C compiler calls it.  */
-#undef inline
-
-/* Define to `int' if <sys/types.h> doesn't define.  */
-#undef mode_t
-
-/* Define to `int' if <sys/types.h> doesn't define.  */
-#undef pid_t
-
-/* Define as the return type of signal handlers (int or void).  */
-#undef RETSIGTYPE
-
-/* Define to `unsigned' if <sys/types.h> doesn't define.  */
-#undef size_t
-
-/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly.  */
-#undef STAT_MACROS_BROKEN
-
-/* Define if you have the ANSI C header files.  */
-#undef STDC_HEADERS
-
-/* Define if you can safely include both <sys/time.h> and <time.h>.  */
-#undef TIME_WITH_SYS_TIME
-
-/* Define to `int' if <sys/types.h> doesn't define.  */
-#undef uid_t
-
-/* Name of the package. */
-#undef PACKAGE
-
-/* Version of the package. */
-#undef VERSION
-
-/* Debugging */
-#undef SILC_DEBUG
-
-/* Default configuration file */
-#undef SILC_SERVER_CONFIG_FILE
-
-/* SIM (SILC Module) support */
-#undef SILC_SIM
-#undef HAVE_RTLD_NOW
-#undef HAVE_RTLD_LAZY
-
-/* Define if you have the bind function.  */
-#undef HAVE_BIND
-
-/* Define if you have the chmod function.  */
-#undef HAVE_CHMOD
-
-/* Define if you have the close function.  */
-#undef HAVE_CLOSE
-
-/* Define if you have the connect function.  */
-#undef HAVE_CONNECT
-
-/* Define if you have the ctime function.  */
-#undef HAVE_CTIME
-
-/* Define if you have the fcntl function.  */
-#undef HAVE_FCNTL
-
-/* Define if you have the fstat function.  */
-#undef HAVE_FSTAT
-
-/* Define if you have the getenv function.  */
-#undef HAVE_GETENV
-
-/* Define if you have the getgid function.  */
-#undef HAVE_GETGID
-
-/* Define if you have the gethostbyaddr function.  */
-#undef HAVE_GETHOSTBYADDR
-
-/* Define if you have the gethostbyname function.  */
-#undef HAVE_GETHOSTBYNAME
-
-/* Define if you have the gethostname function.  */
-#undef HAVE_GETHOSTNAME
-
-/* Define if you have the getopt_long function.  */
-#undef HAVE_GETOPT_LONG
-
-/* Define if you have the getpgid function.  */
-#undef HAVE_GETPGID
-
-/* Define if you have the getpgrp function.  */
-#undef HAVE_GETPGRP
-
-/* Define if you have the getpid function.  */
-#undef HAVE_GETPID
-
-/* Define if you have the getservbyname function.  */
-#undef HAVE_GETSERVBYNAME
-
-/* Define if you have the getservbyport function.  */
-#undef HAVE_GETSERVBYPORT
-
-/* Define if you have the getsid function.  */
-#undef HAVE_GETSID
-
-/* Define if you have the gettimeofday function.  */
-#undef HAVE_GETTIMEOFDAY
-
-/* Define if you have the getuid function.  */
-#undef HAVE_GETUID
-
-/* Define if you have the listen function.  */
-#undef HAVE_LISTEN
-
-/* Define if you have the memcpy function.  */
-#undef HAVE_MEMCPY
-
-/* Define if you have the memmove function.  */
-#undef HAVE_MEMMOVE
-
-/* Define if you have the memset function.  */
-#undef HAVE_MEMSET
-
-/* Define if you have the mlock function.  */
-#undef HAVE_MLOCK
-
-/* Define if you have the munlock function.  */
-#undef HAVE_MUNLOCK
-
-/* Define if you have the putenv function.  */
-#undef HAVE_PUTENV
-
-/* Define if you have the select function.  */
-#undef HAVE_SELECT
-
-/* Define if you have the setsockopt function.  */
-#undef HAVE_SETSOCKOPT
-
-/* Define if you have the shutdown function.  */
-#undef HAVE_SHUTDOWN
-
-/* Define if you have the socket function.  */
-#undef HAVE_SOCKET
-
-/* Define if you have the stat function.  */
-#undef HAVE_STAT
-
-/* Define if you have the strchr function.  */
-#undef HAVE_STRCHR
-
-/* Define if you have the strcpy function.  */
-#undef HAVE_STRCPY
-
-/* Define if you have the strerror function.  */
-#undef HAVE_STRERROR
-
-/* Define if you have the strncpy function.  */
-#undef HAVE_STRNCPY
-
-/* Define if you have the strstr function.  */
-#undef HAVE_STRSTR
-
-/* Define if you have the time function.  */
-#undef HAVE_TIME
-
-/* Define if you have the <arpa/inet.h> header file.  */
-#undef HAVE_ARPA_INET_H
-
-/* Define if you have the <assert.h> header file.  */
-#undef HAVE_ASSERT_H
-
-/* Define if you have the <ctype.h> header file.  */
-#undef HAVE_CTYPE_H
-
-/* Define if you have the <dlfcn.h> header file.  */
-#undef HAVE_DLFCN_H
-
-/* Define if you have the <errno.h> header file.  */
-#undef HAVE_ERRNO_H
-
-/* Define if you have the <fcntl.h> header file.  */
-#undef HAVE_FCNTL_H
-
-/* Define if you have the <getopt.h> header file.  */
-#undef HAVE_GETOPT_H
-
-/* Define if you have the <grp.h> header file.  */
-#undef HAVE_GRP_H
-
-/* Define if you have the <ncurses.h> header file.  */
-#undef HAVE_NCURSES_H
-
-/* Define if you have the <netdb.h> header file.  */
-#undef HAVE_NETDB_H
-
-/* Define if you have the <netinet/in.h> header file.  */
-#undef HAVE_NETINET_IN_H
-
-/* Define if you have the <netinet/tcp.h> header file.  */
-#undef HAVE_NETINET_TCP_H
-
-/* Define if you have the <pwd.h> header file.  */
-#undef HAVE_PWD_H
-
-/* Define if you have the <signal.h> header file.  */
-#undef HAVE_SIGNAL_H
-
-/* Define if you have the <string.h> header file.  */
-#undef HAVE_STRING_H
-
-/* Define if you have the <sys/mman.h> header file.  */
-#undef HAVE_SYS_MMAN_H
-
-/* Define if you have the <sys/stat.h> header file.  */
-#undef HAVE_SYS_STAT_H
-
-/* Define if you have the <sys/time.h> header file.  */
-#undef HAVE_SYS_TIME_H
-
-/* Define if you have the <sys/types.h> header file.  */
-#undef HAVE_SYS_TYPES_H
-
-/* Define if you have the <termcap.h> header file.  */
-#undef HAVE_TERMCAP_H
-
-/* Define if you have the <unistd.h> header file.  */
-#undef HAVE_UNISTD_H
diff --git a/includes/silcepoc.h b/includes/silcepoc.h
new file mode 100644 (file)
index 0000000..ab57922
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+
+  silcepoc.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* Native EPOC specific includes and definitions. */
+
+#ifndef SILCEPOC_H
+#define SILCEPOC_H
+
+#include <e32std.h>
+#include <e32base.h>
+#include <e32cons.h>
+#include <e32svr.h>
+#include <e32hal.h>
+#include <w32std.h>
+#include <apgtask.h>
+#include <es_sock.h>
+#include <in_sock.h>
+
+#endif
index abb8b3f87a31e49af30b0f5d22d875a2b50e6381..fb68ce56c02f9bec916b242bcb876c5ae771fa53 100644 (file)
@@ -2,9 +2,9 @@
 
   silcincludes.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 /* Automatically generated configuration header */
 #include "silcdefs.h"
 
+#ifdef WIN32
+#ifndef SILC_WIN32
+#define SILC_WIN32
+#endif
+#endif
+
+#if defined(__EPOC32__)
+#ifndef SILC_EPOC
+#define SILC_EPOC
+#endif
+#endif
+
+#ifdef SILC_WIN32
+#include "silcwin32.h"
+#endif
+
+#ifdef SILC_EPOC
+#include "silcepoc.h"
+#endif
+
+#ifndef DLLAPI
+#define DLLAPI
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
 #include <stdarg.h>
-
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/times.h>
-
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#else
-#error getopt.h not found in the system
-#endif
+#include <time.h>
 
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
@@ -58,7 +73,7 @@
 #error fcntl.h not found in the system
 #endif
 
-#ifdef HAVE_ASSERT_H
+#ifdef HAVE_ERRNO_H
 #include <errno.h>
 #else
 #error errno.h not found in the system
 #error assert.h not found in the system
 #endif
 
+#ifndef SILC_WIN32
+
+#include <unistd.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/times.h>
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#ifdef SOCKS5
+#include "socks.h"
+#endif
+
 #include <sys/socket.h>
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #error netinet/in.h not found in the system
 #endif
 
+#ifdef HAVE_XTI_H
+#include <xti.h>
+#else
 #ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
 #else
-#error netinet/tcp.h not found in the system
+#error xti.h nor even netinet/tcp.h found in the system
+#endif
 #endif
 
 #ifdef HAVE_NETDB_H
 #include <dlfcn.h>
 #endif
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef HAVE_REGEX_H
+#include "../lib/contrib/regex.h"
+#else
+#include <regex.h>
+#endif
+
+#ifdef SILC_HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#endif                         /* !SILC_WIN32 */
+
+#ifndef HAVE_GETOPT_LONG
+#include "../lib/contrib/getopt.h"
+#endif
+
 #ifndef TRUE
 #define TRUE 1
 #endif
 #define FALSE 0
 #endif
 
+/* Define offsetof */
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/* Define types. The types must be at least of the specified size */
+#undef uint8
+#undef uint16
+#undef uint32
+#undef uin64
+#undef int8
+#undef int16
+#undef int32
+#undef int64
+
+typedef unsigned char uint8;
+typedef signed char int8;
+
+#if SILC_SIZEOF_SHORT > 2
+#error "size of the short must be 2 bytes"
+#endif
+
+typedef unsigned short uint16;
+typedef signed short int16;
+
+#if SILC_SIZEOF_LONG == 4
+typedef unsigned long uint32;
+typedef signed long int32;
+#else
+#if SILC_SIZEOF_INT == 4
+typedef unsigned long uint32;
+typedef signed long int32;
+#else
+#if SILC_SIZEOF_LONG_LONG >= 4
+#ifndef WIN32
+typedef unsigned long long uint32;
+typedef signed long long int32;
+#endif
+#endif
+#endif
+#endif
+
+#if SILC_SIZEOF_LONG >= 8
+typedef unsigned long uint64;
+typedef signed long int64;
+#else
+#if SILC_SIZEOF_LONG_LONG >= 8
+#ifndef WIN32
+typedef unsigned long long uint64;
+typedef signed long long int64;
+#else
+typedef uint32 uint64; /* XXX Use Windows's own 64 bit types */
+typedef int32 int64;
+#endif
+#else
+typedef uint32 uint64;
+typedef int32 int64;
+#endif
+#endif
+
+#if SILC_SIZEOF_VOID_P < 4
+typedef uint32 * void *;
+#endif
+
+#ifndef __cplusplus
+#ifndef bool
+#define bool unsigned char
+#endif
+#endif
+
 /* Generic global SILC includes */
 #include "bitmove.h"
 
 /* Math library includes */
 #include "silcmp.h"
-#include "modinv.h"
-#include "silcprimegen.h"
+#include "silcmath.h"
 
 /* Crypto library includes */
 #include "silccipher.h"
 #include "silcrng.h"
 #include "silcpkcs.h"
 
-/* SILC core library includes */
+/* SILC util library includes */
+#include "silcmutex.h"
+#include "silcthread.h"
+#include "silcschedule.h"
+#include "silchashtable.h"
 #include "silclog.h"
 #include "silcmemory.h"
+#include "silclist.h"
+#include "silcdlist.h"
 #include "silcbuffer.h"
 #include "silcbufutil.h"
 #include "silcbuffmt.h"
 #include "silcnet.h"
 #include "silcutil.h"
 #include "silcconfig.h"
-#include "id.h"
-#include "idcache.h"
-#include "silcpacket.h"
-#include "silctask.h"
-#include "silcschedule.h"
 #include "silcprotocol.h"
+#include "silcsockconn.h"
+
+/* SILC core library includes */
+#include "silcid.h"
+#include "silcidcache.h"
+#include "silcargument.h"
 #include "silccommand.h"
 #include "silcchannel.h"
-#include "silcsockconn.h"
+#include "silcpacket.h"
+#include "silcnotify.h"
+#include "silcmode.h"
+#include "silcauth.h"
+#include "silcprivate.h"
 
 #ifdef SILC_SIM
 /* SILC Module library includes */
 #include "payload.h"
 #include "groups.h"
 
+/* SILC SFTP library */
+#include "silcsftp.h"
+#include "silcsftp_fs.h"
+
 #endif
diff --git a/includes/silcwin32.h b/includes/silcwin32.h
new file mode 100644 (file)
index 0000000..88e103d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+
+  silcwin32.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* Native WIN32 specific includes and definitions. */
+
+#ifndef SILCWIN32_H
+#define SILCWIN32_H
+
+#include <windows.h>
+#include <io.h>
+#include <process.h>
+
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+#ifdef WIN32
+#define strcasecmp strcmp
+#define strncasecmp strncmp
+#endif
+
+#ifdef WIN32
+#ifndef DLL
+#define DLLAPI __declspec(dllimport)
+#else
+#define DLLAPI // Nada, we use .DEF
+#endif
+#endif
+
+#undef inline
+#define inline __inline
+
+#undef sleep
+#define sleep(x) Sleep((x) * 1000)
+
+#endif
index e225709c4b79eef7b12b33f1980789411b19209a..eaba5ba352710d3ed814cadc182a8760e842b583 100644 (file)
 #ifndef VERSION_H
 #define VERSION_H
 
+#include "version_internal.h"
+
 /* Version type definition */
 typedef unsigned char SilcVersion;
 
-/* SILC Versions. XXX not used currently */
-#define SILC_VERSION_MAJ 1
-#define SILC_VERSION_MIN 0
-#define SILC_VERSION_BUILD 0
-
 /* SILC Protocol version number used in SILC packets */
 #define SILC_VERSION_1 '\1'
 
 /* SILC version string */
-const char *silc_version = "27062000";
-const char *silc_name = "SILC";
+const char *silc_version = SILC_VERSION_STRING;
+const char *silc_dist_version = SILC_DIST_VERSION_STRING;
+const char *silc_version_string = SILC_PROTOCOL_VERSION_STRING;
+const char *silc_name = SILC_NAME;
 const char *silc_fullname = "Secure Internet Live Conferencing";
 
 #endif
diff --git a/lib/Makefile.am.pre b/lib/Makefile.am.pre
new file mode 100644 (file)
index 0000000..24d7aca
--- /dev/null
@@ -0,0 +1,114 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#
+#  Copyright (C) 2000 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+COMMONDIRS = \
+       contrib \
+       silccore \
+       silccrypt \
+       silcsim \
+       silcmath \
+       silcske \
+       silcutil \
+       silcclient \
+       silcsftp
+#      zlib
+
+SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+DIST_SUBDIRS = SILC_DISTRIBUTION_SUBDIRS
+
+# SILC Library dirs
+SILCLIB_DIRS = \
+       contrib \
+       silccore \
+       silccrypt \
+       silcsim \
+       silcmath \
+       silcske \
+       silcutil \
+       silcsftp
+
+# SILC Client Library dirs
+SILCCLIENTLIB_DIRS = \
+       silcclient
+
+CLEANFILES = libsilc.a libsilcclient.a
+DISTCLEANFILES = libsilc.a libsilcclient.a
+
+if SILC_DIST_CLIENT
+all:  remove libsilc.a libsilcclient.a
+else
+if SILC_DIST_TOOLKIT
+all:  remove libsilc.a libsilcclient.a
+else
+if SILC_DIST_WIN32DLL
+all:  silc.dll silcclient.dll
+else
+all:  remove libsilc.a
+endif
+endif
+endif
+
+remove:
+       -rm -rf libsilc.a
+       -rm -rf libsilcclient.a
+
+if SILC_DIST_TOOLKIT
+install-exec-hook:
+       -mkdir -p $(libdir)
+       -$(INSTALL) libsilc.a $(libdir)/
+       -$(INSTALL) libsilcclient.a $(libdir)/
+else
+install-exec-hook:
+       -cd
+endif
+
+if SILC_DIST_WIN32DLL
+# WIN32 DLL generation
+silc.dll: libsilc.a
+       dllwrap --export-all --output-def silc.def --output-exp silc.exp \
+       --output-lib silc.lib --driver-name $(CC) --target i386-mingw32 \
+       -mno-cygwin -o silc.dll libsilc.a -lwsock32
+
+silcclient.dll: libsilcclient.a
+       dllwrap --export-all --output-def silcclient.def \
+       --output-lib silcclient.lib --output-exp silcclient.exp \
+       --driver-name $(CC) --target i386-mingw32 \
+       -mno-cygwin -o silcclient.dll libsilcclient.a -L. -lsilc -lwsock32
+endif
+
+libsilc.a:
+       find $(SILCLIB_DIRS) -type f -name *.o | xargs $(AR) cru libsilc.a
+       $(RANLIB) libsilc.a
+
+libsilcclient.a:
+       find $(SILCCLIENTLIB_DIRS) -type f -name *.o | xargs $(AR) cru libsilcclient.a
+       $(RANLIB) libsilcclient.a
+
+if SILC_DIST_TOOLKIT
+SILC_EXTRA_DIST = doc
+else
+if SILC_DIST_SERVER
+SILC_EXTRA_DIST = 
+else
+SILC_EXTRA_DIST =
+endif
+endif
+
+EXTRA_DIST = $(SILC_EXTRA_DIST)
similarity index 74%
rename from lib/Makefile.am
rename to lib/contrib/Makefile.am
index 66c6682a79b4e42dcdce880775223b23cd5310c5..78f02843efde0666653762b9c9bffa722fd6c04d 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-SUBDIRS = \
-       silccore \
-       silccrypt \
-       silcsim \
-       silcmath \
-       silcske
-#        zlib
+noinst_LIBRARIES = libcontrib.a
 
-CLEANFILES = libsilc.a
-DISTCLEANFILES = libsilc.a
+if SILC_WIN32
+libcontrib_a_SOURCES =
+else
+libcontrib_a_SOURCES = \
+       getopt.c \
+       getopt1.c \
+       regex.c
+endif
 
-all:   libsilc.a
+EXTRA_DIST = *.h
 
-libsilc.a:
-       find . -type f -name *.o | xargs $(AR) cru libsilc.a
-       ranlib libsilc.a
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/contrib/getopt.c b/lib/contrib/getopt.c
new file mode 100644 (file)
index 0000000..0b47ea7
--- /dev/null
@@ -0,0 +1,748 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+   before changing it!
+
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+       Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.  */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+#include <stdlib.h>
+#endif /* GNU C library.  */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+   long-named option.  Because this is not POSIX.2 compliant, it is
+   being phased out.  */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* XXX 1003.2 says this must be 1 before any call.  */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+\f
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+#include <string.h>
+#define        my_index        strchr
+#else
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+       return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.
+   (Supposedly there are some machines where it might get a warning,
+   but changing this conditional to __STDC__ is too risky.)  */
+#ifdef __GNUC__
+#ifdef IN_GCC
+#include "gstddef.h"
+#else
+#include <stddef.h>
+#endif
+extern size_t strlen (const char *);
+#endif
+
+#endif                         /* GNU C library.  */
+\f
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+       {
+         /* Bottom segment is the short one.  */
+         int len = middle - bottom;
+         register int i;
+
+         /* Swap it with the top part of the top segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[top - (middle - bottom) + i];
+             argv[top - (middle - bottom) + i] = tem;
+           }
+         /* Exclude the moved bottom segment from further swapping.  */
+         top -= len;
+       }
+      else
+       {
+         /* Top segment is the short one.  */
+         int len = top - middle;
+         register int i;
+
+         /* Swap it with the bottom part of the bottom segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[middle + i];
+             argv[middle + i] = tem;
+           }
+         /* Exclude the moved top segment from further swapping.  */
+         bottom += len;
+       }
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns `EOF'.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  int option_index;
+
+  optarg = 0;
+
+  /* Initialize the internal data when the first call is made.
+     Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  if (optind == 0)
+    {
+      first_nonopt = last_nonopt = optind = 1;
+
+      nextchar = NULL;
+
+      /* Determine how to handle the ordering of options and nonoptions.  */
+
+      if (optstring[0] == '-')
+       {
+         ordering = RETURN_IN_ORDER;
+         ++optstring;
+       }
+      else if (optstring[0] == '+')
+       {
+         ordering = REQUIRE_ORDER;
+         ++optstring;
+       }
+      else if (getenv ("POSIXLY_CORRECT") != NULL)
+       ordering = REQUIRE_ORDER;
+      else
+       ordering = PERMUTE;
+    }
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Now skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc
+                && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+                && (longopts == NULL
+                    || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif                         /* GETOPT_COMPAT */
+                )
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* Special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return EOF;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+         && (longopts == NULL
+             || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif                         /* GETOPT_COMPAT */
+         )
+       {
+         if (ordering == REQUIRE_ORDER)
+           return EOF;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Start decoding its characters.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  if (longopts != NULL
+      && ((argv[optind][0] == '-'
+          && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+         || argv[optind][0] == '+'
+#endif                         /* GETOPT_COMPAT */
+         ))
+    {
+      const struct option *p;
+      char *s = nextchar;
+      int exact = 0;
+      int ambig = 0;
+      const struct option *pfound = NULL;
+      int indfound = 0;
+
+      while (*s && *s != '=')
+       s++;
+
+      /* Test all options for either exact match or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name;
+          p++, option_index++)
+       if (!strncmp (p->name, nextchar, s - nextchar))
+         {
+           if (s - nextchar == strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else
+             /* Second nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (opterr)
+           fprintf (stderr, "%s: option `%s' is ambiguous\n",
+                    argv[0], argv[optind]);
+         nextchar += strlen (nextchar);
+         optind++;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*s)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = s + 1;
+             else
+               {
+                 if (opterr)
+                   {
+                     if (argv[optind - 1][1] == '-')
+                       /* --option */
+                       fprintf (stderr,
+                                "%s: option `--%s' doesn't allow an argument\n",
+                                argv[0], pfound->name);
+                     else
+                       /* +option or -option */
+                       fprintf (stderr,
+                            "%s: option `%c%s' doesn't allow an argument\n",
+                            argv[0], argv[optind - 1][0], pfound->name);
+                   }
+                 nextchar += strlen (nextchar);
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (opterr)
+                   fprintf (stderr, "%s: option `%s' requires an argument\n",
+                            argv[0], argv[optind - 1]);
+                 nextchar += strlen (nextchar);
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+         || argv[optind][0] == '+'
+#endif                         /* GETOPT_COMPAT */
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (opterr)
+           {
+             if (argv[optind][1] == '-')
+               /* --option */
+               fprintf (stderr, "%s: unrecognized option `--%s'\n",
+                        argv[0], nextchar);
+             else
+               /* +option or -option */
+               fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+                        argv[0], argv[optind][0], nextchar);
+           }
+         nextchar = (char *) "";
+         optind++;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (opterr)
+         {
+#if 0
+           if (c < 040 || c >= 0177)
+             fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+                      argv[0], c);
+           else
+             fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+           /* 1003.2 specifies the format of this message.  */
+           fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+         }
+       optopt = c;
+       return '?';
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = 0;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (opterr)
+                 {
+#if 0
+                   fprintf (stderr, "%s: option `-%c' requires an argument\n",
+                            argv[0], c);
+#else
+                   /* 1003.2 specifies the format of this message.  */
+                   fprintf (stderr, "%s: option requires an argument -- %c\n",
+                            argv[0], c);
+#endif
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == EOF)
+       break;
+
+      switch (c)
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/contrib/getopt.h b/lib/contrib/getopt.h
new file mode 100644 (file)
index 0000000..45541f5
--- /dev/null
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+   Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns EOF, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+#if    __STDC__
+  const char *name;
+#else
+  char *name;
+#endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+#define        no_argument             0
+#define required_argument      1
+#define optional_argument      2
+
+#if __STDC__
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+                       const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int argc, char *const *argv,
+                            const char *shortopts,
+                            const struct option *longopts, int *longind,
+                            int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/lib/contrib/getopt1.c b/lib/contrib/getopt1.c
new file mode 100644 (file)
index 0000000..fe6bdd9
--- /dev/null
@@ -0,0 +1,177 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+       Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+
+#include "getopt.h"
+
+#ifndef __STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__.  */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+       {"add", 1, 0, 0},
+       {"append", 0, 0, 0},
+       {"delete", 1, 0, 0},
+       {"verbose", 0, 0, 0},
+       {"create", 0, 0, 0},
+       {"file", 1, 0, 0},
+       {0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+                      long_options, &option_index);
+      if (c == EOF)
+       break;
+
+      switch (c)
+       {
+       case 0:
+         printf ("option %s", long_options[option_index].name);
+         if (optarg)
+           printf (" with arg %s", optarg);
+         printf ("\n");
+         break;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case 'd':
+         printf ("option d with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/contrib/regex.c b/lib/contrib/regex.c
new file mode 100644 (file)
index 0000000..6aec434
--- /dev/null
@@ -0,0 +1,4955 @@
+/* Extended regular expression matching and search library,
+   version 0.12.
+   (Implements POSIX draft P10003.2/D11.2, except for
+   internationalization features.)
+
+   Copyright (C) 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* AIX requires this to be the first thing in the file. */
+#if defined (_AIX) && !defined (REGEX_MALLOC)
+  #pragma alloca
+#endif
+
+/*
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+*/
+
+/* We need this for `regex.h', and perhaps for the Emacs include files.  */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ifdef HAVE_CONFIG_H
+##include "config.h"
+#endif
+*/
+
+/* The `emacs' switch turns on certain matching commands
+   that make sense only in Emacs. */
+#ifdef emacs
+
+#include "lisp.h"
+#include "buffer.h"
+#include "syntax.h"
+
+/* Emacs uses `NULL' as a predicate.  */
+#undef NULL
+
+#else  /* not emacs */
+
+/* We used to test for `BSTRING' here, but only GCC and Emacs define
+   `BSTRING', as far as I know, and neither of them use this code.  */
+#if HAVE_STRING_H || STDC_HEADERS
+#include <string.h>
+#ifndef bcmp
+#define bcmp(s1, s2, n)        memcmp ((s1), (s2), (n))
+#endif
+#ifndef bcopy
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif
+#ifndef bzero
+#define bzero(s, n)    memset ((s), 0, (n))
+#endif
+#else
+#include <strings.h>
+#endif
+
+#include <stdlib.h>
+
+/* Define the syntax stuff for \<, \>, etc.  */
+
+/* This must be nonzero for the wordchar and notwordchar pattern
+   commands in re_match_2.  */
+#ifndef Sword 
+#define Sword 1
+#endif
+
+#ifdef SYNTAX_TABLE
+
+extern char *re_syntax_table;
+
+#else /* not SYNTAX_TABLE */
+
+/* How many characters in the character set.  */
+#define CHAR_SET_SIZE 256
+
+static char re_syntax_table[CHAR_SET_SIZE];
+
+static void
+init_syntax_once ()
+{
+   register int c;
+   static int done = 0;
+
+   if (done)
+     return;
+
+   bzero (re_syntax_table, sizeof re_syntax_table);
+
+   for (c = 'a'; c <= 'z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = 'A'; c <= 'Z'; c++)
+     re_syntax_table[c] = Sword;
+
+   for (c = '0'; c <= '9'; c++)
+     re_syntax_table[c] = Sword;
+
+   re_syntax_table['_'] = Sword;
+
+   done = 1;
+}
+
+#endif /* not SYNTAX_TABLE */
+
+#define SYNTAX(c) re_syntax_table[c]
+
+#endif /* not emacs */
+\f
+/* Get the interface, including the syntax bits.  */
+#include "regex.h"
+
+/* isalpha etc. are used for the character classes.  */
+#include <ctype.h>
+
+#ifndef isascii
+#define isascii(c) 1
+#endif
+
+#ifdef isblank
+#define ISBLANK(c) (isascii (c) && isblank (c))
+#else
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+#ifdef isgraph
+#define ISGRAPH(c) (isascii (c) && isgraph (c))
+#else
+#define ISGRAPH(c) (isascii (c) && isprint (c) && !isspace (c))
+#endif
+
+#define ISPRINT(c) (isascii (c) && isprint (c))
+#define ISDIGIT(c) (isascii (c) && isdigit (c))
+#define ISALNUM(c) (isascii (c) && isalnum (c))
+#define ISALPHA(c) (isascii (c) && isalpha (c))
+#define ISCNTRL(c) (isascii (c) && iscntrl (c))
+#define ISLOWER(c) (isascii (c) && islower (c))
+#define ISPUNCT(c) (isascii (c) && ispunct (c))
+#define ISSPACE(c) (isascii (c) && isspace (c))
+#define ISUPPER(c) (isascii (c) && isupper (c))
+#define ISXDIGIT(c) (isascii (c) && isxdigit (c))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* We remove any previous definition of `SIGN_EXTEND_CHAR',
+   since ours (we hope) works properly with all combinations of
+   machines, compilers, `char' and `unsigned char' argument types.
+   (Per Bothner suggested the basic approach.)  */
+#undef SIGN_EXTEND_CHAR
+#if __STDC__
+#define SIGN_EXTEND_CHAR(c) ((signed char) (c))
+#else  /* not __STDC__ */
+/* As in Harbison and Steele.  */
+#define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
+#endif
+\f
+/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
+   use `alloca' instead of `malloc'.  This is because using malloc in
+   re_search* or re_match* could cause memory leaks when C-g is used in
+   Emacs; also, malloc is slower and causes storage fragmentation.  On
+   the other hand, malloc is more portable, and easier to debug.  
+   
+   Because we sometimes use alloca, some routines have to be macros,
+   not functions -- `alloca'-allocated space disappears at the end of the
+   function it is called in.  */
+
+#ifdef REGEX_MALLOC
+
+#define REGEX_ALLOCATE malloc
+#define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
+
+#else /* not REGEX_MALLOC  */
+
+/* Emacs already defines alloca, sometimes.  */
+#ifndef alloca
+
+/* Make alloca work the best possible way.  */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not __GNUC__ or HAVE_ALLOCA_H */
+#ifndef _AIX /* Already did AIX, up at the top.  */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */ 
+#endif /* not __GNUC__ */
+
+#endif /* not alloca */
+
+#define REGEX_ALLOCATE alloca
+
+/* Assumes a `char *destination' variable.  */
+#define REGEX_REALLOCATE(source, osize, nsize)                         \
+  (destination = (char *) alloca (nsize),                              \
+   bcopy (source, destination, osize),                                 \
+   destination)
+
+#endif /* not REGEX_MALLOC */
+
+
+/* True if `size1' is non-NULL and PTR is pointing anywhere inside
+   `string1' or just past its end.  This works if PTR is NULL, which is
+   a good thing.  */
+#define FIRST_STRING_P(ptr)                                    \
+  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)
+
+/* (Re)Allocate N items of type T using malloc, or fail.  */
+#define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
+#define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
+#define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))
+
+#define BYTEWIDTH 8 /* In bits.  */
+
+#define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+typedef char boolean;
+#define false 0
+#define true 1
+\f
+/* These are the command codes that appear in compiled regular
+   expressions.  Some opcodes are followed by argument bytes.  A
+   command code can specify any interpretation whatsoever for its
+   arguments.  Zero bytes may appear in the compiled regular expression.
+
+   The value of `exactn' is needed in search.c (search_buffer) in Emacs.
+   So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of
+   `exactn' we use here must also be 1.  */
+
+typedef enum
+{
+  no_op = 0,
+
+        /* Followed by one byte giving n, then by n literal bytes.  */
+  exactn = 1,
+
+        /* Matches any (more or less) character.  */
+  anychar,
+
+        /* Matches any one char belonging to specified set.  First
+           following byte is number of bitmap bytes.  Then come bytes
+           for a bitmap saying which chars are in.  Bits in each byte
+           are ordered low-bit-first.  A character is in the set if its
+           bit is 1.  A character too large to have a bit in the map is
+           automatically not in the set.  */
+  charset,
+
+        /* Same parameters as charset, but match any character that is
+           not one of those specified.  */
+  charset_not,
+
+        /* Start remembering the text that is matched, for storing in a
+           register.  Followed by one byte with the register number, in
+           the range 0 to one less than the pattern buffer's re_nsub
+           field.  Then followed by one byte with the number of groups
+           inner to this one.  (This last has to be part of the
+           start_memory only because we need it in the on_failure_jump
+           of re_match_2.)  */
+  start_memory,
+
+        /* Stop remembering the text that is matched and store it in a
+           memory register.  Followed by one byte with the register
+           number, in the range 0 to one less than `re_nsub' in the
+           pattern buffer, and one byte with the number of inner groups,
+           just like `start_memory'.  (We need the number of inner
+           groups here because we don't have any easy way of finding the
+           corresponding start_memory when we're at a stop_memory.)  */
+  stop_memory,
+
+        /* Match a duplicate of something remembered. Followed by one
+           byte containing the register number.  */
+  duplicate,
+
+        /* Fail unless at beginning of line.  */
+  begline,
+
+        /* Fail unless at end of line.  */
+  endline,
+
+        /* Succeeds if at beginning of buffer (if emacs) or at beginning
+           of string to be matched (if not).  */
+  begbuf,
+
+        /* Analogously, for end of buffer/string.  */
+  endbuf,
+        /* Followed by two byte relative address to which to jump.  */
+  jump, 
+
+       /* Same as jump, but marks the end of an alternative.  */
+  jump_past_alt,
+
+        /* Followed by two-byte relative address of place to resume at
+           in case of failure.  */
+  on_failure_jump,
+       
+        /* Like on_failure_jump, but pushes a placeholder instead of the
+           current string position when executed.  */
+  on_failure_keep_string_jump,
+  
+        /* Throw away latest failure point and then jump to following
+           two-byte relative address.  */
+  pop_failure_jump,
+
+        /* Change to pop_failure_jump if know won't have to backtrack to
+           match; otherwise change to jump.  This is used to jump
+           back to the beginning of a repeat.  If what follows this jump
+           clearly won't match what the repeat does, such that we can be
+           sure that there is no use backtracking out of repetitions
+           already matched, then we change it to a pop_failure_jump.
+           Followed by two-byte address.  */
+  maybe_pop_jump,
+
+        /* Jump to following two-byte address, and push a dummy failure
+           point. This failure point will be thrown away if an attempt
+           is made to use it for a failure.  A `+' construct makes this
+           before the first repeat.  Also used as an intermediary kind
+           of jump when compiling an alternative.  */
+  dummy_failure_jump,
+
+       /* Push a dummy failure point and continue.  Used at the end of
+          alternatives.  */
+  push_dummy_failure,
+
+        /* Followed by two-byte relative address and two-byte number n.
+           After matching N times, jump to the address upon failure.  */
+  succeed_n,
+
+        /* Followed by two-byte relative address, and two-byte number n.
+           Jump to the address N times, then fail.  */
+  jump_n,
+
+        /* Set the following two-byte relative address to the
+           subsequent two-byte number.  The address *includes* the two
+           bytes of number.  */
+  set_number_at,
+
+  wordchar,    /* Matches any word-constituent character.  */
+  notwordchar, /* Matches any char that is not a word-constituent.  */
+
+  wordbeg,     /* Succeeds if at word beginning.  */
+  wordend,     /* Succeeds if at word end.  */
+
+  wordbound,   /* Succeeds if at a word boundary.  */
+  notwordbound /* Succeeds if not at a word boundary.  */
+
+#ifdef emacs
+  ,before_dot, /* Succeeds if before point.  */
+  at_dot,      /* Succeeds if at point.  */
+  after_dot,   /* Succeeds if after point.  */
+
+       /* Matches any character whose syntax is specified.  Followed by
+           a byte which contains a syntax code, e.g., Sword.  */
+  syntaxspec,
+
+       /* Matches any character whose syntax is not that specified.  */
+  notsyntaxspec
+#endif /* emacs */
+} re_opcode_t;
+\f
+/* Common operations on the compiled pattern.  */
+
+/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
+
+#define STORE_NUMBER(destination, number)                              \
+  do {                                                                 \
+    (destination)[0] = (number) & 0377;                                        \
+    (destination)[1] = (number) >> 8;                                  \
+  } while (0)
+
+/* Same as STORE_NUMBER, except increment DESTINATION to
+   the byte after where the number is stored.  Therefore, DESTINATION
+   must be an lvalue.  */
+
+#define STORE_NUMBER_AND_INCR(destination, number)                     \
+  do {                                                                 \
+    STORE_NUMBER (destination, number);                                        \
+    (destination) += 2;                                                        \
+  } while (0)
+
+/* Put into DESTINATION a number stored in two contiguous bytes starting
+   at SOURCE.  */
+
+#define EXTRACT_NUMBER(destination, source)                            \
+  do {                                                                 \
+    (destination) = *(source) & 0377;                                  \
+    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;          \
+  } while (0)
+
+#ifdef DEBUG
+static void
+extract_number (dest, source)
+    int *dest;
+    unsigned char *source;
+{
+  int temp = SIGN_EXTEND_CHAR (*(source + 1)); 
+  *dest = *source & 0377;
+  *dest += temp << 8;
+}
+
+#ifndef EXTRACT_MACROS /* To debug the macros.  */
+#undef EXTRACT_NUMBER
+#define EXTRACT_NUMBER(dest, src) extract_number (&dest, src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+
+/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
+   SOURCE must be an lvalue.  */
+
+#define EXTRACT_NUMBER_AND_INCR(destination, source)                   \
+  do {                                                                 \
+    EXTRACT_NUMBER (destination, source);                              \
+    (source) += 2;                                                     \
+  } while (0)
+
+#ifdef DEBUG
+static void
+extract_number_and_incr (destination, source)
+    int *destination;
+    unsigned char **source;
+{ 
+  extract_number (destination, *source);
+  *source += 2;
+}
+
+#ifndef EXTRACT_MACROS
+#undef EXTRACT_NUMBER_AND_INCR
+#define EXTRACT_NUMBER_AND_INCR(dest, src) \
+  extract_number_and_incr (&dest, &src)
+#endif /* not EXTRACT_MACROS */
+
+#endif /* DEBUG */
+\f
+/* If DEBUG is defined, Regex prints many voluminous messages about what
+   it is doing (if the variable `debug' is nonzero).  If linked with the
+   main program in `iregex.c', you can enter patterns and strings
+   interactively.  And if linked with the main program in `main.c' and
+   the other test files, you can run the already-written tests.  */
+
+#ifdef DEBUG
+
+/* We use standard I/O for debugging.  */
+#include <stdio.h>
+
+/* It is useful to test things that ``must'' be true when debugging.  */
+#include <assert.h>
+
+static int debug = 0;
+
+#define DEBUG_STATEMENT(e) e
+#define DEBUG_PRINT1(x) if (debug) printf (x)
+#define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)                          \
+  if (debug) print_partial_compiled_pattern (s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)                 \
+  if (debug) print_double_string (w, s1, sz1, s2, sz2)
+
+
+extern void printchar ();
+
+/* Print the fastmap in human-readable form.  */
+
+void
+print_fastmap (fastmap)
+    char *fastmap;
+{
+  unsigned was_a_range = 0;
+  unsigned i = 0;  
+  
+  while (i < (1 << BYTEWIDTH))
+    {
+      if (fastmap[i++])
+       {
+         was_a_range = 0;
+          printchar (i - 1);
+          while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
+            {
+              was_a_range = 1;
+              i++;
+            }
+         if (was_a_range)
+            {
+              printf ("-");
+              printchar (i - 1);
+            }
+        }
+    }
+  putchar ('\n'); 
+}
+
+
+/* Print a compiled pattern string in human-readable form, starting at
+   the START pointer into it and ending just before the pointer END.  */
+
+void
+print_partial_compiled_pattern (start, end)
+    unsigned char *start;
+    unsigned char *end;
+{
+  int mcnt, mcnt2;
+  unsigned char *p = start;
+  unsigned char *pend = end;
+
+  if (start == NULL)
+    {
+      printf ("(null)\n");
+      return;
+    }
+    
+  /* Loop over pattern commands.  */
+  while (p < pend)
+    {
+      switch ((re_opcode_t) *p++)
+       {
+        case no_op:
+          printf ("/no_op");
+          break;
+
+       case exactn:
+         mcnt = *p++;
+          printf ("/exactn/%d", mcnt);
+          do
+           {
+              putchar ('/');
+             printchar (*p++);
+            }
+          while (--mcnt);
+          break;
+
+       case start_memory:
+          mcnt = *p++;
+          printf ("/start_memory/%d/%d", mcnt, *p++);
+          break;
+
+       case stop_memory:
+          mcnt = *p++;
+         printf ("/stop_memory/%d/%d", mcnt, *p++);
+          break;
+
+       case duplicate:
+         printf ("/duplicate/%d", *p++);
+         break;
+
+       case anychar:
+         printf ("/anychar");
+         break;
+
+       case charset:
+        case charset_not:
+          {
+            register int c;
+
+            printf ("/charset%s",
+                   (re_opcode_t) *(p - 1) == charset_not ? "_not" : "");
+            
+            assert (p + *p < pend);
+
+            for (c = 0; c < *p; c++)
+              {
+                unsigned bit;
+                unsigned char map_byte = p[1 + c];
+                
+                putchar ('/');
+
+               for (bit = 0; bit < BYTEWIDTH; bit++)
+                  if (map_byte & (1 << bit))
+                    printchar (c * BYTEWIDTH + bit);
+              }
+           p += 1 + *p;
+           break;
+         }
+
+       case begline:
+         printf ("/begline");
+          break;
+
+       case endline:
+          printf ("/endline");
+          break;
+
+       case on_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/on_failure_jump/0/%d", mcnt);
+          break;
+
+       case on_failure_keep_string_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/on_failure_keep_string_jump/0/%d", mcnt);
+          break;
+
+       case dummy_failure_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/dummy_failure_jump/0/%d", mcnt);
+          break;
+
+       case push_dummy_failure:
+          printf ("/push_dummy_failure");
+          break;
+          
+        case maybe_pop_jump:
+          extract_number_and_incr (&mcnt, &p);
+         printf ("/maybe_pop_jump/0/%d", mcnt);
+         break;
+
+        case pop_failure_jump:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/pop_failure_jump/0/%d", mcnt);
+         break;          
+          
+        case jump_past_alt:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/jump_past_alt/0/%d", mcnt);
+         break;          
+          
+        case jump:
+         extract_number_and_incr (&mcnt, &p);
+         printf ("/jump/0/%d", mcnt);
+         break;
+
+        case succeed_n: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/succeed_n/0/%d/0/%d", mcnt, mcnt2);
+          break;
+        
+        case jump_n: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/jump_n/0/%d/0/%d", mcnt, mcnt2);
+          break;
+        
+        case set_number_at: 
+          extract_number_and_incr (&mcnt, &p);
+          extract_number_and_incr (&mcnt2, &p);
+         printf ("/set_number_at/0/%d/0/%d", mcnt, mcnt2);
+          break;
+        
+        case wordbound:
+         printf ("/wordbound");
+         break;
+
+       case notwordbound:
+         printf ("/notwordbound");
+          break;
+
+       case wordbeg:
+         printf ("/wordbeg");
+         break;
+          
+       case wordend:
+         printf ("/wordend");
+          
+#ifdef emacs
+       case before_dot:
+         printf ("/before_dot");
+          break;
+
+       case at_dot:
+         printf ("/at_dot");
+          break;
+
+       case after_dot:
+         printf ("/after_dot");
+          break;
+
+       case syntaxspec:
+          printf ("/syntaxspec");
+         mcnt = *p++;
+         printf ("/%d", mcnt);
+          break;
+         
+       case notsyntaxspec:
+          printf ("/notsyntaxspec");
+         mcnt = *p++;
+         printf ("/%d", mcnt);
+         break;
+#endif /* emacs */
+
+       case wordchar:
+         printf ("/wordchar");
+          break;
+         
+       case notwordchar:
+         printf ("/notwordchar");
+          break;
+
+       case begbuf:
+         printf ("/begbuf");
+          break;
+
+       case endbuf:
+         printf ("/endbuf");
+          break;
+
+        default:
+          printf ("?%d", *(p-1));
+       }
+    }
+  printf ("/\n");
+}
+
+
+void
+print_compiled_pattern (bufp)
+    struct re_pattern_buffer *bufp;
+{
+  unsigned char *buffer = bufp->buffer;
+
+  print_partial_compiled_pattern (buffer, buffer + bufp->used);
+  printf ("%d bytes used/%d bytes allocated.\n", bufp->used, bufp->allocated);
+
+  if (bufp->fastmap_accurate && bufp->fastmap)
+    {
+      printf ("fastmap: ");
+      print_fastmap (bufp->fastmap);
+    }
+
+  printf ("re_nsub: %d\t", bufp->re_nsub);
+  printf ("regs_alloc: %d\t", bufp->regs_allocated);
+  printf ("can_be_null: %d\t", bufp->can_be_null);
+  printf ("newline_anchor: %d\n", bufp->newline_anchor);
+  printf ("no_sub: %d\t", bufp->no_sub);
+  printf ("not_bol: %d\t", bufp->not_bol);
+  printf ("not_eol: %d\t", bufp->not_eol);
+  printf ("syntax: %d\n", bufp->syntax);
+  /* Perhaps we should print the translate table?  */
+}
+
+
+void
+print_double_string (where, string1, size1, string2, size2)
+    const char *where;
+    const char *string1;
+    const char *string2;
+    int size1;
+    int size2;
+{
+  unsigned this_char;
+  
+  if (where == NULL)
+    printf ("(null)");
+  else
+    {
+      if (FIRST_STRING_P (where))
+        {
+          for (this_char = where - string1; this_char < size1; this_char++)
+            printchar (string1[this_char]);
+
+          where = string2;    
+        }
+
+      for (this_char = where - string2; this_char < size2; this_char++)
+        printchar (string2[this_char]);
+    }
+}
+
+#else /* not DEBUG */
+
+#undef assert
+#define assert(e)
+
+#define DEBUG_STATEMENT(e)
+#define DEBUG_PRINT1(x)
+#define DEBUG_PRINT2(x1, x2)
+#define DEBUG_PRINT3(x1, x2, x3)
+#define DEBUG_PRINT4(x1, x2, x3, x4)
+#define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
+#define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)
+
+#endif /* not DEBUG */
+\f
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+reg_syntax_t re_syntax_options = RE_SYNTAX_EMACS;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+{
+  reg_syntax_t ret = re_syntax_options;
+  
+  re_syntax_options = syntax;
+  return ret;
+}
+\f
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.  */
+
+static const char *re_error_msg[] =
+  { NULL,                                      /* REG_NOERROR */
+    "No match",                                        /* REG_NOMATCH */
+    "Invalid regular expression",              /* REG_BADPAT */
+    "Invalid collation character",             /* REG_ECOLLATE */
+    "Invalid character class name",            /* REG_ECTYPE */
+    "Trailing backslash",                      /* REG_EESCAPE */
+    "Invalid back reference",                  /* REG_ESUBREG */
+    "Unmatched [ or [^",                       /* REG_EBRACK */
+    "Unmatched ( or \\(",                      /* REG_EPAREN */
+    "Unmatched \\{",                           /* REG_EBRACE */
+    "Invalid content of \\{\\}",               /* REG_BADBR */
+    "Invalid range end",                       /* REG_ERANGE */
+    "Memory exhausted",                                /* REG_ESPACE */
+    "Invalid preceding regular expression",    /* REG_BADRPT */
+    "Premature end of regular expression",     /* REG_EEND */
+    "Regular expression too big",              /* REG_ESIZE */
+    "Unmatched ) or \\)",                      /* REG_ERPAREN */
+  };
+\f
+/* Subroutine declarations and macros for regex_compile.  */
+
+static void store_op1 (), store_op2 ();
+static void insert_op1 (), insert_op2 ();
+static boolean at_begline_loc_p (), at_endline_loc_p ();
+static boolean group_in_compile_stack ();
+static reg_errcode_t compile_range ();
+
+/* Fetch the next character in the uncompiled pattern---translating it 
+   if necessary.  Also cast from a signed character in the constant
+   string passed to us by the user to an unsigned char that we can use
+   as an array index (in, e.g., `translate').  */
+#define PATFETCH(c)                                                    \
+  do {if (p == pend) return REG_EEND;                                  \
+    c = (unsigned char) *p++;                                          \
+    if (translate) c = translate[c];                                   \
+  } while (0)
+
+/* Fetch the next character in the uncompiled pattern, with no
+   translation.  */
+#define PATFETCH_RAW(c)                                                        \
+  do {if (p == pend) return REG_EEND;                                  \
+    c = (unsigned char) *p++;                                          \
+  } while (0)
+
+/* Go backwards one character in the pattern.  */
+#define PATUNFETCH p--
+
+
+/* If `translate' is non-null, return translate[D], else just D.  We
+   cast the subscript to translate because some data is declared as
+   `char *', to avoid warnings when a string constant is passed.  But
+   when we use a character as a subscript we must make it unsigned.  */
+#define TRANSLATE(d) (translate ? translate[(unsigned char) (d)] : (d))
+
+
+/* Macros for outputting the compiled pattern into `buffer'.  */
+
+/* If the buffer isn't allocated when it comes in, use this.  */
+#define INIT_BUF_SIZE  32
+
+/* Make sure we have at least N more bytes of space in buffer.  */
+#define GET_BUFFER_SPACE(n)                                            \
+    while (b - bufp->buffer + (n) > bufp->allocated)                   \
+      EXTEND_BUFFER ()
+
+/* Make sure we have one more byte of buffer space and then add C to it.  */
+#define BUF_PUSH(c)                                                    \
+  do {                                                                 \
+    GET_BUFFER_SPACE (1);                                              \
+    *b++ = (unsigned char) (c);                                                \
+  } while (0)
+
+
+/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
+#define BUF_PUSH_2(c1, c2)                                             \
+  do {                                                                 \
+    GET_BUFFER_SPACE (2);                                              \
+    *b++ = (unsigned char) (c1);                                       \
+    *b++ = (unsigned char) (c2);                                       \
+  } while (0)
+
+
+/* As with BUF_PUSH_2, except for three bytes.  */
+#define BUF_PUSH_3(c1, c2, c3)                                         \
+  do {                                                                 \
+    GET_BUFFER_SPACE (3);                                              \
+    *b++ = (unsigned char) (c1);                                       \
+    *b++ = (unsigned char) (c2);                                       \
+    *b++ = (unsigned char) (c3);                                       \
+  } while (0)
+
+
+/* Store a jump with opcode OP at LOC to location TO.  We store a
+   relative address offset by the three bytes the jump itself occupies.  */
+#define STORE_JUMP(op, loc, to) \
+  store_op1 (op, loc, (to) - (loc) - 3)
+
+/* Likewise, for a two-argument jump.  */
+#define STORE_JUMP2(op, loc, to, arg) \
+  store_op2 (op, loc, (to) - (loc) - 3, arg)
+
+/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP(op, loc, to) \
+  insert_op1 (op, loc, (to) - (loc) - 3, b)
+
+/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
+#define INSERT_JUMP2(op, loc, to, arg) \
+  insert_op2 (op, loc, (to) - (loc) - 3, arg, b)
+
+
+/* This is not an arbitrary limit: the arguments which represent offsets
+   into the pattern are two bytes long.  So if 2^16 bytes turns out to
+   be too small, many things would have to change.  */
+#define MAX_BUF_SIZE (1L << 16)
+
+
+/* Extend the buffer by twice its current size via realloc and
+   reset the pointers that pointed into the old block to point to the
+   correct places in the new one.  If extending the buffer results in it
+   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
+#define EXTEND_BUFFER()                                                        \
+  do {                                                                         \
+    unsigned char *old_buffer = bufp->buffer;                          \
+    if (bufp->allocated == MAX_BUF_SIZE)                               \
+      return REG_ESIZE;                                                        \
+    bufp->allocated <<= 1;                                             \
+    if (bufp->allocated > MAX_BUF_SIZE)                                        \
+      bufp->allocated = MAX_BUF_SIZE;                                  \
+    bufp->buffer = (unsigned char *) realloc (bufp->buffer, bufp->allocated);\
+    if (bufp->buffer == NULL)                                          \
+      return REG_ESPACE;                                               \
+    /* If the buffer moved, move all the pointers into it.  */         \
+    if (old_buffer != bufp->buffer)                                    \
+      {                                                                        \
+        b = (b - old_buffer) + bufp->buffer;                           \
+        begalt = (begalt - old_buffer) + bufp->buffer;                 \
+        if (fixup_alt_jump)                                            \
+          fixup_alt_jump = (fixup_alt_jump - old_buffer) + bufp->buffer;\
+        if (laststart)                                                 \
+          laststart = (laststart - old_buffer) + bufp->buffer;         \
+        if (pending_exact)                                             \
+          pending_exact = (pending_exact - old_buffer) + bufp->buffer; \
+      }                                                                        \
+  } while (0)
+
+
+/* Since we have one byte reserved for the register number argument to
+   {start,stop}_memory, the maximum number of groups we can report
+   things about is what fits in that byte.  */
+#define MAX_REGNUM 255
+
+/* But patterns can have more than `MAX_REGNUM' registers.  We just
+   ignore the excess.  */
+typedef unsigned regnum_t;
+
+
+/* Macros for the compile stack.  */
+
+/* Since offsets can go either forwards or backwards, this type needs to
+   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
+typedef int pattern_offset_t;
+
+typedef struct
+{
+  pattern_offset_t begalt_offset;
+  pattern_offset_t fixup_alt_jump;
+  pattern_offset_t inner_group_offset;
+  pattern_offset_t laststart_offset;  
+  regnum_t regnum;
+} compile_stack_elt_t;
+
+
+typedef struct
+{
+  compile_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;                      /* Offset of next open position.  */
+} compile_stack_type;
+
+
+#define INIT_COMPILE_STACK_SIZE 32
+
+#define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
+#define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)
+
+/* The next available element.  */
+#define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])
+
+
+/* Set the bit for character C in a list.  */
+#define SET_LIST_BIT(c)                               \
+  (b[((unsigned char) (c)) / BYTEWIDTH]               \
+   |= 1 << (((unsigned char) c) % BYTEWIDTH))
+
+
+/* Get the next unsigned number in the uncompiled pattern.  */
+#define GET_UNSIGNED_NUMBER(num)                                       \
+  { if (p != pend)                                                     \
+     {                                                                 \
+       PATFETCH (c);                                                   \
+       while (ISDIGIT (c))                                             \
+         {                                                             \
+           if (num < 0)                                                        \
+              num = 0;                                                 \
+           num = num * 10 + c - '0';                                   \
+           if (p == pend)                                              \
+              break;                                                   \
+           PATFETCH (c);                                               \
+         }                                                             \
+       }                                                               \
+    }          
+
+#define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+#define IS_CHAR_CLASS(string)                                          \
+   (STREQ (string, "alpha") || STREQ (string, "upper")                 \
+    || STREQ (string, "lower") || STREQ (string, "digit")              \
+    || STREQ (string, "alnum") || STREQ (string, "xdigit")             \
+    || STREQ (string, "space") || STREQ (string, "print")              \
+    || STREQ (string, "punct") || STREQ (string, "graph")              \
+    || STREQ (string, "cntrl") || STREQ (string, "blank"))
+\f
+/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
+   Returns one of error codes defined in `regex.h', or zero for success.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate'
+   fields are set in BUFP on entry.
+
+   If it succeeds, results are put in BUFP (if it returns an error, the
+   contents of BUFP are undefined):
+     `buffer' is the compiled pattern;
+     `syntax' is set to SYNTAX;
+     `used' is set to the length of the compiled pattern;
+     `fastmap_accurate' is zero;
+     `re_nsub' is the number of subexpressions in PATTERN;
+     `not_bol' and `not_eol' are zero;
+   
+   The `fastmap' and `newline_anchor' fields are neither
+   examined nor set.  */
+
+static reg_errcode_t
+regex_compile (pattern, size, syntax, bufp)
+     const char *pattern;
+     int size;
+     reg_syntax_t syntax;
+     struct re_pattern_buffer *bufp;
+{
+  /* We fetch characters from PATTERN here.  Even though PATTERN is
+     `char *' (i.e., signed), we declare these variables as unsigned, so
+     they can be reliably used as array indices.  */
+  register unsigned char c, c1;
+  
+  /* A random tempory spot in PATTERN.  */
+  const char *p1;
+
+  /* Points to the end of the buffer, where we should append.  */
+  register unsigned char *b;
+  
+  /* Keeps track of unclosed groups.  */
+  compile_stack_type compile_stack;
+
+  /* Points to the current (ending) position in the pattern.  */
+  const char *p = pattern;
+  const char *pend = pattern + size;
+  
+  /* How to translate the characters in the pattern.  */
+  char *translate = bufp->translate;
+
+  /* Address of the count-byte of the most recently inserted `exactn'
+     command.  This makes it possible to tell if a new exact-match
+     character can be added to that command or if the character requires
+     a new `exactn' command.  */
+  unsigned char *pending_exact = 0;
+
+  /* Address of start of the most recently finished expression.
+     This tells, e.g., postfix * where to find the start of its
+     operand.  Reset at the beginning of groups and alternatives.  */
+  unsigned char *laststart = 0;
+
+  /* Address of beginning of regexp, or inside of last group.  */
+  unsigned char *begalt;
+
+  /* Place in the uncompiled pattern (i.e., the {) to
+     which to go back if the interval is invalid.  */
+  const char *beg_interval;
+                
+  /* Address of the place where a forward jump should go to the end of
+     the containing expression.  Each alternative of an `or' -- except the
+     last -- ends with a forward jump of this sort.  */
+  unsigned char *fixup_alt_jump = 0;
+
+  /* Counts open-groups as they are encountered.  Remembered for the
+     matching close-group on the compile stack, so the same register
+     number is put in the stop_memory as the start_memory.  */
+  regnum_t regnum = 0;
+
+#ifdef DEBUG
+  DEBUG_PRINT1 ("\nCompiling pattern: ");
+  if (debug)
+    {
+      unsigned debug_count;
+      
+      for (debug_count = 0; debug_count < size; debug_count++)
+        printchar (pattern[debug_count]);
+      putchar ('\n');
+    }
+#endif /* DEBUG */
+
+  /* Initialize the compile stack.  */
+  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
+  if (compile_stack.stack == NULL)
+    return REG_ESPACE;
+
+  compile_stack.size = INIT_COMPILE_STACK_SIZE;
+  compile_stack.avail = 0;
+
+  /* Initialize the pattern buffer.  */
+  bufp->syntax = syntax;
+  bufp->fastmap_accurate = 0;
+  bufp->not_bol = bufp->not_eol = 0;
+
+  /* Set `used' to zero, so that if we return an error, the pattern
+     printer (for debugging) will think there's no pattern.  We reset it
+     at the end.  */
+  bufp->used = 0;
+  
+  /* Always count groups, whether or not bufp->no_sub is set.  */
+  bufp->re_nsub = 0;                           
+
+#if !defined (emacs) && !defined (SYNTAX_TABLE)
+  /* Initialize the syntax table.  */
+   init_syntax_once ();
+#endif
+
+  if (bufp->allocated == 0)
+    {
+      if (bufp->buffer)
+       { /* If zero allocated, but buffer is non-null, try to realloc
+             enough space.  This loses if buffer's address is bogus, but
+             that is the user's responsibility.  */
+          RETALLOC (bufp->buffer, INIT_BUF_SIZE, unsigned char);
+        }
+      else
+        { /* Caller did not allocate a buffer.  Do it for them.  */
+          bufp->buffer = TALLOC (INIT_BUF_SIZE, unsigned char);
+        }
+      if (!bufp->buffer) return REG_ESPACE;
+
+      bufp->allocated = INIT_BUF_SIZE;
+    }
+
+  begalt = b = bufp->buffer;
+
+  /* Loop through the uncompiled pattern until we're at the end.  */
+  while (p != pend)
+    {
+      PATFETCH (c);
+
+      switch (c)
+        {
+        case '^':
+          {
+            if (   /* If at start of pattern, it's an operator.  */
+                   p == pattern + 1
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's come before.  */
+                || at_begline_loc_p (pattern, p, syntax))
+              BUF_PUSH (begline);
+            else
+              goto normal_char;
+          }
+          break;
+
+
+        case '$':
+          {
+            if (   /* If at end of pattern, it's an operator.  */
+                   p == pend 
+                   /* If context independent, it's an operator.  */
+                || syntax & RE_CONTEXT_INDEP_ANCHORS
+                   /* Otherwise, depends on what's next.  */
+                || at_endline_loc_p (p, pend, syntax))
+               BUF_PUSH (endline);
+             else
+               goto normal_char;
+           }
+           break;
+
+
+       case '+':
+        case '?':
+          if ((syntax & RE_BK_PLUS_QM)
+              || (syntax & RE_LIMITED_OPS))
+            goto normal_char;
+        handle_plus:
+        case '*':
+          /* If there is no previous pattern... */
+          if (!laststart)
+            {
+              if (syntax & RE_CONTEXT_INVALID_OPS)
+                return REG_BADRPT;
+              else if (!(syntax & RE_CONTEXT_INDEP_OPS))
+                goto normal_char;
+            }
+
+          {
+            /* Are we optimizing this jump?  */
+            boolean keep_string_p = false;
+            
+            /* 1 means zero (many) matches is allowed.  */
+            char zero_times_ok = 0, many_times_ok = 0;
+
+            /* If there is a sequence of repetition chars, collapse it
+               down to just one (the right one).  We can't combine
+               interval operators with these because of, e.g., `a{2}*',
+               which should only match an even number of `a's.  */
+
+            for (;;)
+              {
+                zero_times_ok |= c != '+';
+                many_times_ok |= c != '?';
+
+                if (p == pend)
+                  break;
+
+                PATFETCH (c);
+
+                if (c == '*'
+                    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
+                  ;
+
+                else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
+                  {
+                    if (p == pend) return REG_EESCAPE;
+
+                    PATFETCH (c1);
+                    if (!(c1 == '+' || c1 == '?'))
+                      {
+                        PATUNFETCH;
+                        PATUNFETCH;
+                        break;
+                      }
+
+                    c = c1;
+                  }
+                else
+                  {
+                    PATUNFETCH;
+                    break;
+                  }
+
+                /* If we get here, we found another repeat character.  */
+               }
+
+            /* Star, etc. applied to an empty pattern is equivalent
+               to an empty pattern.  */
+            if (!laststart)  
+              break;
+
+            /* Now we know whether or not zero matches is allowed
+               and also whether or not two or more matches is allowed.  */
+            if (many_times_ok)
+              { /* More than one repetition is allowed, so put in at the
+                   end a backward relative jump from `b' to before the next
+                   jump we're going to put in below (which jumps from
+                   laststart to after this jump).  
+
+                   But if we are at the `*' in the exact sequence `.*\n',
+                   insert an unconditional jump backwards to the .,
+                   instead of the beginning of the loop.  This way we only
+                   push a failure point once, instead of every time
+                   through the loop.  */
+                assert (p - 1 > pattern);
+
+                /* Allocate the space for the jump.  */
+                GET_BUFFER_SPACE (3);
+
+                /* We know we are not at the first character of the pattern,
+                   because laststart was nonzero.  And we've already
+                   incremented `p', by the way, to be the character after
+                   the `*'.  Do we have to do something analogous here
+                   for null bytes, because of RE_DOT_NOT_NULL?  */
+                if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
+                   && zero_times_ok
+                    && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
+                    && !(syntax & RE_DOT_NEWLINE))
+                  { /* We have .*\n.  */
+                    STORE_JUMP (jump, b, laststart);
+                    keep_string_p = true;
+                  }
+                else
+                  /* Anything else.  */
+                  STORE_JUMP (maybe_pop_jump, b, laststart - 3);
+
+                /* We've added more stuff to the buffer.  */
+                b += 3;
+              }
+
+            /* On failure, jump from laststart to b + 3, which will be the
+               end of the buffer after this jump is inserted.  */
+            GET_BUFFER_SPACE (3);
+            INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
+                                       : on_failure_jump,
+                         laststart, b + 3);
+            pending_exact = 0;
+            b += 3;
+
+            if (!zero_times_ok)
+              {
+                /* At least one repetition is required, so insert a
+                   `dummy_failure_jump' before the initial
+                   `on_failure_jump' instruction of the loop. This
+                   effects a skip over that instruction the first time
+                   we hit that loop.  */
+                GET_BUFFER_SPACE (3);
+                INSERT_JUMP (dummy_failure_jump, laststart, laststart + 6);
+                b += 3;
+              }
+            }
+         break;
+
+
+       case '.':
+          laststart = b;
+          BUF_PUSH (anychar);
+          break;
+
+
+        case '[':
+          {
+            boolean had_char_class = false;
+
+            if (p == pend) return REG_EBRACK;
+
+            /* Ensure that we have enough space to push a charset: the
+               opcode, the length count, and the bitset; 34 bytes in all.  */
+           GET_BUFFER_SPACE (34);
+
+            laststart = b;
+
+            /* We test `*p == '^' twice, instead of using an if
+               statement, so we only need one BUF_PUSH.  */
+            BUF_PUSH (*p == '^' ? charset_not : charset); 
+            if (*p == '^')
+              p++;
+
+            /* Remember the first position in the bracket expression.  */
+            p1 = p;
+
+            /* Push the number of bytes in the bitmap.  */
+            BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* Clear the whole map.  */
+            bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);
+
+            /* charset_not matches newline according to a syntax bit.  */
+            if ((re_opcode_t) b[-2] == charset_not
+                && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
+              SET_LIST_BIT ('\n');
+
+            /* Read in characters and ranges, setting map bits.  */
+            for (;;)
+              {
+                if (p == pend) return REG_EBRACK;
+
+                PATFETCH (c);
+
+                /* \ might escape characters inside [...] and [^...].  */
+                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
+                  {
+                    if (p == pend) return REG_EESCAPE;
+
+                    PATFETCH (c1);
+                    SET_LIST_BIT (c1);
+                    continue;
+                  }
+
+                /* Could be the end of the bracket expression.  If it's
+                   not (i.e., when the bracket expression is `[]' so
+                   far), the ']' character bit gets set way below.  */
+                if (c == ']' && p != p1 + 1)
+                  break;
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character class.  */
+                if (had_char_class && c == '-' && *p != ']')
+                  return REG_ERANGE;
+
+                /* Look ahead to see if it's a range when the last thing
+                   was a character: if this is a hyphen not at the
+                   beginning or the end of a list, then it's the range
+                   operator.  */
+                if (c == '-' 
+                    && !(p - 2 >= pattern && p[-2] == '[') 
+                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
+                    && *p != ']')
+                  {
+                    reg_errcode_t ret
+                      = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) return ret;
+                  }
+
+                else if (p[0] == '-' && p[1] != ']')
+                  { /* This handles ranges made up of characters only.  */
+                    reg_errcode_t ret;
+
+                   /* Move past the `-'.  */
+                    PATFETCH (c1);
+                    
+                    ret = compile_range (&p, pend, translate, syntax, b);
+                    if (ret != REG_NOERROR) return ret;
+                  }
+
+                /* See if we're at the beginning of a possible character
+                   class.  */
+
+                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
+                  { /* Leave room for the null.  */
+                    char str[CHAR_CLASS_MAX_LENGTH + 1];
+
+                    PATFETCH (c);
+                    c1 = 0;
+
+                    /* If pattern is `[[:'.  */
+                    if (p == pend) return REG_EBRACK;
+
+                    for (;;)
+                      {
+                        PATFETCH (c);
+                        if (c == ':' || c == ']' || p == pend
+                            || c1 == CHAR_CLASS_MAX_LENGTH)
+                          break;
+                        str[c1++] = c;
+                      }
+                    str[c1] = '\0';
+
+                    /* If isn't a word bracketed by `[:' and:`]':
+                       undo the ending character, the letters, and leave 
+                       the leading `:' and `[' (but set bits for them).  */
+                    if (c == ':' && *p == ']')
+                      {
+                        int ch;
+                        boolean is_alnum = STREQ (str, "alnum");
+                        boolean is_alpha = STREQ (str, "alpha");
+                        boolean is_blank = STREQ (str, "blank");
+                        boolean is_cntrl = STREQ (str, "cntrl");
+                        boolean is_digit = STREQ (str, "digit");
+                        boolean is_graph = STREQ (str, "graph");
+                        boolean is_lower = STREQ (str, "lower");
+                        boolean is_print = STREQ (str, "print");
+                        boolean is_punct = STREQ (str, "punct");
+                        boolean is_space = STREQ (str, "space");
+                        boolean is_upper = STREQ (str, "upper");
+                        boolean is_xdigit = STREQ (str, "xdigit");
+                        
+                        if (!IS_CHAR_CLASS (str)) return REG_ECTYPE;
+
+                        /* Throw away the ] at the end of the character
+                           class.  */
+                        PATFETCH (c);                                  
+
+                        if (p == pend) return REG_EBRACK;
+
+                        for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
+                          {
+                            if (   (is_alnum  && ISALNUM (ch))
+                                || (is_alpha  && ISALPHA (ch))
+                                || (is_blank  && ISBLANK (ch))
+                                || (is_cntrl  && ISCNTRL (ch))
+                                || (is_digit  && ISDIGIT (ch))
+                                || (is_graph  && ISGRAPH (ch))
+                                || (is_lower  && ISLOWER (ch))
+                                || (is_print  && ISPRINT (ch))
+                                || (is_punct  && ISPUNCT (ch))
+                                || (is_space  && ISSPACE (ch))
+                                || (is_upper  && ISUPPER (ch))
+                                || (is_xdigit && ISXDIGIT (ch)))
+                            SET_LIST_BIT (ch);
+                          }
+                        had_char_class = true;
+                      }
+                    else
+                      {
+                        c1++;
+                        while (c1--)    
+                          PATUNFETCH;
+                        SET_LIST_BIT ('[');
+                        SET_LIST_BIT (':');
+                        had_char_class = false;
+                      }
+                  }
+                else
+                  {
+                    had_char_class = false;
+                    SET_LIST_BIT (c);
+                  }
+              }
+
+            /* Discard any (non)matching list bytes that are all 0 at the
+               end of the map.  Decrease the map-length byte too.  */
+            while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) 
+              b[-1]--; 
+            b += b[-1];
+          }
+          break;
+
+
+       case '(':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_open;
+          else
+            goto normal_char;
+
+
+        case ')':
+          if (syntax & RE_NO_BK_PARENS)
+            goto handle_close;
+          else
+            goto normal_char;
+
+
+        case '\n':
+          if (syntax & RE_NEWLINE_ALT)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+       case '|':
+          if (syntax & RE_NO_BK_VBAR)
+            goto handle_alt;
+          else
+            goto normal_char;
+
+
+        case '{':
+           if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
+             goto handle_interval;
+           else
+             goto normal_char;
+
+
+        case '\\':
+          if (p == pend) return REG_EESCAPE;
+
+          /* Do not translate the character after the \, so that we can
+             distinguish, e.g., \B from \b, even if we normally would
+             translate, e.g., B to b.  */
+          PATFETCH_RAW (c);
+
+          switch (c)
+            {
+            case '(':
+              if (syntax & RE_NO_BK_PARENS)
+                goto normal_backslash;
+
+            handle_open:
+              bufp->re_nsub++;
+              regnum++;
+
+              if (COMPILE_STACK_FULL)
+                { 
+                  RETALLOC (compile_stack.stack, compile_stack.size << 1,
+                            compile_stack_elt_t);
+                  if (compile_stack.stack == NULL) return REG_ESPACE;
+
+                  compile_stack.size <<= 1;
+                }
+
+              /* These are the values to restore when we hit end of this
+                 group.  They are all relative offsets, so that if the
+                 whole pattern moves because of realloc, they will still
+                 be valid.  */
+              COMPILE_STACK_TOP.begalt_offset = begalt - bufp->buffer;
+              COMPILE_STACK_TOP.fixup_alt_jump 
+                = fixup_alt_jump ? fixup_alt_jump - bufp->buffer + 1 : 0;
+              COMPILE_STACK_TOP.laststart_offset = b - bufp->buffer;
+              COMPILE_STACK_TOP.regnum = regnum;
+
+              /* We will eventually replace the 0 with the number of
+                 groups inner to this one.  But do not push a
+                 start_memory for groups beyond the last one we can
+                 represent in the compiled pattern.  */
+              if (regnum <= MAX_REGNUM)
+                {
+                  COMPILE_STACK_TOP.inner_group_offset = b - bufp->buffer + 2;
+                  BUF_PUSH_3 (start_memory, regnum, 0);
+                }
+                
+              compile_stack.avail++;
+
+              fixup_alt_jump = 0;
+              laststart = 0;
+              begalt = b;
+             /* If we've reached MAX_REGNUM groups, then this open
+                won't actually generate any code, so we'll have to
+                clear pending_exact explicitly.  */
+             pending_exact = 0;
+              break;
+
+
+            case ')':
+              if (syntax & RE_NO_BK_PARENS) goto normal_backslash;
+
+              if (COMPILE_STACK_EMPTY) 
+               {
+                 if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                   goto normal_backslash;
+                 else
+                   return REG_ERPAREN;
+               }
+
+            handle_close:
+              if (fixup_alt_jump)
+                { /* Push a dummy failure point at the end of the
+                     alternative for a possible future
+                     `pop_failure_jump' to pop.  See comments at
+                     `push_dummy_failure' in `re_match_2'.  */
+                  BUF_PUSH (push_dummy_failure);
+                  
+                  /* We allocated space for this jump when we assigned
+                     to `fixup_alt_jump', in the `handle_alt' case below.  */
+                  STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
+                }
+
+              /* See similar code for backslashed left paren above.  */
+              if (COMPILE_STACK_EMPTY)
+               {
+                 if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
+                   goto normal_char;
+                 else
+                   return REG_ERPAREN;
+               }
+
+              /* Since we just checked for an empty stack above, this
+                 ``can't happen''.  */
+              assert (compile_stack.avail != 0);
+              {
+                /* We don't just want to restore into `regnum', because
+                   later groups should continue to be numbered higher,
+                   as in `(ab)c(de)' -- the second group is #2.  */
+                regnum_t this_group_regnum;
+
+                compile_stack.avail--;         
+                begalt = bufp->buffer + COMPILE_STACK_TOP.begalt_offset;
+                fixup_alt_jump
+                  = COMPILE_STACK_TOP.fixup_alt_jump
+                    ? bufp->buffer + COMPILE_STACK_TOP.fixup_alt_jump - 1 
+                    : 0;
+                laststart = bufp->buffer + COMPILE_STACK_TOP.laststart_offset;
+                this_group_regnum = COMPILE_STACK_TOP.regnum;
+               /* If we've reached MAX_REGNUM groups, then this open
+                  won't actually generate any code, so we'll have to
+                  clear pending_exact explicitly.  */
+               pending_exact = 0;
+
+                /* We're at the end of the group, so now we know how many
+                   groups were inside this one.  */
+                if (this_group_regnum <= MAX_REGNUM)
+                  {
+                    unsigned char *inner_group_loc
+                      = bufp->buffer + COMPILE_STACK_TOP.inner_group_offset;
+                    
+                    *inner_group_loc = regnum - this_group_regnum;
+                    BUF_PUSH_3 (stop_memory, this_group_regnum,
+                                regnum - this_group_regnum);
+                  }
+              }
+              break;
+
+
+            case '|':                                  /* `\|'.  */
+              if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
+                goto normal_backslash;
+            handle_alt:
+              if (syntax & RE_LIMITED_OPS)
+                goto normal_char;
+
+              /* Insert before the previous alternative a jump which
+                 jumps to this alternative if the former fails.  */
+              GET_BUFFER_SPACE (3);
+              INSERT_JUMP (on_failure_jump, begalt, b + 6);
+              pending_exact = 0;
+              b += 3;
+
+              /* The alternative before this one has a jump after it
+                 which gets executed if it gets matched.  Adjust that
+                 jump so it will jump to this alternative's analogous
+                 jump (put in below, which in turn will jump to the next
+                 (if any) alternative's such jump, etc.).  The last such
+                 jump jumps to the correct final destination.  A picture:
+                          _____ _____ 
+                          |   | |   |   
+                          |   v |   v 
+                         a | b   | c   
+
+                 If we are at `b', then fixup_alt_jump right now points to a
+                 three-byte space after `a'.  We'll put in the jump, set
+                 fixup_alt_jump to right after `b', and leave behind three
+                 bytes which we'll fill in when we get to after `c'.  */
+
+              if (fixup_alt_jump)
+                STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+              /* Mark and leave space for a jump after this alternative,
+                 to be filled in later either by next alternative or
+                 when know we're at the end of a series of alternatives.  */
+              fixup_alt_jump = b;
+              GET_BUFFER_SPACE (3);
+              b += 3;
+
+              laststart = 0;
+              begalt = b;
+              break;
+
+
+            case '{': 
+              /* If \{ is a literal.  */
+              if (!(syntax & RE_INTERVALS)
+                     /* If we're at `\{' and it's not the open-interval 
+                        operator.  */
+                  || ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+                  || (p - 2 == pattern  &&  p == pend))
+                goto normal_backslash;
+
+            handle_interval:
+              {
+                /* If got here, then the syntax allows intervals.  */
+
+                /* At least (most) this many matches must be made.  */
+                int lower_bound = -1, upper_bound = -1;
+
+                beg_interval = p - 1;
+
+                if (p == pend)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else
+                      return REG_EBRACE;
+                  }
+
+                GET_UNSIGNED_NUMBER (lower_bound);
+
+                if (c == ',')
+                  {
+                    GET_UNSIGNED_NUMBER (upper_bound);
+                    if (upper_bound < 0) upper_bound = RE_DUP_MAX;
+                  }
+                else
+                  /* Interval such as `{1}' => match exactly once. */
+                  upper_bound = lower_bound;
+
+                if (lower_bound < 0 || upper_bound > RE_DUP_MAX
+                    || lower_bound > upper_bound)
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      return REG_BADBR;
+                  }
+
+                if (!(syntax & RE_NO_BK_BRACES)) 
+                  {
+                    if (c != '\\') return REG_EBRACE;
+
+                    PATFETCH (c);
+                  }
+
+                if (c != '}')
+                  {
+                    if (syntax & RE_NO_BK_BRACES)
+                      goto unfetch_interval;
+                    else 
+                      return REG_BADBR;
+                  }
+
+                /* We just parsed a valid interval.  */
+
+                /* If it's invalid to have no preceding re.  */
+                if (!laststart)
+                  {
+                    if (syntax & RE_CONTEXT_INVALID_OPS)
+                      return REG_BADRPT;
+                    else if (syntax & RE_CONTEXT_INDEP_OPS)
+                      laststart = b;
+                    else
+                      goto unfetch_interval;
+                  }
+
+                /* If the upper bound is zero, don't want to succeed at
+                   all; jump from `laststart' to `b + 3', which will be
+                   the end of the buffer after we insert the jump.  */
+                 if (upper_bound == 0)
+                   {
+                     GET_BUFFER_SPACE (3);
+                     INSERT_JUMP (jump, laststart, b + 3);
+                     b += 3;
+                   }
+
+                 /* Otherwise, we have a nontrivial interval.  When
+                    we're all done, the pattern will look like:
+                      set_number_at <jump count> <upper bound>
+                      set_number_at <succeed_n count> <lower bound>
+                      succeed_n <after jump addr> <succed_n count>
+                      <body of loop>
+                      jump_n <succeed_n addr> <jump count>
+                    (The upper bound and `jump_n' are omitted if
+                    `upper_bound' is 1, though.)  */
+                 else 
+                   { /* If the upper bound is > 1, we need to insert
+                        more at the end of the loop.  */
+                     unsigned nbytes = 10 + (upper_bound > 1) * 10;
+
+                     GET_BUFFER_SPACE (nbytes);
+
+                     /* Initialize lower bound of the `succeed_n', even
+                        though it will be set during matching by its
+                        attendant `set_number_at' (inserted next),
+                        because `re_compile_fastmap' needs to know.
+                        Jump to the `jump_n' we might insert below.  */
+                     INSERT_JUMP2 (succeed_n, laststart,
+                                   b + 5 + (upper_bound > 1) * 5,
+                                   lower_bound);
+                     b += 5;
+
+                     /* Code to initialize the lower bound.  Insert 
+                        before the `succeed_n'.  The `5' is the last two
+                        bytes of this `set_number_at', plus 3 bytes of
+                        the following `succeed_n'.  */
+                     insert_op2 (set_number_at, laststart, 5, lower_bound, b);
+                     b += 5;
+
+                     if (upper_bound > 1)
+                       { /* More than one repetition is allowed, so
+                            append a backward jump to the `succeed_n'
+                            that starts this interval.
+                            
+                            When we've reached this during matching,
+                            we'll have matched the interval once, so
+                            jump back only `upper_bound - 1' times.  */
+                         STORE_JUMP2 (jump_n, b, laststart + 5,
+                                      upper_bound - 1);
+                         b += 5;
+
+                         /* The location we want to set is the second
+                            parameter of the `jump_n'; that is `b-2' as
+                            an absolute address.  `laststart' will be
+                            the `set_number_at' we're about to insert;
+                            `laststart+3' the number to set, the source
+                            for the relative address.  But we are
+                            inserting into the middle of the pattern --
+                            so everything is getting moved up by 5.
+                            Conclusion: (b - 2) - (laststart + 3) + 5,
+                            i.e., b - laststart.
+                            
+                            We insert this at the beginning of the loop
+                            so that if we fail during matching, we'll
+                            reinitialize the bounds.  */
+                         insert_op2 (set_number_at, laststart, b - laststart,
+                                     upper_bound - 1, b);
+                         b += 5;
+                       }
+                   }
+                pending_exact = 0;
+                beg_interval = NULL;
+              }
+              break;
+
+            unfetch_interval:
+              /* If an invalid interval, match the characters as literals.  */
+               assert (beg_interval);
+               p = beg_interval;
+               beg_interval = NULL;
+
+               /* normal_char and normal_backslash need `c'.  */
+               PATFETCH (c);   
+
+               if (!(syntax & RE_NO_BK_BRACES))
+                 {
+                   if (p > pattern  &&  p[-1] == '\\')
+                     goto normal_backslash;
+                 }
+               goto normal_char;
+
+#ifdef emacs
+            /* There is no way to specify the before_dot and after_dot
+               operators.  rms says this is ok.  --karl  */
+            case '=':
+              BUF_PUSH (at_dot);
+              break;
+
+            case 's':  
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
+              break;
+
+            case 'S':
+              laststart = b;
+              PATFETCH (c);
+              BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
+              break;
+#endif /* emacs */
+
+
+            case 'w':
+              laststart = b;
+              BUF_PUSH (wordchar);
+              break;
+
+
+            case 'W':
+              laststart = b;
+              BUF_PUSH (notwordchar);
+              break;
+
+
+            case '<':
+              BUF_PUSH (wordbeg);
+              break;
+
+            case '>':
+              BUF_PUSH (wordend);
+              break;
+
+            case 'b':
+              BUF_PUSH (wordbound);
+              break;
+
+            case 'B':
+              BUF_PUSH (notwordbound);
+              break;
+
+            case '`':
+              BUF_PUSH (begbuf);
+              break;
+
+            case '\'':
+              BUF_PUSH (endbuf);
+              break;
+
+            case '1': case '2': case '3': case '4': case '5':
+            case '6': case '7': case '8': case '9':
+              if (syntax & RE_NO_BK_REFS)
+                goto normal_char;
+
+              c1 = c - '0';
+
+              if (c1 > regnum)
+                return REG_ESUBREG;
+
+              /* Can't back reference to a subexpression if inside of it.  */
+              if (group_in_compile_stack (compile_stack, c1))
+                goto normal_char;
+
+              laststart = b;
+              BUF_PUSH_2 (duplicate, c1);
+              break;
+
+
+            case '+':
+            case '?':
+              if (syntax & RE_BK_PLUS_QM)
+                goto handle_plus;
+              else
+                goto normal_backslash;
+
+            default:
+            normal_backslash:
+              /* You might think it would be useful for \ to mean
+                 not to translate; but if we don't translate it
+                 it will never match anything.  */
+              c = TRANSLATE (c);
+              goto normal_char;
+            }
+          break;
+
+
+       default:
+        /* Expects the character in `c'.  */
+       normal_char:
+             /* If no exactn currently being built.  */
+          if (!pending_exact 
+
+              /* If last exactn not at current position.  */
+              || pending_exact + *pending_exact + 1 != b
+              
+              /* We have only one byte following the exactn for the count.  */
+             || *pending_exact == (1 << BYTEWIDTH) - 1
+
+              /* If followed by a repetition operator.  */
+              || *p == '*' || *p == '^'
+             || ((syntax & RE_BK_PLUS_QM)
+                 ? *p == '\\' && (p[1] == '+' || p[1] == '?')
+                 : (*p == '+' || *p == '?'))
+             || ((syntax & RE_INTERVALS)
+                  && ((syntax & RE_NO_BK_BRACES)
+                     ? *p == '{'
+                      : (p[0] == '\\' && p[1] == '{'))))
+           {
+             /* Start building a new exactn.  */
+              
+              laststart = b;
+
+             BUF_PUSH_2 (exactn, 0);
+             pending_exact = b - 1;
+            }
+            
+         BUF_PUSH (c);
+          (*pending_exact)++;
+         break;
+        } /* switch (c) */
+    } /* while p != pend */
+
+  
+  /* Through the pattern now.  */
+  
+  if (fixup_alt_jump)
+    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);
+
+  if (!COMPILE_STACK_EMPTY) 
+    return REG_EPAREN;
+
+  free (compile_stack.stack);
+
+  /* We have succeeded; set the length of the buffer.  */
+  bufp->used = b - bufp->buffer;
+
+#ifdef DEBUG
+  if (debug)
+    {
+      DEBUG_PRINT1 ("\nCompiled pattern: ");
+      print_compiled_pattern (bufp);
+    }
+#endif /* DEBUG */
+
+  return REG_NOERROR;
+} /* regex_compile */
+\f
+/* Subroutines for `regex_compile'.  */
+
+/* Store OP at LOC followed by two-byte integer parameter ARG.  */
+
+static void
+store_op1 (op, loc, arg)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg);
+}
+
+
+/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+store_op2 (op, loc, arg1, arg2)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+{
+  *loc = (unsigned char) op;
+  STORE_NUMBER (loc + 1, arg1);
+  STORE_NUMBER (loc + 3, arg2);
+}
+
+
+/* Copy the bytes from LOC to END to open up three bytes of space at LOC
+   for OP followed by two-byte integer parameter ARG.  */
+
+static void
+insert_op1 (op, loc, arg, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg;
+    unsigned char *end;    
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 3;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+    
+  store_op1 (op, loc, arg);
+}
+
+
+/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
+
+static void
+insert_op2 (op, loc, arg1, arg2, end)
+    re_opcode_t op;
+    unsigned char *loc;
+    int arg1, arg2;
+    unsigned char *end;    
+{
+  register unsigned char *pfrom = end;
+  register unsigned char *pto = end + 5;
+
+  while (pfrom != loc)
+    *--pto = *--pfrom;
+    
+  store_op2 (op, loc, arg1, arg2);
+}
+
+
+/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
+   after an alternative or a begin-subexpression.  We assume there is at
+   least one character before the ^.  */
+
+static boolean
+at_begline_loc_p (pattern, p, syntax)
+    const char *pattern, *p;
+    reg_syntax_t syntax;
+{
+  const char *prev = p - 2;
+  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';
+  
+  return
+       /* After a subexpression?  */
+       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
+       /* After an alternative?  */
+    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
+}
+
+
+/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
+   at least one character after the $, i.e., `P < PEND'.  */
+
+static boolean
+at_endline_loc_p (p, pend, syntax)
+    const char *p, *pend;
+    int syntax;
+{
+  const char *next = p;
+  boolean next_backslash = *next == '\\';
+  const char *next_next = p + 1 < pend ? p + 1 : NULL;
+  
+  return
+       /* Before a subexpression?  */
+       (syntax & RE_NO_BK_PARENS ? *next == ')'
+        : next_backslash && next_next && *next_next == ')')
+       /* Before an alternative?  */
+    || (syntax & RE_NO_BK_VBAR ? *next == '|'
+        : next_backslash && next_next && *next_next == '|');
+}
+
+
+/* Returns true if REGNUM is in one of COMPILE_STACK's elements and 
+   false if it's not.  */
+
+static boolean
+group_in_compile_stack (compile_stack, regnum)
+    compile_stack_type compile_stack;
+    regnum_t regnum;
+{
+  int this_element;
+
+  for (this_element = compile_stack.avail - 1;  
+       this_element >= 0; 
+       this_element--)
+    if (compile_stack.stack[this_element].regnum == regnum)
+      return true;
+
+  return false;
+}
+
+
+/* Read the ending character of a range (in a bracket expression) from the
+   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
+   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
+   Then we set the translation of all bits between the starting and
+   ending characters (inclusive) in the compiled pattern B.
+   
+   Return an error code.
+   
+   We use these short variable names so we can use the same macros as
+   `regex_compile' itself.  */
+
+static reg_errcode_t
+compile_range (p_ptr, pend, translate, syntax, b)
+    const char **p_ptr, *pend;
+    char *translate;
+    reg_syntax_t syntax;
+    unsigned char *b;
+{
+  unsigned this_char;
+
+  const char *p = *p_ptr;
+  int range_start, range_end;
+  
+  if (p == pend)
+    return REG_ERANGE;
+
+  /* Even though the pattern is a signed `char *', we need to fetch
+     with unsigned char *'s; if the high bit of the pattern character
+     is set, the range endpoints will be negative if we fetch using a
+     signed char *.
+
+     We also want to fetch the endpoints without translating them; the 
+     appropriate translation is done in the bit-setting loop below.  */
+  range_start = ((unsigned char *) p)[-2];
+  range_end   = ((unsigned char *) p)[0];
+
+  /* Have to increment the pointer into the pattern string, so the
+     caller isn't still at the ending character.  */
+  (*p_ptr)++;
+
+  /* If the start is after the end, the range is empty.  */
+  if (range_start > range_end)
+    return syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;
+
+  /* Here we see why `this_char' has to be larger than an `unsigned
+     char' -- the range is inclusive, so if `range_end' == 0xff
+     (assuming 8-bit characters), we would otherwise go into an infinite
+     loop, since all characters <= 0xff.  */
+  for (this_char = range_start; this_char <= range_end; this_char++)
+    {
+      SET_LIST_BIT (TRANSLATE (this_char));
+    }
+  
+  return REG_NOERROR;
+}
+\f
+/* Failure stack declarations and macros; both re_compile_fastmap and
+   re_match_2 use a failure stack.  These have to be macros because of
+   REGEX_ALLOCATE.  */
+   
+
+/* Number of failure points for which to initially allocate space
+   when matching.  If this number is exceeded, we allocate more
+   space, so it is not a hard limit.  */
+#ifndef INIT_FAILURE_ALLOC
+#define INIT_FAILURE_ALLOC 5
+#endif
+
+/* Roughly the maximum number of failure points on the stack.  Would be
+   exactly that if always used MAX_FAILURE_SPACE each time we failed.
+   This is a variable only so users of regex can assign to it; we never
+   change it ourselves.  */
+int re_max_failures = 2000;
+
+typedef const unsigned char *fail_stack_elt_t;
+
+typedef struct
+{
+  fail_stack_elt_t *stack;
+  unsigned size;
+  unsigned avail;                      /* Offset of next open position.  */
+} fail_stack_type;
+
+#define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
+#define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
+#define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
+#define FAIL_STACK_TOP()       (fail_stack.stack[fail_stack.avail])
+
+
+/* Initialize `fail_stack'.  Do `return -2' if the alloc fails.  */
+
+#define INIT_FAIL_STACK()                                              \
+  do {                                                                 \
+    fail_stack.stack = (fail_stack_elt_t *)                            \
+      REGEX_ALLOCATE (INIT_FAILURE_ALLOC * sizeof (fail_stack_elt_t)); \
+                                                                       \
+    if (fail_stack.stack == NULL)                                      \
+      return -2;                                                       \
+                                                                       \
+    fail_stack.size = INIT_FAILURE_ALLOC;                              \
+    fail_stack.avail = 0;                                              \
+  } while (0)
+
+
+/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.
+
+   Return 1 if succeeds, and 0 if either ran out of memory
+   allocating space for it or it was already too large.  
+   
+   REGEX_REALLOCATE requires `destination' be declared.   */
+
+#define DOUBLE_FAIL_STACK(fail_stack)                                  \
+  ((fail_stack).size > re_max_failures * MAX_FAILURE_ITEMS             \
+   ? 0                                                                 \
+   : ((fail_stack).stack = (fail_stack_elt_t *)                                \
+        REGEX_REALLOCATE ((fail_stack).stack,                          \
+          (fail_stack).size * sizeof (fail_stack_elt_t),               \
+          ((fail_stack).size << 1) * sizeof (fail_stack_elt_t)),       \
+                                                                       \
+      (fail_stack).stack == NULL                                       \
+      ? 0                                                              \
+      : ((fail_stack).size <<= 1,                                      \
+         1)))
+
+
+/* Push PATTERN_OP on FAIL_STACK. 
+
+   Return 1 if was able to do so and 0 if ran out of memory allocating
+   space to do so.  */
+#define PUSH_PATTERN_OP(pattern_op, fail_stack)                                \
+  ((FAIL_STACK_FULL ()                                                 \
+    && !DOUBLE_FAIL_STACK (fail_stack))                                        \
+    ? 0                                                                        \
+    : ((fail_stack).stack[(fail_stack).avail++] = pattern_op,          \
+       1))
+
+/* This pushes an item onto the failure stack.  Must be a four-byte
+   value.  Assumes the variable `fail_stack'.  Probably should only
+   be called from within `PUSH_FAILURE_POINT'.  */
+#define PUSH_FAILURE_ITEM(item)                                                \
+  fail_stack.stack[fail_stack.avail++] = (fail_stack_elt_t) item
+
+/* The complement operation.  Assumes `fail_stack' is nonempty.  */
+#define POP_FAILURE_ITEM() fail_stack.stack[--fail_stack.avail]
+
+/* Used to omit pushing failure point id's when we're not debugging.  */
+#ifdef DEBUG
+#define DEBUG_PUSH PUSH_FAILURE_ITEM
+#define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_ITEM ()
+#else
+#define DEBUG_PUSH(item)
+#define DEBUG_POP(item_addr)
+#endif
+
+
+/* Push the information about the state we will need
+   if we ever fail back to it.  
+   
+   Requires variables fail_stack, regstart, regend, reg_info, and
+   num_regs be declared.  DOUBLE_FAIL_STACK requires `destination' be
+   declared.
+   
+   Does `return FAILURE_CODE' if runs out of memory.  */
+
+#define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)  \
+  do {                                                                 \
+    char *destination;                                                 \
+    /* Must be int, so when we don't save any registers, the arithmetic        \
+       of 0 + -1 isn't done as unsigned.  */                           \
+    int this_reg;                                                      \
+                                                                       \
+    DEBUG_STATEMENT (failure_id++);                                    \
+    DEBUG_STATEMENT (nfailure_points_pushed++);                                \
+    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);          \
+    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
+    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
+                                                                       \
+    DEBUG_PRINT2 ("  slots needed: %d\n", NUM_FAILURE_ITEMS);          \
+    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);      \
+                                                                       \
+    /* Ensure we have enough space allocated for what we will push.  */        \
+    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)                  \
+      {                                                                        \
+        if (!DOUBLE_FAIL_STACK (fail_stack))                   \
+          return failure_code;                                         \
+                                                                       \
+        DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",             \
+                      (fail_stack).size);                              \
+        DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
+      }                                                                        \
+                                                                       \
+    /* Push the info, starting with the registers.  */                 \
+    DEBUG_PRINT1 ("\n");                                               \
+                                                                       \
+    for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
+         this_reg++)                                                   \
+      {                                                                        \
+       DEBUG_PRINT2 ("  Pushing reg: %d\n", this_reg);                 \
+        DEBUG_STATEMENT (num_regs_pushed++);                           \
+                                                                       \
+       DEBUG_PRINT2 ("    start: 0x%x\n", regstart[this_reg]);         \
+        PUSH_FAILURE_ITEM (regstart[this_reg]);                                \
+                                                                        \
+       DEBUG_PRINT2 ("    end: 0x%x\n", regend[this_reg]);             \
+        PUSH_FAILURE_ITEM (regend[this_reg]);                          \
+                                                                       \
+       DEBUG_PRINT2 ("    info: 0x%x\n      ", reg_info[this_reg]);    \
+        DEBUG_PRINT2 (" match_null=%d",                                        \
+                      REG_MATCH_NULL_STRING_P (reg_info[this_reg]));   \
+        DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));   \
+        DEBUG_PRINT2 (" matched_something=%d",                         \
+                      MATCHED_SOMETHING (reg_info[this_reg]));         \
+        DEBUG_PRINT2 (" ever_matched=%d",                              \
+                      EVER_MATCHED_SOMETHING (reg_info[this_reg]));    \
+       DEBUG_PRINT1 ("\n");                                            \
+        PUSH_FAILURE_ITEM (reg_info[this_reg].word);                   \
+      }                                                                        \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing  low active reg: %d\n", lowest_active_reg);\
+    PUSH_FAILURE_ITEM (lowest_active_reg);                             \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing high active reg: %d\n", highest_active_reg);\
+    PUSH_FAILURE_ITEM (highest_active_reg);                            \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing pattern 0x%x: ", pattern_place);          \
+    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);          \
+    PUSH_FAILURE_ITEM (pattern_place);                                 \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing string 0x%x: `", string_place);           \
+    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
+                                size2);                                \
+    DEBUG_PRINT1 ("'\n");                                              \
+    PUSH_FAILURE_ITEM (string_place);                                  \
+                                                                       \
+    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);           \
+    DEBUG_PUSH (failure_id);                                           \
+  } while (0)
+
+/* This is the number of items that are pushed and popped on the stack
+   for each register.  */
+#define NUM_REG_ITEMS  3
+
+/* Individual items aside from the registers.  */
+#ifdef DEBUG
+#define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
+#else
+#define NUM_NONREG_ITEMS 4
+#endif
+
+/* We push at most this many items on the stack.  */
+#define MAX_FAILURE_ITEMS ((num_regs - 1) * NUM_REG_ITEMS + NUM_NONREG_ITEMS)
+
+/* We actually push this many items.  */
+#define NUM_FAILURE_ITEMS                                              \
+  ((highest_active_reg - lowest_active_reg + 1) * NUM_REG_ITEMS        \
+    + NUM_NONREG_ITEMS)
+
+/* How many items can still be added to the stack without overflowing it.  */
+#define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
+
+
+/* Pops what PUSH_FAIL_STACK pushes.
+
+   We restore into the parameters, all of which should be lvalues:
+     STR -- the saved data position.
+     PAT -- the saved pattern position.
+     LOW_REG, HIGH_REG -- the highest and lowest active registers.
+     REGSTART, REGEND -- arrays of string positions.
+     REG_INFO -- array of information about each subexpression.
+   
+   Also assumes the variables `fail_stack' and (if debugging), `bufp',
+   `pend', `string1', `size1', `string2', and `size2'.  */
+
+#define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
+{                                                                      \
+  DEBUG_STATEMENT (fail_stack_elt_t failure_id;)                       \
+  int this_reg;                                                                \
+  const unsigned char *string_temp;                                    \
+                                                                       \
+  assert (!FAIL_STACK_EMPTY ());                                       \
+                                                                       \
+  /* Remove failure points and point to how many regs pushed.  */      \
+  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");                               \
+  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);   \
+  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size);    \
+                                                                       \
+  assert (fail_stack.avail >= NUM_NONREG_ITEMS);                       \
+                                                                       \
+  DEBUG_POP (&failure_id);                                             \
+  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);             \
+                                                                       \
+  /* If the saved string location is NULL, it came from an             \
+     on_failure_keep_string_jump opcode, and we want to throw away the \
+     saved NULL, thus retaining our current position in the string.  */        \
+  string_temp = POP_FAILURE_ITEM ();                                   \
+  if (string_temp != NULL)                                             \
+    str = (const char *) string_temp;                                  \
+                                                                       \
+  DEBUG_PRINT2 ("  Popping string 0x%x: `", str);                      \
+  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);     \
+  DEBUG_PRINT1 ("'\n");                                                        \
+                                                                       \
+  pat = (unsigned char *) POP_FAILURE_ITEM ();                         \
+  DEBUG_PRINT2 ("  Popping pattern 0x%x: ", pat);                      \
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);                      \
+                                                                       \
+  /* Restore register info.  */                                                \
+  high_reg = (unsigned) POP_FAILURE_ITEM ();                           \
+  DEBUG_PRINT2 ("  Popping high active reg: %d\n", high_reg);          \
+                                                                       \
+  low_reg = (unsigned) POP_FAILURE_ITEM ();                            \
+  DEBUG_PRINT2 ("  Popping  low active reg: %d\n", low_reg);           \
+                                                                       \
+  for (this_reg = high_reg; this_reg >= low_reg; this_reg--)           \
+    {                                                                  \
+      DEBUG_PRINT2 ("    Popping reg: %d\n", this_reg);                        \
+                                                                       \
+      reg_info[this_reg].word = POP_FAILURE_ITEM ();                   \
+      DEBUG_PRINT2 ("      info: 0x%x\n", reg_info[this_reg]);         \
+                                                                       \
+      regend[this_reg] = (const char *) POP_FAILURE_ITEM ();           \
+      DEBUG_PRINT2 ("      end: 0x%x\n", regend[this_reg]);            \
+                                                                       \
+      regstart[this_reg] = (const char *) POP_FAILURE_ITEM ();         \
+      DEBUG_PRINT2 ("      start: 0x%x\n", regstart[this_reg]);                \
+    }                                                                  \
+                                                                       \
+  DEBUG_STATEMENT (nfailure_points_popped++);                          \
+} /* POP_FAILURE_POINT */
+\f
+/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
+   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
+   characters can start a string that matches the pattern.  This fastmap
+   is used by re_search to skip quickly over impossible starting points.
+
+   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
+   area as BUFP->fastmap.
+   
+   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
+   the pattern buffer.
+
+   Returns 0 if we succeed, -2 if an internal error.   */
+
+int
+re_compile_fastmap (bufp)
+     struct re_pattern_buffer *bufp;
+{
+  int j, k;
+  fail_stack_type fail_stack;
+#ifndef REGEX_MALLOC
+  char *destination;
+#endif
+  /* We don't push any register information onto the failure stack.  */
+  unsigned num_regs = 0;
+  
+  register char *fastmap = bufp->fastmap;
+  unsigned char *pattern = bufp->buffer;
+  unsigned long size = bufp->used;
+  const unsigned char *p = pattern;
+  register unsigned char *pend = pattern + size;
+
+  /* Assume that each path through the pattern can be null until
+     proven otherwise.  We set this false at the bottom of switch
+     statement, to which we get only if a particular path doesn't
+     match the empty string.  */
+  boolean path_can_be_null = true;
+
+  /* We aren't doing a `succeed_n' to begin with.  */
+  boolean succeed_n_p = false;
+
+  assert (fastmap != NULL && p != NULL);
+  
+  INIT_FAIL_STACK ();
+  bzero (fastmap, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
+  bufp->fastmap_accurate = 1;      /* It will be when we're done.  */
+  bufp->can_be_null = 0;
+      
+  while (p != pend || !FAIL_STACK_EMPTY ())
+    {
+      if (p == pend)
+        {
+          bufp->can_be_null |= path_can_be_null;
+          
+          /* Reset for next path.  */
+          path_can_be_null = true;
+          
+          p = fail_stack.stack[--fail_stack.avail];
+       }
+
+      /* We should never be about to go beyond the end of the pattern.  */
+      assert (p < pend);
+      
+#ifdef SWITCH_ENUM_BUG
+      switch ((int) ((re_opcode_t) *p++))
+#else
+      switch ((re_opcode_t) *p++)
+#endif
+       {
+
+        /* I guess the idea here is to simply not bother with a fastmap
+           if a backreference is used, since it's too hard to figure out
+           the fastmap for the corresponding group.  Setting
+           `can_be_null' stops `re_search_2' from using the fastmap, so
+           that is all we do.  */
+       case duplicate:
+         bufp->can_be_null = 1;
+          return 0;
+
+
+      /* Following are the cases which match a character.  These end
+         with `break'.  */
+
+       case exactn:
+          fastmap[p[1]] = 1;
+         break;
+
+
+        case charset:
+          for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+           if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
+              fastmap[j] = 1;
+         break;
+
+
+       case charset_not:
+         /* Chars beyond end of map must be allowed.  */
+         for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
+            fastmap[j] = 1;
+
+         for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
+           if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
+              fastmap[j] = 1;
+          break;
+
+
+       case wordchar:
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) == Sword)
+             fastmap[j] = 1;
+         break;
+
+
+       case notwordchar:
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) != Sword)
+             fastmap[j] = 1;
+         break;
+
+
+        case anychar:
+          /* `.' matches anything ...  */
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+            fastmap[j] = 1;
+
+          /* ... except perhaps newline.  */
+          if (!(bufp->syntax & RE_DOT_NEWLINE))
+            fastmap['\n'] = 0;
+
+          /* Return if we have already set `can_be_null'; if we have,
+             then the fastmap is irrelevant.  Something's wrong here.  */
+         else if (bufp->can_be_null)
+           return 0;
+
+          /* Otherwise, have to check alternative paths.  */
+         break;
+
+
+#ifdef emacs
+        case syntaxspec:
+         k = *p++;
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) == (enum syntaxcode) k)
+             fastmap[j] = 1;
+         break;
+
+
+       case notsyntaxspec:
+         k = *p++;
+         for (j = 0; j < (1 << BYTEWIDTH); j++)
+           if (SYNTAX (j) != (enum syntaxcode) k)
+             fastmap[j] = 1;
+         break;
+
+
+      /* All cases after this match the empty string.  These end with
+         `continue'.  */
+
+
+       case before_dot:
+       case at_dot:
+       case after_dot:
+          continue;
+#endif /* not emacs */
+
+
+        case no_op:
+        case begline:
+        case endline:
+       case begbuf:
+       case endbuf:
+       case wordbound:
+       case notwordbound:
+       case wordbeg:
+       case wordend:
+        case push_dummy_failure:
+          continue;
+
+
+       case jump_n:
+        case pop_failure_jump:
+       case maybe_pop_jump:
+       case jump:
+        case jump_past_alt:
+       case dummy_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+         p += j;       
+         if (j > 0)
+           continue;
+            
+          /* Jump backward implies we just went through the body of a
+             loop and matched nothing.  Opcode jumped to should be
+             `on_failure_jump' or `succeed_n'.  Just treat it like an
+             ordinary jump.  For a * loop, it has pushed its failure
+             point already; if so, discard that as redundant.  */
+          if ((re_opcode_t) *p != on_failure_jump
+             && (re_opcode_t) *p != succeed_n)
+           continue;
+
+          p++;
+          EXTRACT_NUMBER_AND_INCR (j, p);
+          p += j;              
+         
+          /* If what's on the stack is where we are now, pop it.  */
+          if (!FAIL_STACK_EMPTY () 
+             && fail_stack.stack[fail_stack.avail - 1] == p)
+            fail_stack.avail--;
+
+          continue;
+
+
+        case on_failure_jump:
+        case on_failure_keep_string_jump:
+       handle_on_failure_jump:
+          EXTRACT_NUMBER_AND_INCR (j, p);
+
+          /* For some patterns, e.g., `(a?)?', `p+j' here points to the
+             end of the pattern.  We don't want to push such a point,
+             since when we restore it above, entering the switch will
+             increment `p' past the end of the pattern.  We don't need
+             to push such a point since we obviously won't find any more
+             fastmap entries beyond `pend'.  Such a pattern can match
+             the null string, though.  */
+          if (p + j < pend)
+            {
+              if (!PUSH_PATTERN_OP (p + j, fail_stack))
+                return -2;
+            }
+          else
+            bufp->can_be_null = 1;
+
+          if (succeed_n_p)
+            {
+              EXTRACT_NUMBER_AND_INCR (k, p);  /* Skip the n.  */
+              succeed_n_p = false;
+           }
+
+          continue;
+
+
+       case succeed_n:
+          /* Get to the number of times to succeed.  */
+          p += 2;              
+
+          /* Increment p past the n for when k != 0.  */
+          EXTRACT_NUMBER_AND_INCR (k, p);
+          if (k == 0)
+           {
+              p -= 4;
+             succeed_n_p = true;  /* Spaghetti code alert.  */
+              goto handle_on_failure_jump;
+            }
+          continue;
+
+
+       case set_number_at:
+          p += 4;
+          continue;
+
+
+       case start_memory:
+        case stop_memory:
+         p += 2;
+         continue;
+
+
+       default:
+          abort (); /* We have listed all the cases.  */
+        } /* switch *p++ */
+
+      /* Getting here means we have found the possible starting
+         characters for one path of the pattern -- and that the empty
+         string does not match.  We need not follow this path further.
+         Instead, look at the next alternative (remembered on the
+         stack), or quit if no more.  The test at the top of the loop
+         does these things.  */
+      path_can_be_null = false;
+      p = pend;
+    } /* while p */
+
+  /* Set `can_be_null' for the last path (also the first path, if the
+     pattern is empty).  */
+  bufp->can_be_null |= path_can_be_null;
+  return 0;
+} /* re_compile_fastmap */
+\f
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+    struct re_pattern_buffer *bufp;
+    struct re_registers *regs;
+    unsigned num_regs;
+    regoff_t *starts, *ends;
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t) 0;
+    }
+}
+\f
+/* Searching routines.  */
+
+/* Like re_search_2, below, but only one string is specified, and
+   doesn't let you say where to stop matching. */
+
+int
+re_search (bufp, string, size, startpos, range, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, startpos, range;
+     struct re_registers *regs;
+{
+  return re_search_2 (bufp, NULL, 0, string, size, startpos, range, 
+                     regs, size);
+}
+
+
+/* Using the compiled pattern in BUFP->buffer, first tries to match the
+   virtual concatenation of STRING1 and STRING2, starting first at index
+   STARTPOS, then at STARTPOS + 1, and so on.
+   
+   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.
+   
+   RANGE is how far to scan while trying to match.  RANGE = 0 means try
+   only at STARTPOS; in general, the last start tried is STARTPOS +
+   RANGE.
+   
+   In REGS, return the indices of the virtual concatenation of STRING1
+   and STRING2 that matched the entire BUFP->buffer and its contained
+   subexpressions.
+   
+   Do not consider matching one past the index STOP in the virtual
+   concatenation of STRING1 and STRING2.
+
+   We return either the position in the strings at which the match was
+   found, -1 if no match, or -2 if error (such as failure
+   stack overflow).  */
+
+int
+re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int startpos;
+     int range;
+     struct re_registers *regs;
+     int stop;
+{
+  int val;
+  register char *fastmap = bufp->fastmap;
+  register char *translate = bufp->translate;
+  int total_size = size1 + size2;
+  int endpos = startpos + range;
+
+  /* Check for out-of-range STARTPOS.  */
+  if (startpos < 0 || startpos > total_size)
+    return -1;
+    
+  /* Fix up RANGE if it might eventually take us outside
+     the virtual concatenation of STRING1 and STRING2.  */
+  if (endpos < -1)
+    range = -1 - startpos;
+  else if (endpos > total_size)
+    range = total_size - startpos;
+
+  /* If the search isn't to be a backwards one, don't waste time in a
+     search for a pattern that must be anchored.  */
+  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == begbuf && range > 0)
+    {
+      if (startpos > 0)
+       return -1;
+      else
+       range = 1;
+    }
+
+  /* Update the fastmap now if not correct already.  */
+  if (fastmap && !bufp->fastmap_accurate)
+    if (re_compile_fastmap (bufp) == -2)
+      return -2;
+  
+  /* Loop through the string, looking for a place to start matching.  */
+  for (;;)
+    { 
+      /* If a fastmap is supplied, skip quickly over characters that
+         cannot be the start of a match.  If the pattern can match the
+         null string, however, we don't need to skip characters; we want
+         the first null string.  */
+      if (fastmap && startpos < total_size && !bufp->can_be_null)
+       {
+         if (range > 0)        /* Searching forwards.  */
+           {
+             register const char *d;
+             register int lim = 0;
+             int irange = range;
+
+              if (startpos < size1 && startpos + range >= size1)
+                lim = range - (size1 - startpos);
+
+             d = (startpos >= size1 ? string2 - size1 : string1) + startpos;
+   
+              /* Written out as an if-else to avoid testing `translate'
+                 inside the loop.  */
+             if (translate)
+                while (range > lim
+                       && !fastmap[(unsigned char)
+                                  translate[(unsigned char) *d++]])
+                  range--;
+             else
+                while (range > lim && !fastmap[(unsigned char) *d++])
+                  range--;
+
+             startpos += irange - range;
+           }
+         else                          /* Searching backwards.  */
+           {
+             register char c = (size1 == 0 || startpos >= size1
+                                 ? string2[startpos - size1] 
+                                 : string1[startpos]);
+
+             if (!fastmap[(unsigned char) TRANSLATE (c)])
+               goto advance;
+           }
+       }
+
+      /* If can't match the null string, and that's all we have left, fail.  */
+      if (range >= 0 && startpos == total_size && fastmap
+          && !bufp->can_be_null)
+       return -1;
+
+      val = re_match_2 (bufp, string1, size1, string2, size2,
+                       startpos, regs, stop);
+      if (val >= 0)
+       return startpos;
+        
+      if (val == -2)
+       return -2;
+
+    advance:
+      if (!range) 
+        break;
+      else if (range > 0) 
+        {
+          range--; 
+          startpos++;
+        }
+      else
+        {
+          range++; 
+          startpos--;
+        }
+    }
+  return -1;
+} /* re_search_2 */
+\f
+/* Declarations and macros for re_match_2.  */
+
+static int bcmp_translate ();
+static boolean alt_match_null_string_p (),
+               common_op_match_null_string_p (),
+               group_match_null_string_p ();
+
+/* Structure for per-register (a.k.a. per-group) information.
+   This must not be longer than one word, because we push this value
+   onto the failure stack.  Other register information, such as the
+   starting and ending positions (which are addresses), and the list of
+   inner groups (which is a bits list) are maintained in separate
+   variables.  
+   
+   We are making a (strictly speaking) nonportable assumption here: that
+   the compiler will pack our bit fields into something that fits into
+   the type of `word', i.e., is something that fits into one item on the
+   failure stack.  */
+typedef union
+{
+  fail_stack_elt_t word;
+  struct
+  {
+      /* This field is one if this group can match the empty string,
+         zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
+#define MATCH_NULL_UNSET_VALUE 3
+    unsigned match_null_string_p : 2;
+    unsigned is_active : 1;
+    unsigned matched_something : 1;
+    unsigned ever_matched_something : 1;
+  } bits;
+} register_info_type;
+
+#define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
+#define IS_ACTIVE(R)  ((R).bits.is_active)
+#define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
+#define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)
+
+
+/* Call this when have matched a real character; it sets `matched' flags
+   for the subexpressions which we are currently inside.  Also records
+   that those subexprs have matched.  */
+#define SET_REGS_MATCHED()                                             \
+  do                                                                   \
+    {                                                                  \
+      unsigned r;                                                      \
+      for (r = lowest_active_reg; r <= highest_active_reg; r++)                \
+        {                                                              \
+          MATCHED_SOMETHING (reg_info[r])                              \
+            = EVER_MATCHED_SOMETHING (reg_info[r])                     \
+            = 1;                                                       \
+        }                                                              \
+    }                                                                  \
+  while (0)
+
+
+/* This converts PTR, a pointer into one of the search strings `string1'
+   and `string2' into an offset from the beginning of that string.  */
+#define POINTER_TO_OFFSET(ptr)                                         \
+  (FIRST_STRING_P (ptr) ? (ptr) - string1 : (ptr) - string2 + size1)
+
+/* Registers are set to a sentinel when they haven't yet matched.  */
+#define REG_UNSET_VALUE ((char *) -1)
+#define REG_UNSET(e) ((e) == REG_UNSET_VALUE)
+
+
+/* Macros for dealing with the split strings in re_match_2.  */
+
+#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)
+
+/* Call before fetching a character with *d.  This switches over to
+   string2 if necessary.  */
+#define PREFETCH()                                                     \
+  while (d == dend)                                                    \
+    {                                                                  \
+      /* End of string2 => fail.  */                                   \
+      if (dend == end_match_2)                                                 \
+        goto fail;                                                     \
+      /* End of string1 => advance to string2.  */                     \
+      d = string2;                                                     \
+      dend = end_match_2;                                              \
+    }
+
+
+/* Test if at very beginning or at very end of the virtual concatenation
+   of `string1' and `string2'.  If only one string, it's `string2'.  */
+#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
+#define AT_STRINGS_END(d) ((d) == end2)        
+
+
+/* Test if D points to a character which is word-constituent.  We have
+   two special cases to check for: if past the end of string1, look at
+   the first character in string2; and if before the beginning of
+   string2, look at the last character in string1.  */
+#define WORDCHAR_P(d)                                                  \
+  (SYNTAX ((d) == end1 ? *string2                                      \
+           : (d) == string2 - 1 ? *(end1 - 1) : *(d))                  \
+   == Sword)
+
+/* Test if the character before D and the one at D differ with respect
+   to being word-constituent.  */
+#define AT_WORD_BOUNDARY(d)                                            \
+  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)                            \
+   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
+
+
+/* Free everything we malloc.  */
+#ifdef REGEX_MALLOC
+#define FREE_VAR(var) if (var) free (var); var = NULL
+#define FREE_VARIABLES()                                               \
+  do {                                                                 \
+    FREE_VAR (fail_stack.stack);                                       \
+    FREE_VAR (regstart);                                               \
+    FREE_VAR (regend);                                                 \
+    FREE_VAR (old_regstart);                                           \
+    FREE_VAR (old_regend);                                             \
+    FREE_VAR (best_regstart);                                          \
+    FREE_VAR (best_regend);                                            \
+    FREE_VAR (reg_info);                                               \
+    FREE_VAR (reg_dummy);                                              \
+    FREE_VAR (reg_info_dummy);                                         \
+  } while (0)
+#else /* not REGEX_MALLOC */
+/* Some MIPS systems (at least) want this to free alloca'd storage.  */
+#define FREE_VARIABLES() alloca (0)
+#endif /* not REGEX_MALLOC */
+
+
+/* These values must meet several constraints.  They must not be valid
+   register values; since we have a limit of 255 registers (because
+   we use only one byte in the pattern for the register number), we can
+   use numbers larger than 255.  They must differ by 1, because of
+   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
+   be larger than the value for the highest register, so we do not try
+   to actually save any registers when none are active.  */
+#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
+#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)
+\f
+/* Matching routines.  */
+
+#ifndef emacs   /* Emacs never uses this.  */
+/* re_match is like re_match_2 except it takes only a single string.  */
+
+int
+re_match (bufp, string, size, pos, regs)
+     struct re_pattern_buffer *bufp;
+     const char *string;
+     int size, pos;
+     struct re_registers *regs;
+ {
+  return re_match_2 (bufp, NULL, 0, string, size, pos, regs, size); 
+}
+#endif /* not emacs */
+
+
+/* re_match_2 matches the compiled pattern in BUFP against the
+   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+   and SIZE2, respectively).  We start matching at POS, and stop
+   matching at STOP.
+   
+   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
+   store offsets for the substring each group matched in REGS.  See the
+   documentation for exactly how many groups we fill.
+
+   We return -1 if no match, -2 if an internal error (such as the
+   failure stack overflowing).  Otherwise, we return the length of the
+   matched substring.  */
+
+int
+re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+     struct re_pattern_buffer *bufp;
+     const char *string1, *string2;
+     int size1, size2;
+     int pos;
+     struct re_registers *regs;
+     int stop;
+{
+  /* General temporaries.  */
+  int mcnt;
+  unsigned char *p1;
+
+  /* Just past the end of the corresponding string.  */
+  const char *end1, *end2;
+
+  /* Pointers into string1 and string2, just past the last characters in
+     each to consider matching.  */
+  const char *end_match_1, *end_match_2;
+
+  /* Where we are in the data, and the end of the current string.  */
+  const char *d, *dend;
+  
+  /* Where we are in the pattern, and the end of the pattern.  */
+  unsigned char *p = bufp->buffer;
+  register unsigned char *pend = p + bufp->used;
+
+  /* We use this to map every character in the string.  */
+  char *translate = bufp->translate;
+
+  /* Failure point stack.  Each place that can handle a failure further
+     down the line pushes a failure point on this stack.  It consists of
+     restart, regend, and reg_info for all registers corresponding to
+     the subexpressions we're currently inside, plus the number of such
+     registers, and, finally, two char *'s.  The first char * is where
+     to resume scanning the pattern; the second one is where to resume
+     scanning the strings.  If the latter is zero, the failure point is
+     a ``dummy''; if a failure happens and the failure point is a dummy,
+     it gets discarded and the next next one is tried.  */
+  fail_stack_type fail_stack;
+#ifdef DEBUG
+  static unsigned failure_id = 0;
+  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
+#endif
+
+  /* We fill all the registers internally, independent of what we
+     return, for use in backreferences.  The number here includes
+     an element for register zero.  */
+  unsigned num_regs = bufp->re_nsub + 1;
+  
+  /* The currently active registers.  */
+  unsigned lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+  unsigned highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+
+  /* Information on the contents of registers. These are pointers into
+     the input strings; they record just what was matched (on this
+     attempt) by a subexpression part of the pattern, that is, the
+     regnum-th regstart pointer points to where in the pattern we began
+     matching and the regnum-th regend points to right after where we
+     stopped matching the regnum-th subexpression.  (The zeroth register
+     keeps track of what the whole pattern matches.)  */
+  const char **regstart = NULL, **regend = NULL;
+
+  /* If a group that's operated upon by a repetition operator fails to
+     match anything, then the register for its start will need to be
+     restored because it will have been set to wherever in the string we
+     are when we last see its open-group operator.  Similarly for a
+     register's end.  */
+  const char **old_regstart = NULL, **old_regend = NULL;
+
+  /* The is_active field of reg_info helps us keep track of which (possibly
+     nested) subexpressions we are currently in. The matched_something
+     field of reg_info[reg_num] helps us tell whether or not we have
+     matched any of the pattern so far this time through the reg_num-th
+     subexpression.  These two fields get reset each time through any
+     loop their register is in.  */
+  register_info_type *reg_info = NULL;
+
+  /* The following record the register info as found in the above
+     variables when we find a match better than any we've seen before. 
+     This happens as we backtrack through the failure points, which in
+     turn happens only if we have not yet matched the entire string. */
+  unsigned best_regs_set = false;
+  const char **best_regstart = NULL, **best_regend = NULL;
+  /* Logically, this is `best_regend[0]'.  But we don't want to have to
+     allocate space for that if we're not allocating space for anything
+     else (see below).  Also, we never need info about register 0 for
+     any of the other register vectors, and it seems rather a kludge to
+     treat `best_regend' differently than the rest.  So we keep track of
+     the end of the best match so far in a separate variable.  We
+     initialize this to NULL so that when we backtrack the first time
+     and need to test it, it's not garbage.  */
+  const char *match_end = NULL;
+
+  /* Used when we pop values we don't care about.  */
+  const char **reg_dummy = NULL;
+  register_info_type *reg_info_dummy = NULL;
+
+#ifdef DEBUG
+  /* Counts the total number of registers pushed.  */
+  unsigned num_regs_pushed = 0;        
+#endif
+
+  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");
+  
+  INIT_FAIL_STACK ();
+  
+  /* Do not bother to initialize all the register variables if there are
+     no groups in the pattern, as it takes a fair amount of time.  If
+     there are groups, we include space for register 0 (the whole
+     pattern), even though we never use it, since it simplifies the
+     array indexing.  We should fix this.  */
+  if (bufp->re_nsub)
+    {
+      regstart = REGEX_TALLOC (num_regs, const char *);
+      regend = REGEX_TALLOC (num_regs, const char *);
+      old_regstart = REGEX_TALLOC (num_regs, const char *);
+      old_regend = REGEX_TALLOC (num_regs, const char *);
+      best_regstart = REGEX_TALLOC (num_regs, const char *);
+      best_regend = REGEX_TALLOC (num_regs, const char *);
+      reg_info = REGEX_TALLOC (num_regs, register_info_type);
+      reg_dummy = REGEX_TALLOC (num_regs, const char *);
+      reg_info_dummy = REGEX_TALLOC (num_regs, register_info_type);
+
+      if (!(regstart && regend && old_regstart && old_regend && reg_info 
+            && best_regstart && best_regend && reg_dummy && reg_info_dummy)) 
+        {
+          FREE_VARIABLES ();
+          return -2;
+        }
+    }
+#ifdef REGEX_MALLOC
+  else
+    {
+      /* We must initialize all our variables to NULL, so that
+         `FREE_VARIABLES' doesn't try to free them.  */
+      regstart = regend = old_regstart = old_regend = best_regstart
+        = best_regend = reg_dummy = NULL;
+      reg_info = reg_info_dummy = (register_info_type *) NULL;
+    }
+#endif /* REGEX_MALLOC */
+
+  /* The starting position is bogus.  */
+  if (pos < 0 || pos > size1 + size2)
+    {
+      FREE_VARIABLES ();
+      return -1;
+    }
+    
+  /* Initialize subexpression text positions to -1 to mark ones that no
+     start_memory/stop_memory has been seen for. Also initialize the
+     register information struct.  */
+  for (mcnt = 1; mcnt < num_regs; mcnt++)
+    {
+      regstart[mcnt] = regend[mcnt] 
+        = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;
+        
+      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
+      IS_ACTIVE (reg_info[mcnt]) = 0;
+      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
+    }
+  
+  /* We move `string1' into `string2' if the latter's empty -- but not if
+     `string1' is null.  */
+  if (size2 == 0 && string1 != NULL)
+    {
+      string2 = string1;
+      size2 = size1;
+      string1 = 0;
+      size1 = 0;
+    }
+  end1 = string1 + size1;
+  end2 = string2 + size2;
+
+  /* Compute where to stop matching, within the two strings.  */
+  if (stop <= size1)
+    {
+      end_match_1 = string1 + stop;
+      end_match_2 = string2;
+    }
+  else
+    {
+      end_match_1 = end1;
+      end_match_2 = string2 + stop - size1;
+    }
+
+  /* `p' scans through the pattern as `d' scans through the data. 
+     `dend' is the end of the input string that `d' points within.  `d'
+     is advanced into the following input string whenever necessary, but
+     this happens before fetching; therefore, at the beginning of the
+     loop, `d' can be pointing at the end of a string, but it cannot
+     equal `string2'.  */
+  if (size1 > 0 && pos <= size1)
+    {
+      d = string1 + pos;
+      dend = end_match_1;
+    }
+  else
+    {
+      d = string2 + pos - size1;
+      dend = end_match_2;
+    }
+
+  DEBUG_PRINT1 ("The compiled pattern is: ");
+  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
+  DEBUG_PRINT1 ("The string to match is: `");
+  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
+  DEBUG_PRINT1 ("'\n");
+  
+  /* This loops over pattern commands.  It exits by returning from the
+     function if the match is complete, or it drops through if the match
+     fails at this starting point in the input data.  */
+  for (;;)
+    {
+      DEBUG_PRINT2 ("\n0x%x: ", p);
+
+      if (p == pend)
+       { /* End of pattern means we might have succeeded.  */
+          DEBUG_PRINT1 ("end of pattern ... ");
+          
+         /* If we haven't matched the entire string, and we want the
+             longest match, try backtracking.  */
+          if (d != end_match_2)
+           {
+              DEBUG_PRINT1 ("backtracking.\n");
+              
+              if (!FAIL_STACK_EMPTY ())
+                { /* More failure points to try.  */
+                  boolean same_str_p = (FIRST_STRING_P (match_end) 
+                                       == MATCHING_IN_FIRST_STRING);
+
+                  /* If exceeds best match so far, save it.  */
+                  if (!best_regs_set
+                      || (same_str_p && d > match_end)
+                      || (!same_str_p && !MATCHING_IN_FIRST_STRING))
+                    {
+                      best_regs_set = true;
+                      match_end = d;
+                      
+                      DEBUG_PRINT1 ("\nSAVING match as best so far.\n");
+                      
+                      for (mcnt = 1; mcnt < num_regs; mcnt++)
+                        {
+                          best_regstart[mcnt] = regstart[mcnt];
+                          best_regend[mcnt] = regend[mcnt];
+                        }
+                    }
+                  goto fail;          
+                }
+
+              /* If no failure points, don't restore garbage.  */
+              else if (best_regs_set)   
+                {
+               restore_best_regs:
+                  /* Restore best match.  It may happen that `dend ==
+                     end_match_1' while the restored d is in string2.
+                     For example, the pattern `x.*y.*z' against the
+                     strings `x-' and `y-z-', if the two strings are
+                     not consecutive in memory.  */
+                  DEBUG_PRINT1 ("Restoring best registers.\n");
+                  
+                  d = match_end;
+                  dend = ((d >= string1 && d <= end1)
+                          ? end_match_1 : end_match_2);
+
+                 for (mcnt = 1; mcnt < num_regs; mcnt++)
+                   {
+                     regstart[mcnt] = best_regstart[mcnt];
+                     regend[mcnt] = best_regend[mcnt];
+                   }
+                }
+            } /* d != end_match_2 */
+
+          DEBUG_PRINT1 ("Accepting match.\n");
+
+          /* If caller wants register contents data back, do it.  */
+          if (regs && !bufp->no_sub)
+           {
+              /* Have the register data arrays been allocated?  */
+              if (bufp->regs_allocated == REGS_UNALLOCATED)
+                { /* No.  So allocate them with malloc.  We need one
+                     extra element beyond `num_regs' for the `-1' marker
+                     GNU code uses.  */
+                  regs->num_regs = MAX (RE_NREGS, num_regs + 1);
+                  regs->start = TALLOC (regs->num_regs, regoff_t);
+                  regs->end = TALLOC (regs->num_regs, regoff_t);
+                  if (regs->start == NULL || regs->end == NULL)
+                    return -2;
+                  bufp->regs_allocated = REGS_REALLOCATE;
+                }
+              else if (bufp->regs_allocated == REGS_REALLOCATE)
+                { /* Yes.  If we need more elements than were already
+                     allocated, reallocate them.  If we need fewer, just
+                     leave it alone.  */
+                  if (regs->num_regs < num_regs + 1)
+                    {
+                      regs->num_regs = num_regs + 1;
+                      RETALLOC (regs->start, regs->num_regs, regoff_t);
+                      RETALLOC (regs->end, regs->num_regs, regoff_t);
+                      if (regs->start == NULL || regs->end == NULL)
+                        return -2;
+                    }
+                }
+              else
+                assert (bufp->regs_allocated == REGS_FIXED);
+
+              /* Convert the pointer data in `regstart' and `regend' to
+                 indices.  Register zero has to be set differently,
+                 since we haven't kept track of any info for it.  */
+              if (regs->num_regs > 0)
+                {
+                  regs->start[0] = pos;
+                  regs->end[0] = (MATCHING_IN_FIRST_STRING ? d - string1
+                                 : d - string2 + size1);
+                }
+              
+              /* Go through the first `min (num_regs, regs->num_regs)'
+                 registers, since that is all we initialized.  */
+             for (mcnt = 1; mcnt < MIN (num_regs, regs->num_regs); mcnt++)
+               {
+                  if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
+                    regs->start[mcnt] = regs->end[mcnt] = -1;
+                  else
+                    {
+                     regs->start[mcnt] = POINTER_TO_OFFSET (regstart[mcnt]);
+                      regs->end[mcnt] = POINTER_TO_OFFSET (regend[mcnt]);
+                    }
+               }
+              
+              /* If the regs structure we return has more elements than
+                 were in the pattern, set the extra elements to -1.  If
+                 we (re)allocated the registers, this is the case,
+                 because we always allocate enough to have at least one
+                 -1 at the end.  */
+              for (mcnt = num_regs; mcnt < regs->num_regs; mcnt++)
+                regs->start[mcnt] = regs->end[mcnt] = -1;
+           } /* regs && !bufp->no_sub */
+
+          FREE_VARIABLES ();
+          DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
+                        nfailure_points_pushed, nfailure_points_popped,
+                        nfailure_points_pushed - nfailure_points_popped);
+          DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);
+
+          mcnt = d - pos - (MATCHING_IN_FIRST_STRING 
+                           ? string1 
+                           : string2 - size1);
+
+          DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);
+
+          return mcnt;
+        }
+
+      /* Otherwise match next pattern command.  */
+#ifdef SWITCH_ENUM_BUG
+      switch ((int) ((re_opcode_t) *p++))
+#else
+      switch ((re_opcode_t) *p++)
+#endif
+       {
+        /* Ignore these.  Used to ignore the n of succeed_n's which
+           currently have n == 0.  */
+        case no_op:
+          DEBUG_PRINT1 ("EXECUTING no_op.\n");
+          break;
+
+
+        /* Match the next n pattern characters exactly.  The following
+           byte in the pattern defines n, and the n bytes after that
+           are the characters to match.  */
+       case exactn:
+         mcnt = *p++;
+          DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);
+
+          /* This is written out as an if-else so we don't waste time
+             testing `translate' inside the loop.  */
+          if (translate)
+           {
+             do
+               {
+                 PREFETCH ();
+                 if (translate[(unsigned char) *d++] != (char) *p++)
+                    goto fail;
+               }
+             while (--mcnt);
+           }
+         else
+           {
+             do
+               {
+                 PREFETCH ();
+                 if (*d++ != (char) *p++) goto fail;
+               }
+             while (--mcnt);
+           }
+         SET_REGS_MATCHED ();
+          break;
+
+
+        /* Match any character except possibly a newline or a null.  */
+       case anychar:
+          DEBUG_PRINT1 ("EXECUTING anychar.\n");
+
+          PREFETCH ();
+
+          if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
+              || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
+           goto fail;
+
+          SET_REGS_MATCHED ();
+          DEBUG_PRINT2 ("  Matched `%d'.\n", *d);
+          d++;
+         break;
+
+
+       case charset:
+       case charset_not:
+         {
+           register unsigned char c;
+           boolean not = (re_opcode_t) *(p - 1) == charset_not;
+
+            DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
+
+           PREFETCH ();
+           c = TRANSLATE (*d); /* The character to match.  */
+
+            /* Cast to `unsigned' instead of `unsigned char' in case the
+               bit list is a full 32 bytes long.  */
+           if (c < (unsigned) (*p * BYTEWIDTH)
+               && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+             not = !not;
+
+           p += 1 + *p;
+
+           if (!not) goto fail;
+            
+           SET_REGS_MATCHED ();
+            d++;
+           break;
+         }
+
+
+        /* The beginning of a group is represented by start_memory.
+           The arguments are the register number in the next byte, and the
+           number of groups inner to this one in the next.  The text
+           matched within the group is recorded (in the internal
+           registers data structure) under the register number.  */
+        case start_memory:
+         DEBUG_PRINT3 ("EXECUTING start_memory %d (%d):\n", *p, p[1]);
+
+          /* Find out if this group can match the empty string.  */
+         p1 = p;               /* To send to group_match_null_string_p.  */
+          
+          if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
+            REG_MATCH_NULL_STRING_P (reg_info[*p]) 
+              = group_match_null_string_p (&p1, pend, reg_info);
+
+          /* Save the position in the string where we were the last time
+             we were at this open-group operator in case the group is
+             operated upon by a repetition operator, e.g., with `(a*)*b'
+             against `ab'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                             ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
+                             : regstart[*p];
+         DEBUG_PRINT2 ("  old_regstart: %d\n", 
+                        POINTER_TO_OFFSET (old_regstart[*p]));
+
+          regstart[*p] = d;
+         DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));
+
+          IS_ACTIVE (reg_info[*p]) = 1;
+          MATCHED_SOMETHING (reg_info[*p]) = 0;
+          
+          /* This is the new highest active register.  */
+          highest_active_reg = *p;
+          
+          /* If nothing was active before, this is the new lowest active
+             register.  */
+          if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+            lowest_active_reg = *p;
+
+          /* Move past the register number and inner group count.  */
+          p += 2;
+          break;
+
+
+        /* The stop_memory opcode represents the end of a group.  Its
+           arguments are the same as start_memory's: the register
+           number, and the number of inner groups.  */
+       case stop_memory:
+         DEBUG_PRINT3 ("EXECUTING stop_memory %d (%d):\n", *p, p[1]);
+             
+          /* We need to save the string position the last time we were at
+             this close-group operator in case the group is operated
+             upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
+             against `aba'; then we want to ignore where we are now in
+             the string in case this attempt to match fails.  */
+          old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
+                           ? REG_UNSET (regend[*p]) ? d : regend[*p]
+                          : regend[*p];
+         DEBUG_PRINT2 ("      old_regend: %d\n", 
+                        POINTER_TO_OFFSET (old_regend[*p]));
+
+          regend[*p] = d;
+         DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));
+
+          /* This register isn't active anymore.  */
+          IS_ACTIVE (reg_info[*p]) = 0;
+          
+          /* If this was the only register active, nothing is active
+             anymore.  */
+          if (lowest_active_reg == highest_active_reg)
+            {
+              lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+              highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+            }
+          else
+            { /* We must scan for the new highest active register, since
+                 it isn't necessarily one less than now: consider
+                 (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
+                 new highest active register is 1.  */
+              unsigned char r = *p - 1;
+              while (r > 0 && !IS_ACTIVE (reg_info[r]))
+                r--;
+              
+              /* If we end up at register zero, that means that we saved
+                 the registers as the result of an `on_failure_jump', not
+                 a `start_memory', and we jumped to past the innermost
+                 `stop_memory'.  For example, in ((.)*) we save
+                 registers 1 and 2 as a result of the *, but when we pop
+                 back to the second ), we are at the stop_memory 1.
+                 Thus, nothing is active.  */
+             if (r == 0)
+                {
+                  lowest_active_reg = NO_LOWEST_ACTIVE_REG;
+                  highest_active_reg = NO_HIGHEST_ACTIVE_REG;
+                }
+              else
+                highest_active_reg = r;
+            }
+          
+          /* If just failed to match something this time around with a
+             group that's operated on by a repetition operator, try to
+             force exit from the ``loop'', and restore the register
+             information for this group that we had before trying this
+             last match.  */
+          if ((!MATCHED_SOMETHING (reg_info[*p])
+               || (re_opcode_t) p[-3] == start_memory)
+             && (p + 2) < pend)              
+            {
+              boolean is_a_jump_n = false;
+              
+              p1 = p + 2;
+              mcnt = 0;
+              switch ((re_opcode_t) *p1++)
+                {
+                  case jump_n:
+                   is_a_jump_n = true;
+                  case pop_failure_jump:
+                 case maybe_pop_jump:
+                 case jump:
+                 case dummy_failure_jump:
+                    EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                   if (is_a_jump_n)
+                     p1 += 2;
+                    break;
+                  
+                  default:
+                    /* do nothing */ ;
+                }
+             p1 += mcnt;
+        
+              /* If the next operation is a jump backwards in the pattern
+                to an on_failure_jump right before the start_memory
+                 corresponding to this stop_memory, exit from the loop
+                 by forcing a failure after pushing on the stack the
+                 on_failure_jump's jump in the pattern, and d.  */
+              if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
+                  && (re_opcode_t) p1[3] == start_memory && p1[4] == *p)
+               {
+                  /* If this group ever matched anything, then restore
+                     what its registers were before trying this last
+                     failed match, e.g., with `(a*)*b' against `ab' for
+                     regstart[1], and, e.g., with `((a*)*(b*)*)*'
+                     against `aba' for regend[3].
+                     
+                     Also restore the registers for inner groups for,
+                     e.g., `((a*)(b*))*' against `aba' (register 3 would
+                     otherwise get trashed).  */
+                     
+                  if (EVER_MATCHED_SOMETHING (reg_info[*p]))
+                   {
+                     unsigned r; 
+        
+                      EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;
+                      
+                     /* Restore this and inner groups' (if any) registers.  */
+                      for (r = *p; r < *p + *(p + 1); r++)
+                        {
+                          regstart[r] = old_regstart[r];
+
+                          /* xx why this test?  */
+                          if ((int) old_regend[r] >= (int) regstart[r])
+                            regend[r] = old_regend[r];
+                        }     
+                    }
+                 p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  PUSH_FAILURE_POINT (p1 + mcnt, d, -2);
+
+                  goto fail;
+                }
+            }
+          
+          /* Move past the register number and the inner group count.  */
+          p += 2;
+          break;
+
+
+       /* \<digit> has been turned into a `duplicate' command which is
+           followed by the numeric value of <digit> as the register number.  */
+        case duplicate:
+         {
+           register const char *d2, *dend2;
+           int regno = *p++;   /* Get which register to match against.  */
+           DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);
+
+           /* Can't back reference a group which we've never matched.  */
+            if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
+              goto fail;
+              
+            /* Where in input to try to start matching.  */
+            d2 = regstart[regno];
+            
+            /* Where to stop matching; if both the place to start and
+               the place to stop matching are in the same string, then
+               set to the place to stop, otherwise, for now have to use
+               the end of the first string.  */
+
+            dend2 = ((FIRST_STRING_P (regstart[regno]) 
+                     == FIRST_STRING_P (regend[regno]))
+                    ? regend[regno] : end_match_1);
+           for (;;)
+             {
+               /* If necessary, advance to next segment in register
+                   contents.  */
+               while (d2 == dend2)
+                 {
+                   if (dend2 == end_match_2) break;
+                   if (dend2 == regend[regno]) break;
+
+                    /* End of string1 => advance to string2. */
+                    d2 = string2;
+                    dend2 = regend[regno];
+                 }
+               /* At end of register contents => success */
+               if (d2 == dend2) break;
+
+               /* If necessary, advance to next segment in data.  */
+               PREFETCH ();
+
+               /* How many characters left in this segment to match.  */
+               mcnt = dend - d;
+                
+               /* Want how many consecutive characters we can match in
+                   one shot, so, if necessary, adjust the count.  */
+                if (mcnt > dend2 - d2)
+                 mcnt = dend2 - d2;
+                  
+               /* Compare that many; failure if mismatch, else move
+                   past them.  */
+               if (translate 
+                    ? bcmp_translate (d, d2, mcnt, translate) 
+                    : bcmp (d, d2, mcnt))
+                 goto fail;
+               d += mcnt, d2 += mcnt;
+             }
+         }
+         break;
+
+
+        /* begline matches the empty string at the beginning of the string
+           (unless `not_bol' is set in `bufp'), and, if
+           `newline_anchor' is set, after newlines.  */
+       case begline:
+          DEBUG_PRINT1 ("EXECUTING begline.\n");
+          
+          if (AT_STRINGS_BEG (d))
+            {
+              if (!bufp->not_bol) break;
+            }
+          else if (d[-1] == '\n' && bufp->newline_anchor)
+            {
+              break;
+            }
+          /* In all other cases, we fail.  */
+          goto fail;
+
+
+        /* endline is the dual of begline.  */
+       case endline:
+          DEBUG_PRINT1 ("EXECUTING endline.\n");
+
+          if (AT_STRINGS_END (d))
+            {
+              if (!bufp->not_eol) break;
+            }
+          
+          /* We have to ``prefetch'' the next character.  */
+          else if ((d == end1 ? *string2 : *d) == '\n'
+                   && bufp->newline_anchor)
+            {
+              break;
+            }
+          goto fail;
+
+
+       /* Match at the very beginning of the data.  */
+        case begbuf:
+          DEBUG_PRINT1 ("EXECUTING begbuf.\n");
+          if (AT_STRINGS_BEG (d))
+            break;
+          goto fail;
+
+
+       /* Match at the very end of the data.  */
+        case endbuf:
+          DEBUG_PRINT1 ("EXECUTING endbuf.\n");
+         if (AT_STRINGS_END (d))
+           break;
+          goto fail;
+
+
+        /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
+           pushes NULL as the value for the string on the stack.  Then
+           `pop_failure_point' will keep the current value for the
+           string, instead of restoring it.  To see why, consider
+           matching `foo\nbar' against `.*\n'.  The .* matches the foo;
+           then the . fails against the \n.  But the next thing we want
+           to do is match the \n against the \n; if we restored the
+           string value, we would be back at the foo.
+           
+           Because this is used only in specific cases, we don't need to
+           check all the things that `on_failure_jump' does, to make
+           sure the right things get saved on the stack.  Hence we don't
+           share its code.  The only reason to push anything on the
+           stack at all is that otherwise we would have to change
+           `anychar's code to do something besides goto fail in this
+           case; that seems worse than this.  */
+        case on_failure_keep_string_jump:
+          DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");
+          
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
+
+          PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
+          break;
+
+
+       /* Uses of on_failure_jump:
+        
+           Each alternative starts with an on_failure_jump that points
+           to the beginning of the next alternative.  Each alternative
+           except the last ends with a jump that in effect jumps past
+           the rest of the alternatives.  (They really jump to the
+           ending jump of the following alternative, because tensioning
+           these jumps is a hassle.)
+
+           Repeats start with an on_failure_jump that points past both
+           the repetition text and either the following jump or
+           pop_failure_jump back to this on_failure_jump.  */
+       case on_failure_jump:
+        on_failure:
+          DEBUG_PRINT1 ("EXECUTING on_failure_jump");
+
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
+
+          /* If this on_failure_jump comes right before a group (i.e.,
+             the original * applied to a group), save the information
+             for that group and all inner ones, so that if we fail back
+             to this point, the group's information will be correct.
+             For example, in \(a*\)*\1, we need the preceding group,
+             and in \(\(a*\)b*\)\2, we need the inner group.  */
+
+          /* We can't use `p' to check ahead because we push
+             a failure point to `p + mcnt' after we do this.  */
+          p1 = p;
+
+          /* We need to skip no_op's before we look for the
+             start_memory in case this on_failure_jump is happening as
+             the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
+             against aba.  */
+          while (p1 < pend && (re_opcode_t) *p1 == no_op)
+            p1++;
+
+          if (p1 < pend && (re_opcode_t) *p1 == start_memory)
+            {
+              /* We have a new highest active register now.  This will
+                 get reset at the start_memory we are about to get to,
+                 but we will have saved all the registers relevant to
+                 this repetition op, as described above.  */
+              highest_active_reg = *(p1 + 1) + *(p1 + 2);
+              if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
+                lowest_active_reg = *(p1 + 1);
+            }
+
+          DEBUG_PRINT1 (":\n");
+          PUSH_FAILURE_POINT (p + mcnt, d, -2);
+          break;
+
+
+        /* A smart repeat ends with `maybe_pop_jump'.
+          We change it to either `pop_failure_jump' or `jump'.  */
+        case maybe_pop_jump:
+          EXTRACT_NUMBER_AND_INCR (mcnt, p);
+          DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
+          {
+           register unsigned char *p2 = p;
+
+            /* Compare the beginning of the repeat with what in the
+               pattern follows its end. If we can establish that there
+               is nothing that they would both match, i.e., that we
+               would have to backtrack because of (as in, e.g., `a*a')
+               then we can change to pop_failure_jump, because we'll
+               never have to backtrack.
+               
+               This is not true in the case of alternatives: in
+               `(a|ab)*' we do need to backtrack to the `ab' alternative
+               (e.g., if the string was `ab').  But instead of trying to
+               detect that here, the alternative has put on a dummy
+               failure point which is what we will end up popping.  */
+
+           /* Skip over open/close-group commands.  */
+           while (p2 + 2 < pend
+                  && ((re_opcode_t) *p2 == stop_memory
+                      || (re_opcode_t) *p2 == start_memory))
+             p2 += 3;                  /* Skip over args, too.  */
+
+            /* If we're at the end of the pattern, we can change.  */
+            if (p2 == pend)
+             {
+               /* Consider what happens when matching ":\(.*\)"
+                  against ":/".  I don't really understand this code
+                  yet.  */
+               p[-3] = (unsigned char) pop_failure_jump;
+                DEBUG_PRINT1
+                  ("  End of pattern: change to `pop_failure_jump'.\n");
+              }
+
+            else if ((re_opcode_t) *p2 == exactn
+                    || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
+             {
+               register unsigned char c
+                  = *p2 == (unsigned char) endline ? '\n' : p2[2];
+               p1 = p + mcnt;
+
+                /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
+                   to the `maybe_finalize_jump' of this case.  Examine what 
+                   follows.  */
+                if ((re_opcode_t) p1[3] == exactn && p1[5] != c)
+                  {
+                   p[-3] = (unsigned char) pop_failure_jump;
+                    DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
+                                  c, p1[5]);
+                  }
+                  
+               else if ((re_opcode_t) p1[3] == charset
+                        || (re_opcode_t) p1[3] == charset_not)
+                 {
+                   int not = (re_opcode_t) p1[3] == charset_not;
+                    
+                   if (c < (unsigned char) (p1[4] * BYTEWIDTH)
+                       && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
+                     not = !not;
+
+                    /* `not' is equal to 1 if c would match, which means
+                        that we can't change to pop_failure_jump.  */
+                   if (!not)
+                      {
+                       p[-3] = (unsigned char) pop_failure_jump;
+                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
+                      }
+                 }
+             }
+         }
+         p -= 2;               /* Point at relative address again.  */
+         if ((re_opcode_t) p[-1] != pop_failure_jump)
+           {
+             p[-1] = (unsigned char) jump;
+              DEBUG_PRINT1 ("  Match => jump.\n");
+             goto unconditional_jump;
+           }
+        /* Note fall through.  */
+
+
+       /* The end of a simple repeat has a pop_failure_jump back to
+           its matching on_failure_jump, where the latter will push a
+           failure point.  The pop_failure_jump takes off failure
+           points put on by this pop_failure_jump's matching
+           on_failure_jump; we got through the pattern to here from the
+           matching on_failure_jump, so didn't fail.  */
+        case pop_failure_jump:
+          {
+            /* We need to pass separate storage for the lowest and
+               highest registers, even though we don't care about the
+               actual values.  Otherwise, we will restore only one
+               register from the stack, since lowest will == highest in
+               `pop_failure_point'.  */
+            unsigned dummy_low_reg, dummy_high_reg;
+            unsigned char *pdummy;
+            const char *sdummy;
+
+            DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
+            POP_FAILURE_POINT (sdummy, pdummy,
+                               dummy_low_reg, dummy_high_reg,
+                               reg_dummy, reg_dummy, reg_info_dummy);
+          }
+          /* Note fall through.  */
+
+          
+        /* Unconditionally jump (without popping any failure points).  */
+        case jump:
+       unconditional_jump:
+         EXTRACT_NUMBER_AND_INCR (mcnt, p);    /* Get the amount to jump.  */
+          DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
+         p += mcnt;                            /* Do the jump.  */
+          DEBUG_PRINT2 ("(to 0x%x).\n", p);
+         break;
+
+       
+        /* We need this opcode so we can detect where alternatives end
+           in `group_match_null_string_p' et al.  */
+        case jump_past_alt:
+          DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
+          goto unconditional_jump;
+
+
+        /* Normally, the on_failure_jump pushes a failure point, which
+           then gets popped at pop_failure_jump.  We will end up at
+           pop_failure_jump, also, and with a pattern of, say, `a+', we
+           are skipping over the on_failure_jump, so we have to push
+           something meaningless for pop_failure_jump to pop.  */
+        case dummy_failure_jump:
+          DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
+          /* It doesn't matter what we push for the string here.  What
+             the code at `fail' tests is the value for the pattern.  */
+          PUSH_FAILURE_POINT (0, 0, -2);
+          goto unconditional_jump;
+
+
+        /* At the end of an alternative, we need to push a dummy failure
+           point in case we are followed by a `pop_failure_jump', because
+           we don't want the failure point for the alternative to be
+           popped.  For example, matching `(a|ab)*' against `aab'
+           requires that we match the `ab' alternative.  */
+        case push_dummy_failure:
+          DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
+          /* See comments just above at `dummy_failure_jump' about the
+             two zeroes.  */
+          PUSH_FAILURE_POINT (0, 0, -2);
+          break;
+
+        /* Have to succeed matching what follows at least n times.
+           After that, handle like `on_failure_jump'.  */
+        case succeed_n: 
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);
+
+          assert (mcnt >= 0);
+          /* Originally, this is how many times we HAVE to succeed.  */
+          if (mcnt > 0)
+            {
+               mcnt--;
+              p += 2;
+               STORE_NUMBER_AND_INCR (p, mcnt);
+               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p, mcnt);
+            }
+         else if (mcnt == 0)
+            {
+              DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n", p+2);
+             p[2] = (unsigned char) no_op;
+              p[3] = (unsigned char) no_op;
+              goto on_failure;
+            }
+          break;
+        
+        case jump_n: 
+          EXTRACT_NUMBER (mcnt, p + 2);
+          DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);
+
+          /* Originally, this is how many times we CAN jump.  */
+          if (mcnt)
+            {
+               mcnt--;
+               STORE_NUMBER (p + 2, mcnt);
+              goto unconditional_jump;      
+            }
+          /* If don't have to jump any more, skip over the rest of command.  */
+         else      
+           p += 4;                  
+          break;
+        
+       case set_number_at:
+         {
+            DEBUG_PRINT1 ("EXECUTING set_number_at.\n");
+
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            p1 = p + mcnt;
+            EXTRACT_NUMBER_AND_INCR (mcnt, p);
+            DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
+           STORE_NUMBER (p1, mcnt);
+            break;
+          }
+
+        case wordbound:
+          DEBUG_PRINT1 ("EXECUTING wordbound.\n");
+          if (AT_WORD_BOUNDARY (d))
+           break;
+          goto fail;
+
+       case notwordbound:
+          DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
+         if (AT_WORD_BOUNDARY (d))
+           goto fail;
+          break;
+
+       case wordbeg:
+          DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
+         if (WORDCHAR_P (d) && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
+           break;
+          goto fail;
+
+       case wordend:
+          DEBUG_PRINT1 ("EXECUTING wordend.\n");
+         if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
+              && (!WORDCHAR_P (d) || AT_STRINGS_END (d)))
+           break;
+          goto fail;
+
+#ifdef emacs
+#ifdef emacs19
+       case before_dot:
+          DEBUG_PRINT1 ("EXECUTING before_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) >= point)
+           goto fail;
+         break;
+  
+       case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) != point)
+           goto fail;
+         break;
+  
+       case after_dot:
+          DEBUG_PRINT1 ("EXECUTING after_dot.\n");
+          if (PTR_CHAR_POS ((unsigned char *) d) <= point)
+           goto fail;
+         break;
+#else /* not emacs19 */
+       case at_dot:
+          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
+         if (PTR_CHAR_POS ((unsigned char *) d) + 1 != point)
+           goto fail;
+         break;
+#endif /* not emacs19 */
+
+       case syntaxspec:
+          DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
+         mcnt = *p++;
+         goto matchsyntax;
+
+        case wordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
+         mcnt = (int) Sword;
+        matchsyntax:
+         PREFETCH ();
+         if (SYNTAX (*d++) != (enum syntaxcode) mcnt)
+            goto fail;
+          SET_REGS_MATCHED ();
+         break;
+
+       case notsyntaxspec:
+          DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
+         mcnt = *p++;
+         goto matchnotsyntax;
+
+        case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
+         mcnt = (int) Sword;
+        matchnotsyntax:
+         PREFETCH ();
+         if (SYNTAX (*d++) == (enum syntaxcode) mcnt)
+            goto fail;
+         SET_REGS_MATCHED ();
+          break;
+
+#else /* not emacs */
+       case wordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
+         PREFETCH ();
+          if (!WORDCHAR_P (d))
+            goto fail;
+         SET_REGS_MATCHED ();
+          d++;
+         break;
+         
+       case notwordchar:
+          DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
+         PREFETCH ();
+         if (WORDCHAR_P (d))
+            goto fail;
+          SET_REGS_MATCHED ();
+          d++;
+         break;
+#endif /* not emacs */
+          
+        default:
+          abort ();
+       }
+      continue;  /* Successfully executed one pattern command; keep going.  */
+
+
+    /* We goto here if a matching operation fails. */
+    fail:
+      if (!FAIL_STACK_EMPTY ())
+       { /* A restart point is known.  Restore to that state.  */
+          DEBUG_PRINT1 ("\nFAIL:\n");
+          POP_FAILURE_POINT (d, p,
+                             lowest_active_reg, highest_active_reg,
+                             regstart, regend, reg_info);
+
+          /* If this failure point is a dummy, try the next one.  */
+          if (!p)
+           goto fail;
+
+          /* If we failed to the end of the pattern, don't examine *p.  */
+         assert (p <= pend);
+          if (p < pend)
+            {
+              boolean is_a_jump_n = false;
+              
+              /* If failed to a backwards jump that's part of a repetition
+                 loop, need to pop this failure point and use the next one.  */
+              switch ((re_opcode_t) *p)
+                {
+                case jump_n:
+                  is_a_jump_n = true;
+                case maybe_pop_jump:
+                case pop_failure_jump:
+                case jump:
+                  p1 = p + 1;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  p1 += mcnt;  
+
+                  if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
+                      || (!is_a_jump_n
+                          && (re_opcode_t) *p1 == on_failure_jump))
+                    goto fail;
+                  break;
+                default:
+                  /* do nothing */ ;
+                }
+            }
+
+          if (d >= string1 && d <= end1)
+           dend = end_match_1;
+        }
+      else
+        break;   /* Matching at this starting point really fails.  */
+    } /* for (;;) */
+
+  if (best_regs_set)
+    goto restore_best_regs;
+
+  FREE_VARIABLES ();
+
+  return -1;                           /* Failure to match.  */
+} /* re_match_2 */
+\f
+/* Subroutine definitions for re_match_2.  */
+
+
+/* We are passed P pointing to a register number after a start_memory.
+   
+   Return true if the pattern up to the corresponding stop_memory can
+   match the empty string, and false otherwise.
+   
+   If we find the matching stop_memory, sets P to point to one past its number.
+   Otherwise, sets P to an undefined byte less than or equal to END.
+
+   We don't handle duplicates properly (yet).  */
+
+static boolean
+group_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  /* Point to after the args to the start_memory.  */
+  unsigned char *p1 = *p + 2;
+  
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and return true or
+        false, as appropriate, when we get to one that can't, or to the
+         matching stop_memory.  */
+      
+      switch ((re_opcode_t) *p1)
+        {
+        /* Could be either a loop or a series of alternatives.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          
+          /* If the next operation is not a jump backwards in the
+            pattern.  */
+
+         if (mcnt >= 0)
+           {
+              /* Go through the on_failure_jumps of the alternatives,
+                 seeing if any of the alternatives cannot match nothing.
+                 The last alternative starts with only a jump,
+                 whereas the rest start with on_failure_jump and end
+                 with a jump, e.g., here is the pattern for `a|b|c':
+
+                 /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
+                 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
+                 /exactn/1/c                                           
+
+                 So, we have to first go through the first (n-1)
+                 alternatives and then deal with the last one separately.  */
+
+
+              /* Deal with the first (n-1) alternatives, which start
+                 with an on_failure_jump (see above) that jumps to right
+                 past a jump_past_alt.  */
+
+              while ((re_opcode_t) p1[mcnt-3] == jump_past_alt)
+                {
+                  /* `mcnt' holds how many bytes long the alternative
+                     is, including the ending `jump_past_alt' and
+                     its number.  */
+
+                  if (!alt_match_null_string_p (p1, p1 + mcnt - 3, 
+                                                     reg_info))
+                    return false;
+
+                  /* Move to right after this alternative, including the
+                    jump_past_alt.  */
+                  p1 += mcnt;  
+
+                  /* Break if it's the beginning of an n-th alternative
+                     that doesn't begin with an on_failure_jump.  */
+                  if ((re_opcode_t) *p1 != on_failure_jump)
+                    break;
+               
+                 /* Still have to check that it's not an n-th
+                    alternative that starts with an on_failure_jump.  */
+                 p1++;
+                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+                  if ((re_opcode_t) p1[mcnt-3] != jump_past_alt)
+                    {
+                     /* Get to the beginning of the n-th alternative.  */
+                      p1 -= 3;
+                      break;
+                    }
+                }
+
+              /* Deal with the last alternative: go back and get number
+                 of the `jump_past_alt' just before it.  `mcnt' contains
+                 the length of the alternative.  */
+              EXTRACT_NUMBER (mcnt, p1 - 2);
+
+              if (!alt_match_null_string_p (p1, p1 + mcnt, reg_info))
+                return false;
+
+              p1 += mcnt;      /* Get past the n-th alternative.  */
+            } /* if mcnt > 0 */
+          break;
+
+          
+        case stop_memory:
+         assert (p1[1] == **p);
+          *p = p1 + 2;
+          return true;
+
+        
+        default: 
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    } /* while p1 < end */
+
+  return false;
+} /* group_match_null_string_p */
+
+
+/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
+   It expects P to be the first byte of a single alternative and END one
+   byte past the last. The alternative can contain groups.  */
+   
+static boolean
+alt_match_null_string_p (p, end, reg_info)
+    unsigned char *p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  unsigned char *p1 = p;
+  
+  while (p1 < end)
+    {
+      /* Skip over opcodes that can match nothing, and break when we get 
+         to one that can't.  */
+      
+      switch ((re_opcode_t) *p1)
+        {
+       /* It's a loop.  */
+        case on_failure_jump:
+          p1++;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+          break;
+          
+       default: 
+          if (!common_op_match_null_string_p (&p1, end, reg_info))
+            return false;
+        }
+    }  /* while p1 < end */
+
+  return true;
+} /* alt_match_null_string_p */
+
+
+/* Deals with the ops common to group_match_null_string_p and
+   alt_match_null_string_p.  
+   
+   Sets P to one after the op and its arguments, if any.  */
+
+static boolean
+common_op_match_null_string_p (p, end, reg_info)
+    unsigned char **p, *end;
+    register_info_type *reg_info;
+{
+  int mcnt;
+  boolean ret;
+  int reg_no;
+  unsigned char *p1 = *p;
+
+  switch ((re_opcode_t) *p1++)
+    {
+    case no_op:
+    case begline:
+    case endline:
+    case begbuf:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case wordbound:
+    case notwordbound:
+#ifdef emacs
+    case before_dot:
+    case at_dot:
+    case after_dot:
+#endif
+      break;
+
+    case start_memory:
+      reg_no = *p1;
+      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
+      ret = group_match_null_string_p (&p1, end, reg_info);
+      
+      /* Have to set this here in case we're checking a group which
+         contains a group and a back reference to it.  */
+
+      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
+        REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;
+
+      if (!ret)
+        return false;
+      break;
+          
+    /* If this is an optimized succeed_n for zero times, make the jump.  */
+    case jump:
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+      if (mcnt >= 0)
+        p1 += mcnt;
+      else
+        return false;
+      break;
+
+    case succeed_n:
+      /* Get to the number of times to succeed.  */
+      p1 += 2;         
+      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+
+      if (mcnt == 0)
+        {
+          p1 -= 4;
+          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
+          p1 += mcnt;
+        }
+      else
+        return false;
+      break;
+
+    case duplicate: 
+      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
+        return false;
+      break;
+
+    case set_number_at:
+      p1 += 4;
+
+    default:
+      /* All other opcodes mean we cannot match the empty string.  */
+      return false;
+  }
+
+  *p = p1;
+  return true;
+} /* common_op_match_null_string_p */
+
+
+/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
+   bytes; nonzero otherwise.  */
+   
+static int
+bcmp_translate (s1, s2, len, translate)
+     unsigned char *s1, *s2;
+     register int len;
+     char *translate;
+{
+  register unsigned char *p1 = s1, *p2 = s2;
+  while (len)
+    {
+      if (translate[*p1++] != translate[*p2++]) return 1;
+      len--;
+    }
+  return 0;
+}
+\f
+/* Entry points for GNU code.  */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length SIZE) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+   
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.
+   
+   We call regex_compile to do the actual compilation.  */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+     const char *pattern;
+     int length;
+     struct re_pattern_buffer *bufp;
+{
+  reg_errcode_t ret;
+  
+  /* GNU code is written to assume at least RE_NREGS registers will be set
+     (and at least one extra will be -1).  */
+  bufp->regs_allocated = REGS_UNALLOCATED;
+  
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub.  */
+  bufp->no_sub = 0;
+  
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+  
+  ret = regex_compile (pattern, length, re_syntax_options, bufp);
+
+  return re_error_msg[(int) ret];
+}     
+\f
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them if this is an Emacs or POSIX compilation.  */
+
+#if !defined (emacs) && !defined (_POSIX_SOURCE)
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+re_comp (s)
+    const char *s;
+{
+  reg_errcode_t ret;
+  
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+       return "No previous regular expression";
+      return 0;
+    }
+
+  if (!re_comp_buf.buffer)
+    {
+      re_comp_buf.buffer = (unsigned char *) malloc (200);
+      if (re_comp_buf.buffer == NULL)
+        return "Memory exhausted";
+      re_comp_buf.allocated = 200;
+
+      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
+      if (re_comp_buf.fastmap == NULL)
+       return "Memory exhausted";
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
+  
+  /* Yes, we're discarding `const' here.  */
+  return (char *) re_error_msg[(int) ret];
+}
+
+
+int
+re_exec (s)
+    const char *s;
+{
+  const int len = strlen (s);
+  return
+    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
+}
+#endif /* not emacs and not _POSIX_SOURCE */
+\f
+/* POSIX.2 functions.  Don't define these for Emacs.  */
+
+#ifndef emacs
+
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' and `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (preg, pattern, cflags)
+    regex_t *preg;
+    const char *pattern; 
+    int cflags;
+{
+  reg_errcode_t ret;
+  unsigned syntax
+    = (cflags & REG_EXTENDED) ?
+      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;
+
+  /* regex_compile will allocate the space for the compiled pattern.  */
+  preg->buffer = 0;
+  preg->allocated = 0;
+  
+  /* Don't bother to use a fastmap when searching.  This simplifies the
+     REG_NEWLINE case: if we used a fastmap, we'd have to put all the
+     characters after newlines into the fastmap.  This way, we just try
+     every character.  */
+  preg->fastmap = 0;
+  
+  if (cflags & REG_ICASE)
+    {
+      unsigned i;
+      
+      preg->translate = (char *) malloc (CHAR_SET_SIZE);
+      if (preg->translate == NULL)
+        return (int) REG_ESPACE;
+
+      /* Map uppercase characters to corresponding lowercase ones.  */
+      for (i = 0; i < CHAR_SET_SIZE; i++)
+        preg->translate[i] = ISUPPER (i) ? tolower (i) : i;
+    }
+  else
+    preg->translate = NULL;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+
+  preg->no_sub = !!(cflags & REG_NOSUB);
+
+  /* POSIX says a null character in the pattern terminates it, so we 
+     can use strlen here in compiling the pattern.  */
+  ret = regex_compile (pattern, strlen (pattern), syntax, preg);
+  
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN) ret = REG_EPAREN;
+  
+  return (int) ret;
+}
+
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+   
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+   
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+   
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    const regex_t *preg;
+    const char *string; 
+    size_t nmatch; 
+    regmatch_t pmatch[]; 
+    int eflags;
+{
+  int ret;
+  struct re_registers regs;
+  regex_t private_preg;
+  int len = strlen (string);
+  boolean want_reg_info = !preg->no_sub && nmatch > 0;
+
+  private_preg = *preg;
+  
+  private_preg.not_bol = !!(eflags & REG_NOTBOL);
+  private_preg.not_eol = !!(eflags & REG_NOTEOL);
+  
+  /* The user has told us exactly how many registers to return
+     information about, via `nmatch'.  We have to pass that on to the
+     matching routines.  */
+  private_preg.regs_allocated = REGS_FIXED;
+  
+  if (want_reg_info)
+    {
+      regs.num_regs = nmatch;
+      regs.start = TALLOC (nmatch, regoff_t);
+      regs.end = TALLOC (nmatch, regoff_t);
+      if (regs.start == NULL || regs.end == NULL)
+        return (int) REG_NOMATCH;
+    }
+
+  /* Perform the searching operation.  */
+  ret = re_search (&private_preg, string, len,
+                   /* start: */ 0, /* range: */ len,
+                   want_reg_info ? &regs : (struct re_registers *) 0);
+  
+  /* Copy the register information to the POSIX structure.  */
+  if (want_reg_info)
+    {
+      if (ret >= 0)
+        {
+          unsigned r;
+
+          for (r = 0; r < nmatch; r++)
+            {
+              pmatch[r].rm_so = regs.start[r];
+              pmatch[r].rm_eo = regs.end[r];
+            }
+        }
+
+      /* If we needed the temporary register info, free the space now.  */
+      free (regs.start);
+      free (regs.end);
+    }
+
+  /* We want zero return to mean success, unlike `re_search'.  */
+  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
+}
+
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+    int errcode;
+    const regex_t *preg;
+    char *errbuf;
+    size_t errbuf_size;
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (errcode < 0
+      || errcode >= (sizeof (re_error_msg) / sizeof (re_error_msg[0])))
+    /* Only error codes returned by the rest of the code should be passed 
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = re_error_msg[errcode];
+
+  /* POSIX doesn't require that we do anything in this case, but why
+     not be nice.  */
+  if (! msg)
+    msg = "Success";
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+  
+  if (errbuf_size != 0)
+    {
+      if (msg_size > errbuf_size)
+        {
+          strncpy (errbuf, msg, errbuf_size - 1);
+          errbuf[errbuf_size - 1] = 0;
+        }
+      else
+        strcpy (errbuf, msg);
+    }
+
+  return msg_size;
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (preg)
+    regex_t *preg;
+{
+  if (preg->buffer != NULL)
+    free (preg->buffer);
+  preg->buffer = NULL;
+  
+  preg->allocated = 0;
+  preg->used = 0;
+
+  if (preg->fastmap != NULL)
+    free (preg->fastmap);
+  preg->fastmap = NULL;
+  preg->fastmap_accurate = 0;
+
+  if (preg->translate != NULL)
+    free (preg->translate);
+  preg->translate = NULL;
+}
+
+#endif /* not emacs  */
+\f
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/lib/contrib/regex.h b/lib/contrib/regex.h
new file mode 100644 (file)
index 0000000..408dd21
--- /dev/null
@@ -0,0 +1,490 @@
+/* Definitions for data structures and routines for the regular
+   expression library, version 0.12.
+
+   Copyright (C) 1985, 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef __REGEXP_LIBRARY_H__
+#define __REGEXP_LIBRARY_H__
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+   <regex.h>.  */
+
+#ifdef VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+   should be there.  */
+#include <stddef.h>
+#endif
+
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+typedef unsigned reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS (1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals. 
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.  
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically, 
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES. 
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal. 
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+extern reg_syntax_t re_syntax_options;
+\f
+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */ 
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK                                                  \
+  (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL                      \
+   | RE_NO_BK_PARENS            | RE_NO_BK_REFS                                \
+   | RE_NO_BK_VBAR               | RE_NO_EMPTY_RANGES                  \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+#define RE_SYNTAX_POSIX_AWK                                            \
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS)
+
+#define RE_SYNTAX_GREP                                                 \
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES                                \
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                           \
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP                                                        \
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS                   \
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE                   \
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS                            \
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP                                          \
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON                                                \
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL             \
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC                                          \
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC                                  \
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED                                       \
+  (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS                  \
+   | RE_CONTEXT_INDEP_OPS  | RE_NO_BK_BRACES                           \
+   | RE_NO_BK_PARENS       | RE_NO_BK_VBAR                             \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INVALID_OPS
+   replaces RE_CONTEXT_INDEP_OPS and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                               \
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES                          \
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS                            \
+   | RE_NO_BK_VBAR         | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+\f
+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+#undef RE_DUP_MAX
+#endif
+#define RE_DUP_MAX ((1 << 15) - 1) 
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+  REG_NOERROR = 0,     /* Success.  */
+  REG_NOMATCH,         /* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,          /* Invalid pattern.  */
+  REG_ECOLLATE,                /* Not implemented.  */
+  REG_ECTYPE,          /* Invalid character class name.  */
+  REG_EESCAPE,         /* Trailing backslash.  */
+  REG_ESUBREG,         /* Invalid back reference.  */
+  REG_EBRACK,          /* Unmatched left bracket.  */
+  REG_EPAREN,          /* Parenthesis imbalance.  */ 
+  REG_EBRACE,          /* Unmatched \{.  */
+  REG_BADBR,           /* Invalid contents of \{\}.  */
+  REG_ERANGE,          /* Invalid range end.  */
+  REG_ESPACE,          /* Ran out of memory.  */
+  REG_BADRPT,          /* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,            /* Premature end.  */
+  REG_ESIZE,           /* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN          /* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+\f
+/* This data structure represents a compiled pattern.  Before calling
+   the pattern compiler, the fields `buffer', `allocated', `fastmap',
+   `translate', and `no_sub' can be set.  After the pattern has been
+   compiled, the `re_nsub' field is available.  All other fields are
+   private to the regex routines.  */
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+       /* Space that holds the compiled pattern.  It is declared as
+          `unsigned char *' because its elements are
+           sometimes used as array indexes.  */
+  unsigned char *buffer;
+
+       /* Number of bytes to which `buffer' points.  */
+  unsigned long allocated;
+
+       /* Number of bytes actually used in `buffer'.  */
+  unsigned long used;  
+
+        /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t syntax;
+
+        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
+           the fastmap, if there is one, to skip over impossible
+           starting points for matches.  */
+  char *fastmap;
+
+        /* Either a translate table to apply to all characters before
+           comparing them, or zero for no translation.  The translation
+           is applied to a pattern when it is compiled and to a string
+           when it is matched.  */
+  char *translate;
+
+       /* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+        /* Zero if this pattern cannot match the empty string, one else.
+           Well, in truth it's used only in `re_search_2', to see
+           whether or not we should use the fastmap, so we don't set
+           this absolutely perfectly; see `re_compile_fastmap' (the
+           `duplicate' case).  */
+  unsigned can_be_null : 1;
+
+        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+             for `max (RE_NREGS, re_nsub + 1)' groups.
+           If REGS_REALLOCATE, reallocate space if necessary.
+           If REGS_FIXED, use what's there.  */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned regs_allocated : 2;
+
+        /* Set to zero when `regex_compile' compiles a pattern; set to one
+           by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned fastmap_accurate : 1;
+
+        /* If set, `re_match_2' does not return information about
+           subexpressions.  */
+  unsigned no_sub : 1;
+
+        /* If set, a beginning-of-line anchor doesn't match at the
+           beginning of the string.  */ 
+  unsigned not_bol : 1;
+
+        /* Similarly for an end-of-line anchor.  */
+  unsigned not_eol : 1;
+
+        /* If true, an anchor at a newline matches.  */
+  unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+
+/* search.c (search_buffer) in Emacs needs this one opcode value.  It is
+   defined both in `regex.c' and here.  */
+#define RE_EXACTN_VALUE 1
+\f
+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+   `re_match_2' returns information about at least this many registers
+   the first time a `regs' structure is passed.  */
+#ifndef RE_NREGS
+#define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+\f
+/* Declarations for routines.  */
+
+/* To avoid duplicating every routine declaration -- once with a
+   prototype (if we are ANSI), and once without (if we aren't) -- we
+   use the following macro to declare argument types.  This
+   unfortunately clutters up the declarations a bit, but I think it's
+   worth it.  */
+
+#if __STDC__
+
+#define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+#define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+   You can also simply assign to the `re_syntax_options' variable.  */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+   and syntax given by the global `re_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.  */
+extern const char *re_compile_pattern
+  _RE_ARGS ((const char *pattern, int length,
+             struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+   accelerate searches.  Return 0 if successful and -2 if was an
+   internal error.  */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+   compiled into BUFFER.  Start searching at position START, for RANGE
+   characters.  Return the starting position of the match, -1 for no
+   match, or -2 for an internal error.  Also return register
+   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
+extern int re_search
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+            int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+   STRING2.  Also, stop searching at index START + STOP.  */
+extern int re_search_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+   in BUFFER matched, starting at position START.  */
+extern int re_match
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+             int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
+extern int re_match_2 
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
+   for recording register information.  STARTS and ENDS must be
+   allocated with malloc, and must each be at least `NUM_REGS * sizeof
+   (regoff_t)' bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+extern void re_set_registers
+  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+             unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+/* 4.2 bsd compatibility.  */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+
+/* POSIX compatibility.  */
+extern int regcomp _RE_ARGS ((regex_t *preg, const char *pattern, int cflags));
+extern int regexec
+  _RE_ARGS ((const regex_t *preg, const char *string, size_t nmatch,
+             regmatch_t pmatch[], int eflags));
+extern size_t regerror
+  _RE_ARGS ((int errcode, const regex_t *preg, char *errbuf,
+             size_t errbuf_size));
+extern void regfree _RE_ARGS ((regex_t *preg));
+
+#endif /* not __REGEXP_LIBRARY_H__ */
+\f
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/lib/doc/INDEX.tmpl b/lib/doc/INDEX.tmpl
new file mode 100644 (file)
index 0000000..77a7f9e
--- /dev/null
@@ -0,0 +1,16 @@
+<!-- Template file for the big index that appears in the Toolkit reference
+manual on the left side.  With this file it is possible to add other than
+automatically generated links to that list. -->
+
+<a href="index.html"><img src="box.gif" border="0" alt="">SILC Toolkit Reference Manual</a><br />
+
+@BODY@
+
+<br />
+<b>Resource Links</b>
+<br />
+<a href="http://silcnet.org"><img src="box.gif" border="0" alt="">SILC Project Website</a><br />
+<a href="http://silcnet.org/?page=docs"><img src="box.gif" border="0" alt="">SILC Protocol Documentation</a><br />
+<a href="http://silcnet.org/?page=whitepaper"><img src="box.gif" border="0" alt="">SILC White Paper</a><br />
+<a href="http://silcnet.org/?page=faq"><img src="box.gif" border="0" alt="">SILC FAQ</a><br />
+
diff --git a/lib/doc/LIBINDEX b/lib/doc/LIBINDEX
new file mode 100644 (file)
index 0000000..cf12bfa
--- /dev/null
@@ -0,0 +1,41 @@
+<!--
+ Index file for SILC Toolkit Reference Manual. This file is processed with
+ the SILC Document generator.
+-->
+
+<big><b>SILC Toolkit Reference Manual</b></big>
+<br />
+<small>
+Version: @VERSION@<br />
+Copyright &copy; 2001 - 2002 The SILC Project<br />
+Updated: @DATE@
+</small>
+<br /><br /><br />
+Welcome to the SILC Toolkit Reference Manual.  The manual is a complete
+developer guide and reference for the SILC application programmer.  The manual
+is intended for application programmers who would like to integrate the SILC
+support into their application, and to create new SILC applications.
+<br /><br />
+The guide is especially targeted to SILC client application developers, and
+provides full reference for the SILC Client Library, and a developer guide to
+create new SILC client applications.
+<br /><br />
+The application programming interfaces are automatically generated from the
+Toolkit sources, and the documentation is constantly evolving.  New versions
+of the Toolkit always delivers the latest version of this reference manual.
+
+<br /><br /><br />
+<li><a href="intro_reference.html">Introduction to the Manual</a><br />
+@BODY@
+
+<br /><br /><br />
+<b>Resource Links</b>
+<br /><br />
+Please refer to these outside links for more information about the SILC 
+project and SILC Protocol.
+
+<br /><br /><br />
+<li><a href="http://silcnet.org">SILC Project Website</a><br />
+<li><a href="http://silcnet.org/?page=docs">SILC Protocol Documentation</a><br />
+<li><a href="http://silcnet.org/?page=whitepaper">SILC White Paper</a><br />
+<li><a href="http://silcnet.org/?page=faq">SILC FAQ</a><br />
diff --git a/lib/doc/arch.gif b/lib/doc/arch.gif
new file mode 100644 (file)
index 0000000..92a0d3c
Binary files /dev/null and b/lib/doc/arch.gif differ
diff --git a/lib/doc/box.gif b/lib/doc/box.gif
new file mode 100644 (file)
index 0000000..5b9cfcd
Binary files /dev/null and b/lib/doc/box.gif differ
diff --git a/lib/doc/box2.gif b/lib/doc/box2.gif
new file mode 100644 (file)
index 0000000..041f76d
Binary files /dev/null and b/lib/doc/box2.gif differ
diff --git a/lib/doc/dot.gif b/lib/doc/dot.gif
new file mode 100644 (file)
index 0000000..5829b6e
Binary files /dev/null and b/lib/doc/dot.gif differ
diff --git a/lib/doc/index_pic.gif b/lib/doc/index_pic.gif
new file mode 100644 (file)
index 0000000..e84bec4
Binary files /dev/null and b/lib/doc/index_pic.gif differ
diff --git a/lib/doc/intro_reference.html b/lib/doc/intro_reference.html
new file mode 100644 (file)
index 0000000..c5f9681
--- /dev/null
@@ -0,0 +1,129 @@
+<big><b>Introduction to the Manual</b></big>
+<br />&nbsp;<br />
+
+This document is designed to help you understand how the reference manual is
+organized, how it can be used efficiently, and how to find the information
+you need.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Target Audience</b>
+
+<br />&nbsp;<br />
+This Toolkit reference manual is targeted at application developers who
+would like add SILC support into their application, and to create new
+SILC based applications.  It is especially aimed at C and C++ programmers,
+who would like to create SILC client applications, either based on command
+line interface (CLI) or on graphical user interface (GUI).
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Overview</b>
+
+<br />&nbsp;<br />
+The SILC Toolkit Reference Manual has collected the essential information
+needed by application developers.  The following guide and reference
+information is included in this manual:
+
+<br />&nbsp;<br />
+<li>Describing the documentation conventions<br />
+<li>Describing the Toolkit design<br />
+<li>Describing the SILC Protocol<br />
+<li>Describing the programming conventions and idioms<br />
+<li>Documenting the set of public APIs available for programmers<br />
+<li>Describing the usage of various libraries
+
+<br />&nbsp;<br />
+You can download the latest SILC Toolkit from the 
+<a href="http://silcnet.org">SILC Project Website</a>, which includes the
+latest version of the reference manual. The Toolkit package includes the
+full sources of the Toolkit, and includes several example applications and
+piece of example codes.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Using the Reference Manual</b>
+
+<br />&nbsp;<br />
+The API references are ogranized by libraries.  Each library will include
+list of interfaces it provides.  Each of the interface in the library
+provides list of public API items.  Each of the item in the list is a hyper
+link that opens the detailed page describing the API item.  All API 
+references are automatically generated from the sources and they have a clear
+structural layout.  The references can provide cross links to other
+references inside the specific interface or the specific library.
+
+<br />&nbsp;<br />
+The list of the library interface items can also include links to guides
+that describe the use of a specific library or interface.  These are
+intended as HOWTOs for programmers describing all aspects of the library
+or interface.  They make the application development easier by also providing
+small examples.
+
+<br />&nbsp;<br />
+All interfaces provided by the reference manual are public, and it does not
+describe any internal or undocumented interfaces.  Since the reference
+manual is automatically generated, it is constantly evolving.  It also
+may omit some of the interfaces or libraries, that have not yet been
+documented in the sources.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Document Layout</b>
+
+<br />&nbsp;<br />
+The document layout provides quick links to libraries, interfaces and
+specific API items by including list of links in the left and/or right
+side of the page in the web browser.  These links can be used to directly
+access the specific library, interface or API item.  The link lists may include
+other links to guides, and reference links to outside the reference manual
+as well.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Reference Conventions</b>
+
+<br />&nbsp;<br />
+The structural layout of a API item describes the following information 
+about the item:
+
+<br />&nbsp;<br />
+<li>Type.  Types that can appear are Variable, Structure, Function.  A name
+that appears without type is constant, usually #define, enum or typedef.
+Usually the source code of the constants are appended to the reference.
+
+<br />&nbsp;<br />
+<li>Name.  Describes the name of the item.  All functions start with 
+<b>silc_</b> prefix, macros start with <b>SILC_</b> prefix, and type names
+and structures start with <b>Silc</b> prefix.
+
+<br />&nbsp;<br />
+<li>Synopsis.  Functions also describe the synopsis of the function.
+
+<br />&nbsp;<br />
+<li>Description.  Each of the item is described in detail of what the item
+does and how it can be used.
+
+<br />&nbsp;<br />
+<li>Notes.  Optionally the item may describe additional notes to the
+detailed description.  These usually describe various exeptions or other
+important notes that the programmer should be aware of.
+
+<br />&nbsp;<br />
+<li>Example.  Optionally the item may include a piece of source code that
+give short example of how the item may be used.
+
+<br />&nbsp;<br />
+<li>See Also.  Optionally the item may include list of links to other
+items, or some other references that relate to the described item.
+
+<br />&nbsp;<br />
+<li>Source.  Optionally the item may include the actual source code from
+the header file where the documentation was automatically generated.
+
+<br />&nbsp;<br />
+Note that some of these informations are optional and not all API items
+include all of these informations.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Reference Example</b>
+
+<br />&nbsp;<br />
+Please refer to this link for short example of the API item reference 
+layout: <a href="silcexample.html">SILC Example API</a>.
+
diff --git a/lib/doc/silcclient_using.html b/lib/doc/silcclient_using.html
new file mode 100644 (file)
index 0000000..84a41b2
--- /dev/null
@@ -0,0 +1,369 @@
+<big><b>Using SILC Client Library</b></big>
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Introduction</b>
+
+<br />&nbsp;<br />
+SILC Client library is a full featured SILC Client protocol implementation.
+The library has been designed to be complete SILC client without actual
+user interface.  The library provides the API for the appliation which
+it can use to implement generally whatever user interface it wants.  The
+SILC Client Library recides in the lib/silcclient/ directory.  It uses
+common and core compomnent of SILC protocol from the lib/silccore, SKE
+from lib/silcske and general utility routines from lib/silcutil.
+
+<br />&nbsp;<br />
+The `silcapi.h' file defines the function prototypes that application
+must implement in order to be able to create the user interface with the
+library.  The idea is that the application can implement whatever user
+interface routines in the functions and display the data whatever way
+it wants.  The library is entirely transparent to the user interface and
+it does not include any user interface specific issues such as window
+handling or item handling on the screen etc.  These does not interest
+the library.  The `silcapi.h' also defines the client libary interface
+the application can call.  The interface includes for example functions
+for sending channel and private messages, client and channel retrieval
+and other utility functions.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Including Library Headers</b>
+
+<br />&nbsp;<br />
+Your application must include the following includes in your sources to 
+get access all SILC Client Library routines:
+
+<br />&nbsp;<br />
+<tt>
+#include "silcincludes.h"<br />
+#include "clientlibincludes.h"
+</tt>
+
+<br />&nbsp;<br />
+If you are compiling with C++ compiler then you need to include the 
+headers as follows:
+
+<br />&nbsp;<br />
+<tt>
+extern "C" {<br />
+#include "silcincludes.h"<br />
+#include "clientlibincludes.h"<br &/>
+}
+</tt>
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Creating Client</b>
+
+<br />&nbsp;<br />
+The client is context or entity based, so several client entitites can
+be created in the application if needed.  However, it should be noted
+that they are completely independent from each other and can be seen
+as different applications.  Usually only one client entity is needed
+per application.
+
+<br />&nbsp;<br />
+The client object is SilcClient which is usually allocated in following
+manner:
+
+<br />&nbsp;<br />
+<tt>&nbsp;&nbsp;SilcClient client = silc_client_alloc(&ops, params, context, version);</tt>
+
+<br />&nbsp;<br />
+`ops' is the static structure of client operations that library will call.
+`context' can be some application specific context that will be saved into
+the SilcClient object.  It is up to the caller to free this context.
+SilcClient is always passed to the application thus the application
+specific context can be retrieved from the SilcClient object.  See 
+`client.h' file for detailed definition of SilcClient object.
+
+<br />&nbsp;<br />
+`ops' can be defined for example as follows:
+
+<br />&nbsp;<br />
+<tt>
+SilcClientOperations ops = {<br />
+&nbsp;&nbsp;  silc_say,<br />
+&nbsp;&nbsp;  silc_channel_message,<br />
+&nbsp;&nbsp;  silc_private_message,<br />
+&nbsp;&nbsp;  silc_notify,<br />
+&nbsp;&nbsp;  silc_command,<br />
+&nbsp;&nbsp;  silc_command_reply,<br />
+&nbsp;&nbsp;  silc_connect,<br />
+&nbsp;&nbsp;  silc_disconnect,<br />
+&nbsp;&nbsp;  silc_get_auth_method,<br />
+&nbsp;&nbsp;  silc_verify_public_key,<br />
+&nbsp;&nbsp;  silc_ask_passphrase,<br />
+&nbsp;&nbsp;  silc_failure,<br />
+&nbsp;&nbsp;  silc_key_agreement,<br />
+};<br />
+</tt>
+
+<br />&nbsp;<br />
+Please see the `client_ops_example.c' source file in lib/silcclient/
+directory for predefined structure and stub functions for your
+convenience.  It is provided for programmers so that they can copy
+it and use it directly in their application.
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Initializing the Client</b>
+
+<br />&nbsp;<br />
+The client must be initialized before running.  However, there are also
+some other tasks that must be done before initializing the client.
+The following pointers must be set by the application  before calling
+the initializing function:
+
+<br />&nbsp;<br />
+<tt>
+&nbsp;&nbsp;client->username<br />
+&nbsp;&nbsp;client->hostname<br />
+&nbsp;&nbsp;client->realname<br />
+&nbsp;&nbsp;client->pkcs<br />
+&nbsp;&nbsp;client->public_key<br />
+&nbsp;&nbsp;client->private_key
+</tt>
+
+<br />&nbsp;<br />
+You may also set client->nickname if you want.  If it is set then the
+library will change the nickname to that one after the client is connected
+to the server.  If not set, then server will initially give the nickname
+which is same as the username.
+
+<br />&nbsp;<br />
+After setting the pointers one must call:
+
+<br />&nbsp;<br />
+<tt>&nbsp;&nbsp;silc_client_init(client);</tt>
+
+<br />&nbsp;<br />
+which then initializes the client library for the `client'.  If the
+pointers mentioned above are not initialized the silc_client_init will
+fail.  The application should check the return value of the silc_client_init
+function.
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Running the Client</b>
+
+<br />&nbsp;<br />
+The client is run by calling silc_client_run.  The function will call
+the scheduler from utility library that will be run until the program is
+ended.  When silc_client_run returns the application is ended.  Thus,
+to run the client, call:
+
+<br />&nbsp;<br />
+<tt>&nbsp;&nbsp;silc_client_run(client);</tt>
+
+<br />&nbsp;<br />
+Usually application may do some other initializations before calling
+this function.  For example before calling this function application
+should initialize the user interface.
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Running the Client in GUI application</b>
+
+<br />&nbsp;<br />
+Many GUI applications has their own main loop or event loop, which they 
+would like to use or are forced to use by the underlaying system.  If you 
+are developing for example GUI application on Unix system, and you are 
+using GTK+ or QT as GUI library you would probably like to use their own 
+main loop.  SILC Client can be run under external main loop as well.  The 
+interface provides a function silc_client_run_one which will run the 
+client library once, and returns immediately.  During that running it can 
+process incoming data and send outgoing data, but it is guaranteed that it 
+will not block the calling process.
+
+<br />&nbsp;<br />
+It is suggested that you would call this function as many times in a 
+second as possible to provide smooth action for the client library.  You 
+can use an timeout task, or an idle task provided by your GUI library to 
+accomplish this.  After you have initialized the client library with 
+silc_client_init, you should register the timeout task or idle task that 
+will call the silc_client_run_one periodically.  In the Toolkit package 
+there is GTK-- GUI example in silcer/ directory.  That example calls the 
+silc_client_run_one every 50 milliseconds, and it should be sufficient for 
+smooth working.
+
+<br />&nbsp;<br />
+For Win32 the silc_client_run can be used instead of using the Windows's 
+own event loop.  However, if you would like to use the silc_client_run_one 
+also on Win32 systems it is possible.
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Running Client in GTK--</b>
+
+<br />&nbsp;<br />
+Here is a short example how to run the SILC Client libary under the 
+Gnome/GTK--'s main loop:
+
+<br />&nbsp;<br />
+<tt>
+gint YourClass::silc_scheduler()<br />
+{<br />
+&nbsp;&nbsp;  // Run the SILC client once, and return immediately.  This function<br />
+&nbsp;&nbsp;  // is called every 50 milliseconds by the Gnome main loop, to process<br />
+&nbsp;&nbsp;  // SILC stuff.  This function will read data, and write data to network,<br />
+&nbsp;&nbsp;  // etc.  Makes the client library tick! :)<br />
+&nbsp;&nbsp;  silc_client_run_one(silc_client);<br />
+&nbsp;&nbsp;  return 1;<br />
+}<br />
+</tt>
+
+<br />&nbsp;<br />
+then, during initialization of the SILC Client call:
+
+<br />&nbsp;<br />
+<tt>
+// Setup SILC scheduler as timeout task. This will handle the SILC<br />
+// client library every 50 milliseconds.  It will actually make the<br />
+// SILC client work on background.<br />
+Gnome::Main::timeout.connect(slot(this, &YourClass::silc_scheduler), 50);<br />
+</tt>
+
+<br />&nbsp;<br />
+This will call the function silc_scheduler every 50 millisecconds, which 
+on the otherhand will call silc_client_run_one, which will make the SILC 
+Client library work on the background of the GUI application.
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Creating Connection to Server</b>
+
+<br />&nbsp;<br />
+Connection to remote SILC server is done by calling:
+
+<br />&nbsp;<br />
+<tt>&nbsp;&nbsp;silc_client_connect_to_server(client, port, hostname, context);</tt>
+
+<br />&nbsp;<br />
+The function will create the connection asynchronously to the server, ie.
+the function will return before the actual connection is created.  After
+the connection is created the client->ops->connect operation is called.
+
+<br />&nbsp;<br />
+Generally speaking the connections are associated with windows' on the
+screen.  IRC is usually implemented this way, however it is not the
+necessary way to associate the client's connections.  SilcClientConnection
+object is provided by the library (and is always passed to the application)
+that can be used in the application to associate the connection from the
+library.  Application specific context can be saved to the 
+SilcClientConnection object which then can be retrieved in the application,
+thus perhaps associate the connection with what ever object in 
+application (window or something else).
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Using Own Connecting</b>
+
+<br />&nbsp;<br />
+Application might not want to use silc_client_connect_to_server function
+if it wants to perform its own connecting for some reason.  In this case
+application must call function silc_client_start_key_exchange after it
+has created the connection by itself.  This function starts the key
+exhange protocol between the client and server and the library takes care
+of everything after that.
+
+<br />&nbsp;<br />
+After connection has been created application must call:
+
+<br />&nbsp;<br />
+<tt>
+&nbsp;&nbsp;SilcClientConnection conn;
+
+<br />&nbsp;<br />
+&nbsp;&nbsp;/* Add new connection to client */<br />
+&nbsp;&nbsp;conn = silc_client_add_connection(client, hostname, port, context);
+
+<br />&nbsp;<br />
+&nbsp;&nbsp;/* Start key exchange and let the library handle everything<br />
+&nbsp;&nbsp;   after this point on. */<br />
+&nbsp;&nbsp;silc_client_start_key_exchange(client, conn, sock);
+</tt>
+
+<br />&nbsp;<br />
+NOTE: These calls are performed only and only if application did not call
+silc_client_connect_to_server function, but performed the connecting 
+process manually.
+
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Example Client</b>
+
+<br />&nbsp;<br />
+This section includes an example SILC client implementation in pseudo-like
+C code.  It creates and initializes the client and sets up an imaginary
+user interface.  The user will use the user interface then to create
+the connections.  The SilcClientOperations are expected to be implemented.
+
+<br />&nbsp;<br />
+<pre>
+#include "silcincludes.h"
+#include "silcapi.h"
+
+int main()
+{
+       SilcClientOperations ops = {
+         silc_say,
+         silc_channel_message,
+         silc_private_message,
+         silc_notify,
+         silc_command,
+         silc_command_reply,
+         silc_connect,
+         silc_disconnect,
+         silc_get_auth_method,
+         silc_verify_public_key,
+         silc_ask_passphrase,
+         silc_failure,
+         silc_key_agreement,
+       };
+
+       SilcClient client;
+
+       /* Allocate SILC client. The `silc_version_string' is defined
+          in includes/version.h file. */
+       client = silc_client_alloc(&ops, NULL, silc_version_string);
+
+       /* Register default ciphers, pkcs, hash funtions and hmacs. */
+       silc_cipher_register_default();
+       silc_pkcs_register_default();
+       silc_hash_register_default();
+       silc_hmac_register_default();
+
+       /* Set the mandatory pointers, read public and private key from
+          files (or somewhere) and return pointers and PKCS context. */
+       client->username = silc_get_username();
+       client->hostname = silc_net_localhost();
+       client->realname = silc_get_real_name();
+       client->pkcs = get_public_and_private_key(&client->public_key,
+                                                 &client->private_key);
+
+       /* If the keys does not exist, create a key pair since we must
+          provide key pair to the library. */
+       if (!client->pkcs)
+         generate_key_new_key_pair(client);
+
+       /* Iinitialize client */
+       if (!silc_client_init(client))
+         fatal_error("Could not initialize client");
+
+       /* Initialize user interface. The user interface can be generally
+          initialized at any phase, including before actually allocating
+          and initializing the client, if wished. */
+       InitUserInterface();
+       DoCoolThings();
+
+       /* Start the client. This will start the scheduler. At this phase
+          the user might have the user interface in front of him already.
+          He will use the user interface to create the connection to the
+          server for example. When this function returns the program is 
+         ended. */
+       silc_client_run(client);
+
+       /* Client is ended */
+       return 0;
+}
+</pre>
diff --git a/lib/doc/silcexample.h b/lib/doc/silcexample.h
new file mode 100644 (file)
index 0000000..38a1178
--- /dev/null
@@ -0,0 +1,68 @@
+/****h* silcexample/SilcExampleAPI
+ *
+ * DESCRIPTION
+ *
+ * This is example API providing the examples of how API items may appear
+ * in the Toolkit Reference Manual.  This example includes all aspects of
+ * a reference, however, note that all API items that appear in the manual
+ * may not include all of the information that are presented here.
+ *
+ ***/
+
+/****d* silcexample/SilcExampleAPI/SilcExampleType
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcExampleTyle;
+ *
+ * DESCRIPTION
+ *
+ *    Example type definition with the actual source code.
+ *
+ * SOURCE
+ */
+/* Source code from the actual header file is appended */
+typedef enum {
+  SILC_EXAMPLE_1,
+  SILC_EXAMPLE_2,
+  SILC_EXAMPLE_3,
+} SilcExampleType;
+/***/
+
+/****s* silcexample/SilcExampleAPI/SilcExampleStruct
+ *
+ * NAME
+ *
+ *    typedef struct { ... } SilcExampleStruct;
+ *
+ * DESCRIPTION
+ *
+ *    Example structure definition.
+ *
+ ***/
+
+/****f* silcexample/SilcExampleAPI/silc_example_function
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_example_function(SilcExampleType type);
+ *
+ * DESCRIPTION
+ *
+ *    Description of the silc_example_function.
+ *
+ * NOTES
+ *
+ *    There may be additional notes that programmers should be aware of
+ *    for this function.
+ *
+ * EXAMPLE
+ *
+ *    if (!silc_example_function(SILC_EXAMPLE_1))
+ *      SILC_LOG_ERROR(("Error occurred during example function"));
+ *
+ * SEE ALSO
+ *
+ *    SilcExampleType, SILC_LOG_ERROR, SilcExampleStruct
+ *
+ ***/
diff --git a/lib/doc/space.gif b/lib/doc/space.gif
new file mode 100644 (file)
index 0000000..e66849a
Binary files /dev/null and b/lib/doc/space.gif differ
diff --git a/lib/silcclient/DIRECTORY b/lib/silcclient/DIRECTORY
new file mode 100644 (file)
index 0000000..1975f78
--- /dev/null
@@ -0,0 +1,32 @@
+<!--
+@LIBRARY=SILC Client Library
+@FILENAME=silcclientlib.html
+@LINK=silcclient_using.html:Using SILC Client Library
+@LINK=silcapi.html:Client Library Interface
+-->
+
+<BIG><B>SILC Client Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC Client Library is SILC Client implementation without the actual user 
+interface. The library uses common and core components of SILC protocol from
+lib/silccore library and normal utility routines from lib/silcutil library.
+The library has been designed to be complete SILC Client implementation
+without actual user interface.  The library provides the API for the
+application which it can use to implement generally whatever user interface
+it wants.
+
+<BR /><BR />
+The `silcapi.h' file defines the function prototypes that application must
+implement in order to be able to create the user interface with the
+library.  The idea is that the application can implement whatever user
+interface routines in the functions and display the data whatever way
+it wants.  The library is entirely transparent to the user interface and
+it does not include any user interface specific issues such as window
+handling or item handling on the screen etc.  These does not interest
+the library.
+
+<BR /><BR />
+@LINKS@
similarity index 56%
rename from doc/Makefile.am
rename to lib/silcclient/Makefile.am
index 7f6fea86104f263e89122d107e72af8270e24e13..5dafa9749e1fe2d7fcc1f84e8ab84ad864fae414 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-all:
-       touch draft-riikonen-silc-spec-00.txt
-       touch draft-riikonen-silc-pp-00.txt
-       touch draft-riikonen-silc-ke-auth-00.txt
-       -cd ..
+noinst_LIBRARIES = libsilcclient.a
 
-dist-hook:
-       -rm -f draft-riikonen*.txt
-       ./makerfc draft-riikonen-silc-spec-00.nroff \
-               draft-riikonen-silc-spec-00.txt
-       ./makerfc draft-riikonen-silc-pp-00.nroff \
-               draft-riikonen-silc-pp-00.txt
-       ./makerfc draft-riikonen-silc-ke-auth-00.nroff \
-               draft-riikonen-silc-ke-auth-00.txt
+libsilcclient_a_SOURCES = \
+       client.c \
+       client_keyagr.c \
+       client_notify.c \
+       client_prvmsg.c \
+       client_channel.c \
+       client_ftp.c    \
+       command.c \
+       command_reply.c \
+       idlist.c \
+       protocol.c
 
-EXTRA_DIST = \
-       CodingStyle \
-       example_silcd.conf \
-       example_silc.conf \
-       draft-riikonen*.txt
+if SILC_DIST_TOOLKIT
+include_HEADERS=       \
+       client.h        \
+       command.h       \
+       command_reply.h \
+       idlist.h        \
+       protocol.h      \
+       silcapi.h
+endif
+
+EXTRA_DIST = *.h client_ops_example.c
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c
new file mode 100644 (file)
index 0000000..ddd1285
--- /dev/null
@@ -0,0 +1,1797 @@
+/*
+
+  client.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Static task callback prototypes */
+SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
+SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
+SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
+SILC_TASK_CALLBACK(silc_client_rekey_callback);
+SILC_TASK_CALLBACK(silc_client_rekey_final);
+
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context);
+static void silc_client_packet_parse_type(SilcClient client, 
+                                         SilcSocketConnection sock,
+                                         SilcPacketContext *packet);
+void silc_client_resolve_auth_method(bool success,
+                                    SilcProtocolAuthMeth auth_meth,
+                                    const unsigned char *auth_data,
+                                    uint32 auth_data_len, void *context);
+
+/* Allocates new client object. This has to be done before client may
+   work. After calling this one must call silc_client_init to initialize
+   the client. The `application' is application specific user data pointer
+   and caller must free it. */
+
+SilcClient silc_client_alloc(SilcClientOperations *ops, 
+                            SilcClientParams *params,
+                            void *application,
+                            const char *silc_version)
+{
+  SilcClient new_client;
+
+  new_client = silc_calloc(1, sizeof(*new_client));
+  new_client->application = application;
+
+  new_client->internal = silc_calloc(1, sizeof(*new_client->internal));
+  new_client->internal->ops = ops;
+  new_client->internal->params = 
+    silc_calloc(1, sizeof(*new_client->internal->params));
+  new_client->internal->silc_client_version = strdup(silc_version);
+
+  if (params)
+    memcpy(new_client->internal->params, params, sizeof(*params));
+
+  if (!new_client->internal->params->task_max)
+    new_client->internal->params->task_max = 200;
+
+  if (!new_client->internal->params->rekey_secs)
+    new_client->internal->params->rekey_secs = 3600;
+
+  if (!new_client->internal->params->connauth_request_secs)
+    new_client->internal->params->connauth_request_secs = 2;
+
+  new_client->internal->params->
+    nickname_format[sizeof(new_client->internal->
+                          params->nickname_format) - 1] = 0;
+
+  return new_client;
+}
+
+/* Frees client object and its internals. */
+
+void silc_client_free(SilcClient client)
+{
+  if (client) {
+    if (client->rng)
+      silc_rng_free(client->rng);
+
+    silc_free(client->internal->params);
+    silc_free(client->internal->silc_client_version);
+    silc_free(client->internal);
+    silc_free(client);
+  }
+}
+
+/* Initializes the client. This makes all the necessary steps to make
+   the client ready to be run. One must call silc_client_run to run the
+   client. Returns FALSE if error occured, TRUE otherwise. */
+
+int silc_client_init(SilcClient client)
+{
+  SILC_LOG_DEBUG(("Initializing client"));
+
+  /* Initialize hash functions for client to use */
+  silc_hash_alloc("md5", &client->internal->md5hash);
+  silc_hash_alloc("sha1", &client->internal->sha1hash);
+
+  /* Initialize none cipher */
+  silc_cipher_alloc("none", &client->internal->none_cipher);
+
+  /* Initialize random number generator */
+  client->rng = silc_rng_alloc();
+  silc_rng_init(client->rng);
+  silc_rng_global_init(client->rng);
+
+  /* Register protocols */
+  silc_client_protocols_register();
+
+  /* Initialize the scheduler */
+  client->schedule = 
+    silc_schedule_init(client->internal->params->task_max ?
+                      client->internal->params->task_max : 200);
+  if (!client->schedule)
+    return FALSE;
+
+  /* Register commands */
+  silc_client_commands_register(client);
+
+  return TRUE;
+}
+
+/* Stops the client. This is called to stop the client and thus to stop
+   the program. */
+
+void silc_client_stop(SilcClient client)
+{
+  SILC_LOG_DEBUG(("Stopping client"));
+
+  silc_schedule_stop(client->schedule);
+  silc_schedule_uninit(client->schedule);
+
+  silc_client_protocols_unregister();
+  silc_client_commands_unregister(client);
+
+  SILC_LOG_DEBUG(("Client stopped"));
+}
+
+/* Runs the client. This starts the scheduler from the utility library.
+   When this functions returns the execution of the appliation is over. */
+
+void silc_client_run(SilcClient client)
+{
+  SILC_LOG_DEBUG(("Running client"));
+
+  /* Start the scheduler, the heart of the SILC client. When this returns
+     the program will be terminated. */
+  silc_schedule(client->schedule);
+}
+
+/* Runs the client and returns immeadiately. This function is used when
+   the SILC Client object indicated by the `client' is run under some
+   other scheduler, or event loop or main loop.  On GUI applications,
+   for example this may be desired to use to run the client under the
+   GUI application's main loop.  Typically the GUI application would
+   register an idle task that calls this function multiple times in
+   a second to quickly process the SILC specific data. */
+
+void silc_client_run_one(SilcClient client)
+{
+  /* Run the scheduler once. */
+  silc_schedule_one(client->schedule, 0);
+}
+
+static void silc_client_entry_destructor(SilcIDCache cache,
+                                        SilcIDCacheEntry entry)
+{
+  silc_free(entry->name);
+}
+
+/* Allocates and adds new connection to the client. This adds the allocated
+   connection to the connection table and returns a pointer to it. A client
+   can have multiple connections to multiple servers. Every connection must
+   be added to the client using this function. User data `context' may
+   be sent as argument. This function is normally used only if the 
+   application performed the connecting outside the library. The library
+   however may use this internally. */
+
+SilcClientConnection silc_client_add_connection(SilcClient client,
+                                               char *hostname,
+                                               int port,
+                                               void *context)
+{
+  SilcClientConnection conn;
+  int i;
+
+  conn = silc_calloc(1, sizeof(*conn));
+
+  /* Initialize ID caches */
+  conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, 
+                                         silc_client_entry_destructor);
+  conn->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+  conn->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
+  conn->client = client;
+  conn->remote_host = strdup(hostname);
+  conn->remote_port = port;
+  conn->context = context;
+  conn->pending_commands = silc_dlist_init();
+  conn->ftp_sessions = silc_dlist_init();
+
+  /* Add the connection to connections table */
+  for (i = 0; i < client->internal->conns_count; i++)
+    if (client->internal->conns && !client->internal->conns[i]) {
+      client->internal->conns[i] = conn;
+      return conn;
+    }
+
+  client->internal->conns = 
+    silc_realloc(client->internal->conns, sizeof(*client->internal->conns)
+                * (client->internal->conns_count + 1));
+  client->internal->conns[client->internal->conns_count] = conn;
+  client->internal->conns_count++;
+
+  return conn;
+}
+
+/* Removes connection from client. Frees all memory. */
+
+void silc_client_del_connection(SilcClient client, SilcClientConnection conn)
+{
+  int i;
+
+  for (i = 0; i < client->internal->conns_count; i++)
+    if (client->internal->conns[i] == conn) {
+
+      silc_idcache_free(conn->client_cache);
+      silc_idcache_free(conn->channel_cache);
+      silc_idcache_free(conn->server_cache);
+      if (conn->pending_commands)
+       silc_dlist_uninit(conn->pending_commands);
+      silc_free(conn->remote_host);
+      silc_dlist_uninit(conn->ftp_sessions);
+      silc_free(conn);
+
+      client->internal->conns[i] = NULL;
+    }
+}
+
+/* Adds listener socket to the listener sockets table. This function is
+   used to add socket objects that are listeners to the client.  This should
+   not be used to add other connection objects. */
+
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->internal->sockets) {
+    client->internal->sockets = 
+      silc_calloc(1, sizeof(*client->internal->sockets));
+    client->internal->sockets[0] = silc_socket_dup(sock);
+    client->internal->sockets_count = 1;
+    return;
+  }
+
+  for (i = 0; i < client->internal->sockets_count; i++) {
+    if (client->internal->sockets[i] == NULL) {
+      client->internal->sockets[i] = silc_socket_dup(sock);
+      return;
+    }
+  }
+
+  client->internal->sockets = 
+    silc_realloc(client->internal->sockets, 
+                sizeof(*client->internal->sockets) *
+                (client->internal->sockets_count + 1));
+  client->internal->sockets[client->internal->sockets_count] = 
+    silc_socket_dup(sock);
+  client->internal->sockets_count++;
+}
+
+/* Deletes listener socket from the listener sockets table. */
+
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock)
+{
+  int i;
+
+  if (!client->internal->sockets)
+    return;
+
+  for (i = 0; i < client->internal->sockets_count; i++) {
+    if (client->internal->sockets[i] == sock) {
+      silc_socket_free(sock);
+      client->internal->sockets[i] = NULL;
+      return;
+    }
+  }
+}
+
+static int 
+silc_client_connect_to_server_internal(SilcClientInternalConnectContext *ctx)
+{
+  int sock;
+
+  /* XXX In the future we should give up this non-blocking connect all
+     together and use threads instead. */
+  /* Create connection to server asynchronously */
+  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
+  if (sock < 0)
+    return -1;
+
+  /* Register task that will receive the async connect and will
+     read the result. */
+  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
+                                    silc_client_connect_to_server_start,
+                                    (void *)ctx, 0, 0, 
+                                    SILC_TASK_FD,
+                                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
+
+  ctx->sock = sock;
+
+  return sock;
+}
+
+/* Connects to remote server. This is the main routine used to connect
+   to SILC server. Returns -1 on error and the created socket otherwise. 
+   The `context' is user context that is saved into the SilcClientConnection
+   that is created after the connection is created. Note that application
+   may handle the connecting process outside the library. If this is the
+   case then this function is not used at all. When the connecting is
+   done the `connect' client operation is called. */
+
+int silc_client_connect_to_server(SilcClient client, int port,
+                                 char *host, void *context)
+{
+  SilcClientInternalConnectContext *ctx;
+  SilcClientConnection conn;
+  int sock;
+
+  SILC_LOG_DEBUG(("Connecting to port %d of server %s",
+                 port, host));
+
+  conn = silc_client_add_connection(client, host, port, context);
+
+  client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                            "Connecting to port %d of server %s", port, host);
+
+  /* Allocate internal context for connection process. This is
+     needed as we are doing async connecting. */
+  ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->client = client;
+  ctx->conn = conn;
+  ctx->host = strdup(host);
+  ctx->port = port;
+  ctx->tries = 0;
+
+  /* Do the actual connecting process */
+  sock = silc_client_connect_to_server_internal(ctx);
+  if (sock == -1)
+    silc_client_del_connection(client, conn);
+  return sock;
+}
+
+/* Socket hostname and IP lookup callback that is called before actually
+   starting the key exchange.  The lookup is called from the function
+   silc_client_start_key_exchange. */
+
+static void silc_client_start_key_exchange_cb(SilcSocketConnection sock,
+                                             void *context)
+{
+  SilcClientConnection conn = (SilcClientConnection)context;
+  SilcClient client = conn->client;
+  SilcProtocol protocol;
+  SilcClientKEInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* XXX We should most likely use the resolved host name instead of the
+     one user provided for us. */
+  silc_free(conn->sock->hostname);
+  conn->sock->hostname = strdup(conn->remote_host);
+  if (!conn->sock->ip)
+    conn->sock->ip = strdup(conn->remote_host);
+  conn->sock->port = conn->remote_port;
+
+  /* Allocate internal Key Exchange context. This is sent to the
+     protocol as context. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = (void *)client;
+  proto_ctx->sock = silc_socket_dup(conn->sock);
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = FALSE;
+  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+
+  /* Perform key exchange protocol. silc_client_connect_to_server_final
+     will be called after the protocol is finished. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &protocol, (void *)proto_ctx,
+                     silc_client_connect_to_server_second);
+  if (!protocol) {
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                              "Error: Could not start key exchange protocol");
+    silc_net_close_connection(conn->sock->sock);
+    client->internal->ops->connect(client, conn, FALSE);
+    return;
+  }
+  conn->sock->protocol = protocol;
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it will 
+     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)client;
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(conn->sock->sock);
+
+  /* Execute the protocol */
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
+}
+
+/* Start SILC Key Exchange (SKE) protocol to negotiate shared secret
+   key material between client and server.  This function can be called
+   directly if application is performing its own connecting and does not
+   use the connecting provided by this library. This function is normally
+   used only if the application performed the connecting outside the library.
+   The library however may use this internally. */
+
+void silc_client_start_key_exchange(SilcClient client,
+                                   SilcClientConnection conn,
+                                   int fd)
+{
+  /* Allocate new socket connection object */
+  silc_socket_alloc(fd, SILC_SOCKET_TYPE_SERVER, (void *)conn, &conn->sock);
+
+  /* Sometimes when doing quick reconnects the new socket may be same as
+     the old one and there might be pending stuff for the old socket. 
+     If new one is same then those pending sutff might cause problems.
+     Make sure they do not do that. */
+  silc_schedule_task_del_by_fd(client->schedule, fd);
+
+  conn->nickname = (client->nickname ? strdup(client->nickname) :
+                   strdup(client->username));
+
+  /* Resolve the remote hostname and IP address for our socket connection */
+  silc_socket_host_lookup(conn->sock, FALSE, silc_client_start_key_exchange_cb,
+                         conn, client->schedule);
+}
+
+/* Callback called when error has occurred during connecting to the server.
+   The `connect' client operation will be called. */
+
+SILC_TASK_CALLBACK(silc_client_connect_failure)
+{
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  client->internal->ops->connect(client, ctx->sock->user_data, FALSE);
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  silc_free(ctx);
+}
+
+/* Start of the connection to the remote server. This is called after
+   succesful TCP/IP connection has been established to the remote host. */
+
+SILC_TASK_CALLBACK(silc_client_connect_to_server_start)
+{
+  SilcClientInternalConnectContext *ctx =
+    (SilcClientInternalConnectContext *)context;
+  SilcClient client = ctx->client;
+  SilcClientConnection conn = ctx->conn;
+  int opt, opt_len = sizeof(opt);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Check the socket status as it might be in error */
+  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  if (opt != 0) {
+    if (ctx->tries < 2) {
+      /* Connection failed but lets try again */
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to server %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of server %s resumed", 
+                                ctx->port, ctx->host);
+
+      /* Unregister old connection try */
+      silc_schedule_unset_listen_fd(client->schedule, fd);
+      silc_net_close_connection(fd);
+      silc_schedule_task_del(client->schedule, ctx->task);
+
+      /* Try again */
+      silc_client_connect_to_server_internal(ctx);
+      ctx->tries++;
+    } else {
+      /* Connection failed and we won't try anymore */
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to server %s: %s",
+                                ctx->host, strerror(opt));
+      silc_schedule_unset_listen_fd(client->schedule, fd);
+      silc_net_close_connection(fd);
+      silc_schedule_task_del(client->schedule, ctx->task);
+      silc_free(ctx);
+
+      /* Notify application of failure */
+      client->internal->ops->connect(client, conn, FALSE);
+      silc_client_del_connection(client, conn);
+    }
+    return;
+  }
+
+  silc_schedule_unset_listen_fd(client->schedule, fd);
+  silc_schedule_task_del(client->schedule, ctx->task);
+  silc_free(ctx);
+
+  silc_client_start_key_exchange(client, conn, fd);
+}
+
+/* Second part of the connecting to the server. This executed 
+   authentication protocol. */
+
+SILC_TASK_CALLBACK(silc_client_connect_to_server_second)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcSocketConnection sock = NULL;
+  SilcClientConnAuthInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    SILC_LOG_DEBUG(("Error during KE protocol"));
+    silc_protocol_free(protocol);
+    silc_ske_free_key_material(ctx->keymat);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    if (ctx->dest_id)
+      silc_free(ctx->dest_id);
+    ctx->sock->protocol = NULL;
+    silc_socket_free(ctx->sock);
+
+    /* Notify application of failure */
+    silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                          silc_client_connect_failure, ctx,
+                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* We now have the key material as the result of the key exchange
+     protocol. Take the key material into use. Free the raw key material
+     as soon as we've set them into use. */
+  silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+                                  ctx->ske->prop->cipher,
+                                  ctx->ske->prop->pkcs,
+                                  ctx->ske->prop->hash,
+                                  ctx->ske->prop->hmac,
+                                  ctx->ske->prop->group,
+                                  ctx->responder);
+  silc_ske_free_key_material(ctx->keymat);
+
+  /* Allocate internal context for the authentication protocol. This
+     is sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = (void *)client;
+  proto_ctx->sock = sock = ctx->sock;
+  proto_ctx->ske = ctx->ske;   /* Save SKE object from previous protocol */
+  proto_ctx->dest_id_type = ctx->dest_id_type;
+  proto_ctx->dest_id = ctx->dest_id;
+
+  /* Free old protocol as it is finished now */
+  silc_protocol_free(protocol);
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  silc_free(ctx);
+  sock->protocol = NULL;
+
+  /* Resolve the authentication method to be used in this connection. The
+     completion callback is called after the application has resolved
+     the authentication method. */
+  client->internal->ops->get_auth_method(client, sock->user_data, 
+                                        sock->hostname,
+                                        sock->port, 
+                                        silc_client_resolve_auth_method,
+                                        proto_ctx);
+}
+
+/* Authentication method resolving callback. Application calls this function
+   after we've called the client->internal->ops->get_auth_method 
+   client operation to resolve the authentication method. We will continue
+   the executiong of the protocol in this function. */
+
+void silc_client_resolve_auth_method(bool success,
+                                    SilcProtocolAuthMeth auth_meth,
+                                    const unsigned char *auth_data,
+                                    uint32 auth_data_len, void *context)
+{
+  SilcClientConnAuthInternalContext *proto_ctx =
+    (SilcClientConnAuthInternalContext *)context;
+  SilcClient client = (SilcClient)proto_ctx->client;
+
+  if (!success)
+    auth_meth = SILC_AUTH_NONE;
+
+  proto_ctx->auth_meth = auth_meth;
+
+  if (auth_data && auth_data_len) {
+    proto_ctx->auth_data = silc_calloc(auth_data_len, sizeof(*auth_data));
+    memcpy(proto_ctx->auth_data, auth_data, auth_data_len);
+    proto_ctx->auth_data_len = auth_data_len;
+  }
+
+  /* Allocate the authenteication protocol and execute it. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH, 
+                     &proto_ctx->sock->protocol, (void *)proto_ctx, 
+                     silc_client_connect_to_server_final);
+
+  /* Execute the protocol */
+  silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
+}
+
+/* Finalizes the connection to the remote SILC server. This is called
+   after authentication protocol has been completed. This send our
+   user information to the server to receive our client ID from
+   server. */
+
+SILC_TASK_CALLBACK(silc_client_connect_to_server_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientConnAuthInternalContext *ctx = 
+    (SilcClientConnAuthInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcBuffer packet;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    SILC_LOG_DEBUG(("Error during authentication protocol"));
+    silc_protocol_free(protocol);
+    if (ctx->auth_data)
+      silc_free(ctx->auth_data);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    if (ctx->dest_id)
+      silc_free(ctx->dest_id);
+    conn->sock->protocol = NULL;
+    silc_socket_free(ctx->sock);
+
+    /* Notify application of failure */
+    silc_schedule_task_add(client->schedule, ctx->sock->sock,
+                          silc_client_connect_failure, ctx,
+                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* Send NEW_CLIENT packet to the server. We will become registered
+     to the SILC network after sending this packet and we will receive
+     client ID from the server. */
+  packet = silc_buffer_alloc(2 + 2 + strlen(client->username) + 
+                            strlen(client->realname));
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(strlen(client->username)),
+                    SILC_STR_UI_XNSTRING(client->username,
+                                         strlen(client->username)),
+                    SILC_STR_UI_SHORT(strlen(client->realname)),
+                    SILC_STR_UI_XNSTRING(client->realname,
+                                         strlen(client->realname)),
+                    SILC_STR_END);
+
+  /* Send the packet */
+  silc_client_packet_send(client, ctx->sock, SILC_PACKET_NEW_CLIENT,
+                         NULL, 0, NULL, NULL, 
+                         packet->data, packet->len, TRUE);
+  silc_buffer_free(packet);
+
+  /* Save remote ID. */
+  conn->remote_id = ctx->dest_id;
+  conn->remote_id_data = silc_id_id2str(ctx->dest_id, SILC_ID_SERVER);
+  conn->remote_id_data_len = silc_id_get_len(ctx->dest_id, SILC_ID_SERVER);
+
+  /* Register re-key timeout */
+  conn->rekey->timeout = client->internal->params->rekey_secs;
+  conn->rekey->context = (void *)client;
+  silc_schedule_task_add(client->schedule, conn->sock->sock, 
+                        silc_client_rekey_callback,
+                        (void *)conn->sock, conn->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  silc_protocol_free(protocol);
+  if (ctx->auth_data)
+    silc_free(ctx->auth_data);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_socket_free(ctx->sock);
+  silc_free(ctx);
+  conn->sock->protocol = NULL;
+}
+
+/* Internal routine that sends packet or marks packet to be sent. This
+   is used directly only in special cases. Normal cases should use
+   silc_server_packet_send. Returns < 0 on error. */
+
+int silc_client_packet_send_real(SilcClient client,
+                                SilcSocketConnection sock,
+                                bool force_send)
+{
+  int ret;
+
+  /* If rekey protocol is active we must assure that all packets are
+     sent through packet queue. */
+  if (SILC_CLIENT_IS_REKEY(sock))
+    force_send = FALSE;
+
+  /* If outbound data is already pending do not force send */
+  if (SILC_IS_OUTBUF_PENDING(sock))
+    force_send = FALSE;
+
+  /* Send the packet */
+  ret = silc_packet_send(sock, force_send);
+  if (ret != -2)
+    return ret;
+
+  /* Mark that there is some outgoing data available for this connection. 
+     This call sets the connection both for input and output (the input
+     is set always and this call keeps the input setting, actually). 
+     Actual data sending is performed by silc_client_packet_process. */
+  SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(client->schedule, sock->sock);
+
+  /* Mark to socket that data is pending in outgoing buffer. This flag
+     is needed if new data is added to the buffer before the earlier
+     put data is sent to the network. */
+  SILC_SET_OUTBUF_PENDING(sock);
+
+  return 0;
+}
+
+/* Packet processing callback. This is used to send and receive packets
+   from network. This is generic task. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process)
+{
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock = NULL;
+  SilcClientConnection conn;
+  int ret;
+
+  SILC_LOG_DEBUG(("Processing packet"));
+
+  SILC_CLIENT_GET_SOCK(client, fd, sock);
+  if (sock == NULL)
+    return;
+
+  conn = (SilcClientConnection)sock->user_data;
+
+  /* Packet sending */
+  if (type == SILC_TASK_WRITE) {
+    /* Do not send data to disconnected connection */
+    if (SILC_IS_DISCONNECTED(sock))
+      return;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    ret = silc_packet_send(sock, TRUE);
+
+    /* If returned -2 could not write to connection now, will do
+       it later. */
+    if (ret == -2)
+      return;
+
+    /* Error */
+    if (ret == -1)
+      return;
+    
+    /* The packet has been sent and now it is time to set the connection
+       back to only for input. When there is again some outgoing data 
+       available for this connection it will be set for output as well. 
+       This call clears the output setting and sets it only for input. */
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, fd);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+
+    silc_buffer_clear(sock->outbuf);
+    return;
+  }
+
+  /* Packet receiving */
+  if (type == SILC_TASK_READ) {
+    /* Read data from network */
+    ret = silc_packet_receive(sock);
+    if (ret < 0)
+      return;
+    
+    /* EOF */
+    if (ret == 0) {
+      SILC_LOG_DEBUG(("Read EOF"));
+
+      /* If connection is disconnecting already we will finally
+        close the connection */
+      if (SILC_IS_DISCONNECTING(sock)) {
+       if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
+         client->internal->ops->disconnect(client, conn);
+       silc_client_close_connection(client, sock, conn);
+       return;
+      }
+      
+      SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
+      if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
+       client->internal->ops->disconnect(client, conn);
+      silc_client_close_connection(client, sock, conn);
+      return;
+    }
+
+    /* Process the packet. This will call the parser that will then
+       decrypt and parse the packet. */
+    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+      silc_packet_receive_process(sock, FALSE, conn->receive_key, 
+                                 conn->hmac_receive, conn->psn_receive,
+                                 silc_client_packet_parse, client);
+    else
+      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, 
+                                 silc_client_packet_parse, client);
+  }
+}
+
+/* Parser callback called by silc_packet_receive_process. Thie merely
+   registers timeout that will handle the actual parsing when appropriate. */
+
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+                                    void *context)
+{
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock = parser_context->sock;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcPacketContext *packet = parser_context->packet;
+  SilcPacketType ret;
+
+  if (conn && conn->hmac_receive && conn->sock == sock)
+    conn->psn_receive = parser_context->packet->sequence + 1;
+
+  /* Parse the packet immediately */
+  if (parser_context->normal)
+    ret = silc_packet_parse(packet, conn->receive_key);
+  else
+    ret = silc_packet_parse_special(packet, conn->receive_key);
+
+  if (ret == SILC_PACKET_NONE) {
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+    return FALSE;
+  }
+  
+  /* If protocol for this connection is key exchange or rekey then we'll
+     process all packets synchronously, since there might be packets in
+     queue that we are not able to decrypt without first processing the
+     packets before them. */
+  if ((ret == SILC_PACKET_REKEY || ret == SILC_PACKET_REKEY_DONE) ||
+      (sock->protocol && sock->protocol->protocol && 
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY))) {
+
+    /* Parse the incoming packet type */
+    silc_client_packet_parse_type(client, sock, packet);
+    silc_packet_context_free(packet);
+    silc_free(parser_context);
+
+    /* Reprocess the buffer since we'll return FALSE. This is because
+       the `conn->receive_key' might have become valid by processing
+       the previous packet */
+    if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+      silc_packet_receive_process(sock, FALSE, conn->receive_key, 
+                                 conn->hmac_receive, conn->psn_receive,
+                                 silc_client_packet_parse, client);
+    else
+      silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, 
+                                 silc_client_packet_parse, client);
+    
+    return FALSE;
+  }
+
+  /* Parse the incoming packet type */
+  silc_client_packet_parse_type(client, sock, packet);
+  silc_packet_context_free(packet);
+  silc_free(parser_context);
+  return TRUE;
+}
+
+/* Parses the packet type and calls what ever routines the packet type
+   requires. This is done for all incoming packets. */
+
+void silc_client_packet_parse_type(SilcClient client, 
+                                  SilcSocketConnection sock,
+                                  SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcPacketType type = packet->type;
+
+  SILC_LOG_DEBUG(("Parsing packet type %d", type));
+
+  /* Parse the packet type */
+  switch(type) {
+  case SILC_PACKET_DISCONNECT:
+    silc_client_disconnected_by_server(client, sock, buffer);
+    break;
+  case SILC_PACKET_SUCCESS:
+    /*
+     * Success received for something. For now we can have only
+     * one protocol for connection executing at once hence this
+     * success message is for whatever protocol is executing currently.
+     */
+    if (sock->protocol)
+      silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+    break;
+  case SILC_PACKET_FAILURE:
+    /*
+     * Failure received for some protocol. Set the protocol state to 
+     * error and call the protocol callback. This fill cause error on
+     * protocol and it will call the final callback.
+     */
+    silc_client_process_failure(client, sock, packet);
+    break;
+  case SILC_PACKET_REJECT:
+    break;
+
+  case SILC_PACKET_NOTIFY:
+    /*
+     * Received notify message 
+     */
+    silc_client_notify_by_server(client, sock, packet);
+    break;
+
+  case SILC_PACKET_ERROR:
+    /*
+     * Received error message
+     */
+    silc_client_error_by_server(client, sock, buffer);
+    break;
+
+  case SILC_PACKET_CHANNEL_MESSAGE:
+    /*
+     * Received message to (from, actually) a channel
+     */
+    silc_client_channel_message(client, sock, packet);
+    break;
+  case SILC_PACKET_CHANNEL_KEY:
+    /*
+     * Received key for a channel. By receiving this key the client will be
+     * able to talk to the channel it has just joined. This can also be
+     * a new key for existing channel as keys expire peridiocally.
+     */
+    silc_client_receive_channel_key(client, sock, buffer);
+    break;
+
+  case SILC_PACKET_PRIVATE_MESSAGE:
+    /*
+     * Received private message
+     */
+    silc_client_private_message(client, sock, packet);
+    break;
+  case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+    /*
+     * Received private message key
+     */
+    break;
+
+  case SILC_PACKET_COMMAND_REPLY:
+    /*
+     * Recived reply for a command
+     */
+    silc_client_command_reply_process(client, sock, packet);
+    break;
+
+  case SILC_PACKET_KEY_EXCHANGE:
+    if (sock->protocol && sock->protocol->protocol && 
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
+      SilcClientKEInternalContext *proto_ctx = 
+       (SilcClientKEInternalContext *)sock->protocol->context;
+
+      proto_ctx->packet = silc_packet_context_dup(packet);
+      proto_ctx->dest_id_type = packet->src_id_type;
+      proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                         packet->src_id_type);
+      if (!proto_ctx->dest_id)
+       break;
+
+      /* Let the protocol handle the packet */
+      silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+    } else {
+      SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
+                     "protocol active, packet dropped."));
+    }
+    break;
+
+  case SILC_PACKET_KEY_EXCHANGE_1:
+    if (sock->protocol && sock->protocol->protocol && 
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+       SilcClientRekeyInternalContext *proto_ctx = 
+         (SilcClientRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+      } else {
+       SilcClientKEInternalContext *proto_ctx = 
+         (SilcClientKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+       proto_ctx->dest_id_type = packet->src_id_type;
+       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
+       
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+      }
+    } else {
+      SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
+                     "protocol active, packet dropped."));
+    }
+    break;
+  case SILC_PACKET_KEY_EXCHANGE_2:
+    if (sock->protocol && sock->protocol->protocol && 
+       (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+        sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)) {
+
+      if (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+       SilcClientRekeyInternalContext *proto_ctx = 
+         (SilcClientRekeyInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+       
+       proto_ctx->packet = silc_packet_context_dup(packet);
+
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+      } else {
+       SilcClientKEInternalContext *proto_ctx = 
+         (SilcClientKEInternalContext *)sock->protocol->context;
+       
+       if (proto_ctx->packet)
+         silc_packet_context_free(proto_ctx->packet);
+        if (proto_ctx->dest_id)
+          silc_free(proto_ctx->dest_id);
+       proto_ctx->packet = silc_packet_context_dup(packet);
+       proto_ctx->dest_id_type = packet->src_id_type;
+       proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                                           packet->src_id_type);
+       if (!proto_ctx->dest_id)
+         break;
+       
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+      }
+    } else {
+      SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
+                     "protocol active, packet dropped."));
+    }
+    break;
+
+  case SILC_PACKET_NEW_ID:
+    {
+      /*
+       * Received new ID from server. This packet is received at
+       * the connection to the server.  New ID is also received when 
+       * user changes nickname but in that case the new ID is received
+       * as command reply and not as this packet type.
+       */
+      SilcIDPayload idp;
+
+      idp = silc_id_payload_parse(buffer->data, buffer->len);
+      if (!idp)
+       break;
+      if (silc_id_payload_get_type(idp) != SILC_ID_CLIENT)
+       break;
+
+      silc_client_receive_new_id(client, sock, idp);
+      silc_id_payload_free(idp);
+      break;
+    }
+
+  case SILC_PACKET_HEARTBEAT:
+    /*
+     * Received heartbeat packet
+     */
+    SILC_LOG_DEBUG(("Heartbeat packet"));
+    break;
+
+  case SILC_PACKET_KEY_AGREEMENT:
+    /*
+     * Received key agreement packet
+     */
+    SILC_LOG_DEBUG(("Key agreement packet"));
+    silc_client_key_agreement(client, sock, packet);
+    break;
+
+  case SILC_PACKET_REKEY:
+    SILC_LOG_DEBUG(("Re-key packet"));
+    /* We ignore this for now */
+    break;
+
+  case SILC_PACKET_REKEY_DONE:
+    SILC_LOG_DEBUG(("Re-key done packet"));
+
+    if (sock->protocol && sock->protocol->protocol && 
+       sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY) {
+
+      SilcClientRekeyInternalContext *proto_ctx = 
+       (SilcClientRekeyInternalContext *)sock->protocol->context;
+      
+      if (proto_ctx->packet)
+       silc_packet_context_free(proto_ctx->packet);
+      
+      proto_ctx->packet = silc_packet_context_dup(packet);
+
+      /* Let the protocol handle the packet */
+      if (proto_ctx->responder == FALSE)
+       silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+      else
+       /* Let the protocol handle the packet */
+       silc_protocol_execute(sock->protocol, client->schedule, 
+                             0, 100000);
+    } else {
+      SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+                     "protocol active, packet dropped."));
+    }
+    break;
+
+  case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+    /*
+     * Reveived reply to our connection authentication method request
+     * packet. This is used to resolve the authentication method for the
+     * current session from the server if the client does not know it.
+     */
+    silc_client_connection_auth_request(client, sock, packet);
+    break;
+
+  case SILC_PACKET_FTP:
+    /* Received file transfer packet. */
+    silc_client_ftp(client, sock, packet);
+    break;
+
+  default:
+    SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
+    break;
+  }
+}
+
+/* Sends packet. This doesn't actually send the packet instead it assembles
+   it and marks it to be sent. However, if force_send is TRUE the packet
+   is sent immediately. if dst_id, cipher and hmac are NULL those parameters
+   will be derived from sock argument. Otherwise the valid arguments sent
+   are used. */
+
+void silc_client_packet_send(SilcClient client, 
+                            SilcSocketConnection sock,
+                            SilcPacketType type, 
+                            void *dst_id,
+                            SilcIdType dst_id_type,
+                            SilcCipher cipher,
+                            SilcHmac hmac,
+                            unsigned char *data, 
+                            uint32 data_len, 
+                            int force_send)
+{
+  SilcPacketContext packetdata;
+  int block_len;
+  uint32 sequence = 0;
+
+  if (!sock)
+    return;
+
+  SILC_LOG_DEBUG(("Sending packet, type %d", type));
+
+  /* Get data used in the packet sending, keys and stuff */
+  if ((!cipher || !hmac || !dst_id) && sock->user_data) {
+    if (!cipher && ((SilcClientConnection)sock->user_data)->send_key)
+      cipher = ((SilcClientConnection)sock->user_data)->send_key;
+
+    if (!hmac && ((SilcClientConnection)sock->user_data)->hmac_send)
+      hmac = ((SilcClientConnection)sock->user_data)->hmac_send;
+
+    if (!dst_id && ((SilcClientConnection)sock->user_data)->remote_id) {
+      dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
+      dst_id_type = SILC_ID_SERVER;
+    }
+
+    if (hmac)
+      sequence = ((SilcClientConnection)sock->user_data)->psn_send++;
+  }
+
+  block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+
+  /* Set the packet context pointers */
+  packetdata.flags = 0;
+  packetdata.type = type;
+  if (sock->user_data && 
+      ((SilcClientConnection)sock->user_data)->local_id_data) {
+    packetdata.src_id = ((SilcClientConnection)sock->user_data)->local_id_data;
+    packetdata.src_id_len = 
+      silc_id_get_len(((SilcClientConnection)sock->user_data)->local_id,
+                     SILC_ID_CLIENT);
+  } else { 
+    packetdata.src_id = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char));
+    packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+  }
+  packetdata.src_id_type = SILC_ID_CLIENT;
+  if (dst_id) {
+    packetdata.dst_id = silc_id_id2str(dst_id, dst_id_type);
+    packetdata.dst_id_len = silc_id_get_len(dst_id, dst_id_type);
+    packetdata.dst_id_type = dst_id_type;
+  } else {
+    packetdata.dst_id = NULL;
+    packetdata.dst_id_len = 0;
+    packetdata.dst_id_type = SILC_ID_NONE;
+  }
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          data_len);
+
+  SILC_LOG_DEBUG(("Putting data to outgoing buffer, len %d", data_len));
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the data to the buffer */
+  if (data && data_len)
+    silc_buffer_put(sock->outbuf, data, data_len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata, cipher);
+
+  /* Encrypt the packet */
+  if (cipher)
+    silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf, 
+                       sock->outbuf->len);
+
+  SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_client_packet_send_real(client, sock, force_send);
+}
+
+void silc_client_packet_queue_purge(SilcClient client,
+                                   SilcSocketConnection sock)
+{
+  if (sock && SILC_IS_OUTBUF_PENDING(sock) && 
+      (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_packet_send(sock, TRUE);
+
+    SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+}
+
+/* Closes connection to remote end. Free's all allocated data except
+   for some information such as nickname etc. that are valid at all time. 
+   If the `sock' is NULL then the conn->sock will be used.  If `sock' is
+   provided it will be checked whether the sock and `conn->sock' are the
+   same (they can be different, ie. a socket can use `conn' as its
+   connection but `conn->sock' might be actually a different connection
+   than the `sock'). */
+
+void silc_client_close_connection(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcClientConnection conn)
+{
+  int del = FALSE;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!sock || (sock && conn->sock == sock))
+    del = TRUE;
+  if (!sock)
+    sock = conn->sock;
+
+  /* We won't listen for this connection anymore */
+  silc_schedule_unset_listen_fd(client->schedule, sock->sock);
+
+  /* Unregister all tasks */
+  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
+  silc_schedule_task_del_by_fd(client->schedule, sock->sock);
+
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+
+  /* Cancel any active protocol */
+  if (sock->protocol) {
+    if (sock->protocol->protocol->type == 
+       SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+       sock->protocol->protocol->type == 
+       SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, client->schedule);
+      /* The application will recall this function with these protocols
+        (the ops->connect client operation). */
+      return;
+    } else {
+      sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute_final(sock->protocol, client->schedule);
+      sock->protocol = NULL;
+    }
+  }
+
+  /* Free everything */
+  if (del && sock->user_data) {
+    /* Free all cache entries */
+    SilcIDCacheList list;
+    SilcIDCacheEntry entry;
+    bool ret;
+
+    if (silc_idcache_get_all(conn->client_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_client(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
+      }
+      silc_idcache_list_free(list);
+    }
+
+    if (silc_idcache_get_all(conn->channel_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_channel(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
+      }
+      silc_idcache_list_free(list);
+    }
+
+    if (silc_idcache_get_all(conn->server_cache, &list)) {
+      ret = silc_idcache_list_first(list, &entry);
+      while (ret) {
+       silc_client_del_server(client, conn, entry->context);
+       ret = silc_idcache_list_next(list, &entry);
+      }
+      silc_idcache_list_free(list);
+    }
+
+    /* Clear ID caches */
+    if (conn->client_cache)
+      silc_idcache_free(conn->client_cache);
+    if (conn->channel_cache)
+      silc_idcache_free(conn->channel_cache);
+    if (conn->server_cache)
+      silc_idcache_free(conn->server_cache);
+
+    /* Free data (my ID is freed in above silc_client_del_client).
+       conn->nickname is freed when freeing the local_entry->nickname. */
+    if (conn->remote_host)
+      silc_free(conn->remote_host);
+    if (conn->local_id_data)
+      silc_free(conn->local_id_data);
+    if (conn->send_key)
+      silc_cipher_free(conn->send_key);
+    if (conn->receive_key)
+      silc_cipher_free(conn->receive_key);
+    if (conn->hmac_send)
+      silc_hmac_free(conn->hmac_send);
+    if (conn->hmac_receive)
+      silc_hmac_free(conn->hmac_receive);
+    if (conn->pending_commands)
+      silc_dlist_uninit(conn->pending_commands);
+    if (conn->rekey)
+      silc_free(conn->rekey);
+
+    if (conn->active_session) {
+      sock->user_data = NULL;
+      silc_client_ftp_session_free(conn->active_session);
+      conn->active_session = NULL;
+    }
+
+    silc_client_ftp_free_sessions(client, conn);
+
+    memset(conn, 0, sizeof(*conn));
+    silc_client_del_connection(client, conn);
+  }
+
+  silc_socket_free(sock);
+}
+
+/* Called when we receive disconnection packet from server. This 
+   closes our end properly and displays the reason of the disconnection
+   on the screen. */
+
+SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
+{
+  SilcClient client = (SilcClient)context;
+  SilcSocketConnection sock;
+
+  SILC_CLIENT_GET_SOCK(client, fd, sock);
+  if (sock == NULL)
+    return;
+
+  silc_client_close_connection(client, sock, sock->user_data);
+}
+
+/* Called when we receive disconnection packet from server. This 
+   closes our end properly and displays the reason of the disconnection
+   on the screen. */
+
+void silc_client_disconnected_by_server(SilcClient client,
+                                       SilcSocketConnection sock,
+                                       SilcBuffer message)
+{
+  char *msg;
+
+  SILC_LOG_DEBUG(("Server disconnected us, sock %d", sock->sock));
+
+  msg = silc_calloc(message->len + 1, sizeof(char));
+  memcpy(msg, message->data, message->len);
+  client->internal->ops->say(client, sock->user_data, 
+                            SILC_CLIENT_MESSAGE_AUDIT, msg);
+  silc_free(msg);
+
+  SILC_SET_DISCONNECTED(sock);
+
+  /* Close connection through scheduler. */
+  silc_schedule_task_add(client->schedule, sock->sock, 
+                        silc_client_disconnected_by_server_later,
+                        client, 0, 1, SILC_TASK_TIMEOUT, 
+                        SILC_TASK_PRI_NORMAL);
+}
+
+/* Received error message from server. Display it on the screen. 
+   We don't take any action what so ever of the error message. */
+
+void silc_client_error_by_server(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcBuffer message)
+{
+  char *msg;
+
+  msg = silc_calloc(message->len + 1, sizeof(char));
+  memcpy(msg, message->data, message->len);
+  client->internal->ops->say(client, sock->user_data, 
+                            SILC_CLIENT_MESSAGE_AUDIT, msg);
+  silc_free(msg);
+}
+
+/* Processes the received new Client ID from server. Old Client ID is
+   deleted from cache and new one is added. */
+
+void silc_client_receive_new_id(SilcClient client,
+                               SilcSocketConnection sock,
+                               SilcIDPayload idp)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  int connecting = FALSE;
+  SilcClientID *client_id = silc_id_payload_get_id(idp);
+  SilcBuffer sidp;
+
+  if (!conn->local_entry)
+    connecting = TRUE;
+
+  /* Delete old ID from ID cache */
+  if (conn->local_id) {
+    /* Check whether they are different */
+    if (SILC_ID_CLIENT_COMPARE(conn->local_id, client_id)) {
+      silc_free(client_id);
+      return;
+    }
+
+    silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
+    silc_free(conn->local_id);
+  }
+  
+  /* Save the new ID */
+
+  if (conn->local_id_data)
+    silc_free(conn->local_id_data);
+
+  conn->local_id = client_id;
+  conn->local_id_data = silc_id_payload_get_data(idp);
+  conn->local_id_data_len = silc_id_payload_get_len(idp);;
+
+  if (!conn->local_entry)
+    conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
+
+  conn->local_entry->nickname = conn->nickname;
+  if (!conn->local_entry->username)
+    conn->local_entry->username = strdup(client->username);
+  if (!conn->local_entry->hostname)
+    conn->local_entry->hostname = strdup(client->hostname);
+  conn->local_entry->server = strdup(conn->remote_host);
+  conn->local_entry->id = conn->local_id;
+  conn->local_entry->valid = TRUE;
+  conn->local_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, 
+                                                     NULL, NULL,
+                                                     NULL, NULL, NULL, TRUE);
+
+  /* Put it to the ID cache */
+  silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id, 
+                  (void *)conn->local_entry, 0, NULL);
+
+  if (connecting) {
+    /* Send NICK command if the nickname was set by the application (and is
+       not same as the username). */
+    if (client->nickname && strcmp(client->nickname, client->username))
+      silc_client_command_send(client, conn, SILC_COMMAND_NICK, 
+                              ++conn->cmd_ident, 1, 1, 
+                              client->nickname, strlen(client->nickname));
+
+    /* Issue INFO command to fetch the real server name and server information
+       and other stuff. */
+    silc_client_command_register(client, SILC_COMMAND_INFO, NULL, NULL,
+                                silc_client_command_reply_info_i, 0, 
+                                ++conn->cmd_ident);
+    sidp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER);
+    silc_client_command_send(client, conn, SILC_COMMAND_INFO,
+                            conn->cmd_ident, 1, 2, sidp->data, sidp->len);
+    silc_buffer_free(sidp);
+
+    /* Notify application of successful connection. We do it here now that
+       we've received the Client ID and are allowed to send traffic. */
+    client->internal->ops->connect(client, conn, TRUE);
+  }
+}
+
+/* Removes a client entry from all channels it has joined. */
+
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry)
+{
+  SilcHashTableList htl;
+  SilcChannelUser chu;
+
+  silc_hash_table_list(client_entry->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
+    silc_free(chu);
+  }
+  silc_hash_table_list_reset(&htl);
+}
+
+/* Replaces `old' client entries from all channels to `new' client entry.
+   This can be called for example when nickname changes and old ID entry
+   is replaced from ID cache with the new one. If the old ID entry is only
+   updated, then this fucntion needs not to be called. */
+
+void silc_client_replace_from_channels(SilcClient client, 
+                                      SilcClientConnection conn,
+                                      SilcClientEntry old,
+                                      SilcClientEntry new)
+{
+  SilcHashTableList htl;
+  SilcChannelUser chu;
+
+  silc_hash_table_list(old->channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+    /* Replace client entry */
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
+    
+    chu->client = new;
+    silc_hash_table_add(chu->channel->user_list, chu->client, chu);
+    silc_hash_table_add(chu->client->channels, chu->channel, chu);
+  }
+  silc_hash_table_list_reset(&htl);
+}
+
+/* Registers failure timeout to process the received failure packet
+   with timeout. */
+
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet)
+{
+  uint32 failure = 0;
+
+  if (sock->protocol) {
+    if (packet->buffer->len >= 4)
+      SILC_GET32_MSB(failure, packet->buffer->data);
+
+    /* Notify application */
+    client->internal->ops->failure(client, sock->user_data, sock->protocol,
+                                  (void *)failure);
+  }
+}
+
+/* A timeout callback for the re-key. We will be the initiator of the
+   re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_client_rekey_callback)
+{
+  SilcSocketConnection sock = (SilcSocketConnection)context;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcClient client = (SilcClient)conn->rekey->context;
+  SilcProtocol protocol;
+  SilcClientRekeyInternalContext *proto_ctx;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate internal protocol context. This is sent as context
+     to the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = (void *)client;
+  proto_ctx->sock = silc_socket_dup(sock);
+  proto_ctx->responder = FALSE;
+  proto_ctx->pfs = conn->rekey->pfs;
+      
+  /* Perform rekey protocol. Will call the final callback after the
+     protocol is over. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_REKEY, 
+                     &protocol, proto_ctx, silc_client_rekey_final);
+  sock->protocol = protocol;
+      
+  /* Run the protocol */
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
+
+  /* Re-register re-key timeout */
+  silc_schedule_task_add(client->schedule, sock->sock, 
+                        silc_client_rekey_callback,
+                        context, conn->rekey->timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+   new key material into use. */
+
+SILC_TASK_CALLBACK(silc_client_rekey_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx =
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcSocketConnection sock = ctx->sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    silc_protocol_cancel(protocol, client->schedule);
+    silc_protocol_free(protocol);
+    sock->protocol = NULL;
+    if (ctx->packet)
+      silc_packet_context_free(ctx->packet);
+    if (ctx->ske)
+      silc_ske_free(ctx->ske);
+    silc_socket_free(ctx->sock);
+    silc_free(ctx);
+    return;
+  }
+
+  /* Purge the outgoing data queue to assure that all rekey packets really
+     go to the network before we quit the protocol. */
+  silc_client_packet_queue_purge(client, sock);
+
+  /* Cleanup */
+  silc_protocol_free(protocol);
+  sock->protocol = NULL;
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_socket_free(ctx->sock);
+  silc_free(ctx);
+}
+
+/* Processes incoming connection authentication method request packet.
+   It is a reply to our previously sent request. The packet can be used
+   to resolve the authentication method for the current session if the
+   client does not know it beforehand. */
+
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  uint16 conn_type, auth_meth;
+  int ret;
+
+  /* If we haven't send our request then ignore this one. */
+  if (!conn->connauth)
+    return;
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_SHORT(&conn_type),
+                            SILC_STR_UI_SHORT(&auth_meth),
+                            SILC_STR_END);
+  if (ret == -1)
+    auth_meth = SILC_AUTH_NONE;
+
+  /* Call the request callback to notify application for received 
+     authentication method information. */
+  if (conn->connauth->callback)
+    (*conn->connauth->callback)(client, conn, auth_meth,
+                               conn->connauth->context);
+
+  silc_schedule_task_del(client->schedule, conn->connauth->timeout);
+
+  silc_free(conn->connauth);
+  conn->connauth = NULL;
+}
+
+/* Timeout task callback called if the server does not reply to our 
+   connection authentication method request in the specified time interval. */
+
+SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
+{
+  SilcClientConnection conn = (SilcClientConnection)context;
+  SilcClient client = conn->client;
+
+  if (!conn->connauth)
+    return;
+
+  /* Call the request callback to notify application */
+  if (conn->connauth->callback)
+    (*conn->connauth->callback)(client, conn, SILC_AUTH_NONE,
+                               conn->connauth->context);
+
+  silc_free(conn->connauth);
+  conn->connauth = NULL;
+}
+
+/* This function can be used to request the current authentication method
+   from the server. This may be called when connecting to the server
+   and the client library requests the authentication data from the
+   application. If the application does not know the current authentication
+   method it can request it from the server using this function.
+   The `callback' with `context' will be called after the server has
+   replied back with the current authentication method. */
+
+void 
+silc_client_request_authentication_method(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcConnectionAuthRequest callback,
+                                         void *context)
+{
+  SilcClientConnAuthRequest connauth;
+  SilcBuffer packet;
+
+  connauth = silc_calloc(1, sizeof(*connauth));
+  connauth->callback = callback;
+  connauth->context = context;
+
+  if (conn->connauth)
+    silc_free(conn->connauth);
+
+  conn->connauth = connauth;
+
+  /* Assemble the request packet and send it to the server */
+  packet = silc_buffer_alloc(4);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
+                    SILC_STR_UI_SHORT(SILC_AUTH_NONE),
+                    SILC_STR_END);
+  silc_client_packet_send(client, conn->sock, 
+                         SILC_PACKET_CONNECTION_AUTH_REQUEST,
+                         NULL, 0, NULL, NULL, 
+                         packet->data, packet->len, FALSE);
+  silc_buffer_free(packet);
+
+  /* Register a timeout in case server does not reply anything back. */
+  connauth->timeout =
+    silc_schedule_task_add(client->schedule, conn->sock->sock, 
+                          silc_client_request_authentication_method_timeout,
+                          conn, 
+                          client->internal->params->connauth_request_secs, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
diff --git a/lib/silcclient/client.h b/lib/silcclient/client.h
new file mode 100644 (file)
index 0000000..63593f7
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+
+  client.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+/* Forward declarations */
+typedef struct SilcClientStruct *SilcClient;
+typedef struct SilcClientInternalStruct *SilcClientInternal;
+typedef struct SilcClientConnectionStruct *SilcClientConnection;
+typedef struct SilcClientPingStruct SilcClientPing;
+typedef struct SilcClientAwayStruct SilcClientAway;
+typedef struct SilcClientKeyAgreementStruct *SilcClientKeyAgreement;
+typedef struct SilcClientFtpSessionStruct *SilcClientFtpSession;
+
+#include "idlist.h"
+#include "command.h"
+#include "silcapi.h"
+
+/* Generic rekey context for connections */
+typedef struct {
+  /* Current sending encryption key, provided for re-key. The `pfs'
+     is TRUE if the Perfect Forward Secrecy is performed in re-key. */
+  unsigned char *send_enc_key;
+  uint32 enc_key_len;
+  int ske_group;
+  bool pfs;
+  uint32 timeout;
+  void *context;
+} *SilcClientRekey;
+
+/* Context to hold the connection authentication request callbacks that
+   will be called when the server has replied back to our request about
+   current authentication method in the session. */
+typedef struct {
+  SilcConnectionAuthRequest callback;
+  void *context;
+  SilcTask timeout;
+} *SilcClientConnAuthRequest;
+
+/* Connection structure used in client to associate all the important
+   connection specific data to this structure. */
+struct SilcClientConnectionStruct {
+  /*
+   * Local data 
+   */
+  char *nickname;
+
+  /* Local client ID for this connection */
+  SilcClientID *local_id;
+
+  /* Decoded local ID so that the above defined ID would not have
+     to be decoded for every packet. */
+  unsigned char *local_id_data;
+  uint32 local_id_data_len;
+
+  /* Own client entry. */
+  SilcClientEntry local_entry;
+
+  /*
+   * Remote data 
+   */
+  char *remote_host;
+  int remote_port;
+  int remote_type;
+  char *remote_info;
+
+  /* Remote server ID for this connection */
+  SilcServerID *remote_id;
+
+  /* Decoded remote ID so that the above defined ID would not have
+     to be decoded for every packet. */
+  unsigned char *remote_id_data;
+  uint32 remote_id_data_len;
+
+  /*
+   * Common data 
+   */
+  /* Keys and stuff negotiated in the SKE protocol */
+  SilcCipher send_key;
+  SilcCipher receive_key;
+  SilcHmac hmac_send;
+  SilcHmac hmac_receive;
+  SilcHash hash;
+  uint32 psn_send;
+  uint32 psn_receive;
+
+  /* Client ID and Channel ID cache. Messages transmitted in SILC network
+     are done using different unique ID's. These are the cache for
+     thoses ID's used in the communication. */
+  SilcIDCache client_cache;
+  SilcIDCache channel_cache;
+  SilcIDCache server_cache;
+
+  /* Current channel on window. All channels are saved (allocated) into
+     the cache entries. */
+  SilcChannelEntry current_channel;
+
+  /* Socket connection object for this connection (window). This
+     object will have a back-pointer to this window object for fast
+     referencing (sock->user_data). */
+  SilcSocketConnection sock;
+
+  /* Pending command queue for this connection */
+  SilcDList pending_commands;
+
+  /* Current command identifier, 0 not used */
+  uint16 cmd_ident;
+
+  /* Requested pings. */
+  SilcClientPing *ping;
+  uint32 ping_count;
+
+  /* Set away message */
+  SilcClientAway *away;
+
+  /* Re-key context */
+  SilcClientRekey rekey;
+
+  /* Authentication request context. */
+  SilcClientConnAuthRequest connauth;
+
+  /* File transmission sessions */
+  SilcDList ftp_sessions;
+  uint32 next_session_id;
+  SilcClientFtpSession active_session;
+
+  /* Pointer back to the SilcClient. This object is passed to the application
+     and the actual client object is accesible through this pointer. */
+  SilcClient client;
+
+  /* User data context. Library does not touch this. */
+  void *context;
+};
+
+/* Main client structure. */
+struct SilcClientStruct {
+  char *username;              /* Username, must be set by application */
+  char *nickname;              /* Nickname, may be set by application  */
+  char *hostname;              /* hostname, must be set by application */
+  char *realname;              /* Real name, must be set be application */
+
+  SilcPublicKey public_key;    /* Public key of user, set by application */
+  SilcPrivateKey private_key;  /* Private key of user, set by application */
+  SilcPKCS pkcs;               /* PKCS allocated by application */
+
+  SilcSchedule schedule;       /* Scheduler, automatically allocated by
+                                  the client library. */
+
+  /* Random Number Generator. Application should use this as its primary
+     random number generator. */
+  SilcRng rng;
+
+  /* Application specific user data pointer. Client library does not
+     touch this. This the context sent as argument to silc_client_alloc. */
+  void *application;
+
+  /* Internal data for client library. Application cannot access this
+     data at all. */
+  SilcClientInternal internal;
+};
+
+#endif
diff --git a/lib/silcclient/client_channel.c b/lib/silcclient/client_channel.c
new file mode 100644 (file)
index 0000000..0ac903d
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+
+  client_channel.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes channel message sending and receiving routines,
+   channel key receiving and setting, and channel private key handling 
+   routines. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Sends packet to the `channel'. Packet to channel is always encrypted
+   differently from "normal" packets. SILC header of the packet is 
+   encrypted with the next receiver's key and the rest of the packet is
+   encrypted with the channel specific key. Padding and HMAC is computed
+   with the next receiver's key. The `data' is the channel message. If
+   the `force_send' is TRUE then the packet is sent immediately. */
+
+void silc_client_send_channel_message(SilcClient client, 
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     SilcChannelPrivateKey key,
+                                     SilcMessageFlags flags,
+                                     unsigned char *data, 
+                                     uint32 data_len, 
+                                     int force_send)
+{
+  int i;
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer payload;
+  SilcPacketContext packetdata;
+  SilcCipher cipher;
+  SilcHmac hmac;
+  unsigned char *id_string;
+  uint32 iv_len;
+  int block_len;
+
+  SILC_LOG_DEBUG(("Sending packet to channel"));
+
+  /* Take the key to be used */
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    if (key) {
+      /* Use key application specified */
+      cipher = key->cipher;
+      hmac = key->hmac;
+    } else if (channel->curr_key) {
+      /* Use current private key */
+      cipher = channel->curr_key->cipher;
+      hmac = channel->curr_key->hmac;
+    } else if (!channel->curr_key && channel->private_keys) {
+      /* Use just some private key since we don't know what to use 
+        and private keys are set. */
+      silc_dlist_start(channel->private_keys);
+      key = silc_dlist_get(channel->private_keys);
+      cipher = key->cipher;
+      hmac = key->hmac;
+      
+      /* Use this key as current private key */
+      channel->curr_key = key;
+    } else {
+      /* Use normal channel key generated by the server */
+      cipher = channel->channel_key;
+      hmac = channel->hmac;
+    }
+  } else {
+    /* Use normal channel key generated by the server */
+    cipher = channel->channel_key;
+    hmac = channel->hmac;
+  }
+
+  if (!cipher || !hmac)
+    return;
+
+  block_len = silc_cipher_get_block_len(cipher);
+
+  /* Generate IV */
+  iv_len = silc_cipher_get_block_len(cipher);
+  if (channel->iv[0] == '\0')
+    for (i = 0; i < iv_len; i++) channel->iv[i] = 
+                                  silc_rng_get_byte(client->rng);
+  else
+    silc_hash_make(client->internal->md5hash, channel->iv, iv_len, 
+                  channel->iv);
+
+  /* Encode the channel payload. This also encrypts the message payload. */
+  payload = silc_channel_message_payload_encode(flags, data_len, data, iv_len, 
+                                               channel->iv, cipher, hmac);
+
+  /* Get data used in packet header encryption, keys and stuff. */
+  cipher = conn->send_key;
+  hmac = conn->hmac_send;
+  id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+
+  /* Set the packet context pointers. The destination ID is always
+     the Channel ID of the channel. Server and router will handle the
+     distribution of the packet. */
+  packetdata.flags = 0;
+  packetdata.type = SILC_PACKET_CHANNEL_MESSAGE;
+  packetdata.src_id = conn->local_id_data;
+  packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
+  packetdata.src_id_type = SILC_ID_CLIENT;
+  packetdata.dst_id = id_string;
+  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
+  packetdata.dst_id_type = SILC_ID_CHANNEL;
+  packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                                         packetdata.src_id_len +
+                                         packetdata.dst_id_len), block_len);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          payload->len);
+
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the channel message payload to the outgoing data buffer */
+  silc_buffer_put(sock->outbuf, payload->data, payload->len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata, cipher);
+
+  /* Encrypt the header and padding of the packet. This is encrypted 
+     with normal session key shared with our server. */
+  silc_packet_encrypt(cipher, hmac, conn->psn_send++,
+                     sock->outbuf, SILC_PACKET_HEADER_LEN + 
+                     packetdata.src_id_len + packetdata.dst_id_len +
+                     packetdata.padlen);
+
+  SILC_LOG_HEXDUMP(("Packet to channel, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_client_packet_send_real(client, sock, force_send);
+  silc_buffer_free(payload);
+  silc_free(id_string);
+}
+
+typedef struct {
+  SilcChannelMessagePayload payload;
+  SilcChannelID *channel_id;
+} *SilcChannelClientResolve;
+
+static void silc_client_channel_message_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcClientEntry *clients,
+                                          uint32 clients_count,
+                                          void *context)
+{
+  SilcChannelClientResolve res = (SilcChannelClientResolve)context;
+
+  if (clients_count == 1) {
+    SilcChannelEntry channel;
+    unsigned char *message;
+
+    channel = silc_client_get_channel_by_id(client, conn, res->channel_id);
+    if (!channel)
+      goto out;
+
+    message = silc_channel_message_get_data(res->payload, NULL);
+    
+    /* Pass the message to application */
+    client->internal->ops->channel_message(
+                              client, conn, clients[0], channel,
+                              silc_channel_message_get_flags(res->payload),
+                              message);
+  }
+
+ out:
+  silc_channel_message_payload_free(res->payload);
+  silc_free(res->channel_id);
+  silc_free(res);
+}
+
+/* Process received message to a channel (or from a channel, really). This
+   decrypts the channel message with channel specific key and parses the
+   channel payload. Finally it displays the message on the screen. */
+
+void silc_client_channel_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcBuffer buffer = packet->buffer;
+  SilcChannelMessagePayload payload = NULL;
+  SilcChannelID *id = NULL;
+  SilcChannelEntry channel;
+  SilcClientEntry client_entry;
+  SilcClientID *client_id = NULL;
+  unsigned char *message;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Sanity checks */
+  if (packet->dst_id_type != SILC_ID_CHANNEL)
+    goto out;
+
+  client_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+                            SILC_ID_CLIENT);
+  if (!client_id)
+    goto out;
+  id = silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL);
+  if (!id)
+    goto out;
+
+  /* Find the channel entry from channels on this connection */
+  channel = silc_client_get_channel_by_id(client, conn, id);
+  if (!channel)
+    goto out;
+
+  /* If there is no channel private key then just decrypt the message 
+     with the channel key. If private keys are set then just go through
+     all private keys and check what decrypts correctly. */
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+    /* Parse the channel message payload. This also decrypts the payload */
+    payload = silc_channel_message_payload_parse(buffer->data, buffer->len, 
+                                                channel->channel_key,
+                                                channel->hmac);
+
+    /* If decryption failed and we have just performed channel key rekey
+       we will use the old key in decryption. If that fails too then we
+       cannot do more and will drop the packet. */
+    if (!payload) {
+      if (!channel->old_channel_key) {
+       goto out;
+      }
+
+      payload = silc_channel_message_payload_parse(buffer->data, buffer->len, 
+                                                  channel->old_channel_key,
+                                                  channel->old_hmac);
+      if (!payload) {
+       goto out;
+      }
+    }
+  } else if (channel->private_keys) {
+    SilcChannelPrivateKey entry;
+
+    silc_dlist_start(channel->private_keys);
+    while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+      /* Parse the channel message payload. This also decrypts the payload */
+      payload = silc_channel_message_payload_parse(buffer->data, buffer->len, 
+                                                  entry->cipher,
+                                                  entry->hmac);
+      if (payload)
+       break;
+    }
+    if (entry == SILC_LIST_END)
+      goto out;
+  } else {
+    goto out;
+  }
+
+  /* Find client entry */
+  client_entry = silc_client_get_client_by_id(client, conn, client_id);
+  if (!client_entry || !client_entry->nickname) {
+    /* Resolve the client info */
+    SilcChannelClientResolve res = silc_calloc(1, sizeof(*res));
+    res->payload = payload;
+    res->channel_id = id;
+    silc_client_get_client_by_id_resolve(client, conn, client_id,
+                                        silc_client_channel_message_cb,
+                                        res);
+    payload = NULL;
+    id = NULL;
+    goto out;
+  }
+
+  if (!silc_client_on_channel(channel, client_entry)) {
+    SILC_LOG_WARNING(("Received channel message from client not on channel"));
+    goto out;
+  }
+
+  message = silc_channel_message_get_data(payload, NULL);
+
+  /* Pass the message to application */
+  client->internal->ops->channel_message(
+                                client, conn, client_entry, channel,
+                                silc_channel_message_get_flags(payload),
+                                message);
+
+ out:
+  silc_free(id);
+  silc_free(client_id);
+  if (payload)
+    silc_channel_message_payload_free(payload);
+}
+
+/* Timeout callback that is called after a short period of time after the
+   new channel key has been created. This removes the old channel key all
+   together. */
+
+SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+
+  if (channel->old_channel_key)
+    silc_cipher_free(channel->old_channel_key);
+  if (channel->old_hmac)
+    silc_hmac_free(channel->old_hmac);
+  channel->old_channel_key = NULL;
+  channel->old_hmac = NULL;
+  channel->rekey_task = NULL;
+}
+
+/* Saves channel key from encoded `key_payload'. This is used when we
+   receive Channel Key Payload and when we are processing JOIN command 
+   reply. */
+
+void silc_client_save_channel_key(SilcClient client,
+                                 SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel)
+{
+  unsigned char *id_string, *key, *cipher, *hmac, hash[32];
+  uint32 tmp_len;
+  SilcChannelID *id;
+  SilcChannelKeyPayload payload;
+
+  payload = silc_channel_key_payload_parse(key_payload->data,
+                                          key_payload->len);
+  if (!payload)
+    return;
+
+  id_string = silc_channel_key_get_id(payload, &tmp_len);
+  if (!id_string) {
+    silc_channel_key_payload_free(payload);
+    return;
+  }
+
+  id = silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL);
+  if (!id) {
+    silc_channel_key_payload_free(payload);
+    return;
+  }
+
+  /* Find channel. */
+  if (!channel) {
+    channel = silc_client_get_channel_by_id(client, conn, id);
+    if (!channel)
+      goto out;
+  }
+
+  hmac = (channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : 
+         SILC_DEFAULT_HMAC);
+
+  /* Save the old key for a short period of time so that we can decrypt
+     channel message even after the rekey if some client would be sending
+     messages with the old key after the rekey. */
+  if (channel->old_channel_key)
+    silc_cipher_free(channel->old_channel_key);
+  if (channel->old_hmac)
+    silc_hmac_free(channel->old_hmac);
+  if (channel->rekey_task)
+    silc_schedule_task_del(client->schedule, channel->rekey_task);
+  channel->old_channel_key = channel->channel_key;
+  channel->old_hmac = channel->hmac;
+  channel->rekey_task = 
+    silc_schedule_task_add(client->schedule, 0,
+                          silc_client_save_channel_key_rekey, channel,
+                          10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  /* Free the old channel key data */
+  silc_free(channel->key);
+
+  /* Save the key */
+  key = silc_channel_key_get_key(payload, &tmp_len);
+  cipher = silc_channel_key_get_cipher(payload, NULL);
+  channel->key_len = tmp_len * 8;
+  channel->key = silc_calloc(tmp_len, sizeof(*channel->key));
+  memcpy(channel->key, key, tmp_len);
+
+  if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+    client->internal->ops->say(
+                          conn->client, conn, 
+                          SILC_CLIENT_MESSAGE_AUDIT,
+                          "Cannot talk to channel: unsupported cipher %s", 
+                          cipher);
+    goto out;
+  }
+
+  /* Set the cipher key */
+  silc_cipher_set_key(channel->channel_key, key, channel->key_len);
+
+  /* Generate HMAC key from the channel key data and set it */
+  silc_hmac_alloc(hmac, NULL, &channel->hmac);
+  silc_hash_make(silc_hmac_get_hash(channel->hmac), key, tmp_len, hash);
+  silc_hmac_set_key(channel->hmac, hash, 
+                   silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+  memset(hash, 0, sizeof(hash));
+
+ out:
+  silc_free(id);
+  silc_channel_key_payload_free(payload);
+}
+
+/* Processes received key for channel. The received key will be used
+   to protect the traffic on the channel for now on. Client must receive
+   the key to the channel before talking on the channel is possible. 
+   This is the key that server has generated, this is not the channel
+   private key, it is entirely local setting. */
+
+void silc_client_receive_channel_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcBuffer packet)
+{
+  SILC_LOG_DEBUG(("Received key for channel"));
+
+  /* Save the key */
+  silc_client_save_channel_key(client, sock->user_data, packet, NULL);
+}
+
+/* Adds private key for channel. This may be set only if the channel's mode
+   mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
+   mode is not set. When channel has private key then the messages are
+   encrypted using that key. All clients on the channel must also know the
+   key in order to decrypt the messages. However, it is possible to have
+   several private keys per one channel. In this case only some of the
+   clients on the channel may know the one key and only some the other key.
+
+   If `cipher' and/or `hmac' is NULL then default values will be used 
+   (aes-256-cbc for cipher and hmac-sha1-96 for hmac).
+
+   The private key for channel is optional. If it is not set then the
+   channel messages are encrypted using the channel key generated by the
+   server. However, setting the private key (or keys) for the channel 
+   significantly adds security. If more than one key is set the library
+   will automatically try all keys at the message decryption phase. Note:
+   setting many keys slows down the decryption phase as all keys has to
+   be tried in order to find the correct decryption key. However, setting
+   a few keys does not have big impact to the decryption performace. 
+
+   NOTE: that this is entirely local setting. The key set using this function
+   is not sent to the network at any phase.
+
+   NOTE: If the key material was originated by the SKE protocol (using
+   silc_client_send_key_agreement) then the `key' MUST be the
+   key->send_enc_key as this is dictated by the SILC protocol. However,
+   currently it is not expected that the SKE key material would be used
+   as channel private key. However, this API allows it. */
+
+int silc_client_add_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       char *cipher,
+                                       char *hmac,
+                                       unsigned char *key,
+                                       uint32 key_len)
+{
+  SilcChannelPrivateKey entry;
+  unsigned char hash[32];
+  SilcSKEKeyMaterial *keymat;
+
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+    return FALSE;
+
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
+
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  if (!silc_hmac_is_supported(hmac))
+    return FALSE;
+
+  /* Produce the key material */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
+                                        client->internal->md5hash, keymat) 
+      != SILC_SKE_STATUS_OK)
+    return FALSE;
+
+  /* Remove the current key, if it exists. */
+  if (channel->channel_key) {
+    silc_cipher_free(channel->channel_key);
+    memset(channel->key, 0, channel->key_len / 8);
+    silc_free(channel->key);
+    channel->channel_key = NULL;
+    channel->key = NULL;
+    channel->key_len = 0;
+  }
+  if (channel->hmac) {
+    silc_hmac_free(channel->hmac);
+    channel->hmac = NULL;
+  }
+
+  if (!channel->private_keys)
+    channel->private_keys = silc_dlist_init();
+
+  /* Save the key */
+  entry = silc_calloc(1, sizeof(*entry));
+  entry->key = silc_calloc(keymat->enc_key_len / 8, sizeof(*entry->key));
+  memcpy(entry->key, keymat->send_enc_key, keymat->enc_key_len / 8);
+  entry->key_len = keymat->enc_key_len / 8;
+
+  /* Allocate the cipher and set the key*/
+  silc_cipher_alloc(cipher, &entry->cipher);
+  silc_cipher_set_key(entry->cipher, entry->key, keymat->enc_key_len);
+
+  /* Generate HMAC key from the channel key data and set it */
+  silc_hmac_alloc(hmac, NULL, &entry->hmac);
+  silc_hash_make(silc_hmac_get_hash(entry->hmac), entry->key, 
+                entry->key_len, hash);
+  silc_hmac_set_key(entry->hmac, hash, 
+                   silc_hash_len(silc_hmac_get_hash(entry->hmac)));
+  memset(hash, 0, sizeof(hash));
+
+  /* Add to the private keys list */
+  silc_dlist_add(channel->private_keys, entry);
+
+  if (!channel->curr_key)
+    channel->curr_key = entry;
+
+  /* Free the key material */
+  silc_ske_free_key_material(keymat);
+
+  return TRUE;
+}
+
+/* Removes all private keys from the `channel'. The old channel key is used
+   after calling this to protect the channel messages. Returns FALSE on
+   on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_keys(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel)
+{
+  SilcChannelPrivateKey entry;
+
+  if (!channel->private_keys)
+    return FALSE;
+
+  silc_dlist_start(channel->private_keys);
+  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+    silc_dlist_del(channel->private_keys, entry);
+    memset(entry->key, 0, entry->key_len);
+    silc_free(entry->key);
+    silc_cipher_free(entry->cipher);
+    silc_hmac_free(entry->hmac);
+    silc_free(entry);
+  }
+
+  channel->curr_key = NULL;
+
+  silc_dlist_uninit(channel->private_keys);
+  channel->private_keys = NULL;
+
+  return TRUE;
+}
+
+/* Removes and frees private key `key' from the channel `channel'. The `key'
+   is retrieved by calling the function silc_client_list_channel_private_keys.
+   The key is not used after this. If the key was last private key then the
+   old channel key is used hereafter to protect the channel messages. This
+   returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcChannelPrivateKey key)
+{
+  SilcChannelPrivateKey entry;
+
+  if (!channel->private_keys)
+    return FALSE;
+
+  silc_dlist_start(channel->private_keys);
+  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+    if (entry == key) {
+      if (channel->curr_key == entry)
+       channel->curr_key = NULL;
+
+      silc_dlist_del(channel->private_keys, entry);
+      memset(entry->key, 0, entry->key_len);
+      silc_free(entry->key);
+      silc_cipher_free(entry->cipher);
+      silc_hmac_free(entry->hmac);
+      silc_free(entry);
+
+      if (silc_dlist_count(channel->private_keys) == 0) {
+       silc_dlist_uninit(channel->private_keys);
+       channel->private_keys = NULL;
+      }
+
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Returns array (pointers) of private keys associated to the `channel'.
+   The caller must free the array by calling the function
+   silc_client_free_channel_private_keys. The pointers in the array may be
+   used to delete the specific key by giving the pointer as argument to the
+   function silc_client_del_channel_private_key. */
+
+SilcChannelPrivateKey *
+silc_client_list_channel_private_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     uint32 *key_count)
+{
+  SilcChannelPrivateKey *keys = NULL, entry;
+  uint32 count = 0;
+
+  if (!channel->private_keys)
+    return NULL;
+
+  silc_dlist_start(channel->private_keys);
+  while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
+    keys = silc_realloc(keys, sizeof(*keys) * (count + 1));
+    keys[count] = entry;
+    count++;
+  }
+
+  if (key_count)
+    *key_count = count;
+
+  return keys;
+}
+
+/* Frees the SilcChannelPrivateKey array. */
+
+void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
+                                          uint32 key_count)
+{
+  silc_free(keys);
+}
+
+/* Returns the SilcChannelUser entry if the `client_entry' is joined on the 
+   channel indicated by the `channel'. NULL if client is not joined on
+   the channel. */
+
+SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
+                                      SilcClientEntry client_entry)
+{
+  SilcChannelUser chu;
+
+  if (silc_hash_table_find(channel->user_list, client_entry, NULL, 
+                          (void *)&chu))
+    return chu;
+
+  return NULL;
+}
diff --git a/lib/silcclient/client_ftp.c b/lib/silcclient/client_ftp.c
new file mode 100644 (file)
index 0000000..90642aa
--- /dev/null
@@ -0,0 +1,1143 @@
+/*
+
+  client_ftp.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+static int
+silc_client_connect_to_client(SilcClient client, 
+                             SilcClientConnection conn, int port,
+                             char *host, void *context);
+static int 
+silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
+SILC_TASK_CALLBACK(silc_client_ftp_connected);
+static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
+                                               int sock);
+
+/* File transmission session */
+struct SilcClientFtpSessionStruct {
+  uint32 session_id;
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcClientEntry client_entry;
+
+  SilcSocketConnection sock;
+  SilcBuffer packet;
+
+  char *hostname;
+  uint16 port;
+  int listener;
+
+  SilcClientFileMonitor monitor;
+  void *monitor_context;
+  char *filepath;
+
+  SilcSFTP sftp;
+  SilcSFTPFilesystem fs;
+  bool server;
+
+  SilcSFTPHandle dir_handle;
+  SilcSFTPHandle read_handle;
+  uint64 filesize;
+  uint64 read_offset;
+  int fd;
+};
+
+SILC_TASK_CALLBACK(silc_client_ftp_connected)
+{
+  SilcClientInternalConnectContext *ctx =
+    (SilcClientInternalConnectContext *)context;
+  SilcClient client = ctx->client;
+  SilcClientConnection conn = ctx->conn;
+  SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
+  int opt, opt_len = sizeof(opt);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Check the socket status as it might be in error */
+  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  if (opt != 0) {
+    if (ctx->tries < 2) {
+      /* Connection failed but lets try again */
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of client %s resumed", 
+                                ctx->port, ctx->host);
+
+      /* Unregister old connection try */
+      silc_schedule_unset_listen_fd(client->schedule, fd);
+      silc_net_close_connection(fd);
+      silc_schedule_task_del(client->schedule, ctx->task);
+
+      /* Try again */
+      silc_client_connect_to_client_internal(ctx);
+      ctx->tries++;
+    } else {
+      /* Connection failed and we won't try anymore */
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      silc_schedule_unset_listen_fd(client->schedule, fd);
+      silc_net_close_connection(fd);
+      silc_schedule_task_del(client->schedule, ctx->task);
+      silc_free(ctx);
+      silc_client_ftp_session_free(session);
+    }
+    return;
+  }
+
+  silc_schedule_unset_listen_fd(client->schedule, fd);
+  silc_schedule_task_del(client->schedule, ctx->task);
+
+  /* Start the key agreement */
+  silc_client_ftp_start_key_agreement(session, fd);
+}
+
+static int 
+silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
+{
+  int sock;
+
+  /* Create connection to server asynchronously */
+  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
+  if (sock < 0)
+    return -1;
+
+  /* Register task that will receive the async connect and will
+     read the result. */
+  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
+                                    silc_client_ftp_connected,
+                                    (void *)ctx, 0, 0, 
+                                    SILC_TASK_FD,
+                                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
+  ctx->sock = sock;
+  return sock;
+}
+
+static int
+silc_client_connect_to_client(SilcClient client, 
+                             SilcClientConnection conn, int port,
+                             char *host, void *context)
+{
+  SilcClientInternalConnectContext *ctx;
+
+  /* Allocate internal context for connection process. This is
+     needed as we are doing async connecting. */
+  ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->client = client;
+  ctx->conn = conn;
+  ctx->host = strdup(host);
+  ctx->port = port;
+  ctx->tries = 0;
+  ctx->context = context;
+
+  /* Do the actual connecting process */
+  return silc_client_connect_to_client_internal(ctx);
+}
+
+/* SFTP packet send callback. This will use preallocated buffer to avoid
+   reallocation of outgoing data buffer everytime. */
+
+static void silc_client_ftp_send_packet(SilcSocketConnection sock,
+                                       SilcBuffer packet, void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+  SilcClient client = session->client;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Allocate outgoing packet */
+  if (!session->packet)
+    session->packet = silc_buffer_alloc(1 + packet->len);
+
+  /* Enlarge outgoing packet if needed */
+  if (session->packet->truelen < 1 + packet->len)
+    session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
+
+  /* Encode packet */
+  silc_buffer_pull_tail(session->packet, 1 + packet->len);
+  silc_buffer_format(session->packet,
+                    SILC_STR_UI_CHAR(1),
+                    SILC_STR_UI_XNSTRING(packet->data, packet->len),
+                    SILC_STR_END);
+
+  /* Send the packet immediately */
+  silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
+                         session->packet->data, session->packet->len, TRUE);
+
+  /* Clear buffer */
+  session->packet->data = session->packet->tail = session->packet->head;
+  session->packet->len = 0;
+}
+
+/* SFTP monitor callback for SFTP server. This reports the application 
+   how the transmission is going along. This function is for the client
+   who made the file available for download. */
+
+static void silc_client_ftp_monitor(SilcSFTP sftp,
+                                   SilcSFTPMonitors type,
+                                   const SilcSFTPMonitorData data,
+                                   void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+
+  if (type == SILC_SFTP_MONITOR_READ) {
+    /* Call the monitor for application */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_SEND,
+                         SILC_CLIENT_FILE_OK,
+                         data->offset, session->filesize,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+  }
+}
+
+/* Returns the read data. This is the downloader's function (client side)
+   to receive the read data and read more until EOF is received from
+   the other side. This will also monitor the transmission and notify
+   the application. */
+
+static void silc_client_ftp_data(SilcSFTP sftp,
+                                SilcSFTPStatus status,
+                                const unsigned char *data,
+                                uint32 data_len,
+                                void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (status == SILC_SFTP_STATUS_EOF) {
+    /* EOF received */
+
+    /* Close the handle */
+    silc_sftp_close(sftp, session->read_handle, NULL, NULL);
+    session->read_handle = NULL;
+
+    /* Close the read file descriptor */
+    silc_file_close(session->fd);
+    return;
+  }
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+
+    /* Close the handle */
+    silc_sftp_close(sftp, session->read_handle, NULL, NULL);
+    session->read_handle = NULL;
+
+    /* Close the read file descriptor */
+    silc_file_close(session->fd);
+    return;
+  }
+
+  /* Read more, until EOF is received */
+  session->read_offset += data_len;
+  silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
+                silc_client_ftp_data, session);
+
+  /* Call monitor callback */
+  if (session->monitor)
+    (*session->monitor)(session->client, session->conn,
+                       SILC_CLIENT_FILE_MONITOR_RECEIVE,
+                       SILC_CLIENT_FILE_OK,
+                       session->read_offset, session->filesize,
+                       session->client_entry, session->session_id,
+                       session->filepath, session->monitor_context);
+
+  /* Write the read data to the real file */
+  silc_file_write(session->fd, data, data_len);
+}
+
+/* Returns handle for the opened file. This is the downloader's function.
+   This will begin reading the data from the file. */
+
+static void silc_client_ftp_open_handle(SilcSFTP sftp,
+                                       SilcSFTPStatus status,
+                                       SilcSFTPHandle handle,
+                                       void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+
+  /* Open the actual local file */
+  session->fd = silc_file_open(session->filepath, 
+                              O_RDWR | O_CREAT | O_EXCL);
+  if (session->fd < 0) {
+    /* Call monitor callback */
+    session->client->internal->ops->say(session->client, session->conn, 
+                                       SILC_CLIENT_MESSAGE_ERROR, 
+                                       "File `%s' open failed: %s", 
+                                       session->filepath,
+                                       strerror(errno));
+
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+
+  session->read_handle = handle;
+
+  /* Now, start reading the file */
+  silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
+                silc_client_ftp_data, session);
+
+  /* Call monitor callback */
+  if (session->monitor)
+    (*session->monitor)(session->client, session->conn,
+                       SILC_CLIENT_FILE_MONITOR_RECEIVE,
+                       SILC_CLIENT_FILE_OK,
+                       session->read_offset, session->filesize,
+                       session->client_entry, session->session_id,
+                       session->filepath, session->monitor_context);
+}
+
+/* Returns the file name available for download. This is the downloader's
+   function. */
+
+static void silc_client_ftp_readdir_name(SilcSFTP sftp,
+                                        SilcSFTPStatus status,
+                                        const SilcSFTPName name,
+                                        void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+  SilcSFTPAttributesStruct attr;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+
+  /* Now open the file */
+  memset(&attr, 0, sizeof(attr));
+  silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
+                silc_client_ftp_open_handle, session);
+
+  /* Save the important attributes */
+  session->filepath = strdup(name->filename[0]);
+  session->filesize = name->attrs[0]->size;
+
+  /* Close the directory handle */
+  silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
+  session->dir_handle = NULL;
+}
+
+/* Returns the file handle after giving opendir command. This is the
+   downloader's function. */
+
+static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
+                                          SilcSFTPStatus status,
+                                          SilcSFTPHandle handle,
+                                          void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+
+  /* Now, read the directory */
+  silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
+  session->dir_handle = handle;
+}
+
+/* SFTP version callback for SFTP client. This is the downloader's function
+   after initializing the SFTP connection to the remote client. This will
+   find out the filename available for download. */
+
+static void silc_client_ftp_version(SilcSFTP sftp,
+                                   SilcSFTPStatus status,
+                                   SilcSFTPVersion version,
+                                   void *context)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+
+  /* The SFTP session is open, now retrieve the info about available file. */
+  silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
+}
+
+/* This callback is called after the key agreement protocol has been
+   performed. This calls the final completion callback for the application. */
+
+SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+
+    /* Error occured during protocol */
+    silc_ske_free_key_material(ctx->keymat);
+    goto out;
+  }
+
+  /* Set keys into use */
+  silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+                                  ctx->ske->prop->cipher,
+                                  ctx->ske->prop->pkcs,
+                                  ctx->ske->prop->hash,
+                                  ctx->ske->prop->hmac,
+                                  ctx->ske->prop->group,
+                                  ctx->responder);
+
+  if (!session->server) {
+    /* If we are the SFTP client then start the SFTP session and retrieve
+       the info about the file available for download. */
+    session->sftp = silc_sftp_client_start(conn->sock,
+                                          silc_client_ftp_send_packet,
+                                          session, 
+                                          silc_client_ftp_version, session);
+  } else {
+    /* Start SFTP server */
+    session->sftp = silc_sftp_server_start(conn->sock,
+                                          silc_client_ftp_send_packet,
+                                          session, session->fs);
+
+    /* Monitor transmission */
+    silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
+                                silc_client_ftp_monitor, session);
+  }
+
+  /* Set this as active session */
+  conn->active_session = session;
+
+ out:
+  silc_ske_free_key_material(ctx->keymat);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_free(ctx->dest_id);
+  ctx->sock->protocol = NULL;
+  silc_socket_free(ctx->sock);
+  silc_free(ctx);
+  silc_protocol_free(protocol);
+}
+
+/* The downloader's function to start the key agreement protocol with the
+   remote client after we have connected to it. */
+
+static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
+                                               int sock)
+{
+  SilcClient client = session->client;
+  SilcClientKEInternalContext *proto_ctx;
+  SilcProtocol protocol;
+  SilcClientConnection conn;
+  void *context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Call monitor callback */
+  if (session->monitor)
+    (*session->monitor)(session->client, session->conn,
+                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
+                       SILC_CLIENT_FILE_OK, 0, 0,
+                       session->client_entry, session->session_id,
+                       NULL, session->monitor_context);
+
+  /* Add new connection for this session */
+  conn = silc_client_add_connection(client, session->hostname,
+                                   session->port, session);
+
+  /* Allocate new socket connection object */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
+  conn->sock->hostname = strdup(session->hostname);
+  conn->sock->port = silc_net_get_remote_port(sock);
+  session->sock = silc_socket_dup(conn->sock);
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = client;
+  proto_ctx->sock = silc_socket_dup(conn->sock);
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = FALSE;
+  proto_ctx->context = session;
+  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+
+  /* Perform key exchange protocol. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &protocol, (void *)proto_ctx,
+                     silc_client_ftp_key_agreement_final);
+  conn->sock->protocol = protocol;
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it will 
+     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)client;
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+
+  /* Execute the protocol */
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
+}
+
+/* The remote client's (the client who made the file available for download)
+   function for accepting incoming connection. This will also start the
+   key agreement protocol with the other client. */
+
+SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
+{
+  SilcClientFtpSession session = (SilcClientFtpSession)context;
+  SilcClient client = session->client;
+  SilcClientConnection conn;
+  SilcSocketConnection newsocket;
+  SilcClientKEInternalContext *proto_ctx;
+  int sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  sock = silc_net_accept_connection(session->listener);
+  if (sock < 0) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* Allocate new socket connection object */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
+
+  /* Perform name and address lookups for the remote host. */
+  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
+  if (!newsocket->hostname && !newsocket->ip) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+    return;
+  }
+  if (!newsocket->hostname)
+    newsocket->hostname = strdup(newsocket->ip);
+  newsocket->port = silc_net_get_remote_port(sock);
+
+  /* Call monitor callback */
+  if (session->monitor)
+    (*session->monitor)(session->client, session->conn,
+                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
+                       SILC_CLIENT_FILE_OK, 0, 0,
+                       session->client_entry, session->session_id,
+                       NULL, session->monitor_context);
+
+  /* Add new connection for this session */
+  conn = silc_client_add_connection(client, newsocket->hostname,
+                                   newsocket->port, session);
+  conn->sock = newsocket;
+  conn->sock->user_data = conn;
+  session->sock = silc_socket_dup(conn->sock);
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = client;
+  proto_ctx->sock = silc_socket_dup(conn->sock);
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = TRUE;
+  proto_ctx->context = session;
+  proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+
+  /* Prepare the connection for key exchange protocol. We allocate the
+     protocol but will not start it yet. The connector will be the
+     initiator of the protocol thus we will wait for initiation from 
+     there before we start the protocol. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &newsocket->protocol, proto_ctx, 
+                     silc_client_ftp_key_agreement_final);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it
+     will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)client;
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+}
+
+/* Free all file transfer sessions. */
+
+void silc_client_ftp_free_sessions(SilcClient client,
+                                  SilcClientConnection conn)
+{
+  if (conn->ftp_sessions) {
+    SilcClientFtpSession session;
+    silc_dlist_start(conn->ftp_sessions);
+    while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+      if (session->sock)
+       session->sock->user_data = NULL;
+      silc_client_ftp_session_free(session);
+    }
+    silc_dlist_del(conn->ftp_sessions, session);
+    silc_dlist_uninit(conn->ftp_sessions);
+  }
+}
+
+/* Free file transfer session by client entry. */
+
+void silc_client_ftp_session_free_client(SilcClientConnection conn,
+                                        SilcClientEntry client_entry)
+{
+  SilcClientFtpSession session;
+
+  if (!conn->ftp_sessions)
+    return;
+
+  /* Get the session */
+  silc_dlist_start(conn->ftp_sessions);
+  while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+    if (session->client_entry == client_entry) {
+      if (session->sock)
+       session->sock->user_data = NULL;
+      silc_client_ftp_session_free(session);
+    }
+  }
+}
+
+/* Free session resources. */
+
+void silc_client_ftp_session_free(SilcClientFtpSession session)
+{
+  SilcClientConnection conn;
+
+  SILC_LOG_DEBUG(("Free session"));
+
+  silc_dlist_del(session->conn->ftp_sessions, session);
+
+  if (session->sftp) {
+    if (session->server)
+      silc_sftp_server_shutdown(session->sftp);
+    else
+      silc_sftp_client_shutdown(session->sftp);
+  }
+
+  if (session->fs)
+    silc_sftp_fs_memory_free(session->fs);
+
+  /* Destroy listener */
+  if (session->listener) {
+    silc_schedule_unset_listen_fd(session->client->schedule, 
+                                 session->listener);
+    silc_net_close_connection(session->listener);
+    silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
+  }
+
+  /* Destroy session connection */
+  if (session->sock) {
+    silc_schedule_unset_listen_fd(session->client->schedule, 
+                                 session->sock->sock);
+    silc_net_close_connection(session->sock->sock);
+
+    if (session->sock->user_data) {
+      conn = (SilcClientConnection)session->sock->user_data;
+
+      if (conn->active_session == session)
+       conn->active_session = NULL;
+
+      silc_client_close_connection(session->client, session->sock, conn);
+    } else {
+      silc_socket_free(session->sock);
+    }
+  }
+
+  if (session->packet)
+    silc_buffer_free(session->packet);
+
+  silc_free(session->hostname);
+  silc_free(session->filepath);
+  silc_free(session);
+}
+
+/* Sends a file indicated by the `filepath' to the remote client 
+   indicated by the `client_entry'.  This will negotiate a secret key
+   with the remote client before actually starting the transmission of
+   the file.  The `monitor' callback will be called to monitor the
+   transmission of the file. */
+
+SilcClientFileError 
+silc_client_file_send(SilcClient client,
+                     SilcClientConnection conn,
+                     SilcClientFileMonitor monitor,
+                     void *monitor_context,
+                     const char *local_ip,
+                     uint32 local_port,
+                     SilcClientEntry client_entry,
+                     const char *filepath,
+                     uint32 *session_id)
+{
+  SilcClientFtpSession session;
+  SilcBuffer keyagr, ftp;
+  char *filename, *path;
+  int fd;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Check for existing session for `filepath'. */
+  silc_dlist_start(conn->ftp_sessions);
+  while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+    if (session->filepath && !strcmp(session->filepath, filepath) && 
+       session->client_entry == client_entry)
+      return SILC_CLIENT_FILE_ALREADY_STARTED;
+  }
+
+  /* See whether the file exists, and can be opened in generally speaking */
+  fd = silc_file_open(filepath, O_RDONLY);
+  if (fd < 0)
+    return SILC_CLIENT_FILE_NO_SUCH_FILE;
+  silc_file_close(fd);
+
+  /* Add new session */
+  session = silc_calloc(1, sizeof(*session));
+  session->session_id = ++conn->next_session_id;
+  session->client = client;
+  session->conn = conn;
+  session->client_entry = client_entry;
+  session->monitor = monitor;
+  session->monitor_context = monitor_context;
+  session->filepath = strdup(filepath);
+  session->server = TRUE;
+  silc_dlist_add(conn->ftp_sessions, session);
+
+  path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
+  strcat(path, "file://");
+  strncat(path, filepath, strlen(filepath));
+
+  /* Allocate memory filesystem and put the file to it */
+  if (strrchr(path, '/'))
+    filename = strrchr(path, '/') + 1;
+  else
+    filename = (char *)path;
+  session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
+                                         SILC_SFTP_FS_PERM_EXEC);
+  silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
+                              filename, path);
+
+  session->filesize = silc_file_size(filepath);
+
+  /* Create the listener for incoming key exchange protocol. */
+  if (local_ip)
+    session->hostname = strdup(local_ip);
+  else
+    session->hostname = silc_net_localip();
+  session->listener = silc_net_create_server(local_port, session->hostname);
+  if (session->listener < 0) {
+    /* Could not create listener. Do the second best thing; send empty
+       key agreement packet and let the remote client provide the point
+       for the key exchange. */
+    SILC_LOG_DEBUG(("Could not create listener"));
+    silc_free(session->hostname);
+    session->hostname = NULL;
+    session->port = 0;
+  } else {
+    /* Listener ready */
+    session->port = silc_net_get_local_port(session->listener);
+    silc_schedule_task_add(client->schedule, session->listener,
+                          silc_client_ftp_process_key_agreement, session,
+                          0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
+  }
+
+  /* Send the key agreement inside FTP packet */
+  keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
+
+  ftp = silc_buffer_alloc(1 + keyagr->len);
+  silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
+  silc_buffer_format(ftp,
+                    SILC_STR_UI_CHAR(1),
+                    SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
+                    SILC_STR_END);
+  silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
+                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                         ftp->data, ftp->len, FALSE);
+
+  silc_buffer_free(keyagr);
+  silc_buffer_free(ftp);
+  silc_free(path);
+
+  if (session_id)
+    *session_id = session->session_id;
+
+  return SILC_CLIENT_FILE_OK;
+}
+
+/* Receives a file from a client indicated by the `client_entry'.  The
+   `session_id' indicates the file transmission session and it has been
+   received in the `ftp' client operation function.  This will actually
+   perform the key agreement protocol with the remote client before
+   actually starting the file transmission.  The `monitor' callback
+   will be called to monitor the transmission. */
+
+SilcClientFileError 
+silc_client_file_receive(SilcClient client,
+                        SilcClientConnection conn,
+                        SilcClientFileMonitor monitor,
+                        void *monitor_context,
+                        uint32 session_id)
+{
+  SilcClientFtpSession session;
+  SilcBuffer keyagr, ftp;
+
+  SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
+
+  /* Get the session */
+  silc_dlist_start(conn->ftp_sessions);
+  while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+    if (session->session_id == session_id) {
+      break;
+    }
+  }
+
+  if (session == SILC_LIST_END) {
+    SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
+    return SILC_CLIENT_FILE_UNKNOWN_SESSION;
+  }
+
+  /* See if we have this session running already */
+  if (session->sftp || session->listener) {
+    SILC_LOG_DEBUG(("Session already started"));
+    return SILC_CLIENT_FILE_ALREADY_STARTED;
+  }
+
+  session->monitor = monitor;
+  session->monitor_context = monitor_context;
+  session->conn = conn;
+
+  /* If the hostname and port already exists then the remote client did
+     provide the connection point to us and we won't create listener, but
+     create the connection ourselves. */
+  if (session->hostname && session->port) {
+    if (silc_client_connect_to_client(client, conn, session->port, 
+                                     session->hostname, session) < 0)
+      return SILC_CLIENT_FILE_ERROR;
+  } else {
+    /* Add the listener for the key agreement */
+    session->hostname = silc_net_localip();
+    session->listener = silc_net_create_server(0, session->hostname);
+    if (session->listener < 0) {
+      SILC_LOG_DEBUG(("Could not create listener"));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+                                "Cannot create listener on %s: %s", 
+                                session->hostname, strerror(errno));
+      return SILC_CLIENT_FILE_ERROR;
+    }
+    session->port = silc_net_get_local_port(session->listener);
+    silc_schedule_task_add(client->schedule, session->listener,
+                          silc_client_ftp_process_key_agreement, session,
+                          0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
+    
+    /* Send the key agreement inside FTP packet */
+    keyagr = silc_key_agreement_payload_encode(session->hostname, 
+                                              session->port);
+    ftp = silc_buffer_alloc(1 + keyagr->len);
+    silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
+    silc_buffer_format(ftp,
+                      SILC_STR_UI_CHAR(1),
+                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
+                      SILC_STR_END);
+    silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
+                           session->client_entry->id, 
+                           SILC_ID_CLIENT, NULL, NULL,
+                           ftp->data, ftp->len, FALSE);
+    
+    silc_buffer_free(keyagr);
+    silc_buffer_free(ftp);
+  }
+
+  return SILC_CLIENT_FILE_OK;
+}
+
+/* Closes file transmission session indicated by the `session_id'.
+   If file transmission is being conducted it will be aborted
+   automatically. This function is also used to close the session
+   after successful file transmission. This function can be used
+   also to reject incoming file transmission request. */
+
+SilcClientFileError silc_client_file_close(SilcClient client,
+                                          SilcClientConnection conn,
+                                          uint32 session_id)
+{
+  SilcClientFtpSession session;
+
+  SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
+
+  /* Get the session */
+  silc_dlist_start(conn->ftp_sessions);
+  while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+    if (session->session_id == session_id) {
+      break;
+    }
+  }
+
+  if (session == SILC_LIST_END) {
+    SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
+    return SILC_CLIENT_FILE_UNKNOWN_SESSION;
+  }
+
+  silc_client_ftp_session_free(session);
+
+  return SILC_CLIENT_FILE_OK;
+}
+
+/* Callback called after remote client information has been resolved.
+   This will try to find existing session for the client entry.  If found
+   then continue with the key agreement protocol.  If not then it means
+   this is a file transfer request and we let the application know. */
+
+static void silc_client_ftp_resolve_cb(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry *clients,
+                                      uint32 clients_count,
+                                      void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+  SilcClientFtpSession session;
+  SilcKeyAgreementPayload payload = NULL;
+  SilcClientEntry client_entry;
+  char *hostname;
+  uint16 port;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!clients)
+    goto out;
+
+  client_entry = clients[0];
+
+  silc_dlist_start(conn->ftp_sessions);
+  while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
+    if (session->client_entry == client_entry)
+      break;
+  }
+
+  /* Parse the key agreement payload */
+  payload = silc_key_agreement_payload_parse(packet->buffer->data,
+                                            packet->buffer->len);
+  if (!payload)
+    goto out;
+
+  hostname = silc_key_agreement_get_hostname(payload);
+  port = silc_key_agreement_get_port(payload);
+  if (!hostname)
+    port = 0;
+  if (!port)
+    hostname = NULL;
+
+  if (session == SILC_LIST_END || (!hostname && !port)) {
+    /* No session found, create one and let the application know about
+       incoming file transfer request. */
+    
+    /* Add new session */
+    session = silc_calloc(1, sizeof(*session));
+    session->session_id = ++conn->next_session_id;
+    session->client = client;
+    session->conn = conn;
+    session->client_entry = client_entry;
+    silc_dlist_add(conn->ftp_sessions, session);
+
+    /* Let the application know */
+    client->internal->ops->ftp(client, conn, client_entry,
+                              session->session_id, hostname, port);
+
+    if (hostname && port) {
+      session->hostname = strdup(hostname);
+      session->port = port;
+    }
+    
+    goto out;
+  }
+
+  session->hostname = strdup(hostname);
+  session->port = port;
+
+  /* Session exists, continue with key agreement protocol. */
+  if (silc_client_connect_to_client(client, conn, port, 
+                                   hostname, session) < 0) {
+    /* Call monitor callback */
+    if (session->monitor)
+      (*session->monitor)(session->client, session->conn,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
+                         session->client_entry, session->session_id,
+                         session->filepath, session->monitor_context);
+  }
+
+ out:
+  if (payload)
+    silc_key_agreement_payload_free(payload);
+  silc_packet_context_free(packet);
+}
+
+/* Called when file transfer packet is received. This will parse the
+   packet and give it to the file transfer protocol. */
+
+void silc_client_ftp(SilcClient client,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  uint8 type;
+  int ret;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_END);
+  if (ret == -1)
+    return;
+
+  /* We support only type number 1 (== SFTP) */
+  if (type != 1)
+    return;
+
+  silc_buffer_pull(packet->buffer, 1);
+
+  /* If we have active FTP session then give the packet directly to the
+     protocol processor. */
+  if (conn->active_session) {
+    /* Give it to the SFTP */
+    if (conn->active_session->server)
+      silc_sftp_server_receive_process(conn->active_session->sftp, sock, 
+                                      packet);
+    else
+      silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
+                                      packet);
+  } else {
+    /* We don't have active session, resolve the remote client information
+       and then try to find the correct session. */
+    SilcClientID *remote_id;
+
+    if (packet->src_id_type != SILC_ID_CLIENT)
+      return;
+
+    remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                              SILC_ID_CLIENT);
+    if (!remote_id)
+      return;
+
+    /* Resolve the client */
+    silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                        silc_client_ftp_resolve_cb,
+                                        silc_packet_context_dup(packet));
+    silc_free(remote_id);
+  }
+}
diff --git a/lib/silcclient/client_internal.h b/lib/silcclient/client_internal.h
new file mode 100644 (file)
index 0000000..32e7fdf
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+
+  client_internal.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef CLIENT_INTERNAL_H
+#define CLIENT_INTERNAL_H
+
+/* Internal context for connection process. This is needed as we
+   doing asynchronous connecting. */
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcTask task;
+  int sock;
+  char *host;
+  int port;
+  int tries;
+  void *context;
+} SilcClientInternalConnectContext;
+
+/* Structure to hold ping time information. Every PING command will 
+   add entry of this structure and is removed after reply to the ping
+   as been received. */
+struct SilcClientPingStruct {
+  time_t start_time;
+  void *dest_id;
+  char *dest_name;
+};
+
+/* Structure to hold away messages set by user. This is mainly created
+   for future extensions where away messages could be set according filters
+   such as nickname and hostname. For now only one away message can 
+   be set in one connection. */
+struct SilcClientAwayStruct {
+  char *away;
+  struct SilcClientAwayStruct *next;
+};
+
+/* Internal context for the client->internal pointer in the SilcClient. */
+struct SilcClientInternalStruct {
+  /* All client operations that are implemented by the application. */
+  SilcClientOperations *ops;
+
+  /* Client Parameters */
+  SilcClientParams *params;
+
+  /* Table of connections in client. All the connection data is saved here. */
+  SilcClientConnection *conns;
+  uint32 conns_count;
+
+  /* Table of listenning sockets in client.  Client can have listeners
+     (like key agreement protocol server) and those sockets are saved here.
+     This table is checked always if the connection object cannot be found
+     from the `conns' table. */
+  SilcSocketConnection *sockets;
+  uint32 sockets_count;
+
+  /* Registered commands */
+  SilcList commands;
+
+  /* Generic cipher and hash objects. */
+  SilcCipher none_cipher;
+  SilcHash md5hash;
+  SilcHash sha1hash;
+  SilcHmac md5hmac;
+  SilcHmac sha1hmac;
+
+  /* Client version. Used to compare to remote host's version strings. */
+  char *silc_client_version;
+};
+
+/* Macros */
+
+/* Registers generic task for file descriptor for reading from network and
+   writing to network. As being generic task the actual task is allocated 
+   only once and after that the same task applies to all registered fd's. */
+#define SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(fd)     \
+do {                                                   \
+  silc_schedule_task_add(client->schedule, (fd),       \
+                        silc_client_packet_process,    \
+                        context, 0, 0,                 \
+                        SILC_TASK_GENERIC,             \
+                        SILC_TASK_PRI_NORMAL);         \
+} while(0)
+
+#define SILC_CLIENT_SET_CONNECTION_FOR_INPUT(s, fd)            \
+do {                                                           \
+  silc_schedule_set_listen_fd((s), (fd), SILC_TASK_READ);      \
+} while(0)
+     
+#define SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT(s, fd)           \
+do {                                                           \
+  silc_schedule_set_listen_fd((s), (fd), (SILC_TASK_READ |     \
+                                         SILC_TASK_WRITE));    \
+} while(0)
+
+/* Finds socket connection object by file descriptor */
+#define SILC_CLIENT_GET_SOCK(__x, __fd, __sock)                        \
+do {                                                           \
+  int __i;                                                     \
+                                                               \
+  for (__i = 0; __i < (__x)->internal->conns_count; __i++)     \
+    if ((__x)->internal->conns[__i] &&                         \
+       (__x)->internal->conns[__i]->sock->sock == (__fd))      \
+      break;                                                   \
+                                                               \
+  if (__i >= (__x)->internal->conns_count) {                   \
+    (__sock) = NULL;                                           \
+    for (__i = 0; __i < (__x)->internal->sockets_count; __i++) \
+      if ((__x)->internal->sockets[__i] &&                     \
+         (__x)->internal->sockets[__i]->sock == (__fd))        \
+        (__sock) = (__x)->internal->sockets[__i];              \
+  } else                                                       \
+    (__sock) = (__x)->internal->conns[__i]->sock;              \
+} while(0)
+
+/* Check whether rekey protocol is active */
+#define SILC_CLIENT_IS_REKEY(sock)                                     \
+  (sock->protocol && sock->protocol->protocol &&                       \
+   sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY)
+
+/* Prototypes */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_client_packet_process);
+int silc_client_packet_send_real(SilcClient client,
+                                SilcSocketConnection sock,
+                                bool force_send);
+void silc_client_ftp_free_sessions(SilcClient client,
+                                  SilcClientConnection conn);
+void silc_client_ftp_session_free(SilcClientFtpSession session);
+void silc_client_ftp_session_free_client(SilcClientConnection conn,
+                                        SilcClientEntry client_entry);
+void silc_client_packet_send(SilcClient client, 
+                            SilcSocketConnection sock,
+                            SilcPacketType type, 
+                            void *dst_id,
+                            SilcIdType dst_id_type,
+                            SilcCipher cipher,
+                            SilcHmac hmac,
+                            unsigned char *data, 
+                            uint32 data_len, 
+                            int force_send);
+void silc_client_disconnected_by_server(SilcClient client,
+                                       SilcSocketConnection sock,
+                                       SilcBuffer message);
+void silc_client_error_by_server(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcBuffer message);
+void silc_client_receive_new_id(SilcClient client,
+                               SilcSocketConnection sock,
+                               SilcIDPayload idp);
+void silc_client_save_channel_key(SilcClient client,
+                                 SilcClientConnection conn,
+                                 SilcBuffer key_payload, 
+                                 SilcChannelEntry channel);
+void silc_client_receive_channel_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcBuffer packet);
+void silc_client_channel_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet);
+void silc_client_remove_from_channels(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry);
+void silc_client_replace_from_channels(SilcClient client, 
+                                      SilcClientConnection conn,
+                                      SilcClientEntry old,
+                                      SilcClientEntry newclient);
+void silc_client_process_failure(SilcClient client,
+                                SilcSocketConnection sock,
+                                SilcPacketContext *packet);
+void silc_client_key_agreement(SilcClient client,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet);
+void silc_client_notify_by_server(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet);
+void silc_client_private_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet);
+void silc_client_connection_auth_request(SilcClient client,
+                                        SilcSocketConnection sock,
+                                        SilcPacketContext *packet);
+void silc_client_ftp(SilcClient client,
+                    SilcSocketConnection sock,
+                    SilcPacketContext *packet);
+
+#endif
diff --git a/lib/silcclient/client_keyagr.c b/lib/silcclient/client_keyagr.c
new file mode 100644 (file)
index 0000000..d019789
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+
+  client_keyagr.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the Key Agreement packet processing and actual
+   key agreement routines. This file has nothing to do with the actual
+   connection key exchange protocol, it is implemented in the client.c
+   and in protocol.c. This file implements the client-to-client key 
+   agreement as defined by the SILC protocol. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_final);
+SILC_TASK_CALLBACK(silc_client_process_key_agreement);
+SILC_TASK_CALLBACK(silc_client_key_agreement_timeout);
+SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start);
+
+/* Key agreement context */
+struct SilcClientKeyAgreementStruct {
+  SilcClient client;
+  SilcClientConnection conn;
+  int fd;                                /* Listening/connection socket */
+  SilcSocketConnection sock;             /* Remote socket connection */
+  SilcClientEntry client_entry;                  /* Destination client */
+  SilcKeyAgreementCallback completion;   /* Key agreement completion */
+  void *context;                         /* User context */
+  SilcTask timeout;                      /* Timeout task */
+  SilcClientKEInternalContext *proto_ctx; /* Key Exchange protocol context */
+};
+
+/* Packet sending function used by the SKE in the key agreement process. */
+
+static void silc_client_key_agreement_send_packet(SilcSKE ske,
+                                                 SilcBuffer packet,
+                                                 SilcPacketType type,
+                                                 void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  void *tmp;
+
+  /* Send the packet immediately. We will assure that the packet is not
+     encrypted by setting the socket's user_data pointer to NULL. The
+     silc_client_packet_send would take the keys (wrong keys that is,
+     because user_data is the current SilcClientConnection) from it and
+     we cannot allow that. The packets are never encrypted when doing SKE
+     with another client. */
+  tmp = ske->sock->user_data;
+  ske->sock->user_data = NULL;
+  silc_client_packet_send(ctx->client, ske->sock, type, NULL, 0, NULL, NULL,
+                         packet->data, packet->len, TRUE);
+  ske->sock->user_data = tmp;
+}
+
+/* Timeout callback that is called to close the connection and free the
+   socket connection data. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_close)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->sock->sock);
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+  silc_net_close_connection(ke->sock->sock);
+  silc_net_close_connection(ke->fd);
+  silc_socket_free(ke->sock);
+  silc_free(ke);
+}
+
+/* This callback is called after the key agreement protocol has been
+   performed. This calls the final completion callback for the application. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_final)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+    /* Error occured during protocol */
+    ke->client_entry->ke = NULL;
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
+    silc_ske_free_key_material(ctx->keymat);
+    goto out;
+  }
+
+  /* Pass the negotiated key material to the application. The application
+     is responsible of freeing the key material. */
+  ke->client_entry->ke = NULL;
+  ke->completion(ke->client, ke->conn, ke->client_entry, 
+                SILC_KEY_AGREEMENT_OK, ctx->keymat, ke->context);
+
+ out:
+  silc_protocol_free(protocol);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  if (ctx->dest_id)
+    silc_free(ctx->dest_id);
+  silc_schedule_task_del_by_fd(client->schedule, ke->fd);
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+  silc_net_close_connection(ke->fd);
+  if (ke->timeout)
+    silc_schedule_task_del(client->schedule, ke->timeout);
+  silc_client_del_socket(ke->client, ke->sock);
+
+  silc_schedule_task_add(client->schedule, 0, 
+                    silc_client_key_agreement_close,
+                    (void *)ke, 0, 1, 
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  silc_free(ctx);
+}
+
+/* Key agreement callback that is called when remote end has initiated
+   the key agreement protocol. This accepts the incoming TCP/IP connection
+   for the key agreement protocol. */
+
+SILC_TASK_CALLBACK(silc_client_process_key_agreement)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+  SilcClient client = ke->client;
+  SilcClientConnection conn = ke->conn;
+  SilcSocketConnection newsocket;
+  SilcClientKEInternalContext *proto_ctx;
+  int sock;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  sock = silc_net_accept_connection(ke->fd);
+  if (sock < 0) {
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                              "Could not accept key agreement connection: ", 
+                              strerror(errno));
+    ke->client_entry->ke = NULL;
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
+    silc_schedule_task_del_by_fd(client->schedule, ke->fd);
+    silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+    silc_net_close_connection(ke->fd);
+    if (ke->timeout)
+      silc_schedule_task_del(client->schedule, ke->timeout);
+    silc_free(ke);
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* Create socket for this connection (it is of type UNKNOWN since this
+     really is not a real SILC connection. It is only for the key
+     agreement protocol). */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &newsocket);
+  ke->sock = newsocket;
+
+  /* Perform name and address lookups for the remote host. */
+  silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
+  if (!newsocket->hostname && !newsocket->ip) {
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                              "Could not resolve the remote IP or hostname");
+    ke->client_entry->ke = NULL;
+    ke->completion(ke->client, ke->conn, ke->client_entry, 
+                  SILC_KEY_AGREEMENT_ERROR, NULL, ke->context);
+    silc_schedule_task_del_by_fd(client->schedule, ke->fd);
+    silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+    silc_net_close_connection(ke->fd);
+    if (ke->timeout)
+      silc_schedule_task_del(client->schedule, ke->timeout);
+    silc_free(ke);
+    return;
+  }
+  if (!newsocket->hostname)
+    newsocket->hostname = strdup(newsocket->ip);
+  newsocket->port = silc_net_get_remote_port(sock);
+  silc_client_add_socket(client, newsocket);
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = client;
+  proto_ctx->sock = silc_socket_dup(newsocket);
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = TRUE;
+  proto_ctx->context = context;
+  proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+  ke->proto_ctx = proto_ctx;
+
+  /* Prepare the connection for key exchange protocol. We allocate the
+     protocol but will not start it yet. The connector will be the
+     initiator of the protocol thus we will wait for initiation from 
+     there before we start the protocol. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &newsocket->protocol, proto_ctx, 
+                     silc_client_key_agreement_final);
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it
+     will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)client;
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+}
+
+/* Timeout occured during key agreement. This means that the key agreement
+   protocol was not completed in the specified timeout. We will call the 
+   completion callback. */
+
+SILC_TASK_CALLBACK(silc_client_key_agreement_timeout)
+{
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)context;
+
+  ke->client_entry->ke = NULL;
+  ke->completion(ke->client, ke->conn, ke->client_entry, 
+                SILC_KEY_AGREEMENT_TIMEOUT, NULL, ke->context);
+
+  if (ke->sock) {
+    silc_client_del_socket(ke->client, ke->sock);
+    silc_socket_free(ke->sock);
+  }
+  if (ke->proto_ctx && ke->proto_ctx->ske)
+    silc_ske_free(ke->proto_ctx->ske);
+  ke->client_entry->ke = NULL;
+  if (ke->fd)
+    silc_schedule_task_del_by_fd(ke->client->schedule, ke->fd);
+  silc_schedule_unset_listen_fd(ke->client->schedule, ke->fd);
+  silc_net_close_connection(ke->fd);
+  silc_free(ke);
+}
+
+/* Sends key agreement request to the remote client indicated by the
+   `client_entry'. If the caller provides the `hostname' and the `port'
+   arguments then the library will bind the client to that hostname and
+   that port for the key agreement protocol. It also sends the `hostname'
+   and the `port' in the key agreement packet to the remote client. This
+   would indicate that the remote client may initiate the key agreement
+   protocol to the `hostname' on the `port'.  If port is zero then the
+   bound port is undefined (the operating system defines it).
+
+   If the `hostname' and `port' is not provided then empty key agreement
+   packet is sent to the remote client. The remote client may reply with
+   the same packet including its hostname and port. If the library receives
+   the reply from the remote client the `key_agreement' client operation
+   callback will be called to verify whether the user wants to perform the
+   key agreement or not. 
+
+   NOTE: If the application provided the `hostname' and the `port' and the 
+   remote side initiates the key agreement protocol it is not verified
+   from the user anymore whether the protocol should be executed or not.
+   By setting the `hostname' and `port' the user gives permission to
+   perform the protocol (we are responder in this case).
+
+   NOTE: If the remote side decides not to initiate the key agreement
+   or decides not to reply with the key agreement packet then we cannot
+   perform the key agreement at all. If the key agreement protocol is
+   performed the `completion' callback with the `context' will be called.
+   If remote side decides to ignore the request the `completion' will be
+   called after the specified timeout, `timeout_secs'. 
+
+   NOTE: If the `hostname' and the `port' was not provided the `completion'
+   will not be called at all since this does nothing more than sending
+   a packet to the remote host.
+
+   NOTE: There can be only one active key agreement for one client entry.
+   Before setting new one, the old one must be finished (it is finished
+   after calling the completion callback) or the function 
+   silc_client_abort_key_agreement must be called. */
+
+void silc_client_send_key_agreement(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcClientEntry client_entry,
+                                   const char *hostname,
+                                   const char *bindhost,
+                                   int port,
+                                   uint32 timeout_secs,
+                                   SilcKeyAgreementCallback completion,
+                                   void *context)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcClientKeyAgreement ke = NULL;
+  SilcBuffer buffer;
+
+  if (!client_entry || client_entry->ke)
+    return;
+
+  /* Create the listener if hostname and port was provided.
+   * also, use bindhost if it was specified.
+   */
+   
+  if (hostname) {
+    ke = silc_calloc(1, sizeof(*ke));
+    
+    if (bindhost)
+      ke->fd = silc_net_create_server(port, bindhost);
+    else
+      ke->fd = silc_net_create_server(port, hostname);
+
+    if (ke->fd < 0) {
+      client->internal->ops->say(
+                    client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+                    "Cannot create listener on %s on port %d: %s", 
+                    (bindhost) ? bindhost:hostname, port, strerror(errno));
+      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+                NULL, context);
+      silc_free(ke);
+
+      return;
+    }
+
+    ke->client = client;
+    ke->conn = conn;
+    ke->client_entry = client_entry;
+    ke->completion = completion;
+    ke->context = context;
+
+    /* Add listener task to the scheduler. This task receives the key 
+       negotiations. */
+    silc_schedule_task_add(client->schedule, ke->fd,
+                          silc_client_process_key_agreement,
+                          (void *)ke, 0, 0, 
+                          SILC_TASK_FD,
+                          SILC_TASK_PRI_NORMAL);
+
+    /* Register a timeout task that will be executed if the connector
+       will not start the key exchange protocol within the specified 
+       timeout. */
+    ke->timeout = silc_schedule_task_add(client->schedule, 0, 
+                                        silc_client_key_agreement_timeout,
+                                        (void *)ke, timeout_secs, 0, 
+                                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+  }
+
+  /* Encode the key agreement payload */
+  buffer = silc_key_agreement_payload_encode(hostname, 
+                                            !ke ? port : 
+                                            silc_net_get_local_port(ke->fd));
+
+  /* Send the key agreement packet to the client */
+  silc_client_packet_send(client, sock, SILC_PACKET_KEY_AGREEMENT,
+                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                         buffer->data, buffer->len, FALSE);
+  silc_buffer_free(buffer);
+
+}
+
+static int 
+silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
+{
+  int sock;
+
+  /* Create connection to server asynchronously */
+  sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
+  if (sock < 0)
+    return -1;
+
+  /* Register task that will receive the async connect and will
+     read the result. */
+  ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
+                                    silc_client_perform_key_agreement_start,
+                                    (void *)ctx, 0, 0, 
+                                    SILC_TASK_FD,
+                                    SILC_TASK_PRI_NORMAL);
+  silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
+
+  ctx->sock = sock;
+
+  return sock;
+}
+
+/* Routine used by silc_client_perform_key_agreement to create connection
+   to the remote client on specified port. */
+
+static int
+silc_client_connect_to_client(SilcClient client, 
+                             SilcClientConnection conn, int port,
+                             char *host, void *context)
+{
+  SilcClientInternalConnectContext *ctx;
+
+  /* Allocate internal context for connection process. This is
+     needed as we are doing async connecting. */
+  ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->client = client;
+  ctx->conn = conn;
+  ctx->host = strdup(host);
+  ctx->port = port;
+  ctx->tries = 0;
+  ctx->context = context;
+
+  /* Do the actual connecting process */
+  return silc_client_connect_to_client_internal(ctx);
+}
+
+/* Callback that is called after connection has been created. This actually
+   starts the key agreement protocol. This is initiator function. */
+
+SILC_TASK_CALLBACK(silc_client_perform_key_agreement_start)
+{
+  SilcClientInternalConnectContext *ctx =
+    (SilcClientInternalConnectContext *)context;
+  SilcClient client = ctx->client;
+  SilcClientConnection conn = ctx->conn;
+  SilcClientKeyAgreement ke = (SilcClientKeyAgreement)ctx->context;
+  int opt, opt_len = sizeof(opt);
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Check the socket status as it might be in error */
+  silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
+  if (opt != 0) {
+    if (ctx->tries < 2) {
+      /* Connection failed but lets try again */
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of client %s resumed", 
+                                ctx->port, ctx->host);
+
+      /* Unregister old connection try */
+      silc_schedule_unset_listen_fd(client->schedule, fd);
+      silc_net_close_connection(fd);
+      silc_schedule_task_del(client->schedule, ctx->task);
+
+      /* Try again */
+      silc_client_connect_to_client_internal(ctx);
+      ctx->tries++;
+    } else {
+      /* Connection failed and we won't try anymore */
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      silc_schedule_unset_listen_fd(client->schedule, fd);
+      silc_net_close_connection(fd);
+      silc_schedule_task_del(client->schedule, ctx->task);
+      silc_free(ctx->host);
+      silc_free(ctx);
+
+      /* Call the completion callback */
+      ke->completion(ke->client, ke->conn, ke->client_entry, 
+                    SILC_KEY_AGREEMENT_FAILURE, NULL, ke->context);
+      silc_free(ke);
+    }
+    return;
+  }
+
+  silc_schedule_unset_listen_fd(client->schedule, fd);
+  silc_schedule_task_del(client->schedule, ctx->task);
+
+  ke->fd = fd;
+
+  /* Now actually perform the key agreement protocol */
+  silc_client_perform_key_agreement_fd(ke->client, ke->conn,
+                                      ke->client_entry, ke->fd, ctx->host,
+                                      ke->completion, ke->context);
+  silc_free(ke);
+  silc_free(ctx->host);
+  silc_free(ctx);
+}
+
+/* Performs the actual key agreement protocol. Application may use this
+   to initiate the key agreement protocol. This can be called for example
+   after the application has received the `key_agreement' client operation,
+   and did not return TRUE from it.
+
+   The `hostname' is the remote hostname (or IP address) and the `port'
+   is the remote port. The `completion' callback with the `context' will
+   be called after the key agreement protocol.
+   
+   NOTE: If the application returns TRUE in the `key_agreement' client
+   operation the library will automatically start the key agreement. In this
+   case the application must not call this function. However, application
+   may choose to just ignore the `key_agreement' client operation (and
+   merely just print information about it on the screen) and call this
+   function when the user whishes to do so (by, for example, giving some
+   specific command). Thus, the API provides both, automatic and manual
+   initiation of the key agreement. Calling this function is the manual
+   initiation and returning TRUE in the `key_agreement' client operation
+   is the automatic initiation. */
+
+void silc_client_perform_key_agreement(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry,
+                                      char *hostname,
+                                      int port,
+                                      SilcKeyAgreementCallback completion,
+                                      void *context)
+{
+  SilcClientKeyAgreement ke;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client_entry || !hostname || !port)
+    return;
+
+  ke = silc_calloc(1, sizeof(*ke));
+  ke->client = client;
+  ke->conn = conn;
+  ke->client_entry = client_entry;
+  ke->completion = completion;
+  ke->context = context;
+
+  /* Connect to the remote client */
+  ke->fd = silc_client_connect_to_client(client, conn, port, hostname, ke);
+  if (ke->fd < 0) {
+    completion(client, conn, client_entry, SILC_KEY_AGREEMENT_FAILURE,
+              NULL, context);
+    silc_free(ke);
+    return;
+  }
+}
+
+/* Same as above but application has created already the connection to 
+   the remote host. The `sock' is the socket to the remote connection. 
+   Application can use this function if it does not want the client library
+   to create the connection. */
+
+void silc_client_perform_key_agreement_fd(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         int sock,
+                                         char *hostname,
+                                         SilcKeyAgreementCallback completion,
+                                         void *context)
+{
+  SilcClientKeyAgreement ke;
+  SilcClientKEInternalContext *proto_ctx;
+  SilcProtocol protocol;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client_entry)
+    return;
+
+  ke = silc_calloc(1, sizeof(*ke));
+  ke->client = client;
+  ke->conn = conn;
+  ke->client_entry = client_entry;
+  ke->fd = sock;
+  ke->completion = completion;
+  ke->context = context;
+
+  /* Allocate new socket connection object */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, (void *)conn, &ke->sock);
+  silc_client_add_socket(client, ke->sock);
+  ke->sock->hostname = strdup(hostname);
+  ke->sock->port = silc_net_get_remote_port(sock);
+
+  /* Allocate internal context for key exchange protocol. This is
+     sent as context for the protocol. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->client = client;
+  proto_ctx->sock = silc_socket_dup(ke->sock);
+  proto_ctx->rng = client->rng;
+  proto_ctx->responder = FALSE;
+  proto_ctx->context = ke;
+  proto_ctx->send_packet = silc_client_key_agreement_send_packet;
+  proto_ctx->verify = silc_client_protocol_ke_verify_key;
+  ke->proto_ctx = proto_ctx;
+
+  /* Perform key exchange protocol. */
+  silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
+                     &protocol, (void *)proto_ctx,
+                     silc_client_key_agreement_final);
+  ke->sock->protocol = protocol;
+
+  /* Register the connection for network input and output. This sets
+     that scheduler will listen for incoming packets for this connection 
+     and sets that outgoing packets may be sent to this connection as well.
+     However, this doesn't set the scheduler for outgoing traffic, it will 
+     be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
+     later when outgoing data is available. */
+  context = (void *)client;
+  SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
+
+  /* Execute the protocol */
+  silc_protocol_execute(protocol, client->schedule, 0, 0);
+}
+
+/* This function can be called to unbind the hostname and the port for
+   the key agreement protocol. However, this function has effect only 
+   before the key agreement protocol has been performed. After it has
+   been performed the library will automatically unbind the port. The 
+   `client_entry' is the client to which we sent the key agreement 
+   request. */
+
+void silc_client_abort_key_agreement(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientEntry client_entry)
+{
+  if (!client_entry)
+    return;
+
+  if (client_entry->ke) {
+    SilcClientKeyAgreement ke;
+
+    if (client_entry->ke->sock) {
+      silc_client_del_socket(client_entry->ke->client, client_entry->ke->sock);
+      silc_socket_free(client_entry->ke->sock);
+    }
+    silc_schedule_task_del_by_fd(client->schedule, client_entry->ke->fd);
+    if (client_entry->ke->timeout)
+      silc_schedule_task_del(client->schedule, 
+                            client_entry->ke->timeout);
+    ke = client_entry->ke;
+    client_entry->ke = NULL;
+    ke->completion(client, conn, client_entry, 
+                  SILC_KEY_AGREEMENT_ABORTED, NULL, ke->context);
+    silc_free(ke);
+  }
+}
+
+/* Callback function that is called after we've resolved the client 
+   information who sent us the key agreement packet from the server.
+   We actually call the key_agreement client operation now. */
+
+static void 
+silc_client_key_agreement_resolve_cb(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientEntry *clients,
+                                    uint32 clients_count,
+                                    void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+  SilcKeyAgreementPayload payload;
+  int ret;
+  SilcKeyAgreementCallback completion;
+  void *completion_context;
+
+  if (!clients)
+    goto out;
+
+  /* Parse the key agreement payload */
+  payload = silc_key_agreement_payload_parse(packet->buffer->data,
+                                            packet->buffer->len);
+  if (!payload)
+    goto out;
+
+  /* Call the key_agreement client operation */
+  ret = client->internal->ops->key_agreement(
+                                  client, conn, clients[0], 
+                                  silc_key_agreement_get_hostname(payload),
+                                  silc_key_agreement_get_port(payload),
+                                  &completion, &completion_context);
+
+  /* If the user returned TRUE then we'll start the key agreement right
+     here and right now. */
+  if (ret == TRUE)
+    silc_client_perform_key_agreement(client, conn, clients[0],
+                                     silc_key_agreement_get_hostname(payload),
+                                     silc_key_agreement_get_port(payload),
+                                     completion, completion_context);
+
+  silc_key_agreement_payload_free(payload);
+
+ out:
+  silc_packet_context_free(packet);
+}
+
+/* Received Key Agreement packet from remote client. Process the packet
+   and resolve the client information from the server before actually
+   letting the application know that we've received this packet.  Then 
+   call the key_agreement client operation and let the user decide
+   whether we perform the key agreement protocol now or not. */
+
+void silc_client_key_agreement(SilcClient client,
+                              SilcSocketConnection sock,
+                              SilcPacketContext *packet)
+{
+  SilcClientID *remote_id;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    return;
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    return;
+
+  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                      silc_client_key_agreement_resolve_cb,
+                                      silc_packet_context_dup(packet));
+  silc_free(remote_id);
+}
diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c
new file mode 100644 (file)
index 0000000..09ca773
--- /dev/null
@@ -0,0 +1,879 @@
+/*
+
+  client_notify.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the Notify packet handling. Notify packets are
+   important packets sent by the server. They tell different things to the
+   client such as nick changes, mode changes etc. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+typedef struct {
+  SilcPacketContext *packet;
+  void *context;
+  SilcSocketConnection sock;
+} *SilcClientNotifyResolve;
+
+/* Called when notify is received and some async operation (such as command)
+   is required before processing the notify message. This calls again the
+   silc_client_notify_by_server and reprocesses the original notify packet. */
+
+static void silc_client_notify_by_server_pending(void *context, void *context2)
+{
+  SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
+  SilcClientCommandReplyContext reply = 
+    (SilcClientCommandReplyContext)context2;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (reply) {
+    SilcCommandStatus status;
+    unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
+    SILC_GET16_MSB(status, tmp);
+    if (status != SILC_STATUS_OK)
+      goto out;
+  }
+
+  silc_client_notify_by_server(res->context, res->sock, res->packet);
+
+ out:
+  silc_socket_free(res->sock);
+  silc_packet_context_free(res->packet);
+  silc_free(res);
+}
+
+/* Resolve client information from server by Client ID. */
+
+static void silc_client_notify_by_server_resolve(SilcClient client,
+                                                SilcClientConnection conn,
+                                                SilcPacketContext *packet,
+                                                SilcClientID *client_id)
+{
+  SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
+  SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+
+  res->packet = silc_packet_context_dup(packet);
+  res->context = client;
+  res->sock = silc_socket_dup(conn->sock);
+
+  silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
+                              silc_client_command_reply_whois_i, 0,
+                              ++conn->cmd_ident);
+  silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                             silc_client_notify_by_server_pending, res);
+  silc_buffer_free(idp);
+}
+
+/* Received notify message from server */
+
+void silc_client_notify_by_server(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcNotifyPayload payload;
+  SilcNotifyType type;
+  SilcArgumentPayload args;
+
+  SilcIDPayload idp;
+  SilcClientID *client_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcClientEntry client_entry;
+  SilcClientEntry client_entry2;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcServerEntry server;
+  unsigned char *tmp;
+  uint32 tmp_len, mode;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  payload = silc_notify_payload_parse(buffer->data, buffer->len);
+  if (!payload)
+    goto out;
+
+  type = silc_notify_get_type(payload);
+  args = silc_notify_get_args(payload);
+  if (!args)
+    goto out;
+
+  switch(type) {
+  case SILC_NOTIFY_TYPE_NONE:
+    /* Notify application */
+    client->internal->ops->notify(client, conn, type, 
+                                 silc_argument_get_arg_type(args, 1, NULL));
+    break;
+
+  case SILC_NOTIFY_TYPE_INVITE:
+    /* 
+     * Someone invited me to a channel. Find Client and Channel entries
+     * for the application.
+     */
+    
+    SILC_LOG_DEBUG(("Notify: INVITE"));
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get the channel entry */
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+
+    /* Get sender Client ID */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found query it */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get the channel name */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Notify application */
+    client->internal->ops->notify(client, conn, type, channel, tmp, 
+                                 client_entry);
+    break;
+
+  case SILC_NOTIFY_TYPE_JOIN:
+    /*
+     * Someone has joined to a channel. Get their ID and nickname and
+     * cache them for later use.
+     */
+
+    SILC_LOG_DEBUG(("Notify: JOIN"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found query it */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* If nickname or username hasn't been resolved, do so */
+    if (!client_entry->nickname || !client_entry->username) {
+      if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+       client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+       goto out;
+      }
+      client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    } else {
+      if (client_entry != conn->local_entry)
+       silc_client_nickname_format(client, conn, client_entry);
+    }
+
+    /* Get Channel ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get channel entry */
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Join the client to channel */
+    if (!silc_client_on_channel(channel, client_entry)) {
+      chu = silc_calloc(1, sizeof(*chu));
+      chu->client = client_entry;
+      chu->channel = channel;
+      silc_hash_table_add(channel->user_list, client_entry, chu);
+      silc_hash_table_add(client_entry->channels, channel, chu);
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->internal->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_LEAVE:
+    /*
+     * Someone has left a channel. We will remove it from the channel but
+     * we'll keep it in the cache in case we'll need it later.
+     */
+    
+    SILC_LOG_DEBUG(("Notify: LEAVE"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Remove client from channel */
+    chu = silc_client_on_channel(channel, client_entry);
+    if (chu) {
+      silc_hash_table_del(client_entry->channels, channel);
+      silc_hash_table_del(channel->user_list, client_entry);
+      silc_free(chu);
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->internal->ops->notify(client, conn, type, client_entry, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_SIGNOFF:
+    /*
+     * Someone left SILC. We'll remove it from all channels and from cache.
+     */
+
+    SILC_LOG_DEBUG(("Notify: SIGNOFF"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Remove from all channels */
+    silc_client_remove_from_channels(client, conn, client_entry);
+
+    /* Remove from cache */
+    silc_idcache_del_by_context(conn->client_cache, client_entry);
+
+    /* Get signoff message */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (tmp_len > 128)
+      tmp = NULL;
+
+    /* Notify application */
+    client->internal->ops->notify(client, conn, type, client_entry, tmp);
+
+    /* Free data */
+    silc_client_del_client_entry(client, conn, client_entry);
+    break;
+
+  case SILC_NOTIFY_TYPE_TOPIC_SET:
+    /*
+     * Someone set the topic on a channel.
+     */
+
+    SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    idp = silc_id_payload_parse(tmp, tmp_len);
+    if (!idp)
+      goto out;
+
+    /* Find Client entry */
+    if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
+      client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!client_id) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+
+      /* Find Client entry */
+      client_entry = 
+       silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry)
+       goto out;
+    } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
+      server_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!server_id) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+      
+      server = silc_client_get_server_by_id(client, conn, server_id);
+      if (!server) {
+       silc_id_payload_free(idp);
+       silc_free(server_id);
+       goto out;
+      }
+      
+      /* Save the pointer to the client_entry pointer */
+      client_entry = (SilcClientEntry)server;
+      silc_free(server_id);
+    } else {
+      channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!channel_id) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+      
+      channel = silc_client_get_channel_by_id(client, conn, channel_id);
+      if (!channel) {
+       silc_id_payload_free(idp);
+       silc_free(channel_id);
+       goto out;
+      }
+      
+      /* Save the pointer to the client_entry pointer */
+      client_entry = (SilcClientEntry)channel;
+      silc_free(channel_id);
+    }
+
+    /* Get topic */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->internal->ops->notify(client, conn, type, 
+                                 silc_id_payload_get_type(idp),
+                                 client_entry, tmp, channel);
+
+    silc_id_payload_free(idp);
+    break;
+
+  case SILC_NOTIFY_TYPE_NICK_CHANGE:
+    /*
+     * Someone changed their nickname. If we don't have entry for the new
+     * ID we will query it and return here after it's done. After we've
+     * returned we fetch the old entry and free it and notify the 
+     * application.
+     */
+
+    SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
+
+    /* Get old Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Ignore my ID */
+    if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
+      break;
+
+    /* Find old Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+    silc_free(client_id);
+
+    client_entry->valid = FALSE;
+
+    /* Get new Client ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry and if not found resolve it */
+    client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry2) {
+      /* Resolve the entry information */
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+
+      /* Add the new entry even though we resolved it. This is because we
+        want to replace the old entry with the new entry here right now. */
+      client_entry2 = 
+       silc_client_add_client(client, conn, NULL, NULL, NULL, 
+                              silc_id_dup(client_id, SILC_ID_CLIENT), 
+                              client_entry->mode);
+
+      /* Replace old ID entry with new one on all channels. */
+      silc_client_replace_from_channels(client, conn, client_entry,
+                                       client_entry2);
+    } else {
+      if (client_entry2 != conn->local_entry)
+       silc_client_nickname_format(client, conn, client_entry2);
+
+      /* Remove the old from cache */
+      silc_idcache_del_by_context(conn->client_cache, client_entry);
+
+      /* Replace old ID entry with new one on all channels. */
+      silc_client_replace_from_channels(client, conn, client_entry,
+                                       client_entry2);
+
+      /* Notify application */
+      client->internal->ops->notify(client, conn, type, 
+                                   client_entry, client_entry2);
+
+      /* Free data */
+      silc_client_del_client_entry(client, conn, client_entry);
+    }
+    break;
+
+  case SILC_NOTIFY_TYPE_CMODE_CHANGE:
+    /*
+     * Someone changed a channel mode
+     */
+
+    SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    idp = silc_id_payload_parse(tmp, tmp_len);
+    if (!idp)
+      goto out;
+
+    /* Find Client entry */
+    if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
+      client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!client_id) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+
+      client_entry = silc_client_get_client_by_id(client, conn, client_id);
+      if (!client_entry) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+    } else {
+      server_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (!server_id) {
+       silc_id_payload_free(idp);
+       goto out;
+      }
+      
+      server = silc_client_get_server_by_id(client, conn, server_id);
+      if (!server) {
+       silc_id_payload_free(idp);
+       silc_free(server_id);
+       goto out;
+      }
+      
+      /* Save the pointer to the client_entry pointer */
+      client_entry = (SilcClientEntry)server;
+      silc_free(server_id);
+    }
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp) {
+      silc_id_payload_free(idp);
+      goto out;
+    }
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id) {
+      silc_id_payload_free(idp);
+      goto out;
+    }
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel) {
+      silc_id_payload_free(idp);
+      goto out;
+    }
+
+    /* Save the new mode */
+    channel->mode = mode;
+
+    /* Get the hmac */
+    tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+    if (tmp) {
+      unsigned char hash[32];
+
+      if (channel->hmac)
+       silc_hmac_free(channel->hmac);
+      if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
+       goto out;
+
+      silc_hash_make(silc_hmac_get_hash(channel->hmac), 
+                    channel->key, channel->key_len / 8,
+                    hash);
+      silc_hmac_set_key(channel->hmac, hash, 
+                       silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+      memset(hash, 0, sizeof(hash));
+    }
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->internal->ops->notify(client, conn, type, 
+                                 silc_id_payload_get_type(idp), 
+                                 client_entry, mode, NULL, tmp, channel);
+
+    silc_id_payload_free(idp);
+    break;
+
+  case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
+    /*
+     * Someone changed user's mode on a channel
+     */
+
+    SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    }
+
+    /* Get the mode */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    SILC_GET32_MSB(mode, tmp);
+
+    /* Get target Client ID */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    silc_free(client_id);
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find target Client entry */
+    client_entry2 = 
+      silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry2)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Save the mode */
+    chu = silc_client_on_channel(channel, client_entry2);
+    if (chu)
+      chu->mode = mode;
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->internal->ops->notify(client, conn, type, 
+                                 client_entry, mode, 
+                                 client_entry2, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_MOTD:
+    /*
+     * Received Message of the day
+     */
+
+    SILC_LOG_DEBUG(("Notify: MOTD"));
+
+    /* Get motd */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    
+    /* Notify application */
+    client->internal->ops->notify(client, conn, type, tmp);
+    break;
+
+  case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
+    /*
+     * Router has enforced a new ID to a channel. Let's change the old
+     * ID to the one provided here.
+     */
+
+    SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
+
+    /* Get the old ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Get the channel entry */
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      goto out;
+
+    silc_free(channel_id);
+
+    /* Get the new ID */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+    if (!tmp)
+      goto out;
+    channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!channel_id)
+      goto out;
+
+    /* Replace the Channel ID */
+    silc_client_replace_channel_id(client, conn, channel, channel_id);
+
+    /* Notify application */
+    client->internal->ops->notify(client, conn, type, channel, channel);
+    break;
+
+  case SILC_NOTIFY_TYPE_KICKED:
+    /*
+     * A client (maybe me) was kicked from a channel
+     */
+
+    SILC_LOG_DEBUG(("Notify: KICKED"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get channel entry */
+    channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+                               SILC_ID_CHANNEL);
+    if (!channel_id)
+      goto out;
+    channel = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel)
+      break;
+
+    /* Get the kicker */
+    tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find kicker's client entry and if not found resolve it */
+    client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry2) {
+      silc_client_notify_by_server_resolve(client, conn, packet, client_id);
+      goto out;
+    } else {
+      if (client_entry2 != conn->local_entry)
+       silc_client_nickname_format(client, conn, client_entry2);
+    }
+
+    /* Get comment */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+
+    /* Notify application. The channel entry is sent last as this notify
+       is for channel but application don't know it from the arguments
+       sent by server. */
+    client->internal->ops->notify(client, conn, type, client_entry, tmp, 
+                                 client_entry2, channel);
+
+    /* If I was kicked from channel, remove the channel */
+    if (client_entry == conn->local_entry) {
+      if (conn->current_channel == channel)
+       conn->current_channel = NULL;
+      silc_idcache_del_by_id(conn->channel_cache, channel->id);
+      silc_free(channel->channel_name);
+      silc_free(channel->id);
+      silc_free(channel->key);
+      silc_cipher_free(channel->channel_key);
+      silc_free(channel);
+    }
+    break;
+
+  case SILC_NOTIFY_TYPE_KILLED:
+    /*
+     * A client (maybe me) was killed from the network.
+     */
+
+    SILC_LOG_DEBUG(("Notify: KILLED"));
+
+    /* Get Client ID */
+    tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
+    if (!tmp)
+      goto out;
+
+    client_id = silc_id_payload_parse_id(tmp, tmp_len);
+    if (!client_id)
+      goto out;
+
+    /* Find Client entry */
+    client_entry = silc_client_get_client_by_id(client, conn, client_id);
+    if (!client_entry)
+      goto out;
+
+    /* Get comment */
+    tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
+
+    /* Notify application. */
+    client->internal->ops->notify(client, conn, type, client_entry, tmp);
+
+    if (client_entry != conn->local_entry)
+      /* Remove the client from all channels and free it */
+      silc_client_del_client(client, conn, client_entry);
+
+    break;
+    
+  case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
+    {
+      /*
+       * A server quit the SILC network and some clients must be removed
+       * from channels as they quit as well.
+       */
+      SilcClientEntry *clients = NULL;
+      uint32 clients_count = 0;
+      int i;
+
+      SILC_LOG_DEBUG(("Notify: SIGNOFF"));
+
+      for (i = 1; i < silc_argument_get_arg_num(args); i++) {
+       /* Get Client ID */
+       tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
+       if (tmp) {
+         client_id = silc_id_payload_parse_id(tmp, tmp_len);
+         if (!client_id)
+           goto out;
+         
+         /* Get the client entry */
+         client_entry = silc_client_get_client_by_id(client, conn, client_id);
+         if (client_entry) {
+           clients = silc_realloc(clients, sizeof(*clients) * 
+                                  (clients_count + 1));
+           clients[clients_count] = client_entry;
+           clients_count++;
+         }
+         silc_free(client_id);
+       }
+      }
+      client_id = NULL;
+
+      /* Notify application. We don't keep server entries so the server
+        entry is returned as NULL. The client's are returned as array
+        of SilcClientEntry pointers. */
+      client->internal->ops->notify(client, conn, type, NULL, 
+                                   clients, clients_count);
+
+      for (i = 0; i < clients_count; i++) {
+       /* Remove client from all channels */
+       client_entry = clients[i];
+       if (client_entry == conn->local_entry)
+         continue;
+
+       /* Remove the client from all channels and free it */
+       silc_client_del_client(client, conn, client_entry);
+      }
+      silc_free(clients);
+
+    }
+    break;
+
+  default:
+    break;
+  }
+
+ out:
+  silc_notify_payload_free(payload);
+  silc_free(client_id);
+  silc_free(channel_id);
+}
diff --git a/lib/silcclient/client_ops_example.c b/lib/silcclient/client_ops_example.c
new file mode 100644 (file)
index 0000000..d472a05
--- /dev/null
@@ -0,0 +1,241 @@
+/* Predefined stub functions for the SilcClientOperation callbacks.
+   You can freely use this template in your application. These are
+   the functions that you as an application programmer need to implement
+   for the library.  The library may call these functions at any time.
+
+   At the end of this file SilcClientOperation structure is defined, and
+   it is the one the you will give as an argument to the silc_client_alloc
+   function. See also lib/silcclient/README file, and silcapi.h. */
+
+
+/* Message sent to the application by library. `conn' associates the
+   message to a specific connection.  `conn', however, may be NULL. 
+   The `type' indicates the type of the message sent by the library.
+   The applicationi can for example filter the message according the
+   type. */
+
+static void 
+silc_say(SilcClient client, SilcClientConnection conn, 
+        SilcClientMessageType type, char *msg, ...)
+{
+
+}
+
+
+/* Message for a channel. The `sender' is the sender of the message
+   The `channel' is the channel. The `msg' is the message.  Note that  
+   `msg' maybe NULL. */
+
+static void 
+silc_channel_message(SilcClient client, SilcClientConnection conn, 
+                    SilcClientEntry sender, SilcChannelEntry channel, 
+                    SilcMessageFlags flags, char *msg)
+{
+
+}
+
+
+/* Private message to the client. The `sender' is the sender of the
+   message. */
+
+static void 
+silc_private_message(SilcClient client, SilcClientConnection conn, 
+                    SilcClientEntry sender, SilcMessageFlags flags, char *msg)
+{
+
+}
+
+
+/* Notify message to the client. The notify arguments are sent in the
+   same order as servers sends them. The arguments are same as received
+   from the server except for ID's.  If ID is received application receives
+   the corresponding entry to the ID. For example, if Client ID is received
+   application receives SilcClientEntry.  Also, if the notify type is
+   for channel the channel entry is sent to application (even if server
+   does not send it because client library gets the channel entry from
+   the Channel ID in the packet's header). */
+
+static void 
+silc_notify(SilcClient client, SilcClientConnection conn, 
+           SilcNotifyType type, ...)
+{
+
+}
+
+
+/* Command handler. This function is called always in the command function.
+   If error occurs it will be called as well. `conn' is the associated
+   client connection. `cmd_context' is the command context that was
+   originally sent to the command. `success' is FALSE if error occurred
+   during command. `command' is the command being processed. It must be
+   noted that this is not reply from server. This is merely called just
+   after application has called the command. Just to tell application
+   that the command really was processed. */
+
+static void 
+silc_command(SilcClient client, SilcClientConnection conn, 
+            SilcClientCommandContext cmd_context, int success, 
+            SilcCommand command)
+{
+
+}
+
+
+/* Command reply handler. This function is called always in the command reply
+   function. If error occurs it will be called as well. Normal scenario
+   is that it will be called after the received command data has been parsed
+   and processed. The function is used to pass the received command data to
+   the application. 
+   
+   `conn' is the associated client connection. `cmd_payload' is the command
+   payload data received from server and it can be ignored. It is provided
+   if the application would like to re-parse the received command data,
+   however, it must be noted that the data is parsed already by the library
+   thus the payload can be ignored. `success' is FALSE if error occurred.
+   In this case arguments are not sent to the application. The `status' is
+   the command reply status server returned. The `command' is the command
+   reply being processed. The function has variable argument list and each
+   command defines the number and type of arguments it passes to the
+   application (on error they are not sent). */
+
+static void 
+silc_command_reply(SilcClient client, SilcClientConnection conn, 
+                  SilcCommandPayload cmd_payload, int success, 
+                  SilcCommand command, SilcCommandStatus status, ...)
+{
+
+}
+
+
+/* Called to indicate that connection was either successfully established
+   or connecting failed.  This is also the first time application receives
+   the SilcClientConnection objecet which it should save somewhere.
+   If the `success' is FALSE the application must always call the function
+   silc_client_close_connection. */
+
+static void 
+silc_connect(SilcClient client, SilcClientConnection conn, int success)
+{
+
+}
+
+
+/* Called to indicate that connection was disconnected to the server. */
+
+static void 
+silc_disconnect(SilcClient client, SilcClientConnection conn)
+{
+
+}
+
+
+/* Find authentication method and authentication data by hostname and
+   port. The hostname may be IP address as well. When the authentication
+   method has been resolved the `completion' callback with the found
+   authentication method and authentication data is called. The `conn'
+   may be NULL. */
+
+static void 
+silc_get_auth_method(SilcClient client, SilcClientConnection conn, 
+                    char *hostname, uint16 port, SilcGetAuthMeth completion, 
+                    void *context)
+{
+
+}
+
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the key may be saved as trusted public key for later use. The 
+   `completion' must be called after the public key has been verified. */
+
+static void 
+silc_verify_public_key(SilcClient client, SilcClientConnection conn, 
+                      SilcSocketType conn_type, unsigned char *pk, 
+                      uint32 pk_len, SilcSKEPKType pk_type, 
+                      SilcVerifyPublicKey completion, void *context)
+{
+
+}
+
+
+/* Ask (interact, that is) a passphrase from user. The passphrase is
+   returned to the library by calling the `completion' callback with
+   the `context'. */
+
+static void 
+silc_ask_passphrase(SilcClient client, SilcClientConnection conn, 
+                   SilcAskPassphrase completion, void *context)
+{
+
+}
+
+
+/* Notifies application that failure packet was received.  This is called
+   if there is some protocol active in the client.  The `protocol' is the
+   protocol context.  The `failure' is opaque pointer to the failure
+   indication.  Note, that the `failure' is protocol dependant and
+   application must explicitly cast it to correct type.  Usually `failure'
+   is 32 bit failure type (see protocol specs for all protocol failure
+   types). */
+
+static void 
+silc_failure(SilcClient client, SilcClientConnection conn, 
+            SilcProtocol protocol, void *failure)
+{
+
+}
+
+
+/* Asks whether the user would like to perform the key agreement protocol.
+   This is called after we have received an key agreement packet or an
+   reply to our key agreement packet. This returns TRUE if the user wants
+   the library to perform the key agreement protocol and FALSE if it is not
+   desired (application may start it later by calling the function
+   silc_client_perform_key_agreement). If TRUE is returned also the
+   `completion' and `context' arguments must be set by the application. */
+
+static int 
+silc_key_agreement(SilcClient client, SilcClientConnection conn, 
+                  SilcClientEntry client_entry, const char *hostname, 
+                  uint16 port, SilcKeyAgreementCallback *completion, 
+                  void **context)
+{
+
+}
+
+
+/* Notifies application that file transfer protocol session is being
+   requested by the remote client indicated by the `client_entry' from
+   the `hostname' and `port'. The `session_id' is the file transfer
+   session and it can be used to either accept or reject the file
+   transfer request, by calling the silc_client_file_receive or
+   silc_client_file_close, respectively. */
+
+static void 
+silc_ftp(SilcClient client, SilcClientConnection conn, 
+        SilcClientEntry client_entry, uint32 session_id, 
+        const char *hostname, uint16 port)
+{
+
+}
+
+
+/* The SilcClientOperation structure containing the operation functions.
+   You will give this as an argument to silc_client_alloc function. */
+SilcClientOperations ops = {
+  silc_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connect,
+  silc_disconnect,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_failure,
+  silc_key_agreement,
+  silc_ftp
+};
diff --git a/lib/silcclient/client_prvmsg.c b/lib/silcclient/client_prvmsg.c
new file mode 100644 (file)
index 0000000..450fb40
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+
+  client_prvmsg.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* This file includes the private message sending and receiving routines
+   and private message key handling routines. */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/* Sends private message to remote client. If private message key has
+   not been set with this client then the message will be encrypted using
+   normal session keys. Private messages are special packets in SILC
+   network hence we need this own function for them. This is similiar
+   to silc_client_packet_send_to_channel except that we send private
+   message. The `data' is the private message. If the `force_send' is
+   TRUE the packet is sent immediately. */
+
+void silc_client_send_private_message(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry,
+                                     SilcMessageFlags flags,
+                                     unsigned char *data, 
+                                     uint32 data_len, 
+                                     int force_send)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer buffer;
+  SilcPacketContext packetdata;
+  SilcCipher cipher;
+  SilcHmac hmac;
+  int block_len;
+
+  SILC_LOG_DEBUG(("Sending private message"));
+
+  /* Encode private message payload */
+  buffer = silc_private_message_payload_encode(flags,
+                                              data_len, data,
+                                              client_entry->send_key);
+
+  /* If we don't have private message specific key then private messages
+     are just as any normal packet thus call normal packet sending.  If
+     the key exist then the encryption process is a bit different and
+     will be done in the rest of this function. */
+  if (!client_entry->send_key) {
+    silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
+                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                           buffer->data, buffer->len, force_send);
+    goto out;
+  }
+
+  /* We have private message specific key */
+
+  /* Get data used in the encryption */
+  cipher = client_entry->send_key;
+  hmac = conn->hmac_send;
+  block_len = silc_cipher_get_block_len(cipher);
+
+  /* Set the packet context pointers. */
+  packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
+  packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
+  packetdata.src_id = conn->local_id_data;
+  packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
+  packetdata.src_id_type = SILC_ID_CLIENT;
+  packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
+  packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
+  packetdata.dst_id_type = SILC_ID_CLIENT;
+  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
+    packetdata.src_id_len + packetdata.dst_id_len;
+  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                                         packetdata.src_id_len +
+                                         packetdata.dst_id_len), block_len);
+
+  /* Prepare outgoing data buffer for packet sending */
+  silc_packet_send_prepare(sock, 
+                          SILC_PACKET_HEADER_LEN +
+                          packetdata.src_id_len + 
+                          packetdata.dst_id_len,
+                          packetdata.padlen,
+                          buffer->len);
+  
+  packetdata.buffer = sock->outbuf;
+
+  /* Put the actual encrypted message payload data into the buffer. */
+  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+
+  /* Create the outgoing packet */
+  silc_packet_assemble(&packetdata, cipher);
+
+  /* Encrypt the header and padding of the packet. */
+  cipher = conn->send_key;
+  silc_packet_encrypt(cipher, hmac, conn->psn_send++,
+                     sock->outbuf, SILC_PACKET_HEADER_LEN + 
+                     packetdata.src_id_len + packetdata.dst_id_len +
+                     packetdata.padlen);
+
+  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
+                  sock->outbuf->data, sock->outbuf->len);
+
+  /* Now actually send the packet */
+  silc_client_packet_send_real(client, sock, force_send);
+  silc_free(packetdata.dst_id);
+
+ out:
+  silc_buffer_free(buffer);
+}     
+
+static void silc_client_private_message_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcClientEntry *clients,
+                                          uint32 clients_count,
+                                          void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+
+  if (!clients) {
+    silc_packet_context_free(packet);
+    return;
+  }
+
+  silc_client_private_message(client, conn->sock, packet);
+  silc_packet_context_free(packet);
+}
+
+/* Private message received. This processes the private message and
+   finally displays it on the screen. */
+
+void silc_client_private_message(SilcClient client, 
+                                SilcSocketConnection sock, 
+                                SilcPacketContext *packet)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  SilcPrivateMessagePayload payload = NULL;
+  SilcClientID *remote_id = NULL;
+  SilcClientEntry remote_client;
+  SilcMessageFlags flags;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    goto out;
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    goto out;
+
+  /* Check whether we know this client already */
+  remote_client = silc_client_get_client_by_id(client, conn, remote_id);
+  if (!remote_client || !remote_client->nickname) {
+    if (remote_client) {
+      if (remote_client->status & SILC_CLIENT_STATUS_RESOLVING) {
+       remote_client->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+       goto out;
+      }
+      remote_client->status |= SILC_CLIENT_STATUS_RESOLVING;
+    }
+
+    /* Resolve the client info */
+    silc_client_get_client_by_id_resolve(client, conn, remote_id,
+                                        silc_client_private_message_cb,
+                                        silc_packet_context_dup(packet));
+    return;
+  }
+
+  /* Parse the payload and decrypt it also if private message key is set */
+  payload = silc_private_message_payload_parse(packet->buffer->data,
+                                              packet->buffer->len,
+                                              remote_client->receive_key);
+  if (!payload) {
+    silc_free(remote_id);
+    return;
+  }
+
+  flags = silc_private_message_get_flags(payload);
+
+  /* Pass the private message to application */
+  client->internal->ops->private_message(
+                                client, conn, remote_client, flags,
+                                silc_private_message_get_message(payload, 
+                                                                 NULL));
+
+  /* See if we are away (gone). If we are away we will reply to the
+     sender with the set away message. */
+  if (conn->away && conn->away->away && !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
+    /* If it's me, ignore */
+    if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+      goto out;
+
+    /* Send the away message */
+    silc_client_send_private_message(client, conn, remote_client,
+                                    SILC_MESSAGE_FLAG_AUTOREPLY |
+                                    SILC_MESSAGE_FLAG_NOREPLY,
+                                    conn->away->away,
+                                    strlen(conn->away->away), TRUE);
+  }
+
+ out:
+  if (payload)
+    silc_private_message_payload_free(payload);
+  silc_free(remote_id);
+}
+
+/* Function that actually employes the received private message key */
+
+static void silc_client_private_message_key_cb(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcClientEntry *clients,
+                                              uint32 clients_count,
+                                              void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+  unsigned char *key;
+  uint16 key_len;
+  unsigned char *cipher;
+  int ret;
+
+  if (!clients)
+    goto out;
+
+  /* Parse the private message key payload */
+  ret = silc_buffer_unformat(packet->buffer,
+                            SILC_STR_UI16_NSTRING(&key, &key_len),
+                            SILC_STR_UI16_STRING(&cipher),
+                            SILC_STR_END);
+  if (!ret)
+    goto out;
+
+  if (key_len > packet->buffer->len)
+    goto out;
+
+  /* Now take the key in use */
+  if (!silc_client_add_private_message_key(client, conn, clients[0],
+                                          cipher, key, key_len, FALSE, TRUE))
+    goto out;
+
+  /* Print some info for application */
+  client->internal->ops->say(
+                    client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                    "Received private message key from %s%s%s %s%s%s", 
+                    clients[0]->nickname,
+                    clients[0]->server ? "@" : "",
+                    clients[0]->server ? clients[0]->server : "",
+                    clients[0]->username ? "(" : "",
+                    clients[0]->username ? clients[0]->username : "",
+                    clients[0]->username ? ")" : "");
+
+ out:
+  silc_packet_context_free(packet);
+}
+
+/* Processes incoming Private Message Key payload. The libary always
+   accepts the key and takes it into use. */
+
+void silc_client_private_message_key(SilcClient client,
+                                    SilcSocketConnection sock,
+                                    SilcPacketContext *packet)
+{
+  SilcClientID *remote_id;
+
+  if (packet->src_id_type != SILC_ID_CLIENT)
+    return;
+
+  remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
+                            SILC_ID_CLIENT);
+  if (!remote_id)
+    return;
+
+  silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                      silc_client_private_message_key_cb,
+                                      silc_packet_context_dup(packet));
+  silc_free(remote_id);
+}
+
+/* Adds private message key to the client library. The key will be used to
+   encrypt all private message between the client and the remote client
+   indicated by the `client_entry'. If the `key' is NULL and the boolean
+   value `generate_key' is TRUE the library will generate random key.
+   The `key' maybe for example pre-shared-key, passphrase or similar.
+   The `cipher' MAY be provided but SHOULD be NULL to assure that the
+   requirements of the SILC protocol are met. The API, however, allows
+   to allocate any cipher.
+
+   If `responder' is TRUE then the sending and receiving keys will be
+   set according the client being the receiver of the private key.  If
+   FALSE the client is being the sender (or negotiator) of the private
+   key.
+
+   It is not necessary to set key for normal private message usage. If the
+   key is not set then the private messages are encrypted using normal
+   session keys. Setting the private key, however, increases the security. 
+
+   Returns FALSE if the key is already set for the `client_entry', TRUE
+   otherwise. */
+
+int silc_client_add_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry,
+                                       char *cipher,
+                                       unsigned char *key,
+                                       uint32 key_len,
+                                       bool generate_key,
+                                       bool responder)
+{
+  unsigned char private_key[32];
+  uint32 len;
+  int i;
+  SilcSKEKeyMaterial *keymat;
+
+  assert(client_entry);
+
+  /* Return FALSE if key already set */
+  if (client_entry->send_key && client_entry->receive_key)
+    return FALSE;
+
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+
+  /* Check the requested cipher */
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  /* Generate key if not provided */
+  if (generate_key == TRUE) {
+    len = 32;
+    for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
+    key = private_key;
+    key_len = len;
+    client_entry->generated = TRUE;
+  }
+
+  /* Save the key */
+  client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
+  memcpy(client_entry->key, key, key_len);
+  client_entry->key_len = key_len;
+
+  /* Produce the key material as the protocol defines */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
+                                        client->internal->md5hash, keymat) 
+      != SILC_SKE_STATUS_OK)
+    return FALSE;
+
+  /* Allocate the ciphers */
+  silc_cipher_alloc(cipher, &client_entry->send_key);
+  silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+  /* Set the keys */
+  if (responder == TRUE) {
+    silc_cipher_set_key(client_entry->send_key, keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, keymat->receive_iv);
+    silc_cipher_set_key(client_entry->receive_key, keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, keymat->send_iv);
+  } else {
+    silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
+    silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
+  }
+
+  /* Free the key material */
+  silc_ske_free_key_material(keymat);
+
+  return TRUE;
+}
+
+/* Same as above but takes the key material from the SKE key material
+   structure. This structure is received if the application uses the
+   silc_client_send_key_agreement to negotiate the key material. The
+   `cipher' SHOULD be provided as it is negotiated also in the SKE
+   protocol. */
+
+int silc_client_add_private_message_key_ske(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcClientEntry client_entry,
+                                           char *cipher,
+                                           SilcSKEKeyMaterial *key,
+                                           bool responder)
+{
+  assert(client_entry);
+
+  /* Return FALSE if key already set */
+  if (client_entry->send_key && client_entry->receive_key)
+    return FALSE;
+
+  if (!cipher)
+    cipher = SILC_DEFAULT_CIPHER;
+
+  /* Check the requested cipher */
+  if (!silc_cipher_is_supported(cipher))
+    return FALSE;
+
+  /* Allocate the ciphers */
+  silc_cipher_alloc(cipher, &client_entry->send_key);
+  silc_cipher_alloc(cipher, &client_entry->receive_key);
+
+  /* Set the keys */
+  if (responder == TRUE) {
+    silc_cipher_set_key(client_entry->send_key, key->receive_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, key->receive_iv);
+    silc_cipher_set_key(client_entry->receive_key, key->send_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, key->send_iv);
+  } else {
+    silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, key->send_iv);
+    silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+  }
+
+  return TRUE;
+}
+
+/* Sends private message key payload to the remote client indicated by
+   the `client_entry'. If the `force_send' is TRUE the packet is sent
+   immediately. Returns FALSE if error occurs, TRUE otherwise. The
+   application should call this function after setting the key to the
+   client.
+
+   Note that the key sent using this function is sent to the remote client
+   through the SILC network. The packet is protected using normal session
+   keys. */
+
+int silc_client_send_private_message_key(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        int force_send)
+{
+  SilcSocketConnection sock = conn->sock;
+  SilcBuffer buffer;
+  int cipher_len;
+
+  if (!client_entry->send_key || !client_entry->key)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Sending private message key"));
+
+  cipher_len = strlen(client_entry->send_key->cipher->name);
+
+  /* Create private message key payload */
+  buffer = silc_buffer_alloc(2 + client_entry->key_len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(client_entry->key_len),
+                    SILC_STR_UI_XNSTRING(client_entry->key, 
+                                         client_entry->key_len),
+                    SILC_STR_UI_SHORT(cipher_len),
+                    SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
+                                         cipher_len),
+                    SILC_STR_END);
+
+  /* Send the packet */
+  silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
+                         client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                         buffer->data, buffer->len, force_send);
+  silc_free(buffer);
+
+  return TRUE;
+}
+
+/* Removes the private message from the library. The key won't be used
+   after this to protect the private messages with the remote `client_entry'
+   client. Returns FALSE on error, TRUE otherwise. */
+
+int silc_client_del_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry)
+{
+  assert(client_entry);
+
+  if (!client_entry->send_key && !client_entry->receive_key)
+    return FALSE;
+
+  silc_cipher_free(client_entry->send_key);
+  silc_cipher_free(client_entry->receive_key);
+
+  if (client_entry->key) {
+    memset(client_entry->key, 0, client_entry->key_len);
+    silc_free(client_entry->key);
+  }
+
+  client_entry->send_key = NULL;
+  client_entry->receive_key = NULL;
+  client_entry->key = NULL;
+
+  return TRUE;
+}
+
+/* Returns array of set private message keys associated to the connection
+   `conn'. Returns allocated SilcPrivateMessageKeys array and the array
+   count to the `key_count' argument. The array must be freed by the caller
+   by calling the silc_client_free_private_message_keys function. Note: 
+   the keys returned in the array is in raw format. It might not be desired
+   to show the keys as is. The application might choose not to show the keys
+   at all or to show the fingerprints of the keys. */
+
+SilcPrivateMessageKeys
+silc_client_list_private_message_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     uint32 *key_count)
+{
+  SilcPrivateMessageKeys keys;
+  uint32 count = 0;
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list;
+  SilcClientEntry entry;
+
+  if (!silc_idcache_get_all(conn->client_cache, &list))
+    return NULL;
+
+  if (!silc_idcache_list_count(list)) {
+    silc_idcache_list_free(list);
+    return NULL;
+  }
+
+  keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
+
+  silc_idcache_list_first(list, &id_cache);
+  while (id_cache) {
+    entry = (SilcClientEntry)id_cache->context;
+
+    if (entry->send_key) {
+      keys[count].client_entry = entry;
+      keys[count].cipher = entry->send_key->cipher->name;
+      keys[count].key = entry->generated == FALSE ? entry->key : NULL;
+      keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
+      count++;
+    }
+
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+
+  if (key_count)
+    *key_count = count;
+
+  return keys;
+}
+
+/* Frees the SilcPrivateMessageKeys array returned by the function
+   silc_client_list_private_message_keys. */
+
+void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
+                                          uint32 key_count)
+{
+  silc_free(keys);
+}
+
+/* Sets away `message'.  The away message may be set when the client's
+   mode is changed to SILC_UMODE_GONE and the client whishes to reply
+   to anyone who sends private message.  The `message' will be sent
+   automatically back to the the client who send private message.  If
+   away message is already set this replaces the old message with the
+   new one.  If `message' is NULL the old away message is removed. 
+   The sender may freely free the memory of the `message'. */
+
+void silc_client_set_away_message(SilcClient client,
+                                 SilcClientConnection conn,
+                                 char *message)
+{
+  if (!message && conn->away) {
+    silc_free(conn->away->away);
+    silc_free(conn->away);
+    conn->away = NULL;
+  }
+
+  if (message) {
+    if (!conn->away)
+      conn->away = silc_calloc(1, sizeof(*conn->away));
+    if (conn->away->away)
+      silc_free(conn->away->away);
+    conn->away->away = strdup(message);
+  }
+}
diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c
new file mode 100644 (file)
index 0000000..7c94838
--- /dev/null
@@ -0,0 +1,2352 @@
+/*
+
+  command.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+#define SILC_NOT_CONNECTED(x, c) \
+  x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
+          "You are not connected to a server, use /SERVER to connect");
+
+/* Command operation that is called at the end of all commands. 
+   Usage: COMMAND; */
+#define COMMAND cmd->client->internal->ops->command(cmd->client, cmd->conn, \
+  cmd, TRUE, cmd->command->cmd)
+
+/* Error to application. Usage: COMMAND_ERROR; */
+#define COMMAND_ERROR cmd->client->internal->ops->command(cmd->client, \
+  cmd->conn, cmd, FALSE, cmd->command->cmd)
+
+#define SAY cmd->client->internal->ops->say
+
+/* Generic function to send any command. The arguments must be sent already
+   encoded into correct form and in correct order. */
+
+void silc_client_command_send(SilcClient client, SilcClientConnection conn,
+                             SilcCommand command, uint16 ident,
+                             uint32 argc, ...)
+{
+  SilcBuffer packet;
+  va_list ap;
+
+  va_start(ap, argc);
+
+  packet = silc_command_payload_encode_vap(command, ident, argc, ap);
+  silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
+                         NULL, 0, NULL, NULL, packet->data, 
+                         packet->len, TRUE);
+  silc_buffer_free(packet);
+}
+
+/* Finds and returns a pointer to the command list. Return NULL if the
+   command is not found. */
+
+SilcClientCommand silc_client_command_find(SilcClient client,
+                                          const char *name)
+{
+  SilcClientCommand cmd;
+
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->name && !strcmp(cmd->name, name))
+      return cmd;
+  }
+
+  return NULL;
+}
+
+/* Calls the command (executes it).  Application can call this after
+   it has allocated the SilcClientCommandContext with the function
+   silc_client_command_alloc and found the command from the client
+   library by calling silc_client_command_find.  This will execute
+   the command. */
+
+void silc_client_command_call(SilcClientCommand command, 
+                             SilcClientCommandContext cmd)
+{
+  (*command->command)((void *)cmd, NULL);
+}
+
+/* Add new pending command to be executed when reply to a command has been
+   received.  The `reply_cmd' is the command that will call the `callback'
+   with `context' when reply has been received.  If `ident is non-zero
+   the `callback' will be executed when received reply with command 
+   identifier `ident'. */
+
+void silc_client_command_pending(SilcClientConnection conn,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
+                                SilcCommandCb callback,
+                                void *context)
+{
+  SilcClientCommandPending *reply;
+
+  reply = silc_calloc(1, sizeof(*reply));
+  reply->reply_cmd = reply_cmd;
+  reply->ident = ident;
+  reply->context = context;
+  reply->callback = callback;
+  silc_dlist_add(conn->pending_commands, reply);
+}
+
+/* Deletes pending command by reply command type. */
+
+void silc_client_command_pending_del(SilcClientConnection conn,
+                                    SilcCommand reply_cmd,
+                                    uint16 ident)
+{
+  SilcClientCommandPending *r;
+
+  silc_dlist_start(conn->pending_commands);
+  while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
+    if (r->reply_cmd == reply_cmd && r->ident == ident) {
+      silc_dlist_del(conn->pending_commands, r);
+      break;
+    }
+  }
+}
+
+/* Checks for pending commands and marks callbacks to be called from
+   the command reply function. Returns TRUE if there were pending command. */
+
+int silc_client_command_pending_check(SilcClientConnection conn,
+                                     SilcClientCommandReplyContext ctx,
+                                     SilcCommand command, 
+                                     uint16 ident)
+{
+  SilcClientCommandPending *r;
+
+  silc_dlist_start(conn->pending_commands);
+  while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
+    if (r->reply_cmd == command && r->ident == ident) {
+      ctx->context = r->context;
+      ctx->callback = r->callback;
+      ctx->ident = ident;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Allocate Command Context */
+
+SilcClientCommandContext silc_client_command_alloc(void)
+{
+  SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->users++;
+  return ctx;
+}
+
+/* Free command context and its internals */
+
+void silc_client_command_free(SilcClientCommandContext ctx)
+{
+  ctx->users--;
+  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
+                 ctx->users));
+  if (ctx->users < 1) {
+    int i;
+
+    for (i = 0; i < ctx->argc; i++)
+      silc_free(ctx->argv[i]);
+    silc_free(ctx->argv_lens);
+    silc_free(ctx->argv_types);
+    silc_free(ctx);
+  }
+}
+
+/* Duplicate Command Context by adding reference counter. The context won't
+   be free'd untill it hits zero. */
+
+SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
+{
+  ctx->users++;
+  SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
+                 ctx->users));
+  return ctx;
+}
+
+/* Command WHOIS. This command is used to query information about 
+   specific user. */
+
+SILC_CLIENT_CMD_FUNC(whois)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Given without arguments fetches client's own information */
+  if (cmd->argc < 2) {
+    buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
+    silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, 
+                            ++conn->cmd_ident,
+                            1, 3, buffer->data, buffer->len);
+    silc_buffer_free(buffer);
+    goto out;
+  }
+
+  buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+                                      cmd->argc - 1, ++cmd->argv,
+                                      ++cmd->argv_lens, ++cmd->argv_types,
+                                      0);
+  silc_client_packet_send(cmd->client, cmd->conn->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command WHOWAS. This command is used to query history information about
+   specific user that used to exist in the network. */
+
+SILC_CLIENT_CMD_FUNC(whowas)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2 || cmd->argc > 3) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
+                                      cmd->argc - 1, ++cmd->argv,
+                                      ++cmd->argv_lens, ++cmd->argv_types,
+                                      0);
+  silc_client_packet_send(cmd->client, cmd->conn->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  cmd->argv--;
+  cmd->argv_lens--;
+  cmd->argv_types--;
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command IDENTIFY. This command is used to query information about 
+   specific user, especially ID's. 
+
+   NOTE: This command is used only internally by the client library
+   and application MUST NOT call this command directly. */
+
+SILC_CLIENT_CMD_FUNC(identify)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    goto out;
+  }
+
+  if (cmd->argc < 2 || cmd->argc > 3)
+    goto out;
+
+  if (cmd->argc == 2)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
+                                           ++conn->cmd_ident, 1,
+                                           1, cmd->argv[1],
+                                           cmd->argv_lens[1]);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, 
+                                           ++conn->cmd_ident, 2,
+                                           1, cmd->argv[1],
+                                           cmd->argv_lens[1],
+                                           4, cmd->argv[2],
+                                           cmd->argv_lens[2]);
+
+  silc_client_packet_send(cmd->client, cmd->conn->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Pending callbcak that will be called after the NICK command was
+   replied by the server.  This sets the nickname if there were no
+   errors. */
+
+SILC_CLIENT_CMD_FUNC(nick_change)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClientCommandReplyContext reply = 
+    (SilcClientCommandReplyContext)context2;
+  SilcCommandStatus status;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
+  if (status == SILC_STATUS_OK) {
+    /* Set the nickname */
+    silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
+    if (conn->nickname)
+      silc_free(conn->nickname);
+    conn->nickname = strdup(cmd->argv[1]);
+    conn->local_entry->nickname = conn->nickname;
+    silc_client_nickname_format(cmd->client, conn, conn->local_entry);
+    silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]), 
+                    conn->local_entry->id, conn->local_entry, 0, NULL);
+    COMMAND;
+  } else {
+    COMMAND_ERROR;
+  }
+
+  silc_client_command_free(cmd);
+}
+
+/* Command NICK. Shows current nickname/sets new nickname on current
+   window. */
+
+SILC_CLIENT_CMD_FUNC(nick)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /NICK <nickname>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (!strcmp(conn->nickname, cmd->argv[1]))
+    goto out;
+
+  /* Show current nickname */
+  if (cmd->argc < 2) {
+    if (cmd->conn) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "Your nickname is %s on server %s", 
+         conn->nickname, conn->remote_host);
+    } else {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "Your nickname is %s", conn->nickname);
+    }
+
+    COMMAND;
+    goto out;
+  }
+
+  if (cmd->argv_lens[1] > 128)
+    cmd->argv_lens[1] = 128;
+
+  /* Send the NICK command */
+  buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
+                                      &cmd->argv[1],
+                                      &cmd->argv_lens[1], 
+                                      &cmd->argv_types[1],
+                                      ++cmd->conn->cmd_ident);
+  silc_client_packet_send(cmd->client, cmd->conn->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Register pending callback that will actually set the new nickname
+     if there were no errors returned by the server. */
+  silc_client_command_pending(conn, SILC_COMMAND_NICK, 
+                             cmd->conn->cmd_ident,
+                             silc_client_command_nick_change,
+                             silc_client_command_dup(cmd));
+  cmd->pending = TRUE;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command LIST. Lists channels on the current server. */
+
+SILC_CLIENT_CMD_FUNC(list)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, idp = NULL;
+  char *name;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc == 2) {
+    name = cmd->argv[1];
+
+    /* Get the Channel ID of the channel */
+    if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
+      channel = (SilcChannelEntry)id_cache->context;
+      idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
+    }
+  }
+
+  if (!idp)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
+                                           ++conn->cmd_ident, 0);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, 
+                                           ++conn->cmd_ident, 1,
+                                           1, idp->data, idp->len);
+
+  silc_client_packet_send(cmd->client, cmd->conn->sock,
+                         SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  if (idp)
+    silc_buffer_free(idp);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command TOPIC. Sets/shows topic on a channel. */
+
+SILC_CLIENT_CMD_FUNC(topic)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, idp;
+  char *name;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2 || cmd->argc > 3) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       "Usage: /TOPIC <channel> [<topic>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+    name = conn->current_channel->channel_name;
+  } else {
+    name = cmd->argv[1];
+  }
+
+  if (!conn->current_channel) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "You are not on that channel");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Get the Channel ID of the channel */
+  if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "You are not on that channel");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* Send TOPIC command to the server */
+  idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
+  if (cmd->argc > 2)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                           ++conn->cmd_ident, 2, 
+                                           1, idp->data, idp->len,
+                                           2, cmd->argv[2], 
+                                           strlen(cmd->argv[2]));
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 
+                                           ++conn->cmd_ident, 1,
+                                           1, idp->data, idp->len);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command INVITE. Invites specific client to join a channel. This is
+   also used to mange the invite list of the channel. */
+
+SILC_CLIENT_CMD_FUNC(invite)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcClientEntry client_entry = NULL;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, clidp, chidp;
+  uint32 type = 0;
+  char *nickname = NULL, *name;
+  char *invite = NULL;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       "Usage: /INVITE <channel> [<nickname>[@server>]"
+       "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+
+    channel = conn->current_channel;
+  } else {
+    name = cmd->argv[1];
+
+    channel = silc_client_get_channel(cmd->client, conn, name);
+    if (!channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are on that channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+  }
+
+  /* Parse the typed nickname. */
+  if (cmd->argc == 3) {
+    if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
+      if (client->internal->params->nickname_parse)
+       client->internal->params->nickname_parse(cmd->argv[2], &nickname);
+      else
+       nickname = strdup(cmd->argv[2]);
+
+      /* Find client entry */
+      client_entry = silc_idlist_get_client(client, conn, nickname, 
+                                           cmd->argv[2], TRUE);
+      if (!client_entry) {
+       if (cmd->pending) {
+         COMMAND_ERROR;
+         goto out;
+       }
+      
+       /* Client entry not found, it was requested thus mark this to be
+          pending command. */
+       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                   conn->cmd_ident,
+                                   silc_client_command_invite, 
+                                   silc_client_command_dup(cmd));
+       cmd->pending = 1;
+       goto out;
+      }
+    } else {
+      invite = cmd->argv[2];
+      invite++;
+      if (cmd->argv[2][0] == '+')
+       type = 3;
+      else
+       type = 4;
+    }
+  }
+
+  /* Send the command */
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  if (client_entry) {
+    clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 
+                                           ++conn->cmd_ident, 3,
+                                           1, chidp->data, chidp->len,
+                                           2, clidp->data, clidp->len,
+                                           type, invite, invite ?
+                                           strlen(invite) : 0);
+    silc_buffer_free(clidp);
+  } else {
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 
+                                           ++conn->cmd_ident, 2,
+                                           1, chidp->data, chidp->len,
+                                           type, invite, invite ?
+                                           strlen(invite) : 0);
+  }
+
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(chidp);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+} *QuitInternal;
+
+SILC_TASK_CALLBACK(silc_client_command_quit_cb)
+{
+  QuitInternal q = (QuitInternal)context;
+
+  /* Close connection */
+  q->client->internal->ops->disconnect(q->client, q->conn);
+  silc_client_close_connection(q->client, NULL, q->conn->sock->user_data);
+
+  silc_free(q);
+}
+
+/* Command QUIT. Closes connection with current server. */
+SILC_CLIENT_CMD_FUNC(quit)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcBuffer buffer;
+  QuitInternal q;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc > 1)
+    buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, 
+                                        &cmd->argv[1], &cmd->argv_lens[1],
+                                        &cmd->argv_types[1], 0);
+  else
+    buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
+                                        NULL, NULL, NULL, 0);
+  silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, 
+                         NULL, 0, NULL, NULL, 
+                         buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  q = silc_calloc(1, sizeof(*q));
+  q->client = cmd->client;
+  q->conn = cmd->conn;
+
+  /* Sleep for a while */
+  sleep(2);
+
+  /* We quit the connection with little timeout */
+  silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
+                        silc_client_command_quit_cb, (void *)q,
+                        1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Timeout callback to remove the killed client from cache */
+
+SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcClientEntry target;
+  char *nickname = NULL;
+  
+  /* Parse the typed nickname. */
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
+  else
+    nickname = strdup(cmd->argv[1]);
+
+  /* Get the target client */
+  target = silc_idlist_get_client(cmd->client, conn, nickname, 
+                                 cmd->argv[1], FALSE);
+  if (target)
+    /* Remove the client from all channels and free it */
+    silc_client_del_client(client, conn, target);
+
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+/* Kill command's pending command callback to actually remove the killed
+   client from our local cache. */
+
+SILC_CLIENT_CMD_FUNC(kill_remove)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientCommandReplyContext reply = 
+    (SilcClientCommandReplyContext)context2;
+  SilcCommandStatus status;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
+  if (status == SILC_STATUS_OK) {
+    /* Remove with timeout */
+    silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
+                          silc_client_command_kill_remove_later, context,
+                          1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  silc_client_command_free(cmd);
+}
+
+/* Command KILL. Router operator can use this command to remove an client
+   fromthe SILC Network. */
+
+SILC_CLIENT_CMD_FUNC(kill)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, idp;
+  SilcClientEntry target;
+  char *nickname = NULL;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /KILL <nickname> [<comment>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Parse the typed nickname. */
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
+  else
+    nickname = strdup(cmd->argv[1]);
+
+  /* Get the target client */
+  target = silc_idlist_get_client(cmd->client, conn, nickname, 
+                                 cmd->argv[1], TRUE);
+  if (!target) {
+    if (cmd->pending) {
+      COMMAND_ERROR;
+      goto out;
+    }
+
+    /* Client entry not found, it was requested thus mark this to be
+       pending command. */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                               conn->cmd_ident,  
+                               silc_client_command_kill, 
+                               silc_client_command_dup(cmd));
+    cmd->pending = 1;
+    goto out;
+  }
+
+  /* Send the KILL command to the server */
+  idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
+  if (cmd->argc == 2)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 
+                                           ++conn->cmd_ident, 1, 
+                                           1, idp->data, idp->len);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 
+                                           ++conn->cmd_ident, 2, 
+                                           1, idp->data, idp->len,
+                                           2, cmd->argv[2], 
+                                           strlen(cmd->argv[2]));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+
+  /* Notify application */
+  COMMAND;
+
+  /* Register a pending callback that will actually remove the killed
+     client from our cache. */
+  silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
+                             silc_client_command_kill_remove,
+                             silc_client_command_dup(cmd));
+
+ out:
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+/* Command INFO. Request information about specific server. If specific
+   server is not provided the current server is used. */
+
+SILC_CLIENT_CMD_FUNC(info)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  char *name = NULL;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc == 2)
+    name = strdup(cmd->argv[1]);
+
+  /* Send the command */
+  if (name)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 
+                                           1, name, strlen(name));
+  else
+    buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
+                                        NULL, NULL, NULL, 0);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  if (name)
+    silc_free(name);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command PING. Sends ping to server. This is used to test the 
+   communication channel. */
+
+SILC_CLIENT_CMD_FUNC(ping)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  void *id;
+  int i;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Send the command */
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, 
+                                         1, conn->remote_id_data, 
+                                         silc_id_get_len(conn->remote_id,
+                                                         SILC_ID_SERVER));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
+                     SILC_ID_SERVER);
+  if (!id) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Start counting time */
+  for (i = 0; i < conn->ping_count; i++) {
+    if (conn->ping[i].dest_id == NULL) {
+      conn->ping[i].start_time = time(NULL);
+      conn->ping[i].dest_id = id;
+      conn->ping[i].dest_name = strdup(conn->remote_host);
+      break;
+    }
+  }
+  if (i >= conn->ping_count) {
+    i = conn->ping_count;
+    conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
+    conn->ping[i].start_time = time(NULL);
+    conn->ping[i].dest_id = id;
+    conn->ping[i].dest_name = strdup(conn->remote_host);
+    conn->ping_count++;
+  }
+  
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command JOIN. Joins to a channel. */
+
+SILC_CLIENT_CMD_FUNC(join)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, idp, auth = NULL;
+  char *name, *passphrase = NULL, *cipher = NULL, *hmac = NULL;
+  int i;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    COMMAND_ERROR;
+    goto out;
+  }
+  
+  /* See if we have joined to the requested channel already */
+  channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
+  if (channel && silc_client_on_channel(channel, conn->local_entry))
+    goto out;
+
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+
+  if (cmd->argv_lens[1] > 256)
+    cmd->argv_lens[1] = 256;
+
+  name = cmd->argv[1];
+
+  for (i = 2; i < cmd->argc; i++) {
+    if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
+      cipher = cmd->argv[i + 1];
+      i++;
+    } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
+      hmac = cmd->argv[i + 1];
+      i++;
+    } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc > i + 1) {
+      if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
+       auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                 cmd->client->private_key,
+                                                 conn->hash,
+                                                 conn->local_id,
+                                                 SILC_ID_CLIENT);
+      } else {
+       auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                       cmd->argv[i + 1], 
+                                       cmd->argv_lens[i + 1]);
+      }
+      i++;
+    } else {
+      passphrase = cmd->argv[i];
+    }
+  }
+
+  /* Send JOIN command to the server */
+  buffer =
+    silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
+                                  1, name, strlen(name),
+                                  2, idp->data, idp->len,
+                                  3, passphrase, 
+                                  passphrase ? strlen(passphrase) : 0,
+                                  4, cipher, cipher ? strlen(cipher) : 0,
+                                  5, hmac, hmac ? strlen(hmac) : 0,
+                                  6, auth ? auth->data : NULL,
+                                  auth ? auth->len : 0);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+  if (auth)
+    silc_buffer_free(auth);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* MOTD command. Requests motd from server. */
+
+SILC_CLIENT_CMD_FUNC(motd)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 1 || cmd->argc > 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
+       "Usage: /MOTD [<server>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Send TOPIC command to the server */
+  if (cmd->argc == 1)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
+                                           1, conn->remote_host, 
+                                           strlen(conn->remote_host));
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, 
+                                           1, cmd->argv[1], 
+                                           cmd->argv_lens[1]);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
+   modes as client cannot set itself server/router operator privileges. */
+
+SILC_CLIENT_CMD_FUNC(umode)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, idp;
+  unsigned char *cp, modebuf[4];
+  uint32 mode, add, len;
+  int i;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /UMODE +|-<modes>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  mode = conn->local_entry->mode;
+
+  /* Are we adding or removing mode */
+  if (cmd->argv[1][0] == '-')
+    add = FALSE;
+  else
+    add = TRUE;
+
+  /* Parse mode */
+  cp = cmd->argv[1] + 1;
+  len = strlen(cp);
+  for (i = 0; i < len; i++) {
+    switch(cp[i]) {
+    case 'a':
+      if (add) {
+       mode = 0;
+       mode |= SILC_UMODE_SERVER_OPERATOR;
+       mode |= SILC_UMODE_ROUTER_OPERATOR;
+      } else {
+       mode = SILC_UMODE_NONE;
+      }
+      break;
+    case 's':
+      if (add)
+       mode |= SILC_UMODE_SERVER_OPERATOR;
+      else
+       mode &= ~SILC_UMODE_SERVER_OPERATOR;
+      break;
+    case 'r':
+      if (add)
+       mode |= SILC_UMODE_ROUTER_OPERATOR;
+      else
+       mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+      break;
+    case 'g':
+      if (add)
+       mode |= SILC_UMODE_GONE;
+      else
+       mode &= ~SILC_UMODE_GONE;
+      break;
+    default:
+      COMMAND_ERROR;
+      goto out;
+      break;
+    }
+  }
+
+  idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
+  SILC_PUT32_MSB(mode, modebuf);
+
+  /* Send the command packet. We support sending only one mode at once
+     that requires an argument. */
+  buffer = 
+    silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, 
+                                  1, idp->data, idp->len, 
+                                  2, modebuf, sizeof(modebuf));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* CMODE command. Sets channel mode. Modes that does not require any arguments
+   can be set several at once. Those modes that require argument must be set
+   separately (unless set with modes that does not require arguments). */
+
+SILC_CLIENT_CMD_FUNC(cmode)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, chidp, auth = NULL;
+  unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
+  uint32 mode, add, type, len, arg_len = 0;
+  int i;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+
+    channel = conn->current_channel;
+  } else {
+    name = cmd->argv[1];
+
+    channel = silc_client_get_channel(cmd->client, conn, name);
+    if (!channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are on that channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+  }
+
+  mode = channel->mode;
+
+  /* Are we adding or removing mode */
+  if (cmd->argv[2][0] == '-')
+    add = FALSE;
+  else
+    add = TRUE;
+
+  /* Argument type to be sent to server */
+  type = 0;
+
+  /* Parse mode */
+  cp = cmd->argv[2] + 1;
+  len = strlen(cp);
+  for (i = 0; i < len; i++) {
+    switch(cp[i]) {
+    case 'p':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_PRIVATE;
+      else
+       mode &= ~SILC_CHANNEL_MODE_PRIVATE;
+      break;
+    case 's':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_SECRET;
+      else
+       mode &= ~SILC_CHANNEL_MODE_SECRET;
+      break;
+    case 'k':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_PRIVKEY;
+      else
+       mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
+      break;
+    case 'i':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_INVITE;
+      else
+       mode &= ~SILC_CHANNEL_MODE_INVITE;
+      break;
+    case 't':
+      if (add)
+       mode |= SILC_CHANNEL_MODE_TOPIC;
+      else
+       mode &= ~SILC_CHANNEL_MODE_TOPIC;
+      break;
+    case 'l':
+      if (add) {
+       int ll;
+       mode |= SILC_CHANNEL_MODE_ULIMIT;
+       type = 3;
+       if (cmd->argc < 4) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR;
+         goto out;
+       }
+       ll = atoi(cmd->argv[3]);
+       SILC_PUT32_MSB(ll, tmp);
+       arg = tmp;
+       arg_len = 4;
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_ULIMIT;
+      }
+      break;
+    case 'a':
+      if (add) {
+       mode |= SILC_CHANNEL_MODE_PASSPHRASE;
+       type = 4;
+       if (cmd->argc < 4) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR;
+         goto out;
+       }
+       arg = cmd->argv[3];
+       arg_len = cmd->argv_lens[3];
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
+      }
+      break;
+    case 'c':
+      if (add) {
+       mode |= SILC_CHANNEL_MODE_CIPHER;
+       type = 5;
+       if (cmd->argc < 4) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR;
+         goto out;
+       }
+       arg = cmd->argv[3];
+       arg_len = cmd->argv_lens[3];
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_CIPHER;
+      }
+      break;
+    case 'h':
+      if (add) {
+       mode |= SILC_CHANNEL_MODE_HMAC;
+       type = 6;
+       if (cmd->argc < 4) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR;
+         goto out;
+       }
+       arg = cmd->argv[3];
+       arg_len = cmd->argv_lens[3];
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_HMAC;
+      }
+      break;
+    case 'f':
+      if (add) {
+       mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
+       type = 7;
+
+       if (cmd->argc < 4) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+             "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
+         COMMAND_ERROR;
+         goto out;
+       }
+
+       if (!strcasecmp(cmd->argv[3], "-pubkey")) {
+         auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                   cmd->client->private_key,
+                                                   conn->hash,
+                                                   conn->local_id,
+                                                   SILC_ID_CLIENT);
+       } else {
+         auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                         cmd->argv[3], cmd->argv_lens[3]);
+       }
+
+       arg = auth->data;
+       arg_len = auth->len;
+      } else {
+       mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
+      }
+      break;
+    default:
+      COMMAND_ERROR;
+      goto out;
+      break;
+    }
+  }
+
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  SILC_PUT32_MSB(mode, modebuf);
+
+  /* Send the command packet. We support sending only one mode at once
+     that requires an argument. */
+  if (type && arg) {
+    buffer = 
+      silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, 
+                                    1, chidp->data, chidp->len, 
+                                    2, modebuf, sizeof(modebuf),
+                                    type, arg, arg_len);
+  } else {
+    buffer = 
+      silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 
+                                    1, chidp->data, chidp->len, 
+                                    2, modebuf, sizeof(modebuf));
+  }
+
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(chidp);
+  if (auth)
+    silc_buffer_free(auth);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* CUMODE command. Changes client's mode on a channel. */
+
+SILC_CLIENT_CMD_FUNC(cumode)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcClientEntry client_entry;
+  SilcBuffer buffer, clidp, chidp, auth = NULL;
+  unsigned char *name, *cp, modebuf[4];
+  uint32 mode = 0, add, len;
+  char *nickname = NULL;
+  int i;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 4) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+
+    channel = conn->current_channel;
+  } else {
+    name = cmd->argv[1];
+
+    channel = silc_client_get_channel(cmd->client, conn, name);
+    if (!channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are on that channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+  }
+
+  /* Parse the typed nickname. */
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[3], &nickname);
+  else
+    nickname = strdup(cmd->argv[3]);
+
+  /* Find client entry */
+  client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
+                                       cmd->argv[3], TRUE);
+  if (!client_entry) {
+    if (cmd->pending) {
+      COMMAND_ERROR;
+      goto out;
+    }
+
+    /* Client entry not found, it was requested thus mark this to be
+       pending command. */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                               conn->cmd_ident,  
+                               silc_client_command_cumode, 
+                               silc_client_command_dup(cmd));
+    cmd->pending = 1;
+    goto out;
+  }
+  
+  /* Get the current mode */
+  chu = silc_client_on_channel(channel, client_entry);
+  if (chu)
+    mode = chu->mode;
+
+  /* Are we adding or removing mode */
+  if (cmd->argv[2][0] == '-')
+    add = FALSE;
+  else
+    add = TRUE;
+
+  /* Parse mode */
+  cp = cmd->argv[2] + 1;
+  len = strlen(cp);
+  for (i = 0; i < len; i++) {
+    switch(cp[i]) {
+    case 'a':
+      if (add) {
+       mode |= SILC_CHANNEL_UMODE_CHANFO;
+       mode |= SILC_CHANNEL_UMODE_CHANOP;
+      } else {
+       mode = SILC_CHANNEL_UMODE_NONE;
+      }
+      break;
+    case 'f':
+      if (add) {
+       if (cmd->argc == 5) {
+         if (!strcasecmp(cmd->argv[4], "-pubkey")) {
+           auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                                     cmd->client->private_key,
+                                                     conn->hash,
+                                                     conn->local_id,
+                                                     SILC_ID_CLIENT);
+         } else {
+           auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                           cmd->argv[4], cmd->argv_lens[4]);
+         }
+       }
+       mode |= SILC_CHANNEL_UMODE_CHANFO;
+      } else {
+       mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+      }
+      break;
+    case 'o':
+      if (add)
+       mode |= SILC_CHANNEL_UMODE_CHANOP;
+      else
+       mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+      break;
+    default:
+      COMMAND_ERROR;
+      goto out;
+      break;
+    }
+  }
+
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  SILC_PUT32_MSB(mode, modebuf);
+  clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
+
+  /* Send the command packet. We support sending only one mode at once
+     that requires an argument. */
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 
+                                         auth ? 4 : 3, 
+                                         1, chidp->data, chidp->len, 
+                                         2, modebuf, 4,
+                                         3, clidp->data, clidp->len,
+                                         4, auth ? auth->data : NULL, 
+                                         auth ? auth->len : 0);
+  
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(chidp);
+  silc_buffer_free(clidp);
+  if (auth)
+    silc_buffer_free(auth);
+  
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+/* KICK command. Kicks a client out of channel. */
+
+SILC_CLIENT_CMD_FUNC(kick)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClient client = cmd->client;
+  SilcClientConnection conn = cmd->conn;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, idp, idp2;
+  SilcClientEntry target;
+  char *name;
+  char *nickname = NULL;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /KICK <channel> <nickname> [<comment>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+    name = conn->current_channel->channel_name;
+  } else {
+    name = cmd->argv[1];
+  }
+
+  if (!conn->current_channel) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "You are not on that channel");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Get the Channel ID of the channel */
+  if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "You are not on that channel");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  channel = (SilcChannelEntry)id_cache->context;
+
+  /* Parse the typed nickname. */
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[2], &nickname);
+  else
+    nickname = strdup(cmd->argv[2]);
+
+  /* Get the target client */
+  target = silc_idlist_get_client(cmd->client, conn, nickname, 
+                                 cmd->argv[2], FALSE);
+  if (!target) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "No such client: %s", cmd->argv[2]);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Send KICK command to the server */
+  idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
+  idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
+  if (cmd->argc == 3)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 
+                                           1, idp->data, idp->len,
+                                           2, idp2->data, idp2->len);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 
+                                           1, idp->data, idp->len,
+                                           2, idp2->data, idp2->len,
+                                           3, cmd->argv[3], 
+                                           strlen(cmd->argv[3]));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+  silc_buffer_free(idp2);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+static void silc_client_command_oper_send(unsigned char *data,
+                                         uint32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, auth;
+
+  if (cmd->argc >= 3) {
+    /* Encode the public key authentication payload */
+    auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                             cmd->client->private_key,
+                                             conn->hash,
+                                             conn->local_id,
+                                             SILC_ID_CLIENT);
+  } else {
+    /* Encode the password authentication payload */
+    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                   data, data_len);
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, 
+                                         1, cmd->argv[1], 
+                                         strlen(cmd->argv[1]),
+                                         2, auth->data, auth->len);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+
+  silc_buffer_free(buffer);
+  silc_buffer_free(auth);
+
+  /* Notify application */
+  COMMAND;
+}
+
+/* OPER command. Used to obtain server operator privileges. */
+
+SILC_CLIENT_CMD_FUNC(oper)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /OPER <username> [-pubkey]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    /* Get passphrase */
+    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
+                                    silc_client_command_oper_send,
+                                    context);
+    return;
+  }
+
+  silc_client_command_oper_send(NULL, 0, context);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+static void silc_client_command_silcoper_send(unsigned char *data,
+                                             uint32 data_len, void *context)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer, auth;
+
+  if (cmd->argc >= 3) {
+    /* Encode the public key authentication payload */
+    auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+                                             cmd->client->private_key,
+                                             conn->hash,
+                                             conn->local_id,
+                                             SILC_ID_CLIENT);
+  } else {
+    /* Encode the password authentication payload */
+    auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+                                   data, data_len);
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, 
+                                         1, cmd->argv[1], 
+                                         strlen(cmd->argv[1]),
+                                         2, auth->data, auth->len);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+
+  silc_buffer_free(buffer);
+  silc_buffer_free(auth);
+
+  /* Notify application */
+  COMMAND;
+}
+
+/* SILCOPER command. Used to obtain router operator privileges. */
+
+SILC_CLIENT_CMD_FUNC(silcoper)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /SILCOPER <username> [-pubkey]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 3) {
+    /* Get passphrase */
+    cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
+                                    silc_client_command_silcoper_send,
+                                    context);
+    return;
+  }
+
+  silc_client_command_silcoper_send(NULL, 0, context);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* CONNECT command. Connects the server to another server. */
+
+SILC_CLIENT_CMD_FUNC(connect)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  unsigned char port[4];
+  uint32 tmp;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CONNECT <server> [<port>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc == 3) {
+    tmp = atoi(cmd->argv[2]);
+    SILC_PUT32_MSB(tmp, port);
+  }
+
+  if (cmd->argc == 3)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, 
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]),
+                                           2, port, 4);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command BAN. This is used to manage the ban list of the channel. */
+
+SILC_CLIENT_CMD_FUNC(ban)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcChannelEntry channel;
+  SilcBuffer buffer, chidp;
+  int type = 0;
+  char *name, *ban = NULL;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /BAN <channel> "
+       "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+
+    channel = conn->current_channel;
+  } else {
+    name = cmd->argv[1];
+
+    channel = silc_client_get_channel(cmd->client, conn, name);
+    if (!channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are on that channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+  }
+
+  if (cmd->argc == 3) {
+    if (cmd->argv[2][0] == '+')
+      type = 2;
+    else
+      type = 3;
+
+    ban = cmd->argv[2];
+    ban++;
+  }
+
+  chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
+  /* Send the command */
+  if (ban)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, 
+                                           1, chidp->data, chidp->len,
+                                           type, ban, strlen(ban));
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, 
+                                           1, chidp->data, chidp->len);
+
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(chidp);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* CLOSE command. Close server connection to the remote server */
+SILC_CLIENT_CMD_FUNC(close)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  unsigned char port[4];
+  uint32 tmp;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /CLOSE <server> [<port>]");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc == 3) {
+    tmp = atoi(cmd->argv[2]);
+    SILC_PUT32_MSB(tmp, port);
+  }
+
+  if (cmd->argc == 3)
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, 
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]),
+                                           2, port, 4);
+  else
+    buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
+                                           1, cmd->argv[1], 
+                                           strlen(cmd->argv[1]));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+/* SHUTDOWN command. Shutdowns the server. */
+
+SILC_CLIENT_CMD_FUNC(shutdown)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Send the command */
+  silc_client_command_send(cmd->client, cmd->conn, 
+                          SILC_COMMAND_SHUTDOWN, 0, 0);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* LEAVE command. Leaves a channel. Client removes itself from a channel. */
+
+SILC_CLIENT_CMD_FUNC(leave)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcBuffer buffer, idp;
+  char *name;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc != 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /LEAVE <channel>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+    name = conn->current_channel->channel_name;
+  } else {
+    name = cmd->argv[1];
+  }
+
+  /* Get the channel entry */
+  channel = silc_client_get_channel(cmd->client, conn, name);
+  if (!channel) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "You are not on that channel");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Remove us from channel */
+  chu = silc_client_on_channel(channel, conn->local_entry);
+  if (chu) {
+    silc_hash_table_del(chu->client->channels, chu->channel);
+    silc_hash_table_del(chu->channel->user_list, chu->client);
+    silc_free(chu);
+  }
+
+  /* Send LEAVE command to the server */
+  idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 
+                                         1, idp->data, idp->len);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+
+  /* Notify application */
+  COMMAND;
+
+  if (conn->current_channel == channel)
+    conn->current_channel = NULL;
+
+  silc_client_del_channel(cmd->client, cmd->conn, channel);
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command USERS. Requests the USERS of the clients joined on requested
+   channel. */
+
+SILC_CLIENT_CMD_FUNC(users)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcBuffer buffer;
+  char *name;
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc != 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+       "Usage: /USERS <channel>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argv[1][0] == '*') {
+    if (!conn->current_channel) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "You are not on any channel");
+      COMMAND_ERROR;
+      goto out;
+    }
+    name = conn->current_channel->channel_name;
+  } else {
+    name = cmd->argv[1];
+  }
+
+  /* Send USERS command to the server */
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, 
+                                         ++conn->cmd_ident, 1, 
+                                         2, name, strlen(name));
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
+                         NULL, 0, NULL, NULL, buffer->data, 
+                         buffer->len, TRUE);
+  silc_buffer_free(buffer);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_client_command_free(cmd);
+}
+
+/* Command GETKEY. Used to fetch remote client's public key. */
+
+SILC_CLIENT_CMD_FUNC(getkey)
+{
+  SilcClientCommandContext cmd = (SilcClientCommandContext)context;
+  SilcClientConnection conn = cmd->conn;
+  SilcClient client = cmd->client;
+  SilcClientEntry client_entry = NULL;
+  SilcServerEntry server_entry = NULL;
+  char *nickname = NULL;
+  SilcBuffer idp, buffer;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!cmd->conn) {
+    SILC_NOT_CONNECTED(cmd->client, cmd->conn);
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  if (cmd->argc < 2) {
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, 
+                    "Usage: /GETKEY <nickname or server name>");
+    COMMAND_ERROR;
+    goto out;
+  }
+
+  /* Parse the typed nickname. */
+  if (client->internal->params->nickname_parse)
+    client->internal->params->nickname_parse(cmd->argv[1], &nickname);
+  else
+    nickname = strdup(cmd->argv[1]);
+
+  /* Find client entry */
+  client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
+                                       FALSE);
+  if (!client_entry) {
+    /* Check whether user requested server actually */
+    server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
+
+    if (!server_entry) {
+      /* No. what ever user wants we don't have it, so resolve it. We
+        will first try to resolve the client, and if that fails then
+        we'll try to resolve the server. */
+
+      if (!cmd->pending) {
+       /* This will send the IDENTIFY command for nickname */
+       silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
+       silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                   conn->cmd_ident,  
+                                   silc_client_command_getkey, 
+                                   silc_client_command_dup(cmd));
+       cmd->pending = 1;
+       goto out;
+      } else {
+       SilcClientCommandReplyContext reply = 
+         (SilcClientCommandReplyContext)context2;
+       SilcCommandStatus status;
+       unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
+       SILC_GET16_MSB(status, tmp);
+       
+       /* If nickname was not found, then resolve the server. */
+       if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
+         /* This sends the IDENTIFY command to resolve the server. */
+         silc_client_command_register(client, SILC_COMMAND_IDENTIFY, 
+                                      NULL, NULL,
+                                      silc_client_command_reply_identify_i, 0,
+                                      ++conn->cmd_ident);
+         silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+                                  conn->cmd_ident, 1, 
+                                  2, cmd->argv[1], cmd->argv_lens[1]);
+         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, 
+                                     conn->cmd_ident, 
+                                     silc_client_command_getkey, 
+                                     silc_client_command_dup(cmd));
+         goto out;
+       }
+
+       /* If server was not found, then we've resolved both nickname and
+          server and did not find anybody. */
+       if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
+            silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
+         SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
+           silc_client_command_status_message(status));
+         COMMAND_ERROR;
+         goto out;
+       }
+
+       COMMAND_ERROR;
+       goto out;
+      }
+    }
+
+    idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
+  } else {
+    idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
+  }
+
+  buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 
+                                         1, idp->data, idp->len);
+  silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 
+                         0, NULL, NULL, buffer->data, buffer->len, TRUE);
+  silc_buffer_free(buffer);
+  silc_buffer_free(idp);
+
+  /* Notify application */
+  COMMAND;
+
+ out:
+  silc_free(nickname);
+  silc_client_command_free(cmd);
+}
+
+/* Register a new command indicated by the `command' to the SILC client.
+   The `name' is optional command name.  If provided the command may be
+   searched using the silc_client_command_find by that name.  The
+   `command_function' is the function to be called when the command is
+   executed, and the `command_reply_function' is the function to be
+   called after the server has sent reply back to the command. 
+
+   The `ident' is optional identifier for the command.  If non-zero
+   the `command_reply_function' for the command type `command' will be
+   called only if the command reply sent by server includes the 
+   command identifier `ident'. Application usually does not need it
+   and set it to zero value. */
+
+bool silc_client_command_register(SilcClient client,
+                                 SilcCommand command,
+                                 const char *name,
+                                 SilcCommandCb command_function,
+                                 SilcCommandCb command_reply_function,
+                                 uint8 max_args,
+                                 uint16 ident)
+{
+  SilcClientCommand cmd;
+
+  cmd = silc_calloc(1, sizeof(*cmd));
+  cmd->cmd = command;
+  cmd->command = command_function;
+  cmd->reply = command_reply_function;
+  cmd->name = name ? strdup(name) : NULL;
+  cmd->max_args = max_args;
+  cmd->ident = ident;
+
+  silc_list_add(client->internal->commands, cmd);
+
+  return TRUE;
+}
+
+/* Unregister a command indicated by the `command' with command function
+   `command_function' and command reply function `command_reply_function'.
+   Returns TRUE if the command was found and unregistered. */
+
+bool silc_client_command_unregister(SilcClient client,
+                                   SilcCommand command,
+                                   SilcCommandCb command_function,
+                                   SilcCommandCb command_reply_function,
+                                   uint16 ident)
+{
+  SilcClientCommand cmd;
+
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->cmd == command && cmd->command == command_function &&
+       cmd->reply == command_reply_function && cmd->ident == ident) {
+      silc_list_del(client->internal->commands, cmd);
+      silc_free(cmd->name);
+      silc_free(cmd);
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Register all default commands provided by the client library for the
+   application. */
+
+void silc_client_commands_register(SilcClient client)
+{
+  silc_list_init(client->internal->commands, struct SilcClientCommandStruct, 
+                next);
+
+  SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
+  SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
+  SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
+  SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
+  SILC_CLIENT_CMD(list, LIST, "LIST", 2);
+  SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
+  SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
+  SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
+  SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
+  SILC_CLIENT_CMD(info, INFO, "INFO", 2);
+  SILC_CLIENT_CMD(connect, CONNECT, "CONNECT", 3);
+  SILC_CLIENT_CMD(ping, PING, "PING", 2);
+  SILC_CLIENT_CMD(oper, OPER, "OPER", 2);
+  SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
+  SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
+  SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
+  SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
+  SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
+  SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
+  SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
+  SILC_CLIENT_CMD(close, CLOSE, "CLOSE", 3);
+  SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN", 1);
+  SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
+  SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
+  SILC_CLIENT_CMD(users, USERS, "USERS", 2);
+  SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
+}
+
+/* Unregister all commands. */
+
+void silc_client_commands_unregister(SilcClient client)
+{
+  SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
+  SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
+  SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
+  SILC_CLIENT_CMDU(nick, NICK, "NICK");
+  SILC_CLIENT_CMDU(list, LIST, "LIST");
+  SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
+  SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
+  SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
+  SILC_CLIENT_CMDU(kill, KILL, "KILL");
+  SILC_CLIENT_CMDU(info, INFO, "INFO");
+  SILC_CLIENT_CMDU(connect, CONNECT, "CONNECT");
+  SILC_CLIENT_CMDU(ping, PING, "PING");
+  SILC_CLIENT_CMDU(oper, OPER, "OPER");
+  SILC_CLIENT_CMDU(join, JOIN, "JOIN");
+  SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
+  SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
+  SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
+  SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
+  SILC_CLIENT_CMDU(kick, KICK, "KICK");
+  SILC_CLIENT_CMDU(ban, BAN, "BAN");
+  SILC_CLIENT_CMDU(close, CLOSE, "CLOSE");
+  SILC_CLIENT_CMDU(shutdown, SHUTDOWN, "SHUTDOWN");
+  SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
+  SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
+  SILC_CLIENT_CMDU(users, USERS, "USERS");
+  SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
+}
diff --git a/lib/silcclient/command.h b/lib/silcclient/command.h
new file mode 100644 (file)
index 0000000..bf87b37
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+
+  command.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+/* Forward declarations */
+typedef struct SilcClientCommandStruct *SilcClientCommand;
+typedef struct SilcClientCommandContextStruct *SilcClientCommandContext;
+
+#include "silcapi.h"
+#include "command_reply.h"
+
+/* Structure holding one command and pointer to its function. This
+   structure is allocate into the commands list, and is returned
+   for example by silc_client_command_find function.
+
+   To call a command: command->command(cmd, NULL);
+   To call a command reply: command->reply(cmd, NULL);
+
+*/
+struct SilcClientCommandStruct {
+  SilcCommand cmd;                /* Command type */
+  SilcCommandCb command;          /* Command function */
+  SilcCommandCb reply;            /* Command reply callback */
+  char *name;                     /* Name of the command (optional) */
+  uint8 max_args;                 /* Maximum arguments (optional)  */
+  uint16 ident;                           /* Identifier for command (optional)  */
+  struct SilcClientCommandStruct *next;
+};
+
+/* Context sent as argument to all commands. This is used by the library
+   and application should use this as well. However, application may
+   choose to use some own context for its own local command. All library
+   commands, however, must use this context. */
+struct SilcClientCommandContextStruct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcClientCommand command;
+  uint32 argc;
+  unsigned char **argv;
+  uint32 *argv_lens;
+  uint32 *argv_types;
+  int pending;                 /* Command is being re-processed when TRUE */
+  int users;                   /* Reference counter */
+};
+
+/* Structure holding pending commands. If command is pending it will be
+   executed after command reply has been executed. */
+typedef struct SilcClientCommandPendingStruct {
+  SilcCommand reply_cmd;
+  SilcCommandCb callback;
+  void *context;
+  uint16 ident;
+  struct SilcClientCommandPendingStruct *next;
+} SilcClientCommandPending;
+
+/* List of pending commands */
+extern SilcClientCommandPending *silc_command_pending;
+
+
+/* Macros */
+
+/* Macro used for command registering and unregistering */
+#define SILC_CLIENT_CMD(func, cmd, name, args)                         \
+silc_client_command_register(client, SILC_COMMAND_##cmd, name,                 \
+                            silc_client_command_##func,                \
+                            silc_client_command_reply_##func, args, 0)
+#define SILC_CLIENT_CMDU(func, cmd, name)                              \
+silc_client_command_unregister(client, SILC_COMMAND_##cmd,             \
+                              silc_client_command_##func,              \
+                              silc_client_command_reply_##func, 0)
+
+/* Macro used to declare command functions */
+#define SILC_CLIENT_CMD_FUNC(func)                             \
+void silc_client_command_##func(void *context, void *context2)
+
+/* Executed pending command callback */
+#define SILC_CLIENT_PENDING_EXEC(ctx, cmd)                             \
+do {                                                                   \
+  if ((ctx)->callback)                                                 \
+    (*ctx->callback)(ctx->context, ctx);                               \
+  silc_client_command_pending_del((ctx)->sock->user_data, (cmd),       \
+                                 (ctx)->ident);                        \
+} while(0)
+
+bool silc_client_command_register(SilcClient client,
+                                 SilcCommand command,
+                                 const char *name,
+                                 SilcCommandCb command_function,
+                                 SilcCommandCb command_reply_function,
+                                 uint8 max_args,
+                                 uint16 ident);
+bool silc_client_command_unregister(SilcClient client,
+                                   SilcCommand command,
+                                   SilcCommandCb command_function,
+                                   SilcCommandCb command_reply_function,
+                                   uint16 ident);
+void silc_client_commands_register(SilcClient client);
+void silc_client_commands_unregister(SilcClient client);
+void silc_client_command_pending_del(SilcClientConnection conn,
+                                    SilcCommand reply_cmd,
+                                    uint16 ident);
+int silc_client_command_pending_check(SilcClientConnection conn,
+                                     SilcClientCommandReplyContext ctx,
+                                     SilcCommand command, 
+                                     uint16 ident);
+
+SILC_CLIENT_CMD_FUNC(whois);
+SILC_CLIENT_CMD_FUNC(whowas);
+SILC_CLIENT_CMD_FUNC(identify);
+SILC_CLIENT_CMD_FUNC(nick);
+SILC_CLIENT_CMD_FUNC(list);
+SILC_CLIENT_CMD_FUNC(topic);
+SILC_CLIENT_CMD_FUNC(invite);
+SILC_CLIENT_CMD_FUNC(quit);
+SILC_CLIENT_CMD_FUNC(kill);
+SILC_CLIENT_CMD_FUNC(info);
+SILC_CLIENT_CMD_FUNC(connect);
+SILC_CLIENT_CMD_FUNC(ping);
+SILC_CLIENT_CMD_FUNC(oper);
+SILC_CLIENT_CMD_FUNC(join);
+SILC_CLIENT_CMD_FUNC(motd);
+SILC_CLIENT_CMD_FUNC(umode);
+SILC_CLIENT_CMD_FUNC(cmode);
+SILC_CLIENT_CMD_FUNC(cumode);
+SILC_CLIENT_CMD_FUNC(kick);
+SILC_CLIENT_CMD_FUNC(ban);
+SILC_CLIENT_CMD_FUNC(close);
+SILC_CLIENT_CMD_FUNC(shutdown);
+SILC_CLIENT_CMD_FUNC(silcoper);
+SILC_CLIENT_CMD_FUNC(leave);
+SILC_CLIENT_CMD_FUNC(users);
+SILC_CLIENT_CMD_FUNC(getkey);
+
+#endif
diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c
new file mode 100644 (file)
index 0000000..f565899
--- /dev/null
@@ -0,0 +1,2056 @@
+/*
+
+  command_reply.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/*
+ * Command reply functions are "the otherside" of the command functions.
+ * Reply to a command sent by server is handled by these functions.
+ *
+ * The arguments received from server are also passed to the calling
+ * application through command_reply client operation.  The arguments are
+ * exactly same and in same order as the server sent it.  However, ID's are
+ * not sent to the application.  Instead, corresponding ID entry is sent
+ * to the application.  For example, instead of sending Client ID the 
+ * corresponding SilcClientEntry is sent to the application.  The case is
+ * same with for example Channel ID's.  This way application has all the
+ * necessary data already in hand without redundant searching.  If ID is
+ * received but ID entry does not exist, NULL is sent.
+ */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+const SilcCommandStatusMessage silc_command_status_messages[] = {
+
+  { STAT(NO_SUCH_NICK),      "There was no such nickname" },
+  { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
+  { STAT(NO_SUCH_SERVER),    "There was no such server" },
+  { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
+  { STAT(NO_RECIPIENT),      "No recipient given" },
+  { STAT(UNKNOWN_COMMAND),   "Unknown command" },
+  { STAT(WILDCARDS),         "Unknown command" },
+  { STAT(NO_CLIENT_ID),      "No Client ID given" },
+  { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
+  { STAT(NO_SERVER_ID),      "No Server ID given" },
+  { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
+  { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
+  { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
+  { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
+  { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
+  { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
+  { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
+  { STAT(USER_ON_CHANNEL),   "User already on the channel" },
+  { STAT(NOT_REGISTERED),    "You have not registered" },
+  { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
+  { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
+  { STAT(PERM_DENIED),       "Permission denied" },
+  { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
+  { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
+  { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
+  { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
+  { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
+  { STAT(UNKNOWN_MODE),    "Unknown mode" },
+  { STAT(NOT_YOU),         "Cannot change mode for other users" },
+  { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
+  { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
+  { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
+  { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
+  { STAT(BAD_NICKNAME),    "Bad nickname" },
+  { STAT(BAD_CHANNEL),     "Bad channel name" },
+  { STAT(AUTH_FAILED),     "Authentication failed" },
+  { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
+  { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
+
+  { 0, NULL }
+};
+/* Command reply operation that is called at the end of all command replys. 
+   Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
+#define COMMAND_REPLY(args) cmd->client->internal->ops->command_reply args
+#define ARGS cmd->client, cmd->sock->user_data,                                \
+             cmd->payload, TRUE, silc_command_get(cmd->payload), status
+
+/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
+#define COMMAND_REPLY_ERROR cmd->client->internal->ops->               \
+  command_reply(cmd->client, cmd->sock->user_data, cmd->payload,       \
+  FALSE, silc_command_get(cmd->payload), status)
+
+#define SAY cmd->client->internal->ops->say
+
+/* All functions that call the COMMAND_CHECK_STATUS or the
+   COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
+
+#define COMMAND_CHECK_STATUS                                             \
+do {                                                                     \
+  SILC_LOG_DEBUG(("Start"));                                             \
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
+  if (status != SILC_STATUS_OK)  {                                       \
+    COMMAND_REPLY_ERROR;                                                 \
+    goto out;                                                            \
+  }                                                                      \
+} while(0)
+
+#define COMMAND_CHECK_STATUS_LIST                                        \
+do {                                                                     \
+  SILC_LOG_DEBUG(("Start"));                                             \
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
+  if (status != SILC_STATUS_OK &&                                        \
+      status != SILC_STATUS_LIST_START &&                                \
+      status != SILC_STATUS_LIST_ITEM &&                                 \
+      status != SILC_STATUS_LIST_END) {                                          \
+    COMMAND_REPLY_ERROR;                                                 \
+    goto out;                                                            \
+  }                                                                      \
+} while(0)
+
+/* Process received command reply. */
+
+void silc_client_command_reply_process(SilcClient client,
+                                      SilcSocketConnection sock,
+                                      SilcPacketContext *packet)
+{
+  SilcBuffer buffer = packet->buffer;
+  SilcClientCommand cmd;
+  SilcClientCommandReplyContext ctx;
+  SilcCommandPayload payload;
+  SilcCommand command;
+  SilcCommandCb reply = NULL;
+  
+  /* Get command reply payload from packet */
+  payload = silc_command_payload_parse(buffer->data, buffer->len);
+  if (!payload) {
+    /* Silently ignore bad reply packet */
+    SILC_LOG_DEBUG(("Bad command reply packet"));
+    return;
+  }
+  
+  /* Allocate command reply context. This must be free'd by the
+     command reply routine receiving it. */
+  ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->client = client;
+  ctx->sock = sock;
+  ctx->payload = payload;
+  ctx->args = silc_command_get_args(ctx->payload);
+  ctx->packet = packet;
+  ctx->ident = silc_command_get_ident(ctx->payload);
+
+  /* Check for pending commands and mark to be exeucted */
+  silc_client_command_pending_check(sock->user_data, ctx, 
+                                   silc_command_get(ctx->payload), 
+                                   ctx->ident);
+
+  /* Execute command reply */
+
+  command = silc_command_get(ctx->payload);
+
+  /* Try to find matching the command identifier */
+  silc_list_start(client->internal->commands);
+  while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+    if (cmd->cmd == command && !cmd->ident)
+      reply = cmd->reply;
+    if (cmd->cmd == command && cmd->ident == ctx->ident) {
+      (*cmd->reply)((void *)ctx, NULL);
+      break;
+    }
+  }
+
+  if (cmd == SILC_LIST_END) {
+    if (reply)
+      /* No specific identifier for command reply, call first one found */
+      (*reply)(ctx, NULL);
+    else
+      silc_free(ctx);
+  }
+}
+
+/* Returns status message string */
+
+char *silc_client_command_status_message(SilcCommandStatus status)
+{
+  int i;
+
+  for (i = 0; silc_command_status_messages[i].message; i++) {
+    if (silc_command_status_messages[i].status == status)
+      break;
+  }
+
+  if (silc_command_status_messages[i].message == NULL)
+    return NULL;
+
+  return silc_command_status_messages[i].message;
+}
+
+/* Free command reply context and its internals. */
+
+void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
+{
+  if (cmd) {
+    silc_command_payload_free(cmd->payload);
+    silc_free(cmd);
+  }
+}
+
+static void 
+silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
+                                    SilcCommandStatus status,
+                                    bool notify)
+{
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClientID *client_id;
+  SilcClientEntry client_entry = NULL;
+  int argc;
+  uint32 len;
+  unsigned char *id_data, *tmp;
+  char *nickname = NULL, *username = NULL;
+  char *realname = NULL;
+  uint32 idle = 0, mode = 0;
+  SilcBuffer channels = NULL;
+  unsigned char *fingerprint;
+  uint32 fingerprint_len;
+  
+  argc = silc_argument_get_arg_num(cmd->args);
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!id_data) {
+    if (notify)
+      COMMAND_REPLY_ERROR;
+    return;
+  }
+  
+  client_id = silc_id_payload_parse_id(id_data, len);
+  if (!client_id) {
+    if (notify)
+      COMMAND_REPLY_ERROR;
+    return;
+  }
+  
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
+  username = silc_argument_get_arg_type(cmd->args, 4, &len);
+  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
+  if (!nickname || !username || !realname) {
+    if (notify)
+      COMMAND_REPLY_ERROR;
+    return;
+  }
+
+  tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
+  if (tmp) {
+    channels = silc_buffer_alloc(len);
+    silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels));
+    silc_buffer_put(channels, tmp, len);
+  }
+
+  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  if (tmp)
+    SILC_GET32_MSB(mode, tmp);
+
+  tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
+  if (tmp)
+    SILC_GET32_MSB(idle, tmp);
+
+  fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len);
+
+  /* Check if we have this client cached already. */
+  client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+  if (!client_entry) {
+    SILC_LOG_DEBUG(("Adding new client entry"));
+    client_entry = 
+      silc_client_add_client(cmd->client, conn, nickname, username, realname,
+                            client_id, mode);
+  } else {
+    silc_client_update_client(cmd->client, conn, client_entry, 
+                             nickname, username, realname, mode);
+    silc_free(client_id);
+  }
+
+  if (fingerprint && !client_entry->fingerprint) {
+    client_entry->fingerprint = 
+      silc_calloc(fingerprint_len, 
+                 sizeof(*client_entry->fingerprint));
+    memcpy(client_entry->fingerprint, fingerprint, fingerprint_len);
+    client_entry->fingerprint_len = fingerprint_len;
+  }
+
+  if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
+    client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+
+  /* Notify application */
+  if (!cmd->callback && notify)
+    COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
+                  channels, mode, idle, fingerprint));
+
+  if (channels)
+    silc_buffer_free(channels);
+}
+
+/* Received reply for WHOIS command. This maybe called several times
+   for one WHOIS command as server may reply with list of results. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(whois)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  /* Save WHOIS info */
+  silc_client_command_reply_whois_save(cmd, status, TRUE);
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply for WHOWAS command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(whowas)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcClientID *client_id;
+  SilcClientEntry client_entry = NULL;
+  uint32 len;
+  unsigned char *id_data;
+  char *nickname, *username;
+  char *realname = NULL;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!id_data) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  client_id = silc_id_payload_parse_id(id_data, len);
+  if (!client_id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get the client entry, if exists */
+  client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+  silc_free(client_id);
+
+  nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
+  username = silc_argument_get_arg_type(cmd->args, 4, &len);
+  realname = silc_argument_get_arg_type(cmd->args, 5, &len);
+  if (!nickname || !username) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application. We don't save any history information to any
+     cache. Just pass the data to the application for displaying on 
+     the screen. */
+  COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
+  silc_client_command_reply_free(cmd);
+}
+
+static void 
+silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
+                                       SilcCommandStatus status,
+                                       bool notify)
+{
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcClient client = cmd->client;
+  SilcClientID *client_id = NULL;
+  SilcServerID *server_id = NULL;
+  SilcChannelID *channel_id = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client_entry;
+  SilcServerEntry server_entry;
+  SilcChannelEntry channel_entry;
+  int argc;
+  uint32 len;
+  unsigned char *id_data;
+  char *name = NULL, *info = NULL;
+  SilcIDPayload idp = NULL;
+  SilcIdType id_type;
+  
+  argc = silc_argument_get_arg_num(cmd->args);
+
+  id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!id_data) {
+    if (notify)
+      COMMAND_REPLY_ERROR;
+    return;
+  }
+  idp = silc_id_payload_parse(id_data, len);
+  if (!idp) {
+    if (notify)
+      COMMAND_REPLY_ERROR;
+    return;
+  }
+
+  name = silc_argument_get_arg_type(cmd->args, 3, &len);
+  info = silc_argument_get_arg_type(cmd->args, 4, &len);
+
+  id_type = silc_id_payload_get_type(idp);
+
+  switch (id_type) {
+  case SILC_ID_CLIENT:
+    client_id = silc_id_payload_get_id(idp);
+
+    SILC_LOG_DEBUG(("Received client information"));
+
+    /* Check if we have this client cached already. */
+    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    if (!client_entry) {
+      SILC_LOG_DEBUG(("Adding new client entry"));
+      client_entry = 
+       silc_client_add_client(cmd->client, conn, name, info, NULL,
+                              silc_id_dup(client_id, id_type), 0);
+    } else {
+      silc_client_update_client(cmd->client, conn, client_entry, 
+                               name, info, NULL, 0);
+    }
+
+    if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING)
+      client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+
+    /* Notify application */
+    if (notify)
+      COMMAND_REPLY((ARGS, client_entry, name, info));
+    break;
+
+  case SILC_ID_SERVER:
+    server_id = silc_id_payload_get_id(idp);
+
+    SILC_LOG_DEBUG(("Received server information"));
+
+    /* Check if we have this server cached already. */
+    if (!silc_idcache_find_by_id_one(conn->server_cache, 
+                                    (void *)server_id, &id_cache)) {
+      SILC_LOG_DEBUG(("Adding new server entry"));
+      
+      server_entry = silc_calloc(1, sizeof(*server_entry));
+      server_entry->server_id = silc_id_dup(server_id, id_type);
+      if (name)
+       server_entry->server_name = strdup(name);
+      if (info)
+       server_entry->server_info = strdup(info);
+      
+      /* Add server to cache */
+      silc_idcache_add(conn->server_cache, server_entry->server_name,
+                      server_entry->server_id, (void *)server_entry, 
+                      0, NULL);
+    } else {
+      server_entry = (SilcServerEntry)id_cache->context;
+    }
+
+    /* Notify application */
+    if (notify)
+      COMMAND_REPLY((ARGS, server_entry, name, info));
+    break;
+
+  case SILC_ID_CHANNEL:
+    channel_id = silc_id_payload_get_id(idp);
+
+    SILC_LOG_DEBUG(("Received channel information"));
+
+    /* Check if we have this channel cached already. */
+    channel_entry = silc_client_get_channel_by_id(client, conn, channel_id);
+    if (!channel_entry) {
+      if (!name)
+       break;
+
+      /* Add new channel entry */
+      channel_entry = silc_client_add_channel(client, conn, name, 0,
+                                             channel_id);
+      channel_id = NULL;
+    }
+
+    /* Notify application */
+    if (notify)
+      COMMAND_REPLY((ARGS, channel_entry, name, info));
+    break;
+  }
+
+  silc_id_payload_free(idp);
+  silc_free(client_id);
+  silc_free(server_id);
+  silc_free(channel_id);
+}
+
+/* Received reply for IDENTIFY command. This maybe called several times
+   for one IDENTIFY command as server may reply with list of results. 
+   This is totally silent and does not print anything on screen. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(identify)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  /* Save IDENTIFY info */
+  silc_client_command_reply_identify_save(cmd, status, TRUE);
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply for command NICK. If everything went without errors
+   we just received our new Client ID. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(nick)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcIDPayload idp;
+  unsigned char *tmp;
+  uint32 argc, len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "Cannot set nickname: %s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc < 2 || argc > 2) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "Cannot set nickname: bad reply to command");
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Take received Client ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  idp = silc_id_payload_parse(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  silc_client_receive_new_id(cmd->client, cmd->sock, idp);
+    
+  /* Notify application */
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
+  COMMAND_REPLY((ARGS, conn->local_entry));
+  silc_client_command_reply_free(cmd);
+  return;
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply to the LIST command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(list)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcCommandStatus status;
+  unsigned char *tmp, *name, *topic;
+  uint32 usercount = 0;
+
+  COMMAND_CHECK_STATUS_LIST;
+
+  name = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (tmp)
+    SILC_GET32_MSB(usercount, tmp);
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply to topic command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(topic)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id = NULL;
+  unsigned char *tmp;
+  char *topic;
+  uint32 argc, len;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc < 1 || argc > 3) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Take Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  /* Take topic */
+  topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  if (!topic)
+    goto out;
+
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
+
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  if (!channel) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel, topic));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply to invite command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(invite)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id;
+  unsigned char *tmp;
+  uint32 len;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Take Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
+
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  if (!channel) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get the invite list */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel, tmp));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply to the KILL command. */
+SILC_CLIENT_CMD_REPLY_FUNC(kill)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply to INFO command. We receive the server ID and some
+   information about the server user requested. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(info)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry server;
+  SilcServerID *server_id = NULL;
+  char *server_name, *server_info;
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", 
+       silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  server_id = silc_id_payload_parse_id(tmp, len);
+  if (!server_id)
+    goto out;
+
+  /* Get server name */
+  server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  if (!server_name)
+    goto out;
+
+  /* Get server info */
+  server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (!server_info)
+    goto out;
+
+  /* See whether we have this server cached. If not create it. */
+  if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
+                                  &id_cache)) {
+    SILC_LOG_DEBUG(("New server entry"));
+
+    server = silc_calloc(1, sizeof(*server));
+    server->server_name = strdup(server_name);
+    server->server_info = strdup(server_info);
+    server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
+
+    /* Add it to the cache */
+    silc_idcache_add(conn->server_cache, server->server_name,
+                    server->server_id, (void *)server, 0, NULL);
+  } else {
+    server = (SilcServerEntry)id_cache->context;
+  }
+  
+  /* Notify application */
+  COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
+  silc_free(server_id);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply to PING command. The reply time is shown to user. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(ping)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  void *id;
+  int i;
+  time_t diff, curtime;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  curtime = time(NULL);
+  id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
+                     cmd->packet->src_id_type);
+  if (!id || !conn->ping) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  for (i = 0; i < conn->ping_count; i++) {
+    if (!conn->ping[i].dest_id)
+      continue;
+    if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
+      diff = curtime - conn->ping[i].start_time;
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
+         "Ping reply from %s: %d second%s", 
+         conn->ping[i].dest_name, diff, 
+         diff == 1 ? "" : "s");
+      
+      conn->ping[i].start_time = 0;
+      silc_free(conn->ping[i].dest_id);
+      conn->ping[i].dest_id = NULL;
+      silc_free(conn->ping[i].dest_name);
+      conn->ping[i].dest_name = NULL;
+      break;
+    }
+  }
+
+  silc_free(id);
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply for JOIN command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(join)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  SilcChannelID *channel_id;
+  uint32 argc, mode = 0, len, list_count;
+  char *topic, *tmp, *channel_name = NULL, *hmac;
+  SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
+  int i;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    if (status != SILC_STATUS_ERR_USER_ON_CHANNEL)
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+         "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc < 7 || argc > 14) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "Cannot join channel: Bad reply packet");
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get channel name */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!tmp) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "Cannot join channel: Bad reply packet");
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  channel_name = tmp;
+
+  /* Get Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (!tmp) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "Cannot join channel: Bad reply packet");
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get channel mode */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+  if (tmp)
+    SILC_GET32_MSB(mode, tmp);
+
+  /* Get channel key */
+  tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
+  if (tmp) {
+    keyp = silc_buffer_alloc(len);
+    silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
+    silc_buffer_put(keyp, tmp, len);
+  }
+
+  /* Get topic */
+  topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
+
+  /* Check whether we have this channel entry already. */
+  channel = silc_client_get_channel(cmd->client, conn, channel_name);
+  if (channel) {
+    if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id))
+      silc_client_replace_channel_id(cmd->client, conn, channel, channel_id);
+  } else {
+    /* Create new channel entry */
+    channel = silc_client_add_channel(cmd->client, conn, channel_name, 
+                                     mode, channel_id);
+  }
+
+  conn->current_channel = channel;
+
+  /* Get hmac */
+  hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
+  if (hmac) {
+    if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
+      SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+         "Cannot join channel: Unsupported HMAC `%s'", hmac);
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+  }
+
+  /* Get the list count */
+  tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
+  if (!tmp)
+    goto out;
+  SILC_GET32_MSB(list_count, tmp);
+
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
+  if (!tmp)
+    goto out;
+
+  client_id_list = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(client_id_list, len);
+  silc_buffer_put(client_id_list, tmp, len);
+
+  /* Get client mode list */
+  tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
+  if (!tmp)
+    goto out;
+
+  client_mode_list = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(client_mode_list, len);
+  silc_buffer_put(client_mode_list, tmp, len);
+
+  /* Add clients we received in the reply to the channel */
+  for (i = 0; i < list_count; i++) {
+    uint16 idp_len;
+    uint32 mode;
+    SilcClientID *client_id;
+    SilcClientEntry client_entry;
+
+    /* Client ID */
+    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    if (!client_id)
+      continue;
+
+    /* Mode */
+    SILC_GET32_MSB(mode, client_mode_list->data);
+
+    /* Check if we have this client cached already. */
+    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    if (!client_entry) {
+      /* No, we don't have it, add entry for it. */
+      client_entry = 
+       silc_client_add_client(cmd->client, conn, NULL, NULL, NULL,
+                              silc_id_dup(client_id, SILC_ID_CLIENT), 0);
+    }
+
+    /* Join client to the channel */
+    chu = silc_calloc(1, sizeof(*chu));
+    chu->client = client_entry;
+    chu->channel = channel;
+    chu->mode = mode;
+    silc_hash_table_add(channel->user_list, client_entry, chu);
+    silc_hash_table_add(client_entry->channels, channel, chu);
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+    silc_buffer_pull(client_mode_list, 4);
+  }
+  silc_buffer_push(client_id_list, client_id_list->data - 
+                  client_id_list->head);
+  silc_buffer_push(client_mode_list, client_mode_list->data - 
+                  client_mode_list->head);
+
+  /* Save channel key */
+  if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+    silc_client_save_channel_key(cmd->client, conn, keyp, channel);
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
+                keyp ? keyp->head : NULL, NULL,
+                NULL, topic, hmac, list_count, client_id_list, 
+                client_mode_list));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
+  silc_client_command_reply_free(cmd);
+
+  if (keyp)
+    silc_buffer_free(keyp);
+  if (client_id_list)
+    silc_buffer_free(client_id_list);
+  if (client_mode_list)
+    silc_buffer_free(client_mode_list);
+}
+
+/* Received reply for MOTD command */
+
+SILC_CLIENT_CMD_REPLY_FUNC(motd)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  uint32 argc, i;
+  unsigned char *tmp;
+  char *motd = NULL, *cp, line[256];
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    return;
+  }
+
+  argc = silc_argument_get_arg_num(cmd->args);
+  if (argc > 3) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  if (argc == 3) {
+    motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
+    if (!motd) {
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+
+    i = 0;
+    cp = motd;
+    while(cp[i] != 0) {
+      if (cp[i++] == '\n') {
+       memset(line, 0, sizeof(line));
+       strncat(line, cp, i - 1);
+       cp += i;
+       
+       if (i == 2)
+         line[0] = ' ';
+       
+       SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
+       
+       if (!strlen(cp))
+         break;
+       i = 0;
+      }
+    }
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, motd));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply tot he UMODE command. Save the current user mode */
+
+SILC_CLIENT_CMD_REPLY_FUNC(umode)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+  uint32 mode;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  SILC_GET32_MSB(mode, tmp);
+  conn->local_entry->mode = mode;
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, mode));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply for CMODE command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(cmode)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+  uint32 mode;
+  SilcChannelID *channel_id;
+  SilcChannelEntry channel;
+  uint32 len;
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Take Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
+
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  if (!channel) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Get channel mode */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  if (!tmp) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Save the mode */
+  SILC_GET32_MSB(mode, tmp);
+  channel->mode = mode;
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel, mode));
+
+  silc_free(channel_id);
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Received reply for CUMODE command */
+
+SILC_CLIENT_CMD_REPLY_FUNC(cumode)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcClientID *client_id;
+  SilcChannelID *channel_id;
+  SilcClientEntry client_entry;
+  SilcChannelEntry channel;
+  SilcChannelUser chu;
+  unsigned char *modev, *tmp, *id;
+  uint32 len, mode;
+  
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Get channel mode */
+  modev = silc_argument_get_arg_type(cmd->args, 2, NULL);
+  if (!modev) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Take Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (!tmp)
+    goto out;
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
+
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  if (!channel) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Get Client ID */
+  id = silc_argument_get_arg_type(cmd->args, 4, &len);
+  if (!id) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  client_id = silc_id_payload_parse_id(id, len);
+  if (!client_id) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Get client entry */
+  client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+  if (!client_entry) {
+    silc_free(channel_id);
+    silc_free(client_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Save the mode */
+  SILC_GET32_MSB(mode, modev);
+  chu = silc_client_on_channel(channel, client_entry);
+  if (chu)
+    chu->mode = mode;
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, mode, channel, client_entry));
+  silc_free(client_id);
+  silc_free(channel_id);
+  
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(kick)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(oper)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(connect)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(ban)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcChannelID *channel_id;
+  unsigned char *tmp;
+  uint32 len;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Take Channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  channel_id = silc_id_payload_parse_id(tmp, len);
+  if (!channel_id)
+    goto out;
+
+  /* Get the channel entry */
+  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  if (!channel) {
+    silc_free(channel_id);
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Get the ban list */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel, tmp));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(close)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
+  silc_client_command_reply_free(cmd);
+}
+SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
+  silc_client_command_reply_free(cmd);
+}
+/* Reply to LEAVE command. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(leave)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
+  silc_client_command_reply_free(cmd);
+}
+
+/* Channel resolving callback for USERS command reply. */
+
+static void silc_client_command_reply_users_cb(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelEntry *channels,
+                                              uint32 channels_count,
+                                              void *context)
+{
+  if (!channels_count) {
+    SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+    SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+    SilcCommandStatus status = SILC_STATUS_ERR_NO_SUCH_CHANNEL;
+
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+  silc_client_command_reply_users(context, NULL);
+}
+
+/* Reply to USERS command. Received list of client ID's and theirs modes
+   on the channel we requested. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(users)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcChannelEntry channel;
+  SilcClientEntry client_entry;
+  SilcChannelUser chu;
+  SilcChannelID *channel_id = NULL;
+  SilcBuffer client_id_list = NULL;
+  SilcBuffer client_mode_list = NULL;
+  unsigned char *tmp;
+  uint32 tmp_len, list_count;
+  int i;
+  unsigned char **res_argv = NULL;
+  uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get channel ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+  if (!channel_id) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  
+  /* Get the list count */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  SILC_GET32_MSB(list_count, tmp);
+
+  /* Get Client ID list */
+  tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  client_id_list = silc_buffer_alloc(tmp_len);
+  silc_buffer_pull_tail(client_id_list, tmp_len);
+  silc_buffer_put(client_id_list, tmp, tmp_len);
+
+  /* Get client mode list */
+  tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  client_mode_list = silc_buffer_alloc(tmp_len);
+  silc_buffer_pull_tail(client_mode_list, tmp_len);
+  silc_buffer_put(client_mode_list, tmp, tmp_len);
+
+  /* Get channel entry */
+  channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id);
+  if (!channel) {
+    /* Resolve the channel from server */
+    silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id,
+                                         silc_client_command_reply_users_cb,
+                                         cmd);
+    silc_free(channel_id);
+    if (client_id_list)
+      silc_buffer_free(client_id_list);
+    if (client_mode_list)
+      silc_buffer_free(client_mode_list);
+    return;
+  }
+
+  /* Cache the received Client ID's and modes. */
+  for (i = 0; i < list_count; i++) {
+    uint16 idp_len;
+    uint32 mode;
+    SilcClientID *client_id;
+
+    /* Client ID */
+    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    if (!client_id)
+      continue;
+
+    /* Mode */
+    SILC_GET32_MSB(mode, client_mode_list->data);
+
+    /* Check if we have this client cached already. */
+    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    if (!client_entry || !client_entry->username || !client_entry->realname) {
+      if (client_entry) {
+       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+         silc_buffer_pull(client_id_list, idp_len);
+         silc_buffer_pull(client_mode_list, 4);
+         continue;
+       }
+       client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+      }
+
+      /* No we don't have it (or it is incomplete in information), query
+        it from the server. Assemble argument table that will be sent
+        for the WHOIS command later. */
+      res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
+                             (res_argc + 1));
+      res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
+                                  (res_argc + 1));
+      res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
+                                   (res_argc + 1));
+      res_argv[res_argc] = client_id_list->data;
+      res_argv_lens[res_argc] = idp_len;
+      res_argv_types[res_argc] = res_argc + 3;
+      res_argc++;
+    } else {
+      if (!silc_client_on_channel(channel, client_entry)) {
+       chu = silc_calloc(1, sizeof(*chu));
+       chu->client = client_entry;
+       chu->channel = channel;
+       silc_hash_table_add(channel->user_list, client_entry, chu);
+       silc_hash_table_add(client_entry->channels, channel, chu);
+      }
+    }
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+    silc_buffer_pull(client_mode_list, 4);
+  }
+
+  /* Query the client information from server if the list included clients
+     that we don't know about. */
+  if (res_argc) {
+    SilcBuffer res_cmd;
+
+    /* Send the WHOIS command to server */
+    silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL,
+                                silc_client_command_reply_whois_i, 0,
+                                ++conn->cmd_ident);
+    res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+                                         res_argc, res_argv, res_argv_lens,
+                                         res_argv_types, conn->cmd_ident);
+    silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
+                           NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
+                           TRUE);
+
+    /* Register pending command callback. After we've received the WHOIS
+       command reply we will reprocess this command reply by re-calling this
+       USERS command reply callback. */
+    silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                               silc_client_command_reply_users, cmd);
+
+    silc_buffer_free(res_cmd);
+    silc_free(channel_id);
+    silc_free(res_argv);
+    silc_free(res_argv_lens);
+    silc_free(res_argv_types);
+    if (client_id_list)
+      silc_buffer_free(client_id_list);
+    if (client_mode_list)
+      silc_buffer_free(client_mode_list);
+    return;
+  }
+  
+  silc_buffer_push(client_id_list, (client_id_list->data - 
+                                   client_id_list->head));
+  silc_buffer_push(client_mode_list, (client_mode_list->data - 
+                                     client_mode_list->head));
+
+  /* Notify application */
+  COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
+  silc_client_command_reply_free(cmd);
+  silc_free(channel_id);
+  if (client_id_list)
+    silc_buffer_free(client_id_list);
+  if (client_mode_list)
+    silc_buffer_free(client_mode_list);
+}
+
+/* Received command reply to GETKEY command. WE've received the remote
+   client's public key. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(getkey)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  SilcIDCacheEntry id_cache;
+  SilcIDPayload idp = NULL;
+  SilcClientID *client_id = NULL;
+  SilcClientEntry client_entry;
+  SilcServerID *server_id = NULL;
+  SilcServerEntry server_entry;
+  SilcSKEPKType type;
+  unsigned char *tmp, *pk;
+  uint32 len;
+  uint16 pk_len;
+  SilcIdType id_type;
+  SilcPublicKey public_key = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK) {
+    SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
+       "%s", silc_client_command_status_message(status));
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+  idp = silc_id_payload_parse(tmp, len);
+  if (!idp) {
+    COMMAND_REPLY_ERROR;
+    goto out;
+  }
+
+  /* Get the public key payload */
+  tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
+  if (tmp) {
+    /* Decode the public key */
+    SILC_GET16_MSB(pk_len, tmp);
+    SILC_GET16_MSB(type, tmp + 2);
+    pk = tmp + 4;
+    
+    if (type == SILC_SKE_PK_TYPE_SILC)
+      if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
+       public_key = NULL;
+  } 
+   
+  id_type = silc_id_payload_get_type(idp);
+  if (id_type == SILC_ID_CLIENT) {
+    /* Received client's public key */
+    client_id = silc_id_payload_get_id(idp);
+    client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id);
+    if (!client_entry) {
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+
+    /* Notify application */
+    COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
+  } else if (id_type == SILC_ID_SERVER) {
+    /* Received server's public key */
+    server_id = silc_id_payload_get_id(idp);
+    if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
+                                    &id_cache)) {
+      COMMAND_REPLY_ERROR;
+      goto out;
+    }
+
+    server_entry = (SilcServerEntry)id_cache->context;
+
+    /* Notify application */
+    COMMAND_REPLY((ARGS, id_type, server_entry, public_key));
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
+  if (idp)
+    silc_id_payload_free(idp);
+  if (public_key)
+    silc_pkcs_public_key_free(public_key);
+  silc_free(client_id);
+  silc_free(server_id);
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(quit)
+{
+  silc_client_command_reply_free(context);
+}
+
+
+/******************************************************************************
+
+                      Internal command reply functions
+
+******************************************************************************/
+
+SILC_CLIENT_CMD_REPLY_FUNC(whois_i)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END)
+    goto out;
+
+  /* Save WHOIS info */
+  silc_client_command_reply_whois_save(cmd, status, FALSE);
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
+  /* Unregister this command reply */
+  silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS,
+                                NULL, silc_client_command_reply_whois_i,
+                                cmd->ident);
+
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(identify_i)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_START &&
+      status != SILC_STATUS_LIST_ITEM &&
+      status != SILC_STATUS_LIST_END)
+    goto out;
+
+  /* Save IDENTIFY info */
+  silc_client_command_reply_identify_save(cmd, status, FALSE);
+
+  /* Pending callbacks are not executed if this was an list entry */
+  if (status != SILC_STATUS_OK &&
+      status != SILC_STATUS_LIST_END) {
+    silc_client_command_reply_free(cmd);
+    return;
+  }
+
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
+
+  /* If we received notify for invalid ID we'll remove the ID if we
+     have it cached. */
+  if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
+    SilcClientEntry client_entry;
+    uint32 tmp_len;
+    unsigned char *tmp =
+      silc_argument_get_arg_type(silc_command_get_args(cmd->payload),
+                                2, &tmp_len);
+    if (tmp) {
+      SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
+      if (client_id) {
+       client_entry = silc_client_get_client_by_id(cmd->client, conn,
+                                                   client_id);
+       if (client_entry)
+         silc_client_del_client(cmd->client, conn, client_entry);
+       silc_free(client_id);
+      }
+    }
+  }
+
+  /* Unregister this command reply */
+  silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY,
+                                NULL, silc_client_command_reply_identify_i,
+                                cmd->ident);
+
+  silc_client_command_reply_free(cmd);
+}
+
+SILC_CLIENT_CMD_REPLY_FUNC(info_i)
+{
+  SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+  SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
+  SilcCommandStatus status;
+  unsigned char *tmp;
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry server;
+  SilcServerID *server_id = NULL;
+  char *server_name, *server_info;
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
+  SILC_GET16_MSB(status, tmp);
+  if (status != SILC_STATUS_OK)
+    goto out;
+
+  /* Get server ID */
+  tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+  if (!tmp)
+    goto out;
+
+  server_id = silc_id_payload_parse_id(tmp, len);
+  if (!server_id)
+    goto out;
+
+  /* Get server name */
+  server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
+  if (!server_name)
+    goto out;
+
+  /* Get server info */
+  server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
+  if (!server_info)
+    goto out;
+
+  /* See whether we have this server cached. If not create it. */
+  if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
+                                  &id_cache)) {
+    SILC_LOG_DEBUG(("New server entry"));
+    server = silc_calloc(1, sizeof(*server));
+    server->server_name = strdup(server_name);
+    server->server_info = strdup(server_info);
+    server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
+
+    /* Add it to the cache */
+    silc_idcache_add(conn->server_cache, server->server_name,
+                    server->server_id, (void *)server, 0, NULL);
+  }
+  
+ out:
+  SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
+  silc_free(server_id);
+  silc_client_command_reply_free(cmd);
+}
similarity index 60%
rename from apps/silc/command_reply.h
rename to lib/silcclient/command_reply.h
index 36b312860733035d2df58f02f963c30bc38c93ad..6fd03ff9f025ca092252c93092ab5e37f3a3618e 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  command_reply.h
+  command_reply.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -27,51 +26,42 @@ typedef struct {
   SilcCommand cmd;
 } SilcClientCommandReply;
 
-/* All client command replys */
-extern SilcClientCommandReply silc_command_reply_list[];
-
 /* Context sent as argument to all command reply functions */
 typedef struct {
   SilcClient client;
   SilcSocketConnection sock;
   SilcCommandPayload payload;
+  SilcArgumentPayload args;
+  SilcPacketContext *packet;
 
   /* If defined this executes the pending command. */
+  SilcCommandCb callback;
   void *context;
-  SilcClientCommandCallback callback;
+  uint16 ident;
 } *SilcClientCommandReplyContext;
 
 /* Macros */
 
-/* Macro used for command declaration in command reply list structure */
-#define SILC_CLIENT_CMD_REPLY(func, cmd ) \
-{ silc_client_command_reply_##func, SILC_COMMAND_##cmd }
-
 /* Macro used to declare command reply functions */
-#define SILC_CLIENT_CMD_REPLY_FUNC(func) \
-void silc_client_command_reply_##func(void *context)
-
-/* Macro used to execute command replies */
-#define SILC_CLIENT_COMMAND_REPLY_EXEC(ctx)            \
-do {                                                   \
-  SilcClientCommandReply *cmd;                         \
-                                                       \
-  for (cmd = silc_command_reply_list; cmd->cb; cmd++)  \
-    if (cmd->cmd == silc_command_get(ctx->payload)) {  \
-      cmd->cb(ctx);                                    \
-      break;                                           \
-    }                                                  \
-                                                       \
-  if (cmd == NULL) {                                   \
-    silc_free(ctx);                                    \
-    return;                                            \
-  }                                                    \
-} while(0)
+#define SILC_CLIENT_CMD_REPLY_FUNC(func)                               \
+void silc_client_command_reply_##func(void *context, void *context2)
+
+/* Status message structure. Messages are defined below. */
+typedef struct {
+  SilcCommandStatus status;
+  char *message;
+} SilcCommandStatusMessage;
+
+/* Status messages returned by the server */
+#define STAT(x) SILC_STATUS_ERR_##x
+DLLAPI extern const SilcCommandStatusMessage silc_command_status_messages[];
 
 /* Prototypes */
+
 void silc_client_command_reply_process(SilcClient client,
                                       SilcSocketConnection sock,
-                                      SilcBuffer buffer);
+                                      SilcPacketContext *packet);
+char *silc_client_command_status_message(SilcCommandStatus status);
 SILC_CLIENT_CMD_REPLY_FUNC(whois);
 SILC_CLIENT_CMD_REPLY_FUNC(whowas);
 SILC_CLIENT_CMD_REPLY_FUNC(identify);
@@ -79,13 +69,11 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick);
 SILC_CLIENT_CMD_REPLY_FUNC(list);
 SILC_CLIENT_CMD_REPLY_FUNC(topic);
 SILC_CLIENT_CMD_REPLY_FUNC(invite);
-SILC_CLIENT_CMD_REPLY_FUNC(quit);
 SILC_CLIENT_CMD_REPLY_FUNC(kill);
 SILC_CLIENT_CMD_REPLY_FUNC(info);
 SILC_CLIENT_CMD_REPLY_FUNC(links);
 SILC_CLIENT_CMD_REPLY_FUNC(stats);
 SILC_CLIENT_CMD_REPLY_FUNC(users);
-SILC_CLIENT_CMD_REPLY_FUNC(away);
 SILC_CLIENT_CMD_REPLY_FUNC(connect);
 SILC_CLIENT_CMD_REPLY_FUNC(ping);
 SILC_CLIENT_CMD_REPLY_FUNC(pong);
@@ -94,13 +82,20 @@ SILC_CLIENT_CMD_REPLY_FUNC(join);
 SILC_CLIENT_CMD_REPLY_FUNC(motd);
 SILC_CLIENT_CMD_REPLY_FUNC(umode);
 SILC_CLIENT_CMD_REPLY_FUNC(cmode);
+SILC_CLIENT_CMD_REPLY_FUNC(cumode);
 SILC_CLIENT_CMD_REPLY_FUNC(kick);
-SILC_CLIENT_CMD_REPLY_FUNC(restart);
+SILC_CLIENT_CMD_REPLY_FUNC(ban);
 SILC_CLIENT_CMD_REPLY_FUNC(close);
-SILC_CLIENT_CMD_REPLY_FUNC(die);
+SILC_CLIENT_CMD_REPLY_FUNC(shutdown);
 SILC_CLIENT_CMD_REPLY_FUNC(silcoper);
 SILC_CLIENT_CMD_REPLY_FUNC(leave);
-SILC_CLIENT_CMD_REPLY_FUNC(names);
-SILC_CLIENT_CMD_REPLY_FUNC(msg);
+SILC_CLIENT_CMD_REPLY_FUNC(users);
+SILC_CLIENT_CMD_REPLY_FUNC(getkey);
+SILC_CLIENT_CMD_REPLY_FUNC(quit);
+
+/* Internal command reply functions */
+SILC_CLIENT_CMD_REPLY_FUNC(whois_i);
+SILC_CLIENT_CMD_REPLY_FUNC(identify_i);
+SILC_CLIENT_CMD_REPLY_FUNC(info_i);
 
 #endif
diff --git a/lib/silcclient/idlist.c b/lib/silcclient/idlist.c
new file mode 100644 (file)
index 0000000..aa5ccc2
--- /dev/null
@@ -0,0 +1,1100 @@
+/*
+
+  idlist.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+/******************************************************************************
+
+                         Client Searching Locally
+
+******************************************************************************/
+
+/* Same as silc_client_get_clients function but does not resolve anything
+   from the server. This checks local cache and returns all matching
+   clients from the local cache. If none was found this returns NULL.
+   The `nickname' is the real nickname of the client, and the `format'
+   is the formatted nickname to find exact match from multiple found
+   entries. The format must be same as given in the SilcClientParams
+   structure to the client library. If the `format' is NULL all found
+   clients by `nickname' are returned. */
+
+SilcClientEntry *silc_client_get_clients_local(SilcClient client,
+                                              SilcClientConnection conn,
+                                              const char *nickname,
+                                              const char *format,
+                                              uint32 *clients_count)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list = NULL;
+  SilcClientEntry entry, *clients;
+  int i = 0;
+  bool found = FALSE;
+
+  /* Find ID from cache */
+  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, &list))
+    return NULL;
+
+  if (!silc_idcache_list_count(list)) {
+    silc_idcache_list_free(list);
+    return NULL;
+  }
+
+  clients = silc_calloc(silc_idcache_list_count(list), sizeof(*clients));
+  *clients_count = silc_idcache_list_count(list);
+
+  if (!format) {
+    /* Take all without any further checking */
+    silc_idcache_list_first(list, &id_cache);
+    while (id_cache) {
+      clients[i++] = id_cache->context;
+      found = TRUE;
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  } else {
+    /* Check multiple cache entries for match */
+    silc_idcache_list_first(list, &id_cache);
+    while (id_cache) {
+      entry = (SilcClientEntry)id_cache->context;
+      if (strcasecmp(entry->nickname, format)) {
+       if (!silc_idcache_list_next(list, &id_cache)) {
+         break;
+       } else {
+         continue;
+       }
+      }
+      
+      clients[i++] = id_cache->context;
+      found = TRUE;
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+  }
+
+  if (list)
+    silc_idcache_list_free(list);
+
+  if (!found) {
+    *clients_count = 0;
+    if (clients)
+      silc_free(clients);
+    return NULL;
+  }
+
+  return clients;
+}
+
+
+/******************************************************************************
+
+                        Client Resolving from Server
+
+******************************************************************************/
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcGetClientCallback completion;
+  void *context;
+  char *nickname;
+  char *server;
+} *GetClientInternal;
+
+SILC_CLIENT_CMD_FUNC(get_client_callback)
+{
+  GetClientInternal i = (GetClientInternal)context;
+  SilcClientEntry *clients;
+  uint32 clients_count;
+
+  /* Get the clients */
+  clients = silc_client_get_clients_local(i->client, i->conn,
+                                         i->nickname, i->server,
+                                         &clients_count);
+  if (clients) {
+    i->completion(i->client, i->conn, clients, clients_count, i->context);
+    silc_free(clients);
+  } else {
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+  }
+
+  silc_free(i->nickname);
+  silc_free(i->server);
+  silc_free(i);
+}
+
+/* Finds client entry or entries by the `nickname' and `server'. The 
+   completion callback will be called when the client entries has been found.
+
+   Note: this function is always asynchronous and resolves the client
+   information from the server. Thus, if you already know the client
+   information then use the silc_client_get_client_by_id function to
+   get the client entry since this function may be very slow and should
+   be used only to initially get the client entries. */
+
+void silc_client_get_clients(SilcClient client,
+                            SilcClientConnection conn,
+                            const char *nickname,
+                            const char *server,
+                            SilcGetClientCallback completion,
+                            void *context)
+{
+  GetClientInternal i;
+  char *userhost;
+
+  if (!nickname)
+    return;
+
+  i = silc_calloc(1, sizeof(*i));
+  i->client = client;
+  i->conn = conn;
+  i->nickname = strdup(nickname);
+  i->server = server ? strdup(server) : NULL;
+  i->completion = completion;
+  i->context = context;
+
+  if (nickname && server) {
+    userhost = silc_calloc(strlen(nickname) + strlen(server) + 2,
+                          sizeof(*userhost));
+    strncat(userhost, nickname, strlen(nickname));
+    strncat(userhost, "@", 1);
+    strncat(userhost, server, strlen(server));
+  } else {
+    userhost = strdup(nickname);
+  }
+
+  /* Register our own command reply for this command */
+  silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                              silc_client_command_reply_identify_i, 0,
+                              ++conn->cmd_ident);
+
+  /* Send the command */
+  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                          conn->cmd_ident, 1, 1, userhost, 
+                          strlen(userhost));
+
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                             silc_client_command_get_client_callback, 
+                             (void *)i);
+
+  silc_free(userhost);
+}
+
+/* The old style function to find client entry. This is used by the
+   library internally. If `query' is TRUE then the client information is
+   requested by the server. The pending command callback must be set
+   by the caller. */
+/* XXX This function should be removed */
+
+SilcClientEntry silc_idlist_get_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      const char *nickname,
+                                      const char *format,
+                                      bool query)
+{
+  SilcIDCacheEntry id_cache;
+  SilcIDCacheList list = NULL;
+  SilcClientEntry entry = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Find ID from cache */
+  if (!silc_idcache_find_by_name(conn->client_cache, (char *)nickname, 
+                                &list)) {
+  identify:
+
+    if (query) {
+      SILC_LOG_DEBUG(("Requesting Client ID from server"));
+      
+      /* Register our own command reply for this command */
+      silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                  silc_client_command_reply_identify_i, 0,
+                                  ++conn->cmd_ident);
+
+      /* Send the command */
+      silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                              conn->cmd_ident, 1, 1, nickname,
+                              strlen(nickname));
+
+      if (list)
+       silc_idcache_list_free(list);
+
+      return NULL;
+    }
+    return NULL;
+  }
+
+  if (!format) {
+    /* Take first found cache entry */
+    if (!silc_idcache_list_first(list, &id_cache))
+      goto identify;
+
+    entry = (SilcClientEntry)id_cache->context;
+  } else {
+    /* Check multiple cache entries for match */
+    silc_idcache_list_first(list, &id_cache);
+    while (id_cache) {
+      entry = (SilcClientEntry)id_cache->context;
+
+      if (strcasecmp(entry->nickname, format)) {
+       if (!silc_idcache_list_next(list, &id_cache)) {
+         entry = NULL;
+         break;
+       } else {
+         entry = NULL;
+         continue;
+       }
+      }
+
+      break;
+    }
+
+    /* If match weren't found, request it */
+    if (!entry)
+      goto identify;
+  }
+
+  if (list)
+    silc_idcache_list_free(list);
+
+  return entry;
+}
+
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  uint32 list_count;
+  SilcBuffer client_id_list;
+  SilcGetClientCallback completion;
+  void *context;
+} *GetClientsByListInternal;
+
+SILC_CLIENT_CMD_FUNC(get_clients_list_callback)
+{
+  GetClientsByListInternal i = (GetClientsByListInternal)context;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcBuffer client_id_list = i->client_id_list;
+  SilcClientEntry *clients = NULL;
+  uint32 clients_count = 0;
+  bool found = FALSE;
+  int c;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  for (c = 0; c < i->list_count; c++) {
+    uint16 idp_len;
+    SilcClientID *client_id;
+
+    /* Get Client ID */
+    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    if (!client_id) {
+      silc_buffer_pull(client_id_list, idp_len);
+      continue;
+    }
+
+    /* Get the client entry */
+    if (silc_idcache_find_by_id_one_ext(i->conn->client_cache, 
+                                       (void *)client_id, 
+                                       NULL, NULL, 
+                                       silc_hash_client_id_compare, NULL,
+                                       &id_cache)) {
+      clients = silc_realloc(clients, sizeof(*clients) * 
+                            (clients_count + 1));
+      clients[clients_count] = (SilcClientEntry)id_cache->context;
+      clients_count++;
+      found = TRUE;
+    }
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+  }
+
+  if (found) {
+    i->completion(i->client, i->conn, clients, clients_count, i->context);
+    silc_free(clients);
+  } else {
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+  }
+
+  if (i->client_id_list)
+    silc_buffer_free(i->client_id_list);
+  silc_free(i);
+}
+
+/* Gets client entries by the list of client ID's `client_id_list'. This
+   always resolves those client ID's it does not know yet from the server
+   so this function might take a while. The `client_id_list' is a list
+   of ID Payloads added one after other.  JOIN command reply and USERS
+   command reply for example returns this sort of list. The `completion'
+   will be called after the entries are available. */
+
+void silc_client_get_clients_by_list(SilcClient client,
+                                    SilcClientConnection conn,
+                                    uint32 list_count,
+                                    SilcBuffer client_id_list,
+                                    SilcGetClientCallback completion,
+                                    void *context)
+{
+  SilcIDCacheEntry id_cache = NULL;
+  int i;
+  unsigned char **res_argv = NULL;
+  uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
+  GetClientsByListInternal in;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  in = silc_calloc(1, sizeof(*in));
+  in->client = client;
+  in->conn = conn;
+  in->list_count = list_count;
+  in->client_id_list = silc_buffer_copy(client_id_list);
+  in->completion = completion;
+  in->context = context;
+
+  for (i = 0; i < list_count; i++) {
+    uint16 idp_len;
+    SilcClientID *client_id;
+    SilcClientEntry entry;
+
+    /* Get Client ID */
+    SILC_GET16_MSB(idp_len, client_id_list->data + 2);
+    idp_len += 4;
+    client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
+    if (!client_id) {
+      silc_buffer_pull(client_id_list, idp_len);
+      continue;
+    }
+
+    /* Check if we have this client cached already. */
+    id_cache = NULL;
+    silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
+                                   NULL, NULL, 
+                                   silc_hash_client_id_compare, NULL,
+                                   &id_cache);
+
+    /* If we don't have the entry or it has incomplete info, then resolve
+       it from the server. */
+    entry = id_cache ? (SilcClientEntry)id_cache->context : NULL;
+    if (!id_cache || !entry->nickname) {
+
+      if (entry) {
+       if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+         entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+         silc_free(client_id);
+         silc_buffer_pull(client_id_list, idp_len);
+         continue;
+       }
+
+       entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+      }
+
+      /* No we don't have it, query it from the server. Assemble argument
+        table that will be sent fr the IDENTIFY command later. */
+      res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
+                             (res_argc + 1));
+      res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
+                                  (res_argc + 1));
+      res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
+                                   (res_argc + 1));
+      res_argv[res_argc] = client_id_list->data;
+      res_argv_lens[res_argc] = idp_len;
+      res_argv_types[res_argc] = res_argc + 5;
+      res_argc++;
+    }
+
+    silc_free(client_id);
+    silc_buffer_pull(client_id_list, idp_len);
+  }
+
+  /* Query the client information from server if the list included clients
+     that we don't know about. */
+  if (res_argc) {
+    SilcBuffer res_cmd;
+
+    /* Send the IDENTIFY command to server */
+    res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
+                                         res_argc, res_argv, res_argv_lens,
+                                         res_argv_types, ++conn->cmd_ident);
+    silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, 
+                           NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
+                           TRUE);
+
+    /* Register our own command reply for this command */
+    silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                                silc_client_command_reply_identify_i, 0,
+                                conn->cmd_ident);
+
+    /* Process the applications request after reply has been received  */
+    silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                               silc_client_command_get_clients_list_callback, 
+                               (void *)in);
+
+    silc_buffer_push(client_id_list, client_id_list->data - 
+                    client_id_list->head);
+    silc_buffer_free(res_cmd);
+    silc_free(res_argv);
+    silc_free(res_argv_lens);
+    silc_free(res_argv_types);
+    return;
+  }
+
+  silc_buffer_push(client_id_list, client_id_list->data - 
+                  client_id_list->head);
+
+  /* We have the clients in cache, get them and call the completion */
+  silc_client_command_get_clients_list_callback((void *)in, NULL);
+}
+
+/* Finds entry for client by the client's ID. Returns the entry or NULL
+   if the entry was not found. */
+
+SilcClientEntry silc_client_get_client_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientID *client_id)
+{
+  SilcIDCacheEntry id_cache;
+
+  SILC_LOG_DEBUG(("Finding client by ID (%s)", 
+                 silc_id_render(client_id, SILC_ID_CLIENT)));
+
+  /* Find ID from cache */
+  if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
+                                      NULL, NULL, 
+                                      silc_hash_client_id_compare, NULL,
+                                      &id_cache))
+    return NULL;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return (SilcClientEntry)id_cache->context;
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcClientID *client_id;
+  SilcGetClientCallback completion;
+  void *context;
+} *GetClientByIDInternal;
+
+SILC_CLIENT_CMD_FUNC(get_client_by_id_callback)
+{
+  GetClientByIDInternal i = (GetClientByIDInternal)context;
+  SilcClientEntry entry;
+
+  /* Get the client */
+  entry = silc_client_get_client_by_id(i->client, i->conn, i->client_id);
+  if (entry)
+    i->completion(i->client, i->conn, &entry, 1, i->context);
+  else
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+
+  silc_free(i->client_id);
+  silc_free(i);
+}
+
+/* Same as above but will always resolve the information from the server.
+   Use this only if you know that you don't have the entry and the only
+   thing you know about the client is its ID. */
+
+void silc_client_get_client_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientID *client_id,
+                                         SilcGetClientCallback completion,
+                                         void *context)
+{
+  SilcBuffer idp;
+  GetClientByIDInternal i = silc_calloc(1, sizeof(*i));
+
+  SILC_LOG_DEBUG(("Start"));
+
+  i->client = client;
+  i->conn = conn;
+  i->client_id = silc_id_dup(client_id, SILC_ID_CLIENT);
+  i->completion = completion;
+  i->context = context;
+      
+  /* Register our own command reply for this command */
+  silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
+                              silc_client_command_reply_whois_i, 0,
+                              ++conn->cmd_ident);
+
+  /* Send the command */
+  idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
+  silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                          1, 3, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
+                             silc_client_command_get_client_by_id_callback, 
+                             (void *)i);
+}
+
+
+/******************************************************************************
+
+                Client, Channel and Server entry manipulation
+
+******************************************************************************/
+
+
+/* Creates new client entry and adds it to the ID cache. Returns pointer
+   to the new entry. */
+
+SilcClientEntry
+silc_client_add_client(SilcClient client, SilcClientConnection conn,
+                      char *nickname, char *username, 
+                      char *userinfo, SilcClientID *id, uint32 mode)
+{
+  SilcClientEntry client_entry;
+  char *nick = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Save the client infos */
+  client_entry = silc_calloc(1, sizeof(*client_entry));
+  client_entry->id = id;
+  client_entry->valid = TRUE;
+  silc_parse_userfqdn(nickname, &nick, &client_entry->server);
+  silc_parse_userfqdn(username, &client_entry->username, 
+                     &client_entry->hostname);
+  if (userinfo)
+    client_entry->realname = strdup(userinfo);
+  client_entry->mode = mode;
+  if (nick)
+    client_entry->nickname = strdup(nick);
+  client_entry->channels = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
+                                                NULL, NULL, NULL, TRUE);
+
+  /* Format the nickname */
+  silc_client_nickname_format(client, conn, client_entry);
+  
+  /* Add client to cache, the non-formatted nickname is saved to cache */
+  if (!silc_idcache_add(conn->client_cache, nick, client_entry->id, 
+                       (void *)client_entry, 0, NULL)) {
+    silc_free(client_entry->nickname);
+    silc_free(client_entry->username);
+    silc_free(client_entry->hostname);
+    silc_free(client_entry->server);
+    silc_hash_table_free(client_entry->channels);
+    silc_free(client_entry);
+    return NULL;
+  }
+
+  return client_entry;
+}
+
+/* Updates the `client_entry' with the new information sent as argument. */
+
+void silc_client_update_client(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcClientEntry client_entry,
+                              const char *nickname,
+                              const char *username,
+                              const char *userinfo,
+                              uint32 mode)
+{
+  char *nick = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client_entry->username && username)
+    silc_parse_userfqdn(username, &client_entry->username, 
+                       &client_entry->hostname);
+  if (!client_entry->realname && userinfo)
+    client_entry->realname = strdup(userinfo);
+  if (!client_entry->nickname && nickname) {
+    silc_parse_userfqdn(nickname, &nick, &client_entry->server);
+    client_entry->nickname = strdup(nick);
+    silc_client_nickname_format(client, conn, client_entry);
+  }
+  client_entry->mode = mode;
+
+  if (nick) {
+    /* Remove the old cache entry and create a new one */
+    silc_idcache_del_by_context(conn->client_cache, client_entry);
+    silc_idcache_add(conn->client_cache, nick, client_entry->id, 
+                    client_entry, 0, NULL);
+  }
+}
+
+/* Deletes the client entry and frees all memory. */
+
+void silc_client_del_client_entry(SilcClient client, 
+                                 SilcClientConnection conn,
+                                 SilcClientEntry client_entry)
+{
+  SILC_LOG_DEBUG(("Start"));
+
+  silc_free(client_entry->nickname);
+  silc_free(client_entry->username);
+  silc_free(client_entry->realname);
+  silc_free(client_entry->hostname);
+  silc_free(client_entry->server);
+  silc_free(client_entry->id);
+  silc_free(client_entry->fingerprint);
+  silc_hash_table_free(client_entry->channels);
+  if (client_entry->send_key)
+    silc_cipher_free(client_entry->send_key);
+  if (client_entry->receive_key)
+    silc_cipher_free(client_entry->receive_key);
+  silc_free(client_entry->key);
+  silc_client_ftp_session_free_client(conn, client_entry);
+  if (client_entry->ke)
+    silc_client_abort_key_agreement(client, conn, client_entry);
+  silc_free(client_entry);
+}
+
+/* Removes client from the cache by the client entry. */
+
+bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+                           SilcClientEntry client_entry)
+{
+  bool ret = silc_idcache_del_by_context(conn->client_cache, client_entry);
+
+  /* Remove from channels */
+  silc_client_remove_from_channels(client, conn, client_entry);
+
+  /* Free the client entry data */
+  silc_client_del_client_entry(client, conn, client_entry);
+
+  return ret;
+}
+
+/* Add new channel entry to the ID Cache */
+
+SilcChannelEntry silc_client_add_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        const char *channel_name,
+                                        uint32 mode, 
+                                        SilcChannelID *channel_id)
+{
+  SilcChannelEntry channel;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  channel = silc_calloc(1, sizeof(*channel));
+  channel->channel_name = strdup(channel_name);
+  channel->id = channel_id;
+  channel->mode = mode;
+  channel->user_list = silc_hash_table_alloc(1, silc_hash_ptr, NULL, NULL,
+                                            NULL, NULL, NULL, TRUE);
+
+  /* Put it to the ID cache */
+  if (!silc_idcache_add(conn->channel_cache, channel->channel_name, 
+                       (void *)channel->id, (void *)channel, 0, NULL)) {
+    silc_free(channel->channel_name);
+    silc_hash_table_free(channel->user_list);
+    silc_free(channel);
+    return NULL;
+  }
+
+  return channel;
+}
+
+/* Foreach callbcak to free all users from the channel when deleting a
+   channel entry. */
+
+static void silc_client_del_channel_foreach(void *key, void *context,
+                                           void *user_context)
+{
+  SilcChannelUser chu = (SilcChannelUser)context;
+
+  /* Remove the context from the client's channel hash table as that
+     table and channel's user_list hash table share this same context. */
+  silc_hash_table_del(chu->client->channels, chu->channel);
+  silc_free(chu);
+}
+
+/* Removes channel from the cache by the channel entry. */
+
+bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
+                            SilcChannelEntry channel)
+{
+  bool ret = silc_idcache_del_by_context(conn->channel_cache, channel);
+
+  /* Free all client entrys from the users list. The silc_hash_table_free
+     will free all the entries so they are not freed at the foreach 
+     callback. */
+  silc_hash_table_foreach(channel->user_list, silc_client_del_channel_foreach,
+                         NULL);
+  silc_hash_table_free(channel->user_list);
+
+  silc_free(channel->channel_name);
+  silc_free(channel->id);
+  silc_free(channel->key);
+  if (channel->channel_key)
+    silc_cipher_free(channel->channel_key);
+  if (channel->hmac)
+    silc_hmac_free(channel->hmac);
+  if (channel->old_channel_key)
+    silc_cipher_free(channel->old_channel_key);
+  if (channel->old_hmac)
+    silc_hmac_free(channel->old_hmac);
+  if (channel->rekey_task)
+    silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
+  silc_client_del_channel_private_keys(client, conn, channel);
+  silc_free(channel);
+  return ret;
+}
+
+/* Replaces the channel ID of the `channel' to `new_id'. Returns FALSE
+   if the ID could not be changed. */
+
+bool silc_client_replace_channel_id(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
+                                   SilcChannelID *new_id)
+{
+  if (!new_id)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
+                 silc_id_render(channel->id, SILC_ID_CHANNEL)));
+  SILC_LOG_DEBUG(("New Channel ID id(%s)", 
+                 silc_id_render(new_id, SILC_ID_CHANNEL)));
+
+  silc_idcache_del_by_id(conn->channel_cache, channel->id);
+  silc_free(channel->id);
+  channel->id = new_id;
+  return silc_idcache_add(conn->channel_cache, channel->channel_name, 
+                         (void *)channel->id, (void *)channel, 0, NULL);
+
+}
+
+/* Finds entry for channel by the channel name. Returns the entry or NULL
+   if the entry was not found. It is found only if the client is joined
+   to the channel. */
+
+SilcChannelEntry silc_client_get_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        char *channel)
+{
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!silc_idcache_find_by_name_one(conn->channel_cache, channel, 
+                                    &id_cache))
+    return NULL;
+
+  entry = (SilcChannelEntry)id_cache->context;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return entry;
+}
+
+/* Finds entry for channel by the channel ID. Returns the entry or NULL
+   if the entry was not found. It is found only if the client is joined
+   to the channel. */
+
+SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcChannelEntry entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, channel_id, 
+                                  &id_cache))
+    return NULL;
+
+  entry = (SilcChannelEntry)id_cache->context;
+
+  SILC_LOG_DEBUG(("Found"));
+
+  return entry;
+}
+
+typedef struct {
+  SilcClient client;
+  SilcClientConnection conn;
+  SilcChannelID *channel_id;
+  SilcGetChannelCallback completion;
+  void *context;
+} *GetChannelByIDInternal;
+
+SILC_CLIENT_CMD_FUNC(get_channel_by_id_callback)
+{
+  GetChannelByIDInternal i = (GetChannelByIDInternal)context;
+  SilcChannelEntry entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Get the channel */
+  entry = silc_client_get_channel_by_id(i->client, i->conn, i->channel_id);
+  if (entry) {
+    i->completion(i->client, i->conn, &entry, 1, i->context);
+  } else {
+    i->completion(i->client, i->conn, NULL, 0, i->context);
+  }
+
+  silc_free(i->channel_id);
+  silc_free(i);
+}
+
+/* Resolves channel information from the server by the channel ID. */
+
+void silc_client_get_channel_by_id_resolve(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcChannelID *channel_id,
+                                          SilcGetChannelCallback completion,
+                                          void *context)
+{
+  SilcBuffer idp;
+  GetChannelByIDInternal i = silc_calloc(1, sizeof(*i));
+
+  SILC_LOG_DEBUG(("Start"));
+
+  i->client = client;
+  i->conn = conn;
+  i->channel_id = silc_id_dup(channel_id, SILC_ID_CHANNEL);
+  i->completion = completion;
+  i->context = context;
+      
+  /* Register our own command reply for this command */
+  silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
+                              silc_client_command_reply_identify_i, 0,
+                              ++conn->cmd_ident);
+
+  /* Send the command */
+  idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+  silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
+                          conn->cmd_ident,
+                          1, 5, idp->data, idp->len);
+  silc_buffer_free(idp);
+
+  /* Add pending callback */
+  silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
+                             silc_client_command_get_channel_by_id_callback, 
+                             (void *)i);
+}
+
+/* Finds entry for server by the server name. */
+
+SilcServerEntry silc_client_get_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *server_name)
+{
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!silc_idcache_find_by_name_one(conn->server_cache, server_name, 
+                                    &id_cache))
+    return NULL;
+
+  entry = (SilcServerEntry)id_cache->context;
+
+  return entry;
+}
+
+/* Finds entry for server by the server ID. */
+
+SilcServerEntry silc_client_get_server_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcServerID *server_id)
+{
+  SilcIDCacheEntry id_cache;
+  SilcServerEntry entry;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, 
+                                  &id_cache))
+    return NULL;
+
+  entry = (SilcServerEntry)id_cache->context;
+
+  return entry;
+}
+
+/* Removes server from the cache by the server entry. */
+
+bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
+                           SilcServerEntry server)
+{
+  bool ret = silc_idcache_del_by_context(conn->server_cache, server);
+  silc_free(server->server_name);
+  silc_free(server->server_info);
+  silc_free(server->server_id);
+  silc_free(server);
+  return ret;
+}
+
+/* Formats the nickname of the client specified by the `client_entry'.
+   If the format is specified by the application this will format the
+   nickname and replace the old nickname in the client entry. If the
+   format string is not specified then this function has no effect. */
+
+void silc_client_nickname_format(SilcClient client, 
+                                SilcClientConnection conn,
+                                SilcClientEntry client_entry)
+{
+  char *cp;
+  char *newnick = NULL;
+  int i, off = 0, len;
+  SilcClientEntry *clients;
+  uint32 clients_count = 0;
+  SilcClientEntry unformatted = NULL;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (!client->internal->params->nickname_format[0])
+    return;
+
+  if (!client_entry->nickname)
+    return;
+
+  /* Get all clients with same nickname. Do not perform the formatting
+     if there aren't any clients with same nickname unless the application
+     is forcing us to do so. */
+  clients = silc_client_get_clients_local(client, conn,
+                                         client_entry->nickname, NULL,
+                                         &clients_count);
+  if (!clients && !client->internal->params->nickname_force_format)
+    return;
+
+  len = 0;
+  for (i = 0; i < clients_count; i++)
+    if (clients[i]->valid && clients[i] != client_entry)
+      len++;
+  if (!len)
+    return;
+
+  cp = client->internal->params->nickname_format;
+  while (*cp) {
+    if (*cp == '%') {
+      cp++;
+      continue;
+    }
+
+    switch(*cp) {
+    case 'n':
+      /* Nickname */
+      if (!client_entry->nickname)
+       break;
+      len = strlen(client_entry->nickname);
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->nickname, len);
+      off += len;
+      break;
+    case 'h':
+      /* Stripped hostname */
+      if (!client_entry->hostname)
+       break;
+      len = strcspn(client_entry->hostname, ".");
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->hostname, len);
+      off += len;
+      break;
+    case 'H':
+      /* Full hostname */
+      if (!client_entry->hostname)
+       break;
+      len = strlen(client_entry->hostname);
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->hostname, len);
+      off += len;
+      break;
+    case 's':
+      /* Stripped server name */
+      if (!client_entry->server)
+       break;
+      len = strcspn(client_entry->server, ".");
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->server, len);
+      off += len;
+      break;
+    case 'S':
+      /* Full server name */
+      if (!client_entry->server)
+       break;
+      len = strlen(client_entry->server);
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+      memcpy(&newnick[off], client_entry->server, len);
+      off += len;
+      break;
+    case 'a':
+      /* Ascending number */
+      {
+       char tmp[6];
+       int num, max = 1;
+
+       if (clients_count == 1) {
+         unformatted = clients[0];
+         break;
+       }
+
+       for (i = 0; i < clients_count; i++) {
+         if (!strncasecmp(clients[i]->nickname, client_entry->nickname,
+                          strlen(clients[i]->nickname)))
+           unformatted = clients[i];
+         if (strncasecmp(clients[i]->nickname, newnick, off))
+           continue;
+         if (strlen(clients[i]->nickname) <= off)
+           continue;
+         num = atoi(&clients[i]->nickname[off]);
+         if (num > max)
+           max = num;
+       }
+
+       memset(tmp, 0, sizeof(tmp));
+       snprintf(tmp, sizeof(tmp) - 1, "%d", ++max);
+       len = strlen(tmp);
+       newnick = silc_realloc(newnick, sizeof(*newnick) * (off + len));
+       memcpy(&newnick[off], tmp, len);
+       off += len;
+      }
+      break;
+    default:
+      /* Some other character in the string */
+      newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
+      memcpy(&newnick[off], cp, 1);
+      off++;
+      break;
+    }
+
+    cp++;
+  }
+
+  newnick = silc_realloc(newnick, sizeof(*newnick) * (off + 1));
+  newnick[off] = 0;
+
+  /* If we are changing nickname of our local entry we'll enforce
+     that we will always get the unformatted nickname.  Give our
+     format number to the one that is not formatted now. */
+  if (unformatted && client_entry == conn->local_entry)
+    client_entry = unformatted;
+
+  silc_free(client_entry->nickname);
+  client_entry->nickname = newnick;
+  silc_free(clients);
+}
diff --git a/lib/silcclient/idlist.h b/lib/silcclient/idlist.h
new file mode 100644 (file)
index 0000000..b515baa
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+
+  idlist.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef IDLIST_H
+#define IDLIST_H
+
+typedef struct SilcChannelEntryStruct *SilcChannelEntry;
+
+/* Client entry status */
+typedef enum {
+  SILC_CLIENT_STATUS_NONE       = 0x0000,
+  SILC_CLIENT_STATUS_RESOLVING  = 0x0001,
+} SilcClientStatus;
+
+/* Client entry context. When client receives information about new client
+   (it receives its ID, for example, by IDENTIFY request) we create new
+   client entry. This entry also includes the private message keys if
+   they are used. */
+typedef struct {
+  char *nickname;              /* nickname */
+  char *username;              /* username */
+  char *hostname;              /* hostname */
+  char *server;                        /* SILC server name */
+  char *realname;              /* Realname (userinfo) */
+  uint32 num;
+  uint32 mode;                 /* User mode in SILC */
+  SilcClientID *id;            /* The Client ID */
+  unsigned char *fingerprint;  /* Fingerprint of client's public key */
+  uint32 fingerprint_len;      /* Length of the fingerprint */
+  bool valid;                  /* FALSE if this entry is not valid */
+  SilcCipher send_key;         /* Private message key for sending */
+  SilcCipher receive_key;      /* Private message key for receiving */
+  unsigned char *key;          /* Set only if appliation provided the
+                                  key material. NULL if the library 
+                                  generated the key. */
+  uint32 key_len;
+  bool generated;              /* TRUE if library generated the key */
+  SilcClientKeyAgreement ke;   /* Current key agreement context or NULL */
+  SilcClientStatus status;     /* Status mask */
+  SilcHashTable channels;      /* All channels client has joined */
+} *SilcClientEntry;
+
+/* Client and its mode on a channel */
+typedef struct SilcChannelUserStruct {
+  SilcClientEntry client;
+  uint32 mode;
+  SilcChannelEntry channel;
+} *SilcChannelUser;
+
+/* Structure to hold one channel private key. */
+typedef struct {
+  SilcCipher cipher;                 /* The cipher and key */
+  SilcHmac hmac;                     /* The HMAC and hmac key */
+  unsigned char *key;                /* The key data */
+  uint32 key_len;                    /* The key length */
+} *SilcChannelPrivateKey;
+
+/* Channel entry context. This is allocate for every channel client has
+   joined to. This includes for example the channel specific keys */
+struct SilcChannelEntryStruct {
+  char *channel_name;
+  SilcChannelID *id;
+  uint32 mode;
+
+  /* All clients that has joined this channel */
+  SilcHashTable user_list;
+
+  /* Channel keys */
+  SilcCipher channel_key;                    /* The channel key */
+  unsigned char *key;                       /* Raw key data */
+  uint32 key_len;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE]; /* Current IV */
+  SilcHmac hmac;                            /* Current HMAC */
+  SilcDList private_keys;                   /* List of private keys or NULL */
+  SilcChannelPrivateKey curr_key;           /* Current private key */
+
+  /* Old channel key is saved for a short period of time when rekey occurs
+     in case if someone is sending messages after the rekey encrypted with
+     the old key, we can still decrypt them. */
+  SilcCipher old_channel_key;
+  SilcHmac old_hmac;
+  SilcTask rekey_task;
+};
+
+/* Server entry context. This represents one server. When server information
+   is resolved with INFO command the server info is saved in this context. 
+   Also the connected servers are saved here. */
+typedef struct {
+  char *server_name;
+  char *server_info;
+  SilcServerID *server_id;
+} *SilcServerEntry;
+
+/* Prototypes. These are used only by the library. Application should not
+   call these directly. */
+
+SilcClientEntry
+silc_client_add_client(SilcClient client, SilcClientConnection conn,
+                      char *nickname, char *username, 
+                      char *userinfo, SilcClientID *id, uint32 mode);
+void silc_client_update_client(SilcClient client,
+                              SilcClientConnection conn,
+                              SilcClientEntry client_entry,
+                              const char *nickname,
+                              const char *username,
+                              const char *userinfo,
+                              uint32 mode);
+void silc_client_del_client_entry(SilcClient client, 
+                                 SilcClientConnection conn,
+                                 SilcClientEntry client_entry);
+SilcClientEntry silc_idlist_get_client(SilcClient client,
+                                      SilcClientConnection conn,
+                                      const char *nickname,
+                                      const char *format,
+                                      bool query);
+SilcChannelEntry silc_client_add_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        const char *channel_name,
+                                        uint32 mode, 
+                                        SilcChannelID *channel_id);
+bool silc_client_replace_channel_id(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcChannelEntry channel,
+                                   SilcChannelID *new_id);
+void silc_client_nickname_format(SilcClient client, 
+                                SilcClientConnection conn,
+                                SilcClientEntry client_entry);
+
+#endif
diff --git a/lib/silcclient/protocol.c b/lib/silcclient/protocol.c
new file mode 100644 (file)
index 0000000..41daced
--- /dev/null
@@ -0,0 +1,1167 @@
+/*
+
+  protocol.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/*
+ * Client side of the protocols.
+ */
+/* $Id$ */
+
+#include "clientlibincludes.h"
+#include "client_internal.h"
+
+SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
+SILC_TASK_CALLBACK(silc_client_protocol_key_exchange);
+SILC_TASK_CALLBACK(silc_client_protocol_rekey);
+
+/*
+ * Key Exhange protocol functions
+ */
+
+/* Function that is called when SKE protocol sends packets to network. */
+
+void silc_client_protocol_ke_send_packet(SilcSKE ske,
+                                        SilcBuffer packet,
+                                        SilcPacketType type,
+                                        void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  /* Send the packet immediately */
+  silc_client_packet_send(client, ske->sock, type, NULL, 0, NULL, NULL,
+                         packet->data, packet->len, TRUE);
+}
+
+/* Public key verification callback. Called by the application. */
+
+typedef struct {
+  SilcSKE ske;
+  SilcSKEVerifyCbCompletion completion;
+  void *completion_context;
+} *VerifyKeyContext;
+
+static void silc_client_verify_key_cb(bool success, void *context)
+{
+  VerifyKeyContext verify = (VerifyKeyContext)context;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Call the completion callback back to the SKE */
+  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK : 
+                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, 
+                    verify->completion_context);
+
+  silc_free(verify);
+}
+
+/* Callback that is called when we have received KE payload from
+   responder. We try to verify the public key now. */
+
+void silc_client_protocol_ke_verify_key(SilcSKE ske,
+                                       unsigned char *pk_data,
+                                       uint32 pk_len,
+                                       SilcSKEPKType pk_type,
+                                       void *context,
+                                       SilcSKEVerifyCbCompletion completion,
+                                       void *completion_context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  VerifyKeyContext verify;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  verify = silc_calloc(1, sizeof(*verify));
+  verify->ske = ske;
+  verify->completion = completion;
+  verify->completion_context = completion_context;
+
+  /* Verify public key from user. */
+  client->internal->ops->verify_public_key(client, ctx->sock->user_data, 
+                                          ctx->sock->type,
+                                          pk_data, pk_len, pk_type,
+                                          silc_client_verify_key_cb, verify);
+}
+
+/* Sets the negotiated key material into use for particular connection. */
+
+void silc_client_protocol_ke_set_keys(SilcSKE ske,
+                                     SilcSocketConnection sock,
+                                     SilcSKEKeyMaterial *keymat,
+                                     SilcCipher cipher,
+                                     SilcPKCS pkcs,
+                                     SilcHash hash,
+                                     SilcHmac hmac,
+                                     SilcSKEDiffieHellmanGroup group,
+                                     bool is_responder)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+
+  SILC_LOG_DEBUG(("Setting new keys into use"));
+
+  /* Allocate cipher to be used in the communication */
+  silc_cipher_alloc(cipher->cipher->name, &conn->send_key);
+  silc_cipher_alloc(cipher->cipher->name, &conn->receive_key);
+  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, &conn->hmac_send);
+  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, &conn->hmac_receive);
+
+  if (is_responder == TRUE) {
+    silc_cipher_set_key(conn->send_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->send_key, keymat->receive_iv);
+    silc_cipher_set_key(conn->receive_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->receive_key, keymat->send_iv);
+    silc_hmac_set_key(conn->hmac_send, keymat->receive_hmac_key, 
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(conn->hmac_receive, keymat->send_hmac_key, 
+                     keymat->hmac_key_len);
+  } else {
+    silc_cipher_set_key(conn->send_key, keymat->send_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->send_key, keymat->send_iv);
+    silc_cipher_set_key(conn->receive_key, keymat->receive_enc_key, 
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(conn->receive_key, keymat->receive_iv);
+    silc_hmac_set_key(conn->hmac_send, keymat->send_hmac_key, 
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(conn->hmac_receive, keymat->receive_hmac_key, 
+                     keymat->hmac_key_len);
+  }
+
+  /* Rekey stuff */
+  conn->rekey = silc_calloc(1, sizeof(*conn->rekey));
+  conn->rekey->send_enc_key = 
+    silc_calloc(keymat->enc_key_len / 8,
+               sizeof(*conn->rekey->send_enc_key));
+  memcpy(conn->rekey->send_enc_key, 
+        keymat->send_enc_key, keymat->enc_key_len / 8);
+  conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+
+  if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
+    conn->rekey->pfs = TRUE;
+  conn->rekey->ske_group = silc_ske_group_get_number(group);
+
+  /* Save the HASH function */
+  silc_hash_alloc(hash->hash->name, &conn->hash);
+}
+
+/* Checks the version string of the server. */
+
+SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
+                                    uint32 len, void *context)
+{
+  SilcClientConnection conn = (SilcClientConnection)ske->sock->user_data;
+  SilcClient client = (SilcClient)ske->user_data;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  char *cp;
+  int maj = 0, min = 0, build = 0, maj2 = 0, min2 = 0, build2 = 0;
+
+  /* Check for initial version string */
+  if (!strstr(version, "SILC-1.0-"))
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  /* Check software version */
+
+  cp = version + 9;
+  if (!cp)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  maj = atoi(cp);
+  cp = strchr(cp, '.');
+  if (cp) {
+    min = atoi(cp + 1);
+    cp++;
+  }
+  cp = strchr(cp, '.');
+  if (cp)
+    build = atoi(cp + 1);
+
+  cp = client->internal->silc_client_version + 9;
+  if (!cp)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  maj2 = atoi(cp);
+  cp = strchr(cp, '.');
+  if (cp) {
+    min2 = atoi(cp + 1);
+    cp++;
+  }
+  cp = strchr(cp, '.');
+  if (cp)
+    build2 = atoi(cp + 1);
+
+  if (maj != maj2)
+    status = SILC_SKE_STATUS_BAD_VERSION;
+
+  /* XXX backward support for 0.6.1 */
+  if (maj == 0 && min == 6 && build < 2)
+    ske->backward_version = 1;
+
+  if (status != SILC_SKE_STATUS_OK)
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                              "We don't support server version `%s'", 
+                              version);
+
+  return status;
+}
+
+/* Callback that is called by the SKE to indicate that it is safe to
+   continue the execution of the protocol. Is given as argument to the 
+   silc_ske_initiator_finish or silc_ske_responder_phase_2 functions. 
+   This is called due to the fact that the public key verification
+   process is asynchronous and we must not continue the protocl until
+   the public key has been verified and this callback is called. */
+
+static void silc_client_protocol_ke_continue(SilcSKE ske,
+                                            void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = ctx->sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    /* Call failure client operation */
+    client->internal->ops->failure(client, conn, protocol, 
+                                  (void *)ske->status);
+    protocol->state = SILC_PROTOCOL_STATE_ERROR;
+    silc_protocol_execute(protocol, client->schedule, 0, 0);
+    return;
+  }
+
+  /* Send Ok to the other end. We will end the protocol as server
+     sends Ok to us when we will take the new keys into use. Do this
+     if we are initiator. This is happens when this callback was sent
+     to silc_ske_initiator_finish function. */
+  if (ctx->responder == FALSE) {
+    silc_ske_end(ctx->ske);
+
+    /* End the protocol on the next round */
+    protocol->state = SILC_PROTOCOL_STATE_END;
+  }
+
+  /* Advance protocol state and call the next state if we are responder. 
+     This happens when this callback was sent to silc_ske_responder_phase_2
+     function. */
+  if (ctx->responder == TRUE) {
+    protocol->state++;
+    silc_protocol_execute(protocol, client->schedule, 0, 100000);
+  }
+}
+
+/* Performs key exchange protocol. This is used for both initiator
+   and responder key exchange. This may be called recursively. */
+
+SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientKEInternalContext *ctx = 
+    (SilcClientKEInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = ctx->sock->user_data;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    {
+      /*
+       * Start Protocol
+       */
+      SilcSKE ske;
+
+      /* Allocate Key Exchange object */
+      ske = silc_ske_alloc();
+      ctx->ske = ske;
+      ske->rng = client->rng;
+      ske->user_data = (void *)client;
+
+      silc_ske_set_callbacks(ske, ctx->send_packet, NULL,
+                            ctx->verify,
+                            silc_client_protocol_ke_continue,
+                            silc_ske_check_version, 
+                            context);
+      
+      if (ctx->responder == TRUE) {
+       /* Start the key exchange by processing the received security
+          properties packet from initiator. */
+       status = 
+         silc_ske_responder_start(ske, ctx->rng, ctx->sock,
+                                  client->internal->silc_client_version,
+                                  ctx->packet->buffer, TRUE);
+      } else {
+       SilcSKEStartPayload *start_payload;
+
+       /* Assemble security properties. */
+       silc_ske_assemble_security_properties(
+                                 ske, SILC_SKE_SP_FLAG_MUTUAL, 
+                                 client->internal->silc_client_version,
+                                 &start_payload);
+
+       /* Start the key exchange by sending our security properties
+          to the remote end. */
+       status = silc_ske_initiator_start(ske, ctx->rng, ctx->sock,
+                                         start_payload);
+      }
+
+      /* Return now if the procedure is pending */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
+      if (status != SILC_SKE_STATUS_OK) {
+       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                         status));
+       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                       status));
+
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       silc_protocol_execute(protocol, client->schedule, 0, 0);
+       return;
+      }
+
+      /* Advance protocol state and call the next state if we are responder */
+      protocol->state++;
+      if (ctx->responder == TRUE)
+       silc_protocol_execute(protocol, client->schedule, 0, 100000);
+    }
+    break;
+  case 2:
+    {
+      /* 
+       * Phase 1 
+       */
+      if (ctx->responder == TRUE) {
+       /* Sends the selected security properties to the initiator. */
+       status = 
+         silc_ske_responder_phase_1(ctx->ske, 
+                                    ctx->ske->start_payload);
+      } else {
+       /* Call Phase-1 function. This processes the Key Exchange Start
+          paylaod reply we just got from the responder. The callback
+          function will receive the processed payload where we will
+          save it. */
+       status = silc_ske_initiator_phase_1(ctx->ske, ctx->packet->buffer);
+      }
+
+      if (status != SILC_SKE_STATUS_OK) {
+       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                         status));
+       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                       status));
+
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       silc_protocol_execute(protocol, client->schedule, 0, 0);
+       return;
+      }
+
+      /* Advance protocol state and call next state if we are initiator */
+      protocol->state++;
+      if (ctx->responder == FALSE)
+       silc_protocol_execute(protocol, client->schedule, 0, 100000);
+    }
+    break;
+  case 3:
+    {
+      /* 
+       * Phase 2 
+       */
+      if (ctx->responder == TRUE) {
+       /* Process the received Key Exchange 1 Payload packet from
+          the initiator. This also creates our parts of the Diffie
+          Hellman algorithm. The silc_client_protocol_ke_continue will
+          be called after the public key has been verified. */
+       status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
+      } else {
+       /* Call the Phase-2 function. This creates Diffie Hellman
+          key exchange parameters and sends our public part inside
+          Key Exhange 1 Payload to the responder. */
+       status = silc_ske_initiator_phase_2(ctx->ske,
+                                           client->public_key,
+                                           client->private_key);
+       protocol->state++;
+      }
+
+      /* Return now if the procedure is pending */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
+      if (status != SILC_SKE_STATUS_OK) {
+       SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                         status));
+       SILC_LOG_DEBUG(("Error (type %d) during Key Exchange protocol",
+                       status));
+
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       silc_protocol_execute(protocol, client->schedule, 0, 0);
+       return;
+      }
+    }
+    break;
+  case 4:
+    {
+      /* 
+       * Finish protocol
+       */
+      if (ctx->responder == TRUE) {
+       /* This creates the key exchange material and sends our
+          public parts to the initiator inside Key Exchange 2 Payload. */
+       status = 
+         silc_ske_responder_finish(ctx->ske, 
+                                   client->public_key, client->private_key,
+                                   SILC_SKE_PK_TYPE_SILC);
+
+       /* End the protocol on the next round */
+       protocol->state = SILC_PROTOCOL_STATE_END;
+      } else {
+       /* Finish the protocol. This verifies the Key Exchange 2 payload
+          sent by responder. The silc_client_protocol_ke_continue will
+          be called after the public key has been verified. */
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
+      }
+
+      /* Return now if the procedure is pending */
+      if (status == SILC_SKE_STATUS_PENDING)
+       return;
+
+      if (status != SILC_SKE_STATUS_OK) {
+        if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
+          client->internal->ops->say(
+                            client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                            "Received unsupported server %s public key",
+                            ctx->sock->hostname);
+        } else {
+          client->internal->ops->say(
+                          client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                          "Error during key exchange protocol with server %s",
+                          ctx->sock->hostname);
+        }
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       silc_protocol_execute(protocol, client->schedule, 0, 0);
+       return;
+      }
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    {
+      /* 
+       * End protocol
+       */
+      SilcSKEKeyMaterial *keymat;
+      int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
+      int hash_len = ctx->ske->prop->hash->hash->hash_len;
+
+      /* Process the key material */
+      keymat = silc_calloc(1, sizeof(*keymat));
+      status = silc_ske_process_key_material(ctx->ske, 16, key_len, hash_len,
+                                            keymat);
+      if (status != SILC_SKE_STATUS_OK) {
+       protocol->state = SILC_PROTOCOL_STATE_ERROR;
+       silc_protocol_execute(protocol, client->schedule, 0, 300000);
+       silc_ske_free_key_material(keymat);
+       return;
+      }
+      ctx->keymat = keymat;
+
+      /* Send Ok to the other end if we are responder. If we are initiator
+        we have sent this already. */
+      if (ctx->responder == TRUE)
+       silc_ske_end(ctx->ske);
+
+      /* Unregister the timeout task since the protocol has ended. 
+        This was the timeout task to be executed if the protocol is
+        not completed fast enough. */
+      if (ctx->timeout_task)
+       silc_schedule_task_del(client->schedule, ctx->timeout_task);
+
+      /* Protocol has ended, call the final callback */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, client->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    /*
+     * Error during protocol
+     */
+    
+    /* Send abort notification */
+    silc_ske_abort(ctx->ske, ctx->ske->status);
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, client->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * Received failure from remote.
+     */
+
+    /* Unregister the timeout task since the protocol has ended. 
+       This was the timeout task to be executed if the protocol is
+       not completed fast enough. */
+    if (ctx->timeout_task)
+      silc_schedule_task_del(client->schedule, ctx->timeout_task);
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, client->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+}
+
+/*
+ * Connection Authentication protocol functions
+ */
+
+static int
+silc_client_get_public_key_auth(SilcClient client,
+                               SilcClientConnection conn,
+                               unsigned char *auth_data,
+                               uint32 *auth_data_len,
+                               SilcSKE ske)
+{
+  int len;
+  SilcPKCS pkcs;
+  SilcBuffer auth;
+
+  /* Use our default key */
+  pkcs = client->pkcs;
+
+  /* Make the authentication data. Protocol says it is HASH plus
+     KE Start Payload. */
+  len = ske->hash_len + ske->start_payload_copy->len;
+  auth = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(auth, len);
+  silc_buffer_format(auth,
+                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
+                                         ske->start_payload_copy->len),
+                    SILC_STR_END);
+
+  if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data, 
+                              auth->len, auth_data, auth_data_len)) {
+    silc_buffer_free(auth);
+    return TRUE;
+  }
+
+  silc_buffer_free(auth);
+  return FALSE;
+}
+
+/* Continues the connection authentication protocol. This funtion may
+   be called directly or used as SilcAskPassphrase callback. */
+
+static void 
+silc_client_conn_auth_continue(unsigned char *auth_data,
+                              uint32 auth_data_len, void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientConnAuthInternalContext *ctx = 
+    (SilcClientConnAuthInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcBuffer packet;
+  int payload_len = 0;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  payload_len = 4 + auth_data_len;
+  packet = silc_buffer_alloc(payload_len);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(payload_len),
+                    SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
+                    SILC_STR_UI_XNSTRING(auth_data, auth_data_len),
+                    SILC_STR_END);
+
+  /* Send the packet to server */
+  silc_client_packet_send(client, ctx->sock,
+                         SILC_PACKET_CONNECTION_AUTH,
+                         NULL, 0, NULL, NULL,
+                         packet->data, packet->len, TRUE);
+  silc_buffer_free(packet);
+      
+  /* Next state is end of protocol */
+  protocol->state = SILC_PROTOCOL_STATE_END;
+}
+                                                   
+SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientConnAuthInternalContext *ctx = 
+    (SilcClientConnAuthInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = ctx->sock->user_data;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    {
+      /* 
+       * Start protocol. We send authentication data to the server
+       * to be authenticated.
+       */
+      unsigned char *auth_data = NULL;
+      uint32 auth_data_len = 0;
+      unsigned char sign[1024];
+
+      switch(ctx->auth_meth) {
+      case SILC_AUTH_NONE:
+       /* No authentication required */
+       break;
+
+      case SILC_AUTH_PASSWORD:
+       /* Password authentication */
+       if (ctx->auth_data && ctx->auth_data_len) {
+         auth_data = ctx->auth_data;
+         auth_data_len = ctx->auth_data_len;
+         break;
+       }
+
+       client->internal->ops->say(
+                       client, conn, SILC_CLIENT_MESSAGE_INFO,
+                       "Password authentication required by server %s",
+                       ctx->sock->hostname);
+       client->internal->ops->ask_passphrase(client, conn,
+                                             silc_client_conn_auth_continue,
+                                             protocol);
+       return;
+       break;
+
+      case SILC_AUTH_PUBLIC_KEY:
+       if (!ctx->auth_data) {
+         /* Public key authentication */
+         silc_client_get_public_key_auth(client, conn, sign, &auth_data_len, 
+                                         ctx->ske);
+         auth_data = sign;
+       } else {
+         auth_data = ctx->auth_data;
+         auth_data_len = ctx->auth_data_len;
+       }
+       
+       break;
+      }
+
+      silc_client_conn_auth_continue(auth_data,
+                                    auth_data_len, protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    {
+      /* 
+       * End protocol. Nothing special to be done here.
+       */
+
+      /* Protocol has ended, call the final callback */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, client->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    {
+      /* 
+       * Error. Send notify to remote.
+       */
+      unsigned char error[4];
+
+      SILC_PUT32_MSB(SILC_AUTH_FAILED, error);
+
+      /* Error in protocol. Send FAILURE packet. Although I don't think
+        this could ever happen on client side. */
+      silc_client_packet_send(client, ctx->sock, SILC_PACKET_FAILURE,
+                             NULL, 0, NULL, NULL, error, 4, TRUE);
+
+      /* On error the final callback is always called. */
+      if (protocol->final_callback)
+       silc_protocol_execute_final(protocol, client->schedule);
+      else
+       silc_protocol_free(protocol);
+    }
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * Received failure from remote.
+     */
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, client->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+}
+
+/*
+ * Re-key protocol routines
+ */
+
+/* Actually takes the new keys into use. */
+
+static void 
+silc_client_protocol_rekey_validate(SilcClient client,
+                                   SilcClientRekeyInternalContext *ctx,
+                                   SilcSocketConnection sock,
+                                   SilcSKEKeyMaterial *keymat,
+                                   bool send)
+{
+  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+
+  if (ctx->responder == TRUE) {
+    if (send) {
+      silc_cipher_set_key(conn->send_key, keymat->receive_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(conn->send_key, keymat->receive_iv);
+      silc_hmac_set_key(conn->hmac_send, keymat->receive_hmac_key, 
+                       keymat->hmac_key_len);
+    } else {
+      silc_cipher_set_key(conn->receive_key, keymat->send_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(conn->receive_key, keymat->send_iv);
+      silc_hmac_set_key(conn->hmac_receive, keymat->send_hmac_key, 
+                       keymat->hmac_key_len);
+    }
+  } else {
+    if (send) {
+      silc_cipher_set_key(conn->send_key, keymat->send_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(conn->send_key, keymat->send_iv);
+      silc_hmac_set_key(conn->hmac_send, keymat->send_hmac_key, 
+                       keymat->hmac_key_len);
+    } else {
+      silc_cipher_set_key(conn->receive_key, keymat->receive_enc_key, 
+                         keymat->enc_key_len);
+      silc_cipher_set_iv(conn->receive_key, keymat->receive_iv);
+      silc_hmac_set_key(conn->hmac_receive, keymat->receive_hmac_key, 
+                       keymat->hmac_key_len);
+    }
+  }
+
+  /* Save the current sending encryption key */
+  if (!send) {
+    memset(conn->rekey->send_enc_key, 0, conn->rekey->enc_key_len);
+    silc_free(conn->rekey->send_enc_key);
+    conn->rekey->send_enc_key = 
+      silc_calloc(keymat->enc_key_len / 8,
+                 sizeof(*conn->rekey->send_enc_key));
+    memcpy(conn->rekey->send_enc_key, keymat->send_enc_key, 
+          keymat->enc_key_len / 8);
+    conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+  }
+}
+
+/* This function actually re-generates (when not using PFS) the keys and
+   takes them into use. */
+
+static void 
+silc_client_protocol_rekey_generate(SilcClient client,
+                                   SilcClientRekeyInternalContext *ctx,
+                                   bool send)
+{
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(conn->send_key);
+  uint32 hash_len = conn->hash->hash->hash_len;
+
+  SILC_LOG_DEBUG(("Generating new %s session keys (no PFS)",
+                 send ? "sending" : "receiving"));
+
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(conn->rekey->send_enc_key,
+                                    conn->rekey->enc_key_len,
+                                    16, key_len, hash_len, 
+                                    conn->hash, keymat);
+
+  /* Set the keys into use */
+  silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat, send);
+
+  silc_ske_free_key_material(keymat);
+}
+
+/* This function actually re-generates (with PFS) the keys and
+   takes them into use. */
+
+static void 
+silc_client_protocol_rekey_generate_pfs(SilcClient client,
+                                       SilcClientRekeyInternalContext *ctx,
+                                       bool send)
+{
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcSKEKeyMaterial *keymat;
+  uint32 key_len = silc_cipher_get_key_len(conn->send_key);
+  uint32 hash_len = conn->hash->hash->hash_len;
+  unsigned char *tmpbuf;
+  uint32 klen;
+
+  SILC_LOG_DEBUG(("Generating new %s session keys (with PFS)",
+                 send ? "sending" : "receiving"));
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ctx->ske->KEY, 0, &klen);
+
+  /* Generate the new key */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, 
+                                    conn->hash, keymat);
+
+  /* Set the keys into use */
+  silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat, send);
+
+  memset(tmpbuf, 0, klen);
+  silc_free(tmpbuf);
+  silc_ske_free_key_material(keymat);
+}
+
+/* Packet sending callback. This function is provided as packet sending
+   routine to the Key Exchange functions. */
+
+static void 
+silc_client_protocol_rekey_send_packet(SilcSKE ske,
+                                      SilcBuffer packet,
+                                      SilcPacketType type,
+                                      void *context)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx = 
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+
+  /* Send the packet immediately */
+  silc_client_packet_send(client, ctx->sock, type, NULL, 0, NULL, NULL,
+                         packet->data, packet->len, FALSE);
+}
+
+/* Performs re-key as defined in the SILC protocol specification. */
+
+SILC_TASK_CALLBACK(silc_client_protocol_rekey)
+{
+  SilcProtocol protocol = (SilcProtocol)context;
+  SilcClientRekeyInternalContext *ctx = 
+    (SilcClientRekeyInternalContext *)protocol->context;
+  SilcClient client = (SilcClient)ctx->client;
+  SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
+  SilcSKEStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (protocol->state == SILC_PROTOCOL_STATE_UNKNOWN)
+    protocol->state = SILC_PROTOCOL_STATE_START;
+
+  SILC_LOG_DEBUG(("State=%d", protocol->state));
+
+  switch(protocol->state) {
+  case SILC_PROTOCOL_STATE_START:
+    {
+      /* 
+       * Start protocol.
+       */
+
+      if (ctx->responder == TRUE) {
+       /*
+        * We are receiving party
+        */
+
+       if (ctx->pfs == TRUE) {
+         /* 
+          * Use Perfect Forward Secrecy, ie. negotiate the key material
+          * using the SKE protocol.
+          */
+
+         if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+           /* Error in protocol */
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, client->schedule, 0, 300000);
+         }
+
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = client->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_group_get_by_number(conn->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         silc_ske_set_callbacks(ctx->ske, 
+                                silc_client_protocol_rekey_send_packet,
+                                NULL,  NULL, NULL, silc_ske_check_version,
+                                context);
+      
+         status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, client->schedule, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+       } else {
+         /*
+          * Do normal and simple re-key.
+          */
+
+         /* Send the REKEY_DONE to indicate we will take new keys into use */
+         silc_client_packet_send(client, ctx->sock, 
+                                 SILC_PACKET_REKEY_DONE, 
+                                 NULL, 0, NULL, NULL, NULL, 0, FALSE);
+
+         /* After we send REKEY_DONE we must set the sending encryption
+            key to the new key since all packets after this packet must
+            encrypted with the new key. */
+         silc_client_protocol_rekey_generate(client, ctx, TRUE);
+
+         /* The protocol ends in next stage. */
+         protocol->state = SILC_PROTOCOL_STATE_END;
+       }
+      
+      } else {
+       /*
+        * We are the initiator of this protocol
+        */
+
+       /* Start the re-key by sending the REKEY packet */
+       silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY, 
+                               NULL, 0, NULL, NULL, NULL, 0, FALSE);
+
+       if (ctx->pfs == TRUE) {
+         /* 
+          * Use Perfect Forward Secrecy, ie. negotiate the key material
+          * using the SKE protocol.
+          */
+         ctx->ske = silc_ske_alloc();
+         ctx->ske->rng = client->rng;
+         ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
+         silc_ske_group_get_by_number(conn->rekey->ske_group,
+                                      &ctx->ske->prop->group);
+
+         silc_ske_set_callbacks(ctx->ske, 
+                                silc_client_protocol_rekey_send_packet,
+                                NULL,  NULL, NULL, silc_ske_check_version,
+                                context);
+      
+         status =  silc_ske_initiator_phase_2(ctx->ske, NULL, NULL);
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, client->schedule, 0, 300000);
+           return;
+         }
+
+         /* Advance the protocol state */
+         protocol->state++;
+       } else {
+         /*
+          * Do normal and simple re-key.
+          */
+
+         /* Send the REKEY_DONE to indicate we will take new keys into use 
+            now. */ 
+         silc_client_packet_send(client, ctx->sock, 
+                                 SILC_PACKET_REKEY_DONE, 
+                                 NULL, 0, NULL, NULL, NULL, 0, FALSE);
+
+         /* After we send REKEY_DONE we must set the sending encryption
+            key to the new key since all packets after this packet must
+            encrypted with the new key. */
+         silc_client_protocol_rekey_generate(client, ctx, TRUE);
+
+         /* The protocol ends in next stage. */
+         protocol->state = SILC_PROTOCOL_STATE_END;
+       }
+      }
+    }
+    break;
+
+  case 2:
+    /*
+     * Second state, used only when oding re-key with PFS.
+     */
+    if (ctx->responder == TRUE) {
+      if (ctx->pfs == TRUE) {
+       /*
+        * Send our KE packe to the initiator now that we've processed
+        * the initiator's KE packet.
+        */
+       status = silc_ske_responder_finish(ctx->ske, NULL, NULL, 
+                                          SILC_SKE_PK_TYPE_SILC);
+
+         if (status != SILC_SKE_STATUS_OK) {
+           SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                             status));
+           
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, client->schedule, 0, 300000);
+           return;
+         }
+      }
+
+    } else {
+      if (ctx->pfs == TRUE) {
+       /*
+        * The packet type must be KE packet
+        */
+       if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+         /* Error in protocol */
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 300000);
+       }
+       
+       status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
+       if (status != SILC_SKE_STATUS_OK) {
+         SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
+                           status));
+         
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 300000);
+         return;
+       }
+      }
+    }
+
+    /* Send the REKEY_DONE to indicate we will take new keys into use 
+       now. */ 
+    silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY_DONE, 
+                           NULL, 0, NULL, NULL, NULL, 0, FALSE);
+    
+    /* After we send REKEY_DONE we must set the sending encryption
+       key to the new key since all packets after this packet must
+       encrypted with the new key. */
+    silc_client_protocol_rekey_generate_pfs(client, ctx, TRUE);
+
+    /* The protocol ends in next stage. */
+    protocol->state = SILC_PROTOCOL_STATE_END;
+    break;
+
+  case SILC_PROTOCOL_STATE_END:
+    /* 
+     * End protocol
+     */
+
+    if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
+      /* Error in protocol */
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute(protocol, client->schedule, 0, 0);
+    }
+
+    /* We received the REKEY_DONE packet and all packets after this is
+       encrypted with the new key so set the decryption key to the new key */
+    silc_client_protocol_rekey_generate(client, ctx, FALSE);
+
+    /* Protocol has ended, call the final callback */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, client->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_ERROR:
+    /*
+     * Error occured
+     */
+
+    if (ctx->pfs == TRUE) {
+      /* Send abort notification */
+      silc_ske_abort(ctx->ske, ctx->ske->status);
+    }
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, client->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_FAILURE:
+    /*
+     * We have received failure from remote
+     */
+
+    /* On error the final callback is always called. */
+    if (protocol->final_callback)
+      silc_protocol_execute_final(protocol, client->schedule);
+    else
+      silc_protocol_free(protocol);
+    break;
+
+  case SILC_PROTOCOL_STATE_UNKNOWN:
+    break;
+  }
+
+}
+
+/* Registers protocols used in client */
+
+void silc_client_protocols_register(void)
+{
+  silc_protocol_register(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
+                        silc_client_protocol_connection_auth);
+  silc_protocol_register(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
+                        silc_client_protocol_key_exchange);
+  silc_protocol_register(SILC_PROTOCOL_CLIENT_REKEY,
+                        silc_client_protocol_rekey);
+}
+
+/* Unregisters protocols */
+
+void silc_client_protocols_unregister(void)
+{
+  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
+                          silc_client_protocol_connection_auth);
+  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
+                          silc_client_protocol_key_exchange);
+  silc_protocol_unregister(SILC_PROTOCOL_CLIENT_REKEY,
+                          silc_client_protocol_rekey);
+}
diff --git a/lib/silcclient/protocol.h b/lib/silcclient/protocol.h
new file mode 100644 (file)
index 0000000..c229ff0
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+
+  protocol.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+/* SILC client protocol types */
+#define SILC_PROTOCOL_CLIENT_NONE               0
+#define SILC_PROTOCOL_CLIENT_CONNECTION_AUTH    1
+#define SILC_PROTOCOL_CLIENT_KEY_EXCHANGE       2
+#define SILC_PROTOCOL_CLIENT_REKEY              3
+/* #define SILC_PROTOCOL_CLIENT_MAX             255 */
+
+/* Internal context for key exchange protocol */
+typedef struct {
+  void *client;
+  SilcSocketConnection sock;
+  SilcRng rng;
+  int responder;
+
+  void *dest_id;                   /* Destination ID from packet */
+  SilcIdType dest_id_type;         /* Destination ID type */
+
+  SilcTask timeout_task;
+  SilcPacketContext *packet;
+
+  SilcSKESendPacketCb send_packet;  /* SKE's packet sending callback */
+  SilcSKEVerifyCb verify;          /* SKE's key verify callback */
+  SilcSKE ske;                     /* The SKE object */
+  SilcSKEKeyMaterial *keymat;      /* The negotiated key material */
+  void *context;                   /* Internal context */
+} SilcClientKEInternalContext;
+
+/* Internal context for connection authentication protocol */
+typedef struct {
+  void *client;
+  SilcSocketConnection sock;
+
+  /* SKE object from Key Exchange protocol. */
+  SilcSKE ske;
+
+  /* Auth method that must be used. This is resolved before this
+     connection authentication protocol is started. */
+  SilcProtocolAuthMeth auth_meth;
+
+  /* Destinations ID from KE protocol context */
+  void *dest_id;
+  SilcIdType dest_id_type;
+
+  /* Authentication data if we alreay know it. This is filled before
+     starting the protocol if we know the authentication data. Otherwise
+     these are and remain NULL. */
+  unsigned char *auth_data;
+  uint32 auth_data_len;
+
+  SilcTask timeout_task;
+} SilcClientConnAuthInternalContext;
+
+/* Internal context for the rekey protocol */
+typedef struct {
+  void *client;
+  void *context;
+  SilcSocketConnection sock;
+  bool responder;                  /* TRUE if we are receiving party */
+  bool pfs;                        /* TRUE if PFS is to be used */
+  SilcSKE ske;                     /* Defined if PFS is used */
+  SilcPacketContext *packet;
+} SilcClientRekeyInternalContext;
+
+/* Prototypes */
+void silc_client_protocols_register(void);
+void silc_client_protocols_unregister(void);
+void silc_client_protocol_ke_send_packet(SilcSKE ske,
+                                        SilcBuffer packet,
+                                        SilcPacketType type,
+                                        void *context);
+void silc_client_protocol_ke_verify_key(SilcSKE ske,
+                                       unsigned char *pk_data,
+                                       uint32 pk_len,
+                                       SilcSKEPKType pk_type,
+                                       void *context,
+                                       SilcSKEVerifyCbCompletion completion,
+                                       void *completion_context);
+void silc_client_protocol_ke_set_keys(SilcSKE ske,
+                                     SilcSocketConnection sock,
+                                     SilcSKEKeyMaterial *keymat,
+                                     SilcCipher cipher,
+                                     SilcPKCS pkcs,
+                                     SilcHash hash,
+                                     SilcHmac hmac,
+                                     SilcSKEDiffieHellmanGroup group,
+                                     bool is_responder);
+
+#endif
diff --git a/lib/silcclient/silcapi.h b/lib/silcclient/silcapi.h
new file mode 100644 (file)
index 0000000..293b8d6
--- /dev/null
@@ -0,0 +1,2021 @@
+/*
+
+  silcapi.h
+  
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  
+  Copyright (C) 2000 - 2001 Pekka Riikonen
+  
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcclient/SilcClientAPI
+ *
+ * DESCRIPTION
+ *
+ * This interface defines the SILC Client Library API for the application.
+ * The client operations are defined first.  These are callback functions that
+ * the application MUST implement since the library may call the functions
+ * at any time.  At the end of file is the API for the application that
+ * it can use from the library.  This is the only file that the application
+ * may include from the SIlC Client Library.
+ *
+ * o SILC Client Operations
+ *
+ *   These functions must be implemented by the application calling the SILC
+ *   client library. The client library can call these functions at any time.
+ *
+ *   To use this structure: define a static SilcClientOperations variable,
+ *   fill it and pass its pointer to silc_client_alloc function.
+ *
+ * o SILC Client Library API
+ *
+ *   This is the API that is published by the SILC Client Library for the
+ *   applications.  These functions are implemented in the SILC Client Library.
+ *   Application may freely call these functions from the library.
+ *
+ * Please, refer to the README file in this directory for the directions
+ * of how to use the SILC Client Library.
+ *
+ ***/
+
+#ifndef SILCAPI_H
+#define SILCAPI_H
+
+#include "clientlibincludes.h"
+
+/* General definitions */
+
+/****d* silcclient/SilcClientAPI/SilcKeyAgreementStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcKeyAgreementStatus;
+ *
+ * DESCRIPTION
+ *
+ *    Key agreement status types indicating the status of the key
+ *    agreement protocol.  These types are returned to the application 
+ *    in the SilcKeyAgreementCallback callback function.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_KEY_AGREEMENT_OK,              /* Everything is Ok */
+  SILC_KEY_AGREEMENT_ERROR,           /* Unknown error occurred */
+  SILC_KEY_AGREEMENT_FAILURE,         /* The protocol failed */
+  SILC_KEY_AGREEMENT_TIMEOUT,         /* The protocol timeout */
+  SILC_KEY_AGREEMENT_ABORTED,         /* The protocol aborted */
+} SilcKeyAgreementStatus;
+/***/
+
+/****f* silcclient/SilcClientAPI/SilcKeyAgreementCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcKeyAgreementCallback)(SilcClient client,
+ *                                             SilcClientConnection conn,
+ *                                             SilcClientEntry client_entry,
+ *                                             SilcKeyAgreementStatus status,
+ *                                             SilcSKEKeyMaterial *key,
+ *                                             void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Key agreement callback that is called after the key agreement protocol
+ *    has been performed. This is called also if error occurred during the
+ *    key agreement protocol. The `key' is the allocated key material and
+ *    the caller is responsible of freeing it. The `key' is NULL if error
+ *    has occurred. The application can freely use the `key' to whatever
+ *    purpose it needs. See lib/silcske/silcske.h for the definition of
+ *    the SilcSKEKeyMaterial structure.
+ *
+ ***/
+typedef void (*SilcKeyAgreementCallback)(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        SilcKeyAgreementStatus status,
+                                        SilcSKEKeyMaterial *key,
+                                        void *context);
+
+/****s* silcclient/SilcClientAPI/SilcPrivateMessageKeys
+ *
+ * NAME
+ *
+ *    typedef struct { ... } SilcPrivateMessageKeys;
+ *
+ * DESCRIPTION
+ *
+ *    Structure to hold the list of private message keys. The array of this
+ *    structure is returned by the silc_client_list_private_message_keys
+ *    function.
+ *
+ * SOURCE
+ */
+typedef struct {
+  SilcClientEntry client_entry;       /* The remote client entry */
+  char *cipher;                              /* The cipher name */
+  unsigned char *key;                /* The original key, If the appliation
+                                        provided it. This is NULL if the
+                                        library generated the key or if
+                                        the SKE key material was used. */
+  uint32 key_len;                    /* The key length */
+} *SilcPrivateMessageKeys;
+/***/
+
+
+/****f* silcclient/SilcClientAPI/SilcAskPassphrase
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcAskPassphrase)(unsigned char *passphrase,
+ *                                     uint32 passphrase_len,
+ *                                     void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Ask passphrase callback. This is called by the application when the
+ *    library calls `ask_passphrase' client operation.  The callback delivers
+ *    the passphrase to the library.
+ *
+ ***/
+typedef void (*SilcAskPassphrase)(unsigned char *passphrase,
+                                 uint32 passphrase_len,
+                                 void *context);
+
+/****f* silcclient/SilcClientAPI/SilcVerifyPublicKey
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcVerifyPublicKey)(bool success, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Public key (or certificate) verification callback. This is called
+ *    by the application to indicate that the public key verification was
+ *    either success or failure.
+ *
+ ***/
+typedef void (*SilcVerifyPublicKey)(bool success, void *context);
+
+/****f* silcclient/SilcClientAPI/SilcGetAuthMeth
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetAuthMeth)(bool success, 
+ *                                    SilcProtocolAuthMeth auth_meth,
+ *                                    const unsigned char *auth_data,
+ *                                    uint32 auth_data_len, void *context);
+ * 
+ * DESCRIPTION
+ *
+ *    Authentication method resolving callback. This is called by the
+ *    application to return the resolved authentication method. The client
+ *    library has called the get_auth_method client operation and given
+ *    this function pointer as argument. The `success' will indicate whether
+ *    the authentication method could be resolved. The `auth_meth' is the
+ *    resolved authentication method. The `auth_data' and the `auth_data_len'
+ *    are the resolved authentication data. The `context' is the libary's
+ *    context sent to the get_auth_method client operation.
+ *
+ ***/
+typedef void (*SilcGetAuthMeth)(bool success, 
+                               SilcProtocolAuthMeth auth_meth,
+                               const unsigned char *auth_data,
+                               uint32 auth_data_len, void *context);
+
+/****d* silcclient/SilcClientAPI/SilcClientMessageType
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcClientMessageType;
+ *
+ * DESCRIPTION
+ *
+ *    Different message types for `say' client operation.  The application
+ *    may filter the message sent by the library according this type.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_CLIENT_MESSAGE_INFO,           /* Informational */
+  SILC_CLIENT_MESSAGE_WARNING,        /* Warning */
+  SILC_CLIENT_MESSAGE_ERROR,          /* Error */
+  SILC_CLIENT_MESSAGE_AUDIT,          /* Auditable */
+} SilcClientMessageType;
+/***/
+
+/****s* silcclient/SilcClientAPI/SilcClientOperations
+ *
+ * NAME
+ *
+ *    typedef struct { ... } SilcClientOperations;
+ *
+ * DESCRIPTION
+ *
+ *    SILC Client Operations. These must be implemented by the application.
+ *    The Client library may call any of these routines at any time.  The
+ *    routines are used to deliver certain information to the application
+ *    or from the application to the client library.
+ *
+ * SOURCE
+ */
+typedef struct {
+  /* Message sent to the application by library. `conn' associates the
+     message to a specific connection.  `conn', however, may be NULL. 
+     The `type' indicates the type of the message sent by the library.
+     The applicationi can for example filter the message according the
+     type. */
+  void (*say)(SilcClient client, SilcClientConnection conn, 
+             SilcClientMessageType type, char *msg, ...);
+
+  /* Message for a channel. The `sender' is the sender of the message 
+     The `channel' is the channel. The `msg' is the message.  Note that
+     `msg' maybe NULL. */
+  void (*channel_message)(SilcClient client, SilcClientConnection conn, 
+                         SilcClientEntry sender, SilcChannelEntry channel, 
+                         SilcMessageFlags flags, char *msg);
+
+  /* Private message to the client. The `sender' is the sender of the
+     message. */
+  void (*private_message)(SilcClient client, SilcClientConnection conn,
+                         SilcClientEntry sender, SilcMessageFlags flags,
+                         char *msg);
+
+  /* Notify message to the client. The notify arguments are sent in the
+     same order as servers sends them. The arguments are same as received
+     from the server except for ID's.  If ID is received application receives
+     the corresponding entry to the ID. For example, if Client ID is received
+     application receives SilcClientEntry.  Also, if the notify type is
+     for channel the channel entry is sent to application (even if server
+     does not send it because client library gets the channel entry from
+     the Channel ID in the packet's header). */
+  void (*notify)(SilcClient client, SilcClientConnection conn, 
+                SilcNotifyType type, ...);
+
+  /* Command handler. This function is called always in the command function.
+     If error occurs it will be called as well. `conn' is the associated
+     client connection. `cmd_context' is the command context that was
+     originally sent to the command. `success' is FALSE if error occurred
+     during command. `command' is the command being processed. It must be
+     noted that this is not reply from server. This is merely called just
+     after application has called the command. Just to tell application
+     that the command really was processed. */
+  void (*command)(SilcClient client, SilcClientConnection conn, 
+                 SilcClientCommandContext cmd_context, int success,
+                 SilcCommand command);
+
+  /* Command reply handler. This function is called always in the command reply
+     function. If error occurs it will be called as well. Normal scenario
+     is that it will be called after the received command data has been parsed
+     and processed. The function is used to pass the received command data to
+     the application. 
+     
+     `conn' is the associated client connection. `cmd_payload' is the command
+     payload data received from server and it can be ignored. It is provided
+     if the application would like to re-parse the received command data,
+     however, it must be noted that the data is parsed already by the library
+     thus the payload can be ignored. `success' is FALSE if error occurred.
+     In this case arguments are not sent to the application. The `status' is
+     the command reply status server returned. The `command' is the command
+     reply being processed. The function has variable argument list and each
+     command defines the number and type of arguments it passes to the
+     application (on error they are not sent). */
+  void (*command_reply)(SilcClient client, SilcClientConnection conn,
+                       SilcCommandPayload cmd_payload, int success,
+                       SilcCommand command, SilcCommandStatus status, ...);
+
+  /* Called to indicate that connection was either successfully established
+     or connecting failed.  This is also the first time application receives
+     the SilcClientConnection objecet which it should save somewhere.
+     If the `success' is FALSE the application must always call the function
+     silc_client_close_connection. */
+  void (*connect)(SilcClient client, SilcClientConnection conn, int success);
+
+  /* Called to indicate that connection was disconnected to the server. */
+  void (*disconnect)(SilcClient client, SilcClientConnection conn);
+
+  /* Find authentication method and authentication data by hostname and
+     port. The hostname may be IP address as well. When the authentication
+     method has been resolved the `completion' callback with the found
+     authentication method and authentication data is called. The `conn'
+     may be NULL. */
+  void (*get_auth_method)(SilcClient client, SilcClientConnection conn,
+                         char *hostname, uint16 port,
+                         SilcGetAuthMeth completion, void *context);
+
+  /* Verifies received public key. The `conn_type' indicates which entity
+     (server, client etc.) has sent the public key. If user decides to trust
+     the key may be saved as trusted public key for later use. The 
+     `completion' must be called after the public key has been verified. */
+  void (*verify_public_key)(SilcClient client, SilcClientConnection conn,
+                           SilcSocketType conn_type, unsigned char *pk, 
+                           uint32 pk_len, SilcSKEPKType pk_type,
+                           SilcVerifyPublicKey completion, void *context);
+
+  /* Ask (interact, that is) a passphrase from user. The passphrase is
+     returned to the library by calling the `completion' callback with
+     the `context'. */
+  void (*ask_passphrase)(SilcClient client, SilcClientConnection conn,
+                        SilcAskPassphrase completion, void *context);
+
+  /* Notifies application that failure packet was received.  This is called
+     if there is some protocol active in the client.  The `protocol' is the
+     protocol context.  The `failure' is opaque pointer to the failure
+     indication.  Note, that the `failure' is protocol dependant and
+     application must explicitly cast it to correct type.  Usually `failure'
+     is 32 bit failure type (see protocol specs for all protocol failure
+     types). */
+  void (*failure)(SilcClient client, SilcClientConnection conn, 
+                 SilcProtocol protocol, void *failure);
+
+  /* Asks whether the user would like to perform the key agreement protocol.
+     This is called after we have received an key agreement packet or an
+     reply to our key agreement packet. This returns TRUE if the user wants
+     the library to perform the key agreement protocol and FALSE if it is not
+     desired (application may start it later by calling the function
+     silc_client_perform_key_agreement). If TRUE is returned also the
+     `completion' and `context' arguments must be set by the application. */
+  int (*key_agreement)(SilcClient client, SilcClientConnection conn,
+                      SilcClientEntry client_entry, const char *hostname,
+                      uint16 port, SilcKeyAgreementCallback *completion,
+                      void **context);
+
+  /* Notifies application that file transfer protocol session is being
+     requested by the remote client indicated by the `client_entry' from
+     the `hostname' and `port'. The `session_id' is the file transfer
+     session and it can be used to either accept or reject the file
+     transfer request, by calling the silc_client_file_receive or
+     silc_client_file_close, respectively. */
+  void (*ftp)(SilcClient client, SilcClientConnection conn,
+             SilcClientEntry client_entry, uint32 session_id,
+             const char *hostname, uint16 port);
+} SilcClientOperations;
+/***/
+
+/****f* silcclient/SilcClientAPI/SilcNicknameFormatParse
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcNicknameFormatParse)(const char *nickname,
+ *                                            char **ret_nickname);
+ *
+ * DESCRIPTION
+ *
+ *    A callback function provided by the application for the library in
+ *    SilcClientParams structure. This function parses the formatted
+ *    nickname string `nickname' and returns the true nickname to the
+ *    `ret_nickname' pointer. The library can call this function at
+ *    any time.
+ *
+ ***/
+typedef void (*SilcNicknameFormatParse)(const char *nickname,
+                                       char **ret_nickname);
+
+/****s* silcclient/SilcClientAPI/SilcClientParams
+ *
+ * NAME
+ *
+ *    typedef struct { ... } SilcClientParams;
+ *
+ * DESCRIPTION
+ *
+ *    Client parameters. This can be filled with proper values and
+ *    given as argument to the silc_client_alloc function. The structure
+ *    hold various parameters which affects the function of the client.
+ *
+ * SOURCE
+ */
+typedef struct {
+  /* Number of maximum tasks the client library's scheduler can handle.
+     If set to zero, the default value will be used (200). For WIN32
+     systems this should be set to 64 as it is the hard limit dictated
+     by the WIN32. */
+  int task_max;
+
+  /* Rekey timeout in seconds. The client will perform rekey in this
+     time interval. If set to zero, the default value will be used. */
+  unsigned int rekey_secs;
+
+  /* Connection authentication method request timeout. If server does not
+     reply back the current authentication method when we've requested it
+     in this time interval we'll assume the reply will not come at all. 
+     If set to zero, the default value (2 seconds) will be used. */
+  unsigned int connauth_request_secs;
+
+  /* Nickname format string. This can be used to order the client library
+     to save the nicknames in the library in a certain format. Since 
+     nicknames are not unique in SILC it is possible to have multiple same
+     nicknames. Using this format string it is possible to order the library
+     to separate the multiple same nicknames from each other. The format
+     types are defined below and they can appear in any order in the format
+     string. If this is NULL then default format is used which is the
+     default nickname without anything else. The string MUST be NULL
+     terminated.
+     
+     Following format types are available:
+     
+     %n  nickname      - the real nickname returned by the server (mandatory)
+     %h  hostname      - the stripped hostname of the client
+     %H  full hostname - the full hostname of the client
+     %s  server name   - the server name the client is connected
+     %S  full server   - the full server name the client is connected
+     %a  number        - ascending number in case there are several
+                         same nicknames (fe. nick@host and nick@host2)
+
+     Example format strings: "%n@%h%a"   (fe. nick@host, nick@host2)
+                             "%a!%n@%s"  (fe. nick@server, 2!nick@server)
+                            "%n@%H"     (fe. nick@host.domain.com)
+
+     By default this format is employed to the nicknames by the libary
+     only when there appears multiple same nicknames. If the library has
+     only one nickname cached the nickname is saved as is and without the
+     defined format. If you want always to save the nickname in the defined
+     format set the boolean field `nickname_force_format' to value TRUE.
+  */
+  char nickname_format[32];
+
+  /* If this is set to TRUE then the `nickname_format' is employed to all
+     saved nicknames even if there are no multiple same nicknames in the 
+     cache. By default this is FALSE, which means that the `nickname_format'
+     is employed only if the library will receive a nickname that is
+     already saved in the cache. It is recommended to leave this to FALSE
+     value. */
+  bool nickname_force_format;
+
+  /* A callback function provided by the application for the library to
+     parse the nickname from the formatted nickname string. Even though
+     the libary formats the nicknames the application knows generally the
+     format better so this function should be provided for the library
+     if the application sets the `nickname_format' field. The library
+     will call this to get the true nickname from the provided formatted
+     nickname string whenever it needs the true nickname. */
+  SilcNicknameFormatParse nickname_parse;
+
+} SilcClientParams;
+/***/
+
+
+/* Initialization functions (client.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcClient silc_client_alloc(SilcClientOperations *ops, 
+ *                                 SilcClientParams *params,
+ *                                 void *application,
+ *                                 const char *silc_version);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates new client object. This has to be done before client may
+ *    work. After calling this one must call silc_client_init to initialize
+ *    the client. The `application' is application specific user data pointer
+ *    and caller must free it. The `silc_version' is the application version
+ *    that will be used to compare against remote host's (usually a server)
+ *    version string.
+ *
+ ***/
+SilcClient silc_client_alloc(SilcClientOperations *ops, 
+                            SilcClientParams *params,
+                            void *application,
+                            const char *silc_version);
+
+/****f* silcclient/SilcClientAPI/silc_client_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_free(SilcClient client);
+ *
+ * DESCRIPTION
+ *
+ *    Frees client object and its internals.  The execution of the client
+ *    should be stopped with silc_client_stop function before calling
+ *    this function.
+ *
+ ***/
+void silc_client_free(SilcClient client);
+
+/****f* silcclient/SilcClientAPI/silc_client_init
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_init(SilcClient client);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes the client. This makes all the necessary steps to make
+ *    the client ready to be run. One must call silc_client_run to run the
+ *    client. Returns FALSE if error occurred, TRUE otherwise.
+ *
+ ***/
+int silc_client_init(SilcClient client);
+
+/****f* silcclient/SilcClientAPI/silc_client_run
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_run(SilcClient client);
+ *
+ * DESCRIPTION
+ *
+ *    Runs the client. This starts the scheduler from the utility library.
+ *    When this functions returns the execution of the appliation is over.
+ *
+ ***/
+void silc_client_run(SilcClient client);
+
+/****f* silcclient/SilcClientAPI/silc_client_run_one
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_run_one(SilcClient client);
+ *
+ * DESCRIPTION
+ *
+ *    Runs the client and returns immeadiately. This function is used when
+ *    the SILC Client object indicated by the `client' is run under some
+ *    other scheduler, or event loop or main loop.  On GUI applications,
+ *    for example this may be desired to used to run the client under the
+ *    GUI application's main loop.  Typically the GUI application would
+ *    register an idle task that calls this function multiple times in
+ *    a second to quickly process the SILC specific data.
+ *
+ ***/
+void silc_client_run_one(SilcClient client);
+
+/****f* silcclient/SilcClientAPI/silc_client_stop
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_stop(SilcClient client);
+ *
+ * DESCRIPTION
+ *
+ *    Stops the client. This is called to stop the client and thus to stop
+ *    the program.  The client context must be freed with the silc_client_free
+ *    function.
+ *
+ ***/
+void silc_client_stop(SilcClient client);
+
+
+/* Connecting functions (client.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_connect_to_server
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_connect_to_server(SilcClient client, int port,
+ *                                      char *host, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Connects to remote server. This is the main routine used to connect
+ *    to SILC server. Returns -1 on error and the created socket otherwise. 
+ *    The `context' is user context that is saved into the SilcClientConnection
+ *    that is created after the connection is created. Note that application
+ *    may handle the connecting process outside the library. If this is the
+ *    case then this function is not used at all. When the connecting is
+ *    done the `connect' client operation is called.
+ *
+ ***/
+int silc_client_connect_to_server(SilcClient client, int port,
+                                 char *host, void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_add_connection
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientConnection silc_client_add_connection(SilcClient client,
+ *                                                    char *hostname,
+ *                                                   int port,
+ *                                                   void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates and adds new connection to the client. This adds the allocated
+ *    connection to the connection table and returns a pointer to it. A client
+ *    can have multiple connections to multiple servers. Every connection must
+ *    be added to the client using this function. User data `context' may
+ *    be sent as argument. This function is normally used only if the 
+ *    application performed the connecting outside the library. The library
+ *    however may use this internally.
+ *
+ ***/
+SilcClientConnection silc_client_add_connection(SilcClient client,
+                                               char *hostname,
+                                               int port,
+                                               void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_connection
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_del_connection(SilcClient client, 
+ *                                    SilcClientConnection conn);
+ *
+ * DESCRIPTION
+ *
+ *    Removes connection from client. Frees all memory. The library
+ *    call this function automatically for all connection contexts.
+ *    The application however may free the connection contexts it has
+ *    allocated.
+ *
+ ***/
+void silc_client_del_connection(SilcClient client, SilcClientConnection conn);
+
+/****f* silcclient/SilcClientAPI/silc_client_add_socket
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_add_socket(SilcClient client, 
+ *                                SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Adds listener socket to the listener sockets table. This function is
+ *    used to add socket objects that are listeners to the client.  This should
+ *    not be used to add other connection objects.
+ *
+ ***/
+void silc_client_add_socket(SilcClient client, SilcSocketConnection sock);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_socket
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_del_socket(SilcClient client, 
+ *                                SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes listener socket from the listener sockets table.  If the
+ *    application has added a socket with silc_client_add_socket it must
+ *    also free it using this function.
+ *
+ ***/
+void silc_client_del_socket(SilcClient client, SilcSocketConnection sock);
+
+/****f* silcclient/SilcClientAPI/silc_client_start_key_exchange
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_start_key_exchange(SilcClient client,
+ *                                        SilcClientConnection conn,
+ *                                        int fd);
+ *
+ * DESCRIPTION
+ *
+ *    Start SILC Key Exchange (SKE) protocol to negotiate shared secret
+ *    key material between client and server.  This function can be called
+ *    directly if application is performing its own connecting and does not
+ *    use the connecting provided by this library. This function is normally
+ *    used only if the application performed the connecting outside the
+ *    library. The library however may use this internally. Returns FALSE
+ *    if the key exchange could not be started.
+ *
+ ***/
+void silc_client_start_key_exchange(SilcClient client,
+                                   SilcClientConnection conn,
+                                   int fd);
+
+/****f* silcclient/SilcClientAPI/silc_client_close_connection
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_close_connection(SilcClient client,
+ *                                      SilcSocketConnection sock,
+ *                                      SilcClientConnection conn);
+ *
+ * DESCRIPTION
+ *
+ *    Closes connection to remote end. Free's all allocated data except
+ *    for some information such as nickname etc. that are valid at all time. 
+ *    If the `sock' is NULL then the conn->sock will be used.  If `sock' is
+ *    provided it will be checked whether the sock and `conn->sock' are the
+ *    same (they can be different, ie. a socket can use `conn' as its
+ *    connection but `conn->sock' might be actually a different connection
+ *    than the `sock'). 
+ *
+ ***/
+void silc_client_close_connection(SilcClient client,
+                                 SilcSocketConnection sock,
+                                 SilcClientConnection conn);
+
+
+/* Message sending functions (client_channel.c and client_prvmsg.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_send_channel_message
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_send_channel_message(SilcClient client, 
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelEntry channel,
+ *                                          SilcChannelPrivateKey key,
+ *                                          SilcMessageFlags flags,
+ *                                          unsigned char *data, 
+ *                                          uint32 data_len, 
+ *                                          int force_send);
+ *
+ * DESCRIPTION
+ *
+ *    Sends packet to the `channel'. Packet to channel is always encrypted
+ *    differently from "normal" packets. SILC header of the packet is 
+ *    encrypted with the next receiver's key and the rest of the packet is
+ *    encrypted with the channel specific key. Padding and HMAC is computed
+ *    with the next receiver's key. The `data' is the channel message. If
+ *    the `force_send' is TRUE then the packet is sent immediately. 
+ *
+ *    If `key' is provided then that private key is used to encrypt the
+ *    channel message.  If it is not provided, private keys has not been
+ *    set at all, the normal channel key is used automatically.  If private
+ *    keys are set then the first key (the key that was added first as
+ *    private key) is used. 
+ *
+ ***/
+void silc_client_send_channel_message(SilcClient client, 
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     SilcChannelPrivateKey key,
+                                     SilcMessageFlags flags,
+                                     unsigned char *data, 
+                                     uint32 data_len, 
+                                     int force_send);
+
+/****f* silcclient/SilcClientAPI/silc_client_send_private_message
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_send_private_message(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcClientEntry client_entry,
+ *                                          SilcMessageFlags flags,
+ *                                          unsigned char *data, 
+ *                                          uint32 data_len, 
+ *                                          int force_send);
+ *
+ * DESCRIPTION
+ *
+ *    Sends private message to remote client. If private message key has
+ *    not been set with this client then the message will be encrypted using
+ *    normal session keys. Private messages are special packets in SILC
+ *    network hence we need this own function for them. This is similar
+ *    to silc_client_packet_send_to_channel except that we send private
+ *    message. The `data' is the private message. If the `force_send' is
+ *    TRUE the packet is sent immediately. 
+ *
+ ***/
+void silc_client_send_private_message(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry client_entry,
+                                     SilcMessageFlags flags,
+                                     unsigned char *data, 
+                                     uint32 data_len, 
+                                     int force_send);
+
+
+/* Client and Channel entry retrieval (idlist.c) */
+
+/****f* silcclient/SilcClientAPI/SilcGetClientCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetClientCallback)(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcClientEntry *clients,
+ *                                          uint32 clients_count,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function given to the silc_client_get_client function. The
+ *    found entries are allocated into the `clients' array. The array must
+ *    not be freed by the receiver, the library will free it later. If the
+ *    `clients' is NULL, no such clients exist in the SILC Network.
+ *
+ ***/
+typedef void (*SilcGetClientCallback)(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientEntry *clients,
+                                     uint32 clients_count,
+                                     void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_get_clients(SilcClient client,
+ *                                 SilcClientConnection conn,
+ *                                 const char *nickname,
+ *                                 const char *server,
+ *                                 SilcGetClientCallback completion,
+ *                                 void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds client entry or entries by the `nickname' and `server'. The 
+ *    completion callback will be called when the client entries has been
+ *    found.  After the server returns the client information it is cached
+ *    and can be accesses locally at a later time.
+ *
+ * NOTES
+ *
+ *    NOTE: This function is always asynchronous and resolves the client
+ *    information from the server. Thus, if you already know the client
+ *    information then use the silc_client_get_client_by_id function to
+ *    get the client entry since this function may be very slow and should
+ *    be used only to initially get the client entries. 
+ *
+ ***/
+void silc_client_get_clients(SilcClient client,
+                            SilcClientConnection conn,
+                            const char *nickname,
+                            const char *server,
+                            SilcGetClientCallback completion,
+                            void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients_local
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientEntry *silc_client_get_clients_local(SilcClient client,
+ *                                                   SilcClientConnection conn,
+ *                                                   const char *nickname,
+ *                                                   const char *format,
+ *                                                   uint32 *clients_count);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_client_get_clients function but does not resolve anything
+ *    from the server. This checks local cache and returns all matching
+ *    clients from the local cache. If none was found this returns NULL.
+ *    The `nickname' is the real nickname of the client, and the `format'
+ *    is the formatted nickname to find exact match from multiple found
+ *    entries. The format must be same as given in the SilcClientParams
+ *    structure to the client library. If the `format' is NULL all found
+ *    clients by `nickname' are returned. The caller must return the
+ *    returned array.
+ *
+ ***/
+SilcClientEntry *silc_client_get_clients_local(SilcClient client,
+                                              SilcClientConnection conn,
+                                              const char *nickname,
+                                              const char *format,
+                                              uint32 *clients_count);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_clients_by_list
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_get_clients_by_list(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         uint32 list_count,
+ *                                         SilcBuffer client_id_list,
+ *                                         SilcGetClientCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets client entries by the list of client ID's `client_id_list'. This
+ *    always resolves those client ID's it does not know yet from the server
+ *    so this function might take a while. The `client_id_list' is a list
+ *    of ID Payloads added one after other.  JOIN command reply and USERS
+ *    command reply for example returns this sort of list. The `completion'
+ *    will be called after the entries are available. When server returns
+ *    the client information it will be cached and can be accessed locally
+ *    at a later time.
+ *
+ ***/
+void silc_client_get_clients_by_list(SilcClient client,
+                                    SilcClientConnection conn,
+                                    uint32 list_count,
+                                    SilcBuffer client_id_list,
+                                    SilcGetClientCallback completion,
+                                    void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_client_by_id
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientEntry silc_client_get_client_by_id(SilcClient client,
+ *                                                 SilcClientConnection conn,
+ *                                                 SilcClientID *client_id);
+ *
+ * DESCRIPTION
+ *
+ *    Find entry for client by the client's ID. Returns the entry or NULL
+ *    if the entry was not found.  This checks the local cache and does
+ *    not resolve anything from server.
+ *
+ ***/
+SilcClientEntry silc_client_get_client_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientID *client_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_client_by_id_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_get_client_by_id_resolve(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         SilcClientID *client_id,
+ *                                         SilcGetClientCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_client_get_client_by_id but will always resolve the
+ *    information from the server. Use this only if you know that you
+ *    do not have the entry and the only thing you know about the client
+ *    is its ID. When server returns the client information it will be
+ *    cache and can be accessed locally at a later time.
+ *
+ ***/
+void silc_client_get_client_by_id_resolve(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientID *client_id,
+                                         SilcGetClientCallback completion,
+                                         void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_client
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+ *                                SilcClientEntry client_entry)
+ *
+ * DESCRIPTION
+ *
+ *    Removes client from local cache by the client entry indicated by
+ *    the `client_entry'.  Returns TRUE if the deletion were successful.
+ *
+ ***/
+bool silc_client_del_client(SilcClient client, SilcClientConnection conn,
+                           SilcClientEntry client_entry);
+
+/****f* silcclient/SilcClientAPI/SilcGetChannelCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcGetChannelCallback)(SilcClient client,
+ *                                           SilcClientConnection conn,
+ *                                           SilcChannelEntry *channels,
+ *                                           uint32 channels_count,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Callback function given to the silc_client_get_channel_* functions.
+ *    The found entries are allocated into the `channels' array. The array
+ *    must not be freed by the receiver, the library will free it later.
+ *    If the `channel' is NULL, no such channel exist in the SILC Network.
+ *
+ ***/
+typedef void (*SilcGetChannelCallback)(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcChannelEntry *channels,
+                                      uint32 channels_count,
+                                      void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelEntry silc_client_get_channel(SilcClient client,
+ *                                             SilcClientConnection conn,
+ *                                             char *channel);
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for channel by the channel name. Returns the entry or NULL
+ *    if the entry was not found. It is found only if the client is joined
+ *    to the channel. 
+ *
+ ***/
+SilcChannelEntry silc_client_get_channel(SilcClient client,
+                                        SilcClientConnection conn,
+                                        char *channel);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel_id_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_get_channel_by_id_resolve(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelID *channel_id,
+ *                                          SilcGetClientCallback completion,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds channel entry by the channel name. Returns the entry or NULL
+ *    if it was not found.
+ *
+ ***/
+SilcChannelEntry silc_client_get_channel_by_id(SilcClient client,
+                                              SilcClientConnection conn,
+                                              SilcChannelID *channel_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_channel_by_id_resolve
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_get_channel_by_id_resolve(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelID *channel_id,
+ *                                          SilcGetClientCallback completion,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves the channel information (its name mainly) from the server
+ *    by the `channel_id'. Use this only if you know that you do not have
+ *    the entry cached locally.
+ *
+ ***/
+void silc_client_get_channel_by_id_resolve(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcChannelID *channel_id,
+                                          SilcGetChannelCallback completion,
+                                          void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_channel
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_client_del_channel(SilcClient client, 
+ *                                 SilcClientConnection conn,
+ *                                 SilcChannelEntry channel)
+ *
+ * DESCRIPTION
+ *
+ *    Removes channel from local cache by the channel entry indicated by
+ *    the `channel'.  Returns TRUE if the deletion were successful.
+ *
+ ***/
+bool silc_client_del_channel(SilcClient client, SilcClientConnection conn,
+                            SilcChannelEntry channel);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_server
+ *
+ * SYNOPSIS
+ *
+ *    SilcServerEntry silc_client_get_server(SilcClient client,
+ *                                           SilcClientConnection conn,
+ *                                           char *server_name)
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for server by the server name. Returns the entry or NULL
+ *    if the entry was not found.
+ *
+ ***/
+SilcServerEntry silc_client_get_server(SilcClient client,
+                                      SilcClientConnection conn,
+                                      char *server_name);
+
+/****f* silcclient/SilcClientAPI/silc_client_get_server_by_id
+ *
+ * SYNOPSIS
+ *
+ *    SilcServerEntry silc_client_get_server_by_id(SilcClient client,
+ *                                                 SilcClientConnection conn,
+ *                                                 SilcServerID *server_id);
+ *
+ * DESCRIPTION
+ *
+ *    Finds entry for server by the server ID. Returns the entry or NULL
+ *    if the entry was not found.
+ *
+ ***/
+SilcServerEntry silc_client_get_server_by_id(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcServerID *server_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_server
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
+ *                                SilcServerEntry server);
+ *
+ * DESCRIPTION
+ *
+ *    Removes server from local cache by the server entry indicated by
+ *    the `server'.  Returns TRUE if the deletion were successful.
+ *
+ ***/
+bool silc_client_del_server(SilcClient client, SilcClientConnection conn,
+                           SilcServerEntry server);
+
+/****f* silcclient/SilcClientAPI/silc_client_on_channel
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
+ *                                           SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the ChannelUser entry if the `client_entry' is joined on the 
+ *    channel indicated by the `channel'. NULL if client is not joined on
+ *    the channel. 
+ *
+ ***/
+SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
+                                      SilcClientEntry client_entry);
+
+/* Command management (command.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_command_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientCommandContext silc_client_command_alloc(void);
+ *
+ * DESCRIPTION
+ *
+ *    Allocate Command Context. The context is defined in `command.h' file.
+ *    The context is used by the library commands and applications should use
+ *    it as well. However, application may choose to use some own context
+ *    for its local commands. All library commands, however, must use this
+ *    context. 
+ *
+ ***/
+SilcClientCommandContext silc_client_command_alloc(void);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_command_free(SilcClientCommandContext ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Free command context and its internals.  If the contex was duplicated
+ *    with silc_client_command_dup this may not actually free the data, 
+ *    instead it will decrease the reference counter of the context.  The
+ *    context will be freed when the reference counter hits zero.
+ *
+ ***/
+void silc_client_command_free(SilcClientCommandContext ctx);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_dup
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientCommandContext 
+ *    silc_client_command_dup(SilcClientCommandContext ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicate Command Context by adding reference counter. The context won't
+ *    be free'd untill it hits zero. 
+ *
+ ***/
+SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_find
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientCommand silc_client_command_find(SilcClient client,
+ *                                               const char *name);
+ *
+ * DESCRIPTION
+ *
+ *    Finds and returns a pointer to the command list. Return NULL if the
+ *    command is not found. See the `command.[ch]' for the command list. 
+ *    Command names are not case-sensitive.
+ *
+ ***/
+SilcClientCommand silc_client_command_find(SilcClient client,
+                                          const char *name);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_call
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_command_call(SilcClientCommand command);
+ *
+ * DESCRIPTION
+ *
+ *    Calls the command (executes it).  Application can call this after
+ *    it has allocated the SilcClientCommandContext with the function
+ *    silc_client_command_alloc and found the command from the client
+ *    library by calling silc_client_command_find.  This will execute
+ *    the command.
+ *
+ *    Application can call the command function directly too if it
+ *    wishes to do so.  See the command.h for details of the
+ *    SilcClientCommand structure.
+ *
+ ***/
+void silc_client_command_call(SilcClientCommand command,
+                             SilcClientCommandContext cmd);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_send
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_command_send(SilcClient client, 
+ *                                  SilcClientConnection conn,
+ *                                  SilcCommand command, uint16 ident,
+ *                                  uint32 argc, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Generic function to send any command. The arguments must be sent already
+ *    encoded into correct form and in correct order. If application wants
+ *    to perform the commands by itself, it can do so and send the data
+ *    directly to the server using this function.  If application is using
+ *    the silc_client_command_call, this function is usually not used.
+ *
+ ***/
+void silc_client_command_send(SilcClient client, SilcClientConnection conn,
+                             SilcCommand command, uint16 ident,
+                             uint32 argc, ...);
+
+/****f* silcclient/SilcClientAPI/silc_client_command_pending
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_command_pending(SilcClientConnection conn,
+ *                                     SilcCommand reply_cmd,
+ *                                     uint16 ident,
+ *                                     SilcCommandCb callback,
+ *                                     void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Add new pending command to be executed when reply to a command has been
+ *    received.  The `reply_cmd' is the command that will call the `callback'
+ *    with `context' when reply has been received.  If `ident' is non-zero
+ *    the `callback' will be executed when received reply with command 
+ *    identifier `ident'. 
+ *
+ *    Note that the application is notified about the received command
+ *    reply through the `command_reply' client operation before calling
+ *    the `callback` pending command callback.
+ *
+ ***/
+void silc_client_command_pending(SilcClientConnection conn,
+                                SilcCommand reply_cmd,
+                                uint16 ident,
+                                SilcCommandCb callback,
+                                void *context);
+
+
+/* Private Message key management (client_prvmsg.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_add_private_message_key
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_add_private_message_key(SilcClient client,
+ *                                            SilcClientConnection conn,
+ *                                            SilcClientEntry client_entry,
+ *                                            char *cipher,
+ *                                            unsigned char *key,
+ *                                            uint32 key_len,
+ *                                            bool generate_key,
+ *                                            bool responder);
+ *
+ * DESCRIPTION
+ *
+ *    Adds private message key to the client library. The key will be used to
+ *    encrypt all private message between the client and the remote client
+ *    indicated by the `client_entry'. If the `key' is NULL and the boolean
+ *    value `generate_key' is TRUE the library will generate random key.
+ *    The `key' maybe for example pre-shared-key, passphrase or similar.
+ *    The `cipher' MAY be provided but SHOULD be NULL to assure that the
+ *    requirements of the SILC protocol are met. The API, however, allows
+ *    to allocate any cipher.
+ *
+ *    If `responder' is TRUE then the sending and receiving keys will be
+ *    set according the client being the receiver of the private key.  If
+ *    FALSE the client is being the sender (or negotiator) of the private
+ *    key.
+ *
+ *    It is not necessary to set key for normal private message usage. If the
+ *    key is not set then the private messages are encrypted using normal
+ *    session keys. Setting the private key, however, increases the security. 
+ *
+ *    Returns FALSE if the key is already set for the `client_entry', TRUE
+ *    otherwise. 
+ *
+ ***/
+int silc_client_add_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry,
+                                       char *cipher,
+                                       unsigned char *key,
+                                       uint32 key_len,
+                                       bool generate_key,
+                                       bool responder);
+
+/****f* silcclient/SilcClientAPI/silc_client_add_private_message_key_ske
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_add_private_message_key_ske(SilcClient client,
+ *                                                SilcClientConnection conn,
+ *                                                SilcClientEntry client_entry,
+ *                                                char *cipher,
+ *                                                SilcSKEKeyMaterial *key);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_client_add_private_message_key but takes the key material
+ *    from the SKE key material structure. This structure is received if
+ *    the application uses the silc_client_send_key_agreement to negotiate
+ *    the key material. The `cipher' SHOULD be provided as it is negotiated
+ *    also in the SKE protocol. 
+ *
+ ***/
+int silc_client_add_private_message_key_ske(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcClientEntry client_entry,
+                                           char *cipher,
+                                           SilcSKEKeyMaterial *key,
+                                           bool responder);
+
+/****f* silcclient/SilcClientAPI/silc_client_send_private_message_key
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_send_private_message_key(SilcClient client,
+ *                                             SilcClientConnection conn,
+ *                                             SilcClientEntry client_entry,
+ *                                             int force_send);
+ *
+ * DESCRIPTION
+ *
+ *    Sends private message key payload to the remote client indicated by
+ *    the `client_entry'. If the `force_send' is TRUE the packet is sent
+ *    immediately. Returns FALSE if error occurs, TRUE otherwise. The
+ *    application should call this function after setting the key to the
+ *    client.
+ *
+ *    Note that the key sent using this function is sent to the remote client
+ *    through the SILC network. The packet is protected using normal session
+ *    keys. 
+ *
+ ***/
+int silc_client_send_private_message_key(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        int force_send);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_private_message_key
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_del_private_message_key(SilcClient client,
+ *                                            SilcClientConnection conn,
+ *                                            SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    Removes the private message from the library. The key won't be used
+ *    after this to protect the private messages with the remote `client_entry'
+ *    client. Returns FALSE on error, TRUE otherwise. 
+ *
+ ***/
+int silc_client_del_private_message_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcClientEntry client_entry);
+
+/****f* silcclient/SilcClientAPI/silc_client_list_private_message_keys
+ *
+ * SYNOPSIS
+ *
+ *    SilcPrivateMessageKeys
+ *    silc_client_list_private_message_keys(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          uint32 *key_count);
+ * 
+ * DESCRIPTION
+ *
+ *    Returns array of set private message keys associated to the connection
+ *    `conn'. Returns allocated SilcPrivateMessageKeys array and the array
+ *    count to the `key_count' argument. The array must be freed by the caller
+ *    by calling the silc_client_free_private_message_keys function. Note: 
+ *    the keys returned in the array is in raw format. It might not be desired
+ *    to show the keys as is. The application might choose not to show the keys
+ *    at all or to show the fingerprints of the keys. 
+ *
+ ***/
+SilcPrivateMessageKeys
+silc_client_list_private_message_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     uint32 *key_count);
+
+/****f* silcclient/SilcClientAPI/silc_client_free_private_message_keys
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
+ *                                               uint32 key_count);
+ * 
+ * DESCRIPTION
+ *
+ *    Frees the SilcPrivateMessageKeys array returned by the function
+ *    silc_client_list_private_message_keys. 
+ *
+ ***/
+void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
+                                          uint32 key_count);
+
+
+/* Channel private key management (client_channel.c, 
+   SilcChannelPrivateKey is defined in idlist.h) */
+
+/****f* silcclient/SilcClientAPI/silc_client_add_channel_private_key
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_add_channel_private_key(SilcClient client,
+ *                                            SilcClientConnection conn,
+ *                                            SilcChannelEntry channel,
+ *                                            char *cipher,
+ *                                            char *hmac,
+ *                                            unsigned char *key,
+ *                                            uint32 key_len);
+ * 
+ * DESCRIPTION
+ *
+ *    Adds private key for channel. This may be set only if the channel's mode
+ *    mask includes the SILC_CHANNEL_MODE_PRIVKEY. This returns FALSE if the
+ *    mode is not set. When channel has private key then the messages are
+ *    encrypted using that key. All clients on the channel must also know the
+ *    key in order to decrypt the messages. However, it is possible to have
+ *    several private keys per one channel. In this case only some of the
+ *    clients on the channel may know the one key and only some the other key.
+ *
+ *    The private key for channel is optional. If it is not set then the
+ *    channel messages are encrypted using the channel key generated by the
+ *    server. However, setting the private key (or keys) for the channel 
+ *    significantly adds security. If more than one key is set the library
+ *    will automatically try all keys at the message decryption phase. Note:
+ *    setting many keys slows down the decryption phase as all keys has to
+ *    be tried in order to find the correct decryption key. However, setting
+ *    a few keys does not have big impact to the decryption performace. 
+ *
+ * NOTES
+ *
+ *    NOTE: This is entirely local setting. The key set using this function
+ *    is not sent to the network at any phase.
+ *
+ *    NOTE: If the key material was originated by the SKE protocol (using
+ *    silc_client_send_key_agreement) then the `key' MUST be the
+ *    key->send_enc_key as this is dictated by the SILC protocol. However,
+ *    currently it is not expected that the SKE key material would be used
+ *    as channel private key. However, this API allows it. 
+ *
+ ***/
+int silc_client_add_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       char *cipher,
+                                       char *hmac,
+                                       unsigned char *key,
+                                       uint32 key_len);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_channel_private_keys
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_del_channel_private_keys(SilcClient client,
+ *                                             SilcClientConnection conn,
+ *                                             SilcChannelEntry channel);
+ * 
+ * DESCRIPTION
+ *
+ *    Removes all private keys from the `channel'. The old channel key is used
+ *    after calling this to protect the channel messages. Returns FALSE on
+ *    on error, TRUE otherwise. 
+ *
+ ***/
+int silc_client_del_channel_private_keys(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcChannelEntry channel);
+
+/****f* silcclient/SilcClientAPI/silc_client_del_channel_private_key
+ *
+ * SYNOPSIS
+ *
+ *    int silc_client_del_channel_private_key(SilcClient client,
+ *                                            SilcClientConnection conn,
+ *                                            SilcChannelEntry channel,
+ *                                            SilcChannelPrivateKey key);
+ * 
+ * DESCRIPTION
+ *
+ *    Removes and frees private key `key' from the channel `channel'. 
+ *    The `key' is retrieved by calling the function 
+ *    silc_client_list_channel_private_keys. The key is not used after
+ *    this. If the key was last private key then the old channel key is
+ *    used hereafter to protect the channel messages. This returns FALSE
+ *    on error, TRUE otherwise. 
+ *
+ ***/
+int silc_client_del_channel_private_key(SilcClient client,
+                                       SilcClientConnection conn,
+                                       SilcChannelEntry channel,
+                                       SilcChannelPrivateKey key);
+
+/****f* silcclient/SilcClientAPI/silc_client_list_channel_private_keys
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelPrivateKey *
+ *    silc_client_list_channel_private_keys(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcChannelEntry channel,
+ *                                          uint32 *key_count);
+ *
+ * DESCRIPTION
+ *
+ *    Returns array (pointers) of private keys associated to the `channel'.
+ *    The caller must free the array by calling the function
+ *    silc_client_free_channel_private_keys. The pointers in the array may be
+ *    used to delete the specific key by giving the pointer as argument to the
+ *    function silc_client_del_channel_private_key. 
+ *
+ ***/
+SilcChannelPrivateKey *
+silc_client_list_channel_private_keys(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcChannelEntry channel,
+                                     uint32 *key_count);
+
+/****f* silcclient/SilcClientAPI/silc_client_free_channel_private_keys
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
+ *                                               uint32 key_count);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the SilcChannelPrivateKey array.
+ *
+ ***/
+void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
+                                          uint32 key_count);
+
+
+/* Key Agreement routines (client_keyagr.c) */
+
+/****f* silcclient/SilcClientAPI/silc_client_send_key_agreement
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_send_key_agreement(SilcClient client,
+ *                                        SilcClientConnection conn,
+ *                                        SilcClientEntry client_entry,
+ *                                        char *hostname,
+ *                                        int port,
+ *                                        uint32 timeout_secs,
+ *                                        SilcKeyAgreementCallback completion,
+ *                                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Sends key agreement request to the remote client indicated by the
+ *    `client_entry'. If the caller provides the `hostname' and the `port'
+ *    arguments then the library will bind the client to that hostname and
+ *    that port for the key agreement protocol. It also sends the `hostname'
+ *    and the `port' in the key agreement packet to the remote client. This
+ *    would indicate that the remote client may initiate the key agreement
+ *    protocol to the `hostname' on the `port'.  If port is zero then the
+ *    bound port is undefined (the operating system defines it).
+ *
+ *    If the `hostname' and `port' is not provided then empty key agreement
+ *    packet is sent to the remote client. The remote client may reply with
+ *    the same packet including its hostname and port. If the library receives
+ *    the reply from the remote client the `key_agreement' client operation
+ *    callback will be called to verify whether the user wants to perform the
+ *    key agreement or not. 
+ *
+ * NOTES
+ *
+ *    NOTE: If the application provided the `hostname' and the `port' and the 
+ *    remote side initiates the key agreement protocol it is not verified
+ *    from the user anymore whether the protocol should be executed or not.
+ *    By setting the `hostname' and `port' the user gives permission to
+ *    perform the protocol (we are responder in this case).
+ *
+ *    NOTE: If the remote side decides not to initiate the key agreement
+ *    or decides not to reply with the key agreement packet then we cannot
+ *    perform the key agreement at all. If the key agreement protocol is
+ *    performed the `completion' callback with the `context' will be called.
+ *    If remote side decides to ignore the request the `completion' will be
+ *    called after the specified timeout, `timeout_secs'. 
+ *
+ *    NOTE: If the `hostname' and the `port' was not provided the `completion'
+ *    will not be called at all since this does nothing more than sending
+ *    a packet to the remote host.
+ *
+ *    NOTE: There can be only one active key agreement for one client entry.
+ *    Before setting new one, the old one must be finished (it is finished
+ *    after calling the completion callback) or the function 
+ *    silc_client_abort_key_agreement must be called. 
+ *
+ ***/
+void silc_client_send_key_agreement(SilcClient client,
+                                   SilcClientConnection conn,
+                                   SilcClientEntry client_entry,
+                                   const char *hostname,
+                                   const char *bindhost,
+                                   int port,
+                                   uint32 timeout_secs,
+                                   SilcKeyAgreementCallback completion,
+                                   void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_perform_key_agreement
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_perform_key_agreement(SilcClient client,
+ *                                      SilcClientConnection conn,
+ *                                      SilcClientEntry client_entry,
+ *                                      char *hostname,
+ *                                      int port,
+ *                                      SilcKeyAgreementCallback completion,
+ *                                      void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Performs the actual key agreement protocol. Application may use this
+ *    to initiate the key agreement protocol. This can be called for example
+ *    after the application has received the `key_agreement' client operation,
+ *    and did not return TRUE from it.
+ *
+ *    The `hostname' is the remote hostname (or IP address) and the `port'
+ *    is the remote port. The `completion' callback with the `context' will
+ *    be called after the key agreement protocol.
+ *
+ * NOTES
+ * 
+ *    NOTE: If the application returns TRUE in the `key_agreement' client
+ *    operation the library will automatically start the key agreement. In this
+ *    case the application must not call this function. However, application
+ *    may choose to just ignore the `key_agreement' client operation (and
+ *    merely just print information about it on the screen) and call this
+ *    function when the user whishes to do so (by, for example, giving some
+ *    specific command). Thus, the API provides both, automatic and manual
+ *    initiation of the key agreement. Calling this function is the manual
+ *    initiation and returning TRUE in the `key_agreement' client operation
+ *    is the automatic initiation. 
+ *
+ ***/
+void silc_client_perform_key_agreement(SilcClient client,
+                                      SilcClientConnection conn,
+                                      SilcClientEntry client_entry,
+                                      char *hostname,
+                                      int port,
+                                      SilcKeyAgreementCallback completion,
+                                      void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_perform_key_agreement_fd
+ *
+ * SYNOPSIS
+ *
+ *    void
+ *    silc_client_perform_key_agreement_fd(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         SilcClientEntry client_entry,
+ *                                         int sock,
+ *                                         char *hostname,
+ *                                         SilcKeyAgreementCallback completion,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as above but application has created already the connection to 
+ *    the remote host. The `sock' is the socket to the remote connection. 
+ *    Application can use this function if it does not want the client library
+ *    to create the connection. 
+ *
+ ***/
+void silc_client_perform_key_agreement_fd(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcClientEntry client_entry,
+                                         int sock,
+                                         char *hostname,
+                                         SilcKeyAgreementCallback completion,
+                                         void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_abort_key_agreement
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_abort_key_agreement(SilcClient client,
+ *                                         SilcClientConnection conn,
+ *                                         SilcClientEntry client_entry);
+ *
+ * DESCRIPTION
+ *
+ *    This function can be called to unbind the hostname and the port for
+ *    the key agreement protocol. However, this function has effect only 
+ *    before the key agreement protocol has been performed. After it has
+ *    been performed the library will automatically unbind the port. The 
+ *    `client_entry' is the client to which we sent the key agreement 
+ *    request.  The key agreement completion callback will be called
+ *    with SILC_KEY_AGREEMENT_ABORTED status.
+ *
+ ***/
+void silc_client_abort_key_agreement(SilcClient client,
+                                    SilcClientConnection conn,
+                                    SilcClientEntry client_entry);
+
+
+/* Misc functions */
+
+/****f* silcclient/SilcClientAPI/silc_client_set_away_message
+ *
+ * SYNOPSIS
+ *
+ *    void silc_client_set_away_message(SilcClient client,
+ *                                      SilcClientConnection conn,
+ *                                      char *message);
+ *
+ * DESCRIPTION
+ *
+ *    Sets away `message'.  The away message may be set when the client's
+ *    mode is changed to SILC_UMODE_GONE and the client whishes to reply
+ *    to anyone who sends private message.  The `message' will be sent
+ *    automatically back to the the client who send private message.  If
+ *    away message is already set this replaces the old message with the
+ *    new one.  If `message' is NULL the old away message is removed. 
+ *    The sender may freely free the memory of the `message'. 
+ *
+ ***/
+void silc_client_set_away_message(SilcClient client,
+                                 SilcClientConnection conn,
+                                 char *message);
+
+
+/****f* silcclient/SilcClientAPI/SilcConnectionAuthRequest
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcConnectionAuthRequest)(SilcClient client,
+ *                                              SilcClientConnection conn,
+ *                                              SilcAuthMethod auth_meth,
+ *                                              void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Connection authentication method request callback. This is called
+ *    by the client library after it has received the authentication method
+ *    that the application requested by calling the function
+ *    silc_client_request_authentication_method.
+ *
+ ***/
+typedef void (*SilcConnectionAuthRequest)(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcAuthMethod auth_meth,
+                                         void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_request_authentication_method
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_client_request_authentication_method(SilcClient client,
+ *                                              SilcClientConnection conn,
+ *                                              SilcConnectionAuthRequest 
+ *                                                callback,
+ *                                              void *context);
+ *
+ * DESCRIPTION
+ *
+ *    This function can be used to request the current authentication method
+ *    from the server. This may be called when connecting to the server
+ *    and the client library requests the authentication data from the
+ *    application. If the application does not know the current authentication
+ *    method it can request it from the server using this function.
+ *    The `callback' with `context' will be called after the server has
+ *    replied back with the current authentication method.
+ *
+ ***/
+void 
+silc_client_request_authentication_method(SilcClient client,
+                                         SilcClientConnection conn,
+                                         SilcConnectionAuthRequest callback,
+                                         void *context);
+
+/****d* silcclient/SilcClientAPI/SilcClientMonitorStatus
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcClientMonitorStatus;
+ *
+ * DESCRIPTION
+ *
+ *    File transmission session status types.  These will indicate
+ *    the status of the file transmission session.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT,    /* In key agreemenet phase */
+  SILC_CLIENT_FILE_MONITOR_SEND,            /* Sending file */
+  SILC_CLIENT_FILE_MONITOR_RECEIVE,         /* Receiving file */
+  SILC_CLIENT_FILE_MONITOR_GET,
+  SILC_CLIENT_FILE_MONITOR_PUT,
+  SILC_CLIENT_FILE_MONITOR_CLOSED,          /* Session closed */
+  SILC_CLIENT_FILE_MONITOR_ERROR,           /* Error during session */
+} SilcClientMonitorStatus;
+/***/
+
+/****d* silcclient/SilcClientAPI/SilcClientFileError
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcClientFileError;
+ *
+ * DESCRIPTION
+ *
+ *    File transmission error types.  These types are returned by
+ *    some of the file transmission functions, and by the monitor
+ *    callback to indicate error.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_CLIENT_FILE_OK,
+  SILC_CLIENT_FILE_ERROR,
+  SILC_CLIENT_FILE_UNKNOWN_SESSION,
+  SILC_CLIENT_FILE_ALREADY_STARTED,
+  SILC_CLIENT_FILE_NO_SUCH_FILE,
+  SILC_CLIENT_FILE_PERMISSION_DENIED,
+  SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED,
+} SilcClientFileError;
+/***/
+
+/****f* silcclient/SilcClientAPI/SilcClientFileMonitor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcClientFileMonitor)(SilcClient client,
+ *                                          SilcClientConnection conn,
+ *                                          SilcClientMonitorStatus status,
+ *                                          SilcClientFileError error,
+ *                                          uint64 offset,
+ *                                          uint64 filesize,
+ *                                          SilcClientEntry client_entry,
+ *                                          uint32 session_id,
+ *                                          const char *filepath,
+ *                                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Monitor callback that is called during the file transmission to
+ *    monitor the transmission process.  The `status' indicates the current
+ *    monitoring process.  The `error' will indicate the error type
+ *    if `status' is SILC_CLIENT_FILE_MONITOR_ERROR.  The `offset' is the
+ *    currently transmitted amount of total `filesize'.  The `client_entry'
+ *    indicates the remote client, and the transmission session ID is the 
+ *    `session_id'.  The filename being transmitted is indicated by the 
+ *    `filepath'.
+ *
+ ***/
+typedef void (*SilcClientFileMonitor)(SilcClient client,
+                                     SilcClientConnection conn,
+                                     SilcClientMonitorStatus status,
+                                     SilcClientFileError error,
+                                     uint64 offset,
+                                     uint64 filesize,
+                                     SilcClientEntry client_entry,
+                                     uint32 session_id,
+                                     const char *filepath,
+                                     void *context);
+
+/****f* silcclient/SilcClientAPI/silc_client_file_send
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientFileError 
+ *    silc_client_file_send(SilcClient client,
+ *                          SilcClientConnection conn,
+ *                          SilcClientFileMonitor monitor,
+ *                          void *monitor_context,
+ *                          const char *local_ip,
+ *                          uint32 local_port,
+ *                          SilcClientEntry client_entry,
+ *                          const char *filepath);
+ *                          uint32 *session_id);
+ *
+ * DESCRIPTION
+ *
+ *    Sends a file indicated by the `filepath' to the remote client 
+ *    indicated by the `client_entry'.  This will negotiate a secret key
+ *    with the remote client before actually starting the transmission of
+ *    the file.  The `monitor' callback will be called to monitor the
+ *    transmission of the file.
+ *
+ *    This returns a file session ID for the file transmission to the
+ *    `session_id' pointer..  It can be used to close the session (and
+ *    abort the file transmission) by calling the silc_client_file_close
+ *    function.  The session ID is also returned in the `monitor' callback. 
+ *
+ *    If the `local_ip' is provided then this will try to bind the 
+ *    listener for key exchange protocol to that IP.  If `local_port' is
+ *    non-zero that port is used.  If `local_ip' is NULL then this will
+ *    automatically attempt to bind it to local IP address of the machine.
+ *    If that fails then this does not bind to any address and port, and
+ *    assume that the remote client will provide the listener for the
+ *    key exchange protocol.
+ *
+ *    If error will occur during the file transfer process the error
+ *    status will be returned in the monitor callback.  In this case
+ *    the application must call silc_client_file_close to close the
+ *    session.
+ *
+ ***/
+SilcClientFileError 
+silc_client_file_send(SilcClient client,
+                     SilcClientConnection conn,
+                     SilcClientFileMonitor monitor,
+                     void *monitor_context,
+                     const char *local_ip,
+                     uint32 local_port,
+                     SilcClientEntry client_entry,
+                     const char *filepath,
+                     uint32 *session_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_file_receive
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientFileError 
+ *    silc_client_file_receive(SilcClient client,
+ *                             SilcClientConnection conn,
+ *                             SilcClientFileMonitor monitor,
+ *                             void *monitor_context,
+ *                             uint32 session_id);
+ *
+ * DESCRIPTION
+ *
+ *    Receives a file from a client indicated by the `client_entry'.  The
+ *    `session_id' indicates the file transmission session and it has been
+ *    received in the `ftp' client operation function.  This will actually
+ *    perform the key agreement protocol with the remote client before
+ *    actually starting the file transmission.  The `monitor' callback
+ *    will be called to monitor the transmission.
+ *
+ *    If error will occur during the file transfer process the error
+ *    status will be returned in the monitor callback.  In this case
+ *    the application must call silc_client_file_close to close the
+ *    session.
+ *
+ ***/
+SilcClientFileError 
+silc_client_file_receive(SilcClient client,
+                        SilcClientConnection conn,
+                        SilcClientFileMonitor monitor,
+                        void *monitor_context,
+                        uint32 session_id);
+
+/****f* silcclient/SilcClientAPI/silc_client_file_close
+ *
+ * SYNOPSIS
+ *
+ *    SilcClientFileError silc_client_file_close(SilcClient client,
+ *                                               SilcClientConnection conn,
+ *                                               uint32 session_id);
+ *
+ * DESCRIPTION
+ *
+ *    Closes file transmission session indicated by the `session_id'.
+ *    If file transmission is being conducted it will be aborted
+ *    automatically. This function is also used to close the session
+ *    after successful file transmission. This function can be used
+ *    also to reject incoming file transmission request.
+ *
+ ***/
+SilcClientFileError silc_client_file_close(SilcClient client,
+                                          SilcClientConnection conn,
+                                          uint32 session_id);
+
+#endif
diff --git a/lib/silccore/DIRECTORY b/lib/silccore/DIRECTORY
new file mode 100644 (file)
index 0000000..bc33525
--- /dev/null
@@ -0,0 +1,27 @@
+<!--
+@LIBRARY=SILC Core Library
+@FILENAME=silccorelib.html
+@LINK=silcauth.html:SILC Auth API
+@LINK=silcchannel.html:SILC Channel API
+@LINK=silccommand.html:SILC Command API
+@LINK=silcid.html:SILC ID API
+@LINK=silcidcache.html:SILC ID Cache API
+@LINK=silcmode.html:SILC Modes
+@LINK=silcnotify.html:SILC Notify API
+@LINK=silcpacket.html:SILC Packet API
+@LINK=silcargument.html:SILC Argument API
+@LINK=silcprivate.html:SILC Private API
+-->
+
+<BIG><B>SILC Core Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC Core Library includes all the core components of the SILC Protocol.
+It provides routines to encode and decode all SILC packet payloads defined
+in the protocol specification.  It provides packet assembling and parsing
+routines, and routines for sending private message and channel messages.
+
+<BR /><BR />
+@LINKS@
index 5811b56a5438b3b3cc0cae1f9feb90f67149ca7a..bf01a0f103df4ebdcc71d0a07aa6878736944687 100644 (file)
@@ -21,26 +21,30 @@ AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 noinst_LIBRARIES = libsilccore.a
 
 libsilccore_a_SOURCES = \
-       id.c \
-       idcache.c \
-       silcbuffer.c \
-       silcbuffmt.c \
-       silcbufutil.c \
+       silcid.c \
+       silcidcache.c \
        silcchannel.c \
        silccommand.c \
-       silcconfig.c \
-       silclog.c \
-       silcmemory.c \
-       silcnet.c \
        silcpacket.c \
-       silcprotocol.c \
-       silcschedule.c \
-       silcsockconn.c \
-       silctask.c \
-       silcutil.c
+       silcargument.c \
+       silcnotify.c \
+       silcauth.c \
+       silcprivate.c
+
+if SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       silcauth.h      \
+       silcchannel.h   \
+       silccommand.h   \
+       silcidcache.h   \
+       silcid.h        \
+       silcmode.h      \
+       silcnotify.h    \
+       silcpacket.h    \
+       silcargument.h  \
+       silcprivate.h
+endif
 
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../silccrypt -I../silcmath -I../silcske \
-       -I../silcsim -I../.. -I../../includes \
-       -I../silcmath/gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silccore/id.c b/lib/silccore/id.c
deleted file mode 100644 (file)
index 324c9cf..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
-
-  id.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* Converts ID to string. */
-
-unsigned char *silc_id_id2str(void *id, SilcIdType type)
-{
-  unsigned char *ret_id;
-  SilcServerID *server_id;
-  SilcClientID *client_id;
-  SilcChannelID *channel_id;
-
-  switch(type) {
-  case SILC_ID_SERVER:
-    server_id = (SilcServerID *)id;
-    ret_id = silc_calloc(8, sizeof(unsigned char));
-    SILC_PUT32_MSB(server_id->ip.s_addr, ret_id);
-    SILC_PUT16_MSB(server_id->port, &ret_id[4]);
-    SILC_PUT16_MSB(server_id->rnd, &ret_id[6]);
-    return ret_id;
-    break;
-  case SILC_ID_CLIENT:
-    client_id = (SilcClientID *)id;
-    ret_id = silc_calloc(16, sizeof(unsigned char));
-    SILC_PUT32_MSB(client_id->ip.s_addr, ret_id);
-    ret_id[4] = client_id->rnd;
-    memcpy(&ret_id[5], client_id->hash, CLIENTID_HASH_LEN);
-    return ret_id;
-    break;
-  case SILC_ID_CHANNEL:
-    channel_id = (SilcChannelID *)id;
-    ret_id = silc_calloc(8, sizeof(unsigned char));
-    SILC_PUT32_MSB(channel_id->ip.s_addr, ret_id);
-    SILC_PUT16_MSB(channel_id->port, &ret_id[4]);
-    SILC_PUT16_MSB(channel_id->rnd, &ret_id[6]);
-    return ret_id;
-    break;
-  }
-
-  return NULL;
-}
-
-/* Converts string to a ID */
-
-void *silc_id_str2id(unsigned char *id, SilcIdType type) 
-{
-
-  switch(type) {
-  case SILC_ID_SERVER:
-    {
-      SilcServerID *server_id = silc_calloc(1, sizeof(*server_id));
-      SILC_GET32_MSB(server_id->ip.s_addr, id);
-      SILC_GET16_MSB(server_id->port, &id[4]);
-      SILC_GET16_MSB(server_id->rnd, &id[6]);
-      return server_id;
-    }
-    break;
-  case SILC_ID_CLIENT:
-    {
-      SilcClientID *client_id = silc_calloc(1, sizeof(*client_id));
-      SILC_GET32_MSB(client_id->ip.s_addr, id);
-      client_id->rnd = id[4];
-      memcpy(client_id->hash, &id[5], CLIENTID_HASH_LEN);
-      return client_id;
-    }
-    break;
-  case SILC_ID_CHANNEL:
-    {
-      SilcChannelID *channel_id = silc_calloc(1, sizeof(*channel_id));
-      SILC_GET32_MSB(channel_id->ip.s_addr, id);
-      SILC_GET16_MSB(channel_id->port, &id[4]);
-      SILC_GET16_MSB(channel_id->rnd, &id[6]);
-      return channel_id;
-    }
-    break;
-  }
-
-  return NULL;
-}
-
-/* Returns length of the ID */
-
-unsigned int silc_id_get_len(SilcIdType type)
-{
-  switch(type) {
-  case SILC_ID_SERVER:
-    return SILC_ID_SERVER_LEN;
-    break;
-  case SILC_ID_CLIENT:
-    return SILC_ID_CLIENT_LEN;
-    break;
-  case SILC_ID_CHANNEL:
-    return SILC_ID_CHANNEL_LEN;
-    break;
-  }
-
-  return 0;
-}
diff --git a/lib/silccore/id.h b/lib/silccore/id.h
deleted file mode 100644 (file)
index 56551c4..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-
-  id.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/* These are important ID types used in SILC. SILC server creates these
-   but SILC client has to handle these as well since these are used in
-   packet sending and reception. However, client never creates these
-   but it receives the correct ID's from server. Clients, servers and
-   channels are identified by the these ID's.
-
-   Note that these are currently IPv4 specific, although adding IPv6
-   support is not a bad task and SILC protocol already supports IPv6.
-*/
-
-#ifndef ID_H
-#define ID_H
-
-#define SILC_ID_SERVER_LEN     (64 / 8)
-#define SILC_ID_CLIENT_LEN     (128 / 8)
-#define SILC_ID_CHANNEL_LEN    (64 / 8)
-#define CLIENTID_HASH_LEN       (88 / 8) /* Client ID's 88 bit MD5 hash */
-
-/* SILC ID Types */
-#define SILC_ID_NONE 0
-#define SILC_ID_SERVER 1
-#define SILC_ID_CLIENT 2
-#define SILC_ID_CHANNEL 3
-
-/* Type definition for the ID types. */
-typedef unsigned char SilcIdType;
-
-/* 
-   64 bit SilcServerID structure:
-   
-   32 bit IP address
-   16 bit port
-   16 bit random number
-*/
-typedef struct {
-  struct in_addr ip;                           /* 32 bit IP */
-  unsigned short port;                         /* 16 bit port */
-  unsigned short rnd;                          /* 16 bit random number */
-} SilcServerID;
-
-/* 
-   128 bit SilcClientID structure:
-
-   32 bit ServerID IP address [bits 1-32]
-    8 bit random number
-   88 bit hash value from nickname
-*/
-typedef struct {
-  struct in_addr ip;                           /* 32 bit IP */
-  unsigned char rnd;                           /* 8 bit random number */
-  unsigned char hash[CLIENTID_HASH_LEN];       /* 88 bit MD5 hash */
-} SilcClientID;
-
-/* 
-   64 bit SilcChannel ID structure:
-
-   32 bit Router's ServerID IP address [bits 1-32]
-   16 bit Router's ServerID port [bits 33-48]
-   16 bit random number
-*/
-typedef struct {
-  struct in_addr ip;                           /* 32 bit IP */
-  unsigned short port;                         /* 16 bit port */
-  unsigned short rnd;                          /* 16 bit random number */
-} SilcChannelID;
-
-/* Macros */
-
-/* Compares two ID's */
-#define SILC_ID_COMPARE(id1, id2, len) (memcmp(id1, id2, len))
-
-/* Compares Channel ID's */
-#define SILC_ID_CHANNEL_COMPARE(id1, id2) \
-  SILC_ID_COMPARE(id1, id2, SILC_ID_CHANNEL_LEN)
-
-/* Compares Client ID's */
-#define SILC_ID_CLIENT_COMPARE(id1, id2) \
-  SILC_ID_COMPARE(id1, id2, SILC_ID_CLIENT_LEN)
-
-/* Compares Server ID's */
-#define SILC_ID_SERVER_COMPARE(id1, id2) \
-  SILC_ID_COMPARE(id1, id2, SILC_ID_SERVER_LEN)
-
-/* Compares IP addresses from the ID's. */
-#define SILC_ID_COMPARE_IP(id1, id2) \
-  SILC_ID_COMPARE(id1, id2, 4)
-
-/* Compare nickname hash from Client ID */
-#define SILC_ID_COMPARE_HASH(id, hash) \
-  memcmp(id->hash, hash, CLIENTID_HASH_LEN)
-
-/* Prototypes */
-unsigned char *silc_id_id2str(void *id, SilcIdType type);
-void *silc_id_str2id(unsigned char *id, SilcIdType type);
-unsigned int silc_id_get_len(SilcIdType type);
-
-#endif
diff --git a/lib/silccore/idcache.c b/lib/silccore/idcache.c
deleted file mode 100644 (file)
index 6f9c130..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
-
-  idcache.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/* XXX: This ID cache system sucks and must be rewritten! */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* qsort() sorter function. */
-
-static int silc_idcache_sorter(const void *a, const void *b)
-{
-  SilcIDCache *a1, *b1;
-  
-  a1 = (SilcIDCache *)a;
-  b1 = (SilcIDCache *)b;
-  
-  return a1->data[0] - b1->data[0];
-}
-
-/* Sorts given cache by data */
-
-void silc_idcache_sort_by_data(SilcIDCache *cache, unsigned int count)
-{
-  qsort(cache, count, sizeof(*cache), silc_idcache_sorter);
-}
-
-/* Find ID Cache entry by data. The data maybe anything that must
-   match exactly. */
-
-int silc_idcache_find_by_data(SilcIDCache *cache, unsigned int cache_count,
-                             char *data, SilcIDCache **ret)
-{
-  int i;
-
-  if (cache == NULL)
-    return FALSE;
-
-  if (data == NULL)
-    return FALSE;
-
-  for (i = 0; i < cache_count; i++)
-    if (cache[i].data && !memcmp(cache[i].data, data, strlen(data))) {
-      if (ret)
-       *ret = &(cache[i]);
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-/* Find ID Cache entry by ID. */
-
-int silc_idcache_find_by_id(SilcIDCache *cache, unsigned int cache_count, 
-                           void *id, SilcIdType type, SilcIDCache **ret)
-{
-  int i, id_len;
-
-  if (cache == NULL)
-    return FALSE;
-
-  if (id == NULL)
-    return FALSE;
-
-  id_len = silc_id_get_len(type);
-
-  for (i = 0; i < cache_count; i++)
-    if (cache[i].id && !memcmp(cache[i].id, id, id_len)) {
-      if (ret)
-       *ret = &(cache[i]);
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-/* Add new entry to the cache. Returns number of allocated cache
-   entries in the cache. */
-
-int silc_idcache_add(SilcIDCache **cache, unsigned int cache_count,
-                    char *data, SilcIdType id_type, void *id, 
-                    void *context)
-{
-  SilcIDCache *c;
-  int i;
-  unsigned long curtime = time(NULL);
-
-  SILC_LOG_DEBUG(("Adding cache entry"));
-
-  c = *cache;
-
-  if (c == NULL) {
-    c = silc_calloc(5, sizeof(*c));
-    if (!c)
-      return 0;
-    cache_count = 5;
-  }
-
-  /* See if it exists already */
-  if (silc_idcache_find_by_id(c, cache_count, id, id_type, NULL) == TRUE)
-    return cache_count;
-
-  for (i = 0; i < cache_count; i++) {
-    if (c[i].data == NULL) {
-      c[i].data = data;
-      c[i].type = id_type;
-      c[i].id = id;
-      c[i].expire = curtime + SILC_ID_CACHE_EXPIRE;
-      c[i].context = context;
-      break;
-    }
-  }
-
-  if (i == cache_count) {
-    c = silc_realloc(c, sizeof(*c) * (cache_count + 5));
-    if (!c)
-      return cache_count;
-    for (i = cache_count; i < cache_count + 5; i++) {
-      c[i].data = NULL;
-      c[i].id = NULL;
-    }
-    c[cache_count].data = data;
-    c[cache_count].type = id_type;
-    c[cache_count].id = id;
-    c[cache_count].expire = curtime + SILC_ID_CACHE_EXPIRE;
-    c[cache_count].context = context;
-    cache_count += 5;
-  }
-
-  *cache = c;
-
-  return cache_count;
-}
-
-/* Delete cache entry from cache. */
-/* XXX */
-
-int silc_idcache_del(SilcIDCache *cache, SilcIDCache *old)
-{
-
-  return TRUE;
-}
-
-/* XXX */
-
-int silc_idcache_del_by_data(SilcIDCache *cache, unsigned int cache_count,
-                            char *data)
-{
-
-  return TRUE;
-}
-
-/* Deletes ID cache entry by ID. */
-
-int silc_idcache_del_by_id(SilcIDCache *cache, unsigned int cache_count,
-                          SilcIdType type, void *id)
-{
-  int i, id_len;
-
-  if (cache == NULL)
-    return FALSE;
-
-  if (id == NULL)
-    return FALSE;
-
-  id_len = silc_id_get_len(type);
-
-  for (i = 0; i < cache_count; i++)
-    if (cache[i].id && !memcmp(cache[i].id, id, id_len)) {
-      cache[i].id = NULL;
-      cache[i].data = NULL;
-      cache[i].type = 0;
-      cache[i].context = NULL;
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-/* Deletes all ID entries from cache. Free's memory as well. */
-
-int silc_idcache_del_all(SilcIDCache **cache, unsigned int cache_count)
-{
-  SilcIDCache *c = *cache;
-  int i;
-
-  if (c == NULL)
-    return FALSE;
-
-  for (i = 0; i < cache_count; i++) {
-    c[i].id = NULL;
-    c[i].data = NULL;
-    c[i].type = 0;
-    c[i].expire = 0;
-    c[i].context = NULL;
-  }
-
-  silc_free(*cache);
-  *cache = NULL;
-
-  return TRUE;
-}
-
-/* Purges the cache by removing expired cache entires. This does not
-   free any memory though. */
-
-int silc_idcache_purge(SilcIDCache *cache, unsigned int cache_count)
-{
-  unsigned long curtime = time(NULL);
-  int i;
-
-  if (cache == NULL)
-    return FALSE;
-
-  for (i = 0; i < cache_count; i++) {
-    if (cache[i].data && 
-       (cache[i].expire == 0 || cache[i].expire < curtime)) {
-      cache[i].id = NULL;
-      cache[i].data = NULL;
-      cache[i].type = 0;
-      cache[i].expire = 0;
-      cache[i].context = NULL;
-    }
-  }
-
-  return TRUE;
-}
diff --git a/lib/silccore/idcache.h b/lib/silccore/idcache.h
deleted file mode 100644 (file)
index 074dc48..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-
-  idcache.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef IDCACHE_H
-#define IDCACHE_H
-
-/* 
-   SilcIDCache structure.
-
-   char *data
-
-      The data that is usually used to find the data from the cache.
-      For example for Client ID's this is nickname.
-
-   SilcIdType type
-
-      Type of the ID.
-
-   void *id
-
-      The actual ID.
-
-   unsigned long expire
-
-      Time when this cache entry expires.  This is normal time() value
-      plus the validity.  Cache entry has expired if current time is
-      more than value in this field, or if this field has been set to
-      zero (0) value.
-
-   void *context
-
-      Any caller specified context.
-
-*/
-typedef struct {
-  char *data;
-  SilcIdType type;
-  void *id;
-  unsigned long expire;
-  void *context;
-} SilcIDCache;
-
-#define SILC_ID_CACHE_EXPIRE 3600
-
-/* Prototypes */
-void silc_idcache_sort_by_data(SilcIDCache *cache, unsigned int count);
-int silc_idcache_find_by_data(SilcIDCache *cache, unsigned int cache_count,
-                             char *data, SilcIDCache **ret);
-int silc_idcache_find_by_id(SilcIDCache *cache, unsigned int cache_count, 
-                           void *id, SilcIdType type, SilcIDCache **ret);
-int silc_idcache_add(SilcIDCache **cache, unsigned int cache_count,
-                    char *data, SilcIdType id_type, void *id, 
-                    void *context);
-int silc_idcache_del(SilcIDCache *cache, SilcIDCache *old);
-int silc_idcache_del_by_data(SilcIDCache *cache, unsigned int cache_count,
-                            char *data);
-int silc_idcache_del_by_id(SilcIDCache *cache, unsigned int cache_count,
-                          SilcIdType type, void *id);
-int silc_idcache_del_all(SilcIDCache **cache, unsigned int cache_count);
-int silc_idcache_purge(SilcIDCache *cache, unsigned int cache_count);
-
-#endif
diff --git a/lib/silccore/silcargument.c b/lib/silccore/silcargument.c
new file mode 100644 (file)
index 0000000..471e92c
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+
+  silcargument.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* Implementation of Argument Payload routines */ 
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcargument.h"
+
+/******************************************************************************
+
+                             Argument Payload
+
+******************************************************************************/
+
+struct SilcArgumentPayloadStruct {
+  uint32 argc;
+  unsigned char **argv;
+  uint32 *argv_lens;
+  uint32 *argv_types;
+  uint32 pos;
+};
+
+/* Parses arguments and returns them into Argument Payload structure. */
+
+SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
+                                               uint32 payload_len,
+                                               uint32 argc)
+{
+  SilcBufferStruct buffer;
+  SilcArgumentPayload new;
+  uint16 p_len = 0;
+  unsigned char arg_num = 0;
+  unsigned char arg_type = 0;
+  uint32 pull_len = 0;
+  int i = 0, ret;
+
+  SILC_LOG_DEBUG(("Parsing argument payload"));
+
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
+  new = silc_calloc(1, sizeof(*new));
+  new->argv = silc_calloc(argc, sizeof(unsigned char *));
+  new->argv_lens = silc_calloc(argc, sizeof(uint32));
+  new->argv_types = silc_calloc(argc, sizeof(uint32));
+    
+  /* Get arguments */
+  arg_num = 1;
+  for (i = 0; i < argc; i++) {
+    ret = silc_buffer_unformat(&buffer,
+                              SILC_STR_UI_SHORT(&p_len),
+                              SILC_STR_UI_CHAR(&arg_type),
+                              SILC_STR_END);
+    if (ret == -1)
+      goto err;
+    
+    new->argv_lens[i] = p_len;
+    new->argv_types[i] = arg_type;
+
+    if (p_len > buffer.len - 3)
+      break;
+    
+    /* Get argument data */
+    silc_buffer_pull(&buffer, 3);
+    ret = silc_buffer_unformat(&buffer,
+                              SILC_STR_UI_XNSTRING_ALLOC(&new->argv[i], 
+                                                         p_len),
+                              SILC_STR_END);
+    if (ret == -1)
+      goto err;
+
+    silc_buffer_pull(&buffer, p_len);
+    pull_len += 3 + p_len;
+  }
+
+  if (buffer.len != 0)
+    goto err;
+
+  new->argc = argc;
+  new->pos = 0;
+
+  silc_buffer_push(&buffer, pull_len);
+
+  return new;
+
+ err:
+  if (i) {
+    int k;
+
+    for (k = 0; k < i; k++)
+      silc_free(new->argv[k]);
+  }
+
+  silc_free(new->argv);
+  silc_free(new->argv_lens);
+  silc_free(new->argv_types);
+
+  if (new)
+    silc_free(new);
+
+  return NULL;
+}
+
+/* Encodes arguments in to Argument Paylods returning them to SilcBuffer. */
+
+SilcBuffer silc_argument_payload_encode(uint32 argc,
+                                       unsigned char **argv,
+                                       uint32 *argv_lens,
+                                       uint32 *argv_types)
+{
+  SilcBuffer buffer;
+  uint32 len;
+  int i;
+
+  SILC_LOG_DEBUG(("Encoding Argument payload"));
+
+  len = 0;
+  for (i = 0; i < argc; i++)
+    len += 3 + argv_lens[i];
+
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  /* Put arguments */
+  for (i = 0; i < argc; i++) {
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_SHORT(argv_lens[i]),
+                      SILC_STR_UI_CHAR(argv_types[i]),
+                      SILC_STR_UI_XNSTRING(argv[i], argv_lens[i]),
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 3 + argv_lens[i]);
+  }
+
+  silc_buffer_push(buffer, len);
+
+  return buffer;
+}
+
+/* Same as above but encode the buffer from SilcArgumentPayload structure
+   instead of raw data. */
+
+SilcBuffer silc_argument_payload_encode_payload(SilcArgumentPayload payload)
+{
+  SilcBuffer buffer;
+  uint32 len;
+  int i;
+
+  SILC_LOG_DEBUG(("Encoding Argument payload"));
+
+  len = 0;
+  for (i = 0; i < payload->argc; i++)
+    len += 3 + payload->argv_lens[i];
+
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  /* Put arguments */
+  for (i = 0; i < payload->argc; i++) {
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_SHORT(payload->argv_lens[i]),
+                      SILC_STR_UI_CHAR(payload->argv_types[i]),
+                      SILC_STR_UI_XNSTRING(payload->argv[i], 
+                                           payload->argv_lens[i]),
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 3 + payload->argv_lens[i]);
+  }
+
+  silc_buffer_push(buffer, len);
+
+  return buffer;
+}
+
+/* Frees Argument Payload */
+
+void silc_argument_payload_free(SilcArgumentPayload payload)
+{
+  int i;
+
+  if (payload) {
+    for (i = 0; i < payload->argc; i++)
+      silc_free(payload->argv[i]);
+
+    silc_free(payload->argv);
+    silc_free(payload->argv_lens);
+    silc_free(payload->argv_types);
+    silc_free(payload);
+  }
+}
+
+/* Returns number of arguments in payload */
+
+uint32 silc_argument_get_arg_num(SilcArgumentPayload payload)
+{
+  return payload ? payload->argc : 0;
+}
+
+/* Returns first argument from payload. */
+
+unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
+                                          uint32 *ret_len)
+{
+  if (!payload)
+    return NULL;
+
+  payload->pos = 0;
+
+  if (ret_len)
+    *ret_len = payload->argv_lens[payload->pos];
+
+  return payload->argv[payload->pos++];
+}
+
+/* Returns next argument from payload or NULL if no more arguments. */
+
+unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
+                                         uint32 *ret_len)
+{
+  if (!payload)
+    return NULL;
+
+  if (payload->pos >= payload->argc)
+    return NULL;
+
+  if (ret_len)
+    *ret_len = payload->argv_lens[payload->pos];
+
+  return payload->argv[payload->pos++];
+}
+
+/* Returns argument which type is `type'. */
+
+unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
+                                         uint32 type,
+                                         uint32 *ret_len)
+{
+  int i;
+
+  if (!payload)
+    return NULL;
+
+  for (i = 0; i < payload->argc; i++)
+    if (payload->argv_types[i] == type)
+      break;
+
+  if (i >= payload->argc)
+    return NULL;
+
+  if (ret_len)
+    *ret_len = payload->argv_lens[i];
+
+  return payload->argv[i];
+}
diff --git a/lib/silccore/silcargument.h b/lib/silccore/silcargument.h
new file mode 100644 (file)
index 0000000..f5de0d9
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+
+  silcargument.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcArgumentAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the Arugment Payload, that is used to include 
+ * argument to other payload that needs arguments.
+ *
+ ***/
+
+#ifndef SILCPAYLOAD_H
+#define SILCPAYLOAD_H
+
+/****f* silccore/SilcArgumentAPI/silc_argument_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcArgumentPayload 
+ *    silc_argument_payload_parse(const unsigned char *payload,
+ *                                uint32 payload_len,
+ *                                uint32 argc);
+ *
+ * DESCRIPTION
+ *
+ *    Parses arguments and returns them into Argument Payload structure.
+ *    the `buffer' is raw Argument Payload data buffer. The `argc' is
+ *    the number of arguments in the Argument Payload. The caller must
+ *    know the number of the arguments. This is always known as the
+ *    Argument payload is associated with other payloads which defines
+ *    the number of the arguments.
+ *
+ ***/
+SilcArgumentPayload silc_argument_payload_parse(const unsigned char *payload,
+                                               uint32 payload_len,
+                                               uint32 argc);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_argument_payload_encode(uint32 argc,
+ *                                            unsigned char **argv,
+ *                                            uint32 *argv_lens,
+ *                                            uint32 *argv_types);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes arguments in to Argument Paylods returning them to SilcBuffer.
+ *    The `argv' is the array of the arguments, the `argv_lens' array of
+ *    the length of the `argv' arguments and the `argv_types' array of
+ *    the argument types of the `argv' arguments. The `argc' is the 
+ *    number of arguments.
+ *
+ ***/
+SilcBuffer silc_argument_payload_encode(uint32 argc,
+                                       unsigned char **argv,
+                                       uint32 *argv_lens,
+                                       uint32 *argv_types);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_payload_encode_payload
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer 
+ *    silc_argument_payload_encode_payload(SilcArgumentPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_argument_payload_encode but encodes the payload from
+ *    already allocated SilcArgumentPayload structure instead of raw data.
+ *
+ ***/
+SilcBuffer silc_argument_payload_encode_payload(SilcArgumentPayload payload);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_argument_payload_free(SilcArgumentPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the Argument Payload and all data in it.
+ *
+ ***/
+void silc_argument_payload_free(SilcArgumentPayload payload);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_get_arg_num
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_argument_get_arg_num(SilcArgumentPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the number of argument in the Argument Payload.
+ *
+ ***/
+uint32 silc_argument_get_arg_num(SilcArgumentPayload payload);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_get_first_arg
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
+ *                                               uint32 *ret_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the first argument in the Argument Payload. The lenght
+ *    of the argument is returned to `ret_len'. The caller must not
+ *    free the returned argument. Returns NULL on error.
+ *
+ ***/
+unsigned char *silc_argument_get_first_arg(SilcArgumentPayload payload,
+                                          uint32 *ret_len);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_get_next_arg
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
+ *                                              uint32 *ret_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns next argument from the Argument Payload. The length of
+ *    the argument is returned to `ret_len'. The caller must not free
+ *    the returned argument. This returns NULL when there are no more
+ *    arguments in the payload.
+ *
+ ***/
+unsigned char *silc_argument_get_next_arg(SilcArgumentPayload payload,
+                                         uint32 *ret_len);
+
+/****f* silccore/SilcArgumentAPI/silc_argument_get_arg_type
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
+ *                                              uint32 type,
+ *                                              uint32 *ret_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns argument by type. The returned argument has type `type'
+ *    in the Argument Payload. Each argument has their own type (or zero
+ *    if no specific type is set). The length of the argument is returned
+ *    to the `ret_len'. The caller must not free the returned argument.
+ *    Returns NULL on error.
+ *
+ ***/
+unsigned char *silc_argument_get_arg_type(SilcArgumentPayload payload,
+                                         uint32 type,
+                                         uint32 *ret_len);
+
+#endif
diff --git a/lib/silccore/silcauth.c b/lib/silccore/silcauth.c
new file mode 100644 (file)
index 0000000..e66f965
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+
+  silcauth.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcauth.h"
+
+/******************************************************************************
+
+                           Authentication Payload
+
+******************************************************************************/
+
+/* Authentication Payload structure */
+struct SilcAuthPayloadStruct {
+  uint16 len;
+  uint16 auth_method;
+  uint16 random_len;
+  unsigned char *random_data;
+  uint16 auth_len;
+  unsigned char *auth_data;
+};
+
+/* Parses and returns Authentication Payload */
+
+SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
+                                       uint32 data_len)
+{
+  SilcBufferStruct buffer;
+  SilcAuthPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing Authentication Payload"));
+
+  silc_buffer_set(&buffer, (unsigned char *)data, data_len);
+  new = silc_calloc(1, sizeof(*new));
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(&buffer, 
+                            SILC_STR_UI_SHORT(&new->len),
+                            SILC_STR_UI_SHORT(&new->auth_method),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->random_data,
+                                                        &new->random_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->auth_data,
+                                                        &new->auth_len),
+                            SILC_STR_END);
+  if (ret == -1) {
+    silc_free(new);
+    return NULL;
+  }
+
+  if (new->len != buffer.len) {
+    silc_auth_payload_free(new);
+    return NULL;
+  }
+
+  /* If password authentication, random data must not be set */
+  if (new->auth_method == SILC_AUTH_PASSWORD && new->random_len) {
+    silc_auth_payload_free(new);
+    return NULL;
+  }
+
+  return new;
+}
+
+/* Encodes authentication payload into buffer and returns it */
+
+SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
+                                   const unsigned char *random_data,
+                                   uint16 random_len,
+                                   const unsigned char *auth_data,
+                                   uint16 auth_len)
+{
+  SilcBuffer buffer;
+  uint32 len;
+
+  SILC_LOG_DEBUG(("Encoding Authentication Payload"));
+
+  len = 2 + 2 + 2 + random_len + 2 + auth_len;
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(len),
+                    SILC_STR_UI_SHORT(method),
+                    SILC_STR_UI_SHORT(random_len),
+                    SILC_STR_UI_XNSTRING(random_data, random_len),
+                    SILC_STR_UI_SHORT(auth_len),
+                    SILC_STR_UI_XNSTRING(auth_data, auth_len),
+                    SILC_STR_END);
+
+  return buffer;
+}
+
+/* Frees authentication payload. */
+
+void silc_auth_payload_free(SilcAuthPayload payload)
+{
+  if (payload) {
+    if (payload->random_data) {
+      memset(payload->random_data, 0, payload->random_len);
+      silc_free(payload->random_data);
+    }
+    if (payload->auth_data) {
+      memset(payload->auth_data, 0, payload->auth_len);
+      silc_free(payload->auth_data);
+    }
+    silc_free(payload);
+  }
+}
+
+/* Get authentication method */
+
+SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload)
+{
+  return payload->auth_method;
+}
+
+/* Get the authentication data */
+
+unsigned char *silc_auth_get_data(SilcAuthPayload payload,
+                                 uint32 *auth_len)
+{
+  if (auth_len)
+    *auth_len = payload->auth_len;
+
+  return payload->auth_data;
+}
+
+/******************************************************************************
+
+                           Authentication Routines
+
+******************************************************************************/
+
+/* Encodes the authentication data for hashing and signing as the protocol
+   dictates. */
+
+static unsigned char *
+silc_auth_public_key_encode_data(SilcPublicKey public_key, 
+                                const unsigned char *random,
+                                uint32 random_len, const void *id,
+                                SilcIdType type, uint32 *ret_len)
+{
+  SilcBuffer buf;
+  unsigned char *pk, *id_data, *ret;
+  uint32 pk_len, id_len;
+
+  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+  if (!pk)
+    return NULL;
+
+  id_data = silc_id_id2str(id, type);
+  if (!id_data) {
+    silc_free(pk);
+    return NULL;
+  }
+  id_len = silc_id_get_len(id, type);
+
+  buf = silc_buffer_alloc(random_len + id_len + pk_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  silc_buffer_format(buf,
+                    SILC_STR_UI_XNSTRING(random, random_len),
+                    SILC_STR_UI_XNSTRING(id_data, id_len),
+                    SILC_STR_UI_XNSTRING(pk, pk_len),
+                    SILC_STR_END);
+  
+  ret = silc_calloc(buf->len + 1, sizeof(*ret));
+  memcpy(ret, buf->data, buf->len);
+
+  if (ret_len)
+    *ret_len = buf->len;
+
+  silc_buffer_free(buf);
+  silc_free(id_data);
+  silc_free(pk);
+
+  return ret;
+}
+
+/* Generates Authentication Payload with authentication data. This is used
+   to do public key based authentication. This generates the random data
+   and the actual authentication data. Returns NULL on error. */
+
+SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
+                                             SilcPrivateKey private_key,
+                                             SilcHash hash,
+                                             const void *id, SilcIdType type)
+{
+  unsigned char *random;
+  unsigned char auth_data[1024];
+  uint32 auth_len;
+  unsigned char *tmp;
+  uint32 tmp_len;
+  SilcBuffer buf;
+  SilcPKCS pkcs;
+
+  SILC_LOG_DEBUG(("Generating Authentication Payload with data"));
+
+  /* Get 256 bytes of random data */
+  random = silc_rng_global_get_rn_data(256);
+  if (!random)
+    return NULL;
+  
+  /* Encode the auth data */
+  tmp = silc_auth_public_key_encode_data(public_key, random, 256, id, type, 
+                                        &tmp_len);
+  if (!tmp)
+    return NULL;
+
+  /* Allocate PKCS object */
+  if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
+    memset(tmp, 0, tmp_len);
+    silc_free(tmp);
+    return NULL;
+  }
+  silc_pkcs_public_key_set(pkcs, public_key);
+  silc_pkcs_private_key_set(pkcs, private_key);
+
+  /* Compute the hash and the signature. */
+  if (!silc_pkcs_sign_with_hash(pkcs, hash, tmp, tmp_len, auth_data,
+                               &auth_len)) {
+    memset(random, 0, 256);
+    memset(tmp, 0, tmp_len);
+    silc_free(tmp);
+    silc_free(random);
+    silc_pkcs_free(pkcs);
+    return NULL;
+  }
+
+  /* Encode Authentication Payload */
+  buf = silc_auth_payload_encode(SILC_AUTH_PUBLIC_KEY, random, 256,
+                                auth_data, auth_len);
+
+  memset(tmp, 0, tmp_len);
+  memset(auth_data, 0, sizeof(auth_data));
+  memset(random, 0, 256);
+  silc_free(tmp);
+  silc_free(random);
+  silc_pkcs_free(pkcs);
+
+  return buf;
+}
+
+/* Verifies the authentication data. Returns TRUE if authentication was
+   successful. */
+
+bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
+                                     SilcPublicKey public_key, SilcHash hash,
+                                     const void *id, SilcIdType type)
+{
+  unsigned char *tmp;
+  uint32 tmp_len;
+  SilcPKCS pkcs;
+
+  SILC_LOG_DEBUG(("Verifying authentication data"));
+
+  /* Encode auth data */
+  tmp = silc_auth_public_key_encode_data(public_key, payload->random_data, 
+                                        payload->random_len, 
+                                        id, type, &tmp_len);
+  if (!tmp) {
+    SILC_LOG_DEBUG(("Authentication failed"));
+    return FALSE;
+  }
+
+  /* Allocate PKCS object */
+  if (!silc_pkcs_alloc(public_key->name, &pkcs)) {
+    memset(tmp, 0, tmp_len);
+    silc_free(tmp);
+    return FALSE;
+  }
+  silc_pkcs_public_key_set(pkcs, public_key);
+
+  /* Verify the authentication data */
+  if (!silc_pkcs_verify_with_hash(pkcs, hash, payload->auth_data,
+                                 payload->auth_len, tmp, tmp_len)) {
+
+    memset(tmp, 0, tmp_len);
+    silc_free(tmp);
+    silc_pkcs_free(pkcs);
+    SILC_LOG_DEBUG(("Authentication failed"));
+    return FALSE;
+  }
+
+  memset(tmp, 0, tmp_len);
+  silc_free(tmp);
+  silc_pkcs_free(pkcs);
+
+  SILC_LOG_DEBUG(("Authentication successful"));
+
+  return TRUE;
+}
+
+/* Same as above but the payload is not parsed yet. This will parse it. */
+
+bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
+                                          uint32 payload_len,
+                                          SilcPublicKey public_key, 
+                                          SilcHash hash,
+                                          const void *id, SilcIdType type)
+{
+  SilcAuthPayload auth_payload;
+  int ret;
+
+  auth_payload = silc_auth_payload_parse(payload, payload_len);
+  if (!auth_payload) {
+    SILC_LOG_DEBUG(("Authentication failed"));
+    return FALSE;
+  }
+
+  ret = silc_auth_public_key_auth_verify(auth_payload, public_key, hash, 
+                                        id, type);
+
+  silc_auth_payload_free(auth_payload);
+
+  return ret;
+}
+
+/* Verifies the authentication data directly from the Authentication 
+   Payload. Supports all authentication methods. If the authentication
+   method is passphrase based then the `auth_data' and `auth_data_len'
+   are the passphrase and its length. If the method is public key
+   authentication then the `auth_data' is the SilcPublicKey and the
+   `auth_data_len' is ignored. */
+
+bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
+                     const void *auth_data, uint32 auth_data_len, 
+                     SilcHash hash, const void *id, SilcIdType type)
+{
+  SILC_LOG_DEBUG(("Verifying authentication"));
+
+  if (auth_method != payload->auth_method)
+    return FALSE;
+
+  switch (payload->auth_method) {
+  case SILC_AUTH_NONE:
+    /* No authentication */
+    SILC_LOG_DEBUG(("No authentication required"));
+    return TRUE;
+
+  case SILC_AUTH_PASSWORD:
+    /* Passphrase based authentication. The `pkcs', `hash', `id' and `type'
+       arguments are not needed. */
+    if (!memcmp(payload->auth_data, auth_data, auth_data_len)) {
+      SILC_LOG_DEBUG(("Authentication successful"));
+      return TRUE;
+    }
+    break;
+
+  case SILC_AUTH_PUBLIC_KEY:
+    /* Public key based authentication */
+    return silc_auth_public_key_auth_verify(payload, (SilcPublicKey)auth_data,
+                                           hash, id, type);
+    break;
+
+  default:
+    break;
+  }
+
+  SILC_LOG_DEBUG(("Authentication failed"));
+
+  return FALSE;
+}
+
+/* Same as above but parses the authentication payload before verify. */
+
+bool silc_auth_verify_data(const unsigned char *payload, uint32 payload_len,
+                          SilcAuthMethod auth_method, const void *auth_data,
+                          uint32 auth_data_len, SilcHash hash, 
+                          const void *id, SilcIdType type)
+{
+  SilcAuthPayload auth_payload;
+  int ret;
+
+  auth_payload = silc_auth_payload_parse(payload, payload_len);
+  if (!auth_payload)
+    return FALSE;
+
+  ret = silc_auth_verify(auth_payload, auth_method, auth_data, auth_data_len, 
+                        hash, id, type);
+
+  silc_auth_payload_free(auth_payload);
+
+  return ret;
+}
+
+/******************************************************************************
+
+                            Key Agreement Payload
+
+******************************************************************************/
+
+/* The Key Agreement protocol structure */
+struct SilcKeyAgreementPayloadStruct {
+  uint16 hostname_len;
+  unsigned char *hostname;
+  uint32 port;
+};
+
+/* Parses and returns an allocated Key Agreement payload. */
+
+SilcKeyAgreementPayload 
+silc_key_agreement_payload_parse(const unsigned char *payload,
+                                uint32 payload_len)
+{
+  SilcBufferStruct buffer;
+  SilcKeyAgreementPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing Key Agreement Payload"));
+
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
+  new = silc_calloc(1, sizeof(*new));
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(&buffer, 
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->hostname,
+                                                        &new->hostname_len),
+                            SILC_STR_UI_INT(&new->port),
+                            SILC_STR_END);
+  if (ret == -1) {
+    silc_free(new);
+    return NULL;
+  }
+
+  return new;
+}
+
+/* Encodes the Key Agreement protocol and returns the encoded buffer */
+
+SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
+                                            uint32 port)
+{
+  SilcBuffer buffer;
+  uint32 len = hostname ? strlen(hostname) : 0;
+
+  SILC_LOG_DEBUG(("Encoding Key Agreement Payload"));
+
+  buffer = silc_buffer_alloc(2 + len + 4);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(len),
+                    SILC_STR_UI_XNSTRING(hostname, len),
+                    SILC_STR_UI_INT(port),
+                    SILC_STR_END);
+
+  return buffer;
+}
+
+/* Frees the Key Agreement protocol */
+
+void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload)
+{
+  if (payload) {
+    silc_free(payload->hostname);
+    silc_free(payload);
+  }
+}
+
+/* Returns the hostname in the payload */
+
+char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload)
+{
+  return payload->hostname;
+}
+
+/* Returns the port in the payload */
+
+uint32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload)
+{
+  return payload->port;
+}
diff --git a/lib/silccore/silcauth.h b/lib/silccore/silcauth.h
new file mode 100644 (file)
index 0000000..c280dab
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+
+  silcauth.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcAuthAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementations of the Silc Authentication Payload and authentication
+ * routines.  The SILC Authentication Payload is used to deliver 
+ * authentication data usually from client to server in purpose of 
+ * gaining access to some service.  The Payload and the authentication
+ * routines supports both passphrase and public key (signature) based
+ * authentication.
+ *
+ * This interface defines also the SILC Key Agreement Payload that is
+ * used by client to agree on key material usually with another client
+ * in the network.
+ *
+ ***/
+
+#ifndef SILCAUTH_H
+#define SILCAUTH_H
+
+/****s* silccore/SilcAuthAPI/SilcAuthPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcAuthPayloadStruct *SilcAuthPayload; 
+ *
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Authentication Payload and is allocated
+ *    by silc_auth_payload_parse and given as argument usually to all
+ *    silc_auth_payload_* functions.  It is freed by silc_auth_payload_free
+ *    function.
+ *
+ ***/
+typedef struct SilcAuthPayloadStruct *SilcAuthPayload;
+
+/****s* silccore/SilcAuthAPI/SilcKeyAgreementPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcKeyAgreementPayloadStruct *SilcKeyAgreementPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Key Agreement Payload and is allocated
+ *    by silc_key_agreement_payload_parse and given as argument usually to all
+ *    silc_key_agreement_* functions.  It is freed by the function
+ *    silc_key_agreement_payload_free.
+ *
+ ***/
+typedef struct SilcKeyAgreementPayloadStruct *SilcKeyAgreementPayload;
+
+/****d* silccore/SilcAuthAPI/SilcAuthMethod
+ *
+ * NAME
+ * 
+ *    typedef uint16 SilcAuthMethod;
+ *
+ * DESCRIPTION
+ *
+ *    Authentication method type definition, the authentication methods
+ *    and the authentication status'.  The status defines are used by
+ *    all authentication protocols in the SILC.
+ *
+ * SOURCE
+ */
+typedef uint16 SilcAuthMethod;
+
+#define SILC_AUTH_NONE        0                   /* No authentication */
+#define SILC_AUTH_PASSWORD    1                   /* Passphrase authentication */
+#define SILC_AUTH_PUBLIC_KEY  2                   /* Public key authentication */
+
+/* Authentication protocol status message (used by all authentication
+   protocols in the SILC). */
+#define SILC_AUTH_OK          0
+#define SILC_AUTH_FAILED      1
+/***/
+
+/* Prototypes */
+
+/****f* silccore/SilcAuthAPI/silc_auth_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
+ *                                            uint32 data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses and returns Authentication Payload.  The `data' and the
+ *    `data_len' are the raw payload buffer.
+ *
+ ***/
+SilcAuthPayload silc_auth_payload_parse(const unsigned char *data,
+                                       uint32 data_len);
+
+/****f* silccore/SilcAuthAPI/silc_auth_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
+ *                                        const unsigned char *random_data,
+ *                                        uint16 random_len,
+ *                                        const unsigned char *auth_data,
+ *                                        uint16 auth_len);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes authentication payload into buffer and returns it.
+ *    The `random_data' is provided only if doing public key authentication.
+ *    The `auth_data' is the actual authentication data.
+ *
+ ***/
+SilcBuffer silc_auth_payload_encode(SilcAuthMethod method,
+                                   const unsigned char *random_data,
+                                   uint16 random_len,
+                                   const unsigned char *auth_data,
+                                   uint16 auth_len);
+
+/****f* silccore/SilcAuthAPI/silc_auth_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_auth_payload_free(SilcAuthPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees authentication payload and all data in it.
+ *
+ ***/
+void silc_auth_payload_free(SilcAuthPayload payload);
+
+/****f* silccore/SilcAuthAPI/silc_auth_get_method
+ *
+ * SYNOPSIS
+ *
+ *    SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Get authentication method.
+ *
+ ***/
+SilcAuthMethod silc_auth_get_method(SilcAuthPayload payload);
+
+/****f* silccore/SilcAuthAPI/silc_auth_get_data
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_auth_get_data(SilcAuthPayload payload,
+ *                                      uint32 *auth_len);
+ *
+ * DESCRIPTION
+ *
+ *    Get the authentication data. The caller must not free the data.
+ *
+ ***/
+unsigned char *silc_auth_get_data(SilcAuthPayload payload,
+                                 uint32 *auth_len);
+
+/****f* silccore/SilcAuthAPI/silc_auth_public_key_auth_generate
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
+ *                                                  SilcPrivateKey private_key,
+ *                                                  SilcHash hash,
+ *                                                  const void *id, 
+ *                                                  SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Generates Authentication Payload with authentication data. This is used
+ *    to do public key based authentication. This generates the random data
+ *    and the actual authentication data. Returns NULL on error and the
+ *    encoded Authentication Payload on success.
+ *
+ ***/
+SilcBuffer silc_auth_public_key_auth_generate(SilcPublicKey public_key,
+                                             SilcPrivateKey private_key,
+                                             SilcHash hash,
+                                             const void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_public_key_auth_verify
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
+ *                                          SilcPublicKey public_key, 
+ *                                          SilcHash hash,
+ *                                          const void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Verifies the authentication data. Returns TRUE if authentication was
+ *    successful.
+ *
+ ***/
+bool silc_auth_public_key_auth_verify(SilcAuthPayload payload,
+                                     SilcPublicKey public_key, SilcHash hash,
+                                     const void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_public_key_auth_verify_data
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
+ *                                               uint32 payload_len,
+ *                                               SilcPublicKey public_key, 
+ *                                               SilcHash hash,
+ *                                               const void *id, 
+ *                                               SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_auth_public_key_auth_verify but the payload has not
+ *    been parsed yet.  This will parse it.  Returns TRUE if authentication
+ *    was successful.
+ *
+ ***/
+bool silc_auth_public_key_auth_verify_data(const unsigned char *payload,
+                                          uint32 payload_len,
+                                          SilcPublicKey public_key, 
+                                          SilcHash hash,
+                                          const void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_verify
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_auth_verify(SilcAuthPayload payload, 
+ *                          SilcAuthMethod auth_method,
+ *                          const void *auth_data, uint32 auth_data_len, 
+ *                          SilcHash hash, const void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Verifies the authentication data directly from the Authentication 
+ *    Payload. Supports all authentication methods. If the authentication
+ *    method is passphrase based then the `auth_data' and `auth_data_len'
+ *    are the passphrase and its length. If the method is public key
+ *    authentication then the `auth_data' is the SilcPublicKey and the
+ *    `auth_data_len' is ignored.
+ *
+ ***/
+bool silc_auth_verify(SilcAuthPayload payload, SilcAuthMethod auth_method,
+                     const void *auth_data, uint32 auth_data_len, 
+                     SilcHash hash, const void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_auth_verify_data
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_auth_verify_data(const unsigned char *payload, 
+ *                               uint32 payload_len,
+ *                               SilcAuthMethod auth_method, 
+ *                               const void *auth_data,
+ *                               uint32 auth_data_len, SilcHash hash, 
+ *                               const void *id, SilcIdType type);
+ * 
+ * DESCRIPTION
+ *
+ *    Same as silc_auth_verify but the payload has not been parsed yet.
+ *    Verifies the authentication data directly from the Authentication 
+ *    Payload. Supports all authentication methods. If the authentication
+ *    method is passphrase based then the `auth_data' and `auth_data_len'
+ *    are the passphrase and its length. If the method is public key
+ *    authentication then the `auth_data' is the SilcPublicKey and the
+ *    `auth_data_len' is ignored.
+ *
+ ***/
+bool silc_auth_verify_data(const unsigned char *payload, uint32 payload_len,
+                          SilcAuthMethod auth_method, const void *auth_data,
+                          uint32 auth_data_len, SilcHash hash, 
+                          const void *id, SilcIdType type);
+
+/****f* silccore/SilcAuthAPI/silc_key_agreement_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcKeyAgreementPayload 
+ *    silc_key_agreement_payload_parse(const unsigned char *payload,
+ *                                     uint32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses and returns an allocated Key Agreement payload.
+ *
+ ***/
+SilcKeyAgreementPayload 
+silc_key_agreement_payload_parse(const unsigned char *payload,
+                                uint32 payload_len);
+
+/****f* silccore/SilcAuthAPI/silc_key_agreement_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_key_agreement_payload_encode(char *hostname,
+ *                                                 uint32 port);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes the Key Agreement protocol and returns the encoded buffer
+ *
+ ***/
+SilcBuffer silc_key_agreement_payload_encode(const char *hostname,
+                                            uint32 port);
+
+/****f* silccore/SilcAuthAPI/silc_key_agreement_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the Key Agreement protocol and all data in it.
+ *
+ ***/
+void silc_key_agreement_payload_free(SilcKeyAgreementPayload payload);
+
+/****f* silccore/SilcAuthAPI/silc_key_agreement_get_hostname
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the hostname in the payload. Caller must not free it.
+ *    The hostname is the host that is able to accept key negotiation
+ *    using the SILC Key Exchange protocol.
+ *
+ ***/
+char *silc_key_agreement_get_hostname(SilcKeyAgreementPayload payload);
+
+/****f* silccore/SilcAuthAPI/silc_key_agreement_get_port
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the port in the payload.  The port is the port on the
+ *    host returned by silc_key_agreement_get_hostname that is running
+ *    the SILC Key Exchange protocol.
+ *
+ ***/
+uint32 silc_key_agreement_get_port(SilcKeyAgreementPayload payload);
+
+#endif
diff --git a/lib/silccore/silcbuffer.c b/lib/silccore/silcbuffer.c
deleted file mode 100644 (file)
index 7559f59..0000000
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
-
-  silcbuffer.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1998 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-#include "silcbuffer.h"
-
-static unsigned char *silc_buffer_pull_i(SilcBuffer sb, unsigned int len)
-{
-  return silc_buffer_pull(sb, len);
-}
-
-static unsigned char *silc_buffer_push_i(SilcBuffer sb, unsigned int len)
-{
-  return silc_buffer_push(sb, len);
-}
-
-static unsigned char *silc_buffer_pull_tail_i(SilcBuffer sb, unsigned int len)
-{
-  return silc_buffer_pull_tail(sb, len);
-}
-
-static unsigned char *silc_buffer_push_tail_i(SilcBuffer sb, unsigned int len)
-{
-  return silc_buffer_push_tail(sb, len);
-}
-
-static unsigned char *silc_buffer_put_head_i(SilcBuffer sb, 
-                                            unsigned char *data,
-                                            unsigned int len)
-{
-  return silc_buffer_put_head(sb, data, len);
-}
-
-static unsigned char *silc_buffer_put_i(SilcBuffer sb, 
-                                       unsigned char *data,
-                                       unsigned int len)
-{
-  return silc_buffer_put(sb, data, len);
-}
-
-static unsigned char *silc_buffer_put_tail_i(SilcBuffer sb, 
-                                            unsigned char *data,
-                                            unsigned int len)
-{
-  return silc_buffer_put_tail(sb, data, len);
-}
-
-/* Allocates a new SilcBuffer and returns a pointer to it. The data
-   area of the new buffer is set to the real beginning of the buffer. 
-
-   Buffer after allocation:
-   ---------------------------------
-   |                               |
-   ---------------------------------
-   ^ head, data, tail              ^ end
-
-*/
-
-SilcBuffer silc_buffer_alloc(unsigned int len)
-{
-  SilcBuffer sb;
-  unsigned char *data;
-
-  /* Allocate new SilcBuffer */
-  sb = silc_calloc(1, sizeof(*sb));
-  if (!sb)
-    return NULL;
-
-  /* Allocate the actual data area */
-  data = silc_malloc(len);
-  if (!data)
-    return NULL;
-  memset(data, 0, len);
-
-  /* Set pointers to the new buffer */
-  sb->truelen = len;
-  sb->len = 0;
-  sb->head = data;
-  sb->data = data;
-  sb->tail = data;
-  sb->end = data + sb->truelen;
-
-  /* Set the function pointers */
-  sb->pull = silc_buffer_pull_i;
-  sb->push = silc_buffer_push_i;
-  sb->pull_tail = silc_buffer_pull_tail_i;
-  sb->push_tail = silc_buffer_push_tail_i;
-  sb->put = silc_buffer_put_i;
-  sb->put_head = silc_buffer_put_head_i;
-  sb->put_tail = silc_buffer_put_tail_i;
-
-  return sb;
-}
-
-/* Free's a SilcBuffer */
-
-void silc_buffer_free(SilcBuffer sb)
-{
-  if (sb) {
-    memset(sb->head, 'F', sb->truelen);
-    silc_free(sb->head);
-    silc_free(sb);
-  }
-}
-
-#ifdef SILC_DEBUG              /* If we are doing debugging we won't
-                                  have the optimized inline buffer functions
-                                  available as optimization is not set
-                                  to compiler. These normal routines are
-                                  used in debugging mode. */
-
-/* XXX These are currenly obsolte as SILC is compiled always with -O
-   flag thus inline functions maybe used. So, fix these. */
-
-/* Pulls current data area towards end. The length of the currently
-   valid data area is also decremented. Returns pointer to the data
-   area before pulling. 
-
-   Example:
-   ---------------------------------
-   | head  | data       | tail     |
-   ---------------------------------
-           ^
-           Pulls the start of the data area.
-
-   ---------------------------------
-   | head     | data    | tail     |
-   ---------------------------------
-           ^
-*/
-
-unsigned char *silc_buffer_pull(SilcBuffer sb, unsigned int len)
-{
-  unsigned char *old_data = sb->data;
-
-  assert(len <= (sb->tail - sb->data));
-
-  sb->data += len;
-  sb->len -= len;
-
-  return old_data;
-}
-
-/* Pushes current data area towards beginning. Length of the currently
-   valid data area is also incremented. Returns a pointer to the 
-   data area before pushing. 
-
-   Example:
-   ---------------------------------
-   | head     | data    | tail     |
-   ---------------------------------
-              ^
-              Pushes the start of the data area.
-
-   ---------------------------------
-   | head  | data       | tail     |
-   ---------------------------------
-              ^
-*/
-
-unsigned char *silc_buffer_push(SilcBuffer sb, unsigned int len)
-{
-  unsigned char *old_data = sb->data;
-
-  assert((sb->data - len) >= sb->head);
-
-  sb->data -= len;
-  sb->len += len;
-
-  return old_data;
-}
-
-/* Pulls current tail section towards end. Length of the current valid
-   data area is also incremented. Returns a pointer to the data area 
-   before pulling.
-
-   Example:
-   ---------------------------------
-   | head  | data       | tail     |
-   ---------------------------------
-                        ^
-                        Pulls the start of the tail section.
-
-   ---------------------------------
-   | head  | data           | tail |
-   ---------------------------------
-                        ^
-*/
-
-unsigned char *silc_buffer_pull_tail(SilcBuffer sb, unsigned int len)
-{
-  unsigned char *old_tail = sb->tail;
-
-  assert((sb->end - sb->tail) >= len);
-
-  sb->tail += len;
-  sb->len += len;
-
-  return old_tail;
-}
-
-/* Pushes current tail section towards beginning. Length of the current
-   valid data area is also decremented. Returns a pointer to the 
-   tail section before pushing. 
-
-   Example:
-   ---------------------------------
-   | head  | data           | tail |
-   ---------------------------------
-                            ^
-                            Pushes the start of the tail section.
-
-   ---------------------------------
-   | head  | data       | tail     |
-   ---------------------------------
-                            ^
-*/
-
-unsigned char *silc_buffer_push_tail(SilcBuffer sb, unsigned int len)
-{
-  unsigned char *old_tail = sb->tail;
-
-  assert((sb->tail - len) >= sb->data);
-
-  sb->tail -= len;
-  sb->len -= len;
-
-  return old_tail;
-}
-
-/* Puts data at the head of the buffer. Returns pointer to the copied
-   data area. 
-   
-   Example:
-   ---------------------------------
-   | head  | data       | tail     |
-   ---------------------------------
-   ^
-   Puts data to the head section. 
-*/
-
-unsigned char *silc_buffer_put_head(SilcBuffer sb, 
-                                   unsigned char *data,
-                                   unsigned int len)
-{
-  assert((sb->data - sb->head) >= len);
-  return memcpy(sb->head, data, len);
-}
-
-/* Puts data at the start of the valid data area. Returns a pointer 
-   to the copied data area. 
-
-   Example:
-   ---------------------------------
-   | head  | data       | tail     |
-   ---------------------------------
-           ^
-           Puts data to the data section.
-*/
-
-unsigned char *silc_buffer_put(SilcBuffer sb, 
-                              unsigned char *data,
-                              unsigned int len)
-{
-  assert((sb->tail - sb->data) >= len);
-  return memcpy(sb->data, data, len);
-}
-
-/* Puts data at the tail of the buffer. Returns pointer to the copied
-   data area. 
-
-   Example:
-   ---------------------------------
-   | head  | data           | tail |
-   ---------------------------------
-                            ^
-                           Puts data to the tail section.
-*/
-
-unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
-                                   unsigned char *data,
-                                   unsigned int len)
-{
-  assert((sb->end - sb->tail) >= len);
-  return memcpy(sb->tail, data, len);
-}
-
-#endif
diff --git a/lib/silccore/silcbufutil.c b/lib/silccore/silcbufutil.c
deleted file mode 100644 (file)
index f424248..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-
-  silcbufutil.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-#ifdef SILC_DEBUG              /* If we are doing debugging we won't
-                                  have the optimized inline buffer functions
-                                  available as optimization is not set
-                                  to compiler. These normal routines are
-                                  used in debugging mode. */
-
-/* Clears and initialiazes the buffer to the state as if it was just
-   allocated by silc_buffer_alloc. */
-
-void silc_buffer_clear(SilcBuffer sb)
-{
-  memset(sb->head, 0, sb->truelen);
-  sb->data = sb->head;
-  sb->tail = sb->head;
-  sb->len = 0;
-}
-
-/* Generates copy of a SilcBuffer. This copies everything inside the
-   currently valid data area, nothing more. Use silc_buffer_clone to
-   copy entire buffer. */
-
-SilcBuffer silc_buffer_copy(SilcBuffer sb)
-{
-  SilcBuffer sb_new;
-
-  sb_new = silc_buffer_alloc(sb->len);
-  silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
-  silc_buffer_put(sb_new, sb->data, sb->len);
-
-  return sb_new;
-}
-
-/* Clones SilcBuffer. This generates new SilcBuffer and copies
-   everything from the source buffer. The result is exact clone of
-   the original buffer. */
-
-SilcBuffer silc_buffer_clone(SilcBuffer sb)
-{
-  SilcBuffer sb_new;
-
-  sb_new = silc_buffer_alloc(sb->truelen);
-  silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
-  silc_buffer_put(sb_new, sb->head, sb->truelen);
-  sb_new->data = sb_new->head + sb->len;
-  sb_new->tail = sb_new->head + (sb->end - sb->tail);
-  sb_new->len = sb->len;
-
-  return sb_new;
-}
-
-#endif /* SILC_DEBUG */
index bb183cb0870499fccf7ce9661d1be44305821885..fef7a197cd482787af40d732e44fa6e53cc1c35d 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* Channel Payload, Channel Message Payload and Channel Key Payload 
+   implementations. */
+/* $Id$ */
 
 #include "silcincludes.h"
 #include "silcchannel.h"
 
-/* Channel Payload structure. Contents of this structure is parsed
+/******************************************************************************
+
+                              Channel Payload
+
+******************************************************************************/
+
+/* Channel Message Payload structure. Contents of this structure is parsed
    from SILC packets. */
 struct SilcChannelPayloadStruct {
-  unsigned short nick_len;
-  unsigned char *nick;
-  unsigned short data_len;
-  unsigned char *data;
-  unsigned short iv_len;
-  unsigned char *iv;
+  uint16 name_len;
+  unsigned char *channel_name;
+  uint16 id_len;
+  unsigned char *channel_id;
+  uint32 mode;
 };
 
-/* Channel Key Payload structrue. Channel keys are parsed from SILC
-   packets into this structure. */
-struct SilcChannelKeyPayloadStruct {
-  unsigned short id_len;
-  unsigned char *id;
-  unsigned short cipher_len;
-  unsigned char *cipher;
-  unsigned short key_len;
-  unsigned char *key;
-};
+/* Parses channel payload returning new channel payload structure. */
 
-/* Parses channel payload returning new channel payload structure */
-
-SilcChannelPayload silc_channel_parse_payload(SilcBuffer buffer)
+SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload,
+                                             uint32 payload_len)
 {
+  SilcBufferStruct buffer;
   SilcChannelPayload new;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing channel payload"));
 
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
   new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new channel payload"));
-    return NULL;
-  }
 
-  /* Parse the Channel Payload. Ignore padding and IV, we don't need
-     them. */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_NSTRING_ALLOC(&new->nick, &new->nick_len),
-                      SILC_STR_UI16_NSTRING_ALLOC(&new->data, &new->data_len),
-                      SILC_STR_UI16_NSTRING_ALLOC(NULL, NULL),
-                      SILC_STR_END);
+  /* Parse the Channel Payload. Ignore the padding. */
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
+                                                        &new->name_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
+                                                        &new->id_len),
+                            SILC_STR_UI_INT(&new->mode),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
 
-  if (new->data_len < 1) {
+  if ((new->name_len < 1 || new->name_len > buffer.len) ||
+      (new->id_len < 1 || new->id_len > buffer.len)) {
     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
     goto err;
   }
@@ -81,96 +74,369 @@ SilcChannelPayload silc_channel_parse_payload(SilcBuffer buffer)
   return new;
 
  err:
-  if (new->nick)
-    silc_free(new->nick);
-  if (new->data)
-    silc_free(new->data);
-  if (new->iv)
-    silc_free(new->iv);
-  silc_free(new);
+  silc_channel_payload_free(new);
   return NULL;
 }
 
-/* Encodes channel payload into a buffer and returns it. This is used 
-   to add channel payload into a packet. As the channel payload is
+/* Parses list of channel payloads returning list of payloads. */
+
+SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
+                                         uint32 payload_len)
+{
+  SilcBufferStruct buffer;
+  SilcDList list;
+  SilcChannelPayload new;
+  int len, ret;
+
+  SILC_LOG_DEBUG(("Parsing channel payload list"));
+
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
+  list = silc_dlist_init();
+
+  while (buffer.len) {
+    new = silc_calloc(1, sizeof(*new));
+    ret = silc_buffer_unformat(&buffer,
+                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
+                                                          &new->name_len),
+                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
+                                                          &new->id_len),
+                              SILC_STR_UI_INT(&new->mode),
+                              SILC_STR_END);
+    if (ret == -1)
+      goto err;
+
+    if ((new->name_len < 1 || new->name_len > buffer.len) ||
+       (new->id_len < 1 || new->id_len > buffer.len)) {
+      SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
+      goto err;
+    }
+
+    len = 2 + new->name_len + 2 + new->id_len + 4;
+    if (buffer.len < len)
+      break;
+    silc_buffer_pull(&buffer, len);
+
+    silc_dlist_add(list, new);
+  }
+  
+  return list;
+
+ err:
+  silc_channel_payload_list_free(list);
+  return NULL;
+}
+
+/* Encode new channel payload and returns it as buffer. */
+
+SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
+                                      uint16 channel_name_len,
+                                      const unsigned char *channel_id,
+                                      uint32 channel_id_len,
+                                      uint32 mode)
+{
+  SilcBuffer buffer;
+
+  SILC_LOG_DEBUG(("Encoding message payload"));
+
+  buffer = silc_buffer_alloc(2 + channel_name_len + 2 + channel_id_len + 4);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  /* Encode the Channel Payload */
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_SHORT(channel_name_len),
+                    SILC_STR_UI_XNSTRING(channel_name, channel_name_len),
+                    SILC_STR_UI_SHORT(channel_id_len),
+                    SILC_STR_UI_XNSTRING(channel_id, channel_id_len),
+                    SILC_STR_UI_INT(mode),
+                    SILC_STR_END);
+
+  return buffer;
+}
+
+/* Frees Channel Payload */
+
+void silc_channel_payload_free(SilcChannelPayload payload)
+{
+  silc_free(payload->channel_name);
+  silc_free(payload->channel_id);
+  silc_free(payload);
+}
+
+/* Free's list of Channel Payloads */
+
+void silc_channel_payload_list_free(SilcDList list)
+{
+  SilcChannelPayload entry;
+
+  silc_dlist_start(list);
+  while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+    silc_free(entry->channel_name);
+    silc_free(entry->channel_id);
+    silc_free(entry);
+    silc_dlist_del(list, entry);
+  }
+
+  silc_dlist_uninit(list);
+}
+
+/* Return the channel name */
+
+unsigned char *silc_channel_get_name(SilcChannelPayload payload,
+                                    uint32 *channel_name_len)
+{
+  if (channel_name_len)
+    *channel_name_len = payload->name_len;
+
+  return payload->channel_name;
+}
+
+/* Return the channel ID */
+
+unsigned char *silc_channel_get_id(SilcChannelPayload payload,
+                                  uint32 *channel_id_len)
+{
+  if (channel_id_len)
+    *channel_id_len = payload->id_len;
+
+  return payload->channel_id;
+}
+
+/* Return the channel ID as parsed ID. */
+
+SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload)
+{
+  return silc_id_str2id(payload->channel_id, payload->id_len,
+                       SILC_ID_CHANNEL);
+}
+
+/* Return the mode. The mode is arbitrary. It can be the mode of the
+   channel or perhaps the mode of the client on the channel.  The protocol
+   dictates what the usage of the mode is in different circumstances. */
+
+uint32 silc_channel_get_mode(SilcChannelPayload payload)
+{
+  return payload->mode;
+}
+
+/******************************************************************************
+
+                          Channel Message Payload
+
+******************************************************************************/
+
+#define SILC_CHANNEL_MESSAGE_PAD(__payloadlen) (16 - (__payloadlen) % 16)
+
+/* Channel Message Payload structure. Contents of this structure is parsed
+   from SILC packets. */
+struct SilcChannelMessagePayloadStruct {
+  SilcMessageFlags flags;
+  uint16 data_len;
+  unsigned char *data;
+  unsigned char *mac;
+  unsigned char *iv;
+};
+
+/* Decrypts the channel message payload. First push the IV out of the
+   packet. The IV is used in the decryption process. Then decrypt the
+   message. After decyprtion, take the MAC from the decrypted packet, 
+   compute MAC and compare the MACs.  If they match, the decryption was
+   successful and we have the channel message ready to be displayed. */
+
+bool silc_channel_message_payload_decrypt(unsigned char *data,
+                                         size_t data_len,
+                                         SilcCipher cipher,
+                                         SilcHmac hmac)
+{
+  uint32 iv_len, mac_len;
+  unsigned char *end, *mac, mac2[32];
+  unsigned char *dst, iv[SILC_CIPHER_MAX_IV_SIZE];
+
+  /* Push the IV out of the packet, and copy the IV since we do not want
+     to modify the original data buffer. */
+  end = data + data_len;
+  iv_len = silc_cipher_get_block_len(cipher);
+  memcpy(iv, end - iv_len, iv_len);
+
+  /* Allocate destination decryption buffer since we do not want to modify
+     the original data buffer, since we might want to call this function 
+     many times for same payload. */
+  if (hmac)
+    dst = silc_calloc(data_len - iv_len, sizeof(*dst));
+  else
+    dst = data;
+
+  /* Decrypt the channel message */
+  silc_cipher_decrypt(cipher, data, dst, data_len - iv_len, iv);
+
+  if (hmac) {
+    /* Take the MAC */
+    end = dst + (data_len - iv_len);
+    mac_len = silc_hmac_len(hmac);
+    mac = (end - mac_len);
+
+    /* Check the MAC of the message */
+    SILC_LOG_DEBUG(("Checking channel message MACs"));
+    silc_hmac_make(hmac, dst, (data_len - iv_len - mac_len), mac2, &mac_len);
+    if (memcmp(mac, mac2, mac_len)) {
+      SILC_LOG_DEBUG(("Channel message MACs does not match"));
+      silc_free(dst);
+      return FALSE;
+    }
+    SILC_LOG_DEBUG(("MAC is Ok"));
+
+    /* Now copy the decrypted data into the buffer since it is verified
+       it decrypted correctly. */
+    memcpy(data, dst, data_len - iv_len);
+    memset(dst, 0, data_len - iv_len);
+    silc_free(dst);
+  }
+
+  return TRUE;
+}
+
+/* Parses channel message payload returning new channel payload structure.
+   This also decrypts it and checks the MAC. */
+
+SilcChannelMessagePayload 
+silc_channel_message_payload_parse(unsigned char *payload,
+                                  uint32 payload_len,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac)
+{
+  SilcBufferStruct buffer;
+  SilcChannelMessagePayload new;
+  int ret;
+  uint32 iv_len, mac_len;
+
+  SILC_LOG_DEBUG(("Parsing channel message payload"));
+
+  silc_buffer_set(&buffer, payload, payload_len);
+
+  /* Decrypt the payload */
+  ret = silc_channel_message_payload_decrypt(buffer.data, buffer.len,
+                                            cipher, hmac);
+  if (ret == FALSE)
+    return NULL;
+
+  iv_len = silc_cipher_get_block_len(cipher);
+  mac_len = silc_hmac_len(hmac);
+
+  new = silc_calloc(1, sizeof(*new));
+
+  /* Parse the Channel Message Payload. Ignore the padding. */
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&new->flags),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->data, 
+                                                        &new->data_len),
+                            SILC_STR_UI16_NSTRING(NULL, NULL),
+                            SILC_STR_UI_XNSTRING(&new->mac, mac_len),
+                            SILC_STR_UI_XNSTRING(&new->iv, iv_len),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  if (new->data_len > buffer.len) {
+    SILC_LOG_ERROR(("Incorrect channel message payload in packet, "
+                   "packet dropped"));
+    goto err;
+  }
+
+  return new;
+
+ err:
+  silc_channel_message_payload_free(new);
+  return NULL;
+}
+
+/* Encodes channel message payload into a buffer and returns it. This is used 
+   to add channel message payload into a packet. As the channel payload is
    encrypted separately from other parts of the packet padding must
    be applied to the payload. */
 
-SilcBuffer silc_channel_encode_payload(unsigned short nick_len,
-                                      unsigned char *nick,
-                                      unsigned short data_len,
-                                      unsigned char *data,
-                                      unsigned short iv_len,
-                                      unsigned char *iv,
-                                      SilcRng rng)
+SilcBuffer silc_channel_message_payload_encode(uint16 flags,
+                                              uint16 data_len,
+                                              const unsigned char *data,
+                                              uint16 iv_len,
+                                              unsigned char *iv,
+                                              SilcCipher cipher,
+                                              SilcHmac hmac)
 {
   int i;
   SilcBuffer buffer;
-  unsigned int len, pad_len;
-  unsigned char pad[SILC_PACKET_MAX_PADLEN];
+  uint32 len, pad_len, mac_len;
+  unsigned char pad[16];
+  unsigned char mac[32];
 
-  SILC_LOG_DEBUG(("Encoding channel payload"));
+  SILC_LOG_DEBUG(("Encoding channel message payload"));
 
   /* Calculate length of padding. IV is not included into the calculation
      since it is not encrypted. */
-  len = 2 + nick_len + 2 + data_len + 2;
-  pad_len = SILC_PACKET_PADLEN((len + 2));
+  mac_len = silc_hmac_len(hmac);
+  len = 6 + data_len + mac_len;
+  pad_len = SILC_CHANNEL_MESSAGE_PAD(len);
 
   /* Allocate channel payload buffer */
-  len += pad_len;
-  buffer = silc_buffer_alloc(len + iv_len);
-  if (!buffer)
-    return NULL;
+  len += pad_len + iv_len;
+  buffer = silc_buffer_alloc(len);
 
   /* Generate padding */
-  for (i = 0; i < pad_len; i++)
-    pad[i] = silc_rng_get_byte(rng);
+  for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
 
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
-
-  /* Encode the Channel Payload */
+  /* Encode the Channel Message Payload */
+  silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
   silc_buffer_format(buffer, 
-                    SILC_STR_UI_SHORT(nick_len),
-                    SILC_STR_UI_XNSTRING(nick, nick_len),
+                    SILC_STR_UI_SHORT(flags),
                     SILC_STR_UI_SHORT(data_len),
                     SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_UI_SHORT(pad_len),
                     SILC_STR_UI_XNSTRING(pad, pad_len),
+                    SILC_STR_END);
+
+  /* Compute the MAC of the channel message data */
+  silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
+
+  /* Put rest of the data to the payload */
+  silc_buffer_pull_tail(buffer, mac_len + iv_len);
+  silc_buffer_pull(buffer, 6 + data_len + pad_len);
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_XNSTRING(mac, mac_len),
                     SILC_STR_UI_XNSTRING(iv, iv_len),
                     SILC_STR_END);
+  silc_buffer_push(buffer, 6 + data_len + pad_len);
+
+  /* Encrypt payload of the packet. This is encrypted with the channel key. */
+  silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
+                     buffer->len - iv_len, iv);
+
+  memset(pad, 0, sizeof(pad));
+  memset(mac, 0, sizeof(mac));
 
-  memset(pad, 0, pad_len);
   return buffer;
 }
 
-/* Free's Channel Payload */
+/* Free's Channel Message Payload */
 
-void silc_channel_free_payload(SilcChannelPayload payload)
+void silc_channel_message_payload_free(SilcChannelMessagePayload payload)
 {
-  if (payload) {
-    if (payload->data)
-      silc_free(payload->data);
-    if (payload->iv)
-      silc_free(payload->iv);
-    silc_free(payload);
+  if (payload->data) {
+    memset(payload->data, 0, payload->data_len);
+    silc_free(payload->data);
   }
+  silc_free(payload);
 }
 
-/* Return nickname */
+/* Return flags */
 
-unsigned char *silc_channel_get_nickname(SilcChannelPayload payload,
-                                        unsigned int *nick_len)
+SilcMessageFlags
+silc_channel_message_get_flags(SilcChannelMessagePayload payload)
 {
-  if (nick_len)
-    *nick_len = payload->nick_len;
-
-  return payload->nick;
+  return payload->flags;
 }
 
 /* Return data */
 
-unsigned char *silc_channel_get_data(SilcChannelPayload payload,
-                                    unsigned int *data_len)
+unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload,
+                                            uint32 *data_len)
 {
   if (data_len)
     *data_len = payload->data_len;
@@ -178,38 +444,62 @@ unsigned char *silc_channel_get_data(SilcChannelPayload payload,
   return payload->data;
 }
 
-/* Return initial vector */
+/* Return MAC. The caller knows the length of the MAC */
 
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload,
-                                  unsigned int *iv_len)
+unsigned char *silc_channel_message_get_mac(SilcChannelMessagePayload payload)
 {
-  if (iv_len)
-    *iv_len = payload->iv_len;
+  return payload->mac;
+}
+
+/* Return IV. The caller knows the length of the IV */
 
+unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload)
+{
   return payload->iv;
 }
 
+/******************************************************************************
+
+                             Channel Key Payload
+
+******************************************************************************/
+
+/* Channel Key Payload structrue. Channel keys are parsed from SILC
+   packets into this structure. */
+struct SilcChannelKeyPayloadStruct {
+  uint16 id_len;
+  unsigned char *id;
+  uint16 cipher_len;
+  unsigned char *cipher;
+  uint16 key_len;
+  unsigned char *key;
+};
+
 /* Parses channel key payload returning new channel key payload structure */
 
-SilcChannelKeyPayload silc_channel_key_parse_payload(SilcBuffer buffer)
+SilcChannelKeyPayload 
+silc_channel_key_payload_parse(const unsigned char *payload,
+                              uint32 payload_len)
 {
+  SilcBufferStruct buffer;
   SilcChannelKeyPayload new;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing channel key payload"));
 
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
   new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new channel key payload"));
-    return NULL;
-  }
 
   /* Parse the Channel Key Payload */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI16_NSTRING_ALLOC(&new->id, &new->id_len),
-                      SILC_STR_UI16_NSTRING_ALLOC(&new->cipher, 
-                                                  &new->cipher_len),
-                      SILC_STR_UI16_NSTRING_ALLOC(&new->key, &new->key_len),
-                      SILC_STR_END);
+  ret =
+    silc_buffer_unformat(&buffer,
+                        SILC_STR_UI16_NSTRING_ALLOC(&new->id, &new->id_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&new->cipher, 
+                                                    &new->cipher_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&new->key, &new->key_len),
+                        SILC_STR_END);
+  if (ret == -1)
+    goto err;
 
   if (new->id_len < 1 || new->key_len < 1 || new->cipher_len < 1) {
     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
@@ -232,28 +522,22 @@ SilcChannelKeyPayload silc_channel_key_parse_payload(SilcBuffer buffer)
 /* Encodes channel key payload into a buffer and returns it. This is used 
    to add channel key payload into a packet. */
 
-SilcBuffer silc_channel_key_encode_payload(unsigned short id_len,
-                                          unsigned char *id,
-                                          unsigned short cipher_len,
-                                          unsigned char *cipher,
-                                          unsigned short key_len,
-                                          unsigned char *key)
+SilcBuffer silc_channel_key_payload_encode(uint16 id_len,
+                                          const unsigned char *id,
+                                          uint16 cipher_len,
+                                          const unsigned char *cipher,
+                                          uint16 key_len,
+                                          const unsigned char *key)
 {
   SilcBuffer buffer;
-  unsigned int len;
+  uint32 len;
 
   SILC_LOG_DEBUG(("Encoding channel key payload"));
 
-  /* Sanity checks */
-  if (!id_len || !key_len || !id || !key || !cipher_len || !cipher)
-    return NULL;
-
   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
      2 + cipher */
   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
   buffer = silc_buffer_alloc(len);
-  if (!buffer)
-    return NULL;
 
   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
 
@@ -270,15 +554,13 @@ SilcBuffer silc_channel_key_encode_payload(unsigned short id_len,
   return buffer;
 }
 
-/* Free's Channel Key Payload */
+/* Frees Channel Key Payload */
 
-void silc_channel_key_free_payload(SilcChannelKeyPayload payload)
+void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
 {
   if (payload) {
-    if (payload->id)
-      silc_free(payload->id);
-    if (payload->cipher)
-      silc_free(payload->cipher);
+    silc_free(payload->id);
+    silc_free(payload->cipher);
     if (payload->key) {
       memset(payload->key, 0, payload->key_len);
       silc_free(payload->key);
@@ -290,7 +572,7 @@ void silc_channel_key_free_payload(SilcChannelKeyPayload payload)
 /* Return ID */
 
 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
-                                      unsigned int *id_len)
+                                      uint32 *id_len)
 {
   if (id_len)
     *id_len = payload->id_len;
@@ -301,7 +583,7 @@ unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload,
 /* Return cipher name */
 
 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
-                                          unsigned int *cipher_len)
+                                          uint32 *cipher_len)
 {
   if (cipher_len)
     *cipher_len = payload->cipher_len;
@@ -312,7 +594,7 @@ unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
 /* Return key */
 
 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
-                                       unsigned int *key_len)
+                                       uint32 *key_len)
 {
   if (key_len)
     *key_len = payload->key_len;
index b39045fa9d80472de3d9fe40780515767e49f558..1db0c7b0e491fcbb911e38272c3bbb08e007bac2 100644 (file)
@@ -1,16 +1,16 @@
 /*
-
   silcchannel.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 */
 
+/****h* silccore/SilcChannelAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementations of the Channel Payload, Channel Message Payload and
+ * Channel Key Payload.  The Channel Payload represents new channel and
+ * is used to distribute the information of the new channel.  The Channel
+ * Message Payload is used to deliver messages to the channel.  The routines
+ * for Channel Message Payload also handles the encryption and decryption
+ * of the payload.  Last, the Channel Key Payload is used to distribute
+ * a new key to the channel.  It is done for example every time someone
+ * joins a channel or the old key expires.
+ *
+ ***/
+
 #ifndef SILCCHANNEL_H
 #define SILCCHANNEL_H
 
-/* Forward declaration for Channel Payload parsed from packet. The
-   actual structure is defined in source file and is private data. */
+#include "silcdlist.h"
+
+/****s* silccore/SilcChannelAPI/SilcChannelPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcChannelPayloadStruct *SilcChannelPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Channel Payload and is allocated
+ *    by silc_channel_payload_parse and given as argument usually to
+ *    all silc_channel_payload_* functions.  It is freed by the
+ *    silc_channel_payload_free function.
+ *
+ ***/
 typedef struct SilcChannelPayloadStruct *SilcChannelPayload;
 
-/* Forward declaration for Channel Key Payload parsed from packet. The
-   actual structure is defined in source file and is private data. */
+/****s* silccore/SilcChannelAPI/SilcChannelMessagePayload
+ *
+ * NAME
+ * 
+ *    typedef struct 
+ *    SilcChannelMessagePayloadStruct *SilcChannelMessagePayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Channel Message Payload and is allocated
+ *    by silc_channel_message_payload_parse and given as argument usually to
+ *    all silc_channel_message_payload_* functions.  It is freed by the
+ *    silc_channel_message_payload_free function.
+ *
+ ***/
+typedef struct SilcChannelMessagePayloadStruct *SilcChannelMessagePayload;
+
+/****s* silccore/SilcChannelAPI/SilcChannelKeyPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcChannelKeyPayloadStruct *SilcChannelKeyPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Channel Key Payload and is allocated
+ *    by silc_channel_key_payload_parse and given as argument usually to
+ *    all silc_channel_key_payload_* functions.  It is freed by the
+ *    silc_channel_key_payload_free function.
+ *
+ ***/
 typedef struct SilcChannelKeyPayloadStruct *SilcChannelKeyPayload;
 
-/* Channel modes */
-#define SILC_CHANNEL_MODE_NONE       0x0000
-#define SILC_CHANNEL_MODE_PRIVATE    0x0001 /* private channel */
-#define SILC_CHANNEL_MODE_SECRET     0x0002 /* secret channel */
-#define SILC_CHANNEL_MODE_PRIVKEY    0x0004 /* channel has private key */
-#define SILC_CHANNEL_MODE_INVITE     0x0008 /* invite only channel */
+/****d* silccore/SilcChannelAPI/SilcMessageFlags
+ *
+ * NAME
+ * 
+ *    typedef uint16 SilcMessageFlags;
+ *
+ * DESCRIPTION
+ *
+ *    The message flags type definition and the message flags.  The 
+ *    message flags are used to indicate some status of the message.
+ *    These flags are also used by the private message interfaces.
+ *
+ * SOURCE
+ */
+typedef uint16 SilcMessageFlags;
 
-/* User modes on channel */
-#define SILC_CHANNEL_UMODE_NONE      0x0000
-#define SILC_CHANNEL_UMODE_CHANFO    0x0001 /* channel founder */
-#define SILC_CHANNEL_UMODE_CHANOP    0x0002 /* channel operator */
+/* The message flags (shared by both channel and private messages) */
+#define SILC_MESSAGE_FLAG_NONE        0x0000
+#define SILC_MESSAGE_FLAG_AUTOREPLY   0x0001
+#define SILC_MESSAGE_FLAG_NOREPLY     0x0002
+#define SILC_MESSAGE_FLAG_ACTION      0x0004
+#define SILC_MESSAGE_FLAG_NOTICE      0x0008
+#define SILC_MESSAGE_FLAG_REQUEST     0x0010
+#define SILC_MESSAGE_FLAG_SIGNED      0x0020
+#define SILC_MESSAGE_FLAG_RESERVED    0x0040 /* to 0x0200 */
+#define SILC_MESSAGE_FLAG_PRIVATE     0x0400 /* to 0x8000 */
+/***/
 
 /* Prototypes */
-SilcChannelPayload silc_channel_parse_payload(SilcBuffer buffer);
-SilcBuffer silc_channel_encode_payload(unsigned short nick_len,
-                                      unsigned char *nick,
-                                      unsigned short data_len,
-                                      unsigned char *data,
-                                      unsigned short iv_len,
-                                      unsigned char *iv,
-                                      SilcRng rng);
-void silc_channel_free_payload(SilcChannelPayload payload);
-unsigned char *silc_channel_get_nickname(SilcChannelPayload payload,
-                                        unsigned int *nick_len);
-unsigned char *silc_channel_get_data(SilcChannelPayload payload,
-                                    unsigned int *data_len);
-unsigned char *silc_channel_get_iv(SilcChannelPayload payload,
-                                  unsigned int *iv_len);
-SilcChannelKeyPayload silc_channel_key_parse_payload(SilcBuffer buffer);
-SilcBuffer silc_channel_key_encode_payload(unsigned short id_len,
-                                          unsigned char *id,
-                                          unsigned short cipher_len,
-                                          unsigned char *cipher,
-                                          unsigned short key_len,
-                                          unsigned char *key);
-void silc_channel_key_free_payload(SilcChannelKeyPayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelPayload 
+ *    silc_channel_payload_parse(const unsigned char *payload,
+ *                               uint32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses channel payload returning new channel payload structure. The
+ *    `buffer' is the raw payload buffer.
+ *
+ ***/
+SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload,
+                                             uint32 payload_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_parse_list
+ *
+ * SYNOPSIS
+ *
+ *    SilcDList
+ *    silc_channel_payload_parse_list(const unsigned char *payload,
+ *                                    uint32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses list of channel payloads returning list of payloads. This
+ *    is equivalent to the silc_channel_payload_parse except that the `buffer'
+ *    now includes multiple Channel Payloads one after the other.
+ *
+ ***/
+SilcDList silc_channel_payload_parse_list(const unsigned char *payload,
+                                         uint32 payload_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
+ *                                           uint16 channel_name_len,
+ *                                           const unsigned char *channel_id,
+ *                                           uint32 channel_id_len,
+ *                                           uint32 mode);
+ *
+ * DESCRIPTION
+ *
+ *    Encode new channel payload and returns it as buffer.
+ *
+ ***/
+SilcBuffer silc_channel_payload_encode(const unsigned char *channel_name,
+                                      uint16 channel_name_len,
+                                      const unsigned char *channel_id,
+                                      uint32 channel_id_len,
+                                      uint32 mode);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_channel_payload_free(SilcChannelPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees Channel Payload and all data in it.
+ *
+ ***/
+void silc_channel_payload_free(SilcChannelPayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_payload_list_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_channel_payload_list_free(SilcDList list);
+ *
+ * DESCRIPTION
+ *
+ *    Frees list of Channel Payloads and all data in them.
+ *
+ ***/
+void silc_channel_payload_list_free(SilcDList list);
+
+/****f* silccore/SilcChannelAPI/silc_channel_get_name
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_channel_get_name(SilcChannelPayload payload,
+ *                                         uint32 *channel_name_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return the channel name from the payload. The caller must not free it.
+ *
+ ***/
+unsigned char *silc_channel_get_name(SilcChannelPayload payload,
+                                    uint32 *channel_name_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_get_id
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_channel_get_id(SilcChannelPayload payload,
+ *                                       uint32 *channel_id_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return the Channel ID data from the payload. The caller must not free it.
+ *
+ ***/
+unsigned char *silc_channel_get_id(SilcChannelPayload payload,
+                                  uint32 *channel_id_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_get_id_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the Channel ID as parsed ID. This is equivalent to the
+ *    silc_channel_get_id execpt that the ID is already parsed. The caller
+ *    must free the parsed Channel ID.
+ *
+ ***/
+SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_get_mode
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_channel_get_mode(SilcChannelPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the mode. The mode is arbitrary. It can be the mode of the
+ *    channel or perhaps the mode of the client on the channel.  The protocol
+ *    dictates what the usage of the mode is in different circumstances.
+ *
+ ***/
+uint32 silc_channel_get_mode(SilcChannelPayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_payload_decrypt
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_channel_message_payload_decrypt(unsigned char *data,
+ *                                              size_t data_len,
+ *                                              SilcCipher cipher,
+ *                                              SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Decrypt the channel message. First push the IV out of the packet `data'.
+ *    The IV is used in the decryption process. Then decrypt the message.
+ *    After decryption, take the MAC from the decrypted packet, compute MAC
+ *    and compare the MACs.  If they match, the decryption was successful
+ *    and we have the channel message ready to be displayed.
+ *
+ *    This is usually used by the Channel Message interface itself but can
+ *    be called by the appliation if separate decryption process is required.
+ *    For example server might need to call this directly in some 
+ *    circumstances. The `cipher' is used to decrypt the payload.
+ *
+ *    If the `hmac' is no provided then the MAC of the channel message is
+ *    not verified.
+ *
+ ***/
+bool silc_channel_message_payload_decrypt(unsigned char *data,
+                                         size_t data_len,
+                                         SilcCipher cipher,
+                                         SilcHmac hmac);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelMessagePayload 
+ *    silc_channel_message_payload_parse(const unsigned char *payload,
+ *                                       uint32 payload_len,
+ *                                       SilcCipher cipher,
+ *                                       SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Parses channel message payload returning new channel payload structure.
+ *    This also decrypts it and checks the MAC. The `cipher's is used to
+ *    decrypt the payload.
+ *
+ *    If the `hmac' is no provided then the MAC of the channel message is
+ *    not verified.
+ *
+ ***/
+SilcChannelMessagePayload 
+silc_channel_message_payload_parse(unsigned char *payload,
+                                  uint32 payload_len,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_channel_message_payload_encode(uint16 flags,
+ *                                                   uint16 data_len,
+ *                                                   const unsigned char *data,
+ *                                                   uint16 iv_len,
+ *                                                   unsigned char *iv,
+ *                                                   SilcCipher cipher,
+ *                                                   SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes channel message payload into a buffer and returns it. This
+ *    is used to add channel message payload into a packet. As the channel
+ *    payload is encrypted separately from other parts of the packet padding
+ *    must be applied to the payload. The function generates the padding
+ *    automatically from random data.  The `cipher' is the cipher used
+ *    encrypt the payload and `hmac' is used to compute the MAC for the
+ *    payload.
+ *
+ ***/
+SilcBuffer silc_channel_message_payload_encode(uint16 flags,
+                                              uint16 data_len,
+                                              const unsigned char *data,
+                                              uint16 iv_len,
+                                              unsigned char *iv,
+                                              SilcCipher cipher,
+                                              SilcHmac hmac);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_channel_message_payload_free(SilcChannelMessagePayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Free's Channel Message Payload and all data in it.
+ *
+ ***/
+void silc_channel_message_payload_free(SilcChannelMessagePayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_get_flags
+ *
+ * SYNOPSIS
+ *
+ *    SilcMessageFlags
+ *    silc_channel_message_get_flags(SilcChannelMessagePayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the message flags from the payload.
+ *
+ ***/
+SilcMessageFlags
+silc_channel_message_get_flags(SilcChannelMessagePayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_get_data
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *
+ *    silc_channel_message_get_data(SilcChannelMessagePayload payload,
+ *                                  uint32 *data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return the data in the payload, that is, the actual channel message.
+ *    The caller must not free it.
+ *
+ ***/
+unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload,
+                                            uint32 *data_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_get_mac
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *
+ *    silc_channel_message_get_mac(SilcChannelMessagePayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the MAC of the payload. The caller must already know the 
+ *    length of the MAC. The caller must not free the MAC.
+ *
+ ***/
+unsigned char *silc_channel_message_get_mac(SilcChannelMessagePayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_message_get_iv
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *
+ *    silc_channel_message_get_iv(SilcChannelMessagePayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the IV of the payload. The caller must already know the 
+ *    length of the IV. The caller must not free the IV.
+ *
+ ***/
+unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcChannelKeyPayload 
+ *    silc_channel_key_payload_parse(const unsigned char *payload,
+ *                                   uin32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *     Parses channel key payload returning new channel key payload 
+ *     structure.
+ *
+ ***/
+SilcChannelKeyPayload 
+silc_channel_key_payload_parse(const unsigned char *payload,
+                              uint32 payload_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_channel_key_payload_encode(uint16 id_len,
+ *                                               const unsigned char *id,
+ *                                               uint16 cipher_len,
+ *                                               const unsigned char *cipher,
+ *                                               uint16 key_len,
+ *                                               const unsigned char *key);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes channel key payload into a buffer and returns it. This is used 
+ *    to add channel key payload into a packet.
+ *
+ ***/
+SilcBuffer silc_channel_key_payload_encode(uint16 id_len,
+                                          const unsigned char *id,
+                                          uint16 cipher_len,
+                                          const unsigned char *cipher,
+                                          uint16 key_len,
+                                          const unsigned char *key);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_channel_key_payload_free(SilcChannelKeyPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the Channel Key Payload and all data in it.
+ *
+ ***/
+void silc_channel_key_payload_free(SilcChannelKeyPayload payload);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_get_id
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
+ *                                           uint32 *id_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return the Channel ID data from the payload. The caller must not
+ *    free it.
+ *
+ ***/
 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
-                                      unsigned int *id_len);
+                                      uint32 *id_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_get_cipher
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
+ *                                               uint32 *cipher_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return the name of the cipher from the payload. The caller must not
+ *    free it.
+ *
+ ***/
 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
-                                          unsigned int *cipher_len);
+                                          uint32 *cipher_len);
+
+/****f* silccore/SilcChannelAPI/silc_channel_key_get_key
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
+ *                                            uint32 *key_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return the raw key material from the payload. The caller must not
+ *    free it.
+ *
+ ***/
 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
-                                       unsigned int *key_len);
+                                       uint32 *key_len);
 
 #endif
index cab272faf32ea705d514bc445da92930758832fc..0d64787d06cd8d3ad778757ae0d3c05864d19562 100644 (file)
@@ -2,9 +2,9 @@
 
   silccommand.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 #include "silccommand.h"
 
+/******************************************************************************
+
+                              Command Payload
+
+******************************************************************************/
+
 /* Command Payload structure. Contents of this structure is parsed
    from SILC packets. */
 struct SilcCommandPayloadStruct {
   SilcCommand cmd;
-  unsigned int argc;
-  unsigned char **argv;
-  unsigned int *argv_lens;
-  unsigned int *argv_types;
-  unsigned int pos;
+  uint16 ident;
+  SilcArgumentPayload args;
 };
 
 /* Length of the command payload */
-#define SILC_COMMAND_PAYLOAD_LEN 4
+#define SILC_COMMAND_PAYLOAD_LEN 6
 
 /* Parses command payload returning new command payload structure */
 
-SilcCommandPayload silc_command_parse_payload(SilcBuffer buffer)
+SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
+                                             uint32 payload_len)
 {
+  SilcBufferStruct buffer;
   SilcCommandPayload new;
-  unsigned short payload_len = 0;
-  unsigned char args_num = 0;
-  unsigned char arg_num = 0;
-  unsigned int arg_type = 0;
-  unsigned int pull_len = 0;
-  int i = 0;
+  unsigned char args_num;
+  uint16 p_len;
+  int ret;
 
   SILC_LOG_DEBUG(("Parsing command payload"));
 
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
   new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new command payload"));
-    return NULL;
-  }
 
   /* Parse the Command Payload */
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI_CHAR(&new->cmd),
-                      SILC_STR_UI_CHAR(&args_num),
-                      SILC_STR_UI_SHORT(&payload_len),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(&buffer, 
+                            SILC_STR_UI_SHORT(&p_len),
+                            SILC_STR_UI_CHAR(&new->cmd),
+                            SILC_STR_UI_CHAR(&args_num),
+                            SILC_STR_UI_SHORT(&new->ident),
+                            SILC_STR_END);
+  if (ret == -1) {
+    silc_free(new);
+    return NULL;
+  }
 
-  if (payload_len != buffer->len) {
+  if (p_len != buffer.len) {
     SILC_LOG_ERROR(("Incorrect command payload in packet, packet dropped"));
+    silc_free(new);
     return NULL;
   }
 
-  if (new->cmd == 0)
+  if (new->cmd == 0) {
+    silc_free(new);
     return NULL;
+  }
 
-  if (args_num && payload_len) {
-
-    new->argv = silc_calloc(args_num, sizeof(unsigned char *));
-    new->argv_lens = silc_calloc(args_num, sizeof(unsigned int));
-    new->argv_types = silc_calloc(args_num, sizeof(unsigned int));
-
-    silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
-    pull_len += SILC_COMMAND_PAYLOAD_LEN;
-
-    /* Parse Command Argument Payloads */
-    arg_num = 1;
-    while(arg_num) {
-      silc_buffer_unformat(buffer,
-                          SILC_STR_UI_CHAR(&arg_num),
-                          SILC_STR_UI_CHAR(&arg_type),
-                          SILC_STR_UI_SHORT(&payload_len),
-                          SILC_STR_END);
-
-      /* Check that argument number is correct */
-      if (arg_num != i + 1)
-       goto err;
-
-      new->argv_lens[i] = payload_len;
-      new->argv_types[i] = arg_type;
-
-      /* Get argument data */
-      silc_buffer_pull(buffer, 4);
-      silc_buffer_unformat(buffer,
-                          SILC_STR_UI_XNSTRING_ALLOC(&new->argv[i], 
-                                                     payload_len),
-                          SILC_STR_END);
-      silc_buffer_pull(buffer, payload_len);
-      pull_len += 4 + payload_len;
-
-      i++;
-
-      if (i == args_num)
-       break;
+  silc_buffer_pull(&buffer, SILC_COMMAND_PAYLOAD_LEN);
+  if (args_num) {
+    new->args = silc_argument_payload_parse(buffer.data, buffer.len, args_num);
+    if (!new->args) {
+      silc_free(new);
+      return NULL;
     }
-
-    /* Check the number of arguments */
-    if (arg_num != args_num)
-      goto err;
   }
+  silc_buffer_push(&buffer, SILC_COMMAND_PAYLOAD_LEN);
 
-  new->argc = i;
-  new->pos = 0;
+  return new;
+}
 
-  silc_buffer_push(buffer, pull_len);
+/* Encodes Command Payload returning it to SilcBuffer. */
 
-  return new;
+SilcBuffer silc_command_payload_encode(SilcCommand cmd,
+                                      uint32 argc,
+                                      unsigned char **argv,
+                                      uint32 *argv_lens,
+                                      uint32 *argv_types,
+                                      uint16 ident)
+{
+  SilcBuffer buffer;
+  SilcBuffer args = NULL;
+  uint32 len = 0;
 
- err:
-  if (i) {
-    int k;
+  SILC_LOG_DEBUG(("Encoding command payload"));
 
-    for (k = 0; k < i; k++)
-      silc_free(new->argv[k]);
+  if (argc) {
+    args = silc_argument_payload_encode(argc, argv, argv_lens, argv_types);
+    len = args->len;
   }
 
-  silc_free(new->argv);
-  silc_free(new->argv_lens);
-  silc_free(new->argv_types);
+  len += SILC_COMMAND_PAYLOAD_LEN;
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
 
-  if (new)
-    silc_free(new);
+  /* Create Command payload */
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(len),
+                    SILC_STR_UI_CHAR(cmd),
+                    SILC_STR_UI_CHAR(argc),
+                    SILC_STR_UI_SHORT(ident),
+                    SILC_STR_END);
+
+  /* Add arguments */
+  if (argc) {
+    silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_END);
+    silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
+    silc_buffer_free(args);
+  }
 
-  return NULL;
+  return buffer;
 }
 
-/* Encodes Command Payload returning it to SilcBuffer. */
+/* Same as above but encode the buffer from SilcCommandPayload structure
+   instead of raw data. */
 
-SilcBuffer silc_command_encode_payload(SilcCommand cmd,
-                                      unsigned int argc,
-                                      unsigned char **argv,
-                                      unsigned int *argv_lens,
-                                      unsigned int *argv_types)
+SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload)
 {
   SilcBuffer buffer;
-  unsigned int len;
-  int i;
+  SilcBuffer args = NULL;
+  uint32 len = 0;
+  uint32 argc = 0;
 
   SILC_LOG_DEBUG(("Encoding command payload"));
 
-  len = 1 + 1 + 2;
-  for (i = 0; i < argc; i++)
-    len += 1 + 1 + 2 + argv_lens[i];
+  if (payload->args) {
+    args = silc_argument_payload_encode_payload(payload->args);
+    len = args->len;
+    argc = silc_argument_get_arg_num(payload->args);
+  }
 
+  len += SILC_COMMAND_PAYLOAD_LEN;
   buffer = silc_buffer_alloc(len);
   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
 
   /* Create Command payload */
   silc_buffer_format(buffer,
-                    SILC_STR_UI_CHAR(cmd),
-                    SILC_STR_UI_CHAR(argc),
                     SILC_STR_UI_SHORT(len),
+                    SILC_STR_UI_CHAR(payload->cmd),
+                    SILC_STR_UI_CHAR(argc),
+                    SILC_STR_UI_SHORT(payload->ident),
                     SILC_STR_END);
 
-  /* Put arguments */
-  if (argc) {
-    silc_buffer_pull(buffer, 4);
-   
-    for (i = 0; i < argc; i++) {
-      silc_buffer_format(buffer,
-                        SILC_STR_UI_CHAR(i + 1),
-                        SILC_STR_UI_CHAR(argv_types[i]),
-                        SILC_STR_UI_SHORT(argv_lens[i]),
-                        SILC_STR_UI_XNSTRING(argv[i], argv_lens[i]),
-                        SILC_STR_END);
-      silc_buffer_pull(buffer, 4 + argv_lens[i]);
-    }
-
-    silc_buffer_push(buffer, len);
+  /* Add arguments */
+  if (args) {
+    silc_buffer_pull(buffer, SILC_COMMAND_PAYLOAD_LEN);
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_END);
+    silc_buffer_push(buffer, SILC_COMMAND_PAYLOAD_LEN);
+    silc_buffer_free(args);
   }
 
   return buffer;
 }
 
 /* Encodes Command payload with variable argument list. The arguments
-   must be: unsigned char *, unsigned int, ... One unsigned char *
-   and unsigned int forms one argument, hence `argc' in case when
-   sending one unsigned char * and unsigned int equals one (1) and
-   when sending two of those it equals two (2), and so on. This has
-   to be preserved or bad things will happen. */
-
-SilcBuffer silc_command_encode_payload_va(SilcCommand cmd, 
-                                         unsigned int argc, ...)
+   must be: uint32, unsigned char *, unsigned int, ... One 
+   {uint32, unsigned char * and unsigned int} forms one argument, 
+   thus `argc' in case when sending one {uint32, unsigned char * 
+   and uint32} equals one (1) and when sending two of those it
+   equals two (2), and so on. This has to be preserved or bad things
+   will happen. The variable arguments is: {type, data, data_len}. */
+
+SilcBuffer silc_command_payload_encode_va(SilcCommand cmd, 
+                                         uint16 ident, 
+                                         uint32 argc, ...)
 {
   va_list ap;
-  unsigned char **argv;
-  unsigned int *argv_lens = NULL, *argv_types = NULL;
-  unsigned char *x;
-  unsigned int x_len;
   SilcBuffer buffer;
-  int i;
 
   va_start(ap, argc);
+  buffer = silc_command_payload_encode_vap(cmd, ident, argc, ap);
+  va_end(ap);
 
-  argv = silc_calloc(argc, sizeof(unsigned char *));
-  argv_lens = silc_calloc(argc, sizeof(unsigned int));
-  argv_types = silc_calloc(argc, sizeof(unsigned int));
+  return buffer;
+}
 
-  for (i = 0; i < argc; i++) {
-    x = va_arg(ap, unsigned char *);
-    x_len = va_arg(ap, unsigned int);
+/* Same as above but with va_list. */
+
+SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
+                                          uint16 ident, 
+                                          uint32 argc, va_list ap)
+{
+  unsigned char **argv = NULL;
+  uint32 *argv_lens = NULL, *argv_types = NULL;
+  unsigned char *x;
+  uint32 x_len;
+  uint32 x_type;
+  SilcBuffer buffer;
+  int i, k = 0;
 
-    argv[i] = silc_calloc(x_len + 1, sizeof(unsigned char));
-    memcpy(argv[i], x, x_len);
-    argv_lens[i] = x_len;
-    argv_types[i] = i + 1;
+  if (argc) {
+    argv = silc_calloc(argc, sizeof(unsigned char *));
+    argv_lens = silc_calloc(argc, sizeof(uint32));
+    argv_types = silc_calloc(argc, sizeof(uint32));
+
+    for (i = 0, k = 0; i < argc; i++) {
+      x_type = va_arg(ap, uint32);
+      x = va_arg(ap, unsigned char *);
+      x_len = va_arg(ap, uint32);
+      
+      if (!x_type || !x || !x_len)
+       continue;
+      
+      argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
+      memcpy(argv[k], x, x_len);
+      argv_lens[k] = x_len;
+      argv_types[k] = x_type;
+      k++;
+    }
   }
 
-  buffer = silc_command_encode_payload(cmd, argc, argv
-                                      argv_lens, argv_types);
+  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens
+                                      argv_types, ident);
 
-  for (i = 0; i < argc; i++)
+  for (i = 0; i < k; i++)
     silc_free(argv[i]);
   silc_free(argv);
   silc_free(argv_lens);
@@ -242,99 +248,123 @@ SilcBuffer silc_command_encode_payload_va(SilcCommand cmd,
   return buffer;
 }
 
-/* Free's Command Payload */
+/* Same as above except that this is used to encode strictly command
+   reply packets. The command status message to be returned is sent as
+   extra argument to this function. The `argc' must not count `status'
+   as on argument. */
 
-void silc_command_free_payload(SilcCommandPayload payload)
+SilcBuffer 
+silc_command_reply_payload_encode_va(SilcCommand cmd, 
+                                    SilcCommandStatus status,
+                                    uint16 ident,
+                                    uint32 argc, ...)
 {
-  int i;
+  va_list ap;
+  SilcBuffer buffer;
 
-  if (payload) {
-    for (i = 0; i < payload->argc; i++)
-      silc_free(payload->argv[i]);
+  va_start(ap, argc);
+  buffer = silc_command_reply_payload_encode_vap(cmd, status, ident, argc, ap);
+  va_end(ap);
 
-    silc_free(payload->argv);
-    silc_free(payload);
-  }
+  return buffer;
 }
 
-/* Returns the command type in payload */
-
-SilcCommand silc_command_get(SilcCommandPayload payload)
+SilcBuffer 
+silc_command_reply_payload_encode_vap(SilcCommand cmd, 
+                                     SilcCommandStatus status,
+                                     uint16 ident, uint32 argc, 
+                                     va_list ap)
 {
-  return payload->cmd;
-}
+  unsigned char **argv;
+  uint32 *argv_lens = NULL, *argv_types = NULL;
+  unsigned char status_data[2];
+  unsigned char *x;
+  uint32 x_len;
+  uint32 x_type;
+  SilcBuffer buffer;
+  int i, k;
 
-/* Returns number of arguments in payload */
+  argc++;
+  argv = silc_calloc(argc, sizeof(unsigned char *));
+  argv_lens = silc_calloc(argc, sizeof(uint32));
+  argv_types = silc_calloc(argc, sizeof(uint32));
 
-unsigned int silc_command_get_arg_num(SilcCommandPayload payload)
-{
-  return payload->argc;
-}
+  SILC_PUT16_MSB(status, status_data);
+  argv[0] = silc_calloc(sizeof(status_data) + 1, sizeof(unsigned char));
+  memcpy(argv[0], status_data, sizeof(status_data));
+  argv_lens[0] = sizeof(status_data);
+  argv_types[0] = 1;
 
-/* Returns first argument from payload. */
+  for (i = 1, k = 1; i < argc; i++) {
+    x_type = va_arg(ap, uint32);
+    x = va_arg(ap, unsigned char *);
+    x_len = va_arg(ap, uint32);
 
-unsigned char *silc_command_get_first_arg(SilcCommandPayload payload,
-                                         unsigned int *ret_len)
-{
-  payload->pos = 0;
+    if (!x_type || !x || !x_len)
+      continue;
 
-  if (ret_len)
-    *ret_len = payload->argv_lens[payload->pos];
+    argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
+    memcpy(argv[k], x, x_len);
+    argv_lens[k] = x_len;
+    argv_types[k] = x_type;
+    k++;
+  }
 
-  return payload->argv[payload->pos++];
-}
+  buffer = silc_command_payload_encode(cmd, k, argv, argv_lens, 
+                                      argv_types, ident);
 
-/* Returns next argument from payload or NULL if no more arguments. */
+  for (i = 0; i < k; i++)
+    silc_free(argv[i]);
+  silc_free(argv);
+  silc_free(argv_lens);
+  silc_free(argv_types);
 
-unsigned char *silc_command_get_next_arg(SilcCommandPayload payload,
-                                        unsigned int *ret_len)
-{
-  if (payload->pos >= payload->argc)
-    return NULL;
+  return buffer;
+}
 
-  if (ret_len)
-    *ret_len = payload->argv_lens[payload->pos];
+/* Frees Command Payload */
 
-  return payload->argv[payload->pos++];
+void silc_command_payload_free(SilcCommandPayload payload)
+{
+  if (payload) {
+    silc_argument_payload_free(payload->args);
+    silc_free(payload);
+  }
 }
 
-/* Returns argument which type is `type'. */
+/* Returns command */
 
-unsigned char *silc_command_get_arg_type(SilcCommandPayload payload,
-                                        unsigned int type,
-                                        unsigned int *ret_len)
+SilcCommand silc_command_get(SilcCommandPayload payload)
 {
-  int i;
+  return payload->cmd;
+}
 
-  for (i = 0; i < payload->argc; i++)
-    if (payload->argv_types[i] == type)
-      break;
+/* Retuns arguments payload */
 
-  if (i >= payload->argc)
-    return NULL;
+SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload)
+{
+  return payload->args;
+}
 
-  if (ret_len)
-    *ret_len = payload->argv_lens[i];
+/* Returns identifier */
 
-  return payload->argv[i];
+uint16 silc_command_get_ident(SilcCommandPayload payload)
+{
+  return payload->ident;
 }
 
-/* Encodes command status payload. Status payload is sent as one reply
-   argument. The returned payload still has to be saved into the 
-   Command Argument payload. */
+/* Function to set identifier to already allocated Command Payload. Command
+   payloads are frequentlly resent in SILC and thusly this makes it easy
+   to set the identifier. */
 
-SilcBuffer silc_command_encode_status_payload(SilcCommandStatus status,
-                                             unsigned char *data,
-                                             unsigned int len)
+void silc_command_set_ident(SilcCommandPayload payload, uint16 ident)
 {
-  SilcBuffer sp;
+  payload->ident = ident;
+}
 
-  sp = silc_buffer_alloc(len + 2);
-  silc_buffer_pull_tail(sp, SILC_BUFFER_END(sp));
-  silc_buffer_format(sp,
-                    SILC_STR_UI_SHORT(status),
-                    SILC_STR_UI_XNSTRING(data, len),
-                    SILC_STR_END);
+/* Function to set the command to already allocated Command Payload. */
 
-  return sp;
+void silc_command_set_command(SilcCommandPayload payload, SilcCommand command)
+{
+  payload->cmd = command;
 }
index 80c7ece9f804ac91f6baf51cadd5c93e696ce2a9..65ed46a2f5b652de0bd14a45009de2e5aa71e1ea 100644 (file)
@@ -1,16 +1,16 @@
 /*
-
   silccommand.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 */
 
+/****h* silccore/SilcCommandAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the Command Payload. The Command Payload is used to
+ * send commands and also command replies usually between client and
+ * server.
+ *
+ ***/
+
 #ifndef SILCCOMMAND_H
 #define SILCCOMMAND_H
 
-/* Command function callback. The actual command function pointer. */
-typedef void (*SilcCommandCb)(void *context);
-
-/* Typedefinition for SILC commands. */
-typedef unsigned char SilcCommand;
+/****f* silccore/SilcCommandAPI/SilcCommandCb
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcCommandCb)(void *context, void *context2);
+ *
+ * DESCRIPTION
+ *
+ *    Command function callback. The actual command function pointer.
+ *    This is generic command callback that the application may choose to
+ *    use with its command routines.  However, none of the generic
+ *    routines depend on this callback so application may freely define
+ *    their own command callback if desired.
+ *
+ ***/
+typedef void (*SilcCommandCb)(void *context, void *context2);
 
-/* Forward declaration for Command Payload parsed from packet. The
-   actual structure is defined in source file and is private data. */
+/****s* silccore/SilcCommandAPI/SilcCommandPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcCommandPayloadStruct *SilcCommandPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Command Payload and is allocated
+ *    by silc_command_payload_parse and given as argument usually to
+ *    all silc_command_payload_* functions.  It is freed by the
+ *    silc_command_payload_free function.
+ *
+ ***/
 typedef struct SilcCommandPayloadStruct *SilcCommandPayload;
 
-/* Command flags. These set how the commands behave on different
-   situations. These can be OR'ed together to set multiple flags. */
+/****d* silccore/SilcCommandAPI/SilcCommandFlags
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcCommandFlags;
+ *
+ * DESCRIPTION
+ *
+ *    Command flags that set how the commands behave on different
+ *    situations. These can be OR'es together to set multiple flags.
+ *    The application is resoponsible of implementing the behaviour
+ *    of these flags. These are here just to define generic flags.
+ *    The server usually makes use of these flags.
+ *
+ * SOURCE
+ */
 typedef enum {
-  SILC_CF_NONE = 0,
+  SILC_CF_NONE           = 0,
+
+  /* Command may only be used once per (about) 2 seconds. Bursts up
+     to 5 commands are allowed though. */
+  SILC_CF_LAG            = (1L << 1),
 
-  /* Command may only be used once per (about) 2 seconds */
-  SILC_CF_LAG = (1L << 1),
+  /* Command may only be used once per (about) 2 seconds. No bursts
+     are allowed at all. */
+  SILC_CF_LAG_STRICT     = (1L << 2),
 
   /* Command is available for registered connections (connections
      whose ID has been created. */
-  SILC_CF_REG = (1L << 2),
+  SILC_CF_REG            = (1L << 3),
 
   /* Command is available only for server operators */
-  SILC_CF_OPER = (1L << 3),
+  SILC_CF_OPER           = (1L << 4),
 
   /* Command is available only for SILC (router) operators. If this 
      is set SILC_CF_OPER is not necessary to be set. */
-  SILC_CF_SILC_OPER = (1L << 4),
+  SILC_CF_SILC_OPER      = (1L << 5),
 
 } SilcCommandFlag;
+/***/
+
+/****d* silccore/SilcCommandAPI/SilcCommand
+ *
+ * NAME
+ * 
+ *    typedef unsigned char SilcCommand;
+ *
+ * DESCRIPTION
+ *
+ *    The SilcCommand type definition and the commands. The commands
+ *    listed here are the official SILC Commands and they have client
+ *    and server counterparts.
+ *
+ * SOURCE
+ */
+typedef unsigned char SilcCommand;
 
 /* All SILC commands. These are commands that have client and server
-   counterparts. These are pretty much the same as in IRC. */
+   counterparts. */
 #define SILC_COMMAND_NONE               0
-#define SILC_COMMAND_WHOIS             2
-#define SILC_COMMAND_WHOWAS            3
-#define SILC_COMMAND_IDENTIFY           4
-#define SILC_COMMAND_NICK              5
-#define SILC_COMMAND_LIST              6
-#define SILC_COMMAND_TOPIC             7
-#define SILC_COMMAND_INVITE            8
-#define SILC_COMMAND_QUIT              9
-#define SILC_COMMAND_KILL              10
-#define SILC_COMMAND_INFO              11
-#define SILC_COMMAND_CONNECT           12
-#define SILC_COMMAND_PING              13
-#define SILC_COMMAND_OPER              14
-#define SILC_COMMAND_JOIN              15
-#define SILC_COMMAND_MOTD              16
-#define SILC_COMMAND_UMODE             17
-#define SILC_COMMAND_CMODE             18
+#define SILC_COMMAND_WHOIS             1
+#define SILC_COMMAND_WHOWAS            2
+#define SILC_COMMAND_IDENTIFY           3
+#define SILC_COMMAND_NICK              4
+#define SILC_COMMAND_LIST              5
+#define SILC_COMMAND_TOPIC             6
+#define SILC_COMMAND_INVITE            7
+#define SILC_COMMAND_QUIT              8
+#define SILC_COMMAND_KILL              9
+#define SILC_COMMAND_INFO              10
+#define SILC_COMMAND_CONNECT           11
+#define SILC_COMMAND_PING              12
+#define SILC_COMMAND_OPER              13
+#define SILC_COMMAND_JOIN              14
+#define SILC_COMMAND_MOTD              15
+#define SILC_COMMAND_UMODE             16
+#define SILC_COMMAND_CMODE             17
+#define SILC_COMMAND_CUMODE            18
 #define SILC_COMMAND_KICK              19
-#define        SILC_COMMAND_RESTART            20
+#define SILC_COMMAND_BAN               20
 #define        SILC_COMMAND_CLOSE              21
-#define        SILC_COMMAND_DIE                22
+#define        SILC_COMMAND_SHUTDOWN           22
 #define SILC_COMMAND_SILCOPER          23
 #define SILC_COMMAND_LEAVE             24
-#define SILC_COMMAND_NAMES             25
-
-/* Local commands. Local commands are unofficial commands and
-   are implementation specific commands. These are used only by the
-   SILC client to extend user commands. */
-#define SILC_COMMAND_HELP              100
-#define SILC_COMMAND_CLEAR             101
-#define SILC_COMMAND_VERSION           102
-#define SILC_COMMAND_SERVER             103
-#define SILC_COMMAND_MSG               104
-#define SILC_COMMAND_AWAY              105
+#define SILC_COMMAND_USERS             25
+#define SILC_COMMAND_GETKEY            26
 
 /* Reserved */
 #define SILC_COMMAND_RESERVED           255
+/***/
 
-/* Command Status type */
-typedef unsigned short SilcCommandStatus;
+/****d* silccore/SilcCommandAPI/SilcCommandStatus
+ *
+ * NAME
+ * 
+ *    typedef uint16 SilcCommandStatus;
+ *
+ * DESCRIPTION
+ *
+ *    The SilcCommandStatus type definition and the status defines.
+ *    The server returns a status in each Command Payload indicating
+ *    the status of the command.
+ *
+ * SOURCE
+ */
+typedef uint16 SilcCommandStatus;
 
 /* Command Status messages */
 #define SILC_STATUS_OK                      0
 #define SILC_STATUS_LIST_START              1
-#define SILC_STATUS_LIST_END                2
+#define SILC_STATUS_LIST_ITEM               2
+#define SILC_STATUS_LIST_END                3
 #define SILC_STATUS_ERR_NO_SUCH_NICK        10
 #define SILC_STATUS_ERR_NO_SUCH_CHANNEL     11
 #define SILC_STATUS_ERR_NO_SUCH_SERVER      12
@@ -109,52 +185,266 @@ typedef unsigned short SilcCommandStatus;
 #define SILC_STATUS_ERR_WILDCARDS           16
 #define SILC_STATUS_ERR_NO_CLIENT_ID        17
 #define SILC_STATUS_ERR_NO_CHANNEL_ID       18
-#define SILC_STATUS_ERR_BAD_CLIENT_ID       19
-#define SILC_STATUS_ERR_BAD_CHANNEL_ID      20
-#define SILC_STATUS_ERR_NO_SUCH_CLIENT_ID   21
-#define SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID  22
-#define SILC_STATUS_ERR_NICKNAME_IN_USE     23
-#define SILC_STATUS_ERR_NOT_ON_CHANNEL      24
-#define SILC_STATUS_ERR_USER_ON_CHANNEL     25
-#define SILC_STATUS_ERR_NOT_REGISTERED      26
-#define SILC_STATUS_ERR_NOT_ENOUGH_PARAMS   27
-#define SILC_STATUS_ERR_TOO_MANY_PARAMS     28
-#define SILC_STATUS_ERR_PERM_DENIED         29
-#define SILC_STATUS_ERR_BANNED_FROM_SERVER  30
-#define SILC_STATUS_ERR_BAD_PASSWORD        31
-#define SILC_STATUS_ERR_CHANNEL_IS_FULL     32
-#define SILC_STATUS_ERR_NOT_INVITED         33
-#define SILC_STATUS_ERR_BANNED_FROM_CHANNEL 34
-#define SILC_STATUS_ERR_UNKNOWN_MODE        35
-#define SILC_STATUS_ERR_NOT_YOU             36
-#define SILC_STATUS_ERR_NO_CHANNEL_PRIV     37
-#define SILC_STATUS_ERR_NO_SERVER_PRIV      38
-#define SILC_STATUS_ERR_NO_ROUTER_PRIV      39
-#define SILC_STATUS_ERR_BAD_NICKNAME        40
-#define SILC_STATUS_ERR_BAD_CHANNEL         41
-#define SILC_STATUS_ERR_AUTH_FAILED         42
+#define SILC_STATUS_ERR_NO_SERVER_ID        19
+#define SILC_STATUS_ERR_BAD_CLIENT_ID       20
+#define SILC_STATUS_ERR_BAD_CHANNEL_ID      21
+#define SILC_STATUS_ERR_NO_SUCH_CLIENT_ID   22
+#define SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID  23
+#define SILC_STATUS_ERR_NICKNAME_IN_USE     24
+#define SILC_STATUS_ERR_NOT_ON_CHANNEL      25
+#define SILC_STATUS_ERR_USER_NOT_ON_CHANNEL 26
+#define SILC_STATUS_ERR_USER_ON_CHANNEL     27
+#define SILC_STATUS_ERR_NOT_REGISTERED      28
+#define SILC_STATUS_ERR_NOT_ENOUGH_PARAMS   29
+#define SILC_STATUS_ERR_TOO_MANY_PARAMS     30
+#define SILC_STATUS_ERR_PERM_DENIED         31
+#define SILC_STATUS_ERR_BANNED_FROM_SERVER  32
+#define SILC_STATUS_ERR_BAD_PASSWORD        33
+#define SILC_STATUS_ERR_CHANNEL_IS_FULL     34
+#define SILC_STATUS_ERR_NOT_INVITED         35
+#define SILC_STATUS_ERR_BANNED_FROM_CHANNEL 36
+#define SILC_STATUS_ERR_UNKNOWN_MODE        37
+#define SILC_STATUS_ERR_NOT_YOU             38
+#define SILC_STATUS_ERR_NO_CHANNEL_PRIV     39
+#define SILC_STATUS_ERR_NO_CHANNEL_FOPRIV   40
+#define SILC_STATUS_ERR_NO_SERVER_PRIV      41
+#define SILC_STATUS_ERR_NO_ROUTER_PRIV      42
+#define SILC_STATUS_ERR_BAD_NICKNAME        43
+#define SILC_STATUS_ERR_BAD_CHANNEL         44
+#define SILC_STATUS_ERR_AUTH_FAILED         45
+#define SILC_STATUS_ERR_UNKNOWN_ALGORITHM   46
+#define SILC_STATUS_ERR_NO_SUCH_SERVER_ID   47
+/***/
 
 /* Prototypes */
-SilcCommandPayload silc_command_parse_payload(SilcBuffer buffer);
-SilcBuffer silc_command_encode_payload(SilcCommand cmd,
-                                      unsigned int argc,
+
+/****f* silccore/SilcCommandAPI/silc_command_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcCommandPayload 
+ *    silc_command_payload_parse(const unsigned char *payload,
+ *                               uint32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses command payload returning new command payload structure. The
+ *    `buffer' is the raw payload.
+ *
+ ***/
+SilcCommandPayload silc_command_payload_parse(const unsigned char *payload,
+                                             uint32 payload_len);
+
+/****f* silccore/SilcCommandAPI/silc_command_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_command_payload_encode(SilcCommand cmd,
+ *                                           uint32 argc,
+ *                                           unsigned char **argv,
+ *                                           uint32 *argv_lens,
+ *                                           uint32 *argv_types,
+ *                                           uint16 ident);
+ *
+ * DESCRIPTION
+ *
+ *     Encodes Command Payload returning it to SilcBuffer.
+ *
+ ***/
+SilcBuffer silc_command_payload_encode(SilcCommand cmd,
+                                      uint32 argc,
                                       unsigned char **argv,
-                                      unsigned int *argv_lens,
-                                      unsigned int *argv_types);
-SilcBuffer silc_command_encode_payload_va(SilcCommand cmd, 
-                                         unsigned int argc, ...);
-void silc_command_free_payload(SilcCommandPayload payload);
+                                      uint32 *argv_lens,
+                                      uint32 *argv_types,
+                                      uint16 ident);
+
+/****f* silccore/SilcCommandAPI/silc_command_payload_encode_payload
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer 
+ *    silc_command_payload_encode_payload(SilcCommandPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_command_payload_encode but encodes the buffer from
+ *    SilcCommandPayload structure instead of raw data.
+ *
+ ***/
+SilcBuffer silc_command_payload_encode_payload(SilcCommandPayload payload);
+
+/****f* silccore/SilcCommandAPI/silc_command_payload_encode_va
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_command_payload_encode_va(SilcCommand cmd, 
+ *                                              uint16 ident, 
+ *                                              uint32 argc, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes Command payload with variable argument list. The arguments
+ *    must be: uint32, unsigned char *, unsigned int, ... One 
+ *    {uint32, unsigned char * and unsigned int} forms one argument, 
+ *    thus `argc' in case when sending one {uint32, unsigned char * 
+ *    and uint32} equals one (1) and when sending two of those it
+ *    equals two (2), and so on. This has to be preserved or bad things
+ *    will happen. The variable arguments is: {type, data, data_len}.
+ *
+ ***/
+SilcBuffer silc_command_payload_encode_va(SilcCommand cmd, 
+                                         uint16 ident, 
+                                         uint32 argc, ...);
+
+/****f* silccore/SilcCommandAPI/silc_command_payload_encode_vap
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
+ *                                               uint16 ident, 
+ *                                               uint32 argc, va_list ap);
+ *
+ * DESCRIPTION
+ *
+ *    This is equivalent to the silc_command_payload_encode_va except
+ *    takes the va_list as argument.
+ *
+ ***/
+SilcBuffer silc_command_payload_encode_vap(SilcCommand cmd, 
+                                          uint16 ident, 
+                                          uint32 argc, va_list ap);
+
+/****f* silccore/SilcCommandAPI/silc_command_reply_payload_encode_va
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer 
+ *    silc_command_reply_payload_encode_va(SilcCommand cmd, 
+ *                                         SilcCommandStatus status,
+ *                                         uint16 ident,
+ *                                         uint32 argc, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_command_payload_encode_va except that this is used to 
+ *    encode strictly command reply packets. The command status message
+ *    to be returned is sent as extra argument to this function. The `argc'
+ *    must not count `status' as on argument.
+ *
+ ***/
+SilcBuffer 
+silc_command_reply_payload_encode_va(SilcCommand cmd, 
+                                    SilcCommandStatus status,
+                                    uint16 ident,
+                                    uint32 argc, ...);
+
+/****f* silccore/SilcCommandAPI/silc_command_reply_payload_encode_vap
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer 
+ *    silc_command_reply_payload_encode_vap(SilcCommand cmd, 
+ *                                          SilcCommandStatus status,
+ *                                          uint16 ident, uint32 argc,
+ *                                          va_list ap);
+ *
+ * DESCRIPTION
+ *
+ *    This is equivalent to the silc_command_reply_payload_encode_va except
+ *    takes the va_list as argument.
+ *
+ ***/
+SilcBuffer 
+silc_command_reply_payload_encode_vap(SilcCommand cmd, 
+                                     SilcCommandStatus status,
+                                     uint16 ident, uint32 argc, 
+                                     va_list ap);
+
+/****f* silccore/SilcCommandAPI/silc_command_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_command_payload_free(SilcCommandPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the Command Payload and all data in it.
+ *
+ ***/
+void silc_command_payload_free(SilcCommandPayload payload);
+
+/****f* silccore/SilcCommandAPI/silc_command_get
+ *
+ * SYNOPSIS
+ *
+ *    SilcCommand silc_command_get(SilcCommandPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the command from the payload.
+ *
+ ***/
 SilcCommand silc_command_get(SilcCommandPayload payload);
-unsigned int silc_command_get_arg_num(SilcCommandPayload payload);
-unsigned char *silc_command_get_first_arg(SilcCommandPayload payload,
-                                         unsigned int *ret_len);
-unsigned char *silc_command_get_next_arg(SilcCommandPayload payload,
-                                        unsigned int *ret_len);
-unsigned char *silc_command_get_arg_type(SilcCommandPayload payload,
-                                        unsigned int type,
-                                        unsigned int *ret_len);
-SilcBuffer silc_command_encode_status_payload(SilcCommandStatus status,
-                                             unsigned char *data,
-                                             unsigned int len);
+
+/****f* silccore/SilcCommandAPI/silc_command_get_args
+ *
+ * SYNOPSIS
+ *
+ *    SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the Arguments Payload containing the arguments from the
+ *    Command Payload. The caller must not free it.
+ *
+ ***/
+SilcArgumentPayload silc_command_get_args(SilcCommandPayload payload);
+
+/****f* silccore/SilcCommandAPI/silc_command_get_ident
+ *
+ * SYNOPSIS
+ *
+ *    uint16 silc_command_get_ident(SilcCommandPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the command identifier from the payload. The identifier can
+ *    be used to identify which command reply belongs to which command.
+ *    The client sets the identifier to the payload and server must return
+ *    the same identifier in the command reply.
+ *
+ ***/
+uint16 silc_command_get_ident(SilcCommandPayload payload);
+
+/****f* silccore/SilcCommandAPI/silc_command_set_ident
+ *
+ * SYNOPSIS
+ *
+ *    void silc_command_set_ident(SilcCommandPayload payload, uint16 ident);
+ *
+ * DESCRIPTION
+ *
+ *    Function to set identifier to already allocated Command Payload. Command
+ *    payloads are frequentlly resent in SILC and thusly this makes it easy
+ *    to set the identifier without encoding new Command Payload. 
+ *
+ ***/
+void silc_command_set_ident(SilcCommandPayload payload, uint16 ident);
+
+/****f* silccore/SilcCommandAPI/silc_command_set_command
+ *
+ * SYNOPSIS
+ *
+ *    void silc_command_set_command(SilcCommandPayload payload, 
+ *                                  SilcCommand command);
+ *
+ * DESCRIPTION
+ *
+ *    Function to set the command to already allocated Command Payload. This
+ *    makes it easy to change the command in the payload without encoding new
+ *    Command Payload.
+ *
+ ***/
+void silc_command_set_command(SilcCommandPayload payload, SilcCommand command);
 
 #endif
diff --git a/lib/silccore/silcconfig.c b/lib/silccore/silcconfig.c
deleted file mode 100644 (file)
index 4e0d3d4..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-
-  silcconfig.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* Opens and reads a configuration file to a buffer. The read data is 
-   returned to the ret_buffer argument. */
-
-void silc_config_open(char *filename, SilcBuffer *ret_buffer)
-{
-  char *buffer;
-  int filelen;
-
-  buffer = silc_file_read(filename, &filelen);
-  if (buffer == NULL)
-    return;
-
-  /* Buffer don't have EOF, but we'll need it. */
-  buffer[filelen] = EOF;
-
-  *ret_buffer = silc_buffer_alloc(filelen + 1);
-  silc_buffer_pull_tail(*ret_buffer, filelen + 1);
-  silc_buffer_put(*ret_buffer, buffer, filelen + 1);
-
-  SILC_LOG_DEBUG(("Config file `%s' opened", filename));
-}
-
-/* Returns next token from a buffer to the dest argument. Returns the
-   length of the token. This is used to take tokens from a configuration
-   line. */
-
-int silc_config_get_token(SilcBuffer buffer, char **dest)
-{
-  int len;
-
-  if (strchr(buffer->data, ':')) {
-    len = strcspn(buffer->data, ":");
-    if (len) {
-      *dest = silc_calloc(len + 1, sizeof(char));
-      memset(*dest, 0, len + 1);
-      memcpy(*dest, buffer->data, len);
-    }
-    silc_buffer_pull(buffer, len + 1);
-    return len;
-  }
-
-  return -1;
-}
-
-/* Returns number of tokens in a buffer. */
-
-int silc_config_check_num_token(SilcBuffer buffer)
-{
-  int len, len2, num;
-
-  if (strchr(buffer->data, ':')) {
-    len = 0;
-    num = 0;
-    while (strchr(buffer->data + len, ':')) {
-      num++;
-      len2 = strcspn(buffer->data + len, ":") + 1;
-      len += len2;
-    }
-
-    return num;
-  }
-
-  return 0;
-}
diff --git a/lib/silccore/silcid.c b/lib/silccore/silcid.c
new file mode 100644 (file)
index 0000000..9a35723
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+
+  id.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcid.h"
+
+/* ID lengths (in bytes) without the IP address part */
+#define ID_SERVER_LEN_PART      4
+#define ID_CLIENT_LEN_PART      CLIENTID_HASH_LEN + 1
+#define ID_CHANNEL_LEN_PART     4
+
+/******************************************************************************
+
+                                ID Payload
+
+******************************************************************************/
+
+struct SilcIDPayloadStruct {
+  SilcIdType type;
+  uint16 len;
+  unsigned char *id;
+};
+
+/* Parses buffer and return ID payload into payload structure */
+
+SilcIDPayload silc_id_payload_parse(const unsigned char *payload,
+                                   uint32 payload_len)
+{
+  SilcBufferStruct buffer;
+  SilcIDPayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing ID payload"));
+
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
+  new = silc_calloc(1, sizeof(*new));
+
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&new->type),
+                            SILC_STR_UI_SHORT(&new->len),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  silc_buffer_pull(&buffer, 4);
+
+  if (new->len > buffer.len)
+    goto err;
+
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_XNSTRING_ALLOC(&new->id, new->len),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  silc_buffer_push(&buffer, 4);
+
+  return new;
+
+ err:
+  silc_free(new);
+  return NULL;
+}
+
+/* Return the ID directly from the raw payload data. */
+
+void *silc_id_payload_parse_id(const unsigned char *data, uint32 len)
+{
+  SilcBufferStruct buffer;
+  SilcIdType type;
+  uint16 idlen;
+  unsigned char *id_data = NULL;
+  int ret;
+  void *id;
+
+  silc_buffer_set(&buffer, (unsigned char *)data, len);
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&type),
+                            SILC_STR_UI_SHORT(&idlen),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  silc_buffer_pull(&buffer, 4);
+
+  if (idlen > buffer.len)
+    goto err;
+
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_XNSTRING_ALLOC(&id_data, idlen),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  id = silc_id_str2id(id_data, idlen, type);
+  silc_free(id_data);
+  return id;
+
+ err:
+  return NULL;
+}
+
+/* Encodes ID Payload */
+
+SilcBuffer silc_id_payload_encode(const void *id, SilcIdType type)
+{
+  SilcBuffer buffer;
+  unsigned char *id_data;
+  uint32 len;
+
+  id_data = silc_id_id2str(id, type);
+  len = silc_id_get_len(id, type);
+  buffer = silc_id_payload_encode_data((const unsigned char *)id_data,
+                                      len, type);
+  silc_free(id_data);
+  return buffer;
+}
+
+SilcBuffer silc_id_payload_encode_data(const unsigned char *id,
+                                      uint32 id_len, SilcIdType type)
+{
+  SilcBuffer buffer;
+
+  SILC_LOG_DEBUG(("Encoding %s ID payload",
+                 type == SILC_ID_CLIENT ? "Client" :
+                 type == SILC_ID_SERVER ? "Server" : "Channel"));
+
+  buffer = silc_buffer_alloc(4 + id_len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(type),
+                    SILC_STR_UI_SHORT(id_len),
+                    SILC_STR_UI_XNSTRING(id, id_len),
+                    SILC_STR_END);
+  return buffer;
+}
+
+/* Free ID Payload */
+
+void silc_id_payload_free(SilcIDPayload payload)
+{
+  if (payload) {
+    silc_free(payload->id);
+    silc_free(payload);
+  }
+}
+
+/* Get ID type */
+
+SilcIdType silc_id_payload_get_type(SilcIDPayload payload)
+{
+  return payload ? payload->type : 0;
+}
+
+/* Get ID */
+
+void *silc_id_payload_get_id(SilcIDPayload payload)
+{
+  return payload ? silc_id_str2id(payload->id, payload->len,
+                                  payload->type) : NULL;
+}
+
+/* Get raw ID data. Data is duplicated. */
+
+unsigned char *silc_id_payload_get_data(SilcIDPayload payload)
+{
+  unsigned char *ret;
+
+  if (!payload)
+    return NULL;
+
+  ret = silc_calloc(payload->len, sizeof(*ret));
+  memcpy(ret, payload->id, payload->len);
+  return ret;
+}
+
+/* Get length of ID */
+
+uint32 silc_id_payload_get_len(SilcIDPayload payload)
+{
+  return payload ? payload->len : 0;
+}
+
+/* Converts ID to string. */
+
+unsigned char *silc_id_id2str(const void *id, SilcIdType type)
+{
+  unsigned char *ret_id;
+  SilcServerID *server_id;
+  SilcClientID *client_id;
+  SilcChannelID *channel_id;
+  uint32 id_len = silc_id_get_len(id, type);
+
+  switch(type) {
+  case SILC_ID_SERVER:
+    server_id = (SilcServerID *)id;
+    ret_id = silc_calloc(id_len, sizeof(unsigned char));
+    memcpy(ret_id, server_id->ip.data, server_id->ip.data_len);
+    SILC_PUT16_MSB(server_id->port, &ret_id[4]);
+    SILC_PUT16_MSB(server_id->rnd, &ret_id[6]);
+    return ret_id;
+    break;
+  case SILC_ID_CLIENT:
+    client_id = (SilcClientID *)id;
+    ret_id = silc_calloc(id_len, sizeof(unsigned char));
+    memcpy(ret_id, client_id->ip.data, client_id->ip.data_len);
+    ret_id[4] = client_id->rnd;
+    memcpy(&ret_id[5], client_id->hash, CLIENTID_HASH_LEN);
+    return ret_id;
+    break;
+  case SILC_ID_CHANNEL:
+    channel_id = (SilcChannelID *)id;
+    ret_id = silc_calloc(id_len, sizeof(unsigned char));
+    memcpy(ret_id, channel_id->ip.data, channel_id->ip.data_len);
+    SILC_PUT16_MSB(channel_id->port, &ret_id[4]);
+    SILC_PUT16_MSB(channel_id->rnd, &ret_id[6]);
+    return ret_id;
+    break;
+  }
+
+  return NULL;
+}
+
+/* Converts string to a ID */
+
+void *silc_id_str2id(const unsigned char *id, uint32 id_len, SilcIdType type)
+{
+
+  switch(type) {
+  case SILC_ID_SERVER:
+    {
+      SilcServerID *server_id;
+
+      if (id_len != ID_SERVER_LEN_PART + 4 &&
+         id_len != ID_SERVER_LEN_PART + 16)
+       return NULL;
+
+      server_id = silc_calloc(1, sizeof(*server_id));
+      memcpy(server_id->ip.data, id, (id_len > ID_SERVER_LEN_PART + 4 ?
+                                     16 : 4));
+      server_id->ip.data_len = (id_len > ID_SERVER_LEN_PART + 4 ? 16 : 4);
+      SILC_GET16_MSB(server_id->port, &id[4]);
+      SILC_GET16_MSB(server_id->rnd, &id[6]);
+      return server_id;
+    }
+    break;
+  case SILC_ID_CLIENT:
+    {
+      SilcClientID *client_id;
+
+      if (id_len != ID_CLIENT_LEN_PART + 4 &&
+         id_len != ID_CLIENT_LEN_PART + 16)
+       return NULL;
+
+      client_id = silc_calloc(1, sizeof(*client_id));
+      memcpy(client_id->ip.data, id, (id_len > ID_CLIENT_LEN_PART + 4 ?
+                                     16 : 4));
+      client_id->ip.data_len = (id_len > ID_CLIENT_LEN_PART + 4 ? 16 : 4);
+      client_id->rnd = id[4];
+      memcpy(client_id->hash, &id[5], CLIENTID_HASH_LEN);
+      return client_id;
+    }
+    break;
+  case SILC_ID_CHANNEL:
+    {
+      SilcChannelID *channel_id;
+
+      if (id_len != ID_CHANNEL_LEN_PART + 4 &&
+         id_len != ID_CHANNEL_LEN_PART + 16)
+       return NULL;
+
+      channel_id = silc_calloc(1, sizeof(*channel_id));
+      memcpy(channel_id->ip.data, id, (id_len > ID_CHANNEL_LEN_PART + 4 ?
+                                      16 : 4));
+      channel_id->ip.data_len = (id_len > ID_CHANNEL_LEN_PART + 4 ? 16 : 4);
+      SILC_GET16_MSB(channel_id->port, &id[4]);
+      SILC_GET16_MSB(channel_id->rnd, &id[6]);
+      return channel_id;
+    }
+    break;
+  }
+
+  return NULL;
+}
+
+/* Returns length of the ID */
+
+uint32 silc_id_get_len(const void *id, SilcIdType type)
+{
+  switch(type) {
+  case SILC_ID_SERVER:
+    {
+      SilcServerID *server_id = (SilcServerID *)id;
+      return ID_SERVER_LEN_PART + server_id->ip.data_len;
+    }
+    break;
+  case SILC_ID_CLIENT:
+    {
+      SilcClientID *client_id = (SilcClientID *)id;
+      return ID_CLIENT_LEN_PART + client_id->ip.data_len;
+    }
+    break;
+  case SILC_ID_CHANNEL:
+    {
+      SilcChannelID *channel_id = (SilcChannelID *)id;
+      return ID_CHANNEL_LEN_PART + channel_id->ip.data_len;
+    }
+    break;
+  }
+
+  return 0;
+}
+
+/* Duplicate ID data */
+
+void *silc_id_dup(const void *id, SilcIdType type)
+{
+  switch(type) {
+  case SILC_ID_SERVER:
+    {
+      SilcServerID *server_id = (SilcServerID *)id, *new;
+      new = silc_calloc(1, sizeof(*server_id));
+      memcpy(new, server_id, sizeof(*server_id));
+      return new;
+    }
+    break;
+  case SILC_ID_CLIENT:
+    {
+      SilcClientID *client_id = (SilcClientID *)id, *new;
+      new = silc_calloc(1, sizeof(*client_id));
+      memcpy(new, client_id, sizeof(*client_id));
+      return new;
+    }
+    break;
+  case SILC_ID_CHANNEL:
+    {
+      SilcChannelID *channel_id = (SilcChannelID *)id, *new;
+      new = silc_calloc(1, sizeof(*channel_id));
+      memcpy(new, channel_id, sizeof(*channel_id));
+      return new;
+    }
+    break;
+  }
+
+  return NULL;
+}
diff --git a/lib/silccore/silcid.h b/lib/silccore/silcid.h
new file mode 100644 (file)
index 0000000..975d19d
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+  silcid.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcIDAPI
+ *
+ * DESCRIPTION
+ *
+ * These are important ID types used in SILC. SILC server creates these
+ * but SILC client has to handle these as well since these are used in
+ * packet sending and reception. However, client never creates these
+ * but it receives the correct ID's from server. Clients, servers and
+ * channels are identified by the these ID's.
+ *
+ * The ID's are based on IP addresses. The IP address provides a good
+ * way to distinguish the ID's from other ID's. The ID's supports both
+ * IPv4 and IPv6.
+ *
+ * This file also includes the implementation of the SILC ID Payload
+ * parsing and encoding.
+ *
+ ***/
+
+#ifndef SILCID_H
+#define SILCID_H
+
+/****d* silccore/SilcIDAPI/SilcIdType
+ *
+ * NAME
+ * 
+ *    typedef uint16 SilcIdType;
+ *
+ * DESCRIPTION
+ *
+ *    SILC ID type definitions and the ID types.
+ *
+ * SOURCE
+ */
+typedef uint16 SilcIdType;
+
+/* The SILC ID Types */
+#define SILC_ID_NONE        0
+#define SILC_ID_SERVER      1
+#define SILC_ID_CLIENT      2
+#define SILC_ID_CHANNEL     3
+/***/
+
+/* The ID Lenghts. These are IPv4 based and should be noted if used directly
+   that these cannot be used with IPv6. */
+#define SILC_ID_SERVER_LEN     (64 / 8)
+#define SILC_ID_CLIENT_LEN     (128 / 8)
+#define SILC_ID_CHANNEL_LEN    (64 / 8)
+
+#define CLIENTID_HASH_LEN       (88 / 8) /* Client ID's 88 bit MD5 hash */
+
+/****s* silccore/SilcIDAPI/SilcIDPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcIDPayloadStruct *SilcIDPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual ID Payload and is allocated by
+ *    silc_id_payload_parse and given as argument usually to all
+ *    silc_id_payload_* functions.  It is freed by the function
+ *    silc_id_payload_free.
+ *
+ ***/
+typedef struct SilcIDPayloadStruct *SilcIDPayload;
+
+/****s* silccore/SilcIDAPI/SilcArgumentPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcArgumentPayloadStruct *SilcArgumentPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Argument Payload and is allocated
+ *    by silc_argument_payload_parse and given as argument usually to
+ *    all silc_argument_payload_* functions.  It is freed by the
+ *    silc_argument_payload_free function.
+ *
+ ***/
+typedef struct SilcArgumentPayloadStruct *SilcArgumentPayload;
+
+/* Prototypes */
+
+/****f* silccore/SilcIDAPI/silc_id_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcIDPayload silc_id_payload_parse(const unsigned char *payload,
+ *                                        uint32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parses buffer and return ID payload into payload structure. The
+ *    `buffer' is raw payload buffer.
+ *
+ ***/
+SilcIDPayload silc_id_payload_parse(const unsigned char *payload,
+                                   uint32 payload_len);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_parse_id
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_payload_parse_id(const unsigned char *data, uint32 len);
+ *
+ * DESCRIPTION
+ *
+ *    Return ID directly from the raw ID Payload data buffer. The
+ *    caller must free the returned ID.
+ *
+ ***/
+void *silc_id_payload_parse_id(const unsigned char *data, uint32 len);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_id_payload_encode(const void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes ID Payload. The `id' is the ID of the type `type' to put
+ *    into the payload. Returns the encoded payload buffer.
+ *
+ ***/
+SilcBuffer silc_id_payload_encode(const void *id, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_encode_data
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_id_payload_encode_data(const unsigned char *id,
+ *                                           uin32 id_len, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes ID Payload. The `id' is raw ID data of the length of `id_len'
+ *    of type of `type'. Returns the encoded payload buffer.
+ *
+ ***/
+SilcBuffer silc_id_payload_encode_data(const unsigned char *id,
+                                      uint32 id_len, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_id_payload_free(SilcIDPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the ID Payload and all data in it.
+ *
+ ***/
+void silc_id_payload_free(SilcIDPayload payload);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_get_type
+ *
+ * SYNOPSIS
+ *
+ *    SilcIdType silc_id_payload_get_type(SilcIDPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the ID type from the ID Payload. The type tells the
+ *    type of the ID in the payload.
+ *
+ ***/
+SilcIdType silc_id_payload_get_type(SilcIDPayload payload);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_get_id
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_payload_get_id(SilcIDPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the ID in the ID Payload. The caller must free the
+ *    returned ID.
+ *
+ ***/
+void *silc_id_payload_get_id(SilcIDPayload payload);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_get_data
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_id_payload_get_data(SilcIDPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the raw ID data from the ID Payload. The data is duplicated
+ *    and the caller must free it.
+ *
+ ***/
+unsigned char *silc_id_payload_get_data(SilcIDPayload payload);
+
+/****f* silccore/SilcIDAPI/silc_id_payload_get_len
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_id_payload_get_len(SilcIDPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the length of the ID in the ID Payload.
+ *
+ ***/
+uint32 silc_id_payload_get_len(SilcIDPayload payload);
+
+/****s* silccore/SilcIDAPI/SilcIDIP
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcIDIP;
+ *
+ * DESCRIPTION
+ *
+ *    Generic IP address structure to indicate either IPv4 or IPv6 address.
+ *    This structure is used inside all SILC ID's. The true length of the
+ *    ID depends of the length of the IP address.
+ *
+ * SOURCE
+ */
+typedef struct {
+  unsigned char data[16];      /* IP data (in MSB first order) */
+  uint8 data_len;              /* Length of the data (4 or 16) */
+} SilcIDIP;
+/***/
+
+/****s* silccore/SilcIDAPI/SilcServerID
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcServerID;
+ *
+ * DESCRIPTION
+ *
+ *    64 or 160 bit SilcServerID structure:
+ *  
+ *     n bit IP address
+ *    16 bit port
+ *    16 bit random number
+ *
+ * SOURCE
+ */
+typedef struct {
+  SilcIDIP ip;                 /* n bit IP address */
+  uint16 port;                 /* 16 bit port */
+  uint16 rnd;                  /* 16 bit random number */
+} SilcServerID;
+/***/
+
+/****s* silccore/SilcIDAPI/SilcClientID
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcClientID;
+ *
+ * DESCRIPTION
+ *
+ *    128 or 224 bit SilcClientID structure:
+ *
+ *      n bit ServerID IP address [bits 1-32 or bits 1-128]
+ *      8 bit random number
+ *     88 bit hash value from nickname
+ *
+ * SOURCE
+ */
+typedef struct {
+  SilcIDIP ip;                                 /* n bit IP address */
+  unsigned char rnd;                           /* 8 bit random number */
+  unsigned char hash[CLIENTID_HASH_LEN];       /* 88 bit MD5 hash */
+} SilcClientID;
+/***/
+
+/****s* silccore/SilcIDAPI/SilcChannelID
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcChannelID;
+ *
+ * DESCRIPTION
+ *
+ *    64 or 160 bit SilcChannel ID structure:
+ *
+ *     n bit Router's ServerID IP address [bits 1-32 or bits 1-128]
+ *    16 bit Router's ServerID port [bits 33-48 or bits 129-144]
+ *    16 bit random number
+ *
+ * SOURCE
+ */
+typedef struct {
+  SilcIDIP ip;                                 /* n bit IP address */
+  uint16 port;                                 /* 16 bit port */
+  uint16 rnd;                                  /* 16 bit random number */
+} SilcChannelID;
+/***/
+
+/* Macros */
+
+/****d* silccore/SilcIDAPI/SILC_ID_COMPARE
+ *
+ * NAME
+ * 
+ *    #define SILC_ID_COMPARE ...
+ *
+ * DESCRIPTION
+ *
+ *    Compares two ID's. Returns TRUE if they match and FALSE if they do
+ *    not.
+ *
+ * SOURCE
+ */
+#define SILC_ID_COMPARE(id1, id2, len) (!memcmp(id1, id2, len))
+/***/
+
+/****d* silccore/SilcIDAPI/SILC_ID_CLIENT_COMPARE
+ *
+ * NAME
+ * 
+ *    #define SILC_ID_CLIENT_COMPARE ...
+ *
+ * DESCRIPTION
+ *
+ *    Compares Client ID's. Returns TRUE if they match.
+ *
+ * SOURCE
+ */
+#define SILC_ID_CLIENT_COMPARE(id1, id2) \
+  SILC_ID_COMPARE(id1, id2, sizeof(SilcClientID))
+/***/
+
+/****d* silccore/SilcIDAPI/SILC_ID_SERVER_COMPARE
+ *
+ * NAME
+ * 
+ *    #define SILC_ID_SERVER_COMPARE ...
+ *
+ * DESCRIPTION
+ *
+ *    Compares Server ID's. Returns TRUE if they match.
+ *
+ * SOURCE
+ */
+#define SILC_ID_SERVER_COMPARE(id1, id2) \
+  SILC_ID_COMPARE(id1, id2, sizeof(SilcServerID))
+/***/
+
+/****d* silccore/SilcIDAPI/SILC_ID_CHANNEL_COMPARE
+ *
+ * NAME
+ * 
+ *    #define SILC_ID_CHANNEL_COMPARE ...
+ *
+ * DESCRIPTION
+ *
+ *    Compares Channel ID's. Returns TRUE if they match.
+ *
+ * SOURCE
+ */
+#define SILC_ID_CHANNEL_COMPARE(id1, id2) \
+  SILC_ID_COMPARE(id1, id2, sizeof(SilcChannelID))
+/***/
+
+/****d* silccore/SilcIDAPI/SILC_ID_COMPARE_TYPE
+ *
+ * NAME
+ * 
+ *    #define SILC_ID_COMPARE_TYPE ...
+ *
+ * DESCRIPTION
+ *
+ *    Compares two ID's by type. Returns TRUE if they match.
+ *
+ * SOURCE
+ */
+#define SILC_ID_COMPARE_TYPE(id1, id2, type)                   \
+  (type == SILC_ID_SERVER ? SILC_ID_SERVER_COMPARE(id1, id2) : \
+   type == SILC_ID_CLIENT ? SILC_ID_CLIENT_COMPARE(id1, id2) : \
+   SILC_ID_CHANNEL_COMPARE(id1, id2))
+/***/
+
+/****d* silccore/SilcIDAPI/SILC_ID_COMPARE_HASH
+ *
+ * NAME
+ * 
+ *    #define SILC_ID_COMPARE_HASH ...
+ *
+ * DESCRIPTION
+ *
+ *    Compares the nickname hash of the Client ID. Returns TRUE if
+ *    they match. Since the nickname hash is based on the nickname of
+ *    the client this can be used to search the ID by nickname (taking
+ *    the hash out of it) or using the hash from the ID.
+ *
+ * SOURCE
+ */
+#define SILC_ID_COMPARE_HASH(id1, id2) \
+  (!memcmp((id1)->hash, (id2)->hash, CLIENTID_HASH_LEN))
+/***/
+
+/* Prototypes */
+
+/****f* silccore/SilcIDAPI/silc_id_id2str
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_id_id2str(const void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Converts an ID of type `type' to data. This can be used to
+ *    convert the ID's to data for inclusion in the packets.
+ *
+ ***/
+unsigned char *silc_id_id2str(const void *id, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_str2id
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_str2id(const unsigned char *id, uint32 id_len, 
+ *                         SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Converts ID data string to an ID. This can be used to get the
+ *    ID out of data that has been taken for example from packet.
+ *
+ ***/
+void *silc_id_str2id(const unsigned char *id, uint32 id_len, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_get_len
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_id_get_len(const void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the true length of the ID of the type `type'.
+ *
+ ***/
+uint32 silc_id_get_len(const void *id, SilcIdType type);
+
+/****f* silccore/SilcIDAPI/silc_id_dup
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_id_dup(const void *id, SilcIdType type);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicates the ID of the type `type'. The caller must free the
+ *    duplicated ID.
+ *
+ ***/
+void *silc_id_dup(const void *id, SilcIdType type);
+
+#endif
diff --git a/lib/silccore/silcidcache.c b/lib/silccore/silcidcache.c
new file mode 100644 (file)
index 0000000..5cf2533
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+
+  silcidcache.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2000 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcidcache.h"
+
+/* Static prototypes */
+static void silc_idcache_destructor(void *key, void *context,
+                                   void *user_context);
+static SilcIDCacheList silc_idcache_list_alloc();
+static void silc_idcache_list_add(SilcIDCacheList list, 
+                                 SilcIDCacheEntry cache);
+
+/*
+   SILC ID Cache object.
+
+   This is context for the ID cache system. This includes all the cache
+   entries and other internal data. This is read-only object and not
+   visible outside this cache system.
+
+   Fields are as follows:
+
+   SilcHashTable id_table
+
+       Hash table using the ID as the key.
+
+   SilcHashTable name_table
+
+       Hash table using the name as the key.
+
+   SilcHashTable context_table
+
+       Hash table using the context as the key.
+
+   SilcIDCacheDestructor destructor
+
+       Destructor callback that is called when an cache entry expires or is
+       purged from the ID cache. The application must not free cache entry
+       because the library will do it automatically. The appliation, however,
+       is responsible of freeing any data in the entry.
+
+   SilcIdType id_type
+
+       Indicates the type of the ID's this cache holds.
+
+*/
+struct SilcIDCacheStruct {
+  SilcHashTable id_table;
+  SilcHashTable name_table;
+  SilcHashTable context_table;
+  SilcIDCacheDestructor destructor;
+  SilcIdType type;
+};
+
+/* 
+   ID Cache list.
+   
+   This is returned when searching the cache. Enumeration functions are
+   provided to traverse the list; actually this is used as table not as
+   list. :)
+
+   By default the found cache entries are saved into the static cache
+   table to provide access without reallocation. However, if the static
+   table is full, rest of the cache entries are dynamically allocated
+   into `cache_dyn' table. Traversing functions automatically handles
+   these situations.
+
+*/
+struct SilcIDCacheListStruct {
+  SilcIDCacheEntry cache[64];
+  SilcIDCacheEntry *cache_dyn;
+  uint32 cache_dyn_count;
+  uint32 cache_count;
+  uint32 pos;
+  bool dyn;
+};
+
+/* Allocates new ID cache object. The initial amount of allocated entries
+   can be sent as argument. If `count' is 0 the system uses default values. 
+   The `id_type' defines the types of the ID's that will be saved to the
+   cache. */
+
+SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
+                              SilcIDCacheDestructor destructor)
+{
+  SilcIDCache cache;
+
+  SILC_LOG_DEBUG(("Allocating new cache"));
+
+  cache = silc_calloc(1, sizeof(*cache));
+  cache->id_table = silc_hash_table_alloc(count, silc_hash_id, 
+                                         (void *)(uint32)id_type,
+                                         silc_hash_id_compare, 
+                                         (void *)(uint32)id_type, 
+                                         silc_idcache_destructor, NULL, 
+                                         FALSE);
+  cache->name_table = silc_hash_table_alloc(count, silc_hash_string, NULL,
+                                           silc_hash_string_compare, NULL, 
+                                           NULL, NULL, FALSE);
+  cache->context_table = silc_hash_table_alloc(count, silc_hash_ptr, NULL,
+                                              NULL, NULL, NULL, NULL, FALSE);
+  cache->destructor = destructor;
+  cache->type = id_type;
+
+  return cache;
+}
+
+/* Frees ID cache object and cache entries */
+
+void silc_idcache_free(SilcIDCache cache)
+{
+  if (cache) {
+    silc_hash_table_free(cache->id_table);
+    silc_hash_table_free(cache->name_table);
+    silc_hash_table_free(cache->context_table);
+    silc_free(cache);
+  }
+}
+
+/* Add new entry to the cache. Returns TRUE if the entry was added and
+   FALSE if it could not be added. The `name' is the name associated with
+   the ID, the `id' the actual ID and the `context' a used specific context.
+   If the `expire' is TRUE the entry expires in default time and if FALSE
+   the entry never expires from the cache. */
+
+bool silc_idcache_add(SilcIDCache cache, char *name, void *id, 
+                     void *context, int expire, SilcIDCacheEntry *ret)
+{
+  SilcIDCacheEntry c;
+
+  SILC_LOG_DEBUG(("Adding cache entry"));
+
+  /* Allocate new cache entry */
+  c = silc_calloc(1, sizeof(*c));
+  c->id = id;
+  c->name = name;
+  c->expire = expire;
+  c->context = context;
+
+  /* Add the new entry to the hash tables */
+
+  if (id)
+    silc_hash_table_add(cache->id_table, id, c);
+  if (name)
+    silc_hash_table_add(cache->name_table, name, c);
+  if (context)
+    silc_hash_table_add(cache->context_table, context, c);
+
+  /* See whether we have time to rehash the tables */
+  if ((silc_hash_table_count(cache->id_table) / 2) >
+      silc_hash_table_size(cache->id_table)) {
+    silc_hash_table_rehash(cache->id_table, 0);
+    silc_hash_table_rehash(cache->name_table, 0);
+    silc_hash_table_rehash(cache->context_table, 0);
+  }
+
+  if (ret)
+    *ret = c;
+
+  return TRUE;
+}
+
+/* Destructor for the ID Cache entry */
+
+static void silc_idcache_destructor(void *key, void *context,
+                                   void *user_context)
+{
+  silc_free(context);
+}
+
+/* Delete cache entry from cache. */
+
+bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old)
+{
+  bool ret = FALSE;
+
+  SILC_LOG_DEBUG(("Deleting cache entry"));
+
+  if (old->name)
+    ret = silc_hash_table_del_by_context(cache->name_table, old->name, old);
+  if (old->context)
+    ret = silc_hash_table_del(cache->context_table, old->context);
+  if (old->id)
+    ret = silc_hash_table_del(cache->id_table, old->id);
+  else
+    silc_free(old);
+
+  return ret;
+}
+
+/* Deletes ID cache entry by ID. */
+
+bool silc_idcache_del_by_id(SilcIDCache cache, void *id)
+{
+  SilcIDCacheEntry c;
+
+  if (!silc_hash_table_find(cache->id_table, id, NULL, (void *)&c))
+    return FALSE;
+
+  return silc_idcache_del(cache, c);
+}
+
+/* Same as above but with specific hash and comparison functions. If the
+   functions are NULL then default values are used. */
+
+bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
+                               SilcHashFunction hash, 
+                               void *hash_context,
+                               SilcHashCompare compare, 
+                               void *compare_context)
+{
+  SilcIDCacheEntry c;
+  bool ret = FALSE;
+
+  SILC_LOG_DEBUG(("Deleting cache entry"));
+
+  if (!silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)&c,
+                               hash, hash_context, compare, 
+                               compare_context))
+    return FALSE;
+
+  if (c->name)
+    ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
+  if (c->context)
+    ret = silc_hash_table_del(cache->context_table, c->context);
+  if (c->id)
+    ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
+                                 hash_context, compare, compare_context,
+                                 NULL, NULL);
+
+  return ret;
+}
+
+/* Deletes ID cache entry by context. */
+
+bool silc_idcache_del_by_context(SilcIDCache cache, void *context)
+{
+  SilcIDCacheEntry c;
+  bool ret = FALSE;
+
+  SILC_LOG_DEBUG(("Deleting cache entry"));
+
+  if (!silc_hash_table_find(cache->context_table, context, NULL, (void *)&c))
+    return FALSE;
+
+  if (c->name)
+    ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
+  if (c->context)
+    ret = silc_hash_table_del(cache->context_table, c->context);
+  if (c->id)
+    ret = silc_hash_table_del_by_context(cache->id_table, c->id, c);
+  else
+    silc_free(c);
+
+  return ret;
+}
+
+/* Deletes all ID entries from cache. Free's memory as well. */
+
+bool silc_idcache_del_all(SilcIDCache cache)
+{
+  silc_hash_table_free(cache->id_table);
+  silc_hash_table_free(cache->name_table);
+  silc_hash_table_free(cache->context_table);
+
+  return TRUE;
+}
+
+static void silc_idcache_destructor_dummy(void *key, void *context,
+                                         void *user_context)
+{
+  /* Dummy - nothing */
+}
+
+/* Foreach callback fro silc_idcache_purge. */
+
+static void silc_idcache_purge_foreach(void *key, void *context,
+                                      void *user_context)
+{
+  SilcIDCache cache = (SilcIDCache)user_context;
+  uint32 curtime = time(NULL);
+  SilcIDCacheEntry c = (SilcIDCacheEntry)context;
+
+  if (c->expire && c->expire < curtime) {
+    /* Remove the entry from the hash tables */
+    if (c->name)
+      silc_hash_table_del_by_context(cache->name_table, c->name, c);
+    if (c->context)
+      silc_hash_table_del(cache->context_table, c->context);
+    if (c->id)
+      silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
+                                        NULL, NULL, NULL, NULL, 
+                                        silc_idcache_destructor_dummy, NULL);
+
+    /* Call the destructor */
+    if (cache->destructor)
+      cache->destructor(cache, c);
+
+    /* Free the entry, it has been deleted from the hash tables */
+    silc_free(c);
+  }
+}
+
+/* Purges the cache by removing expired cache entires. Note that this
+   may be very slow operation. */
+
+bool silc_idcache_purge(SilcIDCache cache)
+{
+  silc_hash_table_foreach(cache->id_table, silc_idcache_purge_foreach, cache);
+  return TRUE;
+}
+
+/* Purges the specific entry by context. */
+
+bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
+{
+  SilcIDCacheEntry c;
+  bool ret = FALSE;
+
+  if (!silc_hash_table_find(cache->context_table, context, NULL, 
+                           (void *)&c))
+    return FALSE;
+
+    /* Remove the entry from the hash tables */
+  if (c->name)
+    ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
+  if (c->context)
+    ret = silc_hash_table_del(cache->context_table, c->context);
+  if (c->id)
+    ret =
+      silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
+                                        NULL, NULL, NULL, NULL, 
+                                        silc_idcache_destructor_dummy, NULL);
+  
+  /* Call the destructor */
+  if (cache->destructor)
+    cache->destructor(cache, c);
+
+  /* Free the entry, it has been deleted from the hash tables */
+  silc_free(c);
+
+  return ret;
+}
+
+/* Callback that is called by the hash table routine when traversing
+   entrys in the hash table. */
+
+static void silc_idcache_get_all_foreach(void *key, void *context,
+                                        void *user_context)
+{
+  SilcIDCacheList list = (SilcIDCacheList)user_context;
+  silc_idcache_list_add(list, (SilcIDCacheEntry)context);
+}
+
+/* Returns all cache entrys from the ID cache to the `ret' ID Cache List. */
+
+bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret)
+{
+  SilcIDCacheList list;
+
+  if (!ret)
+    return TRUE;
+
+  list = silc_idcache_list_alloc();
+  silc_hash_table_foreach(cache->id_table, silc_idcache_get_all_foreach, list);
+
+  if (silc_idcache_list_count(list) == 0) {
+    silc_idcache_list_free(list);
+    return FALSE;
+  }
+
+  *ret = list;
+
+  return TRUE;
+}
+
+/* Find ID Cache entry by ID. May return multiple entries. */
+
+bool silc_idcache_find_by_id(SilcIDCache cache, void *id, 
+                            SilcIDCacheList *ret)
+{
+  SilcIDCacheList list;
+
+  list = silc_idcache_list_alloc();
+
+  if (!ret)
+    return TRUE;
+
+  silc_hash_table_find_foreach(cache->id_table, id,
+                              silc_idcache_get_all_foreach, list);
+
+  if (silc_idcache_list_count(list) == 0) {
+    silc_idcache_list_free(list);
+    return FALSE;
+  }
+
+  *ret = list;
+
+  return TRUE;
+}
+
+/* Find specific ID with specific hash function and comparison functions.
+   If `hash' is NULL then the default hash funtion is used and if `compare'
+   is NULL default comparison function is used. */
+
+bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id, 
+                                    SilcHashFunction hash, 
+                                    void *hash_context,
+                                    SilcHashCompare compare, 
+                                    void *compare_context,
+                                    SilcIDCacheEntry *ret)
+{
+  return silc_hash_table_find_ext(cache->id_table, id, NULL, (void *)ret,
+                                 hash, hash_context, compare, 
+                                 compare_context);
+}
+
+/* Find one specific ID entry. */
+
+bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id, 
+                                SilcIDCacheEntry *ret)
+{
+  return silc_hash_table_find(cache->id_table, id, NULL, (void *)ret);
+}
+
+/* Finds cache entry by context. */
+
+bool silc_idcache_find_by_context(SilcIDCache cache, void *context, 
+                                 SilcIDCacheEntry *ret)
+{
+  return silc_hash_table_find(cache->context_table, context, NULL, 
+                             (void *)ret);
+}
+
+/* Find ID Cache entry by name. Returns list of cache entries. */
+
+bool silc_idcache_find_by_name(SilcIDCache cache, char *name,
+                              SilcIDCacheList *ret)
+{
+  SilcIDCacheList list;
+
+  list = silc_idcache_list_alloc();
+
+  if (!ret)
+    return TRUE;
+
+  silc_hash_table_find_foreach(cache->name_table, name, 
+                              silc_idcache_get_all_foreach, list);
+
+  if (silc_idcache_list_count(list) == 0) {
+    silc_idcache_list_free(list);
+    return FALSE;
+  }
+
+  *ret = list;
+
+  return TRUE;
+}
+
+/* Find ID Cache entry by name. Returns one cache entry. */
+
+bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
+                                  SilcIDCacheEntry *ret)
+{
+  if (!silc_hash_table_find(cache->name_table, name, NULL, (void *)ret))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Allocates ID cache list. */
+
+static SilcIDCacheList silc_idcache_list_alloc()
+{
+  SilcIDCacheList list;
+
+  list = silc_calloc(1, sizeof(*list));
+
+  return list;
+}
+
+/* Adds cache entry to the ID cache list. If needed reallocates memory
+   for the list. */
+
+static void silc_idcache_list_add(SilcIDCacheList list, SilcIDCacheEntry cache)
+{
+  int i;
+
+  /* Try to add to static cache */
+  if (!list->cache_dyn_count)
+    for (i = 0; i < (sizeof(list->cache) / sizeof(list->cache[0])); i++) {
+      if (!list->cache[i]) {
+       list->cache[i] = cache;
+       list->cache_count++;
+       return;
+      }
+    }
+
+  /* Static cache is full, allocate dynamic cache */
+  for (i = 0; i < list->cache_dyn_count; i++) {
+    if (!list->cache_dyn[i]) {
+      list->cache_dyn[i] = cache;
+      list->cache_count++;
+      break;
+    }
+  }
+
+  if (i >= list->cache_dyn_count) {
+    int k;
+
+    i = list->cache_dyn_count;
+    list->cache_dyn = silc_realloc(list->cache_dyn, 
+                                  sizeof(*list->cache_dyn) * (i + 5));
+
+    /* NULL the reallocated area */
+    for (k = i; k < (i + 5); k++)
+      list->cache_dyn[k] = NULL;
+
+    list->cache_dyn[i] = cache;
+    list->cache_count++;
+    list->cache_dyn_count += 5;
+  }
+}
+
+/* Returns number of cache entries in the ID cache list. */
+
+int silc_idcache_list_count(SilcIDCacheList list)
+{
+  return list->cache_count;
+}
+
+/* Returns first entry from the ID cache list. */
+
+bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret)
+{
+  list->pos = 0;
+
+  if (!list->cache[list->pos])
+    return FALSE;
+  
+  if (ret)
+    *ret = list->cache[list->pos];
+
+  return TRUE;
+}
+
+/* Returns next entry from the ID cache list. */
+
+bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret)
+{
+  list->pos++;
+
+  if (!list->dyn &&
+      list->pos >= (sizeof(list->cache) / sizeof(list->cache[0]))) {
+    list->pos = 0;
+    list->dyn = TRUE;
+  }
+
+  if (list->dyn && list->pos >= list->cache_dyn_count)
+    return FALSE;
+
+  if (!list->dyn && !list->cache[list->pos])
+    return FALSE;
+  
+  if (list->dyn && !list->cache_dyn[list->pos])
+    return FALSE;
+  
+  if (ret) {
+    if (!list->dyn)
+      *ret = list->cache[list->pos];
+    else
+      *ret = list->cache_dyn[list->pos];
+  }
+  
+  return TRUE;
+}
+
+/* Frees ID cache list. User must free the list object returned by
+   any of the searching functions. */
+
+void silc_idcache_list_free(SilcIDCacheList list)
+{
+  if (list) {
+    if (list->cache_dyn)
+      silc_free(list->cache_dyn);
+    silc_free(list);
+  }
+}
diff --git a/lib/silccore/silcidcache.h b/lib/silccore/silcidcache.h
new file mode 100644 (file)
index 0000000..e5c86a5
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+  silcidcache.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 2000 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcIDCacheAPI
+ *
+ * DESCRIPTION
+ * 
+ * SILC ID Cache is an cache for all kinds of ID's used in the SILC
+ * protocol.  Application can save here the ID's it uses and the interface
+ * provides fast retrieval of the ID's from the cache.
+ *
+ ***/
+
+#ifndef SILCIDCACHE_H
+#define SILCIDCACHE_H
+
+/****s* silccore/SilcIDCacheAPI/SilcIDCacheEntry
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcIDCacheEntry;
+ *
+ * DESCRIPTION
+ *
+ *    This is one entry in the SILC ID Cache system. Contents of this is
+ *    allocated outside the ID cache system, however, all the fields are 
+ *    filled with ID cache utility functions. The ID cache system does not
+ *    allocate any of these fields nor free them.
+ *
+ *    void *id
+ *
+ *      The actual ID.
+ *
+ *    char name
+ *
+ *      A name associated with the ID.
+ *
+ *    uint32 expire
+ *
+ *      Time when this cache entry expires.  This is normal time() value
+ *      plus the validity.  Cache entry has expired if current time is
+ *      more than value in this field.  If this value is zero (0) the
+ *      entry never expires.
+ *
+ *    void *context
+ *
+ *      Any caller specified context.
+ *
+ * SOURCE
+ */
+typedef struct {
+  void *id;
+  char *name;
+  uint32 expire;
+  void *context;
+} *SilcIDCacheEntry;
+/***/
+
+/****s* silccore/SilcIDCacheAPI/SilcIDCache
+ *
+ * NAME
+ * 
+ *    typedef struct SilcIDCacheStruct *SilcIDCache;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual ID Cache and is allocated by 
+ *    silc_idcache_alloc and given as argument usually to all 
+ *    silc_idcache_* functions.  It is freed by the
+ *    silc_idcache_free function.
+ *
+ ***/
+typedef struct SilcIDCacheStruct *SilcIDCache;
+
+/****s* silccore/SilcIDCacheAPI/SilcIDCacheList
+ *
+ * NAME
+ * 
+ *    typedef struct SilcIDCacheListStruct *SilcIDCacheList;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the ID Cache List and is allocated by 
+ *    some of the silc_idcache_* functions. Functions that may return
+ *    multiple entries from the cache allocate the entries in to the
+ *    SilcIDCacheList. The context is freed by silc_idcache_list_free
+ *    function.
+ *
+ ***/
+typedef struct SilcIDCacheListStruct *SilcIDCacheList;
+
+/****f* silccore/SilcIDCacheAPI/SilcIDCacheDestructor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcIDCacheDestructor)(SilcIDCache cache,
+ *                                          SilcIDCacheEntry entry);
+ *
+ * DESCRIPTION
+ *
+ *    Destructor callback that is called when an cache entry expires or is
+ *    purged from the ID cache. The application must not free cache entry
+ *    because the library will do it automatically. The appliation, however,
+ *    is responsible of freeing any data in the entry.
+ *
+ ***/
+typedef void (*SilcIDCacheDestructor)(SilcIDCache cache,
+                                     SilcIDCacheEntry entry);
+
+#define SILC_ID_CACHE_EXPIRE 3600
+#define SILC_ID_CACHE_EXPIRE_DEF (time(NULL) + SILC_ID_CACHE_EXPIRE)
+
+/* Prototypes */
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
+ *                                   SilcIDCacheDestructor destructor);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates new ID cache object. The initial amount of allocated entries
+ *    can be sent as argument. If `count' is 0 the system uses default values. 
+ *    The `id_type' defines the types of the ID's that will be saved to the
+ *    cache.
+ *
+ ***/
+SilcIDCache silc_idcache_alloc(uint32 count, SilcIdType id_type,
+                              SilcIDCacheDestructor destructor);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_idcache_free(SilcIDCache cache);
+ *
+ * DESCRIPTION
+ *
+ *    Frees ID cache object and all cache entries.
+ *
+ ***/
+void silc_idcache_free(SilcIDCache cache);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_add
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_add(SilcIDCache cache, char *name, void *id, 
+ *                          void *context, int expire);
+ *
+ * DESCRIPTION
+ *
+ *    Add new entry to the cache. Returns TRUE if the entry was added and
+ *    FALSE if it could not be added. The `name' is the name associated with
+ *    the ID, the `id' the actual ID and the `context' a used specific context.
+ *    If the `expire' is non-zero the entry expires in that specified time.
+ *    If zero the entry never expires from the cache.
+ *
+ *    If the `ret' is non-NULL the created ID Cache entry is returned to 
+ *    that pointer.
+ *
+ ***/
+bool silc_idcache_add(SilcIDCache cache, char *name, void *id, 
+                     void *context, int expire, SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_del
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old);
+ *
+ * DESCRIPTION
+ *
+ *    Delete cache entry from cache. Returns TRUE if the entry was deleted.
+ *    The destructor function is not called.
+ *
+ ***/
+bool silc_idcache_del(SilcIDCache cache, SilcIDCacheEntry old);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_del_by_id
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_del_by_id(SilcIDCache cache, void *id);
+ *
+ * DESCRIPTION
+ *
+ *    Delete cache entry by ID. Returns TRUE if the entry was deleted.
+ *    The destructor function is not called.
+ *
+ ***/
+bool silc_idcache_del_by_id(SilcIDCache cache, void *id);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_del_by_id_ext
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
+ *                                    SilcHashFunction hash, 
+ *                                    void *hash_context,
+ *                                    SilcHashCompare compare, 
+ *                                    void *compare_context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_idcache_del_by_id but with specific hash and comparison
+ *    functions. If the functions are NULL then default values are used.
+ *    Returns TRUE if the entry was deleted. The destructor function is
+ *    not called.
+ *
+ ***/
+bool silc_idcache_del_by_id_ext(SilcIDCache cache, void *id,
+                               SilcHashFunction hash, 
+                               void *hash_context,
+                               SilcHashCompare compare, 
+                               void *compare_context);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_del_by_context
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_del_by_context(SilcIDCache cache, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes cachen entry by the user specified context. Returns TRUE
+ *    if the entry was deleted. The destructor function is not called.
+ *
+ ***/
+bool silc_idcache_del_by_context(SilcIDCache cache, void *context);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_del_all
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_del_all(SilcIDCache cache);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes all cache entries from the cache and frees all memory.
+ *    The destructor function is not called.
+ *
+ ***/
+bool silc_idcache_del_all(SilcIDCache cache);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_purge
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_purge(SilcIDCache cache);
+ *
+ * DESCRIPTION
+ *
+ *    Purges the cache by removing expired cache entires. Note that this
+ *    may be very slow operation. Returns TRUE if the purging was successful.
+ *    The destructor function is called for each purged cache entry.
+ *
+ ***/
+bool silc_idcache_purge(SilcIDCache cache);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_by_context
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_purge_by_context(SilcIDCache cache, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Purges the cache by context and removes expired cache entires. 
+ *    Returns TRUE if the puring was successful. The destructor function
+ *    is called for the purged cache entry.
+ *
+ ***/
+bool silc_idcache_purge_by_context(SilcIDCache cache, void *context);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_get_all
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Returns all cache entries from the ID cache to the `ret' SilcIDCacheList.
+ *    Returns TRUE if the retrieval was successful. The caller must free
+ *    the returned SilcIDCacheList.
+ *
+ ***/
+bool silc_idcache_get_all(SilcIDCache cache, SilcIDCacheList *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_find_by_id
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_find_by_id(SilcIDCache cache, void *id, 
+ *                                 SilcIDCacheList *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Find ID Cache entry by ID. This may return multiple entry and the
+ *    `ret' SilcIDCacheList is allocated. Returns TRUE if the entry was
+ *    found. The caller must free the returned SilcIDCacheList.
+ *
+ ***/
+bool silc_idcache_find_by_id(SilcIDCache cache, void *id, 
+                            SilcIDCacheList *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_find_by_id_one
+ *
+ * SYNOPSIS
+ *
+ *     bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id, 
+ *                                      SilcIDCacheEntry *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Find ID Cache entry by ID. Returns only one entry from the cache
+ *    and the found entry is considered to be exact match. Returns TRUE
+ *    if the entry was found.
+ *
+ ***/
+bool silc_idcache_find_by_id_one(SilcIDCache cache, void *id, 
+                                SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_find_by_id_one_ext
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id, 
+ *                                         SilcHashFunction hash, 
+ *                                         void *hash_context,
+ *                                         SilcHashCompare compare, 
+ *                                         void *compare_context,
+ *                                         SilcIDCacheEntry *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_idcache_find_by_id_one but with specific hash and
+ *    comparison functions. If `hash' is NULL then the default hash
+ *    funtion is used and if `compare' is NULL default comparison function
+ *    is used. Returns TRUE if the entry was found.
+ *
+ ***/
+bool silc_idcache_find_by_id_one_ext(SilcIDCache cache, void *id, 
+                                    SilcHashFunction hash, 
+                                    void *hash_context,
+                                    SilcHashCompare compare, 
+                                    void *compare_context,
+                                    SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_find_by_context
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_find_by_context(SilcIDCache cache, void *context, 
+ *                                      SilcIDCacheEntry *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Find cache entry by user specified context. Returns TRUE if the
+ *    entry was found.
+ *
+ ***/
+bool silc_idcache_find_by_context(SilcIDCache cache, void *context, 
+                                 SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_find_by_name
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_find_by_name(SilcIDCache cache, char *name, 
+ *                                   SilcIDCacheList *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Find cache entries by the name associated with the ID. This may
+ *    return muliptle entries allocated to the SilcIDCacheList. Returns
+ *    TRUE if the entry was found. The caller must free the SIlcIDCacheList.
+ *
+ ***/
+bool silc_idcache_find_by_name(SilcIDCache cache, char *name, 
+                              SilcIDCacheList *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_find_by_name_one
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
+ *                                       SilcIDCacheEntry *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Find cache entry by the name associated with the ID. This returns
+ *    one entry and the found entry is considered to be exact match.
+ *    return muliptle entries allocated to the SilcIDCacheList. Returns
+ *    TRUE if the entry was found.
+ *
+ ***/
+bool silc_idcache_find_by_name_one(SilcIDCache cache, char *name,
+                                  SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_list_count
+ *
+ * SYNOPSIS
+ *
+ *    int silc_idcache_list_count(SilcIDCacheList list);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the number of cache entries in the ID cache list.
+ *
+ ***/
+int silc_idcache_list_count(SilcIDCacheList list);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_list_first
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_list_first(SilcIDCacheList list, 
+ *                                 SilcIDCacheEntry *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the first cache entry from the ID cache list. Returns FALSE
+ *    If the entry could not be retrieved.
+ *
+ ***/
+bool silc_idcache_list_first(SilcIDCacheList list, SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_list_next
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the next cache entry from the ID Cache list. Returns FALSE
+ *    when there are not anymore entries in the list.
+ *
+ ***/
+bool silc_idcache_list_next(SilcIDCacheList list, SilcIDCacheEntry *ret);
+
+/****f* silccore/SilcIDCacheAPI/silc_idcache_list_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_idcache_list_free(SilcIDCacheList list);
+ *
+ * DESCRIPTION
+ *
+ *     Frees ID cache list. User must free the list context returned by
+ *     any of the searching functions.
+ *
+ ***/
+void silc_idcache_list_free(SilcIDCacheList list);
+
+#endif
diff --git a/lib/silccore/silclog.c b/lib/silccore/silclog.c
deleted file mode 100644 (file)
index aa997d3..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
-
-  silclog.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* SILC Log name strings. These strings are printed to the log file. */
-const SilcLogTypeName silc_log_types[] =
-{
-  { "Info", SILC_LOG_INFO },
-  { "Warning", SILC_LOG_WARNING },
-  { "Error", SILC_LOG_ERROR },
-  { "Fatal", SILC_LOG_FATAL },
-
-  { NULL, -1 },
-};
-
-char *log_info_file;
-char *log_warning_file;
-char *log_error_file;
-char *log_fatal_file;
-unsigned int log_info_size;
-unsigned int log_warning_size;
-unsigned int log_error_size;
-unsigned int log_fatal_size;
-
-/* Formats arguments to a string and returns it after allocating memory
-   for it. It must be remembered to free it later. */
-
-char *silc_log_format(char *fmt, ...)
-{
-  va_list args;
-  static char buf[1024];
-
-  va_start(args, fmt);
-  vsprintf(buf, fmt, args);
-  va_end(args);
-
-  return strdup(buf);
-}
-
-/* Outputs the log message to what ever log file selected. */
-
-void silc_log_output(const char *filename, unsigned int maxsize,
-                     SilcLogType type, char *string)
-{
-  FILE *fp;
-  const SilcLogTypeName *np;
-
-  /* Purge the log file if the max size is defined. */
-  if (maxsize) {
-    fp = fopen(filename, "r");
-    if (fp) {
-      int filelen;
-      
-      filelen = fseek(fp, (off_t)0L, SEEK_END);
-      fseek(fp, (off_t)0L, SEEK_SET);  
-      
-      /* Purge? */
-      if (maxsize >= filelen)
-       unlink(filename);
-    }
-  }
-
-  /* Open the log file */
-  if ((fp = fopen(filename, "a+")) == NULL) {
-    fprintf(stderr, "warning: could not open log file "
-           "%s: %s\n", filename, strerror(errno));
-    fprintf(stderr, "warning: log messages will be displayed on the screen\n");
-    fp = stderr;
-  }
-  /* Get the log type name */
-  for(np = silc_log_types; np->name; np++) {
-    if (np->type == type)
-      break;
-  }
-
-  fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), np->name, string);
-  fflush(fp);
-  fclose(fp);
-  silc_free(string);
-}
-
-/* Outputs the debug message to stderr. */
-
-void silc_log_output_debug(char *file, char *function, 
-                          int line, char *string)
-{
-  /* fprintf(stderr, "%s:%s:%d: %s\n", file, function, line, string); */
-  fprintf(stderr, "%s:%d: %s\n", function, line, string);
-  fflush(stderr);
-  silc_free(string);
-}
-
-/* Hexdumps a message */
-
-void silc_log_output_hexdump(char *file, char *function, 
-                            int line, void *data_in,
-                            unsigned int len, char *string)
-{
-  int i, k;
-  int off, pos, count;
-  unsigned char *data = (unsigned char *)data_in;
-
-  /* fprintf(stderr, "%s:%s:%d: %s\n", file, function, line, string); */
-  fprintf(stderr, "%s:%d: %s\n", function, line, string);
-  silc_free(string);
-
-  k = 0;
-  off = len % 16;
-  pos = 0;
-  count = 16;
-  while (1) {
-
-    if (off) {
-      if ((len - pos) < 16 && (len - pos <= len - off))
-       count = off;
-    } else {
-      if (pos == len)
-       count = 0;
-    }
-    if (off == len)
-      count = len;
-
-    if (count)
-      fprintf(stderr, "%08X  ", k++ * 16);
-
-    for (i = 0; i < count; i++) {
-      fprintf(stderr, "%02X ", data[pos + i]);
-      
-      if ((i + 1) % 4 == 0)
-       fprintf(stderr, " ");
-    }
-
-    if (count && count < 16) {
-      int j;
-      
-      for (j = 0; j < 16 - count; j++) {
-       fprintf(stderr, "   ");
-
-       if ((j + count + 1) % 4 == 0)
-         fprintf(stderr, " ");
-      }
-    }
-  
-    for (i = 0; i < count; i++) {
-      char ch;
-      
-      if (data[pos] < 32 || data[pos] >= 127)
-       ch = '.';
-      else
-       ch = data[pos];
-
-      fprintf(stderr, "%c", ch);
-      pos++;
-    }
-
-    if (count)
-      fprintf(stderr, "\n");
-
-    if (count < 16)
-      break;
-  }
-}
-
-/* Sets log files */
-
-void silc_log_set_files(char *info, unsigned int info_size, 
-                       char *warning, unsigned int warning_size,
-                       char *error, unsigned int error_size,
-                       char *fatal, unsigned int fatal_size)
-{
-  log_info_file = info;
-  log_warning_file = warning;
-  log_error_file = error;
-  log_fatal_file = fatal;
-
-  log_info_size = info_size;
-  log_warning_size = warning_size;
-  log_error_size = error_size;
-  log_fatal_size = fatal_size;
-}
diff --git a/lib/silccore/silclog.h b/lib/silccore/silclog.h
deleted file mode 100644 (file)
index 3cfc499..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
-
-  silclog.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCLOG_H
-#define SILCLOG_H
-
-/* SILC Log types */
-typedef enum {
-  SILC_LOG_INFO,
-  SILC_LOG_WARNING,
-  SILC_LOG_ERROR,
-  SILC_LOG_FATAL
-} SilcLogType;
-
-/* Log type name structure. */
-typedef struct {
-  char *name;
-  SilcLogType type;
-} SilcLogTypeName;
-
-/* Default log filenames */
-#define SILC_LOG_FILE_INFO "silcd.log"
-#define SILC_LOG_FILE_WARNING "silcd_error.log"
-#define SILC_LOG_FILE_ERROR SILC_LOG_FILE_WARNING
-#define SILC_LOG_FILE_FATAL SILC_LOG_FILE_WARNING
-
-/* Log files. Set by silc_log_set_logfiles. */
-extern char *log_info_file;
-extern char *log_warning_file;
-extern char *log_error_file;
-extern char *log_fatal_file;
-extern unsigned int log_info_size;
-extern unsigned int log_warning_size;
-extern unsigned int log_error_size;
-extern unsigned int log_fatal_size;
-
-/* Log macros. */
-#define SILC_LOG_INFO(fmt) silc_log_output(log_info_file, \
-                                           log_info_size, \
-                                          SILC_LOG_INFO, \
-                                          silc_log_format fmt)) 
-#define SILC_LOG_WARNING(fmt) (silc_log_output(log_warning_file, \
-                                               log_warning_size, \
-                                              SILC_LOG_WARNING, \
-                                              silc_log_format fmt))
-#define SILC_LOG_ERROR(fmt) (silc_log_output(log_error_file, \
-                                             log_error_size, \
-                                            SILC_LOG_ERROR, \
-                                            silc_log_format fmt))
-#define SILC_LOG_FATAL(fmt) (silc_log_output(log_fatal_file, \
-                                             log_fatal_size, \
-                                            SILC_LOG_FATAL, \
-                                            silc_log_format fmt))
-
-/* Debug macro is a bit different from other logging macros and it
-   is compiled in only if debugging is enabled. */
-#ifdef SILC_DEBUG
-#define SILC_LOG_DEBUG(fmt) (silc_log_output_debug(__FILE__, \
-                                                  __FUNCTION__, \
-                                                  __LINE__, \
-                                                  silc_log_format fmt))
-#define SILC_LOG_HEXDUMP(fmt, data, len) \
-  (silc_log_output_hexdump(__FILE__, \
-                          __FUNCTION__, \
-                          __LINE__, \
-                           (data), (len), \
-                          silc_log_format fmt))
-#else
-#define SILC_LOG_DEBUG(fmt)
-#define SILC_LOG_HEXDUMP(fmt, data, len)
-#endif
-
-/* Prototypes */
-char *silc_log_format(char *fmt, ...);
-void silc_log_output_debug(char *file, char *function, 
-                           int line, char *string);
-void silc_log_output(const char *filename, unsigned int maxsize,
-                     SilcLogType type, char *string);
-void silc_log_output_hexdump(char *file, char *function, 
-                            int line, void *data_in,
-                             unsigned int len, char *string);
-void silc_log_set_files(char *info, unsigned int info_size, 
-                       char *warning, unsigned int warning_size,
-                       char *error, unsigned int error_size,
-                        char *fatal, unsigned int fatal_size);
-
-#endif
diff --git a/lib/silccore/silcmode.h b/lib/silccore/silcmode.h
new file mode 100644 (file)
index 0000000..4f42778
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+  silcmode.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  
+  Copyright (C) 2001 Pekka Riikonen
+  
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcMode
+ *
+ * DESCRIPTION
+ *
+ * This header includes all mode definitions for the SILC. It includes
+ * channel modes, channel user mode and user modes.
+ *
+ ***/
+
+#ifndef SILCMODE_H
+#define SILCMODE_H
+
+
+/****d* silccore/Modes/ChannelModes
+ *
+ * DESCRIPTION
+ *
+ *    All channel modes.
+ *
+ * SOURCE
+ */
+#define SILC_CHANNEL_MODE_NONE         0x0000
+#define SILC_CHANNEL_MODE_PRIVATE      0x0001 /* private channel */
+#define SILC_CHANNEL_MODE_SECRET       0x0002 /* secret channel */
+#define SILC_CHANNEL_MODE_PRIVKEY      0x0004 /* channel has private key */
+#define SILC_CHANNEL_MODE_INVITE       0x0008 /* invite only channel */
+#define SILC_CHANNEL_MODE_TOPIC        0x0010 /* topic setting by operator */
+#define SILC_CHANNEL_MODE_ULIMIT       0x0020 /* user limit set */
+#define SILC_CHANNEL_MODE_PASSPHRASE   0x0040 /* passphrase set */
+#define SILC_CHANNEL_MODE_CIPHER       0x0080 /* sets cipher of the channel */
+#define SILC_CHANNEL_MODE_HMAC         0x0100 /* sets hmac of the channel */
+#define SILC_CHANNEL_MODE_FOUNDER_AUTH 0x0200 /* sets founder auth data */
+/***/
+
+/****d* silccore/Modes/ChannelUserModes
+ *
+ * DESCRIPTION
+ *
+ *    All user modes on channel
+ *
+ * SOURCE
+ */
+#define SILC_CHANNEL_UMODE_NONE        0x0000 /* Normal user */
+#define SILC_CHANNEL_UMODE_CHANFO      0x0001 /* channel founder */
+#define SILC_CHANNEL_UMODE_CHANOP      0x0002 /* channel operator */
+/***/
+
+/****d* silccore/Modes/SilcUserMode
+ *
+ * DESCRIPTION
+ *
+ *    SILC User modes. These indicate the status of the client in the
+ *    SILC network.
+ *
+ * SOURCE
+ */
+#define SILC_UMODE_NONE                0x0000 /* Normal SILC user */
+#define SILC_UMODE_SERVER_OPERATOR     0x0001 /* Server operator */
+#define SILC_UMODE_ROUTER_OPERATOR     0x0002 /* Router (SILC) operator */
+#define SILC_UMODE_GONE                0x0004 /* Client is gone */
+/***/
+
+#endif
diff --git a/lib/silccore/silcnet.c b/lib/silccore/silcnet.c
deleted file mode 100644 (file)
index b35c182..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
-
-  silcnet.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-#include "silcnet.h"
-
-/* This function creates server or daemon or listener or what ever. This
-   does not fork a new process, it must be done by the caller if caller
-   wants to create a child process. This is used by the SILC server. 
-   If argument `ip_addr' is NULL `any' address will be used. Returns 
-   the created socket or -1 on error. */
-
-int silc_net_create_server(int port, char *ip_addr)
-{
-  int sock, rval;
-  struct sockaddr_in server;
-
-  SILC_LOG_DEBUG(("Creating a new server listener"));
-
-  /* Create the socket */
-  sock = socket(PF_INET, SOCK_STREAM, 0);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
-    return -1;
-  }
-
-  /* Set the socket options */
-  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
-    return -1;
-  }
-
-  /* Set the socket information for bind() */
-  memset(&server, 0, sizeof(server));
-  server.sin_family = PF_INET;
-  server.sin_port = htons(port);
-
-  /* Convert IP address to network byte order */
-  if (ip_addr)
-    inet_aton(ip_addr, &server.sin_addr);
-  else
-    server.sin_addr.s_addr = INADDR_ANY;
-
-  /* Bind the server socket */
-  rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot bind socket: %s", strerror(errno)));
-    return -1;
-  }
-
-  /* Specify that we are listenning */
-  rval = listen(sock, 5);
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
-    return -1;
-  }
-
-  /* Set the server socket to non-blocking mode */
-  silc_net_set_socket_nonblock(sock);
-
-  SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
-
-  return sock;
-}
-
-void silc_net_close_server(int sock)
-{
-  shutdown(sock, 2);
-  close(sock);
-
-  SILC_LOG_DEBUG(("Server socket closed"));
-}
-
-/* Creates a connection (TCP/IP) to a remote host. Returns the connection
-   socket or -1 on error. This blocks the process while trying to create
-   the connection. */
-
-int silc_net_create_connection(int port, char *host)
-{
-  int sock, rval;
-  struct hostent *dest;
-  struct sockaddr_in desthost;
-
-  SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
-
-  /* Do host lookup */
-  dest = gethostbyname(host);
-  if (!dest) {
-    SILC_LOG_ERROR(("Network (%s) unreachable", host));
-    return -1;
-  }
-
-  /* Set socket information */
-  memset(&desthost, 0, sizeof(desthost));
-  desthost.sin_port = htons(port);
-  memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
-
-  /* Create the connection socket */
-  sock = socket(PF_INET, SOCK_STREAM, 0);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
-    return -1;
-  }
-
-  /* Connect to the host */
-  rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
-  if (rval < 0) {
-    SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
-    shutdown(sock, 2);
-    close(sock);
-    return -1;
-  }
-
-  /* Set appropriate option */
-  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
-
-  SILC_LOG_DEBUG(("Connection created"));
-
-  return sock;
-}
-
-/* Creates a connection (TCP/IP) to a remote host. Returns the connection
-   socket or -1 on error. This creates non-blocking socket hence the
-   connection returns directly. To get the result of the connect() one
-   must select() the socket and read the result after it's ready. */
-
-int silc_net_create_connection_async(int port, char *host)
-{
-  int sock, rval;
-  struct hostent *dest;
-  struct sockaddr_in desthost;
-
-  SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
-                 host, port));
-
-  /* Do host lookup */
-  dest = gethostbyname(host);
-  if (!dest) {
-    SILC_LOG_ERROR(("Network (%s) unreachable", host));
-    return -1;
-  }
-
-  /* Set socket information */
-  memset(&desthost, 0, sizeof(desthost));
-  desthost.sin_port = htons(port);
-  memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
-
-  /* Create the connection socket */
-  sock = socket(PF_INET, SOCK_STREAM, 0);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
-    return -1;
-  }
-
-  /* Set the socket to non-blocking mode */
-  silc_net_set_socket_nonblock(sock);
-
-  /* Connect to the host */
-  rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
-  if (rval < 0) {
-    if (errno !=  EINPROGRESS) {
-      SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
-      shutdown(sock, 2);
-      close(sock);
-      return -1;
-    }
-  }
-
-  /* Set appropriate option */
-  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
-
-  SILC_LOG_DEBUG(("Connection operation in progress"));
-
-  return sock;
-}
-
-/* Closes the connection */
-
-void silc_net_close_connection(int sock)
-{
-  close(sock);
-}
-
-/* Accepts a connection from a particular socket */
-
-int silc_net_accept_connection(int sock)
-{
-  return accept(sock, 0, 0);
-}
-
-/* Set's the socket to non-blocking mode. */
-
-int silc_net_set_socket_nonblock(int sock)
-{
-  return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
-}
-
-/* Sets a option for a socket. */
-
-int silc_net_set_socket_opt(int sock, int level, int option, int on)
-{
-  return setsockopt(sock, level, option, (void *)&on, sizeof(on));
-}
-
-/* Checks whether IP address sent as argument is valid IP address. */
-
-int silc_net_is_ip(const char *addr)
-{
-  struct in_addr tmp;
-  return inet_aton(addr, &tmp);
-}
-
-/* Performs lookups for remote name and IP address. */
-
-void silc_net_check_host_by_sock(int sock, char **hostname, char **ip)
-{
-  struct sockaddr_in remote;
-  struct hostent *dest;
-  char *host_ip = NULL;
-  char host_name[1024];
-  int rval, len;
-  int i;
-
-  *hostname = NULL;
-  *ip = NULL;
-
-  SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
-
-  memset(&remote, 0, sizeof(remote));
-  len = sizeof(remote);
-  rval = getpeername(sock, (struct sockaddr *)&remote, &len);
-  if (rval < 0)
-    return;
-
-  /* Get host by address */
-  dest = gethostbyaddr((char *)&remote.sin_addr, 
-                      sizeof(struct in_addr), AF_INET);
-  if (!dest)
-    return;
-
-  /* Get same hsot by name to see that the remote host really is
-     the who it says it is */
-  memset(host_name, 0, sizeof(host_name));
-  memcpy(host_name, dest->h_name, strlen(dest->h_name));
-  dest = gethostbyname(host_name);
-  if (!dest)
-    return;
-
-  /* Find the address from list */
-  for (i = 0; dest->h_addr_list[i]; i++)
-    if (!memcmp(dest->h_addr_list[i], &remote.sin_addr, 
-              sizeof(struct in_addr)))
-      break;
-  if (!dest->h_addr_list[i])
-    return;
-
-  host_ip = inet_ntoa(remote.sin_addr);
-  if (!host_ip)
-    return;
-
-  *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
-  memcpy(*hostname, host_name, strlen(host_name));
-  SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
-  *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
-  memcpy(*ip, host_ip, strlen(host_ip));
-  SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
-}
diff --git a/lib/silccore/silcnet.h b/lib/silccore/silcnet.h
deleted file mode 100644 (file)
index d6ef5c0..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-
-  silcnet.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCNET_H
-#define SILCNET_H
-
-/* Prototypes */
-int silc_net_create_server(int port, char *ip_addr);
-void silc_net_close_server(int sock);
-int silc_net_create_connection(int port, char *host);
-int silc_net_create_connection_async(int port, char *host);
-void silc_net_close_connection(int sock);
-int silc_net_accept_connection(int sock);
-int silc_net_set_socket_nonblock(int sock);
-int silc_net_set_socket_opt(int sock, int level, int option, int on);
-int silc_net_is_ip(const char *addr);
-void silc_net_check_host_by_sock(int sock, char **hostname, char **ip);
-
-#endif
diff --git a/lib/silccore/silcnotify.c b/lib/silccore/silcnotify.c
new file mode 100644 (file)
index 0000000..99f342c
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+
+  silcnotify.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcnotify.h"
+
+/******************************************************************************
+
+                               Notify Payload
+
+******************************************************************************/
+
+struct SilcNotifyPayloadStruct {
+  SilcNotifyType type;
+  unsigned char argc;
+  SilcArgumentPayload args;
+};
+
+/* Parse notify payload buffer and return data into payload structure */
+
+SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload,
+                                           uint32 payload_len)
+{
+  SilcBufferStruct buffer;
+  SilcNotifyPayload new;
+  uint16 len;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing Notify payload"));
+
+  silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
+  new = silc_calloc(1, sizeof(*new));
+
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&new->type),
+                            SILC_STR_UI_SHORT(&len),
+                            SILC_STR_UI_CHAR(&new->argc),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  if (len > buffer.len)
+    goto err;
+
+  if (new->argc) {
+    silc_buffer_pull(&buffer, 5);
+    new->args = silc_argument_payload_parse(buffer.data, buffer.len, 
+                                           new->argc);
+    silc_buffer_push(&buffer, 5);
+  }
+
+  return new;
+
+ err:
+  silc_free(new);
+  return NULL;
+}
+
+/* Encode notify payload with variable argument list. If `argc' is > 0
+   argument payloads will be associated to the notify payload. Variable
+   arguments must be {usigned char *, uint32 (len)}. */
+
+SilcBuffer silc_notify_payload_encode(SilcNotifyType type, uint32 argc, 
+                                     va_list ap)
+{
+  SilcBuffer buffer;
+  SilcBuffer args = NULL;
+  unsigned char **argv;
+  uint32 *argv_lens = NULL, *argv_types = NULL;
+  unsigned char *x;
+  uint32 x_len;
+  int i, k = 0, len = 0;
+
+  if (argc) {
+    argv = silc_calloc(argc, sizeof(unsigned char *));
+    argv_lens = silc_calloc(argc, sizeof(uint32));
+    argv_types = silc_calloc(argc, sizeof(uint32));
+    
+    for (i = 0, k = 0; i < argc; i++) {
+      x = va_arg(ap, unsigned char *);
+      x_len = va_arg(ap, uint32);
+
+      if (!x || !x_len)
+       continue;
+      
+      argv[k] = silc_calloc(x_len + 1, sizeof(unsigned char));
+      memcpy(argv[k], x, x_len);
+      argv_lens[k] = x_len;
+      argv_types[k] = i + 1;
+      k++;
+    }
+
+    args = silc_argument_payload_encode(k, argv, argv_lens, argv_types);
+    len = args->len;
+
+    for (i = 0; i < k; i++)
+      silc_free(argv[i]);
+    silc_free(argv);
+    silc_free(argv_lens);
+    silc_free(argv_types);
+  }
+
+  len += 5;
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(type),
+                    SILC_STR_UI_SHORT(len),
+                    SILC_STR_UI_CHAR(k),
+                    SILC_STR_END);
+
+  if (k) {
+    silc_buffer_pull(buffer, 5);
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_END);
+    silc_buffer_push(buffer, 5);
+    silc_buffer_free(args);
+  }
+
+  return buffer;
+}
+
+/* Same as above but takes argument from the `args' Argument Payload. */
+
+SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, 
+                                          uint32 argc,
+                                          SilcBuffer args)
+{
+  SilcBuffer buffer;
+  int len;
+
+  len = 5 + (args ? args->len : 0);
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_SHORT(type),
+                    SILC_STR_UI_SHORT(len),
+                    SILC_STR_UI_CHAR(argc),
+                    SILC_STR_END);
+
+  if (args) {
+    silc_buffer_pull(buffer, 5);
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_XNSTRING(args->data, args->len),
+                      SILC_STR_END);
+    silc_buffer_push(buffer, 5);
+  }
+
+  return buffer;
+}
+
+/* Frees notify payload */
+
+void silc_notify_payload_free(SilcNotifyPayload payload)
+{
+  if (payload) {
+    silc_argument_payload_free(payload->args);
+    silc_free(payload);
+  }
+}
+
+/* Return notify type */
+
+SilcNotifyType silc_notify_get_type(SilcNotifyPayload payload)
+{
+  return payload->type;
+}
+
+/* Return argument nums */
+
+uint32 silc_notify_get_arg_num(SilcNotifyPayload payload)
+{
+  return payload->argc;
+}
+
+/* Return argument payload */
+
+SilcArgumentPayload silc_notify_get_args(SilcNotifyPayload payload)
+{
+  return payload->args;
+}
diff --git a/lib/silccore/silcnotify.h b/lib/silccore/silcnotify.h
new file mode 100644 (file)
index 0000000..f00772b
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+  silcnotify.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcNotifyAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the Notify Payload. Notify Payload is used usually
+ * by servers to send different kind of important notify messages to other
+ * servers and to clients.
+ *
+ ***/
+
+#ifndef SILCNOTIFY_H
+#define SILCNOTIFY_H
+
+/****s* silccore/SilcNotifyAPI/SilcNotifyPayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcNotifyPayloadStruct *SilcNotifyPayload;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Notify Payload and is allocated
+ *    by silc_notify_payload_parse and given as argument usually to
+ *    all silc_notify_payload_* functions.  It is freed by the
+ *    silc_notify_payload_free function.
+ *
+ ***/
+typedef struct SilcNotifyPayloadStruct *SilcNotifyPayload;
+
+/****d* silccore/SilcNotifyAPI/SilcNotifyType
+ *
+ * NAME
+ * 
+ *    typedef uint16 SilcNotifyType;
+ *
+ * DESCRIPTION
+ *
+ *    The notify type definition and all of the notify types.
+ *
+ * SOURCE
+ */
+typedef uint16 SilcNotifyType;
+
+/* SILC notify types. Server may send these notify types to client to
+   notify of some action. */
+#define SILC_NOTIFY_TYPE_NONE            0  /* no specific type */
+#define SILC_NOTIFY_TYPE_INVITE          1  /* invites/invite list change */
+#define SILC_NOTIFY_TYPE_JOIN            2  /* "has joined channel" */
+#define SILC_NOTIFY_TYPE_LEAVE           3  /* "has left channel" */
+#define SILC_NOTIFY_TYPE_SIGNOFF         4  /* "signoff" */
+#define SILC_NOTIFY_TYPE_TOPIC_SET       5  /* "topic has been changed" */
+#define SILC_NOTIFY_TYPE_NICK_CHANGE     6  /* "has changed nickname" */
+#define SILC_NOTIFY_TYPE_CMODE_CHANGE    7  /* "has changed channel mode" */
+#define SILC_NOTIFY_TYPE_CUMODE_CHANGE   8  /* "has change mode" */
+#define SILC_NOTIFY_TYPE_MOTD            9  /* message of the day */
+#define SILC_NOTIFY_TYPE_CHANNEL_CHANGE  10 /* Channel's ID has changed */
+#define SILC_NOTIFY_TYPE_SERVER_SIGNOFF  11 /* Server quitting SILC */
+#define SILC_NOTIFY_TYPE_KICKED          12 /* Kicked from channel */
+#define SILC_NOTIFY_TYPE_KILLED          13 /* Killed from the network */
+#define SILC_NOTIFY_TYPE_UMODE_CHANGE    14 /* user mode was changed */
+#define SILC_NOTIFY_TYPE_BAN             15 /* ban list change */
+/***/
+
+/* Prototypes */
+
+/****f* silccore/SilcNotifyAPI/silc_notify_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcNotifyPayload 
+ *    silc_notify_payload_parse(const unsigned char *payload,
+ *                              uint32 payload_len);
+ *
+ * DESCRIPTION
+ *
+ *    Parse notify payload buffer and return data into payload structure.
+ *    The `buffer' is the raw payload data.
+ *
+ ***/
+SilcNotifyPayload silc_notify_payload_parse(const unsigned char *payload,
+                                           uint32 payload_len);
+
+/****f* silccore/SilcNotifyAPI/silc_notify_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_notify_payload_encode(SilcNotifyType type, uint32 argc, 
+ *                                          va_list ap);
+ *
+ * DESCRIPTION
+ *
+ *    Encode notify payload with variable argument list. If `argc' is > 0
+ *    argument payloads will be associated to the notify payload. Variable
+ *    arguments must be {usigned char *, uint32 (len)}.
+ *
+ ***/
+SilcBuffer silc_notify_payload_encode(SilcNotifyType type, uint32 argc, 
+                                     va_list ap);
+
+/****f* silccore/SilcNotifyAPI/silc_notify_payload_encode_args
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, 
+ *                                               uint32 argc,
+ *                                               SilcBuffer args);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_notify_payload_encode but takes arguments from the `args'
+ *    encoded Argument Payload buffer.
+ *
+ ***/
+SilcBuffer silc_notify_payload_encode_args(SilcNotifyType type, 
+                                          uint32 argc,
+                                          SilcBuffer args);
+
+/****f* silccore/SilcNotifyAPI/silc_notify_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_notify_payload_free(SilcNotifyPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the Notify Payload and all data in it.
+ *
+ ***/
+void silc_notify_payload_free(SilcNotifyPayload payload);
+
+/****f* silccore/SilcNotifyAPI/silc_notify_get_type
+ *
+ * SYNOPSIS
+ *
+ *    SilcNotifyType silc_notify_get_type(SilcNotifyPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the notify type from the payload.
+ *
+ ***/
+SilcNotifyType silc_notify_get_type(SilcNotifyPayload payload);
+
+/****f* silccore/SilcNotifyAPI/silc_notify_get_arg_num
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_notify_get_arg_num(SilcNotifyPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the number of the arguments associated with the Notify Payload.
+ *
+ ***/
+uint32 silc_notify_get_arg_num(SilcNotifyPayload payload);
+
+/****f* silccore/SilcNotifyAPI/silc_notify_get_args
+ *
+ * SYNOPSIS
+ *
+ *    SilcArgumentPayload silc_notify_get_args(SilcNotifyPayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Return the Argument Payload containing the arguments from the
+ *    Notify Payload. The caller must not free it.
+ *
+ ***/
+SilcArgumentPayload silc_notify_get_args(SilcNotifyPayload payload);
+
+#endif
index 26c7cbefa9e686236e9b7075af09a087928f0a3b..76c36ff886c23bf95448b85eda1dc6d9d8de568d 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silcpacket.c
+  silcpacket.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 /*
  * Created: Fri Jul 25 18:52:14 1997
  */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 
-/* Writes data from encrypted buffer to the socket connection. If the
-   data cannot be written at once, it will be written later with a timeout. 
-   The data is written from the data section of the buffer, not from head
-   or tail section. This automatically pulls the data section towards end
-   after writing the data. */
+/******************************************************************************
 
-int silc_packet_write(int sock, SilcBuffer src)
+                          Packet Sending Routines
+
+******************************************************************************/
+
+/* Actually sends the packet. This flushes the connections outgoing data
+   buffer. If data is sent directly to the network this returns the bytes
+   written, if error occured this returns -1 and if the data could not
+   be written directly to the network at this time this returns -2, in
+   which case the data should be queued by the caller and sent at some
+   later time. If `force_send' is TRUE this attempts to write the data
+   directly to the network, if FALSE, this returns -2. */
+
+int silc_packet_send(SilcSocketConnection sock, bool force_send)
 {
-  int ret = 0;
+  SILC_LOG_DEBUG(("Sending packet to %s:%d [%s]", sock->hostname,
+                 sock->port,  
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
 
-  SILC_LOG_DEBUG(("Writing data to socket %d", sock));
+  /* Send now if forced to do so */
+  if (force_send == TRUE) {
+    int ret;
 
-  if (src->len > 0) {
-    ret = write(sock, src->data, src->len);
-    if (ret < 0) {
-      if (errno == EAGAIN) {
-       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
-       return -2;
-      }
-      SILC_LOG_ERROR(("Cannot write to socket: %s", strerror(errno)));
-      return -1;
+    SILC_LOG_DEBUG(("Forcing packet send, packet sent immediately"));
+
+    /* Write to network */
+    ret = silc_socket_write(sock);
+
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet, dropped: %s", 
+                      strerror(errno)));
     }
+    if (ret != -2)
+      return ret;
+
+    SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
+  }  
+
+  SILC_LOG_DEBUG(("Packet in queue"));
+
+  return -2;
+}
+
+/* Encrypts a packet. This also creates HMAC of the packet before 
+   encryption and adds the HMAC at the end of the buffer. This assumes
+   that there is enough free space at the end of the buffer to add the
+   computed HMAC. This is the normal way of encrypting packets, if some
+   other process of HMAC computing and encryption is needed this function
+   cannot be used. */
 
-    silc_buffer_pull(src, ret);
+void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, uint32 sequence,
+                        SilcBuffer buffer, uint32 len)
+{
+  unsigned char mac[32];
+  uint32 mac_len;
+
+  /* Compute HMAC. This assumes that HMAC is created from the entire
+     data area thus this uses the length found in buffer, not the length
+     sent as argument. */
+  if (hmac) {
+    unsigned char psn[4];
+
+    silc_hmac_init(hmac);
+    SILC_PUT32_MSB(sequence, psn);
+    silc_hmac_update(hmac, psn, 4);
+    silc_hmac_update(hmac, buffer->data, buffer->len);
+    silc_hmac_final(hmac, mac, &mac_len);
+    silc_buffer_put_tail(buffer, mac, mac_len);
+    memset(mac, 0, sizeof(mac));
   }
 
-  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
+  /* Encrypt the data area of the packet. */
+  if (cipher) {
+    SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d", 
+                   cipher->cipher->name, len));
+    silc_cipher_encrypt(cipher, buffer->data, buffer->data, len, cipher->iv);
+  }
 
-  return ret;
+  /* Pull the HMAC into the visible data area in the buffer */
+  if (hmac)
+    silc_buffer_pull_tail(buffer, mac_len);
 }
 
-/* Reads data from the socket connection into the incoming data buffer.
-   However, this does not parse the packet, it only reads some amount from
-   the network. If there are more data available that can be read at a time
-   the rest of the data will be read later with a timeout and only after
-   that the packet is ready to be parsed. 
+/* Assembles a new packet to be ready for send out. The buffer sent as
+   argument must include the data to be sent and it must not be encrypted. 
+   The packet also must have enough free space so that the SILC header
+   and padding maybe added to the packet. The packet is encrypted after 
+   this function has returned.
 
-   The destination buffer sent as argument must be initialized before 
-   calling this function, and, the data section and the start of the tail
-   section must be same. Ie. we add the read data to the tail section of
-   the buffer hence the data section is the start of the buffer.
+   The buffer sent as argument should be something like following:
 
-   This returns amount of bytes read or -1 on error or -2 on case where
-   all of the data could not be read at once. */
+   --------------------------------------------
+   | head             | data           | tail |
+   --------------------------------------------
+   ^                  ^
+   58 bytes           x bytes
 
-int silc_packet_read(int sock, SilcBuffer dest)
+   So that the SILC header and 1 - 16 bytes of padding can fit to
+   the buffer. After assembly the buffer might look like this:
+
+   --------------------------------------------
+   | data                              |      |
+   --------------------------------------------
+   ^                                   ^
+   Start of assembled packet
+
+   Packet construct is as follows:
+
+   n bytes       SILC Header
+      2 bytes     Payload length
+      1 byte      Flags
+      1 byte      Packet type
+      1 byte      Padding length
+      1 byte      RESERVED
+      1 bytes     Source ID Length
+      1 bytes     Destination ID Length
+      1 byte      Source ID Type
+      n bytes     Source ID
+      1 byte      Destination ID Type
+      n bytes     Destination ID
+
+   1 - 16 bytes    Padding
+
+   n bytes        Data payload
+
+   All fields in the packet will be authenticated by MAC. The MAC is
+   not computed here, it must be computed separately before encrypting
+   the packet.
+
+*/
+
+void silc_packet_assemble(SilcPacketContext *ctx, SilcCipher cipher)
 {
-  int len = 0;
-  unsigned char buf[SILC_PACKET_READ_SIZE];
+  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
+  int block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+  int i;
 
-  SILC_LOG_DEBUG(("Reading data from socket %d", sock));
+  SILC_LOG_DEBUG(("Assembling outgoing packet"));
+  
+  /* Get the true length of the packet. This is saved as payload length
+     into the packet header. This does not include the length of the
+     padding. */
+  if (!ctx->truelen)
+    ctx->truelen = ctx->buffer->len + SILC_PACKET_HEADER_LEN + 
+      ctx->src_id_len + ctx->dst_id_len;
 
-  /* Read the data from the socket. */
-  len = read(sock, buf, sizeof(buf));
-  if (len < 0) {
-    if (errno == EAGAIN) {
-      SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
-      return -2;
+  /* Calculate the length of the padding. The padding is calculated from
+     the data that will be encrypted. */
+  if (!ctx->padlen) {
+    if (ctx->long_pad)
+      ctx->padlen = SILC_PACKET_PADLEN_MAX(ctx->truelen);
+    else
+      ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen, block_len);
+  }
+
+  /* Put the start of the data section to the right place. */
+  silc_buffer_push(ctx->buffer, SILC_PACKET_HEADER_LEN + 
+                  ctx->src_id_len + ctx->dst_id_len + ctx->padlen);
+
+  /* Get random padding */
+#if 1
+  for (i = 0; i < ctx->padlen; i++) tmppad[i] = 
+                                     silc_rng_global_get_byte_fast();
+#else
+  /* XXX: For testing - to be removed */
+  memset(tmppad, 65, sizeof(tmppad));
+#endif
+
+  /* Create the packet. This creates the SILC header and adds padding,
+     rest of the buffer remains as it is. */
+  silc_buffer_format(ctx->buffer, 
+                    SILC_STR_UI_SHORT(ctx->truelen),
+                    SILC_STR_UI_CHAR(ctx->flags),
+                    SILC_STR_UI_CHAR(ctx->type),
+                    SILC_STR_UI_CHAR(ctx->padlen),
+                    SILC_STR_UI_CHAR(0),
+                    SILC_STR_UI_CHAR(ctx->src_id_len),
+                    SILC_STR_UI_CHAR(ctx->dst_id_len),
+                    SILC_STR_UI_CHAR(ctx->src_id_type),
+                    SILC_STR_UI_XNSTRING(ctx->src_id, ctx->src_id_len),
+                    SILC_STR_UI_CHAR(ctx->dst_id_type),
+                    SILC_STR_UI_XNSTRING(ctx->dst_id, ctx->dst_id_len),
+                    SILC_STR_UI_XNSTRING(tmppad, ctx->padlen),
+                    SILC_STR_END);
+
+  SILC_LOG_HEXDUMP(("Assembled packet, len %d", ctx->buffer->len), 
+                  ctx->buffer->data, ctx->buffer->len);
+
+  SILC_LOG_DEBUG(("Outgoing packet assembled"));
+}
+
+/* Prepare outgoing data buffer for packet sending. This moves the data
+   area so that new packet may be added into it. If needed this allocates
+   more space to the buffer. This handles directly the connection's
+   outgoing buffer in SilcSocketConnection object. */
+
+void silc_packet_send_prepare(SilcSocketConnection sock,
+                             uint32 header_len,
+                             uint32 padlen,
+                             uint32 data_len)
+{
+  int totlen, oldlen;
+
+  totlen = header_len + padlen + data_len;
+
+  /* Prepare the outgoing buffer for packet sending. */
+  if (!sock->outbuf) {
+    /* Allocate new buffer. This is done only once per connection. */
+    SILC_LOG_DEBUG(("Allocating outgoing data buffer"));
+    
+    if (totlen > SILC_PACKET_DEFAULT_SIZE)
+      sock->outbuf = silc_buffer_alloc(totlen);
+    else
+      sock->outbuf = silc_buffer_alloc(SILC_PACKET_DEFAULT_SIZE);
+    silc_buffer_pull_tail(sock->outbuf, totlen);
+    silc_buffer_pull(sock->outbuf, header_len + padlen);
+  } else {
+    if (SILC_IS_OUTBUF_PENDING(sock)) {
+      /* There is some pending data in the buffer. */
+
+      /* Allocate more space if needed */
+      if ((sock->outbuf->end - sock->outbuf->tail) < 
+         (totlen + 20)) {
+       SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
+       sock->outbuf = silc_buffer_realloc(sock->outbuf, 
+                                          sock->outbuf->truelen +
+                                          (totlen * 2));
+      }
+
+      oldlen = sock->outbuf->len;
+      silc_buffer_pull_tail(sock->outbuf, totlen);
+      silc_buffer_pull(sock->outbuf, header_len + padlen + oldlen);
+    } else {
+      /* Buffer is free for use */
+      silc_buffer_clear(sock->outbuf);
+
+      /* Allocate more space if needed */
+      if ((sock->outbuf->end - sock->outbuf->tail) < (totlen + 20)) {
+       SILC_LOG_DEBUG(("Reallocating outgoing data buffer"));
+       sock->outbuf = silc_buffer_realloc(sock->outbuf, 
+                                          sock->outbuf->truelen + 
+                                          (totlen * 2));
+      }
+
+      silc_buffer_pull_tail(sock->outbuf, totlen);
+      silc_buffer_pull(sock->outbuf, header_len + padlen);
     }
-    SILC_LOG_ERROR(("Cannot read from socket: %d", strerror(errno)));
-    return -1;
   }
+}
 
-  if (!len)
-    return 0;
+/******************************************************************************
+
+                         Packet Reception Routines
+
+******************************************************************************/
+
+static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, 
+                              uint32 sequence, SilcBuffer buffer, 
+                              bool normal);
+
+/* Receives packet from network and reads the data into connection's
+   incoming data buffer. If the data was read directly this returns the
+   read bytes, if error occured this returns -1, if the data could not
+   be read directly at this time this returns -2 in which case the data
+   should be read again at some later time, or If EOF occured this returns
+   0. */
+
+int silc_packet_receive(SilcSocketConnection sock)
+{
+  int ret;
+
+  SILC_LOG_DEBUG(("Receiving packet from %s:%d [%s]", sock->hostname,
+                 sock->port, 
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
+
+  /* Read some data from connection */
+  ret = silc_socket_read(sock);
+
+  return ret;
+}
+
+/* Processes and decrypts the incmoing data, and calls parser callback
+   for each received packet that will handle the actual packet parsing.
+   If more than one packet was received this calls the parser multiple
+   times.  The parser callback will get context SilcPacketParserContext
+   that includes the packet and the `parser_context' sent to this
+   function. 
+   
+   The `local_is_router' indicates whether the caller is router server
+   in which case the receiving process of a certain packet types may
+   be special.  Normal server and client must set it to FALSE.  The
+   SilcPacketParserContext will indicate also whether the received
+   packet was normal or special packet. */
+
+bool silc_packet_receive_process(SilcSocketConnection sock,
+                                bool local_is_router,
+                                SilcCipher cipher, SilcHmac hmac,
+                                uint32 sequence,
+                                SilcPacketParserCallback parser,
+                                void *parser_context)
+{
+  SilcPacketParserContext *parse_ctx;
+  int packetlen, paddedlen, mac_len = 0;
+  bool cont = TRUE;
+
+  /* Do not process for disconnected connection */
+  if (SILC_IS_DISCONNECTED(sock))
+    return TRUE;
+
+  if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN)
+    return TRUE;
+
+  if (hmac)
+    mac_len = silc_hmac_len(hmac);
+
+  /* Parse the packets from the data */
+  while (sock->inbuf->len > 0 && cont) {
+
+    if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN) {
+      SILC_LOG_DEBUG(("Partial packet in queue, waiting for the rest"));
+      return TRUE;
+    }
+
+    /* Decrypt first 16 bytes of the packet */
+    if (!SILC_IS_INBUF_PENDING(sock) && cipher)
+      silc_cipher_decrypt(cipher, sock->inbuf->data, sock->inbuf->data, 
+                         SILC_PACKET_MIN_HEADER_LEN, cipher->iv);
+
+    /* Get packet lenght and full packet length with padding */
+    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
+
+    /* Sanity checks */
+    if (packetlen < SILC_PACKET_MIN_LEN) {
+      SILC_LOG_DEBUG(("Received invalid packet, dropped"));
+      silc_buffer_clear(sock->inbuf);
+      return FALSE;
+    }
+
+    if (sock->inbuf->len < paddedlen + mac_len) {
+      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest"
+                     "(%d < %d)", sock->inbuf->len, paddedlen + mac_len));
+      SILC_SET_INBUF_PENDING(sock);
+      return TRUE;
+    }
 
-  /* Insert the data to the buffer. If the data doesn't fit to the 
-     buffer space is allocated for the buffer.  
-     XXX: I don't like this. -Pekka */
-  if (dest) {
-
-    /* If the data doesn't fit we just have to allocate a whole new 
-       data area */
-    if (dest->truelen <= len) {
-
-      /* Free the old buffer */
-      memset(dest->head, 'F', dest->truelen);
-      silc_free(dest->head);
-
-      /* Allocate new data area */
-      len += SILC_PACKET_DEFAULT_SIZE;
-      dest->data = silc_calloc(len, sizeof(char));
-      dest->truelen = len;
-      dest->len = 0;
-      dest->head = dest->data;
-      dest->data = dest->data;
-      dest->tail = dest->data;
-      dest->end = dest->data + dest->truelen;
-      len -= SILC_PACKET_DEFAULT_SIZE;
+    SILC_UNSET_INBUF_PENDING(sock);
+    parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
+    parse_ctx->packet = silc_packet_context_alloc();
+    parse_ctx->packet->buffer = silc_buffer_alloc(paddedlen + mac_len);
+    parse_ctx->packet->type = sock->inbuf->data[3];
+    parse_ctx->packet->padlen = sock->inbuf->data[4];
+    parse_ctx->packet->sequence = sequence++;
+    parse_ctx->sock = sock;
+    parse_ctx->context = parser_context;
+
+    silc_buffer_pull_tail(parse_ctx->packet->buffer, 
+                         SILC_BUFFER_END(parse_ctx->packet->buffer));
+    silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data, 
+                   paddedlen + mac_len);
+
+    SILC_LOG_HEXDUMP(("Incoming packet (%d) (%dB decrypted), len %d", 
+                     sequence - 1, SILC_PACKET_MIN_HEADER_LEN, 
+                     paddedlen + mac_len),
+                    sock->inbuf->data, paddedlen + mac_len);
+
+    /* Check whether this is normal or special packet */
+    if (local_is_router) {
+      if (sock->inbuf->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+         (sock->inbuf->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+       parse_ctx->normal = FALSE;
+      else if (sock->inbuf->data[3] != SILC_PACKET_CHANNEL_MESSAGE || 
+              (sock->inbuf->data[3] == SILC_PACKET_CHANNEL_MESSAGE &&
+               sock->type == SILC_SOCKET_TYPE_ROUTER))
+       parse_ctx->normal = TRUE;
+    } else {
+      if (sock->inbuf->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+         (sock->inbuf->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+       parse_ctx->normal = FALSE;
+      else if (sock->inbuf->data[3] != SILC_PACKET_CHANNEL_MESSAGE)
+       parse_ctx->normal = TRUE;
     }
 
-    silc_buffer_put_tail(dest, buf, len);
-    silc_buffer_pull_tail(dest, len);
+    /* Decrypt rest of the packet */
+    if (cipher)
+      if (silc_packet_decrypt(cipher, hmac, parse_ctx->packet->sequence, 
+                             parse_ctx->packet->buffer, 
+                             parse_ctx->normal) == -1) {
+       SILC_LOG_WARNING(("Packet decryption failed %s:%d [%s]", 
+                         sock->hostname, sock->port,
+                         (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                          sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                          sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                          "Router")));
+      }
+
+    /* Pull the packet from inbuf thus we'll get the next one
+       in the inbuf. */
+    silc_buffer_pull(sock->inbuf, paddedlen + mac_len);
+
+    /* Call the parser */
+    cont = (*parser)(parse_ctx, parser_context);
   }
 
-  SILC_LOG_DEBUG(("Read %d bytes", len));
+  if (cont == FALSE && sock->inbuf->len > 0)
+    return TRUE;
+
+  SILC_LOG_DEBUG(("Clearing inbound buffer"));
+  silc_buffer_clear(sock->inbuf);
+  return TRUE;
+}
+
+/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
+   after packet has been totally decrypted and parsed. */
 
-  return len;
+static int silc_packet_check_mac(SilcHmac hmac, SilcBuffer buffer,
+                                uint32 sequence)
+{
+  /* Check MAC */
+  if (hmac) {
+    unsigned char mac[32], psn[4];
+    uint32 mac_len;
+    
+    SILC_LOG_DEBUG(("Verifying MAC"));
+
+    /* Compute HMAC of packet */
+
+    memset(mac, 0, sizeof(mac));
+    silc_hmac_init(hmac);
+    SILC_PUT32_MSB(sequence, psn);
+    silc_hmac_update(hmac, psn, 4);
+    silc_hmac_update(hmac, buffer->data, buffer->len);
+    silc_hmac_final(hmac, mac, &mac_len);
+
+    /* Compare the HMAC's (buffer->tail has the packet's HMAC) */
+    if (memcmp(buffer->tail, mac, mac_len)) {
+      SILC_LOG_ERROR(("MAC failed"));
+      return FALSE;
+    }
+    
+    SILC_LOG_DEBUG(("MAC is Ok"));
+    memset(mac, 0, sizeof(mac));
+  }
+  
+  return TRUE;
 }
 
-/* Encrypts a packet. */
+/* Decrypts rest of the packet (after decrypting just the SILC header).
+   After calling this function the packet is ready to be parsed by calling 
+   silc_packet_parse. If everything goes without errors this returns TRUE,
+   if packet is malformed this returns FALSE. */
 
-void silc_packet_encrypt(SilcCipher cipher, SilcBuffer buffer,
-                        unsigned int len)
+static int silc_packet_decrypt_rest(SilcCipher cipher, SilcHmac hmac,
+                                   SilcBuffer buffer)
 {
-  SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d (%d)", 
-                 cipher->cipher->name, len, len - 2));
+  if (cipher) {
+
+    /* Pull MAC from packet before decryption */
+    if (hmac) {
+      if ((buffer->len - silc_hmac_len(hmac)) > SILC_PACKET_MIN_LEN) {
+       silc_buffer_push_tail(buffer, silc_hmac_len(hmac));
+      } else {
+       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
+       return FALSE;
+      }
+    }
+
+    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
 
-  /* Encrypt the data area of the packet. 3 bytes of the packet
-     are not encrypted. */
-  cipher->cipher->encrypt(cipher->context, buffer->data + 2, 
-                         buffer->data + 2, len - 2, cipher->iv);
+    /* Decrypt rest of the packet */
+    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN);
+    silc_cipher_decrypt(cipher, buffer->data, buffer->data, buffer->len, 
+                       cipher->iv);
+    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN);
 
+    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
+                    buffer->data, buffer->len);
+  }
+
+  return TRUE;
 }
 
-/* Decrypts a packet. */
+/* Decrypts rest of the SILC Packet header that has been decrypted partly
+   already. This decrypts the padding of the packet also. After calling 
+   this function the packet is ready to be parsed by calling function 
+   silc_packet_parse. This is used in special packet reception (protocol
+   defines the way of decrypting special packets). */
+
+static int silc_packet_decrypt_rest_special(SilcCipher cipher,
+                                           SilcHmac hmac,
+                                           SilcBuffer buffer)
+{
+  /* Decrypt rest of the header plus padding */
+  if (cipher) {
+    uint16 len;
+
+    /* Pull MAC from packet before decryption */
+    if (hmac) {
+      if ((buffer->len - silc_hmac_len(hmac)) > SILC_PACKET_MIN_LEN) {
+       silc_buffer_push_tail(buffer, silc_hmac_len(hmac));
+      } else {
+       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
+       return FALSE;
+      }
+    }
+  
+    SILC_LOG_DEBUG(("Decrypting rest of the header"));
+
+    /* padding length + src id len + dst id len + header length - 16
+       bytes already decrypted, gives the rest of the encrypted packet */
+    len = (((uint8)buffer->data[4] + (uint8)buffer->data[6] + 
+          (uint8)buffer->data[7] + SILC_PACKET_HEADER_LEN) -
+          SILC_PACKET_MIN_HEADER_LEN);
+
+    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN);
+    if (len > buffer->len) {
+      SILC_LOG_DEBUG(("Garbage in header of packet, bad packet length, "
+                     "packet dropped"));
+      return FALSE;
+    }
+    silc_cipher_decrypt(cipher, buffer->data, buffer->data, len, cipher->iv);
+    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN);
+    SILC_LOG_HEXDUMP(("packet, len %d", buffer->len), 
+                    buffer->data, buffer->len);
+  }
+
+  return TRUE;
+}
 
-void silc_packet_decrypt(SilcCipher cipher, SilcBuffer buffer, 
-                        unsigned int len)
+/* Decrypts a packet. This assumes that typical SILC packet is the
+   packet to be decrypted and thus checks for normal and special SILC
+   packets and can handle both of them. This also computes and checks
+   the HMAC of the packet. If any other special or customized decryption
+   processing is required this function cannot be used. This returns
+   -1 on error, 0 when packet is normal packet and 1 when the packet
+   is special and requires special processing. 
+
+   The `check_packet' is a callback funtion that this function will 
+   call.  The callback relates to the checking whether the packet is
+   normal packet or special packet and how it should be processed.  If
+   the callback return TRUE the packet is normal and FALSE if the packet
+   is special and requires special procesing. */
+
+static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
+                              uint32 sequence, SilcBuffer buffer, 
+                              bool normal)
 {
-  SILC_LOG_DEBUG(("Decrypting packet, cipher %s, len %d (%d)", 
-                 cipher->cipher->name, len, len - 2));
+  /* If the packet type is not any special type lets decrypt rest
+     of the packet here. */
+  if (normal == TRUE) {
+    /* Normal packet, decrypt rest of the packet */
+    if (!silc_packet_decrypt_rest(cipher, hmac, buffer))
+      return -1;
 
-  /* Decrypt the data area of the packet. 2 bytes of the packet
-     are not decrypted (they are not encrypted). */
-  cipher->cipher->decrypt(cipher->context, buffer->data + 2, 
-                         buffer->data + 2, len - 2, cipher->iv);
+    /* Check MAC */
+    if (!silc_packet_check_mac(hmac, buffer, sequence))
+      return -1;
+
+    return 0;
+  } else {
+    /* Packet requires special handling, decrypt rest of the header.
+       This only decrypts. */
+    if (!silc_packet_decrypt_rest_special(cipher, hmac, buffer))
+      return -1;
 
+    /* Check MAC */
+    if (!silc_packet_check_mac(hmac, buffer, sequence))
+      return -1;
+
+    return 1;
+  }
 }
 
 /* Parses the packet. This is called when a whole packet is ready to be
@@ -167,10 +608,11 @@ void silc_packet_decrypt(SilcCipher cipher, SilcBuffer buffer,
    function returns the type of the packet. The data section of the 
    buffer is parsed, not head or tail sections. */
 
-SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
+SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
 {
   SilcBuffer buffer = ctx->buffer;
-  int len;
+  uint8 tmp;
+  int len, ret;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
@@ -185,29 +627,34 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
                             SILC_STR_UI_SHORT(&ctx->truelen),
                             SILC_STR_UI_CHAR(&ctx->flags),
                             SILC_STR_UI_CHAR(&ctx->type),
-                            SILC_STR_UI_SHORT(&ctx->src_id_len),
-                            SILC_STR_UI_SHORT(&ctx->dst_id_len),
+                            SILC_STR_UI_CHAR(&ctx->padlen),
+                            SILC_STR_UI_CHAR(&tmp),
+                            SILC_STR_UI_CHAR(&ctx->src_id_len),
+                            SILC_STR_UI_CHAR(&ctx->dst_id_len),
                             SILC_STR_UI_CHAR(&ctx->src_id_type),
                             SILC_STR_END);
+  if (len == -1 || tmp != 0)
+    return SILC_PACKET_NONE;
 
   if (ctx->src_id_len > SILC_PACKET_MAX_ID_LEN ||
       ctx->dst_id_len > SILC_PACKET_MAX_ID_LEN) {
-    SILC_LOG_ERROR(("Bad ID lengths in packet"));
+    SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)",
+                   ctx->src_id_len, ctx->dst_id_len));
     return SILC_PACKET_NONE;
   }
 
-  /* Calculate length of padding in packet */
-  ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen);
-
   silc_buffer_pull(buffer, len);
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
-                                                 ctx->src_id_len),
-                      SILC_STR_UI_CHAR(&ctx->dst_id_type),
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                 ctx->dst_id_len),
-                      SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
+                                                       ctx->src_id_len),
+                            SILC_STR_UI_CHAR(&ctx->dst_id_type),
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
+                                                       ctx->dst_id_len),
+                            SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
+                            SILC_STR_END);
+  if (ret == -1)
+    return SILC_PACKET_NONE;
+
   silc_buffer_push(buffer, len);
 
   SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
@@ -228,10 +675,12 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx)
    the header in a way that it does not take the data area into account
    and parses the header and padding area only. */
 
-SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
+SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
+                                        SilcCipher cipher)
 {
   SilcBuffer buffer = ctx->buffer;
-  int len, tmplen;
+  uint8 tmp;
+  int len, ret;
 
   SILC_LOG_DEBUG(("Parsing incoming packet"));
 
@@ -246,32 +695,38 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
                             SILC_STR_UI_SHORT(&ctx->truelen),
                             SILC_STR_UI_CHAR(&ctx->flags),
                             SILC_STR_UI_CHAR(&ctx->type),
-                            SILC_STR_UI_SHORT(&ctx->src_id_len),
-                            SILC_STR_UI_SHORT(&ctx->dst_id_len),
+                            SILC_STR_UI_CHAR(&ctx->padlen),
+                            SILC_STR_UI_CHAR(&tmp),
+                            SILC_STR_UI_CHAR(&ctx->src_id_len),
+                            SILC_STR_UI_CHAR(&ctx->dst_id_len),
                             SILC_STR_UI_CHAR(&ctx->src_id_type),
                             SILC_STR_END);
+  if (len == -1 || tmp != 0) {
+    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
+    return SILC_PACKET_NONE;
+  }
 
   if (ctx->src_id_len > SILC_PACKET_MAX_ID_LEN ||
       ctx->dst_id_len > SILC_PACKET_MAX_ID_LEN) {
-    SILC_LOG_ERROR(("Bad ID lengths in packet"));
+    SILC_LOG_ERROR(("Bad ID lengths in packet (%d and %d)",
+                   ctx->src_id_len, ctx->dst_id_len));
     return SILC_PACKET_NONE;
   }
 
-  /* Calculate length of padding in packet. As this is special packet
-     the data area is not used in the padding calculation as it won't
-     be decrypted by the caller. */
-  tmplen = SILC_PACKET_HEADER_LEN + ctx->src_id_len + ctx->dst_id_len;
-  ctx->padlen = SILC_PACKET_PADLEN(tmplen);
-
   silc_buffer_pull(buffer, len);
-  silc_buffer_unformat(buffer, 
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
-                                                 ctx->src_id_len),
-                      SILC_STR_UI_CHAR(&ctx->dst_id_type),
-                      SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
-                                                 ctx->dst_id_len),
-                      SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
-                      SILC_STR_END);
+  ret = silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
+                                                       ctx->src_id_len),
+                            SILC_STR_UI_CHAR(&ctx->dst_id_type),
+                            SILC_STR_UI_XNSTRING_ALLOC(&ctx->dst_id,
+                                                       ctx->dst_id_len),
+                            SILC_STR_UI_XNSTRING(NULL, ctx->padlen),
+                            SILC_STR_END);
+  if (ret == -1) {
+    SILC_LOG_ERROR(("Malformed packet header, packet dropped"));
+    return SILC_PACKET_NONE;
+  }
+
   silc_buffer_push(buffer, len);
 
   SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
@@ -286,103 +741,41 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx)
   return ctx->type;
 }
 
-/* Assembles a new packet to be ready for send out. The buffer sent as
-   argument must include the data to be sent and it must not be encrypted. 
-   The packet also must have enough free space so that the SILC header
-   and padding maybe added to the packet. The packet is encrypted after 
-   this function has returned.
-
-   The buffer sent as argument should be something like following:
-
-   --------------------------------------------
-   | head             | data           | tail |
-   --------------------------------------------
-   ^                  ^
-   58 bytes           x bytes
-
-   So that the SILC header and 1 - 16 bytes of padding can fit to
-   the buffer. After assembly the buffer might look like this:
+/* Allocate packet context */
 
-   --------------------------------------------
-   | data                              |      |
-   --------------------------------------------
-   ^                                   ^
-   Start of assembled packet
-
-   Packet construct is as follows (* = won't be encrypted):
-
-   x bytes       SILC Header
-      2 bytes     Payload length  (*)
-      1 byte      Flags           (*)
-      1 byte      Packet type
-      1 byte      Source ID Type
-      2 bytes     Source ID Length
-      x bytes     Source ID
-      1 byte      Destination ID Type
-      2 bytes     Destination ID Length
-      x bytes     Destination ID
-
-   1 - 16 bytes    Padding
-
-   x bytes        Data payload
-
-   All fields in the packet will be authenticated by MAC. The MAC is
-   not computed here, it must be computed differently before encrypting
-   the packet.
-
-*/
-
-void silc_packet_assemble(SilcPacketContext *ctx)
+SilcPacketContext *silc_packet_context_alloc(void)
 {
-  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
-  int i;
-
-  SILC_LOG_DEBUG(("Assembling outgoing packet"));
-  
-  /* Get the true length of the packet. This is saved as payload length
-     into the packet header. This does not include the length of the
-     padding. */
-  if (!ctx->truelen)
-    ctx->truelen = ctx->buffer->len + SILC_PACKET_HEADER_LEN + 
-      ctx->src_id_len + ctx->dst_id_len;
-
-  /* Calculate the length of the padding. The padding is calculated from
-     the data that will be encrypted. As protocol states 3 first bytes
-     of the packet are not encrypted they are not included in the
-     padding calculation. */
-  if (!ctx->padlen)
-    ctx->padlen = SILC_PACKET_PADLEN(ctx->truelen);
-
-  /* Put the start of the data section to the right place. */
-  silc_buffer_push(ctx->buffer, SILC_PACKET_HEADER_LEN + 
-                  ctx->src_id_len + ctx->dst_id_len + ctx->padlen);
+  SilcPacketContext *ctx = silc_calloc(1, sizeof(*ctx));
+  ctx->users++;
+  return ctx;
+}
 
-  /* Get random padding */
-#if 1
-  for (i = 0; i < ctx->padlen; i++)
-    tmppad[i] = silc_rng_get_byte(ctx->rng);
-#else
-  /* XXX: For testing - to be removed */
-  memset(tmppad, 65, sizeof(tmppad));
-#endif
+/* Increse the reference count of the packet context. */
 
-  /* Create the packet. This creates the SILC header and adds padding,
-     rest of the buffer remains as it is. */
-  silc_buffer_format(ctx->buffer, 
-                    SILC_STR_UI_SHORT(ctx->truelen),
-                    SILC_STR_UI_CHAR(ctx->flags),
-                    SILC_STR_UI_CHAR(ctx->type),
-                    SILC_STR_UI_SHORT(ctx->src_id_len),
-                    SILC_STR_UI_SHORT(ctx->dst_id_len),
-                    SILC_STR_UI_CHAR(ctx->src_id_type),
-                    SILC_STR_UI_XNSTRING(ctx->src_id, ctx->src_id_len),
-                    SILC_STR_UI_CHAR(ctx->dst_id_type),
-                    SILC_STR_UI_XNSTRING(ctx->dst_id, ctx->dst_id_len),
-                    SILC_STR_UI_XNSTRING(tmppad, ctx->padlen),
-                    SILC_STR_END);
+SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx)
+{
+  ctx->users++;
+  SILC_LOG_DEBUG(("Packet context %p refcnt %d->%d", ctx, ctx->users - 1,
+                 ctx->users));
+  return ctx;
+}
 
-  SILC_LOG_HEXDUMP(("Assembled packet, len %d", ctx->buffer->len), 
-                  ctx->buffer->data, ctx->buffer->len);
+/* Decrese the reference count of the packet context and free it only if
+   it is zero. */
 
-  SILC_LOG_DEBUG(("Outgoing packet assembled"));
+void silc_packet_context_free(SilcPacketContext *ctx)
+{
+  ctx->users--;
+  SILC_LOG_DEBUG(("Packet context %p refcnt %d->%d", ctx, ctx->users + 1,
+                 ctx->users));
+  if (ctx->users < 1)
+    {
+      if (ctx->buffer)
+       silc_buffer_free(ctx->buffer);
+      if (ctx->src_id)
+       silc_free(ctx->src_id);
+      if (ctx->dst_id)
+       silc_free(ctx->dst_id);
+      silc_free(ctx);
+    }
 }
index a26447900093483f8190fc9ee56c3a1399b18497..54d6bc16a1a9d58962547b7a2f3fc4b9e0267fab 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silcpacket.h
+  silcpacket.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 */
 
+/****h* silccore/SilcPacketAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the packet routines for sending and receiving
+ * SILC Packets. These includes the data sending routines and data
+ * reading routines, encrypting and decrypting routines, packet assembling
+ * and packet parsing routines.
+ *
+ ***/
+
 #ifndef SILCPACKET_H
 #define SILCPACKET_H
 
-/* Amount of bytes to be read from the socket connection at once. */
-#define SILC_PACKET_READ_SIZE 16384
-
-/* Default byte size of the packet. This can be set larger if this
-   is not enough, we shall see. */
-#define SILC_PACKET_DEFAULT_SIZE 2048
+/* Default byte size of the packet. */
+#define SILC_PACKET_DEFAULT_SIZE SILC_SOCKET_BUF_SIZE
 
 /* Header length without source and destination ID's. */
-#define SILC_PACKET_HEADER_LEN 8 + 2
+#define SILC_PACKET_HEADER_LEN 10
 
 /* Minimum length of SILC Packet Header. This much is decrypted always
    when packet is received to be able to get all the relevant data out
    from the header. */
-#define SILC_PACKET_MIN_HEADER_LEN 16 + 2
+#define SILC_PACKET_MIN_HEADER_LEN 16
 
 /* Maximum padding length */
-#define SILC_PACKET_MAX_PADLEN 16
+#define SILC_PACKET_MAX_PADLEN 128
+
+/* Default padding length */
+#define SILC_PACKET_DEFAULT_PADLEN 16
 
 /* Minimum packet length */
 #define SILC_PACKET_MIN_LEN (SILC_PACKET_HEADER_LEN + 1)
 /* Maximum length of ID */
 #define SILC_PACKET_MAX_ID_LEN 16
 
-/* SILC packet type definition. For now, it is defined like this and I don't 
-   expect it to change in any near future. If one byte as a packet type is 
-   not enough we can, then, think something else. */
+/****d* silccore/SilcPacketAPI/SilcPacketType
+ *
+ * NAME
+ * 
+ *    typedef unsigned char SilcPacketType;
+ *
+ * DESCRIPTION
+ *
+ *    SILC packet type definition and all the packet types.
+ *
+ * SOURCE
+ */
 typedef unsigned char SilcPacketType;
 
-/* SILC packet version type definition. */
+/* SILC Packet types. */
+#define SILC_PACKET_NONE                0       /* NULL, never sent */
+#define SILC_PACKET_DISCONNECT          1       /* Disconnection */
+#define SILC_PACKET_SUCCESS             2       /* Success */
+#define SILC_PACKET_FAILURE             3       /* Failure */
+#define SILC_PACKET_REJECT              4       /* Rejected */
+#define SILC_PACKET_NOTIFY               5       /* Notify message */
+#define SILC_PACKET_ERROR                6       /* Error message */
+#define SILC_PACKET_CHANNEL_MESSAGE     7       /* Message for channel */
+#define SILC_PACKET_CHANNEL_KEY          8       /* Key of the channel */
+#define SILC_PACKET_PRIVATE_MESSAGE      9       /* Private message */
+#define SILC_PACKET_PRIVATE_MESSAGE_KEY  10      /* Private message key*/
+#define SILC_PACKET_COMMAND              11      /* Command */
+#define SILC_PACKET_COMMAND_REPLY        12      /* Reply to a command */
+#define SILC_PACKET_KEY_EXCHANGE         13      /* Start of KE */
+#define SILC_PACKET_KEY_EXCHANGE_1       14      /* KE1 */
+#define SILC_PACKET_KEY_EXCHANGE_2       15      /* KE2 */
+#define SILC_PACKET_CONNECTION_AUTH_REQUEST 16   /* Request of auth meth */
+#define SILC_PACKET_CONNECTION_AUTH      17      /* Connectinon auth */
+#define SILC_PACKET_NEW_ID               18      /* Sending new ID */
+#define SILC_PACKET_NEW_CLIENT           19      /* Client registering */
+#define SILC_PACKET_NEW_SERVER           20      /* Server registering */
+#define SILC_PACKET_NEW_CHANNEL          21      /* Channel registering */
+#define SILC_PACKET_REKEY                22      /* Re-key start */
+#define SILC_PACKET_REKEY_DONE           23      /* Re-key done */
+#define SILC_PACKET_HEARTBEAT            24      /* Heartbeat */
+#define SILC_PACKET_KEY_AGREEMENT        25      /* Key Agreement request */
+#define SILC_PACKET_RESUME_ROUTER        26      /* Backup router resume */
+#define SILC_PACKET_FTP                  27      /* File Transfer */
+
+#define SILC_PACKET_PRIVATE              200     /* Private range start  */
+#define SILC_PACKET_MAX                  255     /* RESERVED */
+/***/
+
+/****d* silccore/SilcPacketAPI/SilcPacketVersion
+ *
+ * NAME
+ * 
+ *    typedef unsigned char SilcPacketVersion;
+ *
+ * DESCRIPTION
+ *
+ *    SILC packet version type definition.
+ *
+ ***/
 typedef unsigned char SilcPacketVersion;
 
-/* SILC packet flags type definition. */
+/****d* silccore/SilcPacketAPI/SilcPacketFlags
+ *
+ * NAME
+ * 
+ *    typedef unsigned char SilcPacketFlags;
+ *
+ * DESCRIPTION
+ *
+ *    SILC packet flags type definition and all the packet flags.
+ *
+ * SOURCE
+ */
 typedef unsigned char SilcPacketFlags;
 
 /* All defined packet flags */
-#define SILC_PACKET_FLAG_NONE             0x00
-#define SILC_PACKET_FLAG_PRIVMSG_KEY      0x01
-#define SILC_PACKET_FLAG_FORWARDED        0x02 /* XXX deprecated 26062000 */
-#define SILC_PACKET_FLAG_BROADCAST        0x04
+#define SILC_PACKET_FLAG_NONE             0x00    /* No flags */
+#define SILC_PACKET_FLAG_PRIVMSG_KEY      0x01   /* Private message key */
+#define SILC_PACKET_FLAG_LIST             0x02   /* Packet is a list */
+#define SILC_PACKET_FLAG_BROADCAST        0x04   /* Packet is a broadcast */
+/***/
+
 /* Rest of flags still available
 #define SILC_PACKET_FLAG_XXX              0x08
 #define SILC_PACKET_FLAG_XXX              0x10
@@ -69,117 +144,470 @@ typedef unsigned char SilcPacketFlags;
 #define SILC_PACKET_FLAG_XXX              0x80
 */
 
-/* 
-   SILC packet context. 
-
-   In packet sending this is filled and sent to silc_packet_assemble 
-   which then uses it to assemble new packet. In packet reception pointer 
-   to this context is sent to silc_packet_parse which parses the packet 
-   and returns the relevant information to this structure. On packet 
-   reception returned ID's are always the hash values of the ID's from 
-   the packet. 
-
-   Short description of the fields following:
-
-   SilcBuffer buffer
-
-       The data buffer.
-
-   SilcPacketType type
-
-       Type of the packet. Types are defined below.
-
-   SilcPacketFlags flags
-
-       Packet flags. Flags are defined above.
-
-   unsigned char *src_id
-   unsigned int src_id_len
-   SilcIdType src_id_type
-
-       Source ID, its length and type. On packet reception retuned ID's
-       are always the hash values of the ID's from the packet.
-
-   SilcHash hash
-
-       Pointer to allocated hash object. This must be MD5 hash object.
-       This is used to calculate checksum of the packet.
-
-*/
+/****s* silccore/SilcPacketAPI/SilcPacketContext
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcPacketContext;
+ *
+ * DESCRIPTION
+ *
+ *    In packet sending this is filled and sent to silc_packet_assemble 
+ *    which then uses it to assemble new packet. In packet reception pointer 
+ *    to this context is sent to silc_packet_parse which parses the packet 
+ *    and returns the relevant information to this structure. On packet 
+ *    reception returned ID's are always the hash values of the ID's from 
+ *    the packet. 
+ *
+ *    Short description of the fields following:
+ *
+ *    SilcBuffer buffer
+ *
+ *      The data buffer.
+ *
+ *    SilcPacketType type
+ *
+ *      Type of the packet. Types are defined below.
+ *
+ *    SilcPacketFlags flags
+ *
+ *      Packet flags. Flags are defined above.
+ *
+ *    unsigned char *src_id
+ *    uint8 src_id_len
+ *    unsigned char src_id_type
+ *
+ *      Source ID, its length and type. On packet reception retuned ID's
+ *      are always the hash values of the ID's from the packet.
+ *
+ *    unsigned char *dst_id;
+ *    uint8 dst_id_len;
+ *    unsigned char src_id_type;
+ *
+ *      Destination ID, its length and type. On packet reception retuned
+ *      ID's are always the hash values of the ID's from the packet.
+ *
+ *    uint16 truelen
+ *    uint8 padlen
+ *
+ *      The true lenght of the packet and the padded length of the packet.
+ *      These may be set by the caller before calling any of the 
+ *      silc_packet_* routines. If not provided the library will calculate
+ *      the values.
+ *
+ *    int users;
+ *
+ *      Reference counter for this context. The context is freed only 
+ *      after the reference counter hits zero. The counter is added
+ *      calling silc_packet_context_dup and decreased by calling the
+ *      silc_packet_context_free.
+ *
+ *    uint32 sequence;
+ *
+ *      Packet sequence number.
+ *
+ ***/
 typedef struct {
   SilcBuffer buffer;
-  SilcPacketType type;
+
+  uint16 truelen;
   SilcPacketFlags flags;
+  SilcPacketType type;
+  uint8 padlen;
 
   unsigned char *src_id;
-  unsigned int src_id_len;
-  SilcIdType src_id_type;
+  uint8 src_id_len;
+  uint8 src_id_type;
 
   unsigned char *dst_id;
-  unsigned int dst_id_len;
-  SilcIdType dst_id_type;
+  uint8 dst_id_len;
+  uint8 dst_id_type;
 
-  unsigned int truelen;
-  unsigned int padlen;
+  int users;
+  bool long_pad;               /* Set to TRUE to use maximum padding
+                                  in packet (up to 256 bytes). */
 
-  /* For padding generation */
-  SilcRng rng;
+  uint32 sequence;
 } SilcPacketContext;
 
-/* SILC Packet types. */
-#define SILC_PACKET_NONE                0       /* NULL, never sent */
-#define SILC_PACKET_DISCONNECT          1       /* Disconnection */
-#define SILC_PACKET_SUCCESS             2       /* Success */
-#define SILC_PACKET_FAILURE             3       /* Failure */
-#define SILC_PACKET_REJECT              4       /* Rejected */
-#define SILC_PACKET_NOTIFY               5       /* Notify message */
-#define SILC_PACKET_ERROR                6       /* Error message */
-#define SILC_PACKET_CHANNEL_MESSAGE     7       /* Message for channel */
-#define SILC_PACKET_CHANNEL_KEY          8       /* Key of the channel */
-#define SILC_PACKET_PRIVATE_MESSAGE      9       /* Private message */
-#define SILC_PACKET_PRIVATE_MESSAGE_KEY  10      /* Private message key*/
-#define SILC_PACKET_COMMAND              11      /* Command */
-#define SILC_PACKET_COMMAND_REPLY        12      /* Reply to a command */
-#define SILC_PACKET_KEY_EXCHANGE         13      /* Start of KE */
-#define SILC_PACKET_KEY_EXCHANGE_1       14      /* KE1 */
-#define SILC_PACKET_KEY_EXCHANGE_2       15      /* KE2 */
-#define SILC_PACKET_CONNECTION_AUTH_REQUEST 16   /* Request of auth meth */
-#define SILC_PACKET_CONNECTION_AUTH      17      /* Connectinon auth */
-#define SILC_PACKET_NEW_ID               18      /* Sending new ID */
-#define SILC_PACKET_NEW_ID_LIST          19      /* Sending list of them */
-#define SILC_PACKET_NEW_CLIENT           20      /* Registering client */
-#define SILC_PACKET_NEW_SERVER           21      /* Registering server */
-#define SILC_PACKET_NEW_CHANNEL          22      /* Registering channel */
-#define SILC_PACKET_NEW_CHANNEL_USER     23      /*   "" user on channel */
-#define SILC_PACKET_NEW_CHANNEL_LIST     24      /* List of new channels */
-#define SILC_PACKET_NEW_CHANNEL_USER_LIST 25     /* List of users on "" */
-#define SILC_PACKET_REPLACE_ID           26      /* To replace old ID */
-#define SILC_PACKET_REMOVE_ID            27      /* To remove ID */
-/* #define SILC_PACKET_MAX               255 */
+/****s* silccore/SilcPacketAPI/SilcPacketParserContext
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcPacketParserContext;
+ *
+ * DESCRIPTION
+ *
+ *    This context is used in packet reception when the function
+ *    silc_packet_receive_process calls parser callback that performs
+ *    the actual packet decryption and parsing. This context is sent as
+ *    argument to the parser function. This context must be free'd by
+ *    the parser callback function.
+ *
+ *    Following description of the fields:
+ *
+ *    SilcPacketContext *packet
+ *
+ *      The actual packet received from the network. In this phase the
+ *      context is not parsed, only the packet->buffer is allocated and
+ *      it includes the raw packet data, which is encrypted.
+ *
+ *    bool normal
+ *
+ *      Indicates whether the received packet is normal or special packet.
+ *      If special the parsing process is special also.
+ *
+ *    SilcSocketConnection sock
+ *
+ *      The associated connection.
+ *
+ *    void *context
+ *
+ *      User context that is sent to the silc_packet_receive_process
+ *      function. This usually includes application and connection specific
+ *      data.
+ *
+ ***/
+typedef struct {
+  SilcPacketContext *packet;
+  bool normal;
+  SilcSocketConnection sock;
+  void *context;
+} SilcPacketParserContext;
+
+/****f* silccore/SilcPacketAPI/SilcPacketParserCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef bool (*SilcPacketParserCallback)(SilcPacketParserContext 
+ *                                             *parse_context);
+ *
+ * DESCRIPTION
+ *
+ *    This callback is given to the silc_packet_receive_process function.
+ *    The callback is called by the library every time a packet is
+ *    received from the network. After the packet has been decrypted
+ *    and at least partially parsed it is passed to the application
+ *    for further parsing using this callback and the SilcPacketParserContext
+ *    context. The application receiving the SilcPacketParserContext
+ *    must free it.
+ *
+ *    This returns TRUE if the library should continue packet processing
+ *    (assuming there is more data to be processed), and FALSE if the
+ *    upper layer does not want the library to continue but to leave the
+ *    rest of the data is the packet queue untouched.  Application may
+ *    want to do this for example if the cipher is not ready before 
+ *    processing a certain packet.  In this case the application wants
+ *    to recall the processing function with the correct cipher.
+ *
+ ***/
+typedef bool (*SilcPacketParserCallback)(SilcPacketParserContext 
+                                        *parse_context, void *context);
 
 /* Macros */
 
-/* Returns true length of the packet and padded length of the packet */
-#define SILC_PACKET_LENGTH(__packet, __ret_truelen, __ret_padlen)           \
-do {                                                                        \
-  SILC_GET16_MSB((__ret_truelen), (__packet)->data);                        \
-  (__ret_padlen) = (((__ret_truelen) - 2) +                                 \
-                   SILC_PACKET_MAX_PADLEN) & ~(SILC_PACKET_MAX_PADLEN - 1); \
+/****d* silccore/SilcPacketAPI/SILC_PACKET_LENGTH
+ *
+ * NAME
+ * 
+ *    #define SILC_PACKET_LENGTH ...
+ *
+ * DESCRIPTION
+ *
+ *    Returns true length of the packet. This is primarily used by the
+ *    libary in packet parsing phase but the application may use it as
+ *    well if needed.
+ *
+ * SOURCE
+ */
+#define SILC_PACKET_LENGTH(__packet, __ret_truelen, __ret_paddedlen)   \
+do {                                                                   \
+  SILC_GET16_MSB((__ret_truelen), (__packet)->data);                   \
+  (__ret_paddedlen) = (__ret_truelen) + (__packet)->data[4];           \
 } while(0)
-
-/* Returns pad length of the packet */
-#define SILC_PACKET_PADLEN(__packetlen)                                         \
-  SILC_PACKET_MAX_PADLEN - ((__packetlen) - 2) % SILC_PACKET_MAX_PADLEN;
+/***/
+
+/****d* silccore/SilcPacketAPI/SILC_PACKET_PADLEN
+ *
+ * NAME
+ * 
+ *    #define SILC_PACKET_PADLEN ...
+ *
+ * DESCRIPTION
+ *
+ *    Returns the length of the padding in the packet. This is used
+ *    by various library routines to determine needed padding length.
+ *
+ * SOURCE
+ */
+#define SILC_PACKET_PADLEN(__packetlen, __blocklen)            \
+  SILC_PACKET_DEFAULT_PADLEN - (__packetlen) %                 \
+    ((__blocklen) ? (__blocklen) : SILC_PACKET_DEFAULT_PADLEN)
+/***/
+
+/****d* silccore/SilcPacketAPI/SILC_PACKET_PADLEN_MAX
+ *
+ * NAME
+ * 
+ *    #define SILC_PACKET_PADLEN_MAX ...
+ *
+ * DESCRIPTION
+ *
+ *    Returns the length of the padding up to the maximum length, which
+ *    is 128 butes. This is used by various library routines to determine
+ *    needed padding length.
+ *
+ * SOURCE
+ */
+#define SILC_PACKET_PADLEN_MAX(__packetlen)                            \
+  SILC_PACKET_MAX_PADLEN - (__packetlen) % SILC_PACKET_MAX_PADLEN
+/***/
 
 /* Prototypes */
-int silc_packet_write(int sock, SilcBuffer src);
-int silc_packet_read(int sock, SilcBuffer dest);
-void silc_packet_encrypt(SilcCipher cipher, SilcBuffer buffer,
-                        unsigned int len);
-void silc_packet_decrypt(SilcCipher cipher, SilcBuffer buffer, 
-                        unsigned int len);
-SilcPacketType silc_packet_parse(SilcPacketContext *ctx);
-SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx);
-void silc_packet_assemble(SilcPacketContext *ctx);
+
+/****f* silccore/SilcPacketAPI/silc_packet_send
+ *
+ * SYNOPSIS
+ *
+ *    int silc_packet_send(SilcSocketConnection sock, bool force_send);
+ *
+ * DESCRIPTION
+ *
+ *    Actually sends the packet. This flushes the connections outgoing data
+ *    buffer. If data is sent directly to the network this returns the bytes
+ *    written, if error occured this returns -1 and if the data could not
+ *    be written directly to the network at this time this returns -2, in
+ *    which case the data should be queued by the caller and sent at some
+ *    later time. If `force_send' is TRUE this attempts to write the data
+ *    directly to the network, if FALSE, this returns -2.
+ *
+ ***/
+int silc_packet_send(SilcSocketConnection sock, bool force_send);
+
+/****f* silccore/SilcPacketAPI/silc_packet_encrypt
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, 
+ *                             SilcBuffer buffer, uint32 len);
+ *
+ * DESCRIPTION
+ *
+ *    Encrypts a packet. This also creates HMAC of the packet before 
+ *    encryption and adds the HMAC at the end of the buffer. This assumes
+ *    that there is enough free space at the end of the buffer to add the
+ *    computed HMAC. This is the normal way of encrypting packets, if some
+ *    other process of HMAC computing and encryption is needed this function
+ *    cannot be used. 
+ *
+ ***/
+void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, uint32 sequence,
+                        SilcBuffer buffer, uint32 len);
+
+/****f* silccore/SilcPacketAPI/silc_packet_assemble
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_assemble(SilcPacketContext *ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Assembles a new packet to be ready for send out. The buffer sent as
+ *    argument must include the data to be sent and it must not be encrypted. 
+ *    The packet also must have enough free space so that the SILC header
+ *    and padding maybe added to the packet. The packet is encrypted after 
+ *    this function has returned.
+ *
+ *    The buffer sent as argument should be something like following:
+ *
+ *    --------------------------------------------
+ *    | head             | data           | tail |
+ *    --------------------------------------------
+ *    ^                  ^
+ *    58 bytes           x bytes
+ *
+ *    So that the SILC header and 1 - 16 bytes of padding can fit to
+ *    the buffer. After assembly the buffer might look like this:
+ *
+ *    --------------------------------------------
+ *    | data                              |      |
+ *    --------------------------------------------
+ *    ^                                   ^
+ *    Start of assembled packet
+ *
+ *    Packet construct is as follows (* = won't be encrypted):
+ *
+ *    n bytes       SILC Header
+ *      2 bytes     Payload length  (*)
+ *      1 byte      Flags
+ *      1 byte      Packet type
+ *      2 bytes     Source ID Length
+ *      2 bytes     Destination ID Length
+ *      1 byte      Source ID Type
+ *      n bytes     Source ID
+ *      1 byte      Destination ID Type
+ *      n bytes     Destination ID
+ *
+ *    1 - 16 bytes    Padding
+ *
+ *    n bytes        Data payload
+ *
+ *    All fields in the packet will be authenticated by MAC. The MAC is
+ *    not computed here, it must be computed separately before encrypting
+ *    the packet.
+ *
+ ***/
+void silc_packet_assemble(SilcPacketContext *ctx, SilcCipher cipher);
+
+/****f* silccore/SilcPacketAPI/silc_packet_send_prepare
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_send_prepare(SilcSocketConnection sock,
+ *                                  uint32 header_len,
+ *                                  uint32 padlen,
+ *                                  uint32 data_len);
+ *
+ * DESCRIPTION
+ *
+ *    Prepare outgoing data buffer for packet sending. This moves the data
+ *    area so that new packet may be added into it. If needed this allocates
+ *    more space to the buffer. This handles directly the connection's
+ *    outgoing buffer in SilcSocketConnection object.
+ *
+ ***/
+void silc_packet_send_prepare(SilcSocketConnection sock,
+                             uint32 header_len,
+                             uint32 padlen,
+                             uint32 data_len);
+
+/****f* silccore/SilcPacketAPI/silc_packet_receive
+ *
+ * SYNOPSIS
+ *
+ *    int silc_packet_receive(SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Receives packet from network and reads the data into connection's
+ *    incoming data buffer. If the data was read directly this returns the
+ *    read bytes, if error occured this returns -1, if the data could not
+ *    be read directly at this time this returns -2 in which case the data
+ *    should be read again at some later time, or If EOF occured this returns
+ *    0.
+ *
+ ***/
+int silc_packet_receive(SilcSocketConnection sock);
+
+/****f* silccore/SilcPacketAPI/silc_packet_receive_process
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_packet_receive_process(SilcSocketConnection sock,
+ *                                     bool local_is_router,
+ *                                     SilcCipher cipher, SilcHmac hmac,
+ *                                     SilcPacketParserCallback parser,
+ *                                     void *parser_context);
+ *
+ * DESCRIPTION
+ *
+ *    Processes and decrypts the incmoing data, and calls parser callback
+ *    for each received packet that will handle the actual packet parsing.
+ *    If more than one packet was received this calls the parser multiple
+ *    times.  The parser callback will get context SilcPacketParserContext
+ *    that includes the packet and the `parser_context' sent to this
+ *    function. 
+ *
+ *    The `local_is_router' indicates whether the caller is router server
+ *    in which case the receiving process of a certain packet types may
+ *    be special.  Normal server and client must set it to FALSE.  The
+ *    SilcPacketParserContext will indicate also whether the received
+ *    packet was normal or special packet.
+ *
+ ***/
+bool silc_packet_receive_process(SilcSocketConnection sock,
+                                bool local_is_router,
+                                SilcCipher cipher, SilcHmac hmac,
+                                uint32 sequence,
+                                SilcPacketParserCallback parser,
+                                void *parser_context);
+
+/****f* silccore/SilcPacketAPI/silc_packet_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcPacketType silc_packet_parse(SilcPacketContext *ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Parses the packet. This is called when a whole packet is ready to be
+ *    parsed. The buffer sent must be already decrypted before calling this 
+ *    function. The len argument must be the true length of the packet. This 
+ *    function returns the type of the packet. The data section of the 
+ *    buffer is parsed, not head or tail sections.
+ *
+ ***/
+SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher);
+
+/****f* silccore/SilcPacketAPI/silc_packet_parse_special
+ *
+ * SYNOPSIS
+ *
+ *    SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Perform special SILC Packet header parsing. This is required to some
+ *    packet types that have the data payload encrypted with different key
+ *    than the header area plus padding of the packet. Hence, this parses
+ *    the header in a way that it does not take the data area into account
+ *    and parses the header and padding area only.
+ *
+ ***/
+SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
+                                        SilcCipher cipher);
+
+/****f* silccore/SilcPacketAPI/silc_packet_context_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcPacketContext *silc_packet_context_alloc();
+ *
+ * DESCRIPTION
+ *
+ *    Allocates a packet context. Packet contexts are used when 
+ *    packets are assembled and parsed. The context is freed by the
+ *    silc_packet_context_free function.
+ *
+ ***/
+SilcPacketContext *silc_packet_context_alloc(void);
+
+/****f* silccore/SilcPacketAPI/silc_packet_context_dup
+ *
+ * SYNOPSIS
+ *
+ *    SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicates the packet context. It actually does not duplicate
+ *    any data, instead a reference counter is increased.
+ *
+ ***/
+SilcPacketContext *silc_packet_context_dup(SilcPacketContext *ctx);
+
+/****f* silccore/SilcPacketAPI/silc_packet_context_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_packet_context_free(SilcPacketContext *ctx);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the packet context. The context is actually freed when the
+ *    reference counter hits zero.
+ *
+ ***/
+void silc_packet_context_free(SilcPacketContext *ctx);
 
 #endif
diff --git a/lib/silccore/silcprivate.c b/lib/silccore/silcprivate.c
new file mode 100644 (file)
index 0000000..ac56c1d
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+
+  silcprivate.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* Includes the Private Message Payload implementation */
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcprivate.h"
+
+/******************************************************************************
+
+                           Private Message Payload
+
+******************************************************************************/
+
+#define SILC_PRIVATE_MESSAGE_PAD(__payloadlen) (16 - (__payloadlen) % 16)
+
+/* Private Message Payload structure. Contents of this structure is parsed
+   from SILC packets. */
+struct SilcPrivateMessagePayloadStruct {
+  uint16 flags;
+  uint16 message_len;
+  unsigned char *message;
+};
+
+/* Parses private message payload returning new private mesage payload 
+   structure. This also decrypts the message if the `cipher' is provided. */
+
+SilcPrivateMessagePayload 
+silc_private_message_payload_parse(unsigned char *payload,
+                                  uint32 payload_len,
+                                  SilcCipher cipher)
+{
+  SilcBufferStruct buffer;
+  SilcPrivateMessagePayload new;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing private message payload"));
+
+  silc_buffer_set(&buffer, payload, payload_len);
+
+  /* Decrypt the payload */
+  if (cipher)
+    silc_cipher_decrypt(cipher, buffer.data, buffer.data, 
+                       buffer.len, cipher->iv);
+
+  new = silc_calloc(1, sizeof(*new));
+
+  /* Parse the Private Message Payload. Ignore the padding. */
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&new->flags),
+                            SILC_STR_UI16_NSTRING_ALLOC(&new->message, 
+                                                        &new->message_len),
+                            SILC_STR_END);
+  if (ret == -1) {
+    SILC_LOG_DEBUG(("Incorrect private message payload"));
+    goto err;
+  }
+
+  if ((new->message_len < 1 || new->message_len > buffer.len)) {
+    SILC_LOG_DEBUG(("Incorrect private message payload in packet, "
+                   "packet dropped"));
+    goto err;
+  }
+
+  return new;
+
+ err:
+  silc_private_message_payload_free(new);
+  return NULL;
+}
+
+/* Encodes private message payload into a buffer and returns it.  If
+   the cipher is provided the packet is also encrypted here.  It is provided
+   if the private message private keys are used. */
+
+SilcBuffer silc_private_message_payload_encode(uint16 flags,
+                                              uint16 data_len,
+                                              const unsigned char *data,
+                                              SilcCipher cipher)
+{
+  int i;
+  SilcBuffer buffer;
+  uint32 len, pad_len = 0;
+  unsigned char pad[16];
+
+  SILC_LOG_DEBUG(("Encoding private message payload"));
+
+  len = 4 + data_len;
+
+  if (cipher) {
+    /* Calculate length of padding. */
+    pad_len = SILC_PRIVATE_MESSAGE_PAD(len);
+    len += pad_len;
+
+    /* Generate padding */
+    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
+  }
+
+  /* Allocate private message payload buffer */
+  buffer = silc_buffer_alloc(len);
+
+  /* Encode the Channel Message Payload */
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_SHORT(flags),
+                    SILC_STR_UI_SHORT(data_len),
+                    SILC_STR_UI_XNSTRING(data, data_len),
+                    SILC_STR_UI_XNSTRING(pad, pad_len),
+                    SILC_STR_END);
+
+  if (cipher) {
+    /* Encrypt payload of the packet. */
+    silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
+                       buffer->len, cipher->iv);
+    memset(pad, 0, sizeof(pad));
+  }
+
+  return buffer;
+}
+
+/* Frees Private Message Payload */
+
+void silc_private_message_payload_free(SilcPrivateMessagePayload payload)
+{
+  if (payload->message) {
+    memset(payload->message, 0, payload->message_len);
+    silc_free(payload->message);
+  }
+  silc_free(payload);
+}
+
+/* Return flags */
+
+uint16 
+silc_private_message_get_flags(SilcPrivateMessagePayload payload)
+{
+  return payload->flags;
+}
+
+/* Return message */
+
+unsigned char *
+silc_private_message_get_message(SilcPrivateMessagePayload payload,
+                                uint32 *message_len)
+{
+  if (message_len)
+    *message_len = payload->message_len;
+
+  return payload->message;
+}
diff --git a/lib/silccore/silcprivate.h b/lib/silccore/silcprivate.h
new file mode 100644 (file)
index 0000000..f8beca2
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+  silcprivate.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/SilcPrivateAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the SILC Private Message Payload that is used to
+ * deliver private messages.
+ *
+ ***/
+
+#ifndef SILCPRIVATE_H
+#define SILCPRIVATE_H
+
+/****s* silccore/SilcPrivateAPI/SilcPrivateMessagePayload
+ *
+ * NAME
+ * 
+ *    typedef struct SilcPrivateMessagePayloadStruct 
+ *                     *SilcPrivateMessagePayload;
+ *
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Private Message Payload and is allocated
+ *    by silc_private_message_payload_parse and given as argument usually
+ *    to all silc_private_message_* functions.  It is freed by the
+ *    silc_private_message_payload_free function.
+ *
+ ***/
+typedef struct SilcPrivateMessagePayloadStruct *SilcPrivateMessagePayload;
+
+/* Prototypes */
+
+/****f* silccore/SilcPrivateAPI/silc_private_message_payload_parse
+ *
+ * SYNOPSIS
+ *
+ *    SilcPrivateMessagePayload 
+ *    silc_private_message_payload_parse(unsigned char *payload,
+ *                                       uint32 payload_len,
+ *                                       SilcCipher cipher);
+ *
+ * DESCRIPTION
+ *
+ *    Parses private message payload returning new private mesage payload 
+ *    structure. This also decrypts the message if the `cipher' is provided.
+ *
+ ***/
+SilcPrivateMessagePayload 
+silc_private_message_payload_parse(unsigned char *payload,
+                                  uint32 payload_len,
+                                  SilcCipher cipher);
+
+/****f* silccore/SilcPrivateAPI/silc_private_message_payload_encode
+ *
+ * SYNOPSIS
+ *
+ *    SilcBuffer silc_private_message_payload_encode(uint16 flags,
+ *                                                   uint16 data_len,
+ *                                                   const unsigned char *data,
+ *                                                   SilcCipher cipher);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes private message payload into a buffer and returns it.  If
+ *    the cipher is provided the packet is also encrypted here.  It is provided
+ *    if the private message private keys are used.
+ *
+ ***/
+SilcBuffer silc_private_message_payload_encode(uint16 flags,
+                                              uint16 data_len,
+                                              const unsigned char *data,
+                                              SilcCipher cipher);
+
+/****f* silccore/SilcPrivateAPI/silc_private_message_payload_free
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_private_message_payload_free(SilcPrivateMessagePayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Frees Private Message Payload
+ *
+ ***/
+void silc_private_message_payload_free(SilcPrivateMessagePayload payload);
+
+/****f* silccore/SilcPrivateAPI/silc_private_message_get_flags
+ *
+ * SYNOPSIS
+ *
+ *    uint16 
+ *    silc_private_message_get_flags(SilcPrivateMessagePayload payload);
+ *
+ * DESCRIPTION
+ *
+ *    Returns flags from the payload. Message flags may indicate some
+ *    status of the message. Private message flags are equivalent to the
+ *    channel message flags.
+ *
+ ***/
+uint16 
+silc_private_message_get_flags(SilcPrivateMessagePayload payload);
+
+/****f* silccore/SilcPrivateAPI/silc_private_message_get_message
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *
+ *    silc_private_message_get_nickname(SilcPrivateMessagePayload payload,
+ *                                      uint32 *nickname_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the actual private message. The caller must not free it.
+ *
+ ***/
+unsigned char *
+silc_private_message_get_message(SilcPrivateMessagePayload payload,
+                                uint32 *message_len);
+
+#endif
diff --git a/lib/silccore/silcprotocol.c b/lib/silccore/silcprotocol.c
deleted file mode 100644 (file)
index dfdf032..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-
-  silcprotocol.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * Created: Tue Nov 25 19:25:33 GMT+0200 1997
- */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-#include "silcprotocol.h"
-
-/* Allocates a new protocol object. The new allocated and initialized 
-   protocol is returned to the new_protocol argument. The argument context
-   is the context to be sent as argument for the protocol. The callback
-   argument is the function to be called _after_ the protocol has finished. */
-
-void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
-                        void *context, SilcProtocolFinalCallback callback)
-{
-  int i;
-
-  SILC_LOG_DEBUG(("Allocating new protocol type %d", type));
-
-  for (i = 0; silc_protocol_list[i].callback; i++)
-    if (silc_protocol_list[i].type == type)
-      break;
-
-  if (!silc_protocol_list[i].callback) {
-    SILC_LOG_ERROR(("Requested protocol does not exists"));
-    return;
-  }
-
-  *new_protocol = silc_calloc(1, sizeof(**new_protocol));
-  if (*new_protocol == NULL) {
-    SILC_LOG_ERROR(("Cannot allocate new protocol object"));
-    return;
-  }
-
-  (*new_protocol)->protocol = (SilcProtocolObject *)&silc_protocol_list[i];
-  (*new_protocol)->state = SILC_PROTOCOL_STATE_UNKNOWN;
-  (*new_protocol)->context = context;
-  (*new_protocol)->execute = silc_protocol_execute;
-  (*new_protocol)->execute_final = silc_protocol_execute_final;
-  (*new_protocol)->final_callback = callback;
-}
-
-/* Free's a protocol object. */
-
-void silc_protocol_free(SilcProtocol protocol)
-{
-  if (protocol)
-    silc_free(protocol);
-}
-
-/* Executes next state of the protocol. The state must be set before
-   calling this function. */
-
-void silc_protocol_execute(void *qptr, int type,
-                          void *context, int fd,
-                          long secs, long usecs)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (secs + usecs) 
-    silc_task_register(qptr, fd, protocol->protocol->callback, context, 
-                      secs, usecs, 
-                      SILC_TASK_TIMEOUT,
-                      SILC_TASK_PRI_NORMAL);
-  else
-    protocol->protocol->callback(qptr, 0, context, fd);
-}
-
-/* Executes the final callback of the protocol. */
-
-void silc_protocol_execute_final(void *qptr, int type,
-                                void *context, int fd)
-{
-  SilcProtocol protocol = (SilcProtocol)context;
-  SILC_LOG_DEBUG(("Start, state=%d", protocol->state));
-
-  protocol->final_callback(qptr, 0, context, fd);
-}
diff --git a/lib/silccore/silcprotocol.h b/lib/silccore/silcprotocol.h
deleted file mode 100644 (file)
index f04ffd7..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-
-  silcprotocol.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCPROTOCOL_H
-#define SILCPROTOCOL_H
-
-/* Protocol type definition. */
-typedef unsigned char SilcProtocolType;
-
-/* Protocol state definition. */
-typedef unsigned char SilcProtocolState;
-
-/* Protocol states. Do NOT change the values of these states, especially
-   the START state or you break every protocol. */
-#define SILC_PROTOCOL_STATE_UNKNOWN 0
-#define SILC_PROTOCOL_STATE_START 1
-#define SILC_PROTOCOL_STATE_END 253
-#define SILC_PROTOCOL_STATE_ERROR 254
-
-/* Connection Authentication protocols' authentication methods */
-#define SILC_PROTOCOL_CONN_AUTH_NONE 0
-#define SILC_PROTOCOL_CONN_AUTH_PASSWORD 1
-#define SILC_PROTOCOL_CONN_AUTH_PUBLIC_KEY 2
-
-/* 
-   SILC Protocol Object.
-
-   Short description of the field following:
-   
-   SilcProtocolType type
-
-       Protocol type. This is enumeration.
-  
-   SilcProtocolCallback callback;
-
-       Callback function for the protocol. This is SilcTaskCallback function
-       pointer as the protocols in SILC are executed as timeout tasks.
-
-   The object expands to another structure as well. Short description of 
-   these fields following:
-
-   SilcProtocolObject *protocol
-
-       This is the pointer to the protocol object defined above.
-
-   SilcProtocolState state
-
-       Protocol state. This is enumeration. The state of the protocol can
-       be changed in the callback function.
-
-   void *context
-
-       Context to be sent for the callback function. This is usually 
-       object for either SILC client or server. However, this abstraction 
-       makes it possible that this pointer could be some other object as well. 
-
-   SilcProtocolExecute execute;
-
-       Executes the protocol and its states. The correct state must be set
-       before calling this function. The state is usually set in the protocol
-       specific routines.
-
-   SilcProtocolExecute execute_final;
-
-       Executes the final callback function of the protocol. Read on.
-
-   SilcProtocolFinalCallback final_callback;
-
-       This is a callback function that is called with timeout _after_ the
-       protocol has finished or error occurs. If this is NULL, naturally 
-       nothing will be executed. Protocol should call this function only at 
-       SILC_PROTOCOL_STATE_END and SILC_PROTOCOL_STATE_ERROR states.
-
-*/
-typedef SilcTaskCallback SilcProtocolCallback;
-
-typedef struct {
-  SilcProtocolType type;
-  SilcProtocolCallback callback;
-} SilcProtocolObject;
-
-typedef SilcTaskCallback SilcProtocolFinalCallback;
-typedef SilcTaskCallback SilcProtocolExecute;
-
-typedef struct SilcProtocolObjectStruct {
-  SilcProtocolObject *protocol;
-  SilcProtocolState state;
-  void *context;
-
-  //  SilcProtocolExecute execute;
-  void (*execute)(void *, int, void *, int, long, long);
-  SilcProtocolExecute execute_final;
-  SilcProtocolFinalCallback final_callback;
-} *SilcProtocol;
-
-/* Definition for SILC protocol list. This list includes all the
-   protocols in the SILC. SILC server and client defined own list of
-   protocols. */
-extern const SilcProtocolObject silc_protocol_list[];
-
-/* Prototypes */
-void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
-                        void *context, SilcProtocolFinalCallback callback);
-void silc_protocol_free(SilcProtocol protocol);
-void silc_protocol_execute(void *qptr, int type,
-                          void *context, int fd,
-                          long secs, long usecs);
-void silc_protocol_execute_final(void *qptr, int type, 
-                                void *context, int fd);
-
-#endif
diff --git a/lib/silccore/silcschedule.c b/lib/silccore/silcschedule.c
deleted file mode 100644 (file)
index a23440b..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
-
-  silcschedule.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1998 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* The actual schedule object. */
-static SilcSchedule schedule;
-
-/* Initializes the schedule. Sets the non-timeout task queue hook and
-   the timeout task queue hook. This must be called before the schedule
-   is able to work. */
-
-void silc_schedule_init(SilcTaskQueue fd_queue,
-                       SilcTaskQueue timeout_queue,
-                       SilcTaskQueue generic_queue,
-                       int max_fd)
-{
-  int i;
-
-  SILC_LOG_DEBUG(("Initializing scheduler"));
-
-  /* Initialize the schedule */
-  memset(&schedule, 0, sizeof(schedule));
-  schedule.fd_queue = fd_queue;
-  schedule.timeout_queue = timeout_queue;
-  schedule.generic_queue = generic_queue;
-  schedule.fd_list.fd = silc_calloc(max_fd, sizeof(int));
-  schedule.fd_list.last_fd = 0;
-  schedule.fd_list.max_fd = max_fd;
-  schedule.timeout = NULL;
-  schedule.valid = TRUE;
-  FD_ZERO(&schedule.in);
-  FD_ZERO(&schedule.out);
-  schedule.max_fd = -1;
-  for (i = 0; i < max_fd; i++)
-    schedule.fd_list.fd[i] = -1;
-}
-
-/* Uninitializes the schedule. This is called when the program is ready
-   to end. This removes all tasks and task queues. */
-
-int silc_schedule_uninit()
-{
-
-  SILC_LOG_DEBUG(("Uninitializing scheduler"));
-
-  if (schedule.valid == TRUE)
-    return FALSE;
-
-  /* Unregister all tasks */
-  if (schedule.fd_queue)
-    silc_task_remove(schedule.fd_queue, SILC_ALL_TASKS);
-  if (schedule.timeout_queue)
-    silc_task_remove(schedule.timeout_queue, SILC_ALL_TASKS);
-  if (schedule.generic_queue)
-    silc_task_remove(schedule.generic_queue, SILC_ALL_TASKS);
-
-  /* Unregister all task queues */
-  if (schedule.fd_queue)
-    silc_task_queue_free(schedule.fd_queue);
-  if (schedule.timeout_queue)
-    silc_task_queue_free(schedule.timeout_queue);
-  if (schedule.generic_queue)
-    silc_task_queue_free(schedule.generic_queue);
-
-  /* Clear the fd list */
-  if (schedule.fd_list.fd) {
-    memset(schedule.fd_list.fd, -1, schedule.fd_list.max_fd);
-    silc_free(schedule.fd_list.fd);
-  }
-
-  memset(&schedule, 'F', sizeof(schedule));
-  return TRUE;
-}
-
-/* Stops the schedule even if it is not supposed to be stopped yet. 
-   After calling this, one should call silc_schedule_uninit (after the 
-   silc_schedule has returned). */
-
-void silc_schedule_stop()
-{
-  SILC_LOG_DEBUG(("Stopping scheduler"));
-
-  if (schedule.valid == TRUE)
-    schedule.valid = FALSE;
-}
-
-/* Sets a file descriptor to be listened by select() in scheduler. One can
-   call this directly if wanted. This can be called multiple times for
-   one file descriptor to set different iomasks. */
-
-void silc_schedule_set_listen_fd(int fd, unsigned int iomask)
-{
-  assert(schedule.valid != FALSE);
-  assert(fd < schedule.fd_list.max_fd);
-
-  schedule.fd_list.fd[fd] = iomask;
-  
-  if (fd > schedule.fd_list.last_fd)
-    schedule.fd_list.last_fd = fd;
-}
-
-/* Removes a file descriptor from listen list. */
-
-void silc_schedule_unset_listen_fd(int fd)
-{
-  assert(schedule.valid != FALSE);
-  assert(fd < schedule.fd_list.max_fd);
-
-  schedule.fd_list.fd[fd] = -1;
-  
-  if (fd == schedule.fd_list.last_fd) {
-    int i;
-
-    for (i = fd; i >= 0; i--)
-      if (schedule.fd_list.fd[i] != -1)
-       break;
-
-    schedule.fd_list.last_fd = i;
-  }
-}
-
-/* Executes tasks matching the file descriptor set by select(). The task
-   remains on the task queue after execution. Invalid tasks are removed 
-   here from the task queue. This macro is used by silc_schedule function. 
-   We don't have to care about the tasks priority here because the tasks
-   are sorted in their priority order already at the registration phase. */
-
-#define SILC_SCHEDULE_RUN_TASKS                                                   \
-do {                                                                      \
-  queue = schedule.fd_queue;                                              \
-  if (queue && queue->valid == TRUE && queue->task) {                     \
-    task = queue->task;                                                           \
-                                                                          \
-    /* Walk thorugh all tasks in the particular task queue and            \
-       execute the callback functions of those tasks matching the         \
-       fd set by select(). */                                             \
-    while(1) {                                                            \
-      /* Validity of the task is checked always before and after          \
-        execution beacuse the task might have been unregistered           \
-        in the callback function, ie. it is not valid anymore. */         \
-                                                                          \
-      if (task->valid) {                                                  \
-       /* Task ready for reading */                                       \
-       if ((FD_ISSET(task->fd, &schedule.in)) &&                          \
-           (task->iomask & (1L << SILC_TASK_READ))) {                     \
-         task->callback(queue, SILC_TASK_READ, task->context, task->fd);  \
-          is_run = TRUE;                                                  \
-       }                                                                  \
-      }                                                                   \
-                                                                          \
-      if (task->valid) {                                                  \
-       /* Task ready for writing */                                       \
-       if ((FD_ISSET(task->fd, &schedule.out)) &&                         \
-           (task->iomask & (1L << SILC_TASK_WRITE))) {                    \
-         task->callback(queue, SILC_TASK_WRITE, task->context, task->fd); \
-          is_run = TRUE;                                                  \
-       }                                                                  \
-      }                                                                           \
-                                                                          \
-      if (!task->valid) {                                                 \
-       /* Invalid (unregistered) tasks are removed from the               \
-          task queue. */                                                  \
-       if (queue->task == task->next) {                                   \
-         silc_task_remove(queue, task);                                   \
-          break;                                                          \
-        }                                                                 \
-                                                                          \
-        task = task->next;                                                \
-        silc_task_remove(queue, task->prev);                              \
-        continue;                                                         \
-      }                                                                           \
-                                                                          \
-      /* Break if there isn't more tasks in the queue */                  \
-      if (queue->task == task->next)                                      \
-        break;                                                            \
-                                                                          \
-      task = task->next;                                                  \
-    }                                                                     \
-  }                                                                       \
-} while(0)
-
-/* Selects tasks to be listened by select(). These are the non-timeout
-   tasks. This checks the scheduler's fd list. This macro is used by 
-   silc_schedule function. */
-
-#define SILC_SCHEDULE_SELECT_TASKS                             \
-do {                                                           \
-  for (i = 0; i <= schedule.fd_list.last_fd; i++) {            \
-    if (schedule.fd_list.fd[i] != -1) {                                \
-                                                               \
-      /* Set the max fd value for select() to listen */                \
-      if (i > schedule.max_fd)                                 \
-       schedule.max_fd = i;                                    \
-                                                               \
-      /* Add tasks for reading */                              \
-      if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ)))   \
-       FD_SET(i, &schedule.in);                                \
-                                                               \
-      /* Add tasks for writing */                              \
-      if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE)))  \
-       FD_SET(i, &schedule.out);                               \
-    }                                                          \
-  }                                                             \
-} while(0)
-
-/* Executes all tasks whose timeout has expired. The task is removed from
-   the task queue after the callback function has returned. Also, invalid
-   tasks are removed here. The current time must be get before calling this
-   macro. This macro is used by silc_schedule function. We don't have to
-   care about priorities because tasks are already sorted in their priority
-   order at the registration phase. */
-
-#define SILC_SCHEDULE_RUN_TIMEOUT_TASKS                                        \
-do {                                                                   \
-  queue = schedule.timeout_queue;                                      \
-  if (queue && queue->valid == TRUE && queue->task) {                  \
-    task = queue->task;                                                        \
-                                                                       \
-    /* Walk thorugh all tasks in the particular task queue             \
-       and run all the expired tasks. */                               \
-    while(1) {                                                         \
-      /* Execute the task if the timeout has expired */                        \
-      if (silc_task_timeout_compare(&task->timeout, &curtime)) {       \
-                                                                       \
-        /* Task ready for reading */                                   \
-        if (task->valid) {                                             \
-          if ((task->iomask & (1L << SILC_TASK_READ)))                 \
-           task->callback(queue, SILC_TASK_READ,                       \
-                          task->context, task->fd);                    \
-       }                                                               \
-                                                                       \
-        /* Task ready for writing */                                   \
-        if (task->valid) {                                             \
-          if ((task->iomask & (1L << SILC_TASK_WRITE)))                        \
-            task->callback(queue, SILC_TASK_WRITE,                     \
-                          task->context, task->fd);                    \
-        }                                                              \
-                                                                       \
-        /* Break if there isn't more tasks in the queue */             \
-       if (queue->task == task->next) {                                \
-         /* Remove the task from queue */                              \
-         silc_task_remove(queue, task);                                \
-         break;                                                        \
-        }                                                              \
-                                                                       \
-        task = task->next;                                             \
-                                                                       \
-        /* Remove the task from queue */                               \
-        silc_task_remove(queue, task->prev);                           \
-      } else {                                                         \
-        /* The timeout hasn't expired, check for next one */           \
-                                                                       \
-        /* Break if there isn't more tasks in the queue */             \
-        if (queue->task == task->next)                                 \
-          break;                                                       \
-                                                                       \
-        task = task->next;                                             \
-      }                                                                        \
-    }                                                                  \
-  }                                                                    \
-} while(0)
-
-/* Calculates next timeout for select(). This is the timeout value
-   when at earliest some of the timeout tasks expire. If this is in the
-   past, they will be run now. This macro is used by the silc_schedule
-   function. */
-
-#define SILC_SCHEDULE_SELECT_TIMEOUT                                       \
-do {                                                                       \
-  if (schedule.timeout_queue && schedule.timeout_queue->valid == TRUE) {    \
-    queue = schedule.timeout_queue;                                        \
-    task = NULL;                                                           \
-                                                                           \
-    /* Get the current time */                                             \
-    gettimeofday(&curtime, NULL);                                          \
-    schedule.timeout = NULL;                                               \
-                                                                           \
-    /* First task in the task queue has always the smallest timeout. */            \
-    task = queue->task;                                                            \
-    while(1) {                                                             \
-      if (task && task->valid == TRUE) {                                   \
-                                                                           \
-       /* If the timeout is in past, we will run the task and all other    \
-          timeout tasks from the past. */                                  \
-       if (silc_task_timeout_compare(&task->timeout, &curtime)) {          \
-         SILC_SCHEDULE_RUN_TIMEOUT_TASKS;                                  \
-                                                                           \
-         /* The task(s) has expired and doesn't exist on the task queue    \
-            anymore. We continue with new timeout. */                      \
-          queue = schedule.timeout_queue;                                  \
-          task = queue->task;                                              \
-          if (task == NULL || task->valid == FALSE)                        \
-            break;                                                         \
-         goto cont;                                                        \
-        } else {                                                           \
- cont:                                                                     \
-          /* Calculate the next timeout for select() */                            \
-          queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;    \
-          queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec; \
-                                                                           \
-          /* We wouldn't want to go under zero, check for it. */           \
-          if (queue->timeout.tv_usec < 0) {                                \
-            queue->timeout.tv_sec -= 1;                                            \
-            queue->timeout.tv_usec += 1000000L;                                    \
-          }                                                                \
-        }                                                                  \
-        /* We've got the timeout value */                                  \
-       break;                                                              \
-      }        else {                                                              \
-        /* Task is not valid, remove it and try next one. */               \
-       silc_task_remove(queue, task);                                      \
-        task = queue->task;                                                \
-        if (queue->task == NULL)                                           \
-          break;                                                           \
-      }                                                                            \
-    }                                                                      \
-    /* Save the timeout */                                                 \
-    if (task)                                                              \
-      schedule.timeout = &queue->timeout;                                  \
-  }                                                                        \
-} while(0)
-
-/* Execute generic tasks. These are executed only and only if for the
-   specific fd there wasn't other non-timeout tasks. This checks the earlier
-   set fd list, thus the generic tasks apply to all specified fd's. All the
-   generic tasks are executed at once. */
-
-#define SILC_SCHEDULE_RUN_GENERIC_TASKS                                             \
-do {                                                                        \
-  if (is_run == FALSE) {                                                    \
-    SILC_LOG_DEBUG(("Running generic tasks"));                              \
-    for (i = 0; i <= schedule.fd_list.last_fd; i++)                         \
-      if (schedule.fd_list.fd[i] != -1) {                                   \
-                                                                            \
-       /* Check whether this fd is select()ed. */                           \
-       if ((FD_ISSET(i, &schedule.in)) || (FD_ISSET(i, &schedule.out))) {   \
-                                                                            \
-         /* It was selected. Now find the tasks from task queue and execute \
-            all generic tasks. */                                           \
-         if (schedule.generic_queue && schedule.generic_queue->valid) {     \
-           queue = schedule.generic_queue;                                  \
-                                                                            \
-           if (!queue->task)                                                \
-             break;                                                         \
-                                                                            \
-           task = queue->task;                                              \
-                                                                            \
-           while(1) {                                                       \
-             /* Validity of the task is checked always before and after     \
-                execution beacuse the task might have been unregistered     \
-                in the callback function, ie. it is not valid anymore. */   \
-                                                                            \
-             if (task->valid && schedule.fd_list.fd[i] != -1) {             \
-               /* Task ready for reading */                                 \
-               if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_READ)))       \
-                 task->callback(queue, SILC_TASK_READ,                      \
-                                task->context, i);                          \
-             }                                                              \
-                                                                            \
-             if (task->valid && schedule.fd_list.fd[i] != -1) {             \
-               /* Task ready for writing */                                 \
-               if ((schedule.fd_list.fd[i] & (1L << SILC_TASK_WRITE)))      \
-                 task->callback(queue, SILC_TASK_WRITE,                     \
-                                task->context, i);                          \
-             }                                                              \
-                                                                            \
-             if (!task->valid) {                                            \
-               /* Invalid (unregistered) tasks are removed from the         \
-                  task queue. */                                            \
-               if (queue->task == task->next) {                             \
-                 silc_task_remove(queue, task);                             \
-                 break;                                                     \
-               }                                                            \
-                                                                            \
-               task = task->next;                                           \
-               silc_task_remove(queue, task->prev);                         \
-               continue;                                                    \
-             }                                                              \
-                                                                            \
-             /* Break if there isn't more tasks in the queue */             \
-             if (queue->task == task->next)                                 \
-               break;                                                       \
-                                                                            \
-             task = task->next;                                             \
-           }                                                                \
-         }                                                                  \
-       }                                                                    \
-      }                                                                             \
-  }                                                                         \
-} while(0)
-
-/* The SILC scheduler. This is actually the main routine in SILC programs.
-   When this returns the program is to be ended. Before this function can
-   be called, one must call silc_schedule_init function. */
-
-void silc_schedule()
-{
-  int is_run, i;
-  SilcTask task;
-  SilcTaskQueue queue;
-  struct timeval curtime;
-
-  SILC_LOG_DEBUG(("Running scheduler"));
-
-  if (schedule.valid == FALSE) {
-    SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
-    return;
-  }
-
-  /* Start the scheduler loop */
-  while(1) {
-
-    SILC_LOG_DEBUG(("In scheduler loop"));
-
-    /* If the task queues aren't initialized or we aren't valid anymore
-       we will return */
-    if ((!schedule.fd_queue && !schedule.timeout_queue 
-        && !schedule.generic_queue) || schedule.valid == FALSE) {
-      SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
-      break;
-    }
-
-    /* Clear everything */
-    FD_ZERO(&schedule.in);
-    FD_ZERO(&schedule.out);
-    schedule.max_fd = -1;
-    is_run = FALSE;
-
-    /* Calculate next timeout for select(). This is the timeout value
-       when at earliest some of the timeout tasks expire. */
-    SILC_SCHEDULE_SELECT_TIMEOUT;
-
-    /* Add the file descriptors to the fd sets. These are the non-timeout
-       tasks. The select() listens to these file descriptors. */
-    SILC_SCHEDULE_SELECT_TASKS;
-
-    if (schedule.max_fd == -1) {
-      SILC_LOG_ERROR(("Nothing to listen, exiting"));
-      break;
-    }
-
-    if (schedule.timeout)
-      SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule.timeout->tv_sec,
-                     schedule.timeout->tv_usec));
-
-    /* This is the main select(). The program blocks here until some
-       of the selected file descriptors change status or the selected
-       timeout expires. */
-    SILC_LOG_DEBUG(("Select"));
-    switch(select(schedule.max_fd + 1, &schedule.in, 
-                 &schedule.out, 0, schedule.timeout)) {
-    case -1:
-      /* Error */
-      SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
-      break;
-    case 0:
-      /* Timeout */
-      SILC_LOG_DEBUG(("Running timeout tasks"));
-      gettimeofday(&curtime, NULL);
-      SILC_SCHEDULE_RUN_TIMEOUT_TASKS;
-      break;
-    default:
-      /* There is some data available now */
-      SILC_LOG_DEBUG(("Running non-timeout tasks"));
-      SILC_SCHEDULE_RUN_TASKS;
-
-      SILC_SCHEDULE_RUN_GENERIC_TASKS;
-      break;
-    }
-  }
-}
diff --git a/lib/silccore/silcschedule.h b/lib/silccore/silcschedule.h
deleted file mode 100644 (file)
index 87af2d1..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
-
-  silcschedule.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1998 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCSCHEDULE_H
-#define SILCSCHEDULE_H
-
-/* Structure holding list of file descriptors, scheduler is supposed to
-   be listenning. The max_fd field is the maximum number of possible file
-   descriptors in the list. This value is set at the initialization
-   of the scheduler and it usually is the maximum number of connections 
-   allowed. */
-typedef struct {
-  int *fd;
-  unsigned int last_fd;
-  unsigned int max_fd;
-} SilcScheduleFdList;
-
-/* 
-   Silc Schedule object. 
-
-   This is the actual schedule object in Silc. Both Silc client and server 
-   uses this same scheduler. Actually, this scheduler could be used by any
-   program needing scheduling.
-
-   Following short description of the fields:
-
-   SilcTaskQueue fd_queue
-
-       Task queue hook for non-timeout tasks. Usually this means that these
-       tasks perform different kind of I/O on file descriptors. File 
-       descriptors are usually network sockets but they actually can be
-       any file descriptors. This hook is initialized in silc_schedule_init
-       function. Timeout tasks should not be added to this queue because
-       they will never expire.
-
-   SilcTaskQueue timeout_queue
-
-       Task queue hook for timeout tasks. This hook is reserved specificly
-       for tasks with timeout. Non-timeout tasks should not be added to this
-       queue because they will never get scheduled. This hook is also
-       initialized in silc_schedule_init function.
-
-   SilcTaskQueue generic_queue
-
-       Task queue hook for generic tasks. This hook is reserved specificly
-       for generic tasks, tasks that apply to all file descriptors, except
-       to those that have specificly registered a non-timeout task. This hook
-       is also initialized in silc_schedule_init function.
-
-   SilcScheduleFdList fd_list
-
-       List of file descriptors the scheduler is supposed to be listenning.
-       This is updated internally.
-
-   struct timeval *timeout;
-
-       Pointer to the schedules next timeout. Value of this timeout is
-       automatically updated in the silc_schedule function.
-
-   int valid
-
-       Marks validity of the scheduler. This is a boolean value. When this
-       is false the scheduler is terminated and the program will end. This
-       set to true when the scheduler is initialized with silc_schedule_init
-       function.
-
-   fd_set in
-   fd_set out
-
-       File descriptor sets for select(). These are automatically managed
-       by the scheduler and should not be touched otherwise.
-
-   int max_fd
-
-       Number of maximum file descriptors for select(). This, as well, is
-       managed automatically by the scheduler and should be considered to 
-       be read-only field otherwise.
-
-*/
-
-typedef struct {
-  SilcTaskQueue fd_queue;
-  SilcTaskQueue timeout_queue;
-  SilcTaskQueue generic_queue;
-  SilcScheduleFdList fd_list;
-  struct timeval *timeout;
-  int valid;
-  fd_set in;
-  fd_set out;
-  int max_fd;
-} SilcScheduleObject;
-
-typedef SilcScheduleObject SilcSchedule;
-
-/* Prototypes */
-void silc_schedule_init(SilcTaskQueue fd_queue,
-                       SilcTaskQueue timeout_queue,
-                       SilcTaskQueue generic_queue,
-                       int max_fd);
-int silc_schedule_uninit();
-void silc_schedule_stop();
-void silc_schedule_set_listen_fd(int fd, unsigned int iomask);
-void silc_schedule_unset_listen_fd(int fd);
-void silc_schedule();
-
-#endif
diff --git a/lib/silccore/silcsockconn.c b/lib/silccore/silcsockconn.c
deleted file mode 100644 (file)
index 7025205..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-
-  silcsockconn.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* Allocates a new socket connection object. The allocated object is 
-   returned to the new_socket argument. */
-
-void silc_socket_alloc(int sock, SilcSocketType type, void *user_data, 
-                      SilcSocketConnection *new_socket)
-{
-  SILC_LOG_DEBUG(("Allocating new socket connection object"));
-
-  *new_socket = silc_calloc(1, sizeof(**new_socket));
-  if (*new_socket == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new socket connection object"));
-    return;
-  }
-
-  /* Set the pointers. Incoming and outgoing data buffers
-     are allocated by the server when they are first used. */
-  (*new_socket)->sock = sock;
-  (*new_socket)->type = type;
-  (*new_socket)->user_data = user_data;
-  (*new_socket)->protocol = NULL;
-  (*new_socket)->flags = 0;
-  (*new_socket)->inbuf = NULL;
-  (*new_socket)->outbuf = NULL;
-}
-
-/* Free's the Socket connection object. */
-
-void silc_socket_free(SilcSocketConnection sock)
-{
-  if (sock) {
-    //    silc_protocol_free(sock->protocol);
-    silc_buffer_free(sock->inbuf);
-    silc_buffer_free(sock->outbuf);
-    silc_free(sock);
-  }
-}
diff --git a/lib/silccore/silcsockconn.h b/lib/silccore/silcsockconn.h
deleted file mode 100644 (file)
index 5706b68..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
-
-  silcsockconn.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCSOCKCONN_H
-#define SILCSOCKCONN_H
-
-/* Socket types. These identifies the socket connection. */
-typedef enum {
-  SILC_SOCKET_TYPE_UNKNOWN = 0,
-  SILC_SOCKET_TYPE_CLIENT = 1,
-  SILC_SOCKET_TYPE_SERVER = 2,
-  SILC_SOCKET_TYPE_ROUTER = 3
-} SilcSocketType;
-
-/* Socket flags */
-#define SILC_SF_NONE 0
-#define SILC_SF_INBUF_PENDING 1
-#define SILC_SF_OUTBUF_PENDING 2
-#define SILC_SF_DISCONNECTING 3
-#define SILC_SF_DISCONNECTED 4
-
-/* 
-   SILC Socket Connection object.
-
-   This object holds information about the connected sockets to the server.
-   This is quite important object since this is referenced by the server all
-   the time when figuring out what the connection is supposed to be doing
-   and to whom we should send a message.
-
-   Following short description of the fields:
-
-   int sock
-
-       The actual connected socket. This is usually saved when accepting
-       new connection to the server.
-
-   SilcSocketType type
-
-       Type of the socket. This identifies the type of the connection. This
-       is mainly used to identify whether the connection is a client or a
-       server connection.
-
-   void *user_data
-
-       This is a pointer to a data that is is saved here at the same
-       time a new connection object is allocated. Usually this is a 
-       back-pointer to some important data for fast referencing. For
-       SILC server this is a pointer to the ID list and for SILC client
-       to object holding active connections (windows).
-
-   SilcProtocol protocol
-
-       Protocol object for the socket. Currently only one protocol can be
-       executing at a time for a particular socket.
-
-   unsigned int flags
-
-       Socket flags that indicate the status of the socket. This can
-       indicate several different status that can affect the use of the
-       socket object.
-
-   SilcBuffer inbuf
-   SilcBuffer outbuf
-
-       Incoming and outgoing buffers for the particular socket connection.
-       Incoming data from the socket is put after decryption in to the
-       inbuf buffer and outgoing data after encryption is put to the outbuf
-       buffer.
-
-*/
-typedef struct {
-  int sock;
-  SilcSocketType type;
-  void *user_data;
-  SilcProtocol protocol;
-  unsigned int flags;
-
-  char *hostname;
-  char *ip;
-  unsigned short port;
-
-  SilcBuffer inbuf;
-  SilcBuffer outbuf;
-} SilcSocketConnectionObject;
-
-typedef SilcSocketConnectionObject *SilcSocketConnection;
-
-/* Macros */
-
-/* Generic manipulation of flags */
-#define SF_SET(x, f) (x)->flags |= (1L << (f))
-#define SF_UNSET(x, f) (x)->flags &= ~(1L << (f))
-#define SF_IS(x, f) (x)->flags & (1L << (f))
-
-/* Setting/Unsetting flags */
-#define SILC_SET_OUTBUF_PENDING(x) SF_SET((x), SILC_SF_OUTBUF_PENDING)
-#define SILC_SET_INBUF_PENDING(x) SF_SET((x), SILC_SF_INBUF_PENDING)
-#define SILC_SET_DISCONNECTING(x) SF_SET((x), SILC_SF_DISCONNECTING)
-#define SILC_SET_DISCONNECTED(x) SF_SET((x), SILC_SF_DISCONNECTED)
-#define SILC_UNSET_OUTBUF_PENDING(x) SF_UNSET((x), SILC_SF_OUTBUF_PENDING)
-#define SILC_UNSET_INBUF_PENDING(x) SF_UNSET((x), SILC_SF_INBUF_PENDING)
-#define SILC_UNSET_DISCONNECTING(x) SF_UNSET((x), SILC_SF_DISCONNECTING)
-#define SILC_UNSET_DISCONNECTED(x) SF_UNSET((x), SILC_SF_DISCONNECTED)
-
-/* Checking for flags */
-#define SILC_IS_OUTBUF_PENDING(x) SF_IS((x), SILC_SF_OUTBUF_PENDING)
-#define SILC_IS_INBUF_PENDING(x) SF_IS((x), SILC_SF_INBUF_PENDING)
-#define SILC_IS_DISCONNECTING(x) SF_IS((x), SILC_SF_DISCONNECTING)
-#define SILC_IS_DISCONNECTED(x) SF_IS((x), SILC_SF_DISCONNECTED)
-
-/* Prototypes */
-void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
-                      SilcSocketConnection *new_socket);
-void silc_socket_free(SilcSocketConnection sock);
-
-#endif
diff --git a/lib/silccore/silctask.c b/lib/silccore/silctask.c
deleted file mode 100644 (file)
index f9e09eb..0000000
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
-
-  silctask.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1998 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* Allocates a new task queue into the Silc. If 'valid' is TRUE the
-   queue becomes valid task queue. If it is FALSE scheduler will skip
-   the queue. */
-
-void silc_task_queue_alloc(SilcTaskQueue *new, int valid)
-{
-
-  SILC_LOG_DEBUG(("Allocating new task queue"));
-
-  *new = silc_calloc(1, sizeof(**new));
-  if (*new == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new task queue object: %s", 
-                   strerror(errno)));
-    return;
-  }
-
-  /* Set the pointers */
-  (*new)->valid = valid;
-  (*new)->task = NULL;
-  (*new)->register_task = silc_task_register;
-  (*new)->unregister_task = silc_task_unregister;
-  (*new)->set_iotype = silc_task_set_iotype;
-  (*new)->reset_iotype = silc_task_reset_iotype;
-}
-
-/* Free's a task queue. */
-
-void silc_task_queue_free(SilcTaskQueue old)
-{
-  if (old)
-    silc_free(old);
-}
-
-/* Adds a non-timeout task into the task queue. This function is used
-   by silc_task_register function. Returns a pointer to the registered 
-   task. */
-
-SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new, 
-                      SilcTaskPriority priority)
-{
-  SilcTask task, next, prev;
-
-  /* Take the first task in the queue */
-  task = queue->task;
-
-  switch(priority) {
-  case SILC_TASK_PRI_LOW:
-    /* Lowest priority. The task is added at the end of the list. */
-    prev = task->prev;
-    new->prev = prev;
-    new->next = task;
-    prev->next = new;
-    task->prev = new;
-    break;
-  case SILC_TASK_PRI_NORMAL:
-    /* Normal priority. The task is added before lower priority tasks
-       but after tasks with higher priority. */
-    prev = task->prev;
-    while(prev != task) {
-      if (prev->priority > SILC_TASK_PRI_LOW &&
-         prev->priority <= SILC_TASK_PRI_REALTIME)
-       break;
-      prev = prev->prev;
-    }
-    if (prev == task) {
-      /* There are only lower priorities in the list, we will
-        sit before them and become the first task in the queue. */
-      prev = task->prev;
-      new->prev = prev;
-      new->next = task;
-      task->prev = new;
-      prev->next = new;
-
-      /* We are now the first task in queue */
-      queue->task = new;
-    } else {
-      /* Found a spot from the list, add the task to the list. */
-      next = prev->next;
-      new->prev = prev;
-      new->next = next;
-      prev->next = new;
-      next->prev = new;
-    }
-    break;
-  case SILC_TASK_PRI_HIGH:
-    /* High priority. The task is added before lower priority tasks
-       but after tasks with higher priority. */
-    prev = task->prev;
-    while(prev != task) {
-      if (prev->priority > SILC_TASK_PRI_NORMAL &&
-         prev->priority <= SILC_TASK_PRI_REALTIME)
-       break;
-      prev = prev->prev;
-    }
-    if (prev == task) {
-      /* There are only lower priorities in the list, we will
-        sit before them and become the first task in the queue. */
-      prev = task->prev;
-      new->prev = prev;
-      new->next = task;
-      task->prev = new;
-      prev->next = new;
-
-      /* We are now the first task in queue */
-      queue->task = new;
-    } else {
-      /* Found a spot from the list, add the task to the list. */
-      next = prev->next;
-      new->prev = prev;
-      new->next = next;
-      prev->next = new;
-      next->prev = new;
-    }
-    break;
-  case SILC_TASK_PRI_REALTIME:
-    /* Highest priority. The task is added at the head of the list. 
-       The last registered task is added to the very head of the list
-       thus we get the LIFO (Last-In-First-Out) order. */
-    prev = task->prev;
-    new->prev = prev;
-    new->next = task;
-    prev->next = new;
-    task->prev = new;
-
-    /* We are the first task in the queue */
-    queue->task = new;
-    break;
-  default:
-    silc_free(new);
-    return NULL;
-  }
-
-  return new;
-}
-
-/* Adds a timeout task into the task queue. This function is used by
-   silc_task_register function. Returns a pointer to the registered 
-   task. Timeout tasks are sorted by their timeout value in ascending
-   order. The priority matters if there are more than one task with
-   same timeout. */
-
-SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
-                              SilcTaskPriority priority)
-{
-  SilcTask task, prev, next;
-
-  /* Take the first task in the queue */
-  task = queue->task;
-
-  /* Take last task from the list */
-  prev = task->prev;
-    
-  switch(priority) {
-  case SILC_TASK_PRI_LOW:
-    /* Lowest priority. The task is added at the end of the list. */
-    while(prev != task) {
-
-      /* If we have longer timeout than with the task head of us
-        we have found our spot. */
-      if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
-       break;
-
-      /* If we are equal size of timeout we will be after it. */
-      if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
-       break;
-
-      /* We have shorter timeout, compare to next one. */
-      prev = prev->prev;
-    }
-    /* Found a spot from the list, add the task to the list. */
-    next = prev->next;
-    new->prev = prev;
-    new->next = next;
-    prev->next = new;
-    next->prev = new;
-    
-    if (prev == task) {
-      /* Check if we are going to be the first task in the queue */
-      if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
-       break;
-      if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
-       break;
-
-      /* We are now the first task in queue */
-      queue->task = new;
-    }
-    break;
-  case SILC_TASK_PRI_NORMAL:
-    /* Normal priority. The task is added before lower priority tasks
-       but after tasks with higher priority. */
-    while(prev != task) {
-
-      /* If we have longer timeout than with the task head of us
-        we have found our spot. */
-      if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
-       break;
-
-      /* If we are equal size of timeout, priority kicks in place. */
-      if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
-       if (prev->priority >= SILC_TASK_PRI_NORMAL)
-         break;
-
-      /* We have shorter timeout or higher priority, compare to next one. */
-      prev = prev->prev;
-    }
-    /* Found a spot from the list, add the task to the list. */
-    next = prev->next;
-    new->prev = prev;
-    new->next = next;
-    prev->next = new;
-    next->prev = new;
-    
-    if (prev == task) {
-      /* Check if we are going to be the first task in the queue */
-      if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
-       break;
-      if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
-       if (prev->priority >= SILC_TASK_PRI_NORMAL)
-         break;
-
-      /* We are now the first task in queue */
-      queue->task = new;
-    }
-    break;
-  case SILC_TASK_PRI_HIGH:
-    /* High priority. The task is added before lower priority tasks
-       but after tasks with higher priority. */
-    while(prev != task) {
-
-      /* If we have longer timeout than with the task head of us
-        we have found our spot. */
-      if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
-       break;
-
-      /* If we are equal size of timeout, priority kicks in place. */
-      if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
-       if (prev->priority >= SILC_TASK_PRI_HIGH)
-         break;
-
-      /* We have shorter timeout or higher priority, compare to next one. */
-      prev = prev->prev;
-    }
-    /* Found a spot from the list, add the task to the list. */
-    next = prev->next;
-    new->prev = prev;
-    new->next = next;
-    prev->next = new;
-    next->prev = new;
-    
-    if (prev == task) {
-      /* Check if we are going to be the first task in the queue */
-      if (silc_task_timeout_compare(&prev->timeout, &new->timeout))
-       break;
-      if (!silc_task_timeout_compare(&new->timeout, &prev->timeout))
-       if (prev->priority >= SILC_TASK_PRI_HIGH)
-         break;
-
-      /* We are now the first task in queue */
-      queue->task = new;
-    }
-    break;
-  case SILC_TASK_PRI_REALTIME:
-    /* Highest priority. The task is added at the head of the list. 
-       The last registered task is added to the very head of the list
-       thus we get the LIFO (Last-In-First-Out) order. */
-    next = task->next;
-    while(next != task) {
-
-      /* If we have shorter timeout than the next task we've found
-        our spot. */
-      if (silc_task_timeout_compare(&new->timeout, &next->timeout))
-       break;
-
-      /* If we are equal size of timeout we will be first. */
-      if (!silc_task_timeout_compare(&next->timeout, &new->timeout))
-       break;
-
-      /* We have longer timeout, compare to next one. */
-      next = next->next;
-    }
-    /* Found a spot from the list, add the task to the list. */
-    prev = next->prev;
-    new->next = next;
-    new->prev = prev;
-    prev->next = new;
-    next->prev = new;
-    
-    if (next == task) {
-      /* Check if we are going to be the first task in the queue */
-      if (silc_task_timeout_compare(&next->timeout, &new->timeout))
-       break;
-
-      /* We are now the first task in queue */
-      queue->task = new;
-    }
-  default:
-    silc_free(new);
-    return NULL;
-  }
-
-  return new;
-}
-
-/* Registers a new task into the task queue. The task becomes valid
-   automatically when it is registered. Returns a pointer to the 
-   registered task. */
-
-SilcTask silc_task_register(SilcTaskQueue queue, int fd, 
-                           SilcTaskCallback cb, void *context, 
-                           long seconds, long useconds, 
-                           SilcTaskType type, SilcTaskPriority priority)
-{
-  SilcTask new;
-  int timeout = 0;
-
-  SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", 
-                 fd, type, priority));
-
-  /* If the task is generic task, we check whether this task has already
-     been registered. Generic tasks are registered only once and after that
-     the same task applies to all file descriptors to be registered. */
-  if ((type == SILC_TASK_GENERIC) && queue->task) {
-    SilcTask task;
-
-    task = queue->task;
-    while(1) {
-      if ((task->callback == cb) && (task->context == context)) {
-       SILC_LOG_DEBUG(("Found matching generic task, using the match"));
-
-       /* Add the fd to be listened, the task found now applies to this
-          fd as well. */
-       silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
-       return task;
-      }
-
-      if (queue->task == task->next)
-       break;
-      
-      task = task->next;
-    }
-  }
-
-  new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new task object"));
-    return NULL;
-  }
-
-  new->fd = fd;
-  new->context = context;
-  new->callback = cb;
-  new->valid = TRUE;
-  new->priority = priority;
-  new->iomask = (1L << SILC_TASK_READ);
-  new->next = new;
-  new->prev = new;
-
-  /* If the task is non-timeout task we have to tell the scheduler that we
-     would like to have these tasks scheduled at some odd distant future. */
-  if (type != SILC_TASK_TIMEOUT)
-    silc_schedule_set_listen_fd(fd, (1L << SILC_TASK_READ));
-
-  /* Create timeout if marked to be timeout task */
-  if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
-    gettimeofday(&new->timeout, NULL);
-    new->timeout.tv_sec += seconds + (useconds / 1000000L);
-    new->timeout.tv_usec += (useconds % 1000000L);
-    if (new->timeout.tv_usec > 999999L) {
-      new->timeout.tv_sec += 1;
-      new->timeout.tv_usec -= 1000000L;
-    }
-    timeout = 1;
-  }
-
-  /* Is this first task of the queue? */
-  if (queue->task == NULL) {
-    queue->task = new;
-    return new;
-  }
-
-  if (timeout)
-    return silc_task_add_timeout(queue, new, priority);
-  else
-    return silc_task_add(queue, new, priority);
-}
-
-/* Removes (unregisters) a task from particular task queue. This function
-   is used internally by scheduler. One should not call this function
-   to unregister tasks, instead silc_task_unregister_task function
-   should be used. */
-
-int silc_task_remove(SilcTaskQueue queue, SilcTask task)
-{
-  SilcTask first, old, next;
-
-  if (!queue || !queue->task)
-    return FALSE;
-
-  first = queue->task;
-
-  /* Unregister all tasks in queue */
-  if (task == SILC_ALL_TASKS) {
-    SILC_LOG_DEBUG(("Removing all tasks at once"));
-    next = first;
-
-    while(1) {
-      next = next->next;
-      silc_free(next->prev);
-      if (next == first)
-       break;
-    }
-
-    queue->task = NULL;
-    return TRUE;
-  }
-
-  SILC_LOG_DEBUG(("Removing task"));
-
-  /* Unregister the task */
-  old = first;
-  while(1) {
-    if (old == task) {
-      SilcTask prev, next;
-
-      prev = old->prev;
-      next = old->next;
-      prev->next = next;
-      next->prev = prev;
-
-      if (prev == old && next == old)
-       queue->task = NULL;
-      if (queue->task == old)
-       queue->task = next;
-
-      silc_free(old);
-      return TRUE;
-    }
-    old = old->next;
-
-    if (old == first)
-      return FALSE;
-  }
-}
-
-/* Unregisters a task from the task queue. This is the unregister_task
-   function pointer in task queue object. One should use this function
-   to unregister tasks. This function invalidates the task. */
-
-void silc_task_unregister(SilcTaskQueue queue, SilcTask task)
-{
-
-  /* Unregister all tasks */
-  if (task == SILC_ALL_TASKS) {
-    SilcTask next;
-    SILC_LOG_DEBUG(("Unregistering all tasks at once"));
-
-    if (queue->task == NULL)
-      return;
-
-    next = queue->task;
-    
-    while(1) {
-      if (next->valid)
-       next->valid = FALSE;
-      if (queue->task == next->next)
-       break;
-      next = next->next;
-    }
-    return;
-  }
-
-  SILC_LOG_DEBUG(("Unregistering task"));
-
-  /* Unregister the specific task */
-  if (task->valid)
-    task->valid = FALSE;
-}
-
-/* Unregister a task by file descriptor. This invalidates the task. */
-
-void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd)
-{
-  SilcTask next;
-
-  SILC_LOG_DEBUG(("Unregister task by fd"));
-
-  if (queue->task == NULL)
-    return;
-
-  next = queue->task;
-
-  while(1) {
-    if (next->fd == fd)
-      next->valid = FALSE;
-    if (queue->task == next->next)
-      break;
-    next = next->next;
-  }
-}
-
-/* Sets the I/O mask for the task. Only one I/O type can be set at a
-   time. */
-
-void silc_task_set_iotype(SilcTask task, int type)
-{
-  task->iomask |= (1L << type);
-}
-
-/* Resets the I/O mask to the type sent as argument. */
-
-void silc_task_reset_iotype(SilcTask task, int type)
-{
-  task->iomask = (1L << type);
-}
-
-/* Compare two time values. If the first argument is smaller than the
-   second this function returns TRUE. */
-
-int silc_task_timeout_compare(struct timeval *smaller, 
-                             struct timeval *bigger)
-{
-  if ((smaller->tv_sec < bigger->tv_sec) ||
-      ((smaller->tv_sec == bigger->tv_sec) &&
-       (smaller->tv_usec < bigger->tv_usec)))
-    return TRUE;
-
-  return FALSE;
-}
diff --git a/lib/silccore/silctask.h b/lib/silccore/silctask.h
deleted file mode 100644 (file)
index e07e6e9..0000000
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
-
-  silctask.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1998 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCTASK_H
-#define SILCTASK_H
-
-/* 
-   SILC Task object. 
-
-   int fd
-
-       File descriptor. This usually is a network socket but can be
-       any file descriptor. On generic tasks, that applies to all file
-       descriptors, this is set to -1.
-
-   struct timeval timeout
-  
-       The timeout when this task is supposed to be run. This is defined
-       only if the task is a timeout task.
-
-   void *context;
-
-       Context structure passed to callback function as argument.
-
-   SilcTaskCallback callback
-
-       The callback function Silc scheduler calls when this task is scheduled
-       to be run. First argument is the task queue this task belongs to. This
-       is a void pointer and the task queue has to casted out of it. Second
-       argument is the type of the event just occured inside the scheduler 
-       (SILC_TASK_READ or SILC_TASK_WRITE). Third argument is the context 
-       structure of the task. Last argument is the file descriptor of the
-       task.
-
-   int valid
-
-       Marks for validity of the task. Task that is not valid scheduler 
-       will skip. This is boolean value.
-
-   int priority
-
-       Priority of the task. This field is used internally only and it
-       should not be touched otherwise.
-
-   int iomask
-
-       I/O mask which tells to the scheduler for what kind of I/O this 
-       task is ready. If the task is ready for both reading and writing
-       SILC_TASK_READ and SILC_TASK_WRITE are masked into this variable.
-       Masking is done by OR'ing (1 << SILC_TASK_*) values. One can check
-       the mask by AND'ing (1L << SILC_TASK_*) against the mask. At the
-       registering of a new task this mask is set to SILC_TASK_READ by
-       default. If a task doesn't perform reading this value must be
-       reset to SILC_TASK_WRITE. If it performs both reading and writing
-       SILC_TASK_WRITE must be added to the mask. A task must always be 
-       ready for at least for one I/O type.
-
-   struct SilcTaskStruct *next
-   struct SilcTaskStruct *prev
-
-       Next and previous task. If the task is first in the list, prev is
-       set to the last task in the list. If the task is last in the list, 
-       next is set to the first task in the list (forms a circular list).
-
-*/
-
-typedef void (*SilcTaskCallback)(void *, int, void *, int);
-
-typedef struct SilcTaskStruct {
-  int fd;
-  struct timeval timeout;
-  void *context;
-  SilcTaskCallback callback;
-  int valid;
-  int priority;
-  int iomask;
-
-  struct SilcTaskStruct *next;
-  struct SilcTaskStruct *prev;
-} SilcTaskObject;
-
-typedef SilcTaskObject *SilcTask;
-
-/* 
-   SILC Task types.
-
-   SILC has three types of tasks, non-timeout tasks (tasks that perform
-   over file descriptors), timeout tasks and generic tasks (tasks that apply
-   to every file descriptor). This type is sent as argument for the 
-   task registering function.
-
-*/
-typedef enum {
-  SILC_TASK_FD,
-  SILC_TASK_TIMEOUT,
-  SILC_TASK_GENERIC,
-} SilcTaskType;
-
-/* 
-   SILC Task priorities.
-
-   Following description of the priorities uses timeout tasks as example
-   how the priority behaves. However, non-timeout tasks behaves same as
-   timeout tasks with following priorities.
-
-   SILC_TASK_PRI_LOW
-
-       Lowest priority. The task is scheduled to run after its timeout
-       has expired only and only when every other task with higher priority 
-       has already been run. For non-timeout tasks this priority behaves
-       same way. Life is not fair for tasks with this priority.
-
-   SILC_TASK_PRI_NORMAL
-
-       Normal priority that is used mostly in Silc. This is priority that
-       should always be used unless you specificly need some other priority.
-       The scheduler will run this task as soon as its timeout has expired.
-       For non-timeout tasks this priority behaves same way. Tasks are run 
-       in FIFO (First-In-First-Out) order.
-
-   SILC_TASK_PRI_HIGH
-   
-       High priority for important tasks. This priority should be used only
-       for important tasks. Life is very fair for tasks with this priority.
-       These tasks are run as soon as its timeout has expired. They are run 
-       before normal or lower tasks, respectively. For non-timeout tasks
-       this priority behaves same way. Tasks are run in FIFO order.
-
-   SILC_TASK_PRI_REALTIME
-
-       Highest priority. This priority should be used very carefully because
-       it can make the scheduler extremely unfair to other tasks. The task
-       will be run as soon as its timeout has expired. The task is run before
-       any other task. It is also quaranteed that the last registered task
-       with this priority is the first task to be run when its timeout
-       expires. Tasks are run in LIFO (Last-In-First-Out) order. To make
-       scheduler fair there should never be more than one task in the queue
-       with this priority. Currently none of the tasks in SILC are important
-       enough to use this priority. For non-timeout tasks this priority
-       behaves same way.
-
-*/
-typedef enum {
-  SILC_TASK_PRI_LOW,
-  SILC_TASK_PRI_NORMAL,
-  SILC_TASK_PRI_HIGH,
-  SILC_TASK_PRI_REALTIME,
-} SilcTaskPriority;
-
-/* 
-   SILC Task Queue object. 
-   
-   Usually there are three task queues in SILC. Tasks with timeouts
-   has their own queue, tasks without timeout has one as well and generic
-   tasks has their own also. Scheduler has timeout queue hooks and 
-   non-timeout queue hooks in the scheduler and it does not check for 
-   timeouts in non-timeout hooks and vice versa, respectively. Ie. Register 
-   timeout queues to their own SilcTaskQueue pointer and non-timeout queues 
-   to their own pointer. 
-
-   Generic tasks, mentioned earlier, has their own task queue. These tasks 
-   are non-timeout tasks and they apply to all file descriptors, except to 
-   those that have explicitly registered a non-timeout task. These tasks
-   are there to make it simpler and faster to execute common code that
-   applies to all connections. These are, for example, receiving packets
-   from network and sending packets to network. It doesn't make much sense
-   to register a task that receives a packet from network to every connection
-   when you can have one task that applies to all connections. This is what
-   generic tasks are for. Generic tasks are not bound to any specific file
-   descriptor, however, the correct file descriptor must be passed as
-   argument to task registering function.
-
-   Short description of the field following:
-
-   SilcTask task
-
-       Pointer to the tasks in the queue.
-
-   int valid
-
-       Marks for validity of the queue. If the task queue is not valid 
-       scheduler will skip it. This is boolean value.
-
-   struct timeval timeout
-
-       Timeout when earliest some tasks in this queue should expire. The
-       value of this timeout is updated automatically by schedule. This 
-       is used only and only if this queue is a timeout queue. For normal
-       task queue this is not defined. This is meant only for internal
-       use and it should be considered to be read-only field.
-
-   SilcTask (*register_task)(SilcTaskQueue, int, 
-                             SilcTaskCallback, void *, 
-                            long, long, 
-                            SilcTaskType,
-                            SilcTaskPriority)
-
-       Registers a new task to the task queue. Arguments are as follows:
-
-       SilcTaskQueue queue        Queue where the task is to be registered
-       int fd                     File descriptor
-       SilcTaskCallback cb        Callback function to call
-       void *context              Context to be passed to callback function
-       long seconds               Seconds to timeout
-       long useconds              Microseconds to timeout
-       SilcTaskType type          Type of the task
-       SilcTaskPriority priority  Priority of the task
-
-       The same function is used to register all types of tasks. The type
-       argument tells what type of the task is. Note that when registering
-       non-timeout tasks one should also pass 0 as timeout as timeout will
-       be ignored anyway. Also, note, that one cannot register timeout task
-       with 0 timeout. There cannot be zero timeouts, passing zero means
-       no timeout is used for the task and SILC_TASK_FD_TASK is used as
-       default task type in this case.
-
-       One should be careful not to register timeout tasks to the non-timeout
-       task queue, because they will never expire. As one should not register
-       non-timeout tasks to timeout task queue because they will never get
-       scheduled.
-
-       There is a one distinct difference between timeout and non-timeout
-       tasks when they are executed. Non-timeout tasks remain on the task
-       queue after execution. Timeout tasks, however, are removed from the
-       task queue after they have expired. It is safe to re-register a task 
-       in its own callback function. It is also safe to unregister a task 
-       in a callback function.
-
-       Generic tasks apply to all file descriptors, however, one still must
-       pass the correct file descriptor to the function when registering
-       generic tasks.
-
-   void (*unregister_task)(SilcTaskQueue, SilcTask)
-
-       Unregisters a task already in the queue. Arguments are as follows:
-
-       SilcTaskQueue queue      Queue where from the task is unregistered
-       SilcTask task            Task to be unregistered
-
-       The same function is used to unregister timeout and non-timeout 
-       tasks. One can also unregister all tasks from the queue by passing
-       SILC_ALL_TASKS as task to the function. It is safe to unregister
-       a task in a callback function.
-
-   void (*set_iotype)(SilcTask, int type)
-
-       Sets the I/O type of the task. The scheduler checks for this value
-       and a task must always have at least one of the I/O types set at 
-       all time. When registering new task the type is set by default to
-       SILC_TASK_READ. If the task doesn't perform reading one must reset
-       the value to SILC_TASK_WRITE.
-
-       The type sent as argumenet is masked into the task. If the tasks 
-       I/O mask already includes this type this function has no effect. 
-       Only one I/O type can be added at once. If the task must perform
-       both reading and writing one must call this function for value
-       SILC_TASK_WRITE as well.
-
-   void (*reset_iotype)(SilcTask, int type)
-
-       Resets the mask to the type sent as argument. Note that this resets
-       the previous values to zero and then adds the type sent as argument.
-       This function can be used to remove one of the types masked earlier
-       to the task.
-
-*/
-
-typedef struct SilcTaskQueueStruct {
-  SilcTask task;
-  int valid;
-  struct timeval timeout;
-
-  /* Method functions */
-  SilcTask (*register_task)(struct SilcTaskQueueStruct *, int, 
-                           SilcTaskCallback, void *, long, long, 
-                           SilcTaskType, SilcTaskPriority);
-  void (*unregister_task)(struct SilcTaskQueueStruct *, SilcTask);
-  void (*set_iotype)(SilcTask, int type);
-  void (*reset_iotype)(SilcTask, int type);
-} SilcTaskQueueObject;
-
-typedef SilcTaskQueueObject *SilcTaskQueue;
-
-/* Marks for all tasks in a task queue. This can be passed to 
-   unregister_task function to cancel all tasks at once. */
-#define SILC_ALL_TASKS ((SilcTask)1)
-
-/* Marks for all task queues. This can be passed to 
-   silc_task_queue_unregister function to cancel all task queues at once. */
-#define SILC_ALL_TASK_QUEUES ((SilcTaskQueue)1)
-
-/* Silc Task event types. One of these are passed to the task callback
-   function from the schedule. These values are also masked into a task
-   so that scheduler knows for what kind of I/O it needs to perform
-   for that task. */
-#define SILC_TASK_READ 0
-#define SILC_TASK_WRITE 1
-
-/* Macros */
-
-/* These can be used instead of calling directly the registering function. 
-   XXX: These are not used currently, maybe they should be :) */
-#define SILC_REGISTER_FD_TASK(queue, fd, cb, ctx, pri) \
-  (silc_task_register((queue), (fd), (cb), (ctx), 0, 0, \
-                     SILC_TASK_FD, (pri)))
-#define SILC_REGISTER_TIMEOUT_TASK(queue, fd, cb, ctx, sec, usec, pri) \
-  (silc_task_register((queue), (fd), (cb), (ctx), (sec), (usec), \
-                     SILC_TASK_TIMEOUT, (pri)))
-#define SILC_REGISTER_GENERIC_TASK(queue, fd, cb, ctx, pri) \
-  (silc_task_register((queue), (fd), (cb), (ctx), 0, 0, \
-                     SILC_TASK_GENERIC, (pri)))
-
-/* Generic macro to define task callback functions. This defines a function
-   with name 'func' as a task callback function. */
-#define SILC_TASK_CALLBACK(func) \
-static void func(void *qptr, int type, void *context, int fd)
-
-
-/* Prototypes */
-void silc_task_queue_alloc(SilcTaskQueue *new, int valid);
-void silc_task_queue_free(SilcTaskQueue old);
-SilcTask silc_task_add(SilcTaskQueue queue, SilcTask new, 
-                      SilcTaskPriority priority);
-SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask new,
-                              SilcTaskPriority priority);
-SilcTask silc_task_register(SilcTaskQueue queue, int fd, 
-                           SilcTaskCallback cb, void *context, 
-                           long seconds, long useconds, 
-                           SilcTaskType type, 
-                           SilcTaskPriority priority);
-int silc_task_remove(SilcTaskQueue queue, SilcTask task);
-void silc_task_unregister(SilcTaskQueue queue, SilcTask task);
-void silc_task_unregister_by_fd(SilcTaskQueue queue, int fd);
-void silc_task_set_iotype(SilcTask task, int type);
-void silc_task_reset_iotype(SilcTask task, int type);
-int silc_task_timeout_compare(struct timeval *smaller, 
-                             struct timeval *bigger);
-
-#endif
diff --git a/lib/silccore/silcutil.c b/lib/silccore/silcutil.c
deleted file mode 100644 (file)
index 23d2440..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
-
-  silcutil.c
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * These are general utility functions that doesn't belong to any specific
- * group of routines.
- */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#include "silcincludes.h"
-
-/* Reads a file to a buffer. The allocated buffer is returned. Length of
-   the file read is returned to the return_len argument. */
-
-char *silc_file_read(const char *filename, int *return_len)
-{
-  int fd;
-  char *buffer;
-  int filelen;
-
-  fd = open(filename, O_RDONLY);
-  if (fd < 0) {
-    SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
-    return NULL;
-  }
-
-  filelen = lseek(fd, (off_t)0L, SEEK_END);
-  lseek(fd, (off_t)0L, SEEK_SET);  
-
-  if (filelen < 0) {
-    SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
-    return NULL;
-  }
-  
-  buffer = silc_calloc(filelen + 1, sizeof(char));
-  
-  if ((read(fd, buffer, filelen)) == -1) {
-    memset(buffer, 0, sizeof(buffer));
-    close(fd);
-    SILC_LOG_ERROR(("Cannot read from file %s: %s", filename,
-                    strerror(errno)));
-    return NULL;
-  }
-
-  close(fd);
-  buffer[filelen] = EOF;
-  
-  *return_len = filelen;
-  return buffer;
-}
-
-/* Writes a buffer to the file. */
-
-int silc_file_write(const char *filename, const char *buffer, int len)
-{
-  int fd;
-        
-  if ((fd = creat(filename, 0644)) == -1) {
-    SILC_LOG_ERROR(("Cannot open file %s for writing: %s", strerror(errno)));
-    return -1;
-  }
-  
-  if ((write(fd, buffer, len)) == -1) {
-    SILC_LOG_ERROR(("Cannot write to file %s: %s", strerror(errno)));
-    return -1;
-  }
-
-  close(fd);
-  
-  return 0;
-}
-
-/* Gets line from a buffer. Stops reading when a newline or EOF occurs.
-   This doesn't remove the newline sign from the destination buffer. The
-   argument begin is returned and should be passed again for the function. */
-
-int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
-{
-  static int start = 0;
-  int i;
-  
-  memset(dest, 0, destlen);
-  
-  if (begin != start)
-    start = 0;
-  
-  i = 0;
-  for ( ; start <= srclen; i++, start++) {
-    if (i > destlen)
-      return -1;
-    
-    dest[i] = src[start];
-    
-    if (dest[i] == EOF) 
-      return EOF;
-    
-    if (dest[i] == '\n') 
-      break;
-  }
-  start++;
-  
-  return start;
-}
-
-/* Checks line for illegal characters. Return -1 when illegal character
-   were found. This is used to check for bad lines when reading data from
-   for example a configuration file. */
-
-int silc_check_line(char *buf) 
-{
-  /* Illegal characters in line */
-  if (strchr(buf, '#')) return -1;
-  if (strchr(buf, '\'')) return -1;
-  if (strchr(buf, '\\')) return -1;
-  if (strchr(buf, '\r')) return -1;
-  if (strchr(buf, '\a')) return -1;
-  if (strchr(buf, '\b')) return -1;
-  if (strchr(buf, '\f')) return -1;
-  
-  /* Empty line */
-  if (buf[0] == '\n')
-    return -1;
-  
-  return 0;
-}
-
-/* Returns current time as string. */
-
-char *silc_get_time()
-{
-  time_t curtime;
-  char *return_time;
-
-  curtime = time(NULL);
-  return_time = ctime(&curtime);
-  return_time[strlen(return_time) - 1] = '\0';
-
-  return return_time;
-}
-
-/* Converts string to capital characters */
-
-char *silc_to_upper(char *string)
-{
-  int i;
-  char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
-
-  for (i = 0; i < strlen(string); i++)
-    ret[i] = toupper(string[i]);
-
-  return ret;
-}
-
-/* Compares two strings. Strings may include wildcards * and ?.
-   Returns TRUE if strings match. */
-
-int silc_string_compare(char *string1, char *string2)
-{
-  int i;
-  int slen1 = strlen(string1);
-  int slen2 = strlen(string2);
-  char *tmpstr1, *tmpstr2;
-
-  if (!string1 || !string2)
-    return FALSE;
-
-  /* See if they are same already */
-  if (!strncmp(string1, string2, strlen(string2)))
-    return TRUE;
-
-  if (slen2 < slen1)
-    if (!strchr(string1, '*'))
-      return FALSE;
-  
-  /* Take copies of the original strings as we will change them */
-  tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
-  memcpy(tmpstr1, string1, slen1);
-  tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
-  memcpy(tmpstr2, string2, slen2);
-  
-  for (i = 0; i < slen2; i++) {
-    
-    /* * wildcard. Only one * wildcard is possible. */
-    if (tmpstr1[i] == '*')
-      if (!strncmp(tmpstr1, tmpstr2, i)) {
-       memset(tmpstr2, 0, slen2);
-       strncpy(tmpstr2, tmpstr1, i);
-       break;
-      }
-    
-    /* ? wildcard */
-    if (tmpstr1[i] == '?') {
-      if (!strncmp(tmpstr1, tmpstr2, i)) {
-       if (!(slen1 < i + 1))
-         if (tmpstr1[i + 1] != '?' &&
-             tmpstr1[i + 1] != tmpstr2[i + 1])
-           continue;
-       
-       if (!(slen1 < slen2))
-         tmpstr2[i] = '?';
-      }
-#if 0
-    } else {
-      if (strncmp(tmpstr1, tmpstr2, i))
-       strncpy(tmpstr2, string2, slen2);
-#endif
-    }
-  }
-  
-  /* if using *, remove it */
-  if (strchr(tmpstr1, '*'))
-    *strchr(tmpstr1, '*') = 0;
-  
-  if (!strcmp(tmpstr1, tmpstr2)) {
-    memset(tmpstr1, 0, slen1);
-    memset(tmpstr2, 0, slen2);
-    silc_free(tmpstr1);
-    silc_free(tmpstr2);
-    return TRUE;
-  }
-  
-  memset(tmpstr1, 0, slen1);
-  memset(tmpstr2, 0, slen2);
-  silc_free(tmpstr1);
-  silc_free(tmpstr2);
-  return FALSE;
-}
diff --git a/lib/silccore/silcutil.h b/lib/silccore/silcutil.h
deleted file mode 100644 (file)
index dc57eeb..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-
-  silcutil.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SILCUTIL_H
-#define SILCUTIL_H
-
-/* Prototypes */
-char *silc_file_read(const char *filename, int *return_len);
-int silc_file_write(const char *filename, const char *buffer, int len);
-int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin);
-int silc_check_line(char *buf);
-char *silc_get_time();
-char *silc_to_upper(char *string);
-int silc_string_compare(char *string1, char *string2);
-
-#endif
diff --git a/lib/silccrypt/DIRECTORY b/lib/silccrypt/DIRECTORY
new file mode 100644 (file)
index 0000000..b52c4f5
--- /dev/null
@@ -0,0 +1,23 @@
+<!--
+@LIBRARY=SILC Crypto Library
+@FILENAME=silccryptlib.html
+@LINK=silccipher.html:SILC Cipher API
+@LINK=silchash.html:SILC Hash API
+@LINK=silchmac.html:SILC HMAC API
+@LINK=silcpkcs.html:SILC PKCS API
+@LINK=silcrng.html:SILC RNG API
+-->
+
+<BIG><B>SILC Crypto Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC Crypto Library provides cryptographic routines for applications.  It
+provides interfaces for ciphers, hash functions, HMACs and public key
+cryptosystems.  In addition is also provides interfaces for cryptographically
+strong random number generator.
+
+<BR /><BR />
+@LINKS@
+
index b0d7d8185ab09bc745be1f5f546dc98ee91071a6..cce35796f149d7f2b1c4cefe300b37edde72d852 100644 (file)
@@ -22,23 +22,47 @@ noinst_LIBRARIES = libsilccrypt.a
 
 libsilccrypt_a_SOURCES = \
        none.c \
-       blowfish.c \
        rc5.c \
        rc6.c \
        mars.c \
        md5.c \
-       rijndael.c \
+       aes.c \
        rsa.c \
        sha1.c \
        twofish.c \
+       blowfish.c \
+       cast.c \
        silccipher.c \
        silchash.c \
        silchmac.c \
        silcrng.c \
-       silcpkcs.c
+       silcpkcs.c \
+       pkcs1.c
+
+if SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       aes.h           \
+       blowfish.h      \
+       cast.h          \
+       ciphers_def.h   \
+       ciphers.h       \
+       mars.h          \
+       md5.h           \
+       none.h          \
+       pkcs1.h         \
+       rc5.h           \
+       rc6.h           \
+       rsa.h           \
+       sha1.h          \
+       silccipher.h    \
+       silcdh.h        \
+       silchash.h      \
+       silchmac.h      \
+       silcpkcs.h      \
+       silcrng.h       \
+       twofish.h
+endif
 
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../silccore -I../silcmath -I../silcske \
-       -I../silcsim -I../.. -I../../includes \
-       -I../silcmath/gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
similarity index 86%
rename from lib/silccrypt/rijndael.c
rename to lib/silccrypt/aes.c
index f6d5b1de390289124c73457bde42f332f0bcf6cb..840dfdde8f26986434b1b8ff637a81192f558d75 100644 (file)
@@ -1,4 +1,5 @@
 /* Modified for SILC. -Pekka */
+/* The AES */
 
 /* This is an independent implementation of the encryption algorithm:   */
 /*                                                                      */
@@ -39,7 +40,97 @@ Mean:          500 cycles =    51.2 mbits/sec
 */
 
 #include "silcincludes.h"
-#include "rijndael.h"
+#include "rijndael_internal.h"
+#include "aes.h"
+
+/* 
+ * SILC Crypto API for Rijndael
+ */
+
+/* Sets the key for the cipher. */
+
+SILC_CIPHER_API_SET_KEY(aes)
+{
+  uint32 k[8];
+
+  SILC_GET_WORD_KEY(key, k, keylen);
+  rijndael_set_key((RijndaelContext *)context, k, keylen);
+
+  return TRUE;
+}
+
+/* Sets the string as a new key for the cipher. The string is first
+   hashed and then used as a new key. */
+
+SILC_CIPHER_API_SET_KEY_WITH_STRING(aes)
+{
+  /*  unsigned char key[md5_hash_len];
+  SilcMarsContext *ctx = (SilcMarsContext *)context;
+
+  make_md5_hash(string, &key);
+  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
+  memset(&key, 'F', sizeoof(key));
+  */
+
+  return 1;
+}
+
+/* Returns the size of the cipher context. */
+
+SILC_CIPHER_API_CONTEXT_LEN(aes)
+{
+  return sizeof(RijndaelContext);
+}
+
+/* Encrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_ENCRYPT_CBC(aes)
+{
+  uint32 tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_ENC_PRE(tiv, src);
+  rijndael_encrypt((RijndaelContext *)context, tiv, tiv);
+  SILC_CBC_ENC_POST(tiv, dst, src);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_ENC_PRE(tiv, src);
+    rijndael_encrypt((RijndaelContext *)context, tiv, tiv);
+    SILC_CBC_ENC_POST(tiv, dst, src);
+  }
+
+  SILC_CBC_PUT_IV(tiv, iv);
+
+  return TRUE;
+}
+
+/* Decrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_DECRYPT_CBC(aes)
+{
+  uint32 tmp[4], tmp2[4], tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  rijndael_decrypt((RijndaelContext *)context, tmp, tmp2);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_DEC_PRE(tmp, src);
+    rijndael_decrypt((RijndaelContext *)context, tmp, tmp2); 
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+  }
+  
+  SILC_CBC_PUT_IV(tiv, iv);
+  
+  return TRUE;
+}
 
 #define LARGE_TABLES
 
similarity index 69%
rename from lib/silccore/silcconfig.h
rename to lib/silccrypt/aes.h
index 3153f256d72fc862daaf86ab3d2eb960afb828ca..6efd6582de3b6b87a82f2254d809e45ac851bfde 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  silcconfig.h
+  rijndael.h
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
 
 */
 
-#ifndef SILCCONFIG_H
-#define SILCCONFIG_H
+#ifndef RIJNDAEL_H
+#define RIJNDAEL_H
 
-/* Prototypes */
-void silc_config_open(char *filename, SilcBuffer *ret_buffer);
-int silc_config_get_token(SilcBuffer buffer, char **dest);
-int silc_config_check_num_token(SilcBuffer);
+/* 
+ * SILC Crypto API for Rijndael
+ */
+
+SILC_CIPHER_API_SET_KEY(aes);
+SILC_CIPHER_API_SET_KEY_WITH_STRING(aes);
+SILC_CIPHER_API_CONTEXT_LEN(aes);
+SILC_CIPHER_API_ENCRYPT_CBC(aes);
+SILC_CIPHER_API_DECRYPT_CBC(aes);
 
 #endif
index 03ff471cf55b528ef19fd102376ba3c2a64f73ae..1bea03106d4050ea5e5a119c3ee8f8a9c92d0bb2 100644 (file)
  */
 
 #include "silcincludes.h"
+#include "blowfish_internal.h"
 #include "blowfish.h"
 
+/* 
+ * SILC Crypto API for Blowfish
+ */
+
+/* Sets the key for the cipher. */
+
+SILC_CIPHER_API_SET_KEY(blowfish)
+{
+  blowfish_set_key((BlowfishContext *)context, (unsigned char *)key, keylen);
+  return TRUE;
+}
+
+/* Sets the string as a new key for the cipher. The string is first
+   hashed and then used as a new key. */
+
+SILC_CIPHER_API_SET_KEY_WITH_STRING(blowfish)
+{
+  /*  unsigned char key[md5_hash_len];
+  SilcMarsContext *ctx = (SilcMarsContext *)context;
+
+  make_md5_hash(string, &key);
+  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
+  memset(&key, 'F', sizeoof(key));
+  */
+
+  return 1;
+}
+
+/* Returns the size of the cipher context. */
+
+SILC_CIPHER_API_CONTEXT_LEN(blowfish)
+{
+  return sizeof(BlowfishContext);
+}
+
+/* Encrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_ENCRYPT_CBC(blowfish)
+{
+  uint32 tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_ENC_PRE(tiv, src);
+  blowfish_encrypt((BlowfishContext *)context, tiv, tiv, 16);
+  SILC_CBC_ENC_POST(tiv, dst, src);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_ENC_PRE(tiv, src);
+    blowfish_encrypt((BlowfishContext *)context, tiv, tiv, 16);
+    SILC_CBC_ENC_POST(tiv, dst, src);
+  }
+
+  SILC_CBC_PUT_IV(tiv, iv);
+
+  return TRUE;
+}
+
+/* Decrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_DECRYPT_CBC(blowfish)
+{
+  uint32 tmp[4], tmp2[4], tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  blowfish_decrypt((BlowfishContext *)context, tmp, tmp2, 16);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_DEC_PRE(tmp, src);
+    blowfish_decrypt((BlowfishContext *)context, tmp, tmp2, 16);
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+  }
+  
+  SILC_CBC_PUT_IV(tiv, iv);
+  
+  return TRUE;
+}
+
 static u32 bf_pbox[16 + 2] =
 {
     0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
@@ -307,7 +393,7 @@ static u32 bf_sbox[256 * 4] =
 
 /* 
  * Round loop unrolling macros, S is a pointer to a S-Box array
- * organized in 4 unsigned longs at a row.
+ * organized in 4 uint32s at a row.
  */
 
 #define GET32_3(x) (((x) & 0xff))
index 05034a6620be3fc02281d7e742fffac717371c81..c0caeee7cd96f76657492f15a741fefbd71c5b1d 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef BLOWFISH_H
 #define BLOWFISH_H
 
-#include "blowfish_internal.h"
-
 /* 
  * SILC Crypto API for Blowfish
  */
 
-/* Sets the key for the cipher. */
-
-SILC_CIPHER_API_SET_KEY(blowfish)
-{
-  blowfish_set_key((BlowfishContext *)context, 
-                  (unsigned char *)key, keylen);
-  return TRUE;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-SILC_CIPHER_API_SET_KEY_WITH_STRING(blowfish)
-{
-  SilcHash hash;
-  unsigned char key[16];
-
-  silc_hash_alloc("md5", &hash);
-  hash->make_hash(hash, string, stringlen, key);
-
-  blowfish_set_key((BlowfishContext *)context, key, sizeof(key));
-
-  silc_hash_free(hash);
-  memset(&key, 'F', sizeof(key));
-  
-  return TRUE;
-}
-
-/* Returns the size of the cipher context. */
-
-SILC_CIPHER_API_CONTEXT_LEN(blowfish)
-{
-  return sizeof(BlowfishContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-SILC_CIPHER_API_ENCRYPT_CBC(blowfish)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];  
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  blowfish_encrypt((BlowfishContext *)context, tmp, out, 16);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    blowfish_encrypt((BlowfishContext *)context, tmp, out, 16);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-SILC_CIPHER_API_DECRYPT_CBC(blowfish)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  blowfish_decrypt((BlowfishContext *)context, in, out, 16);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    blowfish_decrypt((BlowfishContext *)context, in, out, 16);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
+SILC_CIPHER_API_SET_KEY(blowfish);
+SILC_CIPHER_API_SET_KEY_WITH_STRING(blowfish);
+SILC_CIPHER_API_CONTEXT_LEN(blowfish);
+SILC_CIPHER_API_ENCRYPT_CBC(blowfish);
+SILC_CIPHER_API_DECRYPT_CBC(blowfish);
 
 #endif
index 1f066dfffaabf9c7cb33933e550df4506046af28..b3428cb2301e0fe5712227d42b2d11541dcfdd78 100644 (file)
-/* Modified for SILC. -Pekka */\r
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         CAST-256 by Carlisle Adams of Entrust Tecnhologies           */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-\r
-/* Timing data for CAST-256 (cast.c)\r
-\r
-Core timing without I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    4333 cycles\r
-Encrypt:       633 cycles =    40.4 mbits/sec\r
-Decrypt:       634 cycles =    40.4 mbits/sec\r
-Mean:          634 cycles =    40.4 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    4342 cycles\r
-Encrypt:       633 cycles =    40.4 mbits/sec\r
-Decrypt:       633 cycles =    40.4 mbits/sec\r
-Mean:          633 cycles =    40.4 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    4325 cycles\r
-Encrypt:       639 cycles =    40.1 mbits/sec\r
-Decrypt:       638 cycles =    40.1 mbits/sec\r
-Mean:          639 cycles =    40.1 mbits/sec\r
-\r
-Full timing with I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    4294 cycles\r
-Encrypt:       678 cycles =    37.8 mbits/sec\r
-Decrypt:       669 cycles =    38.3 mbits/sec\r
-Mean:          674 cycles =    38.0 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    4314 cycles\r
-Encrypt:       678 cycles =    37.8 mbits/sec\r
-Decrypt:       670 cycles =    38.2 mbits/sec\r
-Mean:          674 cycles =    38.0 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    4313 cycles\r
-Encrypt:       678 cycles =    37.8 mbits/sec\r
-Decrypt:       669 cycles =    38.3 mbits/sec\r
-Mean:          674 cycles =    38.0 mbits/sec\r
-\r
-*/\r
-\r
-#include "silcincludes.h"\r
-#include "cast.h"\r
-    \r
-u4byte s_box[4][256] = \r
-{ {\r
-    0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9C004dd3, \r
-    0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675,\r
-    0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059, \r
-    0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,\r
-    0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, \r
-    0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, \r
-    0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159, \r
-    0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,\r
-    0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f,\r
-    0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165,\r
-    0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0C50, 0x882240f2, 0x0c6e4f38, \r
-    0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,\r
-    0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, \r
-    0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, \r
-    0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, \r
-    0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,\r
-    0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, \r
-    0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6,\r
-    0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6C2, 0x81383f05, 0x6963c5c8,\r
-    0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,\r
-    0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, \r
-    0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e,\r
-    0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426, \r
-    0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, \r
-    0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, \r
-    0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f,\r
-    0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, 0x7b5a41f0, 0xd37cfbad, \r
-    0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, \r
-    0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, \r
-    0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a,\r
-    0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, 0x6188b153, \r
-    0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, \r
-    0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, \r
-    0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755,\r
-    0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, \r
-    0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,\r
-    0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, \r
-    0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79,\r
-    0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814C, 0x474d6ad7, 0x7c0c5e5c, \r
-    0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,\r
-    0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, \r
-    0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, \r
-    0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf\r
-  },\r
-  {\r
-    0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a,\r
-    0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, \r
-    0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, 0xa0b52f7b, 0x59e83605, \r
-    0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,\r
-    0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, \r
-    0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, \r
-    0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, 0xe113c85b, 0xacc40083, \r
-    0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,\r
-    0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, \r
-    0x361e3084, 0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, \r
-    0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, 0x2537a95e, \r
-    0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,\r
-    0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, \r
-    0x721d9bfd, 0xa58684bb, 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, \r
-    0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, \r
-    0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,\r
-    0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, \r
-    0x83ca6b94, 0x2d6ed23b, 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, \r
-    0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, \r
-    0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,\r
-    0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, \r
-    0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, \r
-    0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, 0xee41e729, 0x6e1d2d7c, \r
-    0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,\r
-    0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, \r
-    0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, \r
-    0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, 0xcdf0b680, 0x17844d3b, \r
-    0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,\r
-    0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, \r
-    0xef8579cc, 0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, \r
-    0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, 0x80823028, \r
-    0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,\r
-    0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6,\r
-    0x273be979, 0xb0ffeaa6, 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, \r
-    0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, \r
-    0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,\r
-    0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, \r
-    0x145892f5, 0x91584f7f, 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, \r
-    0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, \r
-    0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,\r
-    0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, \r
-    0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, \r
-    0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1\r
-  },\r
-  {\r
-    0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b,\r
-    0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, \r
-    0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, 0x11107d9f, 0x07647db9,\r
-    0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,\r
-    0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd,\r
-    0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e,\r
-    0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, 0xa8c01db7, 0x579fc264,\r
-    0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,\r
-    0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, \r
-    0xc5884a28, 0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, \r
-    0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, 0x1651192e, \r
-    0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,\r
-    0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, \r
-    0x796fb449, 0x8252dc15, 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, \r
-    0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, \r
-    0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,\r
-    0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, \r
-    0x96bbb682, 0x93b4b148, 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, \r
-    0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, \r
-    0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,\r
-    0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, \r
-    0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15,\r
-    0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, 0x68cc7bfb, 0xd90f2788, \r
-    0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,\r
-    0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, \r
-    0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, \r
-    0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, 0x285ba1c8, 0x3c62f44f, \r
-    0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,\r
-    0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, \r
-    0x12deca4d, 0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, \r
-    0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, 0xec00c9a9, \r
-    0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,\r
-    0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, \r
-    0xa2e53f55, 0xb9e6d4bc, 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, \r
-    0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, \r
-    0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,\r
-    0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, \r
-    0xf1ac2571, 0xcc8239c2, 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, \r
-    0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, \r
-    0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,\r
-    0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, \r
-    0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, \r
-    0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783\r
-  },\r
-  { \r
-    0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, \r
-    0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, \r
-    0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, 0x28147f5f, 0x4fa2b8cd, \r
-    0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,\r
-    0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, \r
-    0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, \r
-    0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, 0xce84ffdf, 0xf5718801, \r
-    0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,\r
-    0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, \r
-    0x72500e03, 0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746,\r
-    0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, 0x7f3d5ce3, \r
-    0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,\r
-    0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c,\r
-    0x18f8931e, 0x281658e6, 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c,\r
-    0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, \r
-    0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,\r
-    0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7,\r
-    0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, \r
-    0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, \r
-    0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,\r
-    0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, \r
-    0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, \r
-    0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, 0x026a4ceb, 0x52437eff, \r
-    0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,\r
-    0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, \r
-    0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, \r
-    0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, 0x63315c21, 0x5e0a72ec, \r
-    0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,\r
-    0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, \r
-    0xcfcbd12f, 0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, \r
-    0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, 0x58fd7eb6,\r
-    0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,\r
-    0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, \r
-    0xaf9eb3db, 0x29c9ed2a, 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, \r
-    0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, \r
-    0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,\r
-    0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2,\r
-    0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, \r
-    0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda,\r
-    0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,\r
-    0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6,\r
-    0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e,\r
-    0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2\r
-  }\r
-};\r
-\r
-#define f1(y,x,kr,km)           \\r
-    t  = rotl(km + x, kr);      \\r
-    u  = s_box[0][byte(t,3)];   \\r
-    u ^= s_box[1][byte(t,2)];   \\r
-    u -= s_box[2][byte(t,1)];   \\r
-    u += s_box[3][byte(t,0)];   \\r
-    y ^= u\r
-\r
-#define f2(y,x,kr,km)           \\r
-    t  = rotl(km ^ x, kr);      \\r
-    u  = s_box[0][byte(t,3)];   \\r
-    u -= s_box[1][byte(t,2)];   \\r
-    u += s_box[2][byte(t,1)];   \\r
-    u ^= s_box[3][byte(t,0)];   \\r
-    y ^= u\r
-\r
-#define f3(y,x,kr,km)           \\r
-    t  = rotl(km - x, kr);      \\r
-    u  = s_box[0][byte(t,3)];   \\r
-    u += s_box[1][byte(t,2)];   \\r
-    u ^= s_box[2][byte(t,1)];   \\r
-    u -= s_box[3][byte(t,0)];   \\r
-    y ^= u\r
-\r
-#define f_rnd(x,n)                              \\r
-    f1(x[2],x[3],l_key[n],    l_key[n + 4]);    \\r
-    f2(x[1],x[2],l_key[n + 1],l_key[n + 5]);    \\r
-    f3(x[0],x[1],l_key[n + 2],l_key[n + 6]);    \\r
-    f1(x[3],x[0],l_key[n + 3],l_key[n + 7])\r
-\r
-#define i_rnd(x, n)                             \\r
-    f1(x[3],x[0],l_key[n + 3],l_key[n + 7]);    \\r
-    f3(x[0],x[1],l_key[n + 2],l_key[n + 6]);    \\r
-    f2(x[1],x[2],l_key[n + 1],l_key[n + 5]);    \\r
-    f1(x[2],x[3],l_key[n],    l_key[n + 4])\r
-\r
-#define k_rnd(k,tr,tm)          \\r
-    f1(k[6],k[7],tr[0],tm[0]);  \\r
-    f2(k[5],k[6],tr[1],tm[1]);  \\r
-    f3(k[4],k[5],tr[2],tm[2]);  \\r
-    f1(k[3],k[4],tr[3],tm[3]);  \\r
-    f2(k[2],k[3],tr[4],tm[4]);  \\r
-    f3(k[1],k[2],tr[5],tm[5]);  \\r
-    f1(k[0],k[1],tr[6],tm[6]);  \\r
-    f2(k[7],k[0],tr[7],tm[7])\r
-\r
-/* initialise the key schedule from the user supplied key   */\r
-\r
-u4byte *cast_set_key(CastContext *ctx,\r
-                    const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u4byte  i, j, t, u, cm, cr, lk[8], tm[8], tr[8];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    for(i = 0; i < key_len / 32; ++i)\r
-\r
-        lk[i] = io_swap(in_key[i]);\r
-\r
-    for(; i < 8; ++i)\r
-\r
-        lk[i] = 0;\r
-\r
-    cm = 0x5a827999; cr = 19;\r
-\r
-    for(i = 0; i < 96; i += 8)\r
-    {\r
-        for(j = 0; j < 8; ++j)\r
-        {\r
-            tm[j] = cm; cm += 0x6ed9eba1;\r
-            tr[j] = cr; cr += 17;\r
-        }\r
-\r
-        k_rnd(lk, tr, tm);\r
-        \r
-        for(j = 0; j < 8; ++j)\r
-        {\r
-            tm[j] = cm; cm += 0x6ed9eba1;\r
-            tr[j] = cr; cr += 17;\r
-        }\r
-\r
-        k_rnd(lk, tr, tm);\r
-\r
-        l_key[i + 0] = lk[0]; l_key[i + 1] = lk[2];\r
-        l_key[i + 2] = lk[4]; l_key[i + 3] = lk[6];\r
-        l_key[i + 4] = lk[7]; l_key[i + 5] = lk[5];\r
-        l_key[i + 6] = lk[3]; l_key[i + 7] = lk[1];\r
-    }\r
-\r
-    return l_key;\r
-};\r
-\r
-/* encrypt a block of text  */\r
-\r
-void cast_encrypt(CastContext *ctx,\r
-                 const u4byte in_blk[4], u4byte out_blk[])\r
-{   \r
-    u4byte  t, u, blk[4];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    blk[0] = io_swap(in_blk[0]); blk[1] = io_swap(in_blk[1]);\r
-    blk[2] = io_swap(in_blk[2]); blk[3] = io_swap(in_blk[3]);\r
-\r
-    f_rnd(blk,  0); f_rnd(blk,  8);\r
-    f_rnd(blk, 16); f_rnd(blk, 24);\r
-    f_rnd(blk, 32); f_rnd(blk, 40);\r
-    i_rnd(blk, 48); i_rnd(blk, 56);\r
-    i_rnd(blk, 64); i_rnd(blk, 72);\r
-    i_rnd(blk, 80); i_rnd(blk, 88);\r
-\r
-    out_blk[0] = io_swap(blk[0]); out_blk[1] = io_swap(blk[1]);\r
-    out_blk[2] = io_swap(blk[2]); out_blk[3] = io_swap(blk[3]);\r
-};\r
-\r
-/* decrypt a block of text  */\r
-\r
-void cast_decrypt(CastContext *ctx,\r
-                 const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  t, u, blk[4];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    blk[0] = io_swap(in_blk[0]); blk[1] = io_swap(in_blk[1]);\r
-    blk[2] = io_swap(in_blk[2]); blk[3] = io_swap(in_blk[3]);\r
-\r
-    f_rnd(blk, 88); f_rnd(blk, 80);\r
-    f_rnd(blk, 72); f_rnd(blk, 64);\r
-    f_rnd(blk, 56); f_rnd(blk, 48);\r
-    i_rnd(blk, 40); i_rnd(blk, 32);\r
-    i_rnd(blk, 24); i_rnd(blk, 16);\r
-    i_rnd(blk,  8); i_rnd(blk,  0);\r
-\r
-    out_blk[0] = io_swap(blk[0]); out_blk[1] = io_swap(blk[1]);\r
-    out_blk[2] = io_swap(blk[2]); out_blk[3] = io_swap(blk[3]);\r
-};\r
-\r
+/* Modified for SILC. -Pekka */
+
+/* This is an independent implementation of the encryption algorithm:   */
+/*                                                                      */
+/*         CAST-256 by Carlisle Adams of Entrust Tecnhologies           */
+/*                                                                      */
+/* which is a candidate algorithm in the Advanced Encryption Standard   */
+/* programme of the US National Institute of Standards and Technology.  */
+/*                                                                      */
+/* Copyright in this implementation is held by Dr B R Gladman but I     */
+/* hereby give permission for its free direct or derivative use subject */
+/* to acknowledgment of its origin and compliance with any conditions   */
+/* that the originators of the algorithm place on its exploitation.     */
+/*                                                                      */
+/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */
+
+/* Timing data for CAST-256 (cast.c)
+
+Core timing without I/O endian conversion:
+
+128 bit key:
+Key Setup:    4333 cycles
+Encrypt:       633 cycles =    40.4 mbits/sec
+Decrypt:       634 cycles =    40.4 mbits/sec
+Mean:          634 cycles =    40.4 mbits/sec
+
+192 bit key:
+Key Setup:    4342 cycles
+Encrypt:       633 cycles =    40.4 mbits/sec
+Decrypt:       633 cycles =    40.4 mbits/sec
+Mean:          633 cycles =    40.4 mbits/sec
+
+256 bit key:
+Key Setup:    4325 cycles
+Encrypt:       639 cycles =    40.1 mbits/sec
+Decrypt:       638 cycles =    40.1 mbits/sec
+Mean:          639 cycles =    40.1 mbits/sec
+
+Full timing with I/O endian conversion:
+
+128 bit key:
+Key Setup:    4294 cycles
+Encrypt:       678 cycles =    37.8 mbits/sec
+Decrypt:       669 cycles =    38.3 mbits/sec
+Mean:          674 cycles =    38.0 mbits/sec
+
+192 bit key:
+Key Setup:    4314 cycles
+Encrypt:       678 cycles =    37.8 mbits/sec
+Decrypt:       670 cycles =    38.2 mbits/sec
+Mean:          674 cycles =    38.0 mbits/sec
+
+256 bit key:
+Key Setup:    4313 cycles
+Encrypt:       678 cycles =    37.8 mbits/sec
+Decrypt:       669 cycles =    38.3 mbits/sec
+Mean:          674 cycles =    38.0 mbits/sec
+
+*/
+
+#include "silcincludes.h"
+#include "cast_internal.h"
+#include "cast.h"
+
+#define io_swap
+    
+/* 
+ * SILC Crypto API for Cast-256
+ */
+
+/* Sets the key for the cipher. */
+
+SILC_CIPHER_API_SET_KEY(cast)
+{
+  uint32 k[8];
+
+  SILC_GET_WORD_KEY(key, k, keylen);
+  cast_set_key((CastContext *)context, k, keylen);
+
+  return TRUE;
+}
+
+/* Sets the string as a new key for the cipher. The string is first
+   hashed and then used as a new key. */
+
+SILC_CIPHER_API_SET_KEY_WITH_STRING(cast)
+{
+  /*  unsigned char key[md5_hash_len];
+  SilcMarsContext *ctx = (SilcMarsContext *)context;
+
+  make_md5_hash(string, &key);
+  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
+  memset(&key, 'F', sizeoof(key));
+  */
+
+  return 1;
+}
+
+/* Returns the size of the cipher context. */
+
+SILC_CIPHER_API_CONTEXT_LEN(cast)
+{
+  return sizeof(CastContext);
+}
+
+/* Encrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_ENCRYPT_CBC(cast)
+{
+  uint32 tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_ENC_PRE(tiv, src);
+  cast_encrypt((CastContext *)context, tiv, tiv);
+  SILC_CBC_ENC_POST(tiv, dst, src);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_ENC_PRE(tiv, src);
+    cast_encrypt((CastContext *)context, tiv, tiv);
+    SILC_CBC_ENC_POST(tiv, dst, src);
+  }
+
+  SILC_CBC_PUT_IV(tiv, iv);
+
+  return TRUE;
+}
+
+/* Decrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_DECRYPT_CBC(cast)
+{
+  uint32 tmp[4], tmp2[4], tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  cast_decrypt((CastContext *)context, tmp, tmp2);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_DEC_PRE(tmp, src);
+    cast_decrypt((CastContext *)context, tmp, tmp2); 
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+  }
+  
+  SILC_CBC_PUT_IV(tiv, iv);
+  
+  return TRUE;
+}
+
+u4byte s_box[4][256] = 
+{ {
+    0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9C004dd3, 
+    0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675,
+    0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059, 
+    0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
+    0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 
+    0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 
+    0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159, 
+    0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935,
+    0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f,
+    0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165,
+    0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0C50, 0x882240f2, 0x0c6e4f38, 
+    0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe,
+    0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 
+    0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 
+    0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, 
+    0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291,
+    0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 
+    0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6,
+    0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6C2, 0x81383f05, 0x6963c5c8,
+    0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511,
+    0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 
+    0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e,
+    0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 
+    0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 
+    0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 
+    0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f,
+    0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, 0x7b5a41f0, 0xd37cfbad, 
+    0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, 
+    0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 
+    0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a,
+    0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, 0x3f04442f, 0x6188b153, 
+    0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, 
+    0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 
+    0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755,
+    0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, 0x580304f0, 0xca042cf1, 
+    0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
+    0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 
+    0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79,
+    0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814C, 0x474d6ad7, 0x7c0c5e5c, 
+    0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e,
+    0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 
+    0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 
+    0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
+  },
+  {
+    0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a,
+    0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 
+    0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, 0xa0b52f7b, 0x59e83605, 
+    0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb,
+    0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 
+    0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 
+    0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, 0xe113c85b, 0xacc40083, 
+    0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359,
+    0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 
+    0x361e3084, 0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 
+    0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, 0x2537a95e, 
+    0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34,
+    0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 
+    0x721d9bfd, 0xa58684bb, 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 
+    0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, 
+    0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860,
+    0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 
+    0x83ca6b94, 0x2d6ed23b, 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 
+    0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, 
+    0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b,
+    0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 
+    0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 
+    0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, 0xee41e729, 0x6e1d2d7c, 
+    0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13,
+    0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 
+    0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 
+    0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, 0xcdf0b680, 0x17844d3b, 
+    0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6,
+    0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 
+    0xef8579cc, 0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 
+    0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, 0x80823028, 
+    0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
+    0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6,
+    0x273be979, 0xb0ffeaa6, 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 
+    0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, 
+    0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6,
+    0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 
+    0x145892f5, 0x91584f7f, 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 
+    0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, 
+    0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
+    0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 
+    0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 
+    0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1
+  },
+  {
+    0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b,
+    0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 
+    0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, 0x11107d9f, 0x07647db9,
+    0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e,
+    0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd,
+    0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e,
+    0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, 0xa8c01db7, 0x579fc264,
+    0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
+    0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 
+    0xc5884a28, 0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 
+    0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, 0x1651192e, 
+    0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
+    0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 
+    0x796fb449, 0x8252dc15, 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 
+    0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, 
+    0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176,
+    0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 
+    0x96bbb682, 0x93b4b148, 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 
+    0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, 
+    0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
+    0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 
+    0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15,
+    0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, 0x68cc7bfb, 0xd90f2788, 
+    0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f,
+    0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 
+    0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 
+    0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, 0x285ba1c8, 0x3c62f44f, 
+    0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
+    0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 
+    0x12deca4d, 0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 
+    0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, 0xec00c9a9, 
+    0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536,
+    0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 
+    0xa2e53f55, 0xb9e6d4bc, 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 
+    0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, 
+    0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69,
+    0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 
+    0xf1ac2571, 0xcc8239c2, 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 
+    0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, 
+    0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d,
+    0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 
+    0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 
+    0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783
+  },
+  { 
+    0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 
+    0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 
+    0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, 0x28147f5f, 0x4fa2b8cd, 
+    0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15,
+    0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 
+    0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 
+    0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, 0xce84ffdf, 0xf5718801, 
+    0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
+    0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 
+    0x72500e03, 0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746,
+    0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, 0x7f3d5ce3, 
+    0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d,
+    0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c,
+    0x18f8931e, 0x281658e6, 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c,
+    0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, 
+    0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003,
+    0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7,
+    0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 
+    0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, 
+    0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
+    0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 
+    0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 
+    0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, 0x026a4ceb, 0x52437eff, 
+    0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df,
+    0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 
+    0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 
+    0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, 0x63315c21, 0x5e0a72ec, 
+    0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7,
+    0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 
+    0xcfcbd12f, 0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 
+    0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, 0x58fd7eb6,
+    0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2,
+    0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 
+    0xaf9eb3db, 0x29c9ed2a, 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 
+    0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, 
+    0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
+    0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2,
+    0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 
+    0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda,
+    0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04,
+    0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6,
+    0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e,
+    0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
+  }
+};
+
+#define f1(y,x,kr,km)                          \
+    t  = rotl(km + x, kr);                     \
+    u  = s_box[0][byte(t,3)];                  \
+    u ^= s_box[1][byte(t,2)];                  \
+    u -= s_box[2][byte(t,1)];                  \
+    u += s_box[3][byte(t,0)];                  \
+    y ^= u;
+
+#define f2(y,x,kr,km)                          \
+    t  = rotl(km ^ x, kr);                     \
+    u  = s_box[0][byte(t,3)];                  \
+    u -= s_box[1][byte(t,2)];                  \
+    u += s_box[2][byte(t,1)];                  \
+    u ^= s_box[3][byte(t,0)];                  \
+    y ^= u;
+
+#define f3(y,x,kr,km)                          \
+    t  = rotl(km - x, kr);                     \
+    u  = s_box[0][byte(t,3)];                  \
+    u += s_box[1][byte(t,2)];                  \
+    u ^= s_box[2][byte(t,1)];                  \
+    u -= s_box[3][byte(t,0)];                  \
+    y ^= u;
+
+#define f_rnd(x,n)                             \
+    f1(x[2],x[3],l_key[n],    l_key[n + 4]);   \
+    f2(x[1],x[2],l_key[n + 1],l_key[n + 5]);   \
+    f3(x[0],x[1],l_key[n + 2],l_key[n + 6]);   \
+    f1(x[3],x[0],l_key[n + 3],l_key[n + 7]);
+
+#define i_rnd(x, n)                            \
+    f1(x[3],x[0],l_key[n + 3],l_key[n + 7]);   \
+    f3(x[0],x[1],l_key[n + 2],l_key[n + 6]);   \
+    f2(x[1],x[2],l_key[n + 1],l_key[n + 5]);   \
+    f1(x[2],x[3],l_key[n],    l_key[n + 4]);
+
+#define k_rnd(k,tr,tm)                         \
+    f1(k[6],k[7],tr[0],tm[0]);                 \
+    f2(k[5],k[6],tr[1],tm[1]);                 \
+    f3(k[4],k[5],tr[2],tm[2]);                 \
+    f1(k[3],k[4],tr[3],tm[3]);                 \
+    f2(k[2],k[3],tr[4],tm[4]);                 \
+    f3(k[1],k[2],tr[5],tm[5]);                 \
+    f1(k[0],k[1],tr[6],tm[6]);                 \
+    f2(k[7],k[0],tr[7],tm[7]);
+
+/* initialise the key schedule from the user supplied key   */
+
+u4byte *cast_set_key(CastContext *ctx,
+                    const u4byte in_key[], const u4byte key_len)
+{   
+    u4byte  i, j, t, u, cm, cr, lk[8], tm[8], tr[8];
+    u4byte *l_key = ctx->l_key;
+
+    for(i = 0; i < key_len / 32; ++i)
+
+        lk[i] = io_swap(in_key[i]);
+
+    for(; i < 8; ++i)
+
+        lk[i] = 0;
+
+    cm = 0x5a827999; cr = 19;
+
+    for(i = 0; i < 96; i += 8)
+    {
+        for(j = 0; j < 8; ++j)
+        {
+            tm[j] = cm; cm += 0x6ed9eba1;
+            tr[j] = cr; cr += 17;
+        }
+
+        k_rnd(lk, tr, tm);
+        
+        for(j = 0; j < 8; ++j)
+        {
+            tm[j] = cm; cm += 0x6ed9eba1;
+            tr[j] = cr; cr += 17;
+        }
+
+        k_rnd(lk, tr, tm);
+
+        l_key[i + 0] = lk[0]; l_key[i + 1] = lk[2];
+        l_key[i + 2] = lk[4]; l_key[i + 3] = lk[6];
+        l_key[i + 4] = lk[7]; l_key[i + 5] = lk[5];
+        l_key[i + 6] = lk[3]; l_key[i + 7] = lk[1];
+    }
+
+    return l_key;
+}
+
+/* encrypt a block of text  */
+
+void cast_encrypt(CastContext *ctx,
+                 const u4byte in_blk[4], u4byte out_blk[])
+{   
+    u4byte  t, u, blk[4];
+    u4byte *l_key = ctx->l_key;
+
+    blk[0] = io_swap(in_blk[0]); blk[1] = io_swap(in_blk[1]);
+    blk[2] = io_swap(in_blk[2]); blk[3] = io_swap(in_blk[3]);
+
+    f_rnd(blk,  0); f_rnd(blk,  8);
+    f_rnd(blk, 16); f_rnd(blk, 24);
+    f_rnd(blk, 32); f_rnd(blk, 40);
+    i_rnd(blk, 48); i_rnd(blk, 56);
+    i_rnd(blk, 64); i_rnd(blk, 72);
+    i_rnd(blk, 80); i_rnd(blk, 88);
+
+    out_blk[0] = io_swap(blk[0]); out_blk[1] = io_swap(blk[1]);
+    out_blk[2] = io_swap(blk[2]); out_blk[3] = io_swap(blk[3]);
+}
+
+/* decrypt a block of text  */
+
+void cast_decrypt(CastContext *ctx,
+                 const u4byte in_blk[4], u4byte out_blk[4])
+{   
+    u4byte  t, u, blk[4];
+    u4byte *l_key = ctx->l_key;
+
+    blk[0] = io_swap(in_blk[0]); blk[1] = io_swap(in_blk[1]);
+    blk[2] = io_swap(in_blk[2]); blk[3] = io_swap(in_blk[3]);
+
+    f_rnd(blk, 88); f_rnd(blk, 80);
+    f_rnd(blk, 72); f_rnd(blk, 64);
+    f_rnd(blk, 56); f_rnd(blk, 48);
+    i_rnd(blk, 40); i_rnd(blk, 32);
+    i_rnd(blk, 24); i_rnd(blk, 16);
+    i_rnd(blk,  8); i_rnd(blk,  0);
+
+    out_blk[0] = io_swap(blk[0]); out_blk[1] = io_swap(blk[1]);
+    out_blk[2] = io_swap(blk[2]); out_blk[3] = io_swap(blk[3]);
+}
index a83c06e5a9d9399b684c63dec175f3756f10c74d..fe7351906037a48b61e9c25390f09c1a9fcfdd66 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef CAST_H
 #define CAST_H
 
-#include "cast_internal.h"
-
 /* 
- * SILC Crypto API for Cast
+ * SILC Crypto API for Cast-256
  */
 
-/* Sets the key for the cipher. */
-
-inline int silc_cast_init(void *context, 
-                         const unsigned char *key, 
-                         unsigned int keylen)
-{
-  cast_set_key((CastContext *)context, (unsigned int *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-inline int silc_cast_set_string_as_key(void *context, 
-                                      const unsigned char *string,
-                                      unsigned int stringlen)
-{
-  /*  SilcHash hash;
-  unsigned char key[16];
-
-  silc_hash_alloc("md5", &hash);
-  hash->make_hash(hash, string, stringlen, key);
-
-  cast_set_key((CastContext *)context, (const u4byte *)key, sizeof(key));
-
-  silc_hash_free(hash);
-  memset(&key, 'F', sizeof(key));
-  */
-  return TRUE;
-}
-
-/* Returns the size of the cipher context. */
-
-inline unsigned int silc_cast_context_len()
-{
-  return sizeof(CastContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-inline int silc_cast_encrypt_cbc(void *context,
-                                const unsigned char *src,
-                                unsigned char *dst,
-                                unsigned int len,
-                                unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  cast_encrypt((CastContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    cast_encrypt((CastContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-inline int silc_cast_decrypt_cbc(void *context,
-                                const unsigned char *src,
-                                unsigned char *dst,
-                                unsigned int len,
-                                unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  cast_decrypt((CastContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    cast_decrypt((CastContext *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
+SILC_CIPHER_API_SET_KEY(cast);
+SILC_CIPHER_API_SET_KEY_WITH_STRING(cast);
+SILC_CIPHER_API_CONTEXT_LEN(cast);
+SILC_CIPHER_API_ENCRYPT_CBC(cast);
+SILC_CIPHER_API_DECRYPT_CBC(cast);
 
 #endif
index a7b8ece9db549bc0f4e59efefd0d44a9bbd26a35..3bc690084843c1fd9aef65093fa117dc687f580d 100644 (file)
 
 #include "none.h"
 #include "mars.h"
+#include "rc5.h"
 #include "rc6.h"
 #include "twofish.h"
+#include "aes.h"
+#include "blowfish.h"
+#include "cast.h"
 
 #endif
index 3db98254a3d5f880e997b2ed99768ef6f4cb2032..12f455312dda90b78c70e5b8e6ac49cf55b76952 100644 (file)
 
 /* General definitions for algorithms */
 typedef unsigned char u1byte;
-typedef unsigned int u4byte;
-typedef unsigned int u32;
+typedef uint32 u4byte;
+typedef uint32 u32;
 
 #define rotr(x, nr) (((x) >> ((int)(nr))) | ((x) << (32 - (int)(nr))))
 #define rotl(x, nr) (((x) << ((int)(nr))) | ((x) >> (32 - (int)(nr))))
 #define byte(x, nr) ((x) >> (nr * 8) & 255)
 
+/* Byte key to words */
+#define SILC_GET_WORD_KEY(s, d, len)           \
+do {                                           \
+  int _i;                                      \
+  for (_i = 0; _i < (len / 8) / 4; _i++)       \
+    SILC_GET32_LSB(d[_i], s + (_i * 4));       \
+} while(0);
+
+/* CBC mode macros. */
+
+#define SILC_CBC_GET_IV(d, s)                  \
+do {                                           \
+  SILC_GET32_LSB(d[0], &s[0]);                 \
+  SILC_GET32_LSB(d[1], &s[4]);                 \
+  SILC_GET32_LSB(d[2], &s[8]);                 \
+  SILC_GET32_LSB(d[3], &s[12]);                        \
+} while(0);
+
+#define SILC_CBC_PUT_IV(s, d)                  \
+do {                                           \
+  SILC_PUT32_LSB(s[0], &d[0]);                 \
+  SILC_PUT32_LSB(s[1], &d[4]);                 \
+  SILC_PUT32_LSB(s[2], &d[8]);                 \
+  SILC_PUT32_LSB(s[3], &d[12]);                        \
+} while(0);
+
+#define SILC_CBC_ENC_PRE(d, s)                 \
+do {                                           \
+  SILC_GET32_X_LSB(d[0], &s[0]);               \
+  SILC_GET32_X_LSB(d[1], &s[4]);               \
+  SILC_GET32_X_LSB(d[2], &s[8]);               \
+  SILC_GET32_X_LSB(d[3], &s[12]);              \
+} while(0);
+
+#define SILC_CBC_ENC_POST(s, d, t)             \
+do {                                           \
+  SILC_PUT32_LSB(s[0], &d[0]);                 \
+  SILC_PUT32_LSB(s[1], &d[4]);                 \
+  SILC_PUT32_LSB(s[2], &d[8]);                 \
+  SILC_PUT32_LSB(s[3], &d[12]);                        \
+                                               \
+  d += 16;                                     \
+  t += 16;                                     \
+} while(0);
+
+#define SILC_CBC_DEC_PRE(d, s)                 \
+do {                                           \
+  SILC_GET32_LSB(d[0], &s[0]);                 \
+  SILC_GET32_LSB(d[1], &s[4]);                 \
+  SILC_GET32_LSB(d[2], &s[8]);                 \
+  SILC_GET32_LSB(d[3], &s[12]);                        \
+} while(0);
+
+#define SILC_CBC_DEC_POST(s, d, p, t, siv)     \
+do {                                           \
+  s[0] ^= siv[0];                              \
+  s[1] ^= siv[1];                              \
+  s[2] ^= siv[2];                              \
+  s[3] ^= siv[3];                              \
+                                               \
+  SILC_PUT32_LSB(s[0], &d[0]);                 \
+  SILC_PUT32_LSB(s[1], &d[4]);                 \
+  SILC_PUT32_LSB(s[2], &d[8]);                 \
+  SILC_PUT32_LSB(s[3], &d[12]);                        \
+                                               \
+  siv[0] = t[0];                               \
+  siv[1] = t[1];                               \
+  siv[2] = t[2];                               \
+  siv[3] = t[3];                               \
+                                               \
+  d += 16;                                     \
+  p += 16;                                     \
+} while(0);
+
 #endif
diff --git a/lib/silccrypt/crypton.c b/lib/silccrypt/crypton.c
deleted file mode 100644 (file)
index dae0221..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-/* Modified for SILC. -Pekka */\r
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         CRYPTON by Chae Hoon Lim of Future Systms Inc                */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-\r
-/* Timing data for CRYPTON (crypton.c)\r
-\r
-128 bit key:\r
-Key Setup:    531/1369 cycles (encrypt/decrypt)\r
-Encrypt:       474 cycles =    54.0 mbits/sec\r
-Decrypt:       474 cycles =    54.0 mbits/sec\r
-Mean:          474 cycles =    54.0 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    539/1381 cycles (encrypt/decrypt)\r
-Encrypt:       473 cycles =    54.1 mbits/sec\r
-Decrypt:       470 cycles =    54.5 mbits/sec\r
-Mean:          472 cycles =    54.3 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    552/1392 cycles (encrypt/decrypt)\r
-Encrypt:       469 cycles =    54.6 mbits/sec\r
-Decrypt:       483 cycles =    53.0 mbits/sec\r
-Mean:          476 cycles =    53.8 mbits/sec\r
-\r
-*/\r
-\r
-#include "silcincludes.h"\r
-#include "crypton.h"\r
-\r
-#define gamma_tau(x,b,m,p,q) \\r
-    (x) = (((u4byte)s_box[p][byte(b[0],m)]      ) | \\r
-           ((u4byte)s_box[q][byte(b[1],m)] <<  8) | \\r
-           ((u4byte)s_box[p][byte(b[2],m)] << 16) | \\r
-           ((u4byte)s_box[q][byte(b[3],m)] << 24))\r
-\r
-#define ma_0    0x3fcff3fc\r
-#define ma_1    0xfc3fcff3\r
-#define ma_2    0xf3fc3fcf\r
-#define ma_3    0xcff3fc3f\r
-\r
-#define mb_0    0xcffccffc\r
-#define mb_1    0xf33ff33f\r
-#define mb_2    0xfccffccf\r
-#define mb_3    0x3ff33ff3\r
-\r
-#define pi(b,n0,n1,n2,n3)       \\r
-    (((b)[0] & ma_##n0) ^       \\r
-     ((b)[1] & ma_##n1) ^       \\r
-     ((b)[2] & ma_##n2) ^       \\r
-     ((b)[3] & ma_##n3))\r
-\r
-#define phi_n(x,n0,n1,n2,n3)    \\r
-    (     (x)      & mb_##n0) ^ \\r
-    (rotl((x),  8) & mb_##n1) ^ \\r
-    (rotl((x), 16) & mb_##n2) ^ \\r
-    (rotl((x), 24) & mb_##n3)\r
-\r
-#define phi_00(x)   phi_n(x,0,1,2,3)\r
-#define phi_01(x)   phi_n(x,3,0,1,2)\r
-#define phi_02(x)   phi_n(x,2,3,0,1)\r
-#define phi_03(x)   phi_n(x,1,2,3,0)\r
-\r
-#define phi_10(x)   phi_n(x,3,0,1,2)\r
-#define phi_11(x)   phi_n(x,2,3,0,1)\r
-#define phi_12(x)   phi_n(x,1,2,3,0)\r
-#define phi_13(x)   phi_n(x,0,1,2,3)\r
-\r
-#define phi0(x,y)               \\r
-    (y)[0] = phi_00((x)[0]);    \\r
-    (y)[1] = phi_01((x)[1]);    \\r
-    (y)[2] = phi_02((x)[2]);    \\r
-    (y)[3] = phi_03((x)[3])\r
-\r
-#define phi1(x,y)               \\r
-    (y)[0] = phi_10((x)[0]);    \\r
-    (y)[1] = phi_11((x)[1]);    \\r
-    (y)[2] = phi_12((x)[2]);    \\r
-    (y)[3] = phi_13((x)[3])\r
-\r
-u1byte p_box[3][16] = \r
-{   { 15,  9,  6,  8,  9,  9,  4, 12,  6,  2,  6, 10,  1,  3,  5, 15 },\r
-    { 10, 15,  4,  7,  5,  2, 14,  6,  9,  3, 12,  8, 13,  1, 11,  0 },\r
-    {  0,  4,  8,  4,  2, 15,  8, 13,  1,  1, 15,  7,  2, 11, 14, 15 }\r
-};\r
-\r
-u4byte  tab_gen = 0;\r
-u1byte  s_box[2][256];\r
-u4byte  s_tab[4][256];\r
-\r
-void gen_tab(void)\r
-{   u4byte  i, xl, xr, yl, yr;\r
-\r
-    for(i = 0; i < 256; ++i)\r
-    {\r
-        xl = (i & 0xf0) >> 4; xr = i & 15;\r
-\r
-        yr = xr ^ p_box[1][xl ^ p_box[0][xr]];\r
-        yl = xl ^ p_box[0][xr] ^ p_box[2][yr];\r
-\r
-        yr |= (yl << 4); s_box[0][i] = (u1byte)yr; s_box[1][yr] = (u1byte)i;\r
-\r
-        xr = yr * 0x01010101; xl = i * 0x01010101;\r
-\r
-        s_tab[0][ i] = xr & 0x3fcff3fc;\r
-        s_tab[1][yr] = xl & 0xfc3fcff3;\r
-        s_tab[2][ i] = xr & 0xf3fc3fcf;\r
-        s_tab[3][yr] = xl & 0xcff3fc3f;\r
-    }\r
-};\r
-\r
-/* initialise the key schedule from the user supplied key   */\r
-\r
-u4byte  kp[4] = { 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f };\r
-u4byte  kq[4] = { 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, 0xcbbb9d5d };\r
-\r
-#define h0_block(n,r0,r1)                           \\r
-    e_key[4 * n +  8] = rotl(e_key[4 * n + 0], r0); \\r
-    e_key[4 * n +  9] = rc ^ e_key[4 * n + 1];      \\r
-    e_key[4 * n + 10] = rotl(e_key[4 * n + 2], r1); \\r
-    e_key[4 * n + 11] = rc ^ e_key[4 * n + 3]\r
-\r
-#define h1_block(n,r0,r1)                           \\r
-    e_key[4 * n +  8] = rc ^ e_key[4 * n + 0];      \\r
-    e_key[4 * n +  9] = rotl(e_key[4 * n + 1], r0); \\r
-    e_key[4 * n + 10] = rc ^ e_key[4 * n + 2];      \\r
-    e_key[4 * n + 11] = rotl(e_key[4 * n + 3], r1)\r
-\r
-u4byte *crypton_set_key(CryptonContext *ctx,\r
-                       const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u4byte  i, rc, t0, t1, tmp[4];\r
-    u4byte *e_key = ctx->l_key + 52;\r
-    u4byte *d_key = ctx->l_key;\r
-\r
-    if(!tab_gen)\r
-    {\r
-        gen_tab(); tab_gen = 1;\r
-    }\r
-\r
-    e_key[2] = e_key[3] = e_key[6] = e_key[7] = 0;\r
-\r
-    switch((key_len + 63) / 64)\r
-    {\r
-    case 4: e_key[3] = in_key[6]; e_key[7] = in_key[7];\r
-    case 3: e_key[2] = in_key[4]; e_key[6] = in_key[5];\r
-    case 2: e_key[0] = in_key[0]; e_key[4] = in_key[1];\r
-            e_key[1] = in_key[2]; e_key[5] = in_key[3];\r
-    }\r
-\r
-    tmp[0] = pi(e_key, 0, 1, 2, 3) ^ kp[0];\r
-    tmp[1] = pi(e_key, 1, 2, 3, 0) ^ kp[1];\r
-    tmp[2] = pi(e_key, 2, 3, 0, 1) ^ kp[2];\r
-    tmp[3] = pi(e_key, 3, 0, 1, 2) ^ kp[3];\r
-    \r
-    gamma_tau(e_key[0], tmp, 0, 0, 1); \r
-    gamma_tau(e_key[1], tmp, 1, 1, 0);\r
-    gamma_tau(e_key[2], tmp, 2, 0, 1); \r
-    gamma_tau(e_key[3], tmp, 3, 1, 0);\r
-\r
-    tmp[0] = pi(e_key + 4, 1, 2, 3, 0) ^ kq[0]; \r
-    tmp[1] = pi(e_key + 4, 2, 3, 0, 1) ^ kq[1];\r
-    tmp[2] = pi(e_key + 4, 3, 0, 1, 2) ^ kq[2]; \r
-    tmp[3] = pi(e_key + 4, 0, 1, 2, 3) ^ kq[3];\r
-\r
-    gamma_tau(e_key[4], tmp, 0, 1, 0); \r
-    gamma_tau(e_key[5], tmp, 1, 0, 1);\r
-    gamma_tau(e_key[6], tmp, 2, 1, 0); \r
-    gamma_tau(e_key[7], tmp, 3, 0, 1);\r
-\r
-    t0 = e_key[0] ^ e_key[1] ^ e_key[2] ^ e_key[3];\r
-    t1 = e_key[4] ^ e_key[5] ^ e_key[6] ^ e_key[7];\r
-    \r
-    e_key[0] ^= t1; e_key[1] ^= t1;\r
-    e_key[2] ^= t1; e_key[3] ^= t1;\r
-    e_key[4] ^= t0; e_key[5] ^= t0;\r
-    e_key[6] ^= t0; e_key[7] ^= t0;\r
-\r
-    rc = 0x01010101; \r
-    h0_block( 0,  8, 16); h1_block(1, 16, 24); rc <<= 1;\r
-    h1_block( 2, 24,  8); h0_block(3,  8, 16); rc <<= 1;\r
-    h0_block( 4, 16, 24); h1_block(5, 24,  8); rc <<= 1;\r
-    h1_block( 6,  8, 16); h0_block(7, 16, 24); rc <<= 1;\r
-    h0_block( 8, 24,  8); h1_block(9,  8, 16); rc <<= 1;\r
-    h1_block(10, 16, 24);\r
-\r
-    for(i = 0; i < 13; ++i)\r
-    {\r
-        if(i & 1)\r
-        {\r
-            phi0(e_key + 4 * i, d_key + 48 - 4 * i);\r
-        }\r
-        else\r
-        {\r
-            phi1(e_key + 4 * i, d_key + 48 - 4 * i);\r
-        }\r
-    }\r
-\r
-    phi1(d_key + 48, d_key + 48);\r
-    phi1(e_key + 48, e_key + 48);\r
-\r
-    return l_key;\r
-};\r
-\r
-/* encrypt a block of text  */\r
-\r
-#define fr0(i,k)                                    \\r
-    b1[i] = s_tab[ (i)         ][byte(b0[0],i)] ^   \\r
-            s_tab[((i) + 1) & 3][byte(b0[1],i)] ^   \\r
-            s_tab[((i) + 2) & 3][byte(b0[2],i)] ^   \\r
-            s_tab[((i) + 3) & 3][byte(b0[3],i)] ^ (k)\r
-\r
-#define fr1(i,k)                                    \\r
-    b0[i] = s_tab[((i) + 1) & 3][byte(b1[0],i)] ^   \\r
-            s_tab[((i) + 2) & 3][byte(b1[1],i)] ^   \\r
-            s_tab[((i) + 3) & 3][byte(b1[2],i)] ^   \\r
-            s_tab[(i)          ][byte(b1[3],i)] ^ (k)\r
-\r
-#define f0_rnd(kp)                  \\r
-    fr0(0,(kp)[0]); fr0(1,(kp)[1]); \\r
-    fr0(2,(kp)[2]); fr0(3,(kp)[3])\r
-\r
-#define f1_rnd(kp)                  \\r
-    fr1(0,(kp)[0]); fr1(1,(kp)[1]); \\r
-    fr1(2,(kp)[2]); fr1(3,(kp)[3])\r
-\r
-void crypton_encrypt(CryptonContext *ctx,\r
-                    const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  b0[4], b1[4];\r
-    u4byte *e_key = ctx->l_key + 52;\r
-    u4byte *d_key = ctx->l_key;\r
-\r
-    b0[0] = in_blk[0] ^ e_key[0];\r
-    b0[1] = in_blk[1] ^ e_key[1];\r
-    b0[2] = in_blk[2] ^ e_key[2];\r
-    b0[3] = in_blk[3] ^ e_key[3];\r
-\r
-    f0_rnd(e_key +  4); f1_rnd(e_key +  8);\r
-    f0_rnd(e_key + 12); f1_rnd(e_key + 16);\r
-    f0_rnd(e_key + 20); f1_rnd(e_key + 24);\r
-    f0_rnd(e_key + 28); f1_rnd(e_key + 32);\r
-    f0_rnd(e_key + 36); f1_rnd(e_key + 40);\r
-    f0_rnd(e_key + 44);\r
-\r
-    gamma_tau(b0[0], b1, 0, 1, 0); \r
-    gamma_tau(b0[1], b1, 1, 0, 1); \r
-    gamma_tau(b0[2], b1, 2, 1, 0); \r
-    gamma_tau(b0[3], b1, 3, 0, 1);\r
-\r
-    out_blk[0] = b0[0] ^ e_key[48]; \r
-    out_blk[1] = b0[1] ^ e_key[49];\r
-    out_blk[2] = b0[2] ^ e_key[50]; \r
-    out_blk[3] = b0[3] ^ e_key[51];\r
-};\r
-\r
-/* decrypt a block of text  */\r
-\r
-void crypton_decrypt(CryptonContext *ctx,\r
-                    const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  b0[4], b1[4];\r
-    u4byte *e_key = ctx->l_key + 52;\r
-    u4byte *d_key = ctx->l_key;\r
-\r
-    b0[0] = in_blk[0] ^ d_key[0];\r
-    b0[1] = in_blk[1] ^ d_key[1];\r
-    b0[2] = in_blk[2] ^ d_key[2];\r
-    b0[3] = in_blk[3] ^ d_key[3];\r
-\r
-    f0_rnd(d_key +  4); f1_rnd(d_key +  8);\r
-    f0_rnd(d_key + 12); f1_rnd(d_key + 16);\r
-    f0_rnd(d_key + 20); f1_rnd(d_key + 24);\r
-    f0_rnd(d_key + 28); f1_rnd(d_key + 32);\r
-    f0_rnd(d_key + 36); f1_rnd(d_key + 40);\r
-    f0_rnd(d_key + 44);\r
-\r
-    gamma_tau(b0[0], b1, 0, 1, 0); \r
-    gamma_tau(b0[1], b1, 1, 0, 1); \r
-    gamma_tau(b0[2], b1, 2, 1, 0); \r
-    gamma_tau(b0[3], b1, 3, 0, 1);\r
-    \r
-    out_blk[0] = b0[0] ^ d_key[48]; \r
-    out_blk[1] = b0[1] ^ d_key[49];\r
-    out_blk[2] = b0[2] ^ d_key[50]; \r
-    out_blk[3] = b0[3] ^ d_key[51];\r
-};\r
diff --git a/lib/silccrypt/crypton.h b/lib/silccrypt/crypton.h
deleted file mode 100644 (file)
index 22c79b9..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
-
-  crypton.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1999 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef CRYPTON_H
-#define CRYPTON_H
-
-#include "crypton_internal.h"
-
-/* 
- * SILC Crypto API for Crypton
- */
-
-/* Sets the key for the cipher. */
-
-SILC_CIPHER_API_SET_KEY(crypton)
-{
-  crypton_set_key((CryptonContext *)context, (unsigned int *)key, keylen);
-  return TRUE;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-SILC_CIPHER_API_SET_KEY_WITH_STRING(crypton)
-{
-  SilcHash hash;
-  unsigned char key[16];
-
-  silc_hash_alloc("md5", &hash);
-  hash->make_hash(hash, string, stringlen, key);
-
-  crypton_set_key((CryptonContext *)context, key, sizeof(key));
-
-  silc_hash_free(hash);
-  memset(&key, 'F', sizeof(key));
-
-  return TRUE;
-}
-
-/* Returns the size of the cipher context. */
-
-SILC_CIPHER_API_CONTEXT_LEN(crypton)
-{
-  return sizeof(CryptonContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-SILC_CIPHER_API_ENCRYPT_CBC(crypton)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  crypton_encrypt((CryptonContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    crypton_encrypt((CryptonContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return TRUE;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-SILC_CIPHER_API_DECRYPT_CBC(crypton)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  crypton_decrypt((CryptonContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    crypton_decrypt((CryptonContext *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return TRUE;
-}
-
-#endif
diff --git a/lib/silccrypt/crypton_internal.h b/lib/silccrypt/crypton_internal.h
deleted file mode 100644 (file)
index 3dd7976..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-
-  crypton_internal.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1999 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef CRYPTON_INTERNAL_H
-#define CRYPTON_INTERNAL_H
-
-#include "ciphers_def.h"
-
-/* Cipher's context */
-typedef struct {
-  u4byte l_key[104];
-} CryptonContext;
-
-/* Prototypes */
-u4byte *crypton_set_key(CryptonContext *ctx,
-                       const u4byte in_key[], const u4byte key_len);
-void crypton_encrypt(CryptonContext *ctx,
-                    const u4byte in_blk[4], u4byte out_blk[4]);
-void crypton_decrypt(CryptonContext *ctx,
-                    const u4byte in_blk[4], u4byte out_blk[4]);
-
-#endif
diff --git a/lib/silccrypt/dfc.c b/lib/silccrypt/dfc.c
deleted file mode 100644 (file)
index ba1449c..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-/* Modified for SILC. -Pekka */\r
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         DFC designed by a team at CNRS and France Telecom            */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-/*                                                                      */\r
-/* My thanks go to Serge Vaudenay of the Ecole Normale Superieure       */\r
-/* for providing test vectors. This implementation has also been        */\r
-/* tested with an independent implementation by Dr Russell Bradford     */\r
-/* (Department of Mathematical Sciences, University of Bath, Bath,      */\r
-/* UK) and checks out.   My thanks go to Russell for his help in        */\r
-/* comparing our implementations and finding bugs (and for help in      */\r
-/* resolving 'endian' issues before test vectors became available).     */\r
-\r
-/* Timing data for DFC (dfc.c)\r
-\r
-Core timing without I/O endian conversion:\r
-\r
-Algorithm: dfc (dfc.c)\r
-\r
-128 bit key:\r
-Key Setup:    5222 cycles\r
-Encrypt:      1203 cycles =    21.3 mbits/sec\r
-Decrypt:      1244 cycles =    20.6 mbits/sec\r
-Mean:         1224 cycles =    20.9 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    5203 cycles\r
-Encrypt:      1288 cycles =    19.9 mbits/sec\r
-Decrypt:      1235 cycles =    20.7 mbits/sec\r
-Mean:         1262 cycles =    20.3 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    5177 cycles\r
-Encrypt:      1178 cycles =    21.7 mbits/sec\r
-Decrypt:      1226 cycles =    20.9 mbits/sec\r
-Mean:         1202 cycles =    21.3 mbits/sec\r
-\r
-Full timing with I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    5227 cycles\r
-Encrypt:      1247 cycles =    20.5 mbits/sec\r
-Decrypt:      1222 cycles =    20.9 mbits/sec\r
-Mean:         1235 cycles =    20.7 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    5252 cycles\r
-Encrypt:      1215 cycles =    21.1 mbits/sec\r
-Decrypt:      1265 cycles =    20.2 mbits/sec\r
-Mean:         1240 cycles =    20.6 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    5174 cycles\r
-Encrypt:      1247 cycles =    20.5 mbits/sec\r
-Decrypt:      1206 cycles =    21.2 mbits/sec\r
-Mean:         1227 cycles =    20.9 mbits/sec\r
-\r
-*/\r
-\r
-/* The EES string is as follows (the abstract contains an error in \r
-   the last line of this sequence which changes KC and KD):\r
-\r
-    0xb7e15162, 0x8aed2a6a, 0xbf715880, 0x9cf4f3c7, \r
-    0x62e7160f, 0x38b4da56, 0xa784d904, 0x5190cfef, \r
-    0x324e7738, 0x926cfbe5, 0xf4bf8d8d, 0x8c31d763,\r
-    0xda06c80a, 0xbb1185eb, 0x4f7c7b57, 0x57f59584, \r
-\r
-    0x90cfd47d, 0x7c19bb42, 0x158d9554, 0xf7b46bce, \r
-    0xd55c4d79, 0xfd5f24d6, 0x613c31c3, 0x839a2ddf,\r
-    0x8a9a276b, 0xcfbfa1c8, 0x77c56284, 0xdab79cd4, \r
-    0xc2b3293d, 0x20e9e5ea, 0xf02ac60a, 0xcc93ed87, \r
-\r
-    0x4422a52e, 0xcb238fee, 0xe5ab6add, 0x835fd1a0,\r
-    0x753d0a8f, 0x78e537d2, 0xb95bb79d, 0x8dcaec64, \r
-    0x2c1e9f23, 0xb829b5c2, 0x780bf387, 0x37df8bb3, \r
-    0x00d01334, 0xa0d0bd86, 0x45cbfa73, 0xa6160ffe,\r
-\r
-    0x393c48cb, 0xbbca060f, 0x0ff8ec6d, 0x31beb5cc, \r
-    0xeed7f2f0, 0xbb088017, 0x163bc60d, 0xf45a0ecb, \r
-    0x1bcd289b, 0x06cbbfea, 0x21ad08e1, 0x847f3f73,\r
-    0x78d56ced, 0x94640d6e, 0xf0d3d37b, 0xe67008e1, \r
-    \r
-    0x86d1bf27, 0x5b9b241d, 0xeb64749a, 0x47dfdfb9, \r
-\r
-    Where:\r
-\r
-    EES = RT(0) | RT(1) | ... | RT(63) | KD | KC\r
-\r
-    Note that the abstract describing DFC is written \r
-    in big endian notation with the most significant \r
-    digits of a sequence of digits placed at the low\r
-    index positions in arrays. This format is used\r
-    here and is only converted to machine format at\r
-    the point that maths is done on any numbers in \r
-    the round function.\r
-    \r
-    The key input is thus treated as an array of 32 \r
-    bit words numbered from 0..3, 0..5 or 0..7 \r
-    depending on key length.  The first (leftmost) \r
-    bit of this key string as defined in the DFC \r
-    abstract is the most significant bit of word 0 \r
-    and the rightmost bit of this string is the least \r
-    signicant bit of the highest numbered key word.\r
-\r
-    The input and output blocks for the cipher are \r
-    also treated as arrays of 32 bit words numbered\r
-    from 0..3.  The most significant bit of word 0 is\r
-    the 1st (leftmost) bit of the 128 bit input string \r
-    and the least significant bit of word 3 is the \r
-    last (rightmost) bit.\r
-    \r
-    Note that the inputs, the output and the key are\r
-    in Intel little endian format when BYTE_SWAP is \r
-    defined\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include "dfc_internal.h"\r
-\r
-/* The following arrays are all stored in big endian    */\r
-/* format with 32 bit words at lower array positions    */\r
-/* being more significant in multi-word values          */\r
-\r
-u4byte rt64[64] = \r
-{\r
-    0xb7e15162, 0x8aed2a6a, 0xbf715880, 0x9cf4f3c7, \r
-    0x62e7160f, 0x38b4da56, 0xa784d904, 0x5190cfef, \r
-    0x324e7738, 0x926cfbe5, 0xf4bf8d8d, 0x8c31d763,\r
-    0xda06c80a, 0xbb1185eb, 0x4f7c7b57, 0x57f59584, \r
-\r
-    0x90cfd47d, 0x7c19bb42, 0x158d9554, 0xf7b46bce, \r
-    0xd55c4d79, 0xfd5f24d6, 0x613c31c3, 0x839a2ddf,\r
-    0x8a9a276b, 0xcfbfa1c8, 0x77c56284, 0xdab79cd4, \r
-    0xc2b3293d, 0x20e9e5ea, 0xf02ac60a, 0xcc93ed87, \r
-\r
-    0x4422a52e, 0xcb238fee, 0xe5ab6add, 0x835fd1a0,\r
-    0x753d0a8f, 0x78e537d2, 0xb95bb79d, 0x8dcaec64, \r
-    0x2c1e9f23, 0xb829b5c2, 0x780bf387, 0x37df8bb3, \r
-    0x00d01334, 0xa0d0bd86, 0x45cbfa73, 0xa6160ffe,\r
-\r
-    0x393c48cb, 0xbbca060f, 0x0ff8ec6d, 0x31beb5cc, \r
-    0xeed7f2f0, 0xbb088017, 0x163bc60d, 0xf45a0ecb, \r
-    0x1bcd289b, 0x06cbbfea, 0x21ad08e1, 0x847f3f73,\r
-    0x78d56ced, 0x94640d6e, 0xf0d3d37b, 0xe67008e1, \r
-};\r
-\r
-u4byte  kc = 0xeb64749a;\r
-\r
-u4byte  kd2[2] = \r
-{\r
-    0x86d1bf27, 0x5b9b241d  \r
-};\r
-\r
-u4byte  ka2[6] = \r
-{\r
-    0xb7e15162, 0x8aed2a6a, \r
-    0xbf715880, 0x9cf4f3c7, \r
-    0x62e7160f, 0x38b4da56, \r
-};\r
-\r
-u4byte  kb2[6] =\r
-{\r
-    0xa784d904, 0x5190cfef, \r
-    0x324e7738, 0x926cfbe5, \r
-    0xf4bf8d8d, 0x8c31d763,\r
-};\r
-\r
-u4byte  ks8[8] = \r
-{   0xda06c80a, 0xbb1185eb, 0x4f7c7b57, 0x57f59584, \r
-    0x90cfd47d, 0x7c19bb42, 0x158d9554, 0xf7b46bce,\r
-};\r
-\r
-#define lo(x)   ((x) & 0x0000ffff)\r
-#define hi(x)   ((x) >> 16)\r
-\r
-#define mult_64(r,x,y)                                          \\r
-    x0 = lo(x[1]); x1 = hi(x[1]); x2 = lo(x[0]); x3 = hi(x[0]); \\r
-    y0 = lo(y[1]); y1 = hi(y[1]); y2 = lo(y[0]); y3 = hi(y[0]); \\r
-    t0 = x0 * y0; r[0] = lo(t0); c = hi(t0);                    \\r
-    t0 = x0 * y1; t1 = x1 * y0; c += lo(t0) + lo(t1);           \\r
-    r[0] += (c << 16); c = hi(c) + hi(t0) + hi(t1);             \\r
-    t0 = x0 * y2; t1 = x1 * y1; t2 = x2 * y0;                   \\r
-    c += lo(t0) + lo(t1) + lo(t2); r[1] = lo(c);                \\r
-    c = hi(c) + hi(t0) + hi(t1) + hi(t2);                       \\r
-    t0 = x0 * y3; t1 = x1 * y2; t2 = x2 * y1; t3 = x3 * y0;     \\r
-    c += lo(t0) + lo(t1) + lo(t2) + lo(t3); r[1] += (c << 16);  \\r
-    c = hi(c) + hi(t0) + hi(t1) + hi(t2) + hi(t3);              \\r
-    t0 = x1 * y3; t1 = x2 * y2; t2 = x3 * y1;                   \\r
-    c += lo(t0) + lo(t1) + lo(t2); r[2] = lo(c);                \\r
-    c = hi(c) + hi(t0) + hi(t1) + hi(t2);                       \\r
-    t0 = x2 * y3; t1 = x3 * y2; c += lo(t0) + lo(t1);           \\r
-    r[2] += (c << 16); c = hi(c) + hi(t0) + hi(t1);             \\r
-    r[3] = c + x3 * y3\r
-\r
-#define add_64(r,hi,lo)     \\r
-    if((r[0] += lo) < lo)   \\r
-        if(!++r[1])         \\r
-            if(!++r[2])     \\r
-                ++r[3];     \\r
-    if((r[1] += hi) < hi)   \\r
-        if(!++r[2])         \\r
-            ++r[3]\r
-    \r
-#define mult_13(r)                  \\r
-    c = 13 * lo((r)[0]);            \\r
-    d = hi((r)[0]);                 \\r
-    (r)[0] = lo(c);                 \\r
-    c = hi(c) + 13 * d;             \\r
-    (r)[0] += (c << 16);            \\r
-    c = hi(c) + 13 * lo((r)[1]);    \\r
-    d = hi((r)[1]);                 \\r
-    (r)[1] = lo(c);                 \\r
-    c = hi(c) + 13 * d;             \\r
-    (r)[1] += (c << 16);            \\r
-    (r)[2] = hi(c)\r
-\r
-/* Where necessary this is where conversion from big endian to  */\r
-/* little endian format is performed.  Since all the maths is   */\r
-/* little endian care is needed when 64 bit blocks are being    */\r
-/* used to get them in the right order by reversing the order   */\r
-/* in which these are stored. This applies to the key array     */\r
-/* which gives the two values A and B and to the constant KD.   */\r
-/* Since the input and output blocks are big endian we also     */\r
-/* have to invert the order of the 32 bit words in the 64 bit   */\r
-/* blocks being processed.                                      */ \r
-\r
-void r_fun(u4byte outp[2], const u4byte inp[2], const u4byte key[4])\r
-{   u4byte  acc[5], x0, x1, x2, x3, y0, y1, y2, y3, t0, t1, t2, t3, c, d;\r
-\r
-    mult_64(acc, inp, key);  add_64(acc, key[2], key[3]);\r
-\r
-    /* we need the value in the accumulator mod 2^64 + 13 so if */\r
-    /* the accumulator value is hi * 2^64 + lo we need to find  */\r
-    /* a k value such that r = hi * 2^64 + lo - k * (2^64 + 13) */\r
-    /* is 0 <= r < 2^64 + 13.  We can see that k will be close  */\r
-    /* to hi in value - it may equal hi but will not be greater */\r
-    /* and we can let k = hi - e with e >= 0 so that r is given */\r
-    /* by r = e * (2^64 + 13) + lo - 13 * hi. If we compute the */\r
-    /* lo - 13 * hi value, the overflow into the top 64 bits of */\r
-    /* the accumulator has to be 'zeroed' by the e * (2^64 + 13)*/\r
-    /* term and this sets the e value (in fact such an overlow  */\r
-    /* is only removed when the lower word is higher than 12).  */\r
-\r
-    mult_13(acc + 2);   /* multiply top of accumulator by 13    */\r
-\r
-    /* calculate lo - 13 * hi in acc[0] and acc[1] with any     */\r
-    /* overflow into top 64 bits in c                           */\r
-\r
-    d = acc[0]; acc[0] -= acc[2]; c = (acc[0] > d ? 1 : 0);\r
-\r
-    d = acc[1]; acc[1] -= acc[3] + c;\r
-    c = (acc[1] > d ? 1 : (acc[1] == d ? c : 0));\r
-\r
-    c = 13 * (acc[4] + c);  /* overflow into top 64 bits of acc */\r
-\r
-    if(((acc[0] += c) < c) && !(++acc[1]))\r
-    {\r
-        if(acc[0] > 12)\r
-\r
-            acc[0] -= 13;\r
-    }\r
-\r
-    /* do the confusion permutation */\r
-\r
-    d = acc[1] ^ kc; c = acc[0] ^ rt64[acc[1] >> 26];  \r
-    \r
-    c += kd2[0] + ((d += kd2[1]) < kd2[1] ? 1 : 0);\r
-\r
-    outp[0] ^= c; outp[1] ^= d; \r
-};\r
-\r
-u4byte *dfc_set_key(DfcContext *ctx,\r
-                   const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u4byte  i, lk[32], rk[4];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    for(i = 0; i < key_len / 32; ++i)\r
-\r
-        lk[i] = io_swap(in_key[i]);\r
-\r
-    /* pad the key with the KS array            */\r
-\r
-    for(i = 0; i < 8 - key_len / 32; ++i)    /* K|KS */\r
-\r
-        lk[i + key_len / 32] = ks8[i];\r
-\r
-    /* do the reordering of the key parameters  */\r
-    /* the OAP[1]|OBP[1]|OAP[2]... sequence is  */\r
-    /* at lk[0]... and the other at lk[16]...   */\r
-    \r
-    lk[18] = lk[5]; lk[19] = lk[2]; /* EBP */ \r
-    lk[16] = lk[1]; lk[17] = lk[6]; /* EAP */\r
-    lk[ 2] = lk[4]; lk[ 3] = lk[3]; /* OBP */\r
-    lk[ 0] = lk[0]; lk[ 1] = lk[7]; /* OAP */\r
-\r
-    /* create other elements using KA and KB    */\r
-\r
-    for(i = 0; i < 6; i += 2)\r
-    {\r
-        lk[i + i +   4] = lk[ 0] ^ ka2[i];      /* OAP[i] ms */\r
-        lk[i + i +   5] = lk[ 1] ^ ka2[i + 1];  /* OAP[i] ls */\r
-        lk[i + i +   6] = lk[ 2] ^ kb2[i];      /* OBP[i] ms */\r
-        lk[i + i +   7] = lk[ 3] ^ kb2[i + 1];  /* OBP[i] ls */\r
-        lk[i + i +  20] = lk[16] ^ ka2[i];      /* EAP[i] ms */\r
-        lk[i + i +  21] = lk[17] ^ ka2[i + 1];  /* EAP[i] ls */\r
-        lk[i + i +  22] = lk[18] ^ kb2[i];      /* EBP[i] ms */\r
-        lk[i + i +  23] = lk[19] ^ kb2[i + 1];  /* EBP[i] ls */\r
-    }\r
-\r
-    rk[0] = rk[1] = rk[2] = rk[3] = 0;\r
-\r
-    /* do the 4 round key mixing encryption     */\r
-\r
-    for(i = 0; i < 32; i += 8)\r
-    {\r
-        r_fun(rk, rk + 2, lk);      /*  R2|R1   */\r
-        r_fun(rk + 2, rk, lk +  4); /*  R2|R3   */\r
-        r_fun(rk, rk + 2, lk +  8); /*  R4|R3   */\r
-        r_fun(rk + 2, rk, lk + 12); /*  R4|R5   */\r
-\r
-        /* keep key in big endian format with   */\r
-        /* the most significant 32 bit words    */\r
-        /* first (lowest) in the key schedule   */\r
-        /* - note that the upper and lower 64   */\r
-        /* bit blocks are in inverse order at   */\r
-        /* this point in the loop               */\r
-\r
-        l_key[i + 0] = rk[2]; l_key[i + 1] = rk[3]; \r
-        l_key[i + 2] = rk[0]; l_key[i + 3] = rk[1]; \r
-\r
-        r_fun(rk + 2, rk, lk + 16); /*  R1|R2   */\r
-        r_fun(rk, rk + 2, lk + 20); /*  R3|R2   */\r
-        r_fun(rk + 2, rk, lk + 24); /*  R3|R4   */  \r
-        r_fun(rk, rk + 2, lk + 28); /*  R5|R4   */\r
-\r
-        l_key[i + 4] = rk[0]; l_key[i + 5] = rk[1]; \r
-        l_key[i + 6] = rk[2]; l_key[i + 7] = rk[3];\r
-    }\r
-    \r
-    return l_key;\r
-};\r
-\r
-void dfc_encrypt(DfcContext *ctx,\r
-                const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  blk[4];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    /* the input/output format is big endian -  */\r
-    /* any reversals needed are performed when  */\r
-    /* maths is done in the round function      */\r
-\r
-    blk[0] = io_swap(in_blk[0]); blk[1] = io_swap(in_blk[1]);\r
-    blk[2] = io_swap(in_blk[2]); blk[3] = io_swap(in_blk[3]);\r
-\r
-    r_fun(blk, blk + 2, l_key +  0); /*  R2|R1  */ \r
-    r_fun(blk + 2, blk, l_key +  4); /*  R2|R3  */\r
-    r_fun(blk, blk + 2, l_key +  8); /*  R4|R3  */\r
-    r_fun(blk + 2, blk, l_key + 12); /*  R4|R5  */\r
-    r_fun(blk, blk + 2, l_key + 16); /*  R6|R5  */\r
-    r_fun(blk + 2, blk, l_key + 20); /*  R6|R7  */\r
-    r_fun(blk, blk + 2, l_key + 24); /*  R8|R7  */\r
-    r_fun(blk + 2, blk, l_key + 28); /*  R8|R9  */\r
-\r
-    /* swap order to obtain the result R9|R8    */\r
-\r
-    out_blk[0] = io_swap(blk[2]); out_blk[1] = io_swap(blk[3]);\r
-    out_blk[2] = io_swap(blk[0]); out_blk[3] = io_swap(blk[1]);\r
-};\r
-\r
-void dfc_decrypt(DfcContext *ctx,\r
-                const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  blk[4];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    /* the input/output format is big endian -  */\r
-    /* any reversals needed are performed when  */\r
-    /* maths is done in the round function      */\r
-\r
-    blk[0] = io_swap(in_blk[0]); blk[1] = io_swap(in_blk[1]);\r
-    blk[2] = io_swap(in_blk[2]); blk[3] = io_swap(in_blk[3]);\r
-\r
-    r_fun(blk, blk + 2, l_key + 28); /*  R7|R8  */\r
-    r_fun(blk + 2, blk, l_key + 24); /*  R7|R6  */\r
-    r_fun(blk, blk + 2, l_key + 20); /*  R5|R6  */\r
-    r_fun(blk + 2, blk, l_key + 16); /*  R5|R4  */\r
-    r_fun(blk, blk + 2, l_key + 12); /*  R3|R4  */\r
-    r_fun(blk + 2, blk, l_key +  8); /*  R3|R2  */\r
-    r_fun(blk, blk + 2, l_key +  4); /*  R1|R2  */\r
-    r_fun(blk + 2, blk, l_key     ); /*  R1|R0  */ \r
-\r
-    /* swap order to obtain the result R1|R0    */\r
-\r
-    out_blk[0] = io_swap(blk[2]); out_blk[1] = io_swap(blk[3]);\r
-    out_blk[2] = io_swap(blk[0]); out_blk[3] = io_swap(blk[1]);   \r
-};\r
diff --git a/lib/silccrypt/dfc.h b/lib/silccrypt/dfc.h
deleted file mode 100644 (file)
index 15be620..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
-
-  dfc.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef DFC_H
-#define DFC_H
-
-#include "dfc_internal.h"
-
-/* 
- * SILC Crypto API for DFC
- */
-
-/* Sets the key for the cipher. */
-
-inline int silc_dfc_init(void *context, 
-                        const unsigned char *key, 
-                        unsigned int keylen)
-{
-  dfc_set_key((DfcContext *)context, (unsigned int *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-inline int silc_dfc_set_string_as_key(void *context, 
-                                     const unsigned char *string,
-                                     unsigned int keylen)
-{
-  /*  SilcHash hash;
-  unsigned char key[16];
-
-  silc_hash_alloc("md5", &hash);
-  hash->make_hash(hash, string, stringlen, key);
-
-  dfc_set_key((DfcContext *)context, key, sizeof(key));
-
-  silc_hash_free(hash);
-  memset(&key, 'F', sizeof(key));
-  */
-  return TRUE;
-}
-
-/* Returns the size of the cipher context. */
-
-inline unsigned int silc_dfc_context_len()
-{
-  return sizeof(DfcContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-inline int silc_dfc_encrypt_cbc(void *context,
-                               const unsigned char *src,
-                               unsigned char *dst,
-                               unsigned int len,
-                               unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  dfc_encrypt((DfcContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    dfc_encrypt((DfcContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-inline int silc_dfc_decrypt_cbc(void *context,
-                               const unsigned char *src,
-                               unsigned char *dst,
-                               unsigned int len,
-                               unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  dfc_decrypt((DfcContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    dfc_decrypt((DfcContext *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-#endif
diff --git a/lib/silccrypt/dfc_internal.h b/lib/silccrypt/dfc_internal.h
deleted file mode 100644 (file)
index 5ab4e0a..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-
-  dfc_internal.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef DFC_INTERNAL_H
-#define DFC_INTERNAL_H
-
-/* Cipher's context */
-typedef struct {
-  u4byte l_key[32];
-} DfcContext;
-
-/* Prototypes */
-u4byte *dfc_set_key(DfcContext *ctx,
-                   const u4byte in_key[], const u4byte key_len);
-void dfc_encrypt(DfcContext *ctx,
-                const u4byte in_blk[4], u4byte out_blk[4]);
-void dfc_decrypt(DfcContext *ctx,
-                const u4byte in_blk[4], u4byte out_blk[4]);
-
-#endif
diff --git a/lib/silccrypt/e2.c b/lib/silccrypt/e2.c
deleted file mode 100644 (file)
index 8cc8acd..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-/* Modified for SILC. -Pekka */\r
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         E2 by Nippon Telegraph and Telephone (NTT) Japan             */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-/*                                                                      */\r
-/* In accordance with the wishes of NTT this implementation is made     */\r
-/* available for academic and study purposes only.   I gratefully       */\r
-/* acknowledge the contributions made by Kazumaro Aoki of NTT Japan     */\r
-/* for ways to increase the speed of this implementation.               */\r
-\r
-/* Timing data for E2 (e28.c)\r
-\r
-Core timing without I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    9473 cycles\r
-Encrypt:       687 cycles =    37.3 mbits/sec\r
-Decrypt:       691 cycles =    37.0 mbits/sec\r
-Mean:          689 cycles =    37.2 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    9540 cycles\r
-Encrypt:       696 cycles =    36.8 mbits/sec\r
-Decrypt:       693 cycles =    36.9 mbits/sec\r
-Mean:          695 cycles =    36.9 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    9913 cycles\r
-Encrypt:       691 cycles =    37.0 mbits/sec\r
-Decrypt:       706 cycles =    36.3 mbits/sec\r
-Mean:          699 cycles =    36.6 mbits/sec\r
-\r
-Full timing with I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    9598 cycles\r
-Encrypt:       730 cycles =    35.1 mbits/sec\r
-Decrypt:       723 cycles =    35.4 mbits/sec\r
-Mean:          727 cycles =    35.2 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    9393 cycles\r
-Encrypt:       730 cycles =    35.1 mbits/sec\r
-Decrypt:       720 cycles =    35.6 mbits/sec\r
-Mean:          725 cycles =    35.3 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    9720 cycles\r
-Encrypt:       727 cycles =    35.2 mbits/sec\r
-Decrypt:       721 cycles =    35.5 mbits/sec\r
-Mean:          724 cycles =    35.4 mbits/sec\r
-\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include "e2_internal.h"\r
-\r
-u1byte  s_box[] =\r
-{\r
- 0xe1, 0x42, 0x3e, 0x81, 0x4e, 0x17, 0x9e, 0xfd, 0xb4, 0x3f, 0x2c, 0xda,\r
- 0x31, 0x1e, 0xe0, 0x41, 0xcc, 0xf3, 0x82, 0x7d, 0x7c, 0x12, 0x8e, 0xbb,\r
- 0xe4, 0x58, 0x15, 0xd5, 0x6f, 0xe9, 0x4c, 0x4b, 0x35, 0x7b, 0x5a, 0x9a,\r
- 0x90, 0x45, 0xbc, 0xf8, 0x79, 0xd6, 0x1b, 0x88, 0x02, 0xab, 0xcf, 0x64,\r
- 0x09, 0x0c, 0xf0, 0x01, 0xa4, 0xb0, 0xf6, 0x93, 0x43, 0x63, 0x86, 0xdc,\r
- 0x11, 0xa5, 0x83, 0x8b, 0xc9, 0xd0, 0x19, 0x95, 0x6a, 0xa1, 0x5c, 0x24,\r
- 0x6e, 0x50, 0x21, 0x80, 0x2f, 0xe7, 0x53, 0x0f, 0x91, 0x22, 0x04, 0xed,\r
- 0xa6, 0x48, 0x49, 0x67, 0xec, 0xf7, 0xc0, 0x39, 0xce, 0xf2, 0x2d, 0xbe,\r
- 0x5d, 0x1c, 0xe3, 0x87, 0x07, 0x0d, 0x7a, 0xf4, 0xfb, 0x32, 0xf5, 0x8c,\r
- 0xdb, 0x8f, 0x25, 0x96, 0xa8, 0xea, 0xcd, 0x33, 0x65, 0x54, 0x06, 0x8d,\r
- 0x89, 0x0a, 0x5e, 0xd9, 0x16, 0x0e, 0x71, 0x6c, 0x0b, 0xff, 0x60, 0xd2,\r
- 0x2e, 0xd3, 0xc8, 0x55, 0xc2, 0x23, 0xb7, 0x74, 0xe2, 0x9b, 0xdf, 0x77,\r
- 0x2b, 0xb9, 0x3c, 0x62, 0x13, 0xe5, 0x94, 0x34, 0xb1, 0x27, 0x84, 0x9f,\r
- 0xd7, 0x51, 0x00, 0x61, 0xad, 0x85, 0x73, 0x03, 0x08, 0x40, 0xef, 0x68,\r
- 0xfe, 0x97, 0x1f, 0xde, 0xaf, 0x66, 0xe8, 0xb8, 0xae, 0xbd, 0xb3, 0xeb,\r
- 0xc6, 0x6b, 0x47, 0xa9, 0xd8, 0xa7, 0x72, 0xee, 0x1d, 0x7e, 0xaa, 0xb6,\r
- 0x75, 0xcb, 0xd4, 0x30, 0x69, 0x20, 0x7f, 0x37, 0x5b, 0x9d, 0x78, 0xa3,\r
- 0xf1, 0x76, 0xfa, 0x05, 0x3d, 0x3a, 0x44, 0x57, 0x3b, 0xca, 0xc7, 0x8a,\r
- 0x18, 0x46, 0x9c, 0xbf, 0xba, 0x38, 0x56, 0x1a, 0x92, 0x4d, 0x26, 0x29,\r
- 0xa2, 0x98, 0x10, 0x99, 0x70, 0xa0, 0xc5, 0x28, 0xc1, 0x6d, 0x14, 0xac,\r
- 0xf9, 0x5f, 0x4f, 0xc4, 0xc3, 0xd1, 0xfc, 0xdd, 0xb2, 0x59, 0xe6, 0xb5,\r
- 0x36, 0x52, 0x4a, 0x2a\r
-};\r
-\r
-u4byte  l_box[4][256];\r
-u4byte  lb_init = 0;\r
-\r
-#define v_0     0x67452301\r
-#define v_1     0xefcdab89\r
-\r
-/* s_fun(s_fun(s_fun(v)))           */\r
-\r
-#define k2_0    0x30d32e58\r
-#define k2_1    0xb89e4984\r
-\r
-/* s_fun(s_fun(s_fun(s_fun(v))))    */\r
-\r
-#define k3_0    0x0957cfec\r
-#define k3_1    0xd800502e\r
-\r
-#define bp_fun(a,b,c,d,e,f,g,h)     \\r
-    u = (e ^ g) & 0x00ffff00;       \\r
-    v = (f ^ h) & 0x0000ffff;       \\r
-    a = e ^ u; c = g ^ u;           \\r
-    b = f ^ v; d = h ^ v\r
-\r
-#define ibp_fun(a,b,c,d,e,f,g,h)    \\r
-    u = (e ^ g) & 0xff0000ff;       \\r
-    v = (f ^ h) & 0xffff0000;       \\r
-    a = e ^ u; c = g ^ u;           \\r
-    b = f ^ v; d = h ^ v\r
-\r
-#define bp2_fun(x,y)                \\r
-    w = (x ^ y) & 0x00ff00ff;       \\r
-    x ^= w; y ^= w;                 \\r
-\r
-#define s_fun(x,y)          \\r
-    p = x; q = x >> 8;      \\r
-    r = y; s = y >> 8;      \\r
-    x  = l_box[0][r & 255]; \\r
-    y  = l_box[0][p & 255]; \\r
-    p >>=  16; r >>=  16;   \\r
-    x |= l_box[1][q & 255]; \\r
-    y |= l_box[1][s & 255]; \\r
-    x |= l_box[2][r & 255]; \\r
-    y |= l_box[2][p & 255]; \\r
-    x |= l_box[3][p >> 8];  \\r
-    y |= l_box[3][r >> 8]\r
-\r
-#define sx_fun(x,y)         \\r
-    p = x >>  8;            \\r
-    q = x >> 16;            \\r
-    x  = l_box[0][x & 255]; \\r
-    x |= l_box[1][p & 255]; \\r
-    x |= l_box[2][q & 255]; \\r
-    x |= l_box[3][q >> 8];  \\r
-    p = y >>  8;            \\r
-    q = y >> 16;            \\r
-    y  = l_box[0][y & 255]; \\r
-    y |= l_box[1][p & 255]; \\r
-    y |= l_box[2][q & 255]; \\r
-    y |= l_box[3][q >> 8]\r
-\r
-#define spx_fun(x,y)        \\r
-    sx_fun(x,y);            \\r
-    y ^= x;                 \\r
-    x ^= rotr(y, 16);       \\r
-    y ^= rotr(x, 8);        \\r
-    x ^= y\r
-\r
-#define sp_fun(x,y)         \\r
-    s_fun(x,y);             \\r
-    y ^= x;                 \\r
-    x ^= rotr(y, 16);       \\r
-    y ^= rotr(x, 8);        \\r
-    x ^= y\r
-\r
-#define sr_fun(x,y)         \\r
-    p = x; q = x >> 8;      \\r
-    r = y; s = y >> 8;      \\r
-    y  = l_box[1][p & 255]; \\r
-    x  = l_box[1][r & 255]; \\r
-    p >>= 16; r >>= 16;     \\r
-    x |= l_box[2][q & 255]; \\r
-    y |= l_box[2][s & 255]; \\r
-    y |= l_box[3][p & 255]; \\r
-    x |= l_box[3][r & 255]; \\r
-    x |= l_box[0][r >>  8]; \\r
-    y |= l_box[0][p >>  8]\r
-\r
-#define f_fun(a,b,c,d,k)            \\r
-    u = c ^ *(k); v = d ^ *(k + 1); \\r
-    sp_fun(u, v);                   \\r
-    u ^= *(k + 2); v ^= *(k + 3);   \\r
-    sr_fun(u, v);                   \\r
-    a ^= v;                         \\r
-    b ^= u\r
-\r
-#define byte_adr(x,n)   *(((u1byte*)&x)+n)\r
-\r
-u4byte  mod_inv(u4byte x)\r
-{   u4byte  y1, y2, a, b, q;\r
-\r
-    y1 = ~((-x) / x); y2 = 1;\r
-\r
-    a = x; b = y1 * x;\r
-\r
-    for(;;)\r
-    {\r
-        q = a / b; \r
-        \r
-        if((a -= q * b) == 0)\r
-\r
-            return (x * y1 == 1 ? y1 : -y1);\r
-        \r
-        y2 -= q * y1;\r
-\r
-        q = b / a; \r
-        \r
-        if((b -= q * a) == 0)\r
-        \r
-            return (x * y2 == 1 ? y2 : -y2);\r
-\r
-        y1 -= q * y2;\r
-    }\r
-};\r
-\r
-void g_fun(u4byte y[8], u4byte l[8], u4byte v[2])\r
-{   u4byte  p,q;\r
-\r
-    spx_fun(y[0], y[1]); spx_fun(v[0], v[1]); \r
-    l[0] = v[0] ^= y[0]; l[1] = v[1] ^= y[1];\r
-\r
-    spx_fun(y[2], y[3]); spx_fun(v[0], v[1]); \r
-    l[2] = v[0] ^= y[2]; l[3] = v[1] ^= y[3];\r
-\r
-    spx_fun(y[4], y[5]); spx_fun(v[0], v[1]);  \r
-    l[4] = v[0] ^= y[4]; l[5] = v[1] ^= y[5];\r
-\r
-    spx_fun(y[6], y[7]); spx_fun(v[0], v[1]); \r
-    l[6] = v[0] ^= y[6]; l[7] = v[1] ^= y[7];\r
-};\r
-\r
-u4byte *e2_set_key(E2Context *ctx,\r
-                  const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u4byte  lk[8], v[2], lout[8];\r
-    u4byte  i, j, k, w;\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    if(!lb_init)\r
-    {\r
-        for(i = 0; i < 256; ++i)\r
-        {\r
-            l_box[0][i] = ((u4byte)(s_box[i]));\r
-            l_box[1][i] = ((u4byte)(s_box[i])) <<  8;\r
-            l_box[2][i] = ((u4byte)(s_box[i])) << 16;\r
-            l_box[3][i] = ((u4byte)(s_box[i])) << 24;\r
-        }\r
-\r
-        lb_init = 1;\r
-    }\r
-\r
-    v[0] = bswap(v_0); v[1] = bswap(v_1);\r
-\r
-    lk[0] = io_swap(in_key[0]); lk[1] = io_swap(in_key[1]);\r
-    lk[2] = io_swap(in_key[2]); lk[3] = io_swap(in_key[3]);\r
-\r
-    lk[4] = io_swap(key_len > 128 ? in_key[4] : k2_0);\r
-    lk[5] = io_swap(key_len > 128 ? in_key[5] : k2_1);\r
-\r
-    lk[6] = io_swap(key_len > 192 ? in_key[6] : k3_0);\r
-    lk[7] = io_swap(key_len > 192 ? in_key[7] : k3_1);\r
-\r
-    g_fun(lk, lout, v);\r
-\r
-    for(i = 0; i < 8; ++i)\r
-    {\r
-        g_fun(lk, lout, v);\r
-\r
-        for(j = 0; j < 4; ++j)\r
-        {\r
-            // this is complex because of a byte swap in each 32 bit output word\r
-\r
-            k = 2 * (48 - 16 * j + 2 * (i / 2) - i % 2);\r
-\r
-            ((u1byte*)l_key)[k + 3]   = ((u1byte*)lout)[j];\r
-            ((u1byte*)l_key)[k + 2]   = ((u1byte*)lout)[j + 16];\r
-\r
-            ((u1byte*)l_key)[k + 19]  = ((u1byte*)lout)[j +  8];\r
-            ((u1byte*)l_key)[k + 18]  = ((u1byte*)lout)[j + 24];\r
-\r
-            ((u1byte*)l_key)[k + 131] = ((u1byte*)lout)[j +  4];\r
-            ((u1byte*)l_key)[k + 130] = ((u1byte*)lout)[j + 20];\r
-\r
-            ((u1byte*)l_key)[k + 147] = ((u1byte*)lout)[j + 12];\r
-            ((u1byte*)l_key)[k + 146] = ((u1byte*)lout)[j + 28];\r
-        }\r
-    }\r
-\r
-    for(i = 52; i < 60; ++i)\r
-    {\r
-        l_key[i] |= 1; l_key[i + 12] = mod_inv(l_key[i]);\r
-    }\r
-\r
-    for(i = 0; i < 48; i += 4)\r
-    {\r
-        bp2_fun(l_key[i], l_key[i + 1]);\r
-    }\r
-\r
-    return (u4byte*)&l_key;\r
-};\r
-\r
-void e2_encrypt(E2Context *ctx, const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte      a,b,c,d,p,q,r,s,u,v;\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    p = io_swap(in_blk[0]); q = io_swap(in_blk[1]); \r
-    r = io_swap(in_blk[2]); s = io_swap(in_blk[3]);\r
-    \r
-    p ^= l_key[48]; q ^= l_key[49]; r ^= l_key[50]; s ^= l_key[51]; \r
-    p *= l_key[52]; q *= l_key[53]; r *= l_key[54]; s *= l_key[55];\r
-\r
-    bp_fun(a, b, c, d, p, q, r, s);\r
-\r
-    f_fun(a, b, c, d, l_key);\r
-    f_fun(c, d, a, b, l_key +  4);\r
-    f_fun(a, b, c, d, l_key +  8);\r
-    f_fun(c, d, a, b, l_key + 12);\r
-    f_fun(a, b, c, d, l_key + 16);\r
-    f_fun(c, d, a, b, l_key + 20);\r
-    f_fun(a, b, c, d, l_key + 24);\r
-    f_fun(c, d, a, b, l_key + 28);\r
-    f_fun(a, b, c, d, l_key + 32);\r
-    f_fun(c, d, a, b, l_key + 36);\r
-    f_fun(a, b, c, d, l_key + 40);\r
-    f_fun(c, d, a, b, l_key + 44);\r
-\r
-    ibp_fun(p, q, r, s, a, b, c, d);        \r
-    \r
-    p *= l_key[68]; q *= l_key[69]; r *= l_key[70]; s *= l_key[71]; \r
-    p ^= l_key[60]; q ^= l_key[61]; r ^= l_key[62]; s ^= l_key[63];\r
-    \r
-    out_blk[0] = io_swap(p); out_blk[1] = io_swap(q);\r
-    out_blk[2] = io_swap(r); out_blk[3] = io_swap(s);\r
-};\r
-\r
-void e2_decrypt(E2Context *ctx, const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte      a,b,c,d,p,q,r,s,u,v;\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    p = io_swap(in_blk[0]); q = io_swap(in_blk[1]); \r
-    r = io_swap(in_blk[2]); s = io_swap(in_blk[3]);\r
-\r
-    p ^= l_key[60]; q ^= l_key[61]; r ^= l_key[62]; s ^= l_key[63];\r
-    p *= l_key[56]; q *= l_key[57]; r *= l_key[58]; s *= l_key[59];\r
-\r
-    bp_fun(a, b, c, d, p, q, r, s);\r
-\r
-    f_fun(a, b, c, d, l_key + 44);\r
-    f_fun(c, d, a, b, l_key + 40);\r
-\r
-    f_fun(a, b, c, d, l_key + 36);\r
-    f_fun(c, d, a, b, l_key + 32);\r
-\r
-    f_fun(a, b, c, d, l_key + 28);\r
-    f_fun(c, d, a, b, l_key + 24);\r
-    \r
-    f_fun(a, b, c, d, l_key + 20);\r
-    f_fun(c, d, a, b, l_key + 16);\r
-\r
-    f_fun(a, b, c, d, l_key + 12);\r
-    f_fun(c, d, a, b, l_key +  8);\r
-    \r
-    f_fun(a, b, c, d, l_key +  4);\r
-    f_fun(c, d, a, b, l_key);\r
-\r
-    ibp_fun(p, q, r, s, a, b, c, d);        \r
-    \r
-    p *= l_key[64]; q *= l_key[65]; r *= l_key[66]; s *= l_key[67]; \r
-    p ^= l_key[48]; q ^= l_key[49]; r ^= l_key[50]; s ^= l_key[51];\r
-\r
-    out_blk[0] = io_swap(p); out_blk[1] = io_swap(q); \r
-    out_blk[2] = io_swap(r); out_blk[3] = io_swap(s);\r
-};\r
diff --git a/lib/silccrypt/e2.h b/lib/silccrypt/e2.h
deleted file mode 100644 (file)
index 600fafe..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-
-  e2.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef E2_H
-#define E2_H
-
-#include "e2_internal.h"
-
-/* 
- * SILC Crypto API for E2
- */
-
-/* Sets the key for the cipher. */
-
-inline int silc_e2_init(void *context, 
-                       const unsigned char *key, 
-                       size_t keylen)
-{
-  e2_set_key((E2Context *)context, (unsigned int *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-inline int silc_e2_set_string_as_key(void *context, 
-                                    const unsigned char *string,
-                                    size_t keylen)
-{
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return 1;
-}
-
-/* Returns the size of the cipher context. */
-
-inline size_t silc_e2_context_len()
-{
-  return sizeof(E2Context);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-inline int silc_e2_encrypt_cbc(void *context,
-                              const unsigned char *src,
-                              unsigned char *dst,
-                              size_t len,
-                              unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  e2_encrypt((E2Context *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    e2_encrypt((E2Context *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-inline int silc_e2_decrypt_cbc(void *context,
-                              const unsigned char *src,
-                              unsigned char *dst,
-                              size_t len,
-                              unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  e2_decrypt((E2Context *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    e2_decrypt((E2Context *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-#endif
diff --git a/lib/silccrypt/loki.c b/lib/silccrypt/loki.c
deleted file mode 100644 (file)
index c326f27..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/* Modified for SILC. -Pekka */\r
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         LOKI97 by Brown and Pieprzyk                                 */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-\r
-/* Timing data for LOKI97 (loki.c)\r
-\r
-Core timing without I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    7430 cycles\r
-Encrypt:      2134 cycles =    12.0 mbits/sec\r
-Decrypt:      2192 cycles =    11.7 mbits/sec\r
-Mean:         2163 cycles =    11.8 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    7303 cycles\r
-Encrypt:      2138 cycles =    12.0 mbits/sec\r
-Decrypt:      2189 cycles =    11.7 mbits/sec\r
-Mean:         2164 cycles =    11.8 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    7166 cycles\r
-Encrypt:      2131 cycles =    12.0 mbits/sec\r
-Decrypt:      2184 cycles =    11.7 mbits/sec\r
-Mean:         2158 cycles =    11.9 mbits/sec\r
-\r
-Full timing with I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    7582 cycles\r
-Encrypt:      2174 cycles =    11.8 mbits/sec\r
-Decrypt:      2235 cycles =    11.5 mbits/sec\r
-Mean:         2205 cycles =    11.6 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    7477 cycles\r
-Encrypt:      2167 cycles =    11.8 mbits/sec\r
-Decrypt:      2223 cycles =    11.5 mbits/sec\r
-Mean:         2195 cycles =    11.7 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    7365 cycles\r
-Encrypt:      2177 cycles =    11.8 mbits/sec\r
-Decrypt:      2194 cycles =    11.7 mbits/sec\r
-Mean:         2186 cycles =    11.7 mbits/sec\r
-\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include "loki_internal.h"\r
-\r
-#define S1_SIZE     13\r
-#define S1_LEN      (1 << S1_SIZE)\r
-#define S1_MASK     (S1_LEN - 1)\r
-#define S1_HMASK    (S1_MASK & ~0xff)\r
-#define S1_POLY     0x2911\r
-\r
-#define S2_SIZE     11\r
-#define S2_LEN      (1 << S2_SIZE)\r
-#define S2_MASK     (S2_LEN - 1)\r
-#define S2_HMASK    (S2_MASK & ~0xff)\r
-#define S2_POLY     0x0aa7\r
-\r
-#define io_swap(x)  ((x))\r
-\r
-u4byte  delta[2] = { 0x7f4a7c15, 0x9e3779b9 };\r
-\r
-u1byte  sb1[S1_LEN];    // GF(2^11) S box\r
-u1byte  sb2[S2_LEN];    // GF(2^11) S box\r
-u4byte  prm[256][2];\r
-u4byte  init_done = 0;\r
-\r
-#define add_eq(x,y)    (x)[1] += (y)[1] + (((x)[0] += (y)[0]) < (y)[0] ? 1 : x)\r
-#define sub_eq(x,y)    xs = (x)[0]; (x)[1] -= (y)[1] + (((x)[0] -= (y)[0]) > xs ? 1 : 0)   \r
-\r
-u4byte ff_mult(u4byte a, u4byte b, u4byte tpow, u4byte mpol)\r
-{   u4byte  r, s, m;\r
-\r
-    r = s = 0; m = (1 << tpow); \r
-\r
-    while(b)\r
-    {\r
-        if(b & 1)\r
-        \r
-            s ^= a;\r
-            \r
-        b >>= 1; a <<= 1;\r
-        \r
-        if(a & m)\r
-        \r
-            a ^= mpol;\r
-    }\r
-\r
-    return s;\r
-};\r
-\r
-void init_tables(void)\r
-{   u4byte  i, j, v;\r
-\r
-    // initialise S box 1\r
-\r
-    for(i = 0; i < S1_LEN; ++i)\r
-    {\r
-        j = v = i ^ S1_MASK; v = ff_mult(v, j, S1_SIZE, S1_POLY);\r
-        sb1[i] = (u1byte)ff_mult(v, j, S1_SIZE, S1_POLY);\r
-    } \r
-    // initialise S box 2\r
-\r
-    for(i = 0; i < S2_LEN; ++i)\r
-    {\r
-        j = v = i ^ S2_MASK; v = ff_mult(v, j, S2_SIZE, S2_POLY);\r
-        sb2[i] = (u1byte)ff_mult(v, j, S2_SIZE, S2_POLY);\r
-    }\r
-\r
-    // initialise permutation table\r
-\r
-    for(i = 0; i < 256; ++i)\r
-    {\r
-        prm[i][0] = ((i &  1) << 7) | ((i &  2) << 14) | ((i &  4) << 21) | ((i &   8) << 28);\r
-        prm[i][1] = ((i & 16) << 3) | ((i & 32) << 10) | ((i & 64) << 17) | ((i & 128) << 24);\r
-    }\r
-};\r
-\r
-void f_fun(u4byte res[2], const u4byte in[2], const u4byte key[2])\r
-{   u4byte  i, tt[2], pp[2];\r
-\r
-    tt[0] = (in[0] & ~key[0]) | (in[1] & key[0]);\r
-    tt[1] = (in[1] & ~key[0]) | (in[0] & key[0]);\r
-\r
-    i = sb1[((tt[1] >> 24) | (tt[0] << 8)) & S1_MASK];\r
-    pp[0]  = prm[i][0] >> 7; pp[1]  = prm[i][1] >> 7;\r
-    i = sb2[(tt[1] >> 16) & S2_MASK];\r
-    pp[0] |= prm[i][0] >> 6; pp[1] |= prm[i][1] >> 6;\r
-    i = sb1[(tt[1] >>  8) & S1_MASK];\r
-    pp[0] |= prm[i][0] >> 5; pp[1] |= prm[i][1] >> 5;\r
-    i = sb2[tt[1] & S2_MASK]; \r
-    pp[0] |= prm[i][0] >> 4; pp[1] |= prm[i][1] >> 4;\r
-    i = sb2[((tt[0] >> 24) | (tt[1] << 8)) & S2_MASK];\r
-    pp[0] |= prm[i][0] >> 3; pp[1] |= prm[i][1] >> 3;\r
-    i = sb1[(tt[0] >> 16) & S1_MASK]; \r
-    pp[0] |= prm[i][0] >> 2; pp[1] |= prm[i][1] >> 2;\r
-    i = sb2[(tt[0] >>  8) & S2_MASK];      \r
-    pp[0] |= prm[i][0] >> 1; pp[1] |= prm[i][1] >> 1;\r
-    i = sb1[tt[0] & S1_MASK];          \r
-    pp[0] |= prm[i][0];      pp[1] |= prm[i][1];\r
-\r
-    res[0] ^=  sb1[byte(pp[0], 0) | (key[1] <<  8) & S1_HMASK]\r
-            | (sb1[byte(pp[0], 1) | (key[1] <<  3) & S1_HMASK] << 8)\r
-            | (sb2[byte(pp[0], 2) | (key[1] >>  2) & S2_HMASK] << 16)\r
-            | (sb2[byte(pp[0], 3) | (key[1] >>  5) & S2_HMASK] << 24);\r
-    res[1] ^=  sb1[byte(pp[1], 0) | (key[1] >>  8) & S1_HMASK]\r
-            | (sb1[byte(pp[1], 1) | (key[1] >> 13) & S1_HMASK] << 8)\r
-            | (sb2[byte(pp[1], 2) | (key[1] >> 18) & S2_HMASK] << 16)\r
-            | (sb2[byte(pp[1], 3) | (key[1] >> 21) & S2_HMASK] << 24);\r
-};\r
-\r
-u4byte *loki_set_key(LokiContext *ctx,\r
-                    const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u4byte  i, k1[2], k2[2], k3[2], k4[2], del[2], tt[2], sk[2];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    if(!init_done)\r
-    {\r
-        init_tables(); init_done = 1;\r
-    }\r
-\r
-    k4[0] = io_swap(in_key[1]); k4[1] = io_swap(in_key[0]);\r
-    k3[0] = io_swap(in_key[3]); k3[1] = io_swap(in_key[2]);\r
-\r
-    switch ((key_len + 63) / 64)\r
-    {\r
-    case 2:\r
-        k2[0] = 0; k2[1] = 0; f_fun(k2, k3, k4);\r
-        k1[0] = 0; k1[1] = 0; f_fun(k1, k4, k3);\r
-        break;\r
-    case 3:\r
-       k2[0] = io_swap(in_key[5]); k2[1] = io_swap(in_key[4]);\r
-        k1[0] = 0; k1[1] = 0; f_fun(k1, k4, k3);\r
-        break;\r
-    case 4: \r
-        k2[0] = in_key[5]; k2[1] = in_key[4];\r
-        k1[0] = in_key[7]; k1[1] = in_key[6];\r
-       k2[0] = io_swap(in_key[5]); k2[1] = io_swap(in_key[4]);\r
-       k1[0] = io_swap(in_key[7]); k1[1] = io_swap(in_key[6]);\r
-    }\r
-\r
-    del[0] = delta[0]; del[1] = delta[1];\r
-\r
-    for(i = 0; i < 48; ++i)\r
-    {\r
-        tt[0] = k1[0]; tt[1] = k1[1]; \r
-        add_eq(tt, k3); add_eq(tt, del); add_eq(del, delta);\r
-        sk[0] = k4[0]; sk[1] = k4[1];\r
-        k4[0] = k3[0]; k4[1] = k3[1];\r
-        k3[0] = k2[0]; k3[1] = k2[1];\r
-        k2[0] = k1[0]; k2[1] = k1[1];\r
-        k1[0] = sk[0]; k1[1] = sk[1];\r
-        f_fun(k1, tt, k3);\r
-        l_key[i + i] = k1[0]; l_key[i + i + 1] = k1[1];\r
-    }\r
-\r
-    return l_key;\r
-};\r
-\r
-#define r_fun(l,r,k)        \\r
-    add_eq((l),(k));        \\r
-    f_fun((r),(l),(k) + 2); \\r
-    add_eq((l), (k) + 4)\r
-\r
-void loki_encrypt(LokiContext *ctx,\r
-                 const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  blk[4];\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    blk[3] = io_swap(in_blk[0]); blk[2] = io_swap(in_blk[1]);\r
-    blk[1] = io_swap(in_blk[2]); blk[0] = io_swap(in_blk[3]);\r
-\r
-    r_fun(blk, blk + 2, l_key +  0);\r
-    r_fun(blk + 2, blk, l_key +  6);\r
-    r_fun(blk, blk + 2, l_key + 12);\r
-    r_fun(blk + 2, blk, l_key + 18);\r
-    r_fun(blk, blk + 2, l_key + 24);\r
-    r_fun(blk + 2, blk, l_key + 30);\r
-    r_fun(blk, blk + 2, l_key + 36);\r
-    r_fun(blk + 2, blk, l_key + 42);\r
-    r_fun(blk, blk + 2, l_key + 48);\r
-    r_fun(blk + 2, blk, l_key + 54);\r
-    r_fun(blk, blk + 2, l_key + 60);\r
-    r_fun(blk + 2, blk, l_key + 66);\r
-    r_fun(blk, blk + 2, l_key + 72);\r
-    r_fun(blk + 2, blk, l_key + 78);\r
-    r_fun(blk, blk + 2, l_key + 84);\r
-    r_fun(blk + 2, blk, l_key + 90);\r
-\r
-    out_blk[3] = io_swap(blk[2]); out_blk[2] = io_swap(blk[3]);\r
-    out_blk[1] = io_swap(blk[0]); out_blk[0] = io_swap(blk[1]);\r
-};\r
-\r
-#define ir_fun(l,r,k)       \\r
-    sub_eq((l),(k) + 4);    \\r
-    f_fun((r),(l),(k) + 2); \\r
-    sub_eq((l),(k))\r
-\r
-void loki_decrypt(LokiContext *ctx,\r
-                 const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  blk[4], xs;\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    blk[3] = io_swap(in_blk[0]); blk[2] = io_swap(in_blk[1]);\r
-    blk[1] = io_swap(in_blk[2]); blk[0] = io_swap(in_blk[3]);\r
-\r
-    ir_fun(blk, blk + 2, l_key + 90); \r
-    ir_fun(blk + 2, blk, l_key + 84);\r
-    ir_fun(blk, blk + 2, l_key + 78); \r
-    ir_fun(blk + 2, blk, l_key + 72);\r
-    ir_fun(blk, blk + 2, l_key + 66); \r
-    ir_fun(blk + 2, blk, l_key + 60);\r
-    ir_fun(blk, blk + 2, l_key + 54); \r
-    ir_fun(blk + 2, blk, l_key + 48);\r
-    ir_fun(blk, blk + 2, l_key + 42); \r
-    ir_fun(blk + 2, blk, l_key + 36);\r
-    ir_fun(blk, blk + 2, l_key + 30); \r
-    ir_fun(blk + 2, blk, l_key + 24);\r
-    ir_fun(blk, blk + 2, l_key + 18); \r
-    ir_fun(blk + 2, blk, l_key + 12);\r
-    ir_fun(blk, blk + 2, l_key +  6); \r
-    ir_fun(blk + 2, blk, l_key);\r
-\r
-    out_blk[3] = io_swap(blk[2]); out_blk[2] = io_swap(blk[3]);\r
-    out_blk[1] = io_swap(blk[0]); out_blk[0] = io_swap(blk[1]);   \r
-};\r
diff --git a/lib/silccrypt/loki.h b/lib/silccrypt/loki.h
deleted file mode 100644 (file)
index 0f3bc51..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-
-  loki.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef LOKI_H
-#define LOKI_H
-
-#include "loki_internal.h"
-
-/* 
- * SILC Crypto API for Loki
- */
-
-/* Sets the key for the cipher. */
-
-inline int silc_loki_init(void *context, 
-                         const unsigned char *key, 
-                         size_t keylen)
-{
-  loki_set_key((LokiContext *)context, (unsigned int *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string
-   is first hashed and then used as a new key. */
-
-inline int silc_loki_set_string_as_key(void *context, 
-                                      const unsigned char *string,
-                                      size_t keylen)
-{
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return 1;
-}
-
-/* Returns the size of the cipher context. */
-
-inline size_t silc_loki_context_len()
-{
-  return sizeof(LokiContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-inline int silc_loki_encrypt_cbc(void *context,
-                                const unsigned char *src,
-                                unsigned char *dst,
-                                size_t len,
-                                unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  loki_encrypt((LokiContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    loki_encrypt((LokiContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-inline int silc_loki_decrypt_cbc(void *context,
-                                const unsigned char *src,
-                                unsigned char *dst,
-                                size_t len,
-                                unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  loki_decrypt((LokiContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    loki_decrypt((LokiContext *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-#endif
index 31188b7157bac4fa53d8fab9833354da5d910844..73af702f194e3fd070e111a5640d49fe8f7d749d 100644 (file)
@@ -39,6 +39,7 @@ Mean:          373 cycles =    68.7 mbits/sec
 */
 
 #include "silcincludes.h"
+#include "mars_internal.h"
 #include "mars.h"
 
 /* 
@@ -49,7 +50,11 @@ Mean:          373 cycles =    68.7 mbits/sec
 
 SILC_CIPHER_API_SET_KEY(mars)
 {
-  mars_set_key((MarsContext *)context, (unsigned int *)key, keylen);
+  uint32 k[8];
+
+  SILC_GET_WORD_KEY(key, k, keylen);
+  mars_set_key((MarsContext *)context, k, keylen);
+
   return TRUE;
 }
 
@@ -80,36 +85,22 @@ SILC_CIPHER_API_CONTEXT_LEN(mars)
 
 SILC_CIPHER_API_ENCRYPT_CBC(mars)
 {
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
+  uint32 tiv[4];
   int i;
 
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
+  SILC_CBC_GET_IV(tiv, iv);
 
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  mars_encrypt((MarsContext *)context, tmp, out);
-  in += 4;
-  out += 4;
+  SILC_CBC_ENC_PRE(tiv, src);
+  mars_encrypt((MarsContext *)context, tiv, tiv);
+  SILC_CBC_ENC_POST(tiv, dst, src);
 
   for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    mars_encrypt((MarsContext *)context, tmp, out);
-    in += 4;
-    out += 4;
+    SILC_CBC_ENC_PRE(tiv, src);
+    mars_encrypt((MarsContext *)context, tiv, tiv);
+    SILC_CBC_ENC_POST(tiv, dst, src);
   }
 
-  tiv[0] = out[0 - 4];
-  tiv[1] = out[1 - 4];
-  tiv[2] = out[2 - 4];
-  tiv[3] = out[3 - 4];
+  SILC_CBC_PUT_IV(tiv, iv);
 
   return TRUE;
 }
@@ -119,48 +110,22 @@ SILC_CIPHER_API_ENCRYPT_CBC(mars)
 
 SILC_CIPHER_API_DECRYPT_CBC(mars)
 {
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4], tmp2[4];
+  uint32 tmp[4], tmp2[4], tiv[4];
   int i;
 
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0];
-  tmp[1] = in[1];
-  tmp[2] = in[2];
-  tmp[3] = in[3];
-  mars_decrypt((MarsContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  mars_decrypt((MarsContext *)context, tmp, tmp2);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
 
   for (i = 16; i < len; i += 16) {
-    tmp2[0] = tmp[0];
-    tmp2[1] = tmp[1];
-    tmp2[2] = tmp[2];
-    tmp2[3] = tmp[3];
-    tmp[0] = in[0];
-    tmp[1] = in[1];
-    tmp[2] = in[2];
-    tmp[3] = in[3];
-    mars_decrypt((MarsContext *)context, in, out);
-    out[0] ^= tmp2[0];
-    out[1] ^= tmp2[1];
-    out[2] ^= tmp2[2];
-    out[3] ^= tmp2[3];
-    in += 4;
-    out += 4;
+    SILC_CBC_DEC_PRE(tmp, src);
+    mars_decrypt((MarsContext *)context, tmp, tmp2);
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
   }
 
-  tiv[0] = tmp[0];
-  tiv[1] = tmp[1];
-  tiv[2] = tmp[2];
-  tiv[3] = tmp[3];
+  SILC_CBC_PUT_IV(tiv, iv);
 
   return TRUE;
 }
index 5ab5938b97a6c5f5551f8a6aabaa56f2915bb204..23a47b698493bd5dc450b407e7edbefa454dfaf1 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef MARS_H
 #define MARS_H
 
-#include "mars_internal.h"
-
 /* 
  * SILC Crypto API for MARS 
  */
index 20cf9c93d80180c5f5074ebb189fa3d22ebfe93f..63514cd10f3c9684640d30e826aa1d47bd245d70 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include "silcincludes.h"
+#include "md5_internal.h"
 #include "md5.h"
 
 /* 
@@ -53,13 +54,13 @@ SILC_HASH_API_CONTEXT_LEN(md5)
 #ifndef HIGHFIRST
 #define byteReverse(buf, len)  /* Nothing */
 #else
-void byteReverse(unsigned char *buf, unsigned longs);
+void byteReverse(unsigned char *buf, uint32s);
 
 #ifndef ASM_MD5
 /*
  * Note: this code is harmless on little-endian machines.
  */
-void byteReverse(unsigned char *buf, unsigned longs)
+void byteReverse(unsigned char *buf, uint32s)
 {
        uint32 t;
        do {
index c6c2e3b56394972d92496c7e9813ffa3ebd69fc7..8b10d7ea4f5ff1801c3cd22de01f3c0fd34125f6 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef MD5_H
 #define MD5_H
 
-#include "md5_internal.h"
-
 /* 
  * SILC Hash API for MD5
  */
index ef1bddb66200b57bd27edecbb546e2b4961edc23..c48e8d2ba0c1b8fa8a3967264e1fadfd2732cb7c 100644 (file)
@@ -3,8 +3,6 @@
 #ifndef MD5_INTERNAL_H
 #define MD5_INTERNAL_H
 
-typedef unsigned long uint32;
-
 struct MD5Context {
        uint32 buf[4];
        uint32 bits[2];
index 3bba1a02c59ce777e737d10ff700ca2e5cafcc4c..239be9106e4251a88f6d68c77c8e83e5ac1f0653 100644 (file)
@@ -20,8 +20,8 @@
 /*
  * $Id$
  * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
+ * Revision 1.1  2000/06/27 11:36:55  priikone
+ * Initial revision
  *
  *
  */
index 3043de15360a0da76785109f8b72593c70e0032f..316ea8784130bd71534dfcd20babddaad2a6f436 100644 (file)
@@ -20,8 +20,8 @@
 /*
  * $Id$
  * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
+ * Revision 1.1  2000/06/27 11:36:54  priikone
+ * Initial revision
  *
  *
  */
diff --git a/lib/silccrypt/pkcs1.c b/lib/silccrypt/pkcs1.c
new file mode 100644 (file)
index 0000000..970324a
--- /dev/null
@@ -0,0 +1,480 @@
+/* $Id$ */
+/* 
+   PKCS #1 RSA wrapper.
+
+   Heavily modified to work under SILC, rewrote all interfaces, code that
+   is not needed in SILC has been removed for good, and some code was fixed
+   and changed.
+
+   For example, RSA_DecodeOneBlock was not used at all by Mozilla, however,
+   I took this code in to use after doing some fixing (it had some bugs).  
+   Also, OAEP is removed totally for now.  I'm not sure whether OAEP could
+   be used in the future with SILC but not for now.
+
+   This file also implements partial SILC PKCS API for RSA with PKCS #1.
+   It is partial because all the other functions but encrypt, decrypt,
+   sign and verify are common.
+
+   Note:
+
+   The mandatory PKCS #1 implementation in SILC must be compliant to either
+   PKCS #1 version 1.5 or PKCS #1 version 2 with the following notes:
+   The signature encoding is always in same format as the encryption
+   encoding regardles of the PKCS #1 version.  The signature with
+   appendix (with hash algorithm OID in the data) must not be used
+   in the SILC.  Rationale for this is that there is no binding between
+   the PKCS #1 OIDs and the hash algorithms used in the SILC protocol.
+   Hence, the encoding is always in PKCS #1 version 1.5 format.
+
+   Any questions and comments regarding this modified version should be
+   sent to priikone@silcnet.org.
+
+   References: ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc,
+               ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1.asc,
+              and RFC 2437.
+
+   Copyright notice: All code in this file, including the SILC PKCS API
+   code that is not part of the Mozilla code, falls under the same license
+   (MPL or GPL) found attached to this file, below.
+*/
+
+/*
+ * PKCS#1 encoding and decoding functions.
+ * This file is believed to contain no code licensed from other parties.
+ *
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ * 
+ * The Original Code is the Netscape security libraries.
+ * 
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation.  Portions created by Netscape are 
+ * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
+ * Rights Reserved.
+ * 
+ * Contributor(s):
+ * 
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable 
+ * instead of those above.  If you wish to allow use of your 
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL.  If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ *
+ * $Id$
+ */
+
+#include "silcincludes.h"
+#include "rsa_internal.h"
+#include "rsa.h"
+
+#define RSA_BLOCK_MIN_PAD_LEN          8
+#define RSA_BLOCK_FIRST_OCTET          0x00
+#define RSA_BLOCK_PRIVATE0_PAD_OCTET   0x00
+#define RSA_BLOCK_PRIVATE_PAD_OCTET    0xff
+#define RSA_BLOCK_AFTER_PAD_OCTET      0x00
+
+/*
+ * RSA block types
+ *
+ * The actual values are important -- they are fixed, *not* arbitrary.
+ * The explicit value assignments are not needed (because C would give
+ * us those same values anyway) but are included as a reminder...
+ */
+typedef enum {
+    RSA_BlockPrivate0 = 0,     /* unused, really */
+    RSA_BlockPrivate = 1,      /* pad for a private-key operation */
+    RSA_BlockPublic = 2,       /* pad for a public-key operation */
+    RSA_BlockTotal
+} RSA_BlockType;
+
+/*
+ * Format one block of data for public/private key encryption using
+ * the rules defined in PKCS #1.
+ */
+static unsigned char *
+RSA_FormatOneBlock(uint32 modulusLen, RSA_BlockType blockType,
+                  unsigned char *data, uint32 data_len)
+{
+    unsigned char *block;
+    unsigned char *bp;
+    int padLen;
+    int i;
+
+    block = (unsigned char *) silc_malloc(modulusLen);
+    if (block == NULL)
+       return NULL;
+
+    bp = block;
+
+    /*
+     * All RSA blocks start with two octets:
+     * 0x00 || BlockType
+     */
+    *bp++ = RSA_BLOCK_FIRST_OCTET;
+    *bp++ = (unsigned char) blockType;
+
+    switch (blockType) {
+
+      /*
+       * Blocks intended for private-key operation.
+       */
+      case RSA_BlockPrivate0: /* essentially unused */
+      case RSA_BlockPrivate:    /* preferred method */
+       /*
+        * 0x00 || BT || Pad || 0x00 || ActualData
+        *   1      1   padLen    1      data_len
+        * Pad is either all 0x00 or all 0xff bytes, depending on blockType.
+        */
+       padLen = modulusLen - data_len - 3;
+       assert(padLen >= RSA_BLOCK_MIN_PAD_LEN);
+       memset(bp,
+                  blockType == RSA_BlockPrivate0
+                       ? RSA_BLOCK_PRIVATE0_PAD_OCTET
+                       : RSA_BLOCK_PRIVATE_PAD_OCTET,
+                  padLen);
+       bp += padLen;
+       *bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
+       memcpy(bp, data, data_len);
+       break;
+
+      /*
+       * Blocks intended for public-key operation.
+       */
+      case RSA_BlockPublic:
+       /*
+        * 0x00 || BT || Pad || 0x00 || ActualData
+        *   1      1   padLen    1      data_len
+        * Pad is all non-zero random bytes.
+        */
+       padLen = modulusLen - data_len - 3;
+       assert(padLen >= RSA_BLOCK_MIN_PAD_LEN);
+       for (i = 0; i < padLen; i++) {
+           /* Pad with non-zero random data. */
+           do {
+               silc_rng_global_get_byte(bp + i);
+           } while (bp[i] == RSA_BLOCK_AFTER_PAD_OCTET);
+       }
+       bp += padLen;
+       *bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
+       memcpy(bp, data, data_len);
+       break;
+
+      default:
+       silc_free(block);
+       return NULL;
+    }
+
+    return block;
+}
+
+static int
+RSA_FormatBlock(unsigned char **result, uint32 *result_len,
+               uint32 modulusLen,
+               RSA_BlockType blockType, unsigned char *data,
+               uint32 data_len)
+{
+    /*
+     * XXX For now assume that the data length fits in a single
+     * XXX encryption block; the ASSERTs below force this.
+     * XXX To fix it, each case will have to loop over chunks whose
+     * XXX lengths satisfy the assertions, until all data is handled.
+     * XXX (Unless RSA has more to say about how to handle data
+     * XXX which does not fit in a single encryption block?)
+     * XXX And I do not know what the result is supposed to be,
+     * XXX so the interface to this function may need to change
+     * XXX to allow for returning multiple blocks, if they are
+     * XXX not wanted simply concatenated one after the other.
+     */
+
+    switch (blockType) {
+      case RSA_BlockPrivate0:
+      case RSA_BlockPrivate:
+      case RSA_BlockPublic:
+       /*
+        * 0x00 || BT || Pad || 0x00 || ActualData
+        *
+        * The "3" below is the first octet + the second octet + the 0x00
+        * octet that always comes just before the ActualData.
+        */
+       assert(data_len <= (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN)));
+
+       *result = RSA_FormatOneBlock(modulusLen, blockType, data, data_len);
+       if (result == NULL) {
+           *result_len = 0;
+           return FALSE;
+       }
+       *result_len = modulusLen;
+
+       break;
+
+      default:
+       *result = NULL;
+       *result_len = 0;
+       return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * Takes a formatted block and returns the data part.
+ * (This is the inverse of RSA_FormatOneBlock().)
+ * In some formats the start of the data is ambiguous;
+ * if it is non-zero, expectedLen will disambiguate.
+ *
+ */
+unsigned char *
+RSA_DecodeOneBlock(unsigned char *data,
+                  uint32 modulusLen,
+                  uint32 expectedLen,
+                  RSA_BlockType bt,
+                  uint32 *pResultLen)
+{
+    RSA_BlockType blockType;
+    unsigned char *dp, *res;
+    uint32 i, len = 0;
+
+    dp = data;
+    if (dp[0] != RSA_BLOCK_FIRST_OCTET) {
+       return NULL;
+    }
+
+    blockType = (RSA_BlockType)dp[1];
+    if (blockType != bt)
+      return NULL;
+
+    dp += 2;
+
+    switch (blockType) {
+      case RSA_BlockPrivate0:
+       /* Ignored */
+       res = (unsigned char *) silc_malloc(modulusLen);
+       memcpy(res, data, modulusLen);
+       break;
+
+      case RSA_BlockPrivate:
+       for (i = 0; i < modulusLen; i++) {
+           if (*dp++ != RSA_BLOCK_PRIVATE_PAD_OCTET)
+               break;
+       }
+       if (i == modulusLen)
+           return NULL;
+       len = modulusLen - (dp - data);
+       res = (unsigned char *) silc_malloc(len);
+       if (res == NULL) {
+           return NULL;
+       }
+       memcpy(res, dp, len);
+       break;
+
+      case RSA_BlockPublic:
+       for (i = 0; i < modulusLen; i++) {
+           if (*dp++ == RSA_BLOCK_AFTER_PAD_OCTET)
+               break;
+       }
+       if (i == modulusLen)
+           return NULL;
+       len = modulusLen - (dp - data);
+       res = (unsigned char *) silc_malloc(len);
+       if (res == NULL) {
+           return NULL;
+       }
+       memcpy(res, dp, len);
+       break;
+
+      default:
+       return NULL;
+    }
+
+    if (pResultLen)
+      *pResultLen = len;
+    return res;
+}
+
+/*
+ * SILC PKCS API for PKCS #1
+ *
+ * Note all the other PKCS API functions are used from the rsa.c.
+ * See the definitions in rsa.c and in silcpkcs.c.
+ */
+
+SILC_PKCS_API_ENCRYPT(pkcs1)
+{
+  RsaKey *key = (RsaKey *)context;
+  SilcMPInt mp_tmp;
+  SilcMPInt mp_dst;
+  unsigned char *padded;
+  uint32 padded_len, len = key->bits / 8;
+
+  /* Pad data */
+  if (!RSA_FormatBlock(&padded, &padded_len, len,
+                      RSA_BlockPublic, src, src_len))
+    return FALSE;
+
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_dst, 0);
+
+  /* Data to MP */
+  silc_mp_bin2mp(padded, padded_len, &mp_tmp);
+
+  /* Encrypt */
+  rsa_en_de_crypt(&mp_dst, &mp_tmp, &key->e, &key->n);
+  
+  /* MP to data */
+  silc_mp_mp2bin_noalloc(&mp_dst, dst, len);
+  *dst_len = len;
+
+  memset(padded, 0, padded_len);
+  silc_free(padded);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_dst);
+
+  return TRUE;
+}
+
+SILC_PKCS_API_DECRYPT(pkcs1)
+{
+  RsaKey *key = (RsaKey *)context;
+  SilcMPInt mp_tmp;
+  SilcMPInt mp_dst;
+  unsigned char *padded, *unpadded;
+  uint32 padded_len;
+
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_dst, 0);
+
+  /* Data to MP */
+  silc_mp_bin2mp(src, src_len, &mp_tmp);
+
+  /* Decrypt */
+  rsa_en_de_crypt(&mp_dst, &mp_tmp, &key->d, &key->n);
+
+  /* MP to data */
+  padded = silc_mp_mp2bin(&mp_dst, key->bits / 8, &padded_len);
+
+  /* Unpad data */
+  unpadded = RSA_DecodeOneBlock(padded, padded_len, 0, 
+                               RSA_BlockPublic, &padded_len);
+  if (!unpadded) {
+    memset(padded, 0, padded_len);
+    silc_free(padded);
+    silc_mp_uninit(&mp_tmp);
+    silc_mp_uninit(&mp_dst);
+    return FALSE;
+  }
+
+  /* Copy to destination */
+  memcpy(dst, unpadded, padded_len);
+  *dst_len = padded_len;
+
+  memset(padded, 0, padded_len);
+  memset(unpadded, 0, padded_len);
+  silc_free(padded);
+  silc_free(unpadded);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_dst);
+
+  return TRUE;
+}
+
+SILC_PKCS_API_SIGN(pkcs1)
+{
+  RsaKey *key = (RsaKey *)context;
+  SilcMPInt mp_tmp;
+  SilcMPInt mp_dst;
+  unsigned char *padded;
+  uint32 padded_len;
+  uint32 len = key->bits / 8;
+
+  /* Pad data */
+  if (!RSA_FormatBlock(&padded, &padded_len, len, RSA_BlockPrivate, 
+                      src, src_len))
+    return FALSE;
+
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_dst, 0);
+
+  /* Data to MP */
+  silc_mp_bin2mp(padded, len, &mp_tmp);
+
+  /* Sign */
+  rsa_en_de_crypt(&mp_dst, &mp_tmp, &key->d, &key->n);
+  
+  /* MP to data */
+  silc_mp_mp2bin_noalloc(&mp_dst, dst, len);
+  *dst_len = len;
+
+  memset(padded, 0, padded_len);
+  silc_free(padded);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_dst);
+
+  return TRUE;
+}
+
+SILC_PKCS_API_VERIFY(pkcs1)
+{
+  RsaKey *key = (RsaKey *)context;
+  int ret = TRUE;
+  SilcMPInt mp_tmp2;
+  SilcMPInt mp_dst;
+  unsigned char *verify, *unpadded;
+  uint32 verify_len, len = key->bits / 8;
+
+  silc_mp_init(&mp_tmp2);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp2, 0);
+  silc_mp_set_ui(&mp_dst, 0);
+
+  /* Format the signature into MP int */
+  silc_mp_bin2mp(signature, signature_len, &mp_tmp2);
+
+  /* Verify */
+  rsa_en_de_crypt(&mp_dst, &mp_tmp2, &key->e, &key->n);
+
+  /* MP to data */
+  verify = silc_mp_mp2bin(&mp_dst, len, &verify_len);
+
+  /* Unpad data */
+  unpadded = RSA_DecodeOneBlock(verify, len, 0, 
+                               RSA_BlockPrivate, &verify_len);
+  if (!unpadded) {
+    memset(verify, 0, verify_len);
+    silc_free(verify);
+    silc_mp_uninit(&mp_tmp2);
+    silc_mp_uninit(&mp_dst);
+    return FALSE;
+  }
+
+  /* Compare */
+  if (memcmp(data, unpadded, verify_len))
+    ret = FALSE;
+
+  memset(verify, 0, verify_len);
+  memset(unpadded, 0, verify_len);
+  silc_free(verify);
+  silc_free(unpadded);
+  silc_mp_uninit(&mp_tmp2);
+  silc_mp_uninit(&mp_dst);
+
+  return ret;
+}
diff --git a/lib/silccrypt/pkcs1.h b/lib/silccrypt/pkcs1.h
new file mode 100644 (file)
index 0000000..38937ae
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+
+  pkcs1.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+#ifndef PKCS1_H
+#define PKCS1_H
+
+/*
+ * SILC PKCS API for PKCS #1
+ *
+ * Note all the other PKCS API functions are used from the rsa.c.
+ * See the definitions in rsa.c and in silcpkcs.c.
+ */
+
+SILC_PKCS_API_ENCRYPT(pkcs1);
+SILC_PKCS_API_DECRYPT(pkcs1);
+SILC_PKCS_API_SIGN(pkcs1);
+SILC_PKCS_API_VERIFY(pkcs1);
+
+#endif
index c0db8e950cbc57218ce3a888f5852fefa6d1908c..34afc43fac0064b0dd8448ab8050330378f4939e 100644 (file)
  */
 
 #include "silcincludes.h"
+#include "rc5_internal.h"
 #include "rc5.h"
 
+/* 
+ * SILC Crypto API for RC5
+ */
+
+/* Sets the key for the cipher. */
+
+SILC_CIPHER_API_SET_KEY(rc5)
+{
+  uint32 k[8];
+
+  SILC_GET_WORD_KEY(key, k, keylen);
+  rc5_set_key((RC5Context *)context, k, keylen);
+
+  return TRUE;
+}
+
+/* Sets the string as a new key for the cipher. The string is first
+   hashed and then used as a new key. */
+
+SILC_CIPHER_API_SET_KEY_WITH_STRING(rc5)
+{
+  return 1;
+}
+
+/* Returns the size of the cipher context. */
+
+SILC_CIPHER_API_CONTEXT_LEN(rc5)
+{
+  return sizeof(RC5Context);
+}
+
+/* Encrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_ENCRYPT_CBC(rc5)
+{
+  uint32 tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_ENC_PRE(tiv, src);
+  rc5_encrypt((RC5Context *)context, tiv, tiv);
+  SILC_CBC_ENC_POST(tiv, dst, src);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_ENC_PRE(tiv, src);
+    rc5_encrypt((RC5Context *)context, tiv, tiv);
+    SILC_CBC_ENC_POST(tiv, dst, src);
+  }
+
+  SILC_CBC_PUT_IV(tiv, iv);
+
+  return TRUE;
+}
+
+/* Decrypts with the cipher in CBC mode. Source and destination buffers
+   maybe one and same. */
+
+SILC_CIPHER_API_DECRYPT_CBC(rc5)
+{
+  uint32 tmp[4], tmp2[4], tiv[4];
+  int i;
+
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  rc5_decrypt((RC5Context *)context, tmp, tmp2);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+
+  for (i = 16; i < len; i += 16) {
+    SILC_CBC_DEC_PRE(tmp, src);
+    rc5_decrypt((RC5Context *)context, tmp, tmp2); 
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
+  }
+  
+  SILC_CBC_PUT_IV(tiv, iv);
+  
+  return TRUE;
+}
+
 /* RC5 encryption */
 #define RC5E(i, A, B)                          \
                A = A ^ B;                      \
 
 /* Sets RC5 key */
 
-int rc5_set_key(RC5Context *ctx, char *key, int key_len)
+int rc5_set_key(RC5Context *ctx, const uint32 in_key[], int key_len)
 {
-       u32 *in_key = (u32 *)key;
        u32 i, j, k, A, B, L[c];
        u32 *out_key = ctx->out_key;
 
        if (key_len < b || key_len > (2 * b))
                return -1;
 
-       //      key_len *= 8;
-
        /* init L */
        for (i = 0; i < key_len / w; i++)
                L[i] = in_key[i];
index 23186dce881ad63210914b72c7dced267293477c..8909ebc04e5f622cc58809ef51eb04a855a5210e 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef RC5_H
 #define RC5_H
 
-#include "rc5_internal.h"
-
 /* 
  * SILC Crypto API for RC5
  */
 
-/* Sets the key for the cipher. */
-
-SILC_CIPHER_API_SET_KEY(rc5)
-{
-  rc5_set_key((RC5Context *)context, (unsigned char *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-SILC_CIPHER_API_SET_KEY_WITH_STRING(rc5)
-{
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return 1;
-}
-
-/* Returns the size of the cipher context. */
-
-SILC_CIPHER_API_CONTEXT_LEN(rc5)
-{
-  return sizeof(RC5Context);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-SILC_CIPHER_API_ENCRYPT_CBC(rc5)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[2];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  rc5_encrypt((RC5Context *)context, tmp, out);
-  in += 2;
-  out += 2;
-
-  for (i = 8; i < len; i += 8) {
-    tmp[0] = in[0] ^ out[0 - 2];
-    tmp[1] = in[1] ^ out[1 - 2];
-    rc5_encrypt((RC5Context *)context, tmp, out);
-    in += 2;
-    out += 2;
-  }
-
-  return TRUE;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-SILC_CIPHER_API_DECRYPT_CBC(rc5)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[2], tmp2[2];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0];
-  tmp[1] = in[1];
-  tmp[3] = in[3];
-  rc5_decrypt((RC5Context *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  in += 2;
-  out += 2;
-
-  for (i = 8; i < len; i += 8) {
-    tmp2[0] = tmp[0];
-    tmp2[1] = tmp[1];
-    tmp[0] = in[0];
-    tmp[1] = in[1];
-    rc5_decrypt((RC5Context *)context, in, out);
-    out[0] ^= tmp2[0];
-    out[1] ^= tmp2[1];
-    in += 2;
-    out += 2;
-  }
-
-  return TRUE;
-}
+SILC_CIPHER_API_SET_KEY(rc5);
+SILC_CIPHER_API_SET_KEY_WITH_STRING(rc5);
+SILC_CIPHER_API_CONTEXT_LEN(rc5);
+SILC_CIPHER_API_ENCRYPT_CBC(rc5);
+SILC_CIPHER_API_DECRYPT_CBC(rc5);
 
 #endif
index 940056700a060d5636fbba32a1b2612ef2912202..c493fdc519d8160664b1269238d11887db7e9e69 100644 (file)
@@ -36,7 +36,7 @@ typedef struct {
 } RC5Context;
 
 /* Prototypes */
-int rc5_set_key(RC5Context *ctx, char *key, int key_len);
+int rc5_set_key(RC5Context *ctx, const uint32 in_key[], int key_len);
 int rc5_encrypt(RC5Context *ctx, u32 *in, u32 *out);
 int rc5_decrypt(RC5Context *ctx, u32 *in, u32 *out);
 
index e2cc97cc5b4fb794d362d4755316ace3d45fedc3..7939b2888e5af1ce0cb975a24843c451b936a348 100644 (file)
@@ -37,6 +37,7 @@ Mean:          249 cycles =   103.0 mbits/sec
 */
 
 #include "silcincludes.h"
+#include "rc6_internal.h"
 #include "rc6.h"
 
 /* 
@@ -47,8 +48,12 @@ Mean:          249 cycles =   103.0 mbits/sec
 
 SILC_CIPHER_API_SET_KEY(rc6)
 {
-  rc6_set_key((RC6Context *)context, (unsigned int *)key, keylen);
-  return 1;
+  uint32 k[8];
+
+  SILC_GET_WORD_KEY(key, k, keylen);
+  rc6_set_key((RC6Context *)context, k, keylen);
+
+  return TRUE;
 }
 
 /* Sets the string as a new key for the cipher. The string is first
@@ -56,7 +61,7 @@ SILC_CIPHER_API_SET_KEY(rc6)
 
 SILC_CIPHER_API_SET_KEY_WITH_STRING(rc6)
 {
-  return 1;
+  return FALSE;
 }
 
 /* Encrypts with the cipher in CBC mode. Source and destination buffers
@@ -64,36 +69,22 @@ SILC_CIPHER_API_SET_KEY_WITH_STRING(rc6)
 
 SILC_CIPHER_API_ENCRYPT_CBC(rc6)
 {
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
+  uint32 tiv[4];
   int i;
 
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
+  SILC_CBC_GET_IV(tiv, iv);
 
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  rc6_encrypt((RC6Context *)context, tmp, out);
-  in += 4;
-  out += 4;
+  SILC_CBC_ENC_PRE(tiv, src);
+  rc6_encrypt((RC6Context *)context, tiv, tiv);
+  SILC_CBC_ENC_POST(tiv, dst, src);
 
   for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    rc6_encrypt((RC6Context *)context, tmp, out);
-    in += 4;
-    out += 4;
+    SILC_CBC_ENC_PRE(tiv, src);
+    rc6_encrypt((RC6Context *)context, tiv, tiv);
+    SILC_CBC_ENC_POST(tiv, dst, src);
   }
 
-  tiv[0] = out[0 - 4];
-  tiv[1] = out[1 - 4];
-  tiv[2] = out[2 - 4];
-  tiv[3] = out[3 - 4];
+  SILC_CBC_PUT_IV(tiv, iv);
 
   return TRUE;
 }
@@ -103,48 +94,22 @@ SILC_CIPHER_API_ENCRYPT_CBC(rc6)
 
 SILC_CIPHER_API_DECRYPT_CBC(rc6)
 {
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4], tmp2[4];
+  uint32 tmp[4], tmp2[4], tiv[4];
   int i;
 
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0];
-  tmp[1] = in[1];
-  tmp[2] = in[2];
-  tmp[3] = in[3];
-  rc6_decrypt((RC6Context *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  rc6_decrypt((RC6Context *)context, tmp, tmp2);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
 
   for (i = 16; i < len; i += 16) {
-    tmp2[0] = tmp[0];
-    tmp2[1] = tmp[1];
-    tmp2[2] = tmp[2];
-    tmp2[3] = tmp[3];
-    tmp[0] = in[0];
-    tmp[1] = in[1];
-    tmp[2] = in[2];
-    tmp[3] = in[3];
-    rc6_decrypt((RC6Context *)context, in, out);
-    out[0] ^= tmp2[0];
-    out[1] ^= tmp2[1];
-    out[2] ^= tmp2[2];
-    out[3] ^= tmp2[3];
-    in += 4;
-    out += 4;
+    SILC_CBC_DEC_PRE(tmp, src);
+    rc6_decrypt((RC6Context *)context, tmp, tmp2);
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
   }
-
-  tiv[0] = tmp[0];
-  tiv[1] = tmp[1];
-  tiv[2] = tmp[2];
-  tiv[3] = tmp[3];
+  
+  SILC_CBC_PUT_IV(tiv, iv);
 
   return TRUE;
 }
@@ -187,7 +152,7 @@ u4byte *rc6_set_key(RC6Context *ctx,
 
         l[k] = in_key[k];
 
-    t = (key_len / 32) - 1; // t = (key_len / 32);
+    t = (key_len / 32) - 1; /* t = (key_len / 32); */
 
     a = b = i = j = 0;
 
@@ -195,8 +160,8 @@ u4byte *rc6_set_key(RC6Context *ctx,
     {   a = rotl(l_key[i] + a + b, 3); b += a;
         b = rotl(l[j] + b, b);
         l_key[i] = a; l[j] = b;
-        i = (i == 43 ? 0 : i + 1); // i = (i + 1) % 44;  
-        j = (j == t ? 0 : j + 1);  // j = (j + 1) % t;
+        i = (i == 43 ? 0 : i + 1); /* i = (i + 1) % 44; */
+        j = (j == t ? 0 : j + 1);  /* j = (j + 1) % t; */
     }
 
     return l_key;
index 6ca50da2ae02952a69b64c344089a92bc807bf8f..c1d2ec0cd662d0a6f69353b7559a6b0c24ee3d18 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef RC6_H
 #define RC6_H
 
-#include "rc6_internal.h"
-
 /* 
  * SILC Crypto API for RC6
  */
diff --git a/lib/silccrypt/rijndael.h b/lib/silccrypt/rijndael.h
deleted file mode 100644 (file)
index d53a11d..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
-
-  rijndael.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef RIJNDAEL_H
-#define RIJNDAEL_H
-
-#include "rijndael_internal.h"
-
-/* 
- * SILC Crypto API for Rijndael
- */
-
-/* Sets the key for the cipher. */
-
-SILC_CIPHER_API_SET_KEY(rijndael)
-{
-  rijndael_set_key((RijndaelContext *)context, (unsigned int *)key, keylen);
-  return TRUE;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-SILC_CIPHER_API_SET_KEY_WITH_STRING(rijndael)
-{
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return TRUE;
-}
-
-/* Returns the size of the cipher context. */
-
-SILC_CIPHER_API_CONTEXT_LEN(rijnadel)
-{
-  return sizeof(RijndaelContext);
-}
-
-/* Encrypts with the cipher in CBC mode. Source and destination buffers
-   maybe one and same. */
-
-SILC_CIPHER_API_ENCRYPT_CBC(rijndael)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  rijndael_encrypt((RijndaelContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    rijndael_encrypt((RijndaelContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return TRUE;
-}
-
-/* Decrypts with the cipher in CBC mode. Source and destination buffers
-   maybe one and same. */
-
-SILC_CIPHER_API_DECRYPT_CBC(rijndael)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4], tmp2[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0];
-  tmp[1] = in[1];
-  tmp[2] = in[2];
-  tmp[3] = in[3];
-  rijndael_decrypt((RijndaelContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp2[0] = tmp[0];
-    tmp2[1] = tmp[1];
-    tmp2[2] = tmp[2];
-    tmp2[3] = tmp[3];
-    tmp[0] = in[0];
-    tmp[1] = in[1];
-    tmp[2] = in[2];
-    tmp[3] = in[3];
-    rijndael_decrypt((RijndaelContext *)context, in, out);
-    out[0] ^= tmp2[0];
-    out[1] ^= tmp2[1];
-    out[2] ^= tmp2[2];
-    out[3] ^= tmp2[3];
-    in += 4;
-    out += 4;
-  }
-
-  return TRUE;
-}
-
-#endif
index c185adb97f799f0c5057e0ff2ee1888d6add7f51..ec47e6ddd3e0fdbe7295410fa20ed94d056f40eb 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
  *
- * Copyright (C) 1997 - 2000 Pekka Riikonen
+ * Copyright (C) 1997 - 2001 Pekka Riikonen
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *     Public key exponent:
  *     e   relatively prime to (p-1) * (q-1)
  *     Private key exponent:
- *     d = e ^ -1 mod ((p-1) * (q-1))
+ *     d = e ^ -1 mod lcm(((p-1) * (q-1)))
  *
  *     Encryption:
  *     c = m ^ e mod n
  *     Decryption:
  *     m = c ^ d mod n
  *
- * This code is based on SSH's (Secure Shell), PGP's (Pretty Good Privacy) 
- * and RSAREF Toolkit's RSA source codes. They all were a big help for me.
+ * The SSH's (Secure Shell), PGP's (Pretty Good Privacy) and RSAREF 
+ * Toolkit were used as reference when coding this implementation. They 
+ * all were a big help for me.
  *
  * I also suggest reading Bruce Schneier's; Applied Cryptography, Second 
  * Edition, John Wiley & Sons, Inc. 1996. This book deals about RSA and 
  * everything else too about cryptography.
  *
  */
+/* $Id$ */
+
+/*
+   ChangeLog
+
+   o Mon Feb 12 11:20:32 EET 2001  Pekka
+
+     Changed RSA private exponent generation to what PKCS #1 suggests.  We
+     try to find the smallest possible d by doing modinv(e, lcm(phi)) instead
+     of modinv(e, phi).  Note: this is not security fix but optimization.
+
+   o Tue Feb 20 13:58:58 EET 2001  Pekka
+
+     Set key->bits in rsa_generate_key.  It is the modulus length in bits.
+     The `tmplen' in encrypt, decrypt, sign and verify PKCS API functions
+     is now calculated by (key->bits + 7) / 8.  It is the length of one block.
+
+*/
 
 #include "silcincludes.h"
+#include "rsa_internal.h"
 #include "rsa.h"
 
 /*
@@ -55,8 +75,9 @@
 
 SILC_PKCS_API_INIT(rsa)
 {
-  unsigned int prime_bits = keylen / 2;
-  SilcInt p, q;
+  uint32 prime_bits = keylen / 2;
+  SilcMPInt p, q;
+  bool found = FALSE;
 
   printf("Generating RSA Public and Private keys, might take a while...\n");
 
@@ -64,35 +85,36 @@ SILC_PKCS_API_INIT(rsa)
   silc_mp_init(&q);
 
   /* Find p and q */
- retry_primes:
-  printf("Finding p: ");
-  silc_math_gen_prime(&p, prime_bits, TRUE);
-  
-  printf("\nFinding q: ");
-  silc_math_gen_prime(&q, prime_bits, TRUE);
-  
-  if ((silc_mp_cmp(&p, &q)) == 0) {
-    printf("\nFound equal primes, not good, retrying...\n");
-    goto retry_primes;
+  while (!found) {
+    printf("Finding p: ");
+    silc_math_gen_prime(&p, prime_bits, TRUE);
+    
+    printf("\nFinding q: ");
+    silc_math_gen_prime(&q, prime_bits, TRUE);
+
+    if ((silc_mp_cmp(&p, &q)) == 0)
+      printf("\nFound equal primes, not good, retrying...\n");
+    else
+      found = TRUE;
   }
 
   /* If p is smaller than q, switch them */
   if ((silc_mp_cmp(&p, &q)) > 0) {
-    SilcInt hlp;
+    SilcMPInt hlp;
     silc_mp_init(&hlp);
 
     silc_mp_set(&hlp, &p);
     silc_mp_set(&p, &q);
     silc_mp_set(&q, &hlp);
 
-    silc_mp_clear(&hlp);
+    silc_mp_uninit(&hlp);
   }
 
   /* Generate the actual keys */
   rsa_generate_keys((RsaKey *)context, keylen, &p, &q);
 
-  silc_mp_clear(&p);
-  silc_mp_clear(&q);
+  silc_mp_uninit(&p);
+  silc_mp_uninit(&q);
   
   printf("\nKeys generated succesfully.\n");
 
@@ -110,34 +132,28 @@ SILC_PKCS_API_GET_PUBLIC_KEY(rsa)
 {
   RsaKey *key = (RsaKey *)context;
   unsigned char *e, *n, *ret;
-  unsigned int e_len, n_len;
-  unsigned char tmp[2];
+  uint32 e_len, n_len;
+  unsigned char tmp[4];
 
-  e_len = silc_mp_sizeinbase(&key->e, 16);
-  n_len = silc_mp_sizeinbase(&key->n, 16);
-  e = silc_calloc(e_len + 1, sizeof(unsigned char));
-  n = silc_calloc(n_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(e, 16, &key->e);
-  silc_mp_get_str(n, 16, &key->n);
+  e = silc_mp_mp2bin(&key->e, 0, &e_len);
+  n = silc_mp_mp2bin(&key->n, key->bits / 8, &n_len);
 
-  *ret_len = e_len + 2 + n_len + 2;
+  *ret_len = e_len + 4 + n_len + 4;
   ret = silc_calloc(*ret_len, sizeof(unsigned char));
 
   /* Put the length of the e. */
-  tmp[0] = e_len >> 8;
-  tmp[1] = e_len;
-  memcpy(ret, tmp, 2);
+  SILC_PUT32_MSB(e_len, tmp);
+  memcpy(ret, tmp, 4);
 
   /* Put the e. */
-  memcpy(ret + 2, e, e_len);
+  memcpy(ret + 4, e, e_len);
 
   /* Put the length of the n. */
-  tmp[0] = n_len >> 8;
-  tmp[1] = n_len;
-  memcpy(ret + 2 + e_len, tmp, 2);
+  SILC_PUT32_MSB(n_len, tmp);
+  memcpy(ret + 4 + e_len, tmp, 4);
 
   /* Put the n. */
-  memcpy(ret + 2 + e_len + 2, n, n_len);
+  memcpy(ret + 4 + e_len + 4, n, n_len);
 
   memset(e, 0, e_len);
   memset(n, 0, n_len);
@@ -155,45 +171,36 @@ SILC_PKCS_API_GET_PRIVATE_KEY(rsa)
 {
   RsaKey *key = (RsaKey *)context;
   unsigned char *e, *n, *d, *ret;
-  unsigned int e_len, n_len, d_len;
-  unsigned char tmp[2];
-
-  e_len = silc_mp_sizeinbase(&key->e, 16);
-  n_len = silc_mp_sizeinbase(&key->n, 16);
-  d_len = silc_mp_sizeinbase(&key->d, 16);
-  e = silc_calloc(e_len + 1, sizeof(unsigned char));
-  n = silc_calloc(n_len + 1, sizeof(unsigned char));
-  d = silc_calloc(d_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(e, 16, &key->e);
-  silc_mp_get_str(n, 16, &key->n);
-  silc_mp_get_str(d, 16, &key->d);
-
-  *ret_len = e_len + 2 + n_len + 2 + d_len + 2;
+  uint32 e_len, n_len, d_len;
+  unsigned char tmp[4];
+
+  e = silc_mp_mp2bin(&key->e, 0, &e_len);
+  n = silc_mp_mp2bin(&key->n, key->bits / 8, &n_len);
+  d = silc_mp_mp2bin(&key->d, 0, &d_len);
+
+  *ret_len = e_len + 4 + n_len + 4 + d_len + 4;
   ret = silc_calloc(*ret_len, sizeof(unsigned char));
 
   /* Put the length of the e. */
-  tmp[0] = e_len >> 8;
-  tmp[1] = e_len;
-  memcpy(ret, tmp, 2);
+  SILC_PUT32_MSB(e_len, tmp);
+  memcpy(ret, tmp, 4);
 
   /* Put the e. */
-  memcpy(ret + 2, e, e_len);
+  memcpy(ret + 4, e, e_len);
 
   /* Put the length of the n. */
-  tmp[0] = n_len >> 8;
-  tmp[1] = n_len;
-  memcpy(ret + 2 + e_len, tmp, 2);
+  SILC_PUT32_MSB(n_len, tmp);
+  memcpy(ret + 4 + e_len, tmp, 4);
 
   /* Put the n. */
-  memcpy(ret + 2 + e_len + 2, n, n_len);
+  memcpy(ret + 4 + e_len + 4, n, n_len);
 
   /* Put the length of the d. */
-  tmp[0] = d_len >> 8;
-  tmp[1] = d_len;
-  memcpy(ret + 2 + e_len + 2 + n_len, tmp, 2);
+  SILC_PUT32_MSB(d_len, tmp);
+  memcpy(ret + 4 + e_len + 4 + n_len, tmp, 4);
 
   /* Put the n. */
-  memcpy(ret + 2 + e_len + 2 + n_len + 2, d, d_len);
+  memcpy(ret + 4 + e_len + 4 + n_len + 4, d, d_len);
 
   memset(e, 0, e_len);
   memset(n, 0, n_len);
@@ -210,32 +217,42 @@ SILC_PKCS_API_GET_PRIVATE_KEY(rsa)
 SILC_PKCS_API_SET_PUBLIC_KEY(rsa)
 {
   RsaKey *key = (RsaKey *)context;
-  unsigned char *e, *n, tmp[2];
-  unsigned int e_len, n_len;
+  unsigned char tmp[4];
+  uint32 e_len, n_len;
+
+  if (key->pub_set) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->e);
+    key->pub_set = FALSE;
+  }
 
   silc_mp_init(&key->e);
   silc_mp_init(&key->n);
 
-  memcpy(tmp, key_data, 2);
-  e_len = ((unsigned int)tmp[0] << 8) | ((unsigned int)tmp[1]);
+  memcpy(tmp, key_data, 4);
+  SILC_GET32_MSB(e_len, tmp);
+  if (e_len > key_len) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->n);
+    return 0;
+  }
 
-  e = silc_calloc(e_len + 1, sizeof(unsigned char));
-  memcpy(e, key_data + 2, e_len);
-  silc_mp_set_str(&key->e, e, 16);
+  silc_mp_bin2mp(key_data + 4, e_len, &key->e);
   
-  memcpy(tmp, key_data + 2 + e_len, 2);
-  n_len = ((unsigned int)tmp[0] << 8) | ((unsigned int)tmp[1]);
+  memcpy(tmp, key_data + 4 + e_len, 4);
+  SILC_GET32_MSB(n_len, tmp);
+  if (e_len + n_len > key_len) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->n);
+    return 0;
+  }
 
-  n = silc_calloc(n_len + 1, sizeof(unsigned char));
-  memcpy(n, key_data + 2 + e_len + 2, n_len);
-  silc_mp_set_str(&key->n, n, 16);
+  silc_mp_bin2mp(key_data + 4 + e_len + 4, n_len, &key->n);
 
-  memset(e, 0, e_len);
-  memset(n, 0, n_len);
-  silc_free(e);
-  silc_free(n);
+  key->bits = n_len * 8;
+  key->pub_set = TRUE;
 
-  return TRUE;
+  return key->bits;
 }
 
 /* Set private key. This derives the public key from the private
@@ -245,40 +262,60 @@ SILC_PKCS_API_SET_PUBLIC_KEY(rsa)
 SILC_PKCS_API_SET_PRIVATE_KEY(rsa)
 {
   RsaKey *key = (RsaKey *)context;
-  unsigned char *e, *n, *d, tmp[2];
-  unsigned int e_len, n_len, d_len;
+  unsigned char tmp[4];
+  uint32 e_len, n_len, d_len;
+
+  if (key->prv_set) {
+    silc_mp_uninit(&key->d);
+    key->prv_set = FALSE;
+  }
+
+  if (key->pub_set) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->n);
+    key->pub_set = FALSE;
+  }
 
   silc_mp_init(&key->e);
   silc_mp_init(&key->n);
   silc_mp_init(&key->d);
 
-  memcpy(tmp, key_data, 2);
-  e_len = ((unsigned int)tmp[0] << 8) | ((unsigned int)tmp[1]);
+  memcpy(tmp, key_data, 4);
+  SILC_GET32_MSB(e_len, tmp);
+  if (e_len > key_len) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->n);
+    silc_mp_uninit(&key->d);
+    return FALSE;
+  }
 
-  e = silc_calloc(e_len + 1, sizeof(unsigned char));
-  memcpy(e, key_data + 2, e_len);
-  silc_mp_set_str(&key->e, e, 16);
+  silc_mp_bin2mp(key_data + 4, e_len, &key->e);
   
-  memcpy(tmp, key_data + 2 + e_len, 2);
-  n_len = ((unsigned int)tmp[0] << 8) | ((unsigned int)tmp[1]);
+  memcpy(tmp, key_data + 4 + e_len, 4);
+  SILC_GET32_MSB(n_len, tmp);
+  if (e_len + n_len > key_len) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->n);
+    silc_mp_uninit(&key->d);
+    return FALSE;
+  }
 
-  n = silc_calloc(n_len + 1, sizeof(unsigned char));
-  memcpy(n, key_data + 2 + e_len + 2, n_len);
-  silc_mp_set_str(&key->n, n, 16);
+  silc_mp_bin2mp(key_data + 4 + e_len + 4, n_len, &key->n);
 
-  memcpy(tmp, key_data + 2 + e_len + 2 + n_len, 2);
-  d_len = ((unsigned int)tmp[0] << 8) | ((unsigned int)tmp[1]);
+  memcpy(tmp, key_data + 4 + e_len + 4 + n_len, 4);
+  SILC_GET32_MSB(d_len, tmp);
+  if (e_len + n_len + d_len > key_len) {
+    silc_mp_uninit(&key->e);
+    silc_mp_uninit(&key->n);
+    silc_mp_uninit(&key->d);
+    return FALSE;
+  }
 
-  d = silc_calloc(d_len + 1, sizeof(unsigned char));
-  memcpy(d, key_data + 2 + e_len + 2 + n_len + 2, d_len);
-  silc_mp_set_str(&key->d, d, 16);
+  silc_mp_bin2mp(key_data + 4 + e_len + 4 + n_len + 4, d_len, &key->d);
 
-  memset(e, 0, e_len);
-  memset(n, 0, n_len);
-  memset(d, 0, d_len);
-  silc_free(e);
-  silc_free(n);
-  silc_free(d);
+  key->bits = n_len * 8;
+  key->prv_set = TRUE;
+  key->pub_set = TRUE;
 
   return TRUE;
 }
@@ -288,49 +325,17 @@ SILC_PKCS_API_CONTEXT_LEN(rsa)
   return sizeof(RsaKey);
 }
 
-SILC_PKCS_API_DATA_CONTEXT_LEN(rsa)
-{
-  return sizeof(RsaDataContext);
-}
-
-SILC_PKCS_API_SET_ARG(rsa)
-{
-  RsaDataContext *data_ctx = (RsaDataContext *)data_context;
-
-  switch(argnum) {
-  case 1:
-    data_ctx->src = val;
-    return TRUE;
-    break;
-  case 2:
-    data_ctx->dst = val;
-    return TRUE;
-    break;
-  case 3:
-    data_ctx->exp = val;
-    return TRUE;
-    break;
-  case 4:
-    data_ctx->mod = val;
-    return TRUE;
-    break;
-  default:
-    return FALSE;
-    break;
-  }
-
-  return FALSE;
-}
-
 SILC_PKCS_API_ENCRYPT(rsa)
 {
   RsaKey *key = (RsaKey *)context;
   int i, tmplen;
-  SilcInt mp_tmp;
-  SilcInt mp_dst;
+  SilcMPInt mp_tmp;
+  SilcMPInt mp_dst;
 
-  silc_mp_init_set_ui(&mp_tmp, 0);
-  silc_mp_init_set_ui(&mp_dst, 0);
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_dst, 0);
 
   /* Format the data into MP int */
   for (i = 0; i < src_len; i++) {
@@ -338,25 +343,20 @@ SILC_PKCS_API_ENCRYPT(rsa)
     silc_mp_add_ui(&mp_tmp, &mp_tmp, src[i]);
   }
 
-  silc_mp_out_str(stderr, 16, &mp_tmp);
-
   /* Encrypt */
   rsa_en_de_crypt(&mp_dst, &mp_tmp, &key->e, &key->n);
   
-  fprintf(stderr, "\n");
-  silc_mp_out_str(stderr, 16, &mp_dst);
-
-  tmplen = (1024 + 7) / 8;
+  tmplen = (key->bits + 7) / 8;
 
   /* Format the MP int back into data */
   for (i = tmplen; i > 0; i--) {
     dst[i - 1] = (unsigned char)(silc_mp_get_ui(&mp_dst) & 0xff);
-    silc_mp_fdiv_q_2exp(&mp_dst, &mp_dst, 8);
+    silc_mp_div_2exp(&mp_dst, &mp_dst, 8);
   }
   *dst_len = tmplen;
 
-  silc_mp_clear(&mp_tmp);
-  silc_mp_clear(&mp_dst);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_dst);
 
   return TRUE;
 }
@@ -365,11 +365,13 @@ SILC_PKCS_API_DECRYPT(rsa)
 {
   RsaKey *key = (RsaKey *)context;
   int i, tmplen;
-  SilcInt mp_tmp;
-  SilcInt mp_dst;
+  SilcMPInt mp_tmp;
+  SilcMPInt mp_dst;
 
-  silc_mp_init_set_ui(&mp_tmp, 0);
-  silc_mp_init_set_ui(&mp_dst, 0);
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_dst, 0);
 
   /* Format the data into MP int */
   for (i = 0; i < src_len; i++) {
@@ -377,25 +379,20 @@ SILC_PKCS_API_DECRYPT(rsa)
     silc_mp_add_ui(&mp_tmp, &mp_tmp, src[i]);
   }
 
-  silc_mp_out_str(stderr, 16, &mp_tmp);
-
   /* Decrypt */
   rsa_en_de_crypt(&mp_dst, &mp_tmp, &key->d, &key->n);
 
-  fprintf(stderr, "\n");
-  silc_mp_out_str(stderr, 16, &mp_dst);
-
-  tmplen = (1024 + 7) / 8;
+  tmplen = (key->bits + 7) / 8;
 
   /* Format the MP int back into data */
   for (i = tmplen; i > 0; i--) {
     dst[i - 1] = (unsigned char)(silc_mp_get_ui(&mp_dst) & 0xff);
-    silc_mp_fdiv_q_2exp(&mp_dst, &mp_dst, 8);
+    silc_mp_div_2exp(&mp_dst, &mp_dst, 8);
   }
   *dst_len = tmplen;
 
-  silc_mp_clear(&mp_tmp);
-  silc_mp_clear(&mp_dst);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_dst);
 
   return TRUE;
 }
@@ -404,11 +401,13 @@ SILC_PKCS_API_SIGN(rsa)
 {
   RsaKey *key = (RsaKey *)context;
   int i, tmplen;
-  SilcInt mp_tmp;
-  SilcInt mp_dst;
+  SilcMPInt mp_tmp;
+  SilcMPInt mp_dst;
 
-  silc_mp_init_set_ui(&mp_tmp, 0);
-  silc_mp_init_set_ui(&mp_dst, 0);
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_dst, 0);
 
   /* Format the data into MP int */
   for (i = 0; i < src_len; i++) {
@@ -419,17 +418,17 @@ SILC_PKCS_API_SIGN(rsa)
   /* Sign */
   rsa_en_de_crypt(&mp_dst, &mp_tmp, &key->d, &key->n);
 
-  tmplen = (1024 + 7) / 8;
+  tmplen = (key->bits + 7) / 8;
 
   /* Format the MP int back into data */
   for (i = tmplen; i > 0; i--) {
     dst[i - 1] = (unsigned char)(silc_mp_get_ui(&mp_dst) & 0xff);
-    silc_mp_fdiv_q_2exp(&mp_dst, &mp_dst, 8);
+    silc_mp_div_2exp(&mp_dst, &mp_dst, 8);
   }
   *dst_len = tmplen;
 
-  silc_mp_clear(&mp_tmp);
-  silc_mp_clear(&mp_dst);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_dst);
 
   return TRUE;
 }
@@ -438,12 +437,15 @@ SILC_PKCS_API_VERIFY(rsa)
 {
   RsaKey *key = (RsaKey *)context;
   int i, ret;
-  SilcInt mp_tmp, mp_tmp2;
-  SilcInt mp_dst;
+  SilcMPInt mp_tmp, mp_tmp2;
+  SilcMPInt mp_dst;
 
-  silc_mp_init_set_ui(&mp_tmp, 0);
-  silc_mp_init_set_ui(&mp_tmp2, 0);
-  silc_mp_init_set_ui(&mp_dst, 0);
+  silc_mp_init(&mp_tmp);
+  silc_mp_init(&mp_tmp2);
+  silc_mp_init(&mp_dst);
+  silc_mp_set_ui(&mp_tmp, 0);
+  silc_mp_set_ui(&mp_tmp2, 0);
+  silc_mp_set_ui(&mp_dst, 0);
 
   /* Format the signature into MP int */
   for (i = 0; i < signature_len; i++) {
@@ -466,9 +468,9 @@ SILC_PKCS_API_VERIFY(rsa)
   if ((silc_mp_cmp(&mp_tmp, &mp_dst)) != 0)
     ret = FALSE;
 
-  silc_mp_clear(&mp_tmp);
-  silc_mp_clear(&mp_tmp2);
-  silc_mp_clear(&mp_dst);
+  silc_mp_uninit(&mp_tmp);
+  silc_mp_uninit(&mp_tmp2);
+  silc_mp_uninit(&mp_dst);
 
   return ret;
 }
@@ -477,35 +479,33 @@ SILC_PKCS_API_VERIFY(rsa)
    to compute the modulus n has to be generated before calling this. They
    are then sent as argument for the function. */
 
-void rsa_generate_keys(RsaKey *key, unsigned int bits, 
-                      SilcInt *p, SilcInt *q)
+void rsa_generate_keys(RsaKey *key, uint32 bits, 
+                      SilcMPInt *p, SilcMPInt *q)
 {
-  SilcInt phi, hlp;
-  SilcInt dq;
-  SilcInt pm1, qm1;
+  SilcMPInt phi, hlp;
+  SilcMPInt div, lcm;
+  SilcMPInt pm1, qm1;
   
   /* Initialize variables */
-  silc_mp_init(&key->p);
-  silc_mp_init(&key->q);
   silc_mp_init(&key->n);
   silc_mp_init(&key->e);
   silc_mp_init(&key->d);
   silc_mp_init(&phi);
   silc_mp_init(&hlp);
-  silc_mp_init(&dq);
+  silc_mp_init(&div);
+  silc_mp_init(&lcm);
   silc_mp_init(&pm1);
   silc_mp_init(&qm1);
 
-  /* Set the primes */
-  silc_mp_set(&key->p, p);
-  silc_mp_set(&key->q, q);
-  
+  /* Set modulus length */
+  key->bits = bits;
+
   /* Compute modulus, n = p * q */
-  silc_mp_mul(&key->n, &key->p, &key->q);
+  silc_mp_mul(&key->n, p, q);
   
   /* phi = (p - 1) * (q - 1) */
-  silc_mp_sub_ui(&pm1, &key->p, 1);
-  silc_mp_sub_ui(&qm1, &key->q, 1);
+  silc_mp_sub_ui(&pm1, p, 1);
+  silc_mp_sub_ui(&qm1, q, 1);
   silc_mp_mul(&phi, &pm1, &qm1);
   
   /* Set e, the public exponent. We try to use same public exponent
@@ -521,16 +521,17 @@ void rsa_generate_keys(RsaKey *key, unsigned int bits,
     goto retry_e;
   }
   
-  /* Find d, the private exponent. First we do phi / 2, to get it a 
-     bit smaller */
-  silc_mp_div_ui(&dq, &phi, 2);
-  silc_mp_modinv(&key->d, &key->e, &dq);
+  /* Find d, the private exponent. */
+  silc_mp_gcd(&div, &pm1, &qm1);
+  silc_mp_div(&lcm, &phi, &div);
+  silc_mp_modinv(&key->d, &key->e, &lcm);
   
-  silc_mp_clear(&phi);
-  silc_mp_clear(&hlp);
-  silc_mp_clear(&dq);
-  silc_mp_clear(&pm1);
-  silc_mp_clear(&qm1);
+  silc_mp_uninit(&phi);
+  silc_mp_uninit(&hlp);
+  silc_mp_uninit(&div);
+  silc_mp_uninit(&lcm);
+  silc_mp_uninit(&pm1);
+  silc_mp_uninit(&qm1);
 }
 
 /* Clears whole key structure. */
@@ -538,11 +539,12 @@ void rsa_generate_keys(RsaKey *key, unsigned int bits,
 void rsa_clear_keys(RsaKey *key)
 {
   key->bits = 0;
-  silc_mp_clear(&key->p);
-  silc_mp_clear(&key->q);
-  silc_mp_clear(&key->n);
-  silc_mp_clear(&key->e);
-  silc_mp_clear(&key->d);
+  if (key->pub_set) {
+    silc_mp_uninit(&key->n);
+    silc_mp_uninit(&key->e);
+  }
+  if (key->prv_set)
+    silc_mp_uninit(&key->d);
 }
 
 /* RSA encrypt/decrypt function. cm = ciphertext or plaintext,
@@ -553,8 +555,8 @@ void rsa_clear_keys(RsaKey *key)
    Decrypt: m = c ^ d mod n 
 */
 
-void rsa_en_de_crypt(SilcInt *cm, SilcInt *mc, 
-                    SilcInt *expo, SilcInt *modu)
+void rsa_en_de_crypt(SilcMPInt *cm, SilcMPInt *mc, 
+                    SilcMPInt *expo, SilcMPInt *modu)
 {
-  silc_mp_powm(cm, mc, expo, modu);
+  silc_mp_pow_mod(cm, mc, expo, modu);
 }
index 6244feff6b474cf4af7dca50daa8d99db100eb91..5f8262fa80f85c7194c6c8bec9b2c1b3c9e060b9 100644 (file)
 #ifndef RSA_H
 #define RSA_H
 
-#include "rsa_internal.h"
-
-/* Data context for RSA */
-typedef struct {
-  SilcInt src;
-  SilcInt dst;
-  SilcInt exp;
-  SilcInt mod;
-} RsaDataContext;
-
-RsaDataContext *silc_rsa_data_context;
-
 /*
  * SILC PKCS API for RSA
  */
@@ -44,8 +32,6 @@ SILC_PKCS_API_GET_PRIVATE_KEY(rsa);
 SILC_PKCS_API_SET_PUBLIC_KEY(rsa);
 SILC_PKCS_API_SET_PRIVATE_KEY(rsa);
 SILC_PKCS_API_CONTEXT_LEN(rsa);
-SILC_PKCS_API_DATA_CONTEXT_LEN(rsa);
-SILC_PKCS_API_SET_ARG(rsa);
 SILC_PKCS_API_ENCRYPT(rsa);
 SILC_PKCS_API_DECRYPT(rsa);
 SILC_PKCS_API_SIGN(rsa);
index ab18c61f92adf1782740318e076d8adc4926e0fa..c4feb3158d341d773f545269d1c4ff900f995597 100644 (file)
 /* RSA Keys, includes both Private and Public key */
 typedef struct {
   int bits;                    /* bits in key */
-  SilcInt p;                   /* prime p */
-  SilcInt q;                   /* prime q */
-  SilcInt n;                   /* modulus */
-  SilcInt e;                   /* public exponent */
-  SilcInt d;                   /* private exponent */
+  char pub_set;                        /* TRUE is n and e is set */
+  char prv_set;                        /* TRUE if d is set */
+  SilcMPInt n;                 /* modulus */
+  SilcMPInt e;                 /* public exponent */
+  SilcMPInt d;                 /* private exponent */
 } RsaKey;
 
-void rsa_generate_keys(RsaKey *key, unsigned int bits, 
-                      SilcInt *p, SilcInt *q);
+void rsa_generate_keys(RsaKey *key, uint32 bits, 
+                      SilcMPInt *p, SilcMPInt *q);
 void rsa_clear_keys(RsaKey *key);
-void rsa_en_de_crypt(SilcInt *cm, SilcInt *mc, 
-                    SilcInt *expo, SilcInt *modu);
+void rsa_en_de_crypt(SilcMPInt *cm, SilcMPInt *mc, 
+                    SilcMPInt *expo, SilcMPInt *modu);
 
 #endif
diff --git a/lib/silccrypt/safer.c b/lib/silccrypt/safer.c
deleted file mode 100644 (file)
index bb0a3bc..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         SAFER+ by Cylink                                             */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-\r
-/* Timing data for SAFER+ (safer.c)\r
-\r
-Core timing without I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    4278 cycles\r
-Encrypt:      1722 cycles =    14.9 mbits/sec\r
-Decrypt:      1709 cycles =    15.0 mbits/sec\r
-Mean:         1716 cycles =    14.9 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    7426 cycles\r
-Encrypt:      2555 cycles =    10.0 mbits/sec\r
-Decrypt:      2530 cycles =    10.1 mbits/sec\r
-Mean:         2543 cycles =    10.1 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:   11313 cycles\r
-Encrypt:      3391 cycles =     7.5 mbits/sec\r
-Decrypt:      3338 cycles =     7.7 mbits/sec\r
-Mean:         3365 cycles =     7.6 mbits/sec\r
-\r
-Full timing with I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    3977 cycles\r
-Encrypt:      1751 cycles =    14.6 mbits/sec\r
-Decrypt:      1734 cycles =    14.8 mbits/sec\r
-Mean:         1743 cycles =    14.7 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    6490 cycles\r
-Encrypt:      2574 cycles =     9.9 mbits/sec\r
-Decrypt:      2549 cycles =    10.0 mbits/sec\r
-Mean:         2562 cycles =    10.0 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    9487 cycles\r
-Encrypt:      3412 cycles =     7.5 mbits/sec\r
-Decrypt:      3372 cycles =     7.6 mbits/sec\r
-Mean:         3392 cycles =     7.5 mbits/sec\r
-\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include "safer_internal.h"\r
-\r
-u1byte  expf[256] =\r
-{     1,  45, 226, 147, 190,  69,  21, 174, 120,   3, 135, 164, 184,  56, 207,  63, \r
-      8, 103,   9, 148, 235,  38, 168, 107, 189,  24,  52,  27, 187, 191, 114, 247, \r
-     64,  53,  72, 156,  81,  47,  59,  85, 227, 192, 159, 216, 211, 243, 141, 177, \r
-    255, 167,  62, 220, 134, 119, 215, 166,  17, 251, 244, 186, 146, 145, 100, 131, \r
-    241,  51, 239, 218,  44, 181, 178,  43, 136, 209, 153, 203, 140, 132,  29,  20, \r
-    129, 151, 113, 202,  95, 163, 139,  87,  60, 130, 196,  82,  92,  28, 232, 160, \r
-      4, 180, 133,  74, 246,  19,  84, 182, 223,  12,  26, 142, 222, 224,  57, 252, \r
-     32, 155,  36,  78, 169, 152, 158, 171, 242,  96, 208, 108, 234, 250, 199, 217, \r
-      0, 212,  31, 110,  67, 188, 236,  83, 137, 254, 122,  93,  73, 201,  50, 194, \r
-    249, 154, 248, 109,  22, 219,  89, 150,  68, 233, 205, 230,  70,  66, 143,  10, \r
-    193, 204, 185, 101, 176, 210, 198, 172,  30,  65,  98,  41,  46,  14, 116,  80, \r
-      2,  90, 195,  37, 123, 138,  42,  91, 240,   6,  13,  71, 111, 112, 157, 126, \r
-     16, 206,  18,  39, 213,  76,  79, 214, 121,  48, 104,  54, 117, 125, 228, 237, \r
-    128, 106, 144,  55, 162,  94, 118, 170, 197, 127,  61, 175, 165, 229,  25,  97, \r
-    253,  77, 124, 183,  11, 238, 173,  75,  34, 245, 231, 115,  35,  33, 200,   5, \r
-    225, 102, 221, 179,  88, 105,  99,  86,  15, 161,  49, 149,  23,   7,  58,  40 \r
-};\r
-\r
-u1byte logf[512] = \r
-{\r
-    128,   0, 176,   9,  96, 239, 185, 253,  16,  18, 159, 228, 105, 186, 173, 248, \r
-    192,  56, 194, 101,  79,   6, 148, 252,  25, 222, 106,  27,  93,  78, 168, 130, \r
-    112, 237, 232, 236, 114, 179,  21, 195, 255, 171, 182,  71,  68,   1, 172,  37, \r
-    201, 250, 142,  65,  26,  33, 203, 211,  13, 110, 254,  38,  88, 218,  50,  15, \r
-     32, 169, 157, 132, 152,   5, 156, 187,  34, 140,  99, 231, 197, 225, 115, 198, \r
-    175,  36,  91, 135, 102,  39, 247,  87, 244, 150, 177, 183,  92, 139, 213,  84, \r
-    121, 223, 170, 246,  62, 163, 241,  17, 202, 245, 209,  23, 123, 147, 131, 188, \r
-    189,  82,  30, 235, 174, 204, 214,  53,   8, 200, 138, 180, 226, 205, 191, 217, \r
-    208,  80,  89,  63,  77,  98,  52,  10,  72, 136, 181,  86,  76,  46, 107, 158, \r
-    210,  61,  60,   3,  19, 251, 151,  81, 117,  74, 145, 113,  35, 190, 118,  42, \r
-     95, 249, 212,  85,  11, 220,  55,  49,  22, 116, 215, 119, 167, 230,   7, 219, \r
-    164,  47,  70, 243,  97,  69, 103, 227,  12, 162,  59,  28, 133,  24,   4,  29, \r
-     41, 160, 143, 178,  90, 216, 166, 126, 238, 141,  83,  75, 161, 154, 193,  14, \r
-    122,  73, 165,  44, 129, 196, 199,  54,  43, 127,  67, 149,  51, 242, 108, 104, \r
-    109, 240,   2,  40, 206, 221, 155, 234,  94, 153, 124,  20, 134, 207, 229,  66, \r
-    184,  64, 120,  45,  58, 233, 100,  31, 146, 144, 125,  57, 111, 224, 137,  48,\r
-\r
-    128,   0, 176,   9,  96, 239, 185, 253,  16,  18, 159, 228, 105, 186, 173, 248, \r
-    192,  56, 194, 101,  79,   6, 148, 252,  25, 222, 106,  27,  93,  78, 168, 130, \r
-    112, 237, 232, 236, 114, 179,  21, 195, 255, 171, 182,  71,  68,   1, 172,  37, \r
-    201, 250, 142,  65,  26,  33, 203, 211,  13, 110, 254,  38,  88, 218,  50,  15, \r
-     32, 169, 157, 132, 152,   5, 156, 187,  34, 140,  99, 231, 197, 225, 115, 198, \r
-    175,  36,  91, 135, 102,  39, 247,  87, 244, 150, 177, 183,  92, 139, 213,  84, \r
-    121, 223, 170, 246,  62, 163, 241,  17, 202, 245, 209,  23, 123, 147, 131, 188, \r
-    189,  82,  30, 235, 174, 204, 214,  53,   8, 200, 138, 180, 226, 205, 191, 217, \r
-    208,  80,  89,  63,  77,  98,  52,  10,  72, 136, 181,  86,  76,  46, 107, 158, \r
-    210,  61,  60,   3,  19, 251, 151,  81, 117,  74, 145, 113,  35, 190, 118,  42, \r
-     95, 249, 212,  85,  11, 220,  55,  49,  22, 116, 215, 119, 167, 230,   7, 219, \r
-    164,  47,  70, 243,  97,  69, 103, 227,  12, 162,  59,  28, 133,  24,   4,  29, \r
-     41, 160, 143, 178,  90, 216, 166, 126, 238, 141,  83,  75, 161, 154, 193,  14, \r
-    122,  73, 165,  44, 129, 196, 199,  54,  43, 127,  67, 149,  51, 242, 108, 104, \r
-    109, 240,   2,  40, 206, 221, 155, 234,  94, 153, 124,  20, 134, 207, 229,  66, \r
-    184,  64, 120,  45,  58, 233, 100,  31, 146, 144, 125,  57, 111, 224, 137,  48\r
-};\r
-\r
-u4byte *safer_set_key(SaferContext *ctx,\r
-                     const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u1byte  by, lk[33];\r
-    u4byte  i, j, k, l, m;\r
-    u1byte *l_key = ctx->l_key;\r
-\r
-    get_key(lk, key_len);\r
-\r
-    ctx->k_bytes = k_bytes = key_len / 8; lk[k_bytes] = 0;\r
-\r
-    for(i = 0; i < k_bytes; ++i)\r
-    {\r
-        lk[k_bytes] ^= lk[i]; l_key[i] = lk[i];\r
-    }\r
-\r
-    for(i = 0; i < k_bytes; ++i)\r
-    {\r
-        for(j = 0; j <= k_bytes; ++j)\r
-        {\r
-            by = lk[j]; lk[j] = by << 3 | by >> 5;\r
-        }\r
-\r
-        k = 17 * i + 35; l = 16 * i + 16; m = i + 1;\r
-\r
-        if(i < 16)\r
-        {\r
-            for(j = 0; j < 16; ++j)\r
-            {\r
-                l_key[l + j] = lk[m] + expf[expf[(k + j) & 255]];\r
-\r
-                m = (m == k_bytes ? 0 : m + 1);\r
-            }\r
-        }\r
-        else\r
-        {\r
-            for(j = 0; j < 16; ++j)\r
-            {\r
-                l_key[l + j] = lk[m] + expf[(k + j) & 255];\r
-\r
-                m = (m == k_bytes ? 0 : m + 1);\r
-            }\r
-        }\r
-    }\r
-    return (u4byte*)l_key;\r
-};\r
-\r
-void do_fr(u1byte x[16], u1byte *kp)\r
-{   u1byte  t;\r
-\r
-    x[ 0] = expf[x[ 0] ^ kp[ 0]] + kp[16];\r
-    x[ 1] = logf[x[ 1] + kp[ 1]] ^ kp[17]; \r
-    x[ 2] = logf[x[ 2] + kp[ 2]] ^ kp[18]; \r
-    x[ 3] = expf[x[ 3] ^ kp[ 3]] + kp[19];\r
-\r
-    x[ 4] = expf[x[ 4] ^ kp[ 4]] + kp[20];\r
-    x[ 5] = logf[x[ 5] + kp[ 5]] ^ kp[21]; \r
-    x[ 6] = logf[x[ 6] + kp[ 6]] ^ kp[22]; \r
-    x[ 7] = expf[x[ 7] ^ kp[ 7]] + kp[23];\r
\r
-    x[ 8] = expf[x[ 8] ^ kp[ 8]] + kp[24];\r
-    x[ 9] = logf[x[ 9] + kp[ 9]] ^ kp[25]; \r
-    x[10] = logf[x[10] + kp[10]] ^ kp[26]; \r
-    x[11] = expf[x[11] ^ kp[11]] + kp[27];\r
-\r
-    x[12] = expf[x[12] ^ kp[12]] + kp[28];\r
-    x[13] = logf[x[13] + kp[13]] ^ kp[29]; \r
-    x[14] = logf[x[14] + kp[14]] ^ kp[30]; \r
-    x[15] = expf[x[15] ^ kp[15]] + kp[31];\r
-\r
-    x[ 1] += x[ 0]; x[ 0] += x[ 1];\r
-    x[ 3] += x[ 2]; x[ 2] += x[ 3];\r
-    x[ 5] += x[ 4]; x[ 4] += x[ 5];\r
-    x[ 7] += x[ 6]; x[ 6] += x[ 7];\r
-    x[ 9] += x[ 8]; x[ 8] += x[ 9];\r
-    x[11] += x[10]; x[10] += x[11];\r
-    x[13] += x[12]; x[12] += x[13];\r
-    x[15] += x[14]; x[14] += x[15];\r
-\r
-    x[ 7] += x[ 0]; x[ 0] += x[ 7];\r
-    x[ 1] += x[ 2]; x[ 2] += x[ 1];\r
-    x[ 3] += x[ 4]; x[ 4] += x[ 3];\r
-    x[ 5] += x[ 6]; x[ 6] += x[ 5];\r
-    x[11] += x[ 8]; x[ 8] += x[11];\r
-    x[ 9] += x[10]; x[10] += x[ 9];\r
-    x[15] += x[12]; x[12] += x[15];\r
-    x[13] += x[14]; x[14] += x[13];\r
-\r
-    x[ 3] += x[ 0]; x[ 0] += x[ 3];\r
-    x[15] += x[ 2]; x[ 2] += x[15];\r
-    x[ 7] += x[ 4]; x[ 4] += x[ 7];\r
-    x[ 1] += x[ 6]; x[ 6] += x[ 1];\r
-    x[ 5] += x[ 8]; x[ 8] += x[ 5];\r
-    x[13] += x[10]; x[10] += x[13];\r
-    x[11] += x[12]; x[12] += x[11];\r
-    x[ 9] += x[14]; x[14] += x[ 9];\r
-\r
-    x[13] += x[ 0]; x[ 0] += x[13];\r
-    x[ 5] += x[ 2]; x[ 2] += x[ 5];\r
-    x[ 9] += x[ 4]; x[ 4] += x[ 9];\r
-    x[11] += x[ 6]; x[ 6] += x[11];\r
-    x[15] += x[ 8]; x[ 8] += x[15];\r
-    x[ 1] += x[10]; x[10] += x[ 1];\r
-    x[ 3] += x[12]; x[12] += x[ 3];\r
-    x[ 7] += x[14]; x[14] += x[ 7];\r
-\r
-    t = x[0]; x[0] = x[14]; x[14] = x[12]; x[12] = x[10]; x[10] = x[2]; \r
-    x[2] = x[8]; x[8] = x[4]; x[4] = t;\r
-\r
-    t = x[1]; x[1] = x[7]; x[7] = x[11]; x[11] = x[5]; x[5] = x[13]; x[13] = t; \r
-    \r
-    t = x[15]; x[15] = x[3]; x[3] = t;\r
-};\r
-\r
-void do_ir(u1byte x[16], u1byte *kp)\r
-{   u1byte  t;\r
-\r
-    t = x[3]; x[3] = x[15]; x[15] = t; \r
-\r
-    t = x[13]; x[13] = x[5]; x[5] = x[11]; x[11] = x[7]; x[7] = x[1]; x[1] = t; \r
-\r
-    t = x[4]; x[4] = x[8]; x[8] = x[2]; x[2] = x[10]; \r
-    x[10] = x[12]; x[12] = x[14]; x[14] = x[0]; x[0] = t; \r
-\r
-    x[14] -= x[ 7]; x[ 7] -= x[14]; \r
-    x[12] -= x[ 3]; x[ 3] -= x[12];\r
-    x[10] -= x[ 1]; x[ 1] -= x[10];\r
-    x[ 8] -= x[15]; x[15] -= x[ 8];\r
-    x[ 6] -= x[11]; x[11] -= x[ 6]; \r
-    x[ 4] -= x[ 9]; x[ 9] -= x[ 4];\r
-    x[ 2] -= x[ 5]; x[ 5] -= x[ 2]; \r
-    x[ 0] -= x[13]; x[13] -= x[ 0]; \r
-\r
-    x[14] -= x[ 9]; x[ 9] -= x[14]; \r
-    x[12] -= x[11]; x[11] -= x[12]; \r
-    x[10] -= x[13]; x[13] -= x[10]; \r
-    x[ 8] -= x[ 5]; x[ 5] -= x[ 8]; \r
-    x[ 6] -= x[ 1]; x[ 1] -= x[ 6]; \r
-    x[ 4] -= x[ 7]; x[ 7] -= x[ 4]; \r
-    x[ 2] -= x[15]; x[15] -= x[ 2]; \r
-    x[ 0] -= x[ 3]; x[ 3] -= x[ 0]; \r
-\r
-    x[14] -= x[13]; x[13] -= x[14]; \r
-    x[12] -= x[15]; x[15] -= x[12]; \r
-    x[10] -= x[ 9]; x[ 9] -= x[10]; \r
-    x[ 8] -= x[11]; x[11] -= x[ 8];     \r
-    x[ 6] -= x[ 5]; x[ 5] -= x[ 6]; \r
-    x[ 4] -= x[ 3]; x[ 3] -= x[ 4]; \r
-    x[ 2] -= x[ 1]; x[ 1] -= x[ 2]; \r
-    x[ 0] -= x[ 7]; x[ 7] -= x[ 0]; \r
-\r
-    x[14] -= x[15]; x[15] -= x[14]; \r
-    x[12] -= x[13]; x[13] -= x[12];\r
-    x[10] -= x[11]; x[11] -= x[10]; \r
-    x[ 8] -= x[ 9]; x[ 9] -= x[ 8]; \r
-    x[ 6] -= x[ 7]; x[ 7] -= x[ 6];\r
-    x[ 4] -= x[ 5]; x[ 5] -= x[ 4]; \r
-    x[ 2] -= x[ 3]; x[ 3] -= x[ 2]; \r
-    x[ 0] -= x[ 1]; x[ 1] -= x[ 0]; \r
-    \r
-    x[ 0] = logf[x[ 0] - kp[16] + 256] ^ kp[ 0];\r
-    x[ 1] = expf[x[ 1] ^ kp[17]] - kp[ 1];\r
-    x[ 2] = expf[x[ 2] ^ kp[18]] - kp[ 2];\r
-    x[ 3] = logf[x[ 3] - kp[19] + 256] ^ kp[ 3];\r
-\r
-    x[ 4] = logf[x[ 4] - kp[20] + 256] ^ kp[ 4];\r
-    x[ 5] = expf[x[ 5] ^ kp[21]] - kp[ 5];\r
-    x[ 6] = expf[x[ 6] ^ kp[22]] - kp[ 6];\r
-    x[ 7] = logf[x[ 7] - kp[23] + 256] ^ kp[ 7];\r
-\r
-    x[ 8] = logf[x[ 8] - kp[24] + 256] ^ kp[ 8];\r
-    x[ 9] = expf[x[ 9] ^ kp[25]] - kp[ 9];\r
-    x[10] = expf[x[10] ^ kp[26]] - kp[10];\r
-    x[11] = logf[x[11] - kp[27] + 256] ^ kp[11];\r
-\r
-    x[12] = logf[x[12] - kp[28] + 256] ^ kp[12];\r
-    x[13] = expf[x[13] ^ kp[29]] - kp[13];\r
-    x[14] = expf[x[14] ^ kp[30]] - kp[14];\r
-    x[15] = logf[x[15] - kp[31] + 256] ^ kp[15];\r
-};\r
-\r
-void safer_encrypt(SaferContext *ctx,\r
-                  const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u1byte  blk[16], *kp;\r
-    u1byte *l_key = ctx->l_key;\r
-    u4byte k_bytes = ctx->k_bytes;\r
-\r
-    get_block(blk);\r
-\r
-    do_fr(blk, l_key);       do_fr(blk, l_key +  32); \r
-    do_fr(blk, l_key +  64); do_fr(blk, l_key +  96);\r
-    do_fr(blk, l_key + 128); do_fr(blk, l_key + 160);\r
-    do_fr(blk, l_key + 192); do_fr(blk, l_key + 224);\r
-    \r
-    if(k_bytes > 16)\r
-    {\r
-        do_fr(blk, l_key + 256); do_fr(blk, l_key + 288); \r
-        do_fr(blk, l_key + 320); do_fr(blk, l_key + 352);\r
-    }\r
-\r
-    if(k_bytes > 24)\r
-    {\r
-        do_fr(blk, l_key + 384); do_fr(blk, l_key + 416); \r
-        do_fr(blk, l_key + 448); do_fr(blk, l_key + 480);\r
-    }\r
-\r
-    kp = l_key + 16 * k_bytes;\r
-\r
-    blk[ 0] ^= kp[ 0]; blk[ 1] += kp[ 1];\r
-    blk[ 2] += kp[ 2]; blk[ 3] ^= kp[ 3]; \r
-    blk[ 4] ^= kp[ 4]; blk[ 5] += kp[ 5];\r
-    blk[ 6] += kp[ 6]; blk[ 7] ^= kp[ 7]; \r
-    blk[ 8] ^= kp[ 8]; blk[ 9] += kp[ 9];\r
-    blk[10] += kp[10]; blk[11] ^= kp[11]; \r
-    blk[12] ^= kp[12]; blk[13] += kp[13];\r
-    blk[14] += kp[14]; blk[15] ^= kp[15]; \r
-\r
-    put_block(blk);\r
-};\r
-\r
-void safer_decrypt(SaferContext *ctx,\r
-                  const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u1byte  blk[16], *kp;\r
-    u1byte *l_key = ctx->l_key;\r
-    u4byte k_bytes = ctx->k_bytes;\r
-\r
-    get_block(blk);\r
-\r
-    kp = l_key + 16 * k_bytes;\r
-\r
-    blk[ 0] ^= kp[ 0]; blk[ 1] -= kp[ 1];\r
-    blk[ 2] -= kp[ 2]; blk[ 3] ^= kp[ 3];\r
-    blk[ 4] ^= kp[ 4]; blk[ 5] -= kp[ 5];\r
-    blk[ 6] -= kp[ 6]; blk[ 7] ^= kp[ 7];\r
-    blk[ 8] ^= kp[ 8]; blk[ 9] -= kp[ 9];\r
-    blk[10] -= kp[10]; blk[11] ^= kp[11];\r
-    blk[12] ^= kp[12]; blk[13] -= kp[13];\r
-    blk[14] -= kp[14]; blk[15] ^= kp[15];\r
-\r
-    if(k_bytes > 24)\r
-    {\r
-        do_ir(blk, l_key + 480); do_ir(blk, l_key + 448); \r
-        do_ir(blk, l_key + 416); do_ir(blk, l_key + 384);\r
-    }\r
-\r
-    if(k_bytes > 16)\r
-    {\r
-        do_ir(blk, l_key + 352); do_ir(blk, l_key + 320); \r
-        do_ir(blk, l_key + 288); do_ir(blk, l_key + 256);\r
-    }\r
-\r
-    do_ir(blk, l_key + 224); do_ir(blk, l_key + 192); \r
-    do_ir(blk, l_key + 160); do_ir(blk, l_key + 128);\r
-    do_ir(blk, l_key +  96); do_ir(blk, l_key +  64); \r
-    do_ir(blk, l_key +  32); do_ir(blk, l_key);\r
-\r
-    put_block(blk);\r
-};\r
diff --git a/lib/silccrypt/safer.h b/lib/silccrypt/safer.h
deleted file mode 100644 (file)
index 106b4ea..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-
-  safer.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef SAFER_H
-#define SAFER_H
-
-#include "safer_internal.h"
-
-/* 
- * SILC Crypto API for Safer
- */
-
-/* Sets the key for the cipher. */
-
-inline int silc_safer_init(void *context, 
-                          const unsigned char *key, 
-                          size_t keylen)
-{
-  safer_set_key((SaferContext *)context, (unsigned int *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-inline int silc_safer_set_string_as_key(void *context, 
-                                       const unsigned char *string,
-                                       size_t keylen)
-{
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return 1;
-}
-
-/* Returns the size of the cipher context. */
-
-inline size_t silc_safer_context_len()
-{
-  return sizeof(SaferContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-inline int silc_safer_encrypt_cbc(void *context,
-                                 const unsigned char *src,
-                                 unsigned char *dst,
-                                 size_t len,
-                                 unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  safer_encrypt((SaferContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    safer_encrypt((SaferContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-inline int silc_safer_decrypt_cbc(void *context,
-                                 const unsigned char *src,
-                                 unsigned char *dst,
-                                 size_t len,
-                                 unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  safer_decrypt((SaferContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    safer_decrypt((SaferContext *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-#endif
diff --git a/lib/silccrypt/safer_internal.h b/lib/silccrypt/safer_internal.h
deleted file mode 100644 (file)
index ee3bb1b..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-
-  safer_internal.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SAFER_INTERNAL_H
-#define SAFER_INTERNAL_H
-
-/* Cipher's context */
-typedef struct {
-  u1byte l_key[33 * 16];
-  u4byte k_bytes;
-} SaferContext;
-
-/* Prototypes */
-u4byte *safer_set_key(SaferContext *ctx,
-                     const u4byte in_key[], const u4byte key_len);
-void safer_encrypt(SaferContext *ctx,
-                  const u4byte in_blk[4], u4byte out_blk[4]);
-void safer_decrypt(SaferContext *ctx,
-                  const u4byte in_blk[4], u4byte out_blk[4]);
-
-#endif
diff --git a/lib/silccrypt/serpent.c b/lib/silccrypt/serpent.c
deleted file mode 100644 (file)
index 63640c8..0000000
+++ /dev/null
@@ -1,657 +0,0 @@
-/* Modified for SILC. -Pekka */x\r
-\r
-/* This is an independent implementation of the encryption algorithm:   */\r
-/*                                                                      */\r
-/*         Serpent by Ross Anderson, Eli Biham and Lars Knudsen         */\r
-/*                                                                      */\r
-/* which is a candidate algorithm in the Advanced Encryption Standard   */\r
-/* programme of the US National Institute of Standards and Technology.  */\r
-/*                                                                      */\r
-/* Copyright in this implementation is held by Dr B R Gladman but I     */\r
-/* hereby give permission for its free direct or derivative use subject */\r
-/* to acknowledgment of its origin and compliance with any conditions   */\r
-/* that the originators of the algorithm place on its exploitation.     */\r
-/*                                                                      */\r
-/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */\r
-\r
-/* Timing data for Serpent (serpent.c)\r
-\r
-Core timing without I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    2402 cycles\r
-Encrypt:       952 cycles =    26.9 mbits/sec\r
-Decrypt:       914 cycles =    28.0 mbits/sec\r
-Mean:          933 cycles =    27.4 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    2449 cycles\r
-Encrypt:       952 cycles =    26.9 mbits/sec\r
-Decrypt:       914 cycles =    28.0 mbits/sec\r
-Mean:          933 cycles =    27.4 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    2349 cycles\r
-Encrypt:       952 cycles =    26.9 mbits/sec\r
-Decrypt:       914 cycles =    28.0 mbits/sec\r
-Mean:          933 cycles =    27.4 mbits/sec\r
-\r
-Full timing with I/O endian conversion:\r
-\r
-128 bit key:\r
-Key Setup:    2415 cycles\r
-Encrypt:       985 cycles =    26.0 mbits/sec\r
-Decrypt:       954 cycles =    26.8 mbits/sec\r
-Mean:          970 cycles =    26.4 mbits/sec\r
-\r
-192 bit key:\r
-Key Setup:    2438 cycles\r
-Encrypt:       985 cycles =    26.0 mbits/sec\r
-Decrypt:       954 cycles =    26.8 mbits/sec\r
-Mean:          970 cycles =    26.4 mbits/sec\r
-\r
-256 bit key:\r
-Key Setup:    2463 cycles\r
-Encrypt:       985 cycles =    26.0 mbits/sec\r
-Decrypt:       954 cycles =    26.8 mbits/sec\r
-Mean:          970 cycles =    26.4 mbits/sec\r
-\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <sys/types.h>\r
-#include "serpent_internal.h"\r
-\r
-/* Partially optimised Serpent S Box boolean functions derived  */\r
-/* using a recursive descent analyser but without a full search */\r
-/* of all subtrees. This set of S boxes is the result of work   */\r
-/* by Sam Simpson and Brian Gladman using the spare time on a   */\r
-/* cluster of high capacity servers to search for S boxes with  */\r
-/* this customised search engine.                               */\r
-/*                                                              */\r
-/* Copyright:   Dr B. R Gladman (gladman@seven77.demon.co.uk)   */\r
-/*              and Sam Simpson (s.simpson@mia.co.uk)           */ \r
-/*              17th December 1998                              */\r
-/*                                                              */\r
-/* We hereby give permission for information in this file to be */\r
-/* used freely subject only to acknowledgement of its origin    */\r
-\r
-/* 15 terms */\r
-\r
-#define sb0(a,b,c,d,e,f,g,h)    \\r
-    t1 = a ^ d;     \\r
-    t2 = a & d;     \\r
-    t3 = c ^ t1;    \\r
-    t6 = b & t1;    \\r
-    t4 = b ^ t3;    \\r
-    t10 = ~t3;      \\r
-    h = t2 ^ t4;    \\r
-    t7 = a ^ t6;    \\r
-    t14 = ~t7;      \\r
-    t8 = c | t7;    \\r
-    t11 = t3 ^ t7;  \\r
-    g = t4 ^ t8;    \\r
-    t12 = h & t11;  \\r
-    f = t10 ^ t12;  \\r
-    e = t12 ^ t14\r
-\r
-/* 15 terms */\r
-\r
-#define ib0(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~a;        \\r
-    t2 = a ^ b;     \\r
-    t3 = t1 | t2;   \\r
-    t4 = d ^ t3;    \\r
-    t7 = d & t2;    \\r
-    t5 = c ^ t4;    \\r
-    t8 = t1 ^ t7;   \\r
-    g = t2 ^ t5;    \\r
-    t11 = a & t4;   \\r
-    t9 = g & t8;    \\r
-    t14 = t5 ^ t8;  \\r
-    f = t4 ^ t9;    \\r
-    t12 = t5 | f;   \\r
-    h = t11 ^ t12;  \\r
-    e = h ^ t14\r
-\r
-/* 14 terms!  */\r
-\r
-#define sb1(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~a;        \\r
-    t2 = b ^ t1;    \\r
-    t3 = a | t2;    \\r
-    t4 = d | t2;    \\r
-    t5 = c ^ t3;    \\r
-    g = d ^ t5;     \\r
-    t7 = b ^ t4;    \\r
-    t8 = t2 ^ g;    \\r
-    t9 = t5 & t7;   \\r
-    h = t8 ^ t9;    \\r
-    t11 = t5 ^ t7;  \\r
-    f = h ^ t11;    \\r
-    t13 = t8 & t11; \\r
-    e = t5 ^ t13\r
-\r
-/* 17 terms */\r
-\r
-#define ib1(a,b,c,d,e,f,g,h)    \\r
-    t1 = a ^ d;     \\r
-    t2 = a & b;     \\r
-    t3 = b ^ c;     \\r
-    t4 = a ^ t3;    \\r
-    t5 = b | d;     \\r
-    t7 = c | t1;    \\r
-    h = t4 ^ t5;    \\r
-    t8 = b ^ t7;    \\r
-    t11 = ~t2;      \\r
-    t9 = t4 & t8;   \\r
-    f = t1 ^ t9;    \\r
-    t13 = t9 ^ t11; \\r
-    t12 = h & f;    \\r
-    g = t12 ^ t13;  \\r
-    t15 = a & d;    \\r
-    t16 = c ^ t13;  \\r
-    e = t15 ^ t16\r
-\r
-/* 16 terms */\r
-\r
-#define sb2(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~a;        \\r
-    t2 = b ^ d;     \\r
-    t3 = c & t1;    \\r
-    t13 = d | t1;   \\r
-    e = t2 ^ t3;    \\r
-    t5 = c ^ t1;    \\r
-    t6 = c ^ e;     \\r
-    t7 = b & t6;    \\r
-    t10 = e | t5;   \\r
-    h = t5 ^ t7;    \\r
-    t9 = d | t7;    \\r
-    t11 = t9 & t10; \\r
-    t14 = t2 ^ h;   \\r
-    g = a ^ t11;    \\r
-    t15 = g ^ t13;  \\r
-    f = t14 ^ t15\r
-\r
-/* 16 terms */\r
-\r
-#define ib2(a,b,c,d,e,f,g,h)    \\r
-    t1 = b ^ d;     \\r
-    t2 = ~t1;       \\r
-    t3 = a ^ c;     \\r
-    t4 = c ^ t1;    \\r
-    t7 = a | t2;    \\r
-    t5 = b & t4;    \\r
-    t8 = d ^ t7;    \\r
-    t11 = ~t4;      \\r
-    e = t3 ^ t5;    \\r
-    t9 = t3 | t8;   \\r
-    t14 = d & t11;  \\r
-    h = t1 ^ t9;    \\r
-    t12 = e | h;    \\r
-    f = t11 ^ t12;  \\r
-    t15 = t3 ^ t12; \\r
-    g = t14 ^ t15\r
-\r
-/* 17 terms */\r
-\r
-#define sb3(a,b,c,d,e,f,g,h)    \\r
-    t1 = a ^ c;     \\r
-    t2 = d ^ t1;    \\r
-    t3 = a & t2;    \\r
-    t4 = d ^ t3;    \\r
-    t5 = b & t4;    \\r
-    g = t2 ^ t5;    \\r
-    t7 = a | g;     \\r
-    t8 = b | d;     \\r
-    t11 = a | d;    \\r
-    t9 = t4 & t7;   \\r
-    f = t8 ^ t9;    \\r
-    t12 = b ^ t11;  \\r
-    t13 = g ^ t9;   \\r
-    t15 = t3 ^ t8;  \\r
-    h = t12 ^ t13;  \\r
-    t16 = c & t15;  \\r
-    e = t12 ^ t16\r
-\r
-/* 16 term solution that performs less well than 17 term one\r
-   in my environment (PPro/PII)                                  \r
-\r
-#define sb3(a,b,c,d,e,f,g,h)    \\r
-    t1 = a ^ b;     \\r
-    t2 = a & c;     \\r
-    t3 = a | d;     \\r
-    t4 = c ^ d;     \\r
-    t5 = t1 & t3;   \\r
-    t6 = t2 | t5;   \\r
-    g = t4 ^ t6;    \\r
-    t8 = b ^ t3;    \\r
-    t9 = t6 ^ t8;   \\r
-    t10 = t4 & t9;  \\r
-    e = t1 ^ t10;   \\r
-    t12 = g & e;    \\r
-    f = t9 ^ t12;   \\r
-    t14 = b | d;    \\r
-    t15 = t4 ^ t12; \\r
-    h = t14 ^ t15\r
-*/\r
-\r
-/* 17 terms */\r
-\r
-#define ib3(a,b,c,d,e,f,g,h)    \\r
-    t1 = b ^ c;     \\r
-    t2 = b | c;     \\r
-    t3 = a ^ c;     \\r
-    t7 = a ^ d;     \\r
-    t4 = t2 ^ t3;   \\r
-    t5 = d | t4;    \\r
-    t9 = t2 ^ t7;   \\r
-    e = t1 ^ t5;    \\r
-    t8 = t1 | t5;   \\r
-    t11 = a & t4;   \\r
-    g = t8 ^ t9;    \\r
-    t12 = e | t9;   \\r
-    f = t11 ^ t12;  \\r
-    t14 = a & g;    \\r
-    t15 = t2 ^ t14; \\r
-    t16 = e & t15;  \\r
-    h = t4 ^ t16\r
-\r
-/* 15 terms */\r
-\r
-#define sb4(a,b,c,d,e,f,g,h)    \\r
-    t1 = a ^ d;     \\r
-    t2 = d & t1;    \\r
-    t3 = c ^ t2;    \\r
-    t4 = b | t3;    \\r
-    h = t1 ^ t4;    \\r
-    t6 = ~b;        \\r
-    t7 = t1 | t6;   \\r
-    e = t3 ^ t7;    \\r
-    t9 = a & e;     \\r
-    t10 = t1 ^ t6;  \\r
-    t11 = t4 & t10; \\r
-    g = t9 ^ t11;   \\r
-    t13 = a ^ t3;   \\r
-    t14 = t10 & g;  \\r
-    f = t13 ^ t14\r
-\r
-/* 17 terms */\r
-\r
-#define ib4(a,b,c,d,e,f,g,h)    \\r
-    t1 = c ^ d;     \\r
-    t2 = c | d;     \\r
-    t3 = b ^ t2;    \\r
-    t4 = a & t3;    \\r
-    f = t1 ^ t4;    \\r
-    t6 = a ^ d;     \\r
-    t7 = b | d;     \\r
-    t8 = t6 & t7;   \\r
-    h = t3 ^ t8;    \\r
-    t10 = ~a;       \\r
-    t11 = c ^ h;    \\r
-    t12 = t10 | t11;\\r
-    e = t3 ^ t12;   \\r
-    t14 = c | t4;   \\r
-    t15 = t7 ^ t14; \\r
-    t16 = h | t10;  \\r
-    g = t15 ^ t16\r
-\r
-/* 16 terms */\r
-\r
-#define sb5(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~a;        \\r
-    t2 = a ^ b;     \\r
-    t3 = a ^ d;     \\r
-    t4 = c ^ t1;    \\r
-    t5 = t2 | t3;   \\r
-    e = t4 ^ t5;    \\r
-    t7 = d & e;     \\r
-    t8 = t2 ^ e;    \\r
-    t10 = t1 | e;   \\r
-    f = t7 ^ t8;    \\r
-    t11 = t2 | t7;  \\r
-    t12 = t3 ^ t10; \\r
-    t14 = b ^ t7;   \\r
-    g = t11 ^ t12;  \\r
-    t15 = f & t12;  \\r
-    h = t14 ^ t15\r
-\r
-/* 16 terms */\r
-\r
-#define ib5(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~c;        \\r
-    t2 = b & t1;    \\r
-    t3 = d ^ t2;    \\r
-    t4 = a & t3;    \\r
-    t5 = b ^ t1;    \\r
-    h = t4 ^ t5;    \\r
-    t7 = b | h;     \\r
-    t8 = a & t7;    \\r
-    f = t3 ^ t8;    \\r
-    t10 = a | d;    \\r
-    t11 = t1 ^ t7;  \\r
-    e = t10 ^ t11;  \\r
-    t13 = a ^ c;    \\r
-    t14 = b & t10;  \\r
-    t15 = t4 | t13; \\r
-    g = t14 ^ t15\r
-\r
-/* 15 terms */\r
-\r
-#define sb6(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~a;        \\r
-    t2 = a ^ d;     \\r
-    t3 = b ^ t2;    \\r
-    t4 = t1 | t2;   \\r
-    t5 = c ^ t4;    \\r
-    f = b ^ t5;     \\r
-    t13 = ~t5;      \\r
-    t7 = t2 | f;    \\r
-    t8 = d ^ t7;    \\r
-    t9 = t5 & t8;   \\r
-    g = t3 ^ t9;    \\r
-    t11 = t5 ^ t8;  \\r
-    e = g ^ t11;    \\r
-    t14 = t3 & t11; \\r
-    h = t13 ^ t14\r
-\r
-/* 15 terms */\r
-\r
-#define ib6(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~a;        \\r
-    t2 = a ^ b;     \\r
-    t3 = c ^ t2;    \\r
-    t4 = c | t1;    \\r
-    t5 = d ^ t4;    \\r
-    t13 = d & t1;   \\r
-    f = t3 ^ t5;    \\r
-    t7 = t3 & t5;   \\r
-    t8 = t2 ^ t7;   \\r
-    t9 = b | t8;    \\r
-    h = t5 ^ t9;    \\r
-    t11 = b | h;    \\r
-    e = t8 ^ t11;   \\r
-    t14 = t3 ^ t11; \\r
-    g = t13 ^ t14\r
-\r
-/* 17 terms */\r
-\r
-#define sb7(a,b,c,d,e,f,g,h)    \\r
-    t1 = ~c;        \\r
-    t2 = b ^ c;     \\r
-    t3 = b | t1;    \\r
-    t4 = d ^ t3;    \\r
-    t5 = a & t4;    \\r
-    t7 = a ^ d;     \\r
-    h = t2 ^ t5;    \\r
-    t8 = b ^ t5;    \\r
-    t9 = t2 | t8;   \\r
-    t11 = d & t3;   \\r
-    f = t7 ^ t9;    \\r
-    t12 = t5 ^ f;   \\r
-    t15 = t1 | t4;  \\r
-    t13 = h & t12;  \\r
-    g = t11 ^ t13;  \\r
-    t16 = t12 ^ g;  \\r
-    e = t15 ^ t16\r
-\r
-/* 17 terms */\r
-\r
-#define ib7(a,b,c,d,e,f,g,h)    \\r
-    t1 = a & b;     \\r
-    t2 = a | b;     \\r
-    t3 = c | t1;    \\r
-    t4 = d & t2;    \\r
-    h = t3 ^ t4;    \\r
-    t6 = ~d;        \\r
-    t7 = b ^ t4;    \\r
-    t8 = h ^ t6;    \\r
-    t11 = c ^ t7;   \\r
-    t9 = t7 | t8;   \\r
-    f = a ^ t9;     \\r
-    t12 = d | f;    \\r
-    e = t11 ^ t12;  \\r
-    t14 = a & h;    \\r
-    t15 = t3 ^ f;   \\r
-    t16 = e ^ t14;  \\r
-    g = t15 ^ t16\r
-\r
-#define k_xor(r,a,b,c,d)    \\r
-    a ^= l_key[4 * r +  8]; \\r
-    b ^= l_key[4 * r +  9]; \\r
-    c ^= l_key[4 * r + 10]; \\r
-    d ^= l_key[4 * r + 11]\r
-\r
-#define k_set(r,a,b,c,d)    \\r
-    a = l_key[4 * r +  8];  \\r
-    b = l_key[4 * r +  9];  \\r
-    c = l_key[4 * r + 10];  \\r
-    d = l_key[4 * r + 11]\r
-\r
-#define k_get(r,a,b,c,d)    \\r
-    l_key[4 * r +  8] = a;  \\r
-    l_key[4 * r +  9] = b;  \\r
-    l_key[4 * r + 10] = c;  \\r
-    l_key[4 * r + 11] = d\r
-\r
-/* the linear transformation and its inverse    */\r
-\r
-#define rot(a,b,c,d)    \\r
-    a = rotl(a, 13);    \\r
-    c = rotl(c, 3);     \\r
-    d ^= c ^ (a << 3);  \\r
-    b ^= a ^ c;         \\r
-    d = rotl(d, 7);     \\r
-    b = rotl(b, 1);     \\r
-    a ^= b ^ d;         \\r
-    c ^= d ^ (b << 7);  \\r
-    a = rotl(a, 5);     \\r
-    c = rotl(c, 22)\r
-\r
-#define irot(a,b,c,d)   \\r
-    c = rotr(c, 22);    \\r
-    a = rotr(a, 5);     \\r
-    c ^= d ^ (b << 7);  \\r
-    a ^= b ^ d;         \\r
-    d = rotr(d, 7);     \\r
-    b = rotr(b, 1);     \\r
-    d ^= c ^ (a << 3);  \\r
-    b ^= a ^ c;         \\r
-    c = rotr(c, 3);     \\r
-    a = rotr(a, 13)\r
-\r
-/* initialise the key schedule from the user supplied key   */\r
-\r
-u4byte *serpent_set_key(SerpentContext *ctx,\r
-                       const u4byte in_key[], const u4byte key_len)\r
-{   \r
-    u4byte  i,lk,a,b,c,d,e,f,g,h;\r
-    u4byte  t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16;\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-    if(key_len < 0 || key_len > 256)\r
-\r
-        return (u4byte*)0;\r
-\r
-    i = 0; lk = (key_len + 31) / 32;\r
-    \r
-    while(i < lk)\r
-    {\r
-#ifdef  BLOCK_SWAP\r
-        l_key[i] = io_swap(in_key[lk - i - 1]);\r
-#else\r
-        l_key[i] = in_key[i];\r
-#endif  \r
-        i++;\r
-    }\r
-\r
-    if(key_len < 256)\r
-    {\r
-        while(i < 8)\r
-\r
-            l_key[i++] = 0;\r
-\r
-        i = key_len / 32; lk = 1 << key_len % 32; \r
-\r
-        l_key[i] = l_key[i] & (lk - 1) | lk;\r
-    }\r
-\r
-    for(i = 0; i < 132; ++i)\r
-    {\r
-        lk = l_key[i] ^ l_key[i + 3] ^ l_key[i + 5] \r
-                                ^ l_key[i + 7] ^ 0x9e3779b9 ^ i;\r
-\r
-        l_key[i + 8] = (lk << 11) | (lk >> 21); \r
-    }\r
-\r
-    k_set( 0,a,b,c,d);sb3(a,b,c,d,e,f,g,h);k_get( 0,e,f,g,h);\r
-    k_set( 1,a,b,c,d);sb2(a,b,c,d,e,f,g,h);k_get( 1,e,f,g,h);\r
-    k_set( 2,a,b,c,d);sb1(a,b,c,d,e,f,g,h);k_get( 2,e,f,g,h);\r
-    k_set( 3,a,b,c,d);sb0(a,b,c,d,e,f,g,h);k_get( 3,e,f,g,h);\r
-    k_set( 4,a,b,c,d);sb7(a,b,c,d,e,f,g,h);k_get( 4,e,f,g,h);\r
-    k_set( 5,a,b,c,d);sb6(a,b,c,d,e,f,g,h);k_get( 5,e,f,g,h);\r
-    k_set( 6,a,b,c,d);sb5(a,b,c,d,e,f,g,h);k_get( 6,e,f,g,h);\r
-    k_set( 7,a,b,c,d);sb4(a,b,c,d,e,f,g,h);k_get( 7,e,f,g,h);\r
-    k_set( 8,a,b,c,d);sb3(a,b,c,d,e,f,g,h);k_get( 8,e,f,g,h);\r
-    k_set( 9,a,b,c,d);sb2(a,b,c,d,e,f,g,h);k_get( 9,e,f,g,h);\r
-    k_set(10,a,b,c,d);sb1(a,b,c,d,e,f,g,h);k_get(10,e,f,g,h);\r
-    k_set(11,a,b,c,d);sb0(a,b,c,d,e,f,g,h);k_get(11,e,f,g,h);\r
-    k_set(12,a,b,c,d);sb7(a,b,c,d,e,f,g,h);k_get(12,e,f,g,h);\r
-    k_set(13,a,b,c,d);sb6(a,b,c,d,e,f,g,h);k_get(13,e,f,g,h);\r
-    k_set(14,a,b,c,d);sb5(a,b,c,d,e,f,g,h);k_get(14,e,f,g,h);\r
-    k_set(15,a,b,c,d);sb4(a,b,c,d,e,f,g,h);k_get(15,e,f,g,h);\r
-    k_set(16,a,b,c,d);sb3(a,b,c,d,e,f,g,h);k_get(16,e,f,g,h);\r
-    k_set(17,a,b,c,d);sb2(a,b,c,d,e,f,g,h);k_get(17,e,f,g,h);\r
-    k_set(18,a,b,c,d);sb1(a,b,c,d,e,f,g,h);k_get(18,e,f,g,h);\r
-    k_set(19,a,b,c,d);sb0(a,b,c,d,e,f,g,h);k_get(19,e,f,g,h);\r
-    k_set(20,a,b,c,d);sb7(a,b,c,d,e,f,g,h);k_get(20,e,f,g,h);\r
-    k_set(21,a,b,c,d);sb6(a,b,c,d,e,f,g,h);k_get(21,e,f,g,h);\r
-    k_set(22,a,b,c,d);sb5(a,b,c,d,e,f,g,h);k_get(22,e,f,g,h);\r
-    k_set(23,a,b,c,d);sb4(a,b,c,d,e,f,g,h);k_get(23,e,f,g,h);\r
-    k_set(24,a,b,c,d);sb3(a,b,c,d,e,f,g,h);k_get(24,e,f,g,h);\r
-    k_set(25,a,b,c,d);sb2(a,b,c,d,e,f,g,h);k_get(25,e,f,g,h);\r
-    k_set(26,a,b,c,d);sb1(a,b,c,d,e,f,g,h);k_get(26,e,f,g,h);\r
-    k_set(27,a,b,c,d);sb0(a,b,c,d,e,f,g,h);k_get(27,e,f,g,h);\r
-    k_set(28,a,b,c,d);sb7(a,b,c,d,e,f,g,h);k_get(28,e,f,g,h);\r
-    k_set(29,a,b,c,d);sb6(a,b,c,d,e,f,g,h);k_get(29,e,f,g,h);\r
-    k_set(30,a,b,c,d);sb5(a,b,c,d,e,f,g,h);k_get(30,e,f,g,h);\r
-    k_set(31,a,b,c,d);sb4(a,b,c,d,e,f,g,h);k_get(31,e,f,g,h);\r
-    k_set(32,a,b,c,d);sb3(a,b,c,d,e,f,g,h);k_get(32,e,f,g,h);\r
-\r
-    return l_key;\r
-};\r
-\r
-/* encrypt a block of text  */\r
-\r
-void serpent_encrypt(SerpentContext *ctx,\r
-                    const u4byte in_blk[4], u4byte out_blk[])\r
-{   \r
-    u4byte  a,b,c,d,e,f,g,h;\r
-    u4byte  t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16;\r
-    u4byte *l_key = ctx->l_key;\r
-\r
-#ifdef  BLOCK_SWAP\r
-    a = io_swap(in_blk[3]); b = io_swap(in_blk[2]); \r
-    c = io_swap(in_blk[1]); d = io_swap(in_blk[0]);\r
-#else\r
-    a = in_blk[0]; b = in_blk[1]; c = in_blk[2]; d = in_blk[3];\r
-#endif\r
-\r
-    k_xor( 0,a,b,c,d); sb0(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor( 1,e,f,g,h); sb1(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor( 2,a,b,c,d); sb2(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor( 3,e,f,g,h); sb3(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor( 4,a,b,c,d); sb4(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor( 5,e,f,g,h); sb5(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor( 6,a,b,c,d); sb6(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor( 7,e,f,g,h); sb7(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor( 8,a,b,c,d); sb0(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor( 9,e,f,g,h); sb1(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(10,a,b,c,d); sb2(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(11,e,f,g,h); sb3(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(12,a,b,c,d); sb4(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(13,e,f,g,h); sb5(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(14,a,b,c,d); sb6(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(15,e,f,g,h); sb7(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(16,a,b,c,d); sb0(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(17,e,f,g,h); sb1(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(18,a,b,c,d); sb2(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(19,e,f,g,h); sb3(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(20,a,b,c,d); sb4(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(21,e,f,g,h); sb5(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(22,a,b,c,d); sb6(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(23,e,f,g,h); sb7(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(24,a,b,c,d); sb0(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(25,e,f,g,h); sb1(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(26,a,b,c,d); sb2(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(27,e,f,g,h); sb3(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(28,a,b,c,d); sb4(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(29,e,f,g,h); sb5(e,f,g,h,a,b,c,d); rot(a,b,c,d); \r
-    k_xor(30,a,b,c,d); sb6(a,b,c,d,e,f,g,h); rot(e,f,g,h); \r
-    k_xor(31,e,f,g,h); sb7(e,f,g,h,a,b,c,d); k_xor(32,a,b,c,d); \r
-    \r
-#ifdef  BLOCK_SWAP\r
-    out_blk[3] = io_swap(a); out_blk[2] = io_swap(b); \r
-    out_blk[1] = io_swap(c); out_blk[0] = io_swap(d);\r
-#else\r
-    out_blk[0] = a; out_blk[1] = b; out_blk[2] = c; out_blk[3] = d;\r
-#endif\r
-};\r
-\r
-/* decrypt a block of text  */\r
-\r
-void serpent_decrypt(SerpentContext *ctx,\r
-                    const u4byte in_blk[4], u4byte out_blk[4])\r
-{   \r
-    u4byte  a,b,c,d,e,f,g,h;\r
-    u4byte  t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16;\r
-    u4byte *l_key = ctx->l_key;\r
-    \r
-#ifdef  BLOCK_SWAP\r
-    a = io_swap(in_blk[3]); b = io_swap(in_blk[2]); \r
-    c = io_swap(in_blk[1]); d = io_swap(in_blk[0]);\r
-#else\r
-    a = in_blk[0]; b = in_blk[1]; c = in_blk[2]; d = in_blk[3];\r
-#endif\r
-\r
-    k_xor(32,a,b,c,d); ib7(a,b,c,d,e,f,g,h); k_xor(31,e,f,g,h);\r
-    irot(e,f,g,h); ib6(e,f,g,h,a,b,c,d); k_xor(30,a,b,c,d);\r
-    irot(a,b,c,d); ib5(a,b,c,d,e,f,g,h); k_xor(29,e,f,g,h);\r
-    irot(e,f,g,h); ib4(e,f,g,h,a,b,c,d); k_xor(28,a,b,c,d);\r
-    irot(a,b,c,d); ib3(a,b,c,d,e,f,g,h); k_xor(27,e,f,g,h);\r
-    irot(e,f,g,h); ib2(e,f,g,h,a,b,c,d); k_xor(26,a,b,c,d);\r
-    irot(a,b,c,d); ib1(a,b,c,d,e,f,g,h); k_xor(25,e,f,g,h);\r
-    irot(e,f,g,h); ib0(e,f,g,h,a,b,c,d); k_xor(24,a,b,c,d);\r
-    irot(a,b,c,d); ib7(a,b,c,d,e,f,g,h); k_xor(23,e,f,g,h);\r
-    irot(e,f,g,h); ib6(e,f,g,h,a,b,c,d); k_xor(22,a,b,c,d);\r
-    irot(a,b,c,d); ib5(a,b,c,d,e,f,g,h); k_xor(21,e,f,g,h);\r
-    irot(e,f,g,h); ib4(e,f,g,h,a,b,c,d); k_xor(20,a,b,c,d);\r
-    irot(a,b,c,d); ib3(a,b,c,d,e,f,g,h); k_xor(19,e,f,g,h);\r
-    irot(e,f,g,h); ib2(e,f,g,h,a,b,c,d); k_xor(18,a,b,c,d);\r
-    irot(a,b,c,d); ib1(a,b,c,d,e,f,g,h); k_xor(17,e,f,g,h);\r
-    irot(e,f,g,h); ib0(e,f,g,h,a,b,c,d); k_xor(16,a,b,c,d);\r
-    irot(a,b,c,d); ib7(a,b,c,d,e,f,g,h); k_xor(15,e,f,g,h);\r
-    irot(e,f,g,h); ib6(e,f,g,h,a,b,c,d); k_xor(14,a,b,c,d);\r
-    irot(a,b,c,d); ib5(a,b,c,d,e,f,g,h); k_xor(13,e,f,g,h);\r
-    irot(e,f,g,h); ib4(e,f,g,h,a,b,c,d); k_xor(12,a,b,c,d);\r
-    irot(a,b,c,d); ib3(a,b,c,d,e,f,g,h); k_xor(11,e,f,g,h);\r
-    irot(e,f,g,h); ib2(e,f,g,h,a,b,c,d); k_xor(10,a,b,c,d);\r
-    irot(a,b,c,d); ib1(a,b,c,d,e,f,g,h); k_xor( 9,e,f,g,h);\r
-    irot(e,f,g,h); ib0(e,f,g,h,a,b,c,d); k_xor( 8,a,b,c,d);\r
-    irot(a,b,c,d); ib7(a,b,c,d,e,f,g,h); k_xor( 7,e,f,g,h);\r
-    irot(e,f,g,h); ib6(e,f,g,h,a,b,c,d); k_xor( 6,a,b,c,d);\r
-    irot(a,b,c,d); ib5(a,b,c,d,e,f,g,h); k_xor( 5,e,f,g,h);\r
-    irot(e,f,g,h); ib4(e,f,g,h,a,b,c,d); k_xor( 4,a,b,c,d);\r
-    irot(a,b,c,d); ib3(a,b,c,d,e,f,g,h); k_xor( 3,e,f,g,h);\r
-    irot(e,f,g,h); ib2(e,f,g,h,a,b,c,d); k_xor( 2,a,b,c,d);\r
-    irot(a,b,c,d); ib1(a,b,c,d,e,f,g,h); k_xor( 1,e,f,g,h);\r
-    irot(e,f,g,h); ib0(e,f,g,h,a,b,c,d); k_xor( 0,a,b,c,d);\r
-    \r
-#ifdef  BLOCK_SWAP\r
-    out_blk[3] = io_swap(a); out_blk[2] = io_swap(b); \r
-    out_blk[1] = io_swap(c); out_blk[0] = io_swap(d);\r
-#else\r
-    out_blk[0] = a; out_blk[1] = b; out_blk[2] = c; out_blk[3] = d;\r
-#endif\r
-};\r
diff --git a/lib/silccrypt/serpent.h b/lib/silccrypt/serpent.h
deleted file mode 100644 (file)
index f00798d..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-
-  serpent.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
-
-#ifndef SERPENT_H
-#define SERPENT_H
-
-#include "serpent_internal.h"
-
-/* 
- * SILC Crypto API for Serpent
- */
-
-/* Sets the key for the cipher. */
-
-inline int silc_serpent_init(void *context, 
-                            const unsigned char *key, 
-                            size_t keylen)
-{
-  serpent_set_key((SerpentContext *)context, (unsigned int *)key, keylen);
-  return 1;
-}
-
-/* Sets the string as a new key for the cipher. The string is first
-   hashed and then used as a new key. */
-
-inline int silc_serpent_set_string_as_key(void *context, 
-                                         const unsigned char *string,
-                                         size_t keylen)
-{
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return 1;
-}
-
-/* Returns the size of the cipher context. */
-
-inline size_t silc_serpent_context_len()
-{
-  return sizeof(SerpentContext);
-}
-
-/* Encrypts with the cipher in CBC mode. */
-
-inline int silc_serpent_encrypt_cbc(void *context,
-                                   const unsigned char *src,
-                                   unsigned char *dst,
-                                   size_t len,
-                                   unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  serpent_encrypt((SerpentContext *)context, tmp, out);
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    serpent_encrypt((SerpentContext *)context, tmp, out);
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-/* Decrypts with the cipher in CBC mode. */
-
-inline int silc_serpent_decrypt_cbc(void *context,
-                                   const unsigned char *src,
-                                   unsigned char *dst,
-                                   size_t len,
-                                   unsigned char *iv)
-{
-  unsigned int *in, *out, *tiv;
-  int i;
-
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  serpent_decrypt((SerpentContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
-
-  for (i = 16; i < len; i += 16) {
-    serpent_decrypt((SerpentContext *)context, in, out);
-    out[0] ^= in[0 - 4];
-    out[1] ^= in[1 - 4];
-    out[2] ^= in[2 - 4];
-    out[3] ^= in[3 - 4];
-    in += 4;
-    out += 4;
-  }
-
-  return 1;
-}
-
-#endif
diff --git a/lib/silccrypt/serpent_internal.h b/lib/silccrypt/serpent_internal.h
deleted file mode 100644 (file)
index c777ed0..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-
-  serpent_internal.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef SERPENT_INTERNAL_H
-#define SERPENT_INTERNAL_H
-
-/* Cipher's context */
-typedef struct {
-  u4byte l_key[140];
-} SerpentContext;
-
-/* Prototypes */
-u4byte *serpent_set_key(SerpentContext *ctx,
-                       const u4byte in_key[], const u4byte key_len);
-void serpent_encrypt(SerpentContext *ctx,
-                    const u4byte in_blk[4], u4byte out_blk[]);
-void serpent_decrypt(SerpentContext *ctx,
-                    const u4byte in_blk[4], u4byte out_blk[4]);
-
-#endif
index e60a6d022af587b1c5b40421d1da1c4e068c7cf0..2b7577e1c4478eb669f662774e691f790cbae52d 100644 (file)
@@ -1,3 +1,4 @@
+/* Modified to work on various platforms. -Pekka */
 /*
 SHA-1 in C
 By Steve Reid <steve@edmweb.com>
@@ -5,6 +6,7 @@ By Steve Reid <steve@edmweb.com>
 */
 
 #include "silcincludes.h"
+#include "sha1_internal.h"
 #include "sha1.h"
 
 /* 
@@ -36,192 +38,132 @@ SILC_HASH_API_CONTEXT_LEN(sha1)
   return sizeof(SHA1_CTX);
 }
 
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
-
-/* blk0() and blk() perform the initial expand. */
-/* I got the idea of expanding during the round function from SSLeay */
-#ifdef LITTLE_ENDIAN
-#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
-    |(rol(block->l[i],8)&0x00FF00FF))
-#else
-#define blk0(i) block->l[i]
-#endif
-#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
-    ^block->l[(i+2)&15]^block->l[i&15],1))
-
-/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
-
-
-/* Hash a single 512-bit block. This is the core of the algorithm. */
-
-void SHA1Transform(unsigned long state[5], unsigned char buffer[64])
+void SHA1Init(SHA1_CTX* context)
 {
-unsigned long a, b, c, d, e;
-typedef union {
-    unsigned char c[64];
-    unsigned long l[16];
-} CHAR64LONG16;
-CHAR64LONG16* block;
-#ifdef SHA1HANDSOFF
-static unsigned char workspace[64];
-    block = (CHAR64LONG16*)workspace;
-    memcpy(block, buffer, 64);
-#else
-    block = (CHAR64LONG16*)buffer;
-#endif
-    /* Copy context->state[] to working vars */
-    a = state[0];
-    b = state[1];
-    c = state[2];
-    d = state[3];
-    e = state[4];
-    /* 4 rounds of 20 operations each. Loop unrolled. */
-    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
-    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
-    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
-    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
-    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
-    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
-    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
-    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
-    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
-    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
-    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
-    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
-    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
-    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
-    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
-    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
-    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
-    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
-    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
-    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
-    /* Add the working vars back into context.state[] */
-    state[0] += a;
-    state[1] += b;
-    state[2] += c;
-    state[3] += d;
-    state[4] += e;
-    /* Wipe variables */
-    a = b = c = d = e = 0;
+  /* SHA1 initialization constants */
+  context->state[0] = 0x67452301L;
+  context->state[1] = 0xEFCDAB89L;
+  context->state[2] = 0x98BADCFEL;
+  context->state[3] = 0x10325476L;
+  context->state[4] = 0xC3D2E1F0L;
+  context->count[0] = context->count[1] = 0;
 }
 
+#define rol(x, nr) (((x) << ((uint32)(nr))) | ((x) >> (32 - (uint32)(nr))))
 
-/* SHA1Init - Initialize new context */
+#define GET_WORD(cp) ((uint32)(uint8)(cp)[0]) << 24    \
+                   | ((uint32)(uint8)(cp)[1] << 16)    \
+                   | ((uint32)(uint8)(cp)[2] << 8)     \
+                   | ((uint32)(uint8)(cp)[3])
 
-void SHA1Init(SHA1_CTX* context)
-{
-    /* SHA1 initialization constants */
-    context->state[0] = 0x67452301;
-    context->state[1] = 0xEFCDAB89;
-    context->state[2] = 0x98BADCFE;
-    context->state[3] = 0x10325476;
-    context->state[4] = 0xC3D2E1F0;
-    context->count[0] = context->count[1] = 0;
-}
+#define blk0(i) (W[i] = GET_WORD(data))
+#define blk1(i) (W[i&15] = rol(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1))
 
+#define f1(x,y,z) (z^(x&(y^z)))
+#define f2(x,y,z) (x^y^z)
+#define f3(x,y,z) ((x&y)|(z&(x|y)))
+#define f4(x,y,z) (x^y^z)
 
-/* Run your data through this. */
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);data+=4;
+#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
 
-void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len)
+void SHA1Transform(uint32 *state, const unsigned char *data)
 {
-unsigned int i, j;
-
-    j = (context->count[0] >> 3) & 63;
-    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
-    context->count[1] += (len >> 29);
-    if ((j + len) > 63) {
-        memcpy(&context->buffer[j], data, (i = 64-j));
-        SHA1Transform(context->state, context->buffer);
-        for ( ; i + 63 < len; i += 64) {
-            SHA1Transform(context->state, &data[i]);
-        }
-        j = 0;
-    }
-    else i = 0;
-    memcpy(&context->buffer[j], &data[i], len - i);
+  uint32 W[16];
+  
+  /* Copy context->state[] to working vars */
+  uint32 a = state[0];
+  uint32 b = state[1];
+  uint32 c = state[2];
+  uint32 d = state[3];
+  uint32 e = state[4];
+  
+  /* 4 rounds of 20 operations each. Loop unrolled. */
+  R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+  R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+  R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+  R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+  
+  /* Add the working vars back into context.state[] */
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+  state[4] += e;
+  
+  /* Wipe variables */
+  a = b = c = d = e = 0;
+  memset(W, 0, sizeof(W));
 }
 
+/* Run your data through this. */
 
-/* Add padding and return the message digest. */
-
-void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+void SHA1Update(SHA1_CTX* context, unsigned char* data, uint32 len)
 {
-unsigned long i, j;
-unsigned char finalcount[8];
+  uint32 i, j;
 
-    for (i = 0; i < 8; i++) {
-        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
-         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
-    }
-    SHA1Update(context, (unsigned char *)"\200", 1);
-    while ((context->count[0] & 504) != 448) {
-        SHA1Update(context, (unsigned char *)"\0", 1);
-    }
-    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
-    for (i = 0; i < 20; i++) {
-        digest[i] = (unsigned char)
-         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
-    }
-    /* Wipe variables */
-    i = j = 0;
-    memset(context->buffer, 0, 64);
-    memset(context->state, 0, 20);
-    memset(context->count, 0, 8);
-    memset(finalcount, 0, 8);
-#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
+  j = (context->count[0] >> 3) & 63;
+  if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+  context->count[1] += (len >> 29);
+  if ((j + len) > 63) {
+    memcpy(&context->buffer[j], data, (i = 64-j));
     SHA1Transform(context->state, context->buffer);
-#endif
+    for ( ; i + 63 < len; i += 64) {
+      SHA1Transform(context->state, &data[i]);
+    }
+    j = 0;
+  }
+  else i = 0;
+  memcpy(&context->buffer[j], &data[i], len - i);
 }
 
+/* Add padding and return the message digest. */
 
-/*************************************************************/
-
-/* Test Code */
-
-#if 0
-
-int main(int argc, char** argv)
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
 {
-int i, j;
-SHA1_CTX context;
-unsigned char digest[20], buffer[16384];
-FILE* file;
-
-    if (argc > 2) {
-        puts("Public domain SHA-1 implementation - by Steve Reid <steve@edmweb.com>");
-        puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
-        exit(0);
-    }
-    if (argc < 2) {
-        file = stdin;
-    }
-    else {
-        if (!(file = fopen(argv[1], "rb"))) {
-            fputs("Unable to open file.", stderr);
-            exit(-1);
-        }
-    } 
-    SHA1Init(&context);
-    while (!feof(file)) {  /* note: what if ferror(file) */
-        i = fread(buffer, 1, 16384, file);
-        SHA1Update(&context, buffer, i);
-    }
-    SHA1Final(digest, &context);
-    fclose(file);
-    for (i = 0; i < 5; i++) {
-        for (j = 0; j < 4; j++) {
-            printf("%02X", digest[i*4+j]);
-        }
-        putchar(' ');
-    }
-    putchar('\n');
-    exit(0);
+  uint32 i, j;
+  unsigned char finalcount[8];
+  
+  for (i = 0; i < 8; i++) {
+    finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] 
+                                    >> ((3 - (i & 3)) * 8)) & 255);
+  }
+  SHA1Update(context, (unsigned char *)"\200", 1);
+  while ((context->count[0] & 504) != 448) {
+    SHA1Update(context, (unsigned char *)"\0", 1);
+  }
+  
+  SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+  for (i = 0; i < 20; i++) {
+    digest[i] = (unsigned char)
+      ((context->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255);
+  }
+  
+  /* Wipe variables */
+  i = j = 0;
+  memset(context->buffer, 0, 64);
+  memset(context->state, 0, 20);
+  memset(context->count, 0, 8);
+  memset(finalcount, 0, 8);
+  SHA1Transform(context->state, context->buffer);
 }
-
-#endif
index 534d300210a913320f905a04ffc7d6830966abfa..ac52d6242b86f753c4c751233a8e62e8d4fcbc18 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef SHA1_H
 #define SHA1_H
 
-#include "sha1_internal.h"
-
 /* 
  * SILC Hash API for SHA1
  */
index 15ae9bd789eb7166879e44617fbcfba416f0e0c0..ae970448abcbcf332d7967c70f1bc9610c89082d 100644 (file)
@@ -20,15 +20,15 @@ A million repetitions of "a"
 
 /* Context declaration */
 typedef struct {
-    unsigned long state[5];
-    unsigned long count[2];
+    uint32 state[5];
+    uint32 count[2];
     unsigned char buffer[64];
 } SHA1_CTX;
 
 /* Function forward declerations */
-void SHA1Transform(unsigned long state[5], unsigned char buffer[64]);
+void SHA1Transform(uint32 *state, const unsigned char *data);
 void SHA1Init(SHA1_CTX* context);
-void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len);
+void SHA1Update(SHA1_CTX* context, unsigned char* data, uint32 len);
 void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
 
 #endif
index c3d2087017f1f9bab79f5141b3db6f8c09b27a6b..3517317ec5c352a99eee7bd41417e1d372d8f84e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:54  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 
 #include "ciphers.h"           /* Includes cipher definitions */
 
-/* List of all ciphers in SILC. You can dynamically add new ciphers
-   into the list. At the initialization of SILC this list is filled with
-   the configured ciphers. */
-struct SilcCipherListStruct {
-  SilcCipherObject *cipher;
-  struct SilcCipherListStruct *next;
-};
-
 /* Dynamically registered list of ciphers. */
-struct SilcCipherListStruct *silc_cipher_list = NULL;
+SilcDList silc_cipher_list = NULL;
 
-/* XXX: add the other good ciphers here as well */
-
-/* Staticly declared list of ciphers. This is used if system doesn't
-   support SIM's. */
-SilcCipherObject silc_cipher_builtin_list[] =
+/* Static list of ciphers for silc_cipher_register_default(). */
+SilcCipherObject silc_default_ciphers[] =
 {
-  { "none", 0, 0, silc_none_set_key, silc_none_set_key_with_string,
-    silc_none_encrypt_cbc, silc_none_decrypt_cbc, 
-    silc_none_context_len },
-  { "twofish", 16, 16, silc_twofish_set_key, silc_twofish_set_key_with_string,
+  { "aes-256-cbc", 16, 256, silc_aes_set_key, 
+    silc_aes_set_key_with_string, silc_aes_encrypt_cbc,
+    silc_aes_decrypt_cbc, silc_aes_context_len },
+  { "aes-192-cbc", 16, 192, silc_aes_set_key, 
+    silc_aes_set_key_with_string, silc_aes_encrypt_cbc,
+    silc_aes_decrypt_cbc, silc_aes_context_len },
+  { "aes-128-cbc", 16, 128, silc_aes_set_key, 
+    silc_aes_set_key_with_string, silc_aes_encrypt_cbc,
+    silc_aes_decrypt_cbc, silc_aes_context_len },
+  { "twofish-256-cbc", 16, 256, silc_twofish_set_key, 
+    silc_twofish_set_key_with_string,
+    silc_twofish_encrypt_cbc, silc_twofish_decrypt_cbc, 
+    silc_twofish_context_len },
+  { "twofish-192-cbc", 16, 192, silc_twofish_set_key, 
+    silc_twofish_set_key_with_string,
+    silc_twofish_encrypt_cbc, silc_twofish_decrypt_cbc, 
+    silc_twofish_context_len },
+  { "twofish-128-cbc", 16, 128, silc_twofish_set_key, 
+    silc_twofish_set_key_with_string,
     silc_twofish_encrypt_cbc, silc_twofish_decrypt_cbc, 
     silc_twofish_context_len },
-  { "rc6", 16, 16, silc_rc6_set_key, silc_rc6_set_key_with_string,
+  { "rc6-256-cbc", 16, 256, silc_rc6_set_key, silc_rc6_set_key_with_string,
     silc_rc6_encrypt_cbc, silc_rc6_decrypt_cbc, 
     silc_rc6_context_len },
-  { "mars", 16, 16, silc_mars_set_key, silc_mars_set_key_with_string,
+  { "rc6-192-cbc", 16, 192, silc_rc6_set_key, silc_rc6_set_key_with_string,
+    silc_rc6_encrypt_cbc, silc_rc6_decrypt_cbc, 
+    silc_rc6_context_len },
+  { "rc6-128-cbc", 16, 128, silc_rc6_set_key, silc_rc6_set_key_with_string,
+    silc_rc6_encrypt_cbc, silc_rc6_decrypt_cbc, 
+    silc_rc6_context_len },
+  { "mars-256-cbc", 16, 256, silc_mars_set_key, silc_mars_set_key_with_string,
+    silc_mars_encrypt_cbc, silc_mars_decrypt_cbc, 
+    silc_mars_context_len },
+  { "mars-192-cbc", 16, 192, silc_mars_set_key, silc_mars_set_key_with_string,
     silc_mars_encrypt_cbc, silc_mars_decrypt_cbc, 
     silc_mars_context_len },
+  { "mars-128-cbc", 16, 128, silc_mars_set_key, silc_mars_set_key_with_string,
+    silc_mars_encrypt_cbc, silc_mars_decrypt_cbc, 
+    silc_mars_context_len },
+  { "cast-256-cbc", 16, 256, silc_cast_set_key, silc_cast_set_key_with_string,
+    silc_cast_encrypt_cbc, silc_cast_decrypt_cbc, 
+    silc_cast_context_len },
+  { "cast-192-cbc", 16, 192, silc_cast_set_key, silc_cast_set_key_with_string,
+    silc_cast_encrypt_cbc, silc_cast_decrypt_cbc, 
+    silc_cast_context_len },
+  { "cast-128-cbc", 16, 128, silc_cast_set_key, silc_cast_set_key_with_string,
+    silc_cast_encrypt_cbc, silc_cast_decrypt_cbc, 
+    silc_cast_context_len },
+  { "none", 0, 0, silc_none_set_key, silc_none_set_key_with_string,
+    silc_none_encrypt_cbc, silc_none_decrypt_cbc, 
+    silc_none_context_len },
 
   { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
 };
@@ -68,168 +89,112 @@ SilcCipherObject silc_cipher_builtin_list[] =
    registered. Therefore, if memory has been allocated for the object sent
    as argument it has to be free'd after this function returns succesfully. */
 
-int silc_cipher_register(SilcCipherObject *cipher)
+bool silc_cipher_register(SilcCipherObject *cipher)
 {
-  struct SilcCipherListStruct *new, *c;
+  SilcCipherObject *new;
 
-  SILC_LOG_DEBUG(("Registering new cipher"));
+  SILC_LOG_DEBUG(("Registering new cipher `%s'", cipher->name));
 
-  new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new cipher list object: %s",
-                   strerror(errno)));
-    return FALSE;
-  }
-
-  new->cipher = silc_calloc(1, sizeof(*new->cipher));
-  if (!new->cipher) {
-    SILC_LOG_ERROR(("Could not allocate new cipher object: %s",
-                   strerror(errno)));
-    return FALSE;
-  }
-
-  /* Set the pointers */
-  new->cipher->name = strdup(cipher->name);
-  new->cipher->block_len = cipher->block_len;
-  new->cipher->key_len = cipher->key_len;
-  new->cipher->set_key = cipher->set_key;
-  new->cipher->set_key_with_string = cipher->set_key_with_string;
-  new->cipher->encrypt = cipher->encrypt;
-  new->cipher->decrypt = cipher->decrypt;
-  new->cipher->context_len = cipher->context_len;
-  new->next = NULL;
-
-  /* Add the new cipher to the list */
-  if (!silc_cipher_list) {
-    silc_cipher_list = new;
-    return TRUE;
-  }
-
-  c = silc_cipher_list;
-  while (c) {
-    if (!c->next) {
-      c->next = new;
-      break;
+  /* Check if exists already */
+  if (silc_cipher_list) {
+    SilcCipherObject *entry;
+    silc_dlist_start(silc_cipher_list);
+    while ((entry = silc_dlist_get(silc_cipher_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, cipher->name))
+       return FALSE;
     }
-    c = c->next;
   }
 
+  new = silc_calloc(1, sizeof(*new));
+  new->name = strdup(cipher->name);
+  new->block_len = cipher->block_len;
+  new->key_len = cipher->key_len;
+  new->set_key = cipher->set_key;
+  new->set_key_with_string = cipher->set_key_with_string;
+  new->encrypt = cipher->encrypt;
+  new->decrypt = cipher->decrypt;
+  new->context_len = cipher->context_len;
+
+  /* Add to list */
+  if (silc_cipher_list == NULL)
+    silc_cipher_list = silc_dlist_init();
+  silc_dlist_add(silc_cipher_list, new);
+
   return TRUE;
 }
 
 /* Unregister a cipher from the SILC. */
 
-int silc_cipher_unregister(SilcCipherObject *cipher)
+bool silc_cipher_unregister(SilcCipherObject *cipher)
 {
-  struct SilcCipherListStruct *c, *tmp;
+  SilcCipherObject *entry;
 
   SILC_LOG_DEBUG(("Unregistering cipher"));
 
-  c = silc_cipher_list;
-  
-  if (cipher == SILC_ALL_CIPHERS) {
-    /* Unregister all ciphers */
-    while (c) {
-      tmp = c->next;
-      silc_free(c->cipher->name);
-      silc_free(c);
-      c = tmp;
-    }
-
-    return TRUE;
-  }
-
-  /* Unregister the cipher */
-  if (c->cipher == cipher) {
-    tmp = c->next;
-    silc_free(c->cipher->name);
-    silc_free(c);
-    silc_cipher_list = tmp;
-    
-    return TRUE;
-  }
+  if (!silc_cipher_list)
+    return FALSE;
 
-  while (c) {
-    if (c->next->cipher == cipher) {
+  silc_dlist_start(silc_cipher_list);
+  while ((entry = silc_dlist_get(silc_cipher_list)) != SILC_LIST_END) {
+    if (cipher == SILC_ALL_CIPHERS || entry == cipher) {
+      silc_dlist_del(silc_cipher_list, entry);
+      silc_free(entry->name);
+      silc_free(entry);
 
-      tmp = c->next->next;
-      silc_free(c->cipher->name);
-      silc_free(c);
-      c->next = tmp;
+      if (silc_dlist_count(silc_cipher_list) == 0) {
+       silc_dlist_uninit(silc_cipher_list);
+       silc_cipher_list = NULL;
+      }
 
       return TRUE;
     }
-
-    c = c->next;
   }
 
   return FALSE;
 }
 
+/* Function that registers all the default ciphers (all builtin ciphers). 
+   The application may use this to register the default ciphers if specific
+   ciphers in any specific order is not wanted. */
+
+bool silc_cipher_register_default(void)
+{
+  int i;
+
+  for (i = 0; silc_default_ciphers[i].name; i++)
+    silc_cipher_register(&(silc_default_ciphers[i]));
+
+  return TRUE;
+}
+
 /* Allocates a new SILC cipher object. Function returns 1 on succes and 0 
    on error. The allocated cipher is returned in new_cipher argument. The
    caller must set the key to the cipher after this function has returned
    by calling the ciphers set_key function. */
 
-int silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher)
+bool silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher)
 {
-  struct SilcCipherListStruct *c;
-  int i;
+  SilcCipherObject *entry;
 
   SILC_LOG_DEBUG(("Allocating new cipher object"));
-
-  /* Allocate the new object */
-  *new_cipher = silc_calloc(1, sizeof(**new_cipher));
-  if (*new_cipher == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new cipher object"));
-    return FALSE;
-  }
   
   if (silc_cipher_list) {
-
-    c = silc_cipher_list;
-    while (c) {
-      if (!strcmp(c->cipher->name, name))
-       break;
-      c = c->next;
+    silc_dlist_start(silc_cipher_list);
+    while ((entry = silc_dlist_get(silc_cipher_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name)) {
+       *new_cipher = silc_calloc(1, sizeof(**new_cipher));
+       (*new_cipher)->cipher = entry; 
+       (*new_cipher)->context = silc_calloc(1, entry->context_len());
+       (*new_cipher)->set_iv = silc_cipher_set_iv;
+       (*new_cipher)->get_iv = silc_cipher_get_iv;
+       (*new_cipher)->get_key_len = silc_cipher_get_key_len;
+       (*new_cipher)->get_block_len = silc_cipher_get_block_len;
+       return TRUE;
+      }
     }
-
-    if (!c)
-      goto check_builtin;
-
-    /* Set the pointers */
-    (*new_cipher)->cipher = c->cipher;
-    (*new_cipher)->context = silc_calloc(1, c->cipher->context_len());
-    (*new_cipher)->set_iv = silc_cipher_set_iv;
-    (*new_cipher)->get_iv = silc_cipher_get_iv;
-    (*new_cipher)->get_key_len = silc_cipher_get_key_len;
-    (*new_cipher)->get_block_len = silc_cipher_get_block_len;
-    
-    return TRUE;
   }
 
- check_builtin:
-
-  for (i = 0; silc_cipher_builtin_list[i].name; i++)
-    if (!strcmp(silc_cipher_builtin_list[i].name, name))
-      break;
-
-  if (silc_cipher_builtin_list[i].name == NULL) {
-    silc_free(*new_cipher);
-    return FALSE;
-  }
-
-  /* Set the pointers */
-  (*new_cipher)->cipher = &silc_cipher_builtin_list[i];
-  (*new_cipher)->context = 
-    silc_calloc(1, (*new_cipher)->cipher->context_len());
-  (*new_cipher)->set_iv = silc_cipher_set_iv;
-  (*new_cipher)->get_iv = silc_cipher_get_iv;
-  (*new_cipher)->get_key_len = silc_cipher_get_key_len;
-  (*new_cipher)->get_block_len = silc_cipher_get_block_len;
-  memset(&(*new_cipher)->iv, 0, sizeof((*new_cipher)->iv));
-
-  return TRUE;
+  return FALSE;
 }
 
 /* Free's the given cipher. */
@@ -244,100 +209,99 @@ void silc_cipher_free(SilcCipher cipher)
 
 /* Returns TRUE if cipher `name' is supported. */
 
-int silc_cipher_is_supported(const unsigned char *name)
+bool silc_cipher_is_supported(const unsigned char *name)
 {
-  struct SilcCipherListStruct *c;
-  int i;
+  SilcCipherObject *entry;
 
   if (silc_cipher_list) {
-    c = silc_cipher_list;
-
-    while (c) {
-      if (!strcmp(c->cipher->name, name))
+    silc_dlist_start(silc_cipher_list);
+    while ((entry = silc_dlist_get(silc_cipher_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name))
        return TRUE;
-      c = c->next;
     }
   }
 
-  for (i = 0; silc_cipher_builtin_list[i].name; i++)
-    if (!strcmp(silc_cipher_builtin_list[i].name, name))
-      return TRUE;
-
   return FALSE;
 }
 
 /* Returns comma separated list of supported ciphers. */
 
-char *silc_cipher_get_supported()
+char *silc_cipher_get_supported(void)
 {
+  SilcCipherObject *entry;
   char *list = NULL;
-  int i, len;
-  struct SilcCipherListStruct *c;
+  int len;
 
   len = 0;
   if (silc_cipher_list) {
-    c = silc_cipher_list;
-
-    while (c) {
-      len += strlen(c->cipher->name);
+    silc_dlist_start(silc_cipher_list);
+    while ((entry = silc_dlist_get(silc_cipher_list)) != SILC_LIST_END) {
+      len += strlen(entry->name);
       list = silc_realloc(list, len + 1);
       
-      memcpy(list + (len - strlen(c->cipher->name)), 
-            c->cipher->name, strlen(c->cipher->name));
+      memcpy(list + (len - strlen(entry->name)), 
+            entry->name, strlen(entry->name));
       memcpy(list + len, ",", 1);
       len++;
-      
-      c = c->next;
     }
+    list[len - 1] = 0;
   }
 
-  for (i = 0; silc_cipher_builtin_list[i].name; i++) {
-    len += strlen(silc_cipher_builtin_list[i].name);
-    list = silc_realloc(list, len + 1);
-    
-    memcpy(list + (len - strlen(silc_cipher_builtin_list[i].name)), 
-          silc_cipher_builtin_list[i].name, 
-          strlen(silc_cipher_builtin_list[i].name));
-    memcpy(list + len, ",", 1);
-    len++;
-  }
+  return list;
+}
 
-  list[len - 1] = 0;
+/* Encrypts */
 
-  return list;
+bool silc_cipher_encrypt(SilcCipher cipher, const unsigned char *src,
+                        unsigned char *dst, uint32 len, 
+                        unsigned char *iv)
+{
+  return cipher->cipher->encrypt(cipher->context, src, dst, len, iv);
+}
+
+/* Decrypts */
+
+bool silc_cipher_decrypt(SilcCipher cipher, const unsigned char *src,
+                        unsigned char *dst, uint32 len, 
+                        unsigned char *iv)
+{
+  return cipher->cipher->decrypt(cipher->context, src, dst, len, iv);
+}
+
+/* Sets the key for the cipher */
+
+bool silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
+                        uint32 keylen)
+{
+  return cipher->cipher->set_key(cipher->context, key, keylen);
 }
 
 /* Sets the IV (initial vector) for the cipher. */
 
-void silc_cipher_set_iv(SilcCipher itself, const unsigned char *iv)
+void silc_cipher_set_iv(SilcCipher cipher, const unsigned char *iv)
 {
-  memset(&itself->iv, 0, sizeof(itself->iv));
-  memcpy(&itself->iv, iv, itself->cipher->block_len);
+  memset(&cipher->iv, 0, sizeof(cipher->iv));
+  memcpy(&cipher->iv, iv, cipher->cipher->block_len);
 }
 
 /* Returns the IV (initial vector) of the cipher. The IV is returned 
    to 'iv' argument. */
 
-void silc_cipher_get_iv(SilcCipher itself, unsigned char *iv)
+void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv)
 {
-  memcpy(iv, &itself->iv, itself->cipher->block_len);
+  memcpy(iv, &cipher->iv, cipher->cipher->block_len);
 }
 
 /* Returns the key length of the cipher. */
-/* XXX */
 
-unsigned int silc_cipher_get_key_len(SilcCipher itself, 
-                                    const unsigned char *name)
+uint32 silc_cipher_get_key_len(SilcCipher cipher)
 {
-
-  return TRUE;
+  return cipher->cipher->key_len;
 }
 
 /* Returns the block size of the cipher. */
-/* XXX */
 
-unsigned int silc_cipher_get_block_len(SilcCipher itself)
+uint32 silc_cipher_get_block_len(SilcCipher cipher)
 {
-
-  return TRUE;
+  return cipher->cipher->block_len;
 }
index 10801492d259752c713382725c8adc6b1319352b..47cfd3e03f12375b3bb379c5845f5465d59258a5 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
        Logical name of the cipher.
 
-   unsigned int block_len
+   uint32 block_len
 
        Block size of the cipher.
 
-   unsigned int key_len
+   uint32 key_len
 
        Length of the key of the cipher (in bits).
 
 */
 typedef struct {
   char *name;
-  unsigned int block_len;
-  unsigned key_len;
-
-  int (*set_key)(void *, const unsigned char *, unsigned int);
-  int (*set_key_with_string)(void *, const unsigned char *, unsigned int);
-  int (*encrypt)(void *, const unsigned char *, unsigned char *,
-                unsigned int, unsigned char *);
-  int (*decrypt)(void *, const unsigned char *, unsigned char *, 
-                unsigned int, unsigned char *);
-  unsigned int (*context_len)();
+  uint32 block_len;
+  uint32 key_len;
+
+  bool (*set_key)(void *, const unsigned char *, uint32);
+  bool (*set_key_with_string)(void *, const unsigned char *, uint32);
+  bool (*encrypt)(void *, const unsigned char *, unsigned char *,
+                 uint32, unsigned char *);
+  bool (*decrypt)(void *, const unsigned char *, unsigned char *, 
+                 uint32, unsigned char *);
+  uint32 (*context_len)();
 } SilcCipherObject;
 
 #define SILC_CIPHER_MAX_IV_SIZE 16
@@ -63,21 +63,22 @@ typedef struct SilcCipherStruct {
   SilcCipherObject *cipher;
   void *context;
   unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
-
   void (*set_iv)(struct SilcCipherStruct *, const unsigned char *);
   void (*get_iv)(struct SilcCipherStruct *, unsigned char *);
-  unsigned int (*get_key_len)(struct SilcCipherStruct *, 
-                             const unsigned char *);
-  unsigned int (*get_block_len)(struct SilcCipherStruct *);
+  uint32 (*get_key_len)(struct SilcCipherStruct *);
+  uint32 (*get_block_len)(struct SilcCipherStruct *);
 } *SilcCipher;
 
-/* List of all registered ciphers. */
-extern struct SilcCipherListStruct *silc_cipher_list;
-
 /* Marks for all ciphers in silc. This can be used in silc_cipher_unregister
    to unregister all ciphers at once. */
 #define SILC_ALL_CIPHERS ((SilcCipherObject *)1)
 
+/* Static list of ciphers for silc_cipher_register_default(). */
+extern DLLAPI SilcCipherObject silc_default_ciphers[];
+
+/* Default cipher in the SILC protocol */
+#define SILC_DEFAULT_CIPHER "aes-256-cbc"
+
 /* Macros */
 
 /* Function names in SILC Crypto modules. The name of the cipher
@@ -93,39 +94,47 @@ extern struct SilcCipherListStruct *silc_cipher_list;
 /* These macros can be used to implement the SILC Crypto API and to avoid
    errors in the API these macros should be used always. */
 #define SILC_CIPHER_API_SET_KEY(cipher)                        \
-int silc_##cipher##_set_key(void *context,             \
-                           const unsigned char *key,   \
-                           unsigned int keylen)
+bool silc_##cipher##_set_key(void *context,            \
+                            const unsigned char *key,  \
+                            uint32 keylen)
 #define SILC_CIPHER_API_SET_KEY_WITH_STRING(cipher)                    \
-int silc_##cipher##_set_key_with_string(void *context,                 \
-                                       const unsigned char *string,    \
-                                       unsigned int stringlen)
+bool silc_##cipher##_set_key_with_string(void *context,                        \
+                                        const unsigned char *string,   \
+                                        uint32 stringlen)
 #define SILC_CIPHER_API_ENCRYPT_CBC(cipher)                    \
-int silc_##cipher##_encrypt_cbc(void *context,                 \
-                               const unsigned char *src,       \
-                               unsigned char *dst,             \
-                               unsigned int len,               \
-                               unsigned char *iv)
+bool silc_##cipher##_encrypt_cbc(void *context,                        \
+                                const unsigned char *src,      \
+                                unsigned char *dst,            \
+                                uint32 len,            \
+                                unsigned char *iv)
 #define SILC_CIPHER_API_DECRYPT_CBC(cipher)                    \
-int silc_##cipher##_decrypt_cbc(void *context,                 \
-                               const unsigned char *src,       \
-                               unsigned char *dst,             \
-                               unsigned int len,               \
-                               unsigned char *iv)
+bool silc_##cipher##_decrypt_cbc(void *context,                        \
+                                const unsigned char *src,      \
+                                unsigned char *dst,            \
+                                uint32 len,            \
+                                unsigned char *iv)
 #define SILC_CIPHER_API_CONTEXT_LEN(cipher)                    \
-unsigned int silc_##cipher##_context_len()
+uint32 silc_##cipher##_context_len()
 
 /* Prototypes */
-int silc_cipher_register(SilcCipherObject *cipher);
-int silc_cipher_unregister(SilcCipherObject *cipher);
-int silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher);
+bool silc_cipher_register(SilcCipherObject *cipher);
+bool silc_cipher_unregister(SilcCipherObject *cipher);
+bool silc_cipher_register_default(void);
+bool silc_cipher_alloc(const unsigned char *name, SilcCipher *new_cipher);
 void silc_cipher_free(SilcCipher cipher);
-int silc_cipher_is_supported(const unsigned char *name);
-char *silc_cipher_get_supported();
-void silc_cipher_set_iv(SilcCipher itself, const unsigned char *iv);
-void silc_cipher_get_iv(SilcCipher itself, unsigned char *iv);
-unsigned int silc_cipher_get_key_len(SilcCipher itself, 
-                                    const unsigned char *name);
-unsigned int silc_cipher_get_block_len(SilcCipher itself);
+bool silc_cipher_is_supported(const unsigned char *name);
+char *silc_cipher_get_supported(void);
+bool silc_cipher_encrypt(SilcCipher cipher, const unsigned char *src,
+                        unsigned char *dst, uint32 len, 
+                        unsigned char *iv);
+bool silc_cipher_decrypt(SilcCipher cipher, const unsigned char *src,
+                        unsigned char *dst, uint32 len, 
+                        unsigned char *iv);
+bool silc_cipher_set_key(SilcCipher cipher, const unsigned char *key,
+                        uint32 keylen);
+void silc_cipher_set_iv(SilcCipher cipher, const unsigned char *iv);
+void silc_cipher_get_iv(SilcCipher cipher, unsigned char *iv);
+uint32 silc_cipher_get_key_len(SilcCipher cipher);
+uint32 silc_cipher_get_block_len(SilcCipher cipher);
 
 #endif
diff --git a/lib/silccrypt/silcdh.h b/lib/silccrypt/silcdh.h
new file mode 100644 (file)
index 0000000..1f90ba3
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+
+  silcdh.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccrypt/SilcDH/silcdh.h
+ *
+ * DESCRIPTION
+ *
+ * PKCS #3 compliant Diffie Hellman key agreement protocol implementation.
+ * This is used as part of SKE (SILC Key Exchange) protocol.
+ *
+ ***/
+
+#ifndef SILCDH_H
+#define SILCDH_H
+
+#include "silcmp.h"
+
+/****s* silccrypt/SilcDH/SilcDH
+ *
+ * NAME
+ * 
+ *    typedef struct SilcDHStruct *SilcDH;
+ *
+ * DESCRIPTION
+ *
+ *    This context is allocated by silc_dh_alloc and is given as argument
+ *    to all silc_dh_* functions.  It is freed by silc_dh_free function.
+ *
+ ***/
+typedef struct SilcDHStruct *SilcDH;
+
+/* XXX Move to source file */
+/* Diffie Hellman context. This includes the DH parameters including the
+   negotiated key material. */
+struct SilcDHStruct {
+  SilcMPInt *g;                     /* Global base (generator) */
+  SilcMPInt *p;                     /* Global prime (modulus, prime) */
+  SilcMPInt *lpf;           /* Largest prime factor (prime) */
+  SilcMPInt *my_x;          /* x, My private value (random) */
+  SilcMPInt *my_y;          /* y, My public value (y = g ^ x mod p) */
+  SilcMPInt *your_y;        /* y', Your public value (y' = g ^ x' mod p) */
+  SilcMPInt *z;                     /* The computed secret key (z = y' ^ x mod p) */
+};
+
+/****f* silccrypt/SilcDH/silc_dh_alloc
+ *
+ * SYNOPSIS
+ *    
+ *    SilcDH silc_dh_alloc(SilcMPInt *g, SilcMPInt *p, SilcMPInt *lpf);
+ * 
+ * DESCRIPTION
+ *
+ *    Allocate SilcDH context. The `g' is the public base generator used
+ *    in the negotiation, the `p' is the public prime used in the
+ *    negotiation and the `lpf' is largest prime factor of p defined
+ *    publicly as well. The `lpf' is optional and if it is not supplied
+ *    then the private values generated satifies 0 < x < p - 1 instead
+ *    of 0 < x < lpf. Returns NULL on error or allocated SilcDH context
+ *    on success. 
+ *
+ ***/
+SilcDH silc_dh_alloc(SilcMPInt *g, SilcMPInt *p, SilcMPInt *lpf);
+
+/****f* silccrypt/SilcDH/silc_dh_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_dh_free(SilcDH dh);
+ *
+ * DESCRIPTION
+ *
+ *    Free the SilcDH context. Frees all the allocated data inside the 
+ *    SilcDH context. 
+ *
+ ***/
+void silc_dh_free(SilcDH dh);
+
+/****f* silccrypt/SilcDH/silc_dh_generate_private
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_dh_generate_private(SilcDH dh, const SilcMPInt **x);
+ *
+ * DESCRIPTION
+ *
+ *    Generates random private value `x' such that 0 < x < lpf at most of
+ *    length of lpf. Returns FALSE if the random number could not be generated.
+ *    Returns the generated value into `x' pointer sent as argument, unless
+ *    the `x' is NULL. The returned `x' must not be freed by the caller. 
+ *
+ ***/
+bool silc_dh_generate_private(SilcDH dh, const SilcMPInt **x);
+
+/****f* silccrypt/SilcDH/silc_dh_compute_public
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_dh_compute_public(SilcDH dh, const SilcMPInt **y);
+ *
+ * DESCRIPTION
+ *
+ *    Computes the public key y = g ^ x mod p, and returns it to the `y'
+ *    pointer sent as argument, unless the `y' is NULL. Returns FALSE if
+ *    the computation could not be performed. The returned `y' must not be
+ *    freed by the caller. 
+ *
+ ***/
+bool silc_dh_compute_public(SilcDH dh, const SilcMPInt **y);
+
+/****f* silccrypt/SilcDH/silc_dh_remote_public
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_dh_compute_public(SilcDH dh, SilcMPInt *y);
+ *
+ * DESCRIPTION
+ *
+ *    Sets the remote end's public value y' into the SilcDH context.
+ *    This must be done before computing the secret key. Returns FALSE 
+ *    on error. 
+ *
+ ***/
+bool silc_dh_set_remote_public(SilcDH dh, SilcMPInt *y);
+
+/****f* silccrypt/SilcDH/silc_dh_compute_key
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_dh_compute_key(SilcDH dh, const SilcMPInt **z);
+ *
+ * DESCRIPTION
+ *
+ *    Computes the secret key z = y' ^ x mod p, and returns the key to the
+ *    `z' pointer sent as argument, unless the `z' is NULL. Returns FALSE if
+ *    the computation could not be performed. The returned `z' must not be
+ *    freed by the caller. 
+ *
+ ***/
+bool silc_dh_compute_key(SilcDH dh, const SilcMPInt **z);
+
+/****f* silccrypt/SilcDH/silc_dh_remote_public
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_dh_compute_key_data(SilcDH dh, unsigned char **z, 
+ *                                  uint32 *z_len);
+ *
+ * DESCRIPTION
+ *
+ *    Same as above but returns the computed secret key as octet binary
+ *    string.  The caller must free the returned binary string.
+ *
+ ***/
+bool silc_dh_compute_key_data(SilcDH dh, unsigned char **z, 
+                             uint32 *z_len);
+
+#endif
index 6b5f4c42842f887f2a6b9cfad0a7d93ef0a45051..d50374c1bfa3910211a678dba9e8ff515913dd11 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 
 #include "md5.h"
 #include "sha1.h"
 
-/* List of all hash functions in SILC. You can dynamically add new hash
-   functions into the list. At the initialization of SILC this list is 
-   filled with the configured hash functions. */
-struct SilcHashListStruct {
-  SilcHashObject *hash;
-  struct SilcHashListStruct *next;
-};
-
 /* List of dynamically registered hash functions. */
-struct SilcHashListStruct *silc_hash_list = NULL;
+SilcDList silc_hash_list = NULL;
 
-/* Statically declared list of hash functions. */
-SilcHashObject silc_hash_builtin_list[] = 
+/* Default hash functions for silc_hash_register_default(). */
+SilcHashObject silc_default_hash[] = 
 {
-  { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
-    silc_md5_transform, silc_md5_context_len },
   { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final,
     silc_sha1_transform, silc_sha1_context_len },
+  { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final,
+    silc_md5_transform, silc_md5_context_len },
 
   { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL }
 };
 
-/* Registers a ned hash function into the SILC. This function is used at
+/* Registers a new hash function into the SILC. This function is used at
    the initialization of the SILC. */
 
-int silc_hash_register(SilcHashObject *hash)
+bool silc_hash_register(SilcHashObject *hash)
 {
-  struct SilcHashListStruct *new, *h;
-
-  SILC_LOG_DEBUG(("Registering new hash function"));
-
-  new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new hash list object"));
-    return FALSE;
-  }
-
-  new->hash = silc_calloc(1, sizeof(*new->hash));
-  if (!new->hash) {
-    SILC_LOG_ERROR(("Could not allocate new hash object"));
-    return FALSE;
-  }
+  SilcHashObject *new;
 
-  /* Set the pointers */
-  new->hash->name = silc_calloc(1, strlen(hash->name));
-  memcpy(new->hash->name, hash->name, strlen(hash->name));
-  new->hash->hash_len = hash->hash_len;
-  new->hash->block_len = hash->block_len;
-  new->hash->init = hash->init;
-  new->hash->update = hash->update;
-  new->hash->final = hash->final;
-  new->hash->context_len = hash->context_len;
-  new->next = NULL;
-
-  /* Add the new hash function to the list */
-  if (!silc_hash_list) {
-    silc_hash_list = new;
-    return TRUE;
-  }
+  SILC_LOG_DEBUG(("Registering new hash function `%s'", hash->name));
 
-  h = silc_hash_list;
-  while (h) {
-    if (!h->next) {
-      h->next = new;
-      break;
+  /* Check for existing */
+  if (silc_hash_list) {
+    SilcHashObject *entry;
+    silc_dlist_start(silc_hash_list);
+    while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, hash->name))
+       return FALSE;
     }
-    h = h->next;
   }
 
+  new = silc_calloc(1, sizeof(*new));
+  new->name = strdup(hash->name);
+  new->hash_len = hash->hash_len;
+  new->block_len = hash->block_len;
+  new->init = hash->init;
+  new->update = hash->update;
+  new->final = hash->final;
+  new->transform = hash->transform;
+  new->context_len = hash->context_len;
+
+  /* Add to list */
+  if (silc_hash_list == NULL)
+    silc_hash_list = silc_dlist_init();
+  silc_dlist_add(silc_hash_list, new);
+
   return TRUE;
 }
 
 /* Unregister a hash function from the SILC. */
 
-int silc_hash_unregister(SilcHashObject *hash)
+bool silc_hash_unregister(SilcHashObject *hash)
 {
-  struct SilcHashListStruct *h, *tmp;
+  SilcHashObject *entry;
 
   SILC_LOG_DEBUG(("Unregistering hash function"));
 
-  h = silc_hash_list;
-
-  /* Unregister all hash functions */
-  if (hash == SILC_ALL_HASH_FUNCTIONS) {
-    /* Unregister all ciphers */
-    while (h) {
-      tmp = h->next;
-      silc_free(h->hash->name);
-      silc_free(h);
-      h = tmp;
-    }
-
-    return TRUE;
-  }
+  if (!silc_hash_list)
+    return FALSE;
 
-  /* Unregister the hash function */
-  if (h->hash == hash) {
-    tmp = h->next;
-    silc_free(h->hash->name);
-    silc_free(h);
-    silc_hash_list = tmp;
+  silc_dlist_start(silc_hash_list);
+  while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
+    if (hash == SILC_ALL_HASH_FUNCTIONS || entry == hash) {
+      silc_dlist_del(silc_hash_list, entry);
 
-    return TRUE;
-  }
+      if (silc_dlist_count(silc_hash_list) == 0) {
+       silc_dlist_uninit(silc_hash_list);
+       silc_hash_list = NULL;
+      }
 
-  while (h) {
-    if (h->next->hash == hash) {
-      tmp = h->next->next;
-      silc_free(h->hash->name);
-      silc_free(h);
-      h->next = tmp;
       return TRUE;
     }
-
-    h = h->next;
   }
 
   return FALSE;
 }
 
+/* Function that registers all the default hash funcs (all builtin ones). 
+   The application may use this to register the default hash funcs if
+   specific hash funcs in any specific order is not wanted. */
+
+bool silc_hash_register_default(void)
+{
+  int i;
+
+  for (i = 0; silc_default_hash[i].name; i++)
+    silc_hash_register(&(silc_default_hash[i]));
+
+  return TRUE;
+}
+
 /* Allocates a new SilcHash object. New object is returned into new_hash
    argument. */
 
-int silc_hash_alloc(const unsigned char *name, SilcHash *new_hash)
+bool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash)
 {
-  struct SilcHashListStruct *h;
-  int i;
+  SilcHashObject *entry;
   
   SILC_LOG_DEBUG(("Allocating new hash object"));
 
-  /* Allocate the new object */
-  *new_hash = silc_calloc(1, sizeof(**new_hash));
-  if (*new_hash == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new hash object"));
-    return FALSE;
-  }
-
   if (silc_hash_list) {
-    h = silc_hash_list;
-    while (h) {
-      if (!strcmp(h->hash->name, name))
-       break;
-      h = h->next;
+    silc_dlist_start(silc_hash_list);
+    while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name)) {
+       *new_hash = silc_calloc(1, sizeof(**new_hash));
+       (*new_hash)->hash = entry;
+       (*new_hash)->context = silc_calloc(1, entry->context_len());
+       (*new_hash)->make_hash = silc_hash_make;
+       return TRUE;
+      }
     }
-
-    if (!h)
-      goto check_builtin;
-
-    /* Set the pointers */
-    (*new_hash)->hash = h->hash;
-    (*new_hash)->context = silc_calloc(1, h->hash->context_len());
-    (*new_hash)->make_hash = silc_hash_make;
-
-    return TRUE;
   }
 
- check_builtin:
-  for (i = 0; silc_hash_builtin_list[i].name; i++)
-    if (!strcmp(silc_hash_builtin_list[i].name, name))
-      break;
-
-  if (silc_hash_builtin_list[i].name == NULL) {
-    silc_free(*new_hash);
-    return FALSE;
-  }
-  
-  /* Set the pointers */
-  (*new_hash)->hash = &silc_hash_builtin_list[i];
-  (*new_hash)->context = silc_calloc(1, (*new_hash)->hash->context_len());
-  (*new_hash)->make_hash = silc_hash_make;
-  
-  return TRUE;
+  return FALSE;
 }
 
 /* Free's the SilcHash object */
@@ -215,77 +152,156 @@ void silc_hash_free(SilcHash hash)
   }
 }
 
+/* Returns the length of the hash digest. */
+
+uint32 silc_hash_len(SilcHash hash)
+{
+  return hash->hash->hash_len;
+}
+
 /* Returns TRUE if hash algorithm `name' is supported. */
 
-int silc_hash_is_supported(const unsigned char *name)
+bool silc_hash_is_supported(const unsigned char *name)
 {
-  struct SilcHashListStruct *h;
-  int i;
-  
-  if (silc_hash_list) {
-    h = silc_hash_list;
+  SilcHashObject *entry;
 
-    while (h) {
-      if (!strcmp(h->hash->name, name))
+  if (silc_hash_list) {
+    silc_dlist_start(silc_hash_list);
+    while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name))
        return TRUE;
-      h = h->next;
     }
   }
 
-  for (i = 0; silc_hash_builtin_list[i].name; i++)
-    if (!strcmp(silc_hash_builtin_list[i].name, name))
-      return TRUE;
-
   return FALSE;
 }
 
 /* Returns comma separated list of supported hash functions. */
 
-char *silc_hash_get_supported()
+char *silc_hash_get_supported(void)
 {
+  SilcHashObject *entry;
   char *list = NULL;
-  int i, len;
-  struct SilcHashListStruct *h;
+  int len;
 
   len = 0;
   if (silc_hash_list) {
-    h = silc_hash_list;
-
-    while (h) {
-      len += strlen(h->hash->name);
+    silc_dlist_start(silc_hash_list);
+    while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) {
+      len += strlen(entry->name);
       list = silc_realloc(list, len + 1);
       
-      memcpy(list + (len - strlen(h->hash->name)), 
-            h->hash->name, strlen(h->hash->name));
+      memcpy(list + (len - strlen(entry->name)), 
+            entry->name, strlen(entry->name));
       memcpy(list + len, ",", 1);
       len++;
-      
-      h = h->next;
     }
+    list[len - 1] = 0;
   }
 
-  for (i = 0; silc_hash_builtin_list[i].name; i++) {
-    len += strlen(silc_hash_builtin_list[i].name);
-    list = silc_realloc(list, len + 1);
-    
-    memcpy(list + (len - strlen(silc_hash_builtin_list[i].name)), 
-          silc_hash_builtin_list[i].name, 
-          strlen(silc_hash_builtin_list[i].name));
-    memcpy(list + len, ",", 1);
-    len++;
-  }
-
-  list[len - 1] = 0;
-
   return list;
 }
 
 /* Creates the hash value and returns it to the return_hash argument. */
 
 void silc_hash_make(SilcHash hash, const unsigned char *data, 
-                   unsigned int len, unsigned char *return_hash)
+                   uint32 len, unsigned char *return_hash)
 {
   hash->hash->init(hash->context);
   hash->hash->update(hash->context, (unsigned char *)data, len);
   hash->hash->final(hash->context, return_hash);
 }
+
+/* Creates fingerprint of the data. If `hash' is NULL SHA1 is used as
+   default hash function. The returned fingerprint must be free's by the
+   caller. */
+
+char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
+                           uint32 data_len)
+{
+  SilcHash new_hash = NULL;
+  unsigned char h[32];
+  char *ret;
+
+  if (!hash) {
+    silc_hash_alloc("sha1", &new_hash);
+    hash = new_hash;
+  }
+
+  silc_hash_make(hash, data, data_len, h);
+  ret = silc_fingerprint(h, hash->hash->hash_len);
+
+  if (new_hash != NULL)
+    silc_hash_free(new_hash);
+  return ret;
+}
+
+static const char vo[]= "aeiouy";
+static const char co[]= "bcdfghklmnprstvzx";
+
+/* Creates a babbleprint (Bubble Babble Encoding, developed by Antti
+   Huima (draft-huima-babble-01.txt)), by first computing real fingerprint
+   using `hash' or if NULL, then using SHA1, and then encoding the
+   fingerprint to the babbleprint. */
+
+char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
+                           uint32 data_len)
+{
+  SilcHash new_hash = NULL;
+  char *babbleprint;
+  unsigned char hval[32];
+  unsigned int a, b, c, d, e, check;
+  int i, k, out_len;
+
+  if (!hash) {
+    silc_hash_alloc("sha1", &new_hash);
+    hash = new_hash;
+  }
+
+  /* Take fingerprint */
+  silc_hash_make(hash, data, data_len, hval);
+
+  /* Encode babbleprint */
+  out_len = (((hash->hash->hash_len + 1) / 2) + 1) * 6;
+  babbleprint = silc_calloc(out_len, sizeof(*babbleprint));
+  babbleprint[0] = co[16];
+
+  check = 1;
+  for (i = 0, k = 1; i < hash->hash->hash_len - 1; i += 2, k += 6) { 
+    a = (((hval[i] >> 6) & 3) + check) % 6;
+    b = (hval[i] >> 2) & 15;
+    c = ((hval[i] & 3) + (check / 6)) % 6;
+    d = (hval[i + 1] >> 4) & 15;
+    e = hval[i + 1] & 15;
+    
+    check = ((check * 5) + (hval[i] * 7) + hval[i + 1]) % 36;
+    
+    babbleprint[k + 0] = vo[a];
+    babbleprint[k + 1] = co[b];
+    babbleprint[k + 2] = vo[c];
+    babbleprint[k + 3] = co[d];
+    babbleprint[k + 4] = '-';
+    babbleprint[k + 5] = co[e];
+  }
+
+  if ((hash->hash->hash_len % 2) != 0) {
+    a = (((hval[i] >> 6) & 3) + check) % 6;
+    b = (hval[i] >> 2) & 15;
+    c = ((hval[i] & 3) + (check / 6)) % 6;
+    babbleprint[k + 0] = vo[a];
+    babbleprint[k + 1] = co[b];
+    babbleprint[k + 2] = vo[c];
+  } else { 
+    a = check % 6;
+    b = 16;
+    c = check / 6;
+    babbleprint[k + 0] = vo[a];
+    babbleprint[k + 1] = co[b];
+    babbleprint[k + 2] = vo[c];
+  }
+  babbleprint[k + 3] = co[16];
+
+  if (new_hash != NULL)
+    silc_hash_free(new_hash);
+  return babbleprint;
+}
index 98f85ef47d0da1846c6e61f2131b8886858e2aad..26e1c0de589e9fdfd4a9808963498f0fdb39b220 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 /* The default Silc hash object to represent any hash function in SILC. */
 typedef struct {
   char *name;
-  unsigned int hash_len;
-  unsigned int block_len;
+  uint32 hash_len;
+  uint32 block_len;
 
   void (*init)(void *);
-  void (*update)(void *, unsigned char *, unsigned int);
+  void (*update)(void *, unsigned char *, uint32);
   void (*final)(void *, unsigned char *);
-  void (*transform)(unsigned long *, unsigned char *);
-  unsigned int (*context_len)();
+  void (*transform)(uint32 *, unsigned char *);
+  uint32 (*context_len)();
 } SilcHashObject;
 
 /* The main SILC hash structure. Use SilcHash instead of SilcHashStruct.
@@ -41,15 +41,19 @@ typedef struct SilcHashStruct {
   void *context;
 
   void (*make_hash)(struct SilcHashStruct *, const unsigned char *, 
-                   unsigned int, unsigned char *);
+                   uint32, unsigned char *);
 } *SilcHash;
 
-extern struct SilcHashListStruct *silc_hash_list;
-
 /* Marks for all hash functions. This can be used in silc_hash_unregister
    to unregister all hash function at once. */
 #define SILC_ALL_HASH_FUNCTIONS ((SilcHashObject *)1)
 
+/* Default hash functions for silc_hash_register_default(). */
+extern DLLAPI SilcHashObject silc_default_hash[];
+
+/* Default HASH function in the SILC protocol */
+#define SILC_DEFAULT_HASH "sha1"
+
 /* Macros */
 
 /* Following macros are used to implement the SILC Hash API. These
@@ -70,23 +74,29 @@ extern struct SilcHashListStruct *silc_hash_list;
 void silc_##hash##_init(void *context)
 #define SILC_HASH_API_UPDATE(hash)                             \
 void silc_##hash##_update(void *context, unsigned char *data,  \
-                                       unsigned int len)
+                                       uint32 len)
 #define SILC_HASH_API_FINAL(hash)                              \
 void silc_##hash##_final(void *context, unsigned char *digest)
 #define SILC_HASH_API_TRANSFORM(hash)                                  \
-void silc_##hash##_transform(unsigned long *state,                     \
+void silc_##hash##_transform(uint32 *state,                    \
                                          unsigned char *buffer)
 #define SILC_HASH_API_CONTEXT_LEN(hash)                \
-unsigned int silc_##hash##_context_len()
+uint32 silc_##hash##_context_len()
 
 /* Prototypes */
-int silc_hash_register(SilcHashObject *hash);
-int silc_hash_unregister(SilcHashObject *hash);
-int silc_hash_alloc(const unsigned char *name, SilcHash *new_hash);
+bool silc_hash_register(SilcHashObject *hash);
+bool silc_hash_unregister(SilcHashObject *hash);
+bool silc_hash_register_default(void);
+bool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash);
 void silc_hash_free(SilcHash hash);
-int silc_hash_is_supported(const unsigned char *name);
-char *silc_hash_get_supported();
+uint32 silc_hash_len(SilcHash hash);
+bool silc_hash_is_supported(const unsigned char *name);
+char *silc_hash_get_supported(void);
 void silc_hash_make(SilcHash hash, const unsigned char *data,
-                   unsigned int len, unsigned char *return_hash);
+                   uint32 len, unsigned char *return_hash);
+char *silc_hash_fingerprint(SilcHash hash, const unsigned char *data,
+                           uint32 data_len);
+char *silc_hash_babbleprint(SilcHash hash, const unsigned char *data,
+                           uint32 data_len);
 
 #endif
index 5be46cf01f100a66f7784f60a398037e6e4b9788..1a00970e7f720f11dc8b386ef2f4353b09b209d0 100644 (file)
 /*
 
-  silchmac.c
+  silchmac.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1999 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 
-/* Allocates a new SilcHmac object. First argument is the hash function
-   object to tell the hmac which hash function should be used when creating
-   HMAC's. The new SilcHmac object is returned to new_hmac argument. */
+/* HMAC context */
+struct SilcHmacStruct {
+  SilcHmacObject *hmac;
+  SilcHash hash;
+  bool allocated_hash;         /* TRUE if the hash was allocated */
+
+  unsigned char *key;
+  uint32 key_len;
+
+  unsigned char inner_pad[64];
+  unsigned char outer_pad[64];
+  void *hash_context;
+};
+
+/* List of dynamically registered HMACs. */
+SilcDList silc_hmac_list = NULL;
+
+/* Default hmacs for silc_hmac_register_default(). */
+SilcHmacObject silc_default_hmacs[] =
+{
+  { "hmac-sha1-96", 12 },
+  { "hmac-md5-96", 12 },
+  { "hmac-sha1", 20 },
+  { "hmac-md5", 16 },
+
+  { NULL, 0 }
+};
+
+static void silc_hmac_init_internal(SilcHmac hmac, unsigned char *key,
+                                   uint32 key_len)
+{
+  SilcHash hash = hmac->hash;
+  unsigned char hvalue[20];
+  int i;
+
+  memset(hmac->inner_pad, 0, sizeof(hmac->inner_pad));
+  memset(hmac->outer_pad, 0, sizeof(hmac->outer_pad));
+
+  /* If the key length is more than block size of the hash function, the
+     key is hashed. */
+  if (key_len > hash->hash->block_len) {
+    silc_hash_make(hash, key, key_len, hvalue);
+    key = hvalue;
+    key_len = hash->hash->hash_len;
+  }
+
+  /* Copy the key into the pads */
+  memcpy(hmac->inner_pad, key, key_len);
+  memcpy(hmac->outer_pad, key, key_len);
+
+  /* XOR the key with pads */
+  for (i = 0; i < hash->hash->block_len; i++) {
+    hmac->inner_pad[i] ^= 0x36;
+    hmac->outer_pad[i] ^= 0x5c;
+  }
+}
+
+/* Registers a new HMAC into the SILC. This function is used at the
+   initialization of the SILC. */
+
+bool silc_hmac_register(SilcHmacObject *hmac)
+{
+  SilcHmacObject *new;
+
+  SILC_LOG_DEBUG(("Registering new HMAC `%s'", hmac->name));
+
+  /* Check for existing */
+  if (silc_hmac_list) {
+    SilcHmacObject *entry;
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, hmac->name))
+       return FALSE;
+    }
+  }
+
+  new = silc_calloc(1, sizeof(*new));
+  new->name = strdup(hmac->name);
+  new->len = hmac->len;
+
+  /* Add to list */
+  if (silc_hmac_list == NULL)
+    silc_hmac_list = silc_dlist_init();
+  silc_dlist_add(silc_hmac_list, new);
+
+  return TRUE;
+}
+
+/* Unregister a HMAC from the SILC. */
+
+bool silc_hmac_unregister(SilcHmacObject *hmac)
+{
+  SilcHmacObject *entry;
+
+  SILC_LOG_DEBUG(("Unregistering HMAC"));
+
+  if (!silc_hmac_list)
+    return FALSE;
 
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac)
+  silc_dlist_start(silc_hmac_list);
+  while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+    if (hmac == SILC_ALL_HMACS || entry == hmac) {
+      silc_dlist_del(silc_hmac_list, entry);
+
+      if (silc_dlist_count(silc_hmac_list) == 0) {
+       silc_dlist_uninit(silc_hmac_list);
+       silc_hmac_list = NULL;
+      }
+
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/* Function that registers all the default hmacs (all builtin ones). 
+   The application may use this to register the default hmacs if
+   specific hmacs in any specific order is not wanted. */
+
+bool silc_hmac_register_default(void)
 {
-  SILC_LOG_DEBUG(("Allocating new hmac object"));
+  int i;
+
+  for (i = 0; silc_default_hmacs[i].name; i++)
+    silc_hmac_register(&(silc_default_hmacs[i]));
 
+  return TRUE;
+}
+
+/* Allocates a new SilcHmac object of name of `name'.  The `hash' may
+   be provided as argument.  If provided it is used as the hash function
+   of the HMAC.  If it is NULL then the hash function is allocated and
+   the name of the hash algorithm is derived from the `name'. */
+
+bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac)
+{
+  SilcHmacObject *entry;
+
+  SILC_LOG_DEBUG(("Allocating new HMAC"));
+
+  /* Allocate the new object */
   *new_hmac = silc_calloc(1, sizeof(**new_hmac));
-  if (*new_hmac == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new hmac object"));
-    return 0;
+
+  if (!hash) {
+    char *tmp = strdup(name), *hname;
+
+    hname = tmp;
+    if (strchr(hname, '-'))
+      hname = strchr(hname, '-') + 1;
+    if (strchr(hname, '-'))
+      *strchr(hname, '-') = '\0';
+
+    if (!silc_hash_alloc(hname, &hash)) {
+      silc_free(tmp);
+      silc_free(*new_hmac);
+      *new_hmac = NULL;
+      return FALSE;
+    }
+
+    (*new_hmac)->allocated_hash = TRUE;
+    silc_free(tmp);
   }
 
   (*new_hmac)->hash = hash;
-  (*new_hmac)->set_key = silc_hmac_set_key;
-  (*new_hmac)->make_hmac = silc_hmac_make;
-  (*new_hmac)->make_hmac_with_key = silc_hmac_make_with_key;
-  (*new_hmac)->make_hmac_truncated = silc_hmac_make_truncated;
 
-  return 1;
+  if (silc_hmac_list) {
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name)) {
+       (*new_hmac)->hmac = entry; 
+       return TRUE;
+      }
+    }
+  }
+
+  silc_free(*new_hmac);
+  *new_hmac = NULL;
+  return FALSE;
 }
 
 /* Free's the SilcHmac object. */
 
 void silc_hmac_free(SilcHmac hmac)
 {
-  if (hmac)
+  if (hmac) {
+    if (hmac->allocated_hash)
+      silc_hash_free(hmac->hash);
+
+    if (hmac->key) {
+      memset(hmac->key, 0, hmac->key_len);
+      silc_free(hmac->key);
+    }
+
+    silc_free(hmac->hash_context);
     silc_free(hmac);
+  }
 }
 
-/* Creates the HMAC. The created keyed hash value is returned to 
-   return_hash argument. */
+/* Returns the length of the MAC that the HMAC will produce. */
 
-void silc_hmac_make_internal(SilcHmac hmac, unsigned char *data,
-                            unsigned int data_len, unsigned char *key,
-                            unsigned int key_len, unsigned char *return_hash)
+uint32 silc_hmac_len(SilcHmac hmac)
 {
-  SilcHash hash = hmac->hash;
-  unsigned char inner_pad[hash->hash->block_len + 1];
-  unsigned char outer_pad[hash->hash->block_len + 1];
-  unsigned char hvalue[hash->hash->hash_len];
-  void *hash_context;
-  int i;
+  return hmac->hmac->len;
+}
 
-  SILC_LOG_DEBUG(("Making HMAC for message"));
+/* Get hash context */
 
-  hash_context = silc_calloc(1, hash->hash->context_len());
+SilcHash silc_hmac_get_hash(SilcHmac hmac)
+{
+  return hmac->hash;
+}
 
-  memset(inner_pad, 0, sizeof(inner_pad));
-  memset(outer_pad, 0, sizeof(outer_pad));
+/* Return name of hmac */
 
-  /* If the key length is more than block size of the hash function, the
-     key is hashed. */
-  if (key_len > hash->hash->block_len) {
-    hash->make_hash(hash, key, key_len, hvalue);
-    key = hvalue;
-    key_len = hash->hash->hash_len;
+const char *silc_hmac_get_name(SilcHmac hmac)
+{
+  return hmac->hmac->name;
+}
+
+/* Returns TRUE if HMAC `name' is supported. */
+
+bool silc_hmac_is_supported(const char *name)
+{
+  SilcHmacObject *entry;
+
+  if (!name)
+    return FALSE;
+  
+  if (silc_hmac_list) {
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name))
+       return TRUE;
+    }
   }
 
-  /* Copy the key into the pads */
-  memcpy(inner_pad, key, key_len);
-  memcpy(outer_pad, key, key_len);
+  return FALSE;
+}
 
-  /* XOR the key with pads */
-  for (i = 0; i < hash->hash->block_len; i++) {
-    inner_pad[i] ^= 0x36;
-    outer_pad[i] ^= 0x5c;
+/* Returns comma separated list of supported HMACs. */
+
+char *silc_hmac_get_supported()
+{
+  SilcHmacObject *entry;
+  char *list = NULL;
+  int len;
+
+  len = 0;
+  if (silc_hmac_list) {
+    silc_dlist_start(silc_hmac_list);
+    while ((entry = silc_dlist_get(silc_hmac_list)) != SILC_LIST_END) {
+      len += strlen(entry->name);
+      list = silc_realloc(list, len + 1);
+      
+      memcpy(list + (len - strlen(entry->name)), 
+            entry->name, strlen(entry->name));
+      memcpy(list + len, ",", 1);
+      len++;
+    }
+    list[len - 1] = 0;
   }
 
-  /* Do the HMAC transform (too bad I can't do make_hash directly, sigh) */
-  hash->hash->init(hash_context);
-  hash->hash->update(hash_context, inner_pad, hash->hash->block_len);
-  hash->hash->update(hash_context, data, data_len);
-  hash->hash->final(hash_context, return_hash);
-  hash->hash->init(hash_context);
-  hash->hash->update(hash_context, outer_pad, hash->hash->block_len);
-  hash->hash->update(hash_context, return_hash, hash->hash->hash_len);
-  hash->hash->final(hash_context, return_hash);
+  return list;
+}
+
+/* Sets the HMAC key used in the HMAC creation */
+
+void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
+                      uint32 key_len)
+{
+  if (hmac->key) {
+    memset(hmac->key, 0, hmac->key_len);
+    silc_free(hmac->key);
+  }
+  hmac->key = silc_calloc(key_len, sizeof(unsigned char));
+  hmac->key_len = key_len;
+  memcpy(hmac->key, key, key_len);
 }
 
 /* Create the HMAC. This is thee make_hmac function pointer.  This
    uses the internal key set with silc_hmac_set_key. */
 
 void silc_hmac_make(SilcHmac hmac, unsigned char *data,
-                   unsigned int data_len, unsigned char *return_hash)
+                   uint32 data_len, unsigned char *return_hash,
+                   uint32 *return_len)
 {
-  silc_hmac_make_internal(hmac, data, data_len, hmac->key, 
-                         hmac->key_len, return_hash);
+  SILC_LOG_DEBUG(("Making HMAC for message"));
+
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, return_hash, return_len);
+}
+
+/* Creates HMAC just as above except that this doesn't use the internal
+   key. The key is sent as argument to the function. */
+
+void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+                            uint32 data_len, 
+                            unsigned char *key, uint32 key_len,
+                            unsigned char *return_hash,
+                            uint32 *return_len)
+{
+  SILC_LOG_DEBUG(("Making HMAC for message"));
+
+  silc_hmac_init_with_key(hmac, key, key_len);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, return_hash, return_len);
 }
 
 /* Creates the HMAC just as above except that the hash value is truncated
@@ -125,34 +336,71 @@ void silc_hmac_make(SilcHmac hmac, unsigned char *data,
    routine allows these dangerous truncations. */
 
 void silc_hmac_make_truncated(SilcHmac hmac, unsigned char *data,
-                             unsigned int data_len,
-                             unsigned int truncated_len,
+                             uint32 data_len,
+                             uint32 truncated_len,
                              unsigned char *return_hash)
 {
-  unsigned char hvalue[hmac->hash->hash->hash_len];
+  unsigned char hvalue[20];
 
-  silc_hmac_make_internal(hmac, data, data_len, 
-                         hmac->key, hmac->key_len, hvalue);
+  SILC_LOG_DEBUG(("Making HMAC for message"));
+
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, return_hash, NULL);
   memcpy(return_hash, hvalue, truncated_len);
   memset(hvalue, 0, sizeof(hvalue));
 }
 
-/* Creates HMAC just as above except that this doesn't use the internal
-   key. The key is sent as argument to the function. */
+/* Init HMAC for silc_hmac_update and silc_hmac_final. */
 
-void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
-                            unsigned int data_len, 
-                            unsigned char *key, unsigned int key_len,
-                            unsigned char *return_hash)
+void silc_hmac_init(SilcHmac hmac)
 {
-  silc_hmac_make_internal(hmac, data, data_len, key, key_len, return_hash);
+  silc_hmac_init_with_key(hmac, hmac->key, hmac->key_len);
 }
 
-/* Sets the HMAC key used in the HMAC creation */
+/* Same as above but with specific key */
 
-void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
-                      unsigned int key_len)
+void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
+                            uint32 key_len)
 {
-  hmac->key = silc_calloc(key_len, sizeof(unsigned char));
-  memcpy(hmac->key, key, key_len);
+  SilcHash hash = hmac->hash;
+
+  silc_hmac_init_internal(hmac, hmac->key, hmac->key_len);
+
+  if (!hmac->hash_context)
+    hmac->hash_context = silc_calloc(1, hash->hash->context_len());
+
+  hash->hash->init(hmac->hash_context);
+  hash->hash->update(hmac->hash_context, hmac->inner_pad, 
+                    hash->hash->block_len);
+}
+
+/* Add data to be used in the MAC computation. */
+
+void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
+                     uint32 data_len)
+{
+  SilcHash hash = hmac->hash;
+  hash->hash->update(hmac->hash_context, (unsigned char *)data, data_len);
+}
+
+/* Compute the final MAC. */
+
+void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
+                    uint32 *return_len)
+{
+  SilcHash hash = hmac->hash;
+  unsigned char mac[20];
+
+  hash->hash->final(hmac->hash_context, mac);
+  hash->hash->init(hmac->hash_context);
+  hash->hash->update(hmac->hash_context, hmac->outer_pad, 
+                    hash->hash->block_len);
+  hash->hash->update(hmac->hash_context, mac, hash->hash->hash_len);
+  hash->hash->final(hmac->hash_context, mac);
+  memcpy(return_hash, mac, hmac->hmac->len);
+  memset(mac, 0, sizeof(mac));
+
+  if (return_len)
+    *return_len = hmac->hmac->len;
 }
index d69822d06a2091fd7b919f0f5f145e8274122d35..888014884657b0b9bfe230090dde927e4bf73ea2 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silchmac.h
+  silchmac.h 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1999 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #ifndef SILCHMAC_H
 #define SILCHMAC_H
 
-/* 
-   SILC HMAC object. 
-   
-   This is the HMAC object to create keyed hash values for message
-   authentication. These routines uses already implemented hash functions.
-   HMAC's can be created using any hash function implemented in SILC. These
-   routines were created according to RFC2104. Following short description 
-   of the fields:
+/****h* silccrypt/SilcHMACAPI
+ *
+ * DESCRIPTION
+ *
+ *    This is the interface for HMAC, or the keyed hash values, that are
+ *    used for packet and message authentication.  These routines uses
+ *    already implemented hash functions from the SilcHashAPI. These 
+ *    routines were created according to RFC 2104.
+ *
+ ***/
 
-   SilcHash hash
+/****s* silccrypt/SilcHMACAPI/SilcHmac
+ *
+ * NAME
+ * 
+ *    typedef struct SilcHmacStruct *SilcHmac;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual HMAC context and is allocated
+ *    by silc_hmac_alloc and given as argument usually to all
+ *    silc_hmac_* functions.  It is freed by the silc_hmac_free
+ *    function.
+ *
+ ***/
+typedef struct SilcHmacStruct *SilcHmac;
 
-       The hash object to tell what hash function to use with this HMAC.
+/****s* silccrypt/SilcHMACAPI/SilcHmacObject
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcHmacObject;
+ *
+ * DESCRIPTION
+ *
+ *    This structure represents one HMAC.  The HMAC's name and the
+ *    MAC length is defined in the structure.  This structure is
+ *    then given as argument to the silc_hmac_register.  That function
+ *    is used to register all HMACs into SILC.  They can be then
+ *    allocated by the name found in this structure by calling the
+ *    silc_hmac_alloc.
+ *
+ ***/
+typedef struct {
+  char *name;
+  uint32 len;
+} SilcHmacObject;
 
-   unsigned char *key
-   unsigned int len
+/* Marks for all hmacs. This can be used in silc_hmac_unregister
+   to unregister all hmacs at once. */
+#define SILC_ALL_HMACS ((SilcHmacObject *)1)
 
-       The key and its length used to make the HMAC. This is set
-       with silc_hmac_set_key function.
+/* Default hmacs for silc_hmac_register_default(). */
+extern DLLAPI SilcHmacObject silc_default_hmacs[];
 
-   void (*set_key)(SilcHmac, const unsigned char *, unsigned int)
+/* Default HMAC in the SILC protocol */
+#define SILC_DEFAULT_HMAC "hmac-sha1-96"
 
-       Function used to set the key for the HMAC. Second argument is
-       the key to be set and last argument is the length of the key.
+/* Prototypes */
 
-   void (*make_hmac)(SilcHmac, unsigned char *, unsigned int,
-                     unsigned char *)
+/****f* silccrypt/SilcHMACAPI/silc_hmac_register
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hmac_register(SilcHmacObject *hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Registers a new HMAC into the SILC. This function is used at the
+ *    initialization of the SILC.  All registered HMACs should be
+ *    unregistered with silc_hmac_unregister.  The `hmac' includes the
+ *    name of the HMAC and the length of the MAC.  Usually this
+ *    function is not called directly.  Instead, application can call
+ *    the silc_hmac_register_default to register all default HMACs
+ *    that are builtin the sources.  Returns FALSE on error.
+ *
+ ***/
+bool silc_hmac_register(SilcHmacObject *hmac);
 
-       Function what is used to create HMAC's. User can also use directly
-       silc_hmac_make fuction. Although, one needs to allocate a SilcHmac
-       object before doing it, naturally. This uses the key set with
-       silc_hmac_set_key function.
+/****f* silccrypt/SilcHMACAPI/silc_hmac_unregister
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hmac_unregister(SilcHmacObject *hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Unregister a HMAC from SILC by the HMAC structure `hmac'.  This
+ *    should be called for all registered HMAC's.  Returns FALSE on
+ *    error.
+ *
+ ***/
+bool silc_hmac_unregister(SilcHmacObject *hmac);
 
-   void (*make_hmac_with_key)(SilcHmac, unsigned char *, unsigned int,
-                              unsigned char *, unsigned int, unsigned char *)
+/****f* silccrypt/SilcHMACAPI/silc_hmac_register_default
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hmac_register_default(void);
+ *
+ * DESCRIPTION
+ *
+ *    Registers all default HMACs into the SILC.  These are the HMACs
+ *    that are builtin in the sources.  See the list of default HMACs
+ *    in the silchmac.c source file.  The application may use this
+ *    to register default HMACs if specific HMAC in any specific order
+ *    is not wanted (application's configuration usually may decide
+ *    the order of the registration, in which case this should not be
+ *    used).
+ *
+ ***/
+bool silc_hmac_register_default(void);
 
-       Same function as above except that the key used in the HMAC
-       creation is sent as argument. The key set with silc_hmac_set_key
-       is ignored in this case.
+/****f* silccrypt/SilcHMACAPI/silc_hmac_alloc
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates a new SilcHmac object of name of `name'.  The `hash' may
+ *    be provided as argument.  If provided it is used as the hash function
+ *    of the HMAC.  If it is NULL then the hash function is allocated and
+ *    the name of the hash algorithm is derived from the `name'.  Returns
+ *    FALSE if such HMAC does not exist.
+ *
+ ***/
+bool silc_hmac_alloc(char *name, SilcHash hash, SilcHmac *new_hmac);
 
-   void (*make_hmac_truncated)(SilcHmac, unsigned char *, unsigned int,
-                              unsigned int, unsigned char *)
+/****f* silccrypt/SilcHMACAPI/silc_hmac_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_free(SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the allocated HMAC context.  The key that may have been set
+ *    with the silc_hmac_set_key is also destroyed.
+ *
+ ***/
+void silc_hmac_free(SilcHmac hmac);
 
-       Same function as above except that the output hash value is truncated
-       to the length sent as argument (second last argument). This makes
-       variable truncations possible, however, one should not truncate
-       hash values to less than half of the length of the hash value.
+/****f* silccrypt/SilcHMACAPI/silc_hmac_is_supported
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hmac_is_supported(const char *name);
+ *
+ * DESCRIPTION
+ *
+ *    Returns TRUE if the HMAC indicated by the `name' exists.
+ *
+ ***/
+bool silc_hmac_is_supported(const char *name);
 
-*/
-typedef struct SilcHmacStruct *SilcHmac;
+/****f* silccrypt/SilcHMACAPI/silc_hmac_get_supported
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_hmac_get_supported(void);
+ *
+ * DESCRIPTION
+ *
+ *    Returns comma (`,') separated list of registered HMACs.  This is
+ *    used for example when sending supported HMAC list during the SILC
+ *    Key Exchange protocol (SKE).  The caller must free the returned
+ *    pointer.
+ *
+ ***/
+char *silc_hmac_get_supported(void);
 
-struct SilcHmacStruct {
-  SilcHash hash;
-  unsigned char *key;
-  unsigned int key_len;
 void (*set_key)(SilcHmac, const unsigned char *, unsigned int);
-  void (*make_hmac)(SilcHmac, unsigned char *, unsigned int,
-                   unsigned char *);
-  void (*make_hmac_with_key)(SilcHmac, unsigned char *, unsigned int,
-                            unsigned char *, unsigned int, unsigned char *);
-  void (*make_hmac_truncated)(SilcHmac, unsigned char *, 
-                             unsigned int, unsigned int, unsigned char *);
-};
+/****f* silccrypt/SilcHMACAPI/silc_hmac_len
+ *
+ * SYNOPSIS
+ *
*    uint32 silc_hmac_len(SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the length of the MAC that the HMAC will produce.
+ *
+ ***/
+uint32 silc_hmac_len(SilcHmac hmac);
 
-/* Prototypes */
-int silc_hmac_alloc(SilcHash hash, SilcHmac *new_hmac);
-void silc_hmac_free(SilcHmac hmac);
+/****f* silccrypt/SilcHMACAPI/silc_hmac_get_hash
+ *
+ * SYNOPSIS
+ *
+ *    SilcHash silc_hmac_get_hash(SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the SilcHash context that has been associated with the
+ *    HMAC context.  The caller must not free the returned context.
+ *
+ ***/
+SilcHash silc_hmac_get_hash(SilcHmac hmac);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_get_name
+ *
+ * SYNOPSIS
+ *
+ *    const char *silc_hmac_get_name(SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the name of the HMAC context.
+ *
+ ***/
+const char *silc_hmac_get_name(SilcHmac hmac);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_set_key
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
+ *                           uint32 key_len);
+ *
+ * DESCRIPTION
+ *
+ *    Sets the key to be used in the HMAC operation.  This must be set
+ *    before calling silc_hmac_make or silc_hmac_final functions.  If
+ *    you do not want to set the key you can still produce a MAC by
+ *    calling the silc_hmac_make_with_key where you give the key as
+ *    argument.  Usually application still wants to set the key.
+ *
+ ***/
 void silc_hmac_set_key(SilcHmac hmac, const unsigned char *key,
-                      unsigned int key_len);
-void silc_hmac_make(SilcHmac hmac, 
-                   unsigned char *data, 
-                   unsigned int data_len,
-                   unsigned char *return_hash);
-void silc_hmac_make_with_key(SilcHmac hmac, 
-                            unsigned char *data, 
-                            unsigned int data_len,
-                            unsigned char *key, 
-                            unsigned int key_len, 
-                            unsigned char *return_hash);
+                      uint32 key_len);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_make
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_make(SilcHmac hmac, unsigned char *data,
+ *                        uint32 data_len, unsigned char *return_hash,
+ *                        uint32 *return_len);
+ *
+ * DESCRIPTION
+ *
+ *    Computes a MAC from a data buffer indicated by the `data' of the
+ *    length of `data_len'.  The returned MAC is copied into the 
+ *    `return_hash' pointer which must be at least the size of the
+ *    value silc_hmac_len returns.  The returned length is still
+ *    returned to `return_len'.
+ *
+ ***/
+void silc_hmac_make(SilcHmac hmac, unsigned char *data,
+                   uint32 data_len, unsigned char *return_hash,
+                   uint32 *return_len);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_make_with_key
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+ *                                 uint32 data_len, 
+ *                                 unsigned char *key, uint32 key_len,
+ *                                 unsigned char *return_hash,
+ *                                 uint32 *return_len);
+ *
+ * DESCRIPTION
+ *
+ *    Same as the silc_hmac_make but takes the key for the HMAC as
+ *    argument.  If this is used the key that may have been set by calling
+ *    silc_hmac_set_key is ignored.
+ *
+ ***/
+void silc_hmac_make_with_key(SilcHmac hmac, unsigned char *data,
+                            uint32 data_len, 
+                            unsigned char *key, uint32 key_len,
+                            unsigned char *return_hash,
+                            uint32 *return_len);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_make_truncated
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_make_truncated(SilcHmac hmac, 
+ *                                  unsigned char *data, 
+ *                                  uint32 data_len,
+ *                                  uint32 truncated_len,
+ *                                  unsigned char *return_hash);
+ *
+ * DESCRIPTION
+ *
+ *    Same as the silc_hmac_make except that the returned MAC is
+ *    truncated to the length indicated by the `truncated_len'.  Some
+ *    special applications may need this function.  The `return_hash'
+ *    must be at least the size of `truncated_len'.
+ *
+ * NOTES
+ *
+ *    For security reasons, one should not truncate to less than half
+ *    of the length of the true MAC lenght.  However, since this routine
+ *    may be used to non-critical applications this allows these dangerous
+ *    truncations.
+ *
+ ***/
 void silc_hmac_make_truncated(SilcHmac hmac, 
                              unsigned char *data, 
-                             unsigned int data_len,
-                             unsigned int truncated_len,
+                             uint32 data_len,
+                             uint32 truncated_len,
                              unsigned char *return_hash);
 
+/****f* silccrypt/SilcHMACAPI/silc_hmac_init
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_init(SilcHmac hmac);
+ *
+ * DESCRIPTION
+ *
+ *    Sometimes calling the silc_hmac_make might not be the most
+ *    optimal case of doing MACs.  If you have a lot of different data
+ *    that you need to put together for computing a MAC you may either
+ *    put them into a buffer and compute the MAC from the buffer by
+ *    calling the silc_hmac_make, or you can use the silc_hmac_init,
+ *    silc_hmac_update and silc_hmac_final to do the MAC.  This function
+ *    prepares the allocated HMAC context for this kind of MAC 
+ *    computation.  The caller must have been called the function
+ *    silc_hmac_set_key before calling this function.  To add the
+ *    data to be used in the MAC computation call the silc_hmac_update
+ *    function.
+ *
+ ***/
+void silc_hmac_init(SilcHmac hmac);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_init_with_key
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
+ *                                 uint32 key_len);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_hmac_init but initializes with specific key.  The
+ *    key that may have been set with silc_hmac_set_key is ignored.
+ *
+ ***/
+void silc_hmac_init_with_key(SilcHmac hmac, const unsigned char *key,
+                            uint32 key_len);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_update
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
+ *                          uint32 data_len);
+ *
+ * DESCRIPTION
+ *
+ *    This function may be called to add data to be used in the MAC
+ *    computation.  This can be called multiple times to add data from
+ *    many sources before actually performing the HMAC.  Once you've
+ *    added all the data you need you can call the silc_hmac_final to
+ *    actually produce the MAC.
+ *
+ * EXAMPLE
+ *
+ *    unsigned char mac[20];
+ *    uint32 mac_len;
+ *
+ *    silc_hmac_init(hmac);
+ *    silc_hmac_update(hmac, data, data_len);
+ *    silc_hmac_update(hmac, more_data, more_data_len);
+ *    silc_hmac_final(hmac, mac, &mac_len);
+ *
+ ***/
+void silc_hmac_update(SilcHmac hmac, const unsigned char *data,
+                     uint32 data_len);
+
+/****f* silccrypt/SilcHMACAPI/silc_hmac_final
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
+ *                         uint32 *return_len);
+ *
+ * DESCRIPTION
+ *
+ *    This function is used to produce the final MAC from the data
+ *    that has been added to the HMAC context by calling the 
+ *    silc_hmac_update function.  The MAC is copied in to the
+ *    `return_hash' pointer which must be at least the size that
+ *    the silc_hmac_len returns.  The length of the MAC is still
+ *    returned into `return_len'.
+ *
+ ***/
+void silc_hmac_final(SilcHmac hmac, unsigned char *return_hash,
+                    uint32 *return_len);
+
 #endif
index 6b99d59b62f31c0973e45643d67587be2e62dc3a..2f9699bb064bdeb51674a554f13b12f9c839d129 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
+/* $Id$ */
 
 #include "silcincludes.h"
 
 #include "rsa.h"
+#include "pkcs1.h"
 
-/* List of all PKCS's in SILC. PKCS's don't support SIM's thus
-   only static declarations are possible. XXX: I hope this to change
-   real soon. */
-SilcPKCSObject silc_pkcs_list[] =
+/* Dynamically registered list of PKCS. */
+SilcDList silc_pkcs_list = NULL;
+
+/* Static list of PKCS for silc_pkcs_register_default(). */
+SilcPKCSObject silc_default_pkcs[] =
 {
-  { "rsa", &silc_rsa_data_context, 
+  /* RSA with PKCS #1 (Uses directly routines from Raw RSA operations) */
+  { "rsa", 
+    silc_rsa_init, silc_rsa_clear_keys, silc_rsa_get_public_key,
+    silc_rsa_get_private_key, silc_rsa_set_public_key,
+    silc_rsa_set_private_key, silc_rsa_context_len,
+    silc_pkcs1_encrypt, silc_pkcs1_decrypt,
+    silc_pkcs1_sign, silc_pkcs1_verify },
+
+  /* Raw RSA operations */
+  { "rsa-raw", 
     silc_rsa_init, silc_rsa_clear_keys, silc_rsa_get_public_key,
     silc_rsa_get_private_key, silc_rsa_set_public_key,
     silc_rsa_set_private_key, silc_rsa_context_len,
-    silc_rsa_data_context_len, silc_rsa_set_arg,
     silc_rsa_encrypt, silc_rsa_decrypt,
     silc_rsa_sign, silc_rsa_verify },
 
@@ -39,58 +50,127 @@ SilcPKCSObject silc_pkcs_list[] =
     NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
-/* Allocates a new SilcPKCS object. The new allocated object is returned
-   to the 'new_pkcs' argument. This function also initializes the data
-   context structure. Function returns 1 on success and 0 on error.
+/* Register a new PKCS into SILC. This is used at the initialization of
+   the SILC. */
 
-*/
-int silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs)
+bool silc_pkcs_register(SilcPKCSObject *pkcs)
 {
-  int i;
+  SilcPKCSObject *new;
 
-  SILC_LOG_DEBUG(("Allocating new PKCS object"));
+  SILC_LOG_DEBUG(("Registering new PKCS `%s'", pkcs->name));
 
-  for (i = 0; silc_pkcs_list[i].name; i++) {
-    if (!strcmp(silc_pkcs_list[i].name, name))
-      break;
-  }
+  new = silc_calloc(1, sizeof(*new));
+  new->name = strdup(pkcs->name);
+  new->init = pkcs->init;
+  new->clear_keys = pkcs->clear_keys;
+  new->get_public_key = pkcs->get_public_key;
+  new->get_private_key = pkcs->get_private_key;
+  new->set_public_key = pkcs->set_public_key;
+  new->set_private_key = pkcs->set_private_key;
+  new->context_len = pkcs->context_len;
+  new->encrypt = pkcs->encrypt;
+  new->decrypt = pkcs->decrypt;
+  new->sign = pkcs->sign;
+  new->verify = pkcs->verify;
 
-  if (silc_pkcs_list[i].name == NULL)
-    return FALSE;
+  /* Add to list */
+  if (silc_pkcs_list == NULL)
+    silc_pkcs_list = silc_dlist_init();
+  silc_dlist_add(silc_pkcs_list, new);
+
+  return TRUE;
+}
 
-  *new_pkcs = silc_calloc(1, sizeof(**new_pkcs));
-  if (*new_pkcs == NULL) {
-    SILC_LOG_ERROR(("Could not allocate new PKCS object"));
+/* Unregister a PKCS from the SILC. */
+
+bool silc_pkcs_unregister(SilcPKCSObject *pkcs)
+{
+  SilcPKCSObject *entry;
+
+  SILC_LOG_DEBUG(("Unregistering PKCS"));
+
+  if (!silc_pkcs_list)
     return FALSE;
+
+  silc_dlist_start(silc_pkcs_list);
+  while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
+    if (pkcs == SILC_ALL_PKCS || entry == pkcs) {
+      silc_dlist_del(silc_pkcs_list, entry);
+
+      if (silc_dlist_count(silc_pkcs_list) == 0) {
+       silc_dlist_uninit(silc_pkcs_list);
+       silc_pkcs_list = NULL;
+      }
+
+      return TRUE;
+    }
   }
 
-  /* Set the pointers */
-  (*new_pkcs)->pkcs = &silc_pkcs_list[i];
-  (*new_pkcs)->pkcs->data_context = 
-    silc_calloc(1, (*new_pkcs)->pkcs->data_context_len());
-  (*new_pkcs)->context = silc_calloc(1, (*new_pkcs)->pkcs->context_len());
-  (*new_pkcs)->get_key_len = silc_pkcs_get_key_len;
+  return FALSE;
+}
+
+/* Function that registers all the default PKCS (all builtin PKCS). 
+   The application may use this to register the default PKCS if specific
+   PKCS in any specific order is not wanted. */
+
+bool silc_pkcs_register_default(void)
+{
+  int i;
+
+  for (i = 0; silc_default_pkcs[i].name; i++)
+    silc_pkcs_register(&(silc_default_pkcs[i]));
 
   return TRUE;
 }
 
+/* Allocates a new SilcPKCS object. The new allocated object is returned
+   to the 'new_pkcs' argument. */
+
+bool silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs)
+{
+  SilcPKCSObject *entry;
+
+  SILC_LOG_DEBUG(("Allocating new PKCS object"));
+
+  if (silc_pkcs_list) {
+    silc_dlist_start(silc_pkcs_list);
+    while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name)) {
+       *new_pkcs = silc_calloc(1, sizeof(**new_pkcs));
+       (*new_pkcs)->pkcs = entry;
+       (*new_pkcs)->context = silc_calloc(1, entry->context_len());
+       (*new_pkcs)->get_key_len = silc_pkcs_get_key_len;
+       return TRUE;
+      }
+    }
+  }
+
+  return FALSE;
+}
+
 /* Free's the PKCS object */
 
 void silc_pkcs_free(SilcPKCS pkcs)
 {
-  if (pkcs)
+  if (pkcs) {
+    pkcs->pkcs->clear_keys(pkcs->context);
     silc_free(pkcs->context);
+  }
+  silc_free(pkcs);
 }
 
 /* Return TRUE if PKCS algorithm `name' is supported. */
 
 int silc_pkcs_is_supported(const unsigned char *name)
 {
-  int i;
+  SilcPKCSObject *entry;
 
-  for (i = 0; silc_pkcs_list[i].name; i++) {
-    if (!strcmp(silc_pkcs_list[i].name, name))
-      return TRUE;
+  if (silc_pkcs_list) {
+    silc_dlist_start(silc_pkcs_list);
+    while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
+      if (!strcmp(entry->name, name))
+       return TRUE;
+    }
   }
 
   return FALSE;
@@ -98,146 +178,935 @@ int silc_pkcs_is_supported(const unsigned char *name)
 
 /* Returns comma separated list of supported PKCS algorithms */
 
-char *silc_pkcs_get_supported()
+char *silc_pkcs_get_supported(void)
 {
+  SilcPKCSObject *entry;
   char *list = NULL;
-  int i, len;
+  int len;
 
   len = 0;
-  for (i = 0; silc_pkcs_list[i].name; i++) {
-    len += strlen(silc_pkcs_list[i].name);
-    list = silc_realloc(list, len + 1);
-
-    memcpy(list + (len - strlen(silc_pkcs_list[i].name)), 
-          silc_pkcs_list[i].name, strlen(silc_pkcs_list[i].name));
-    memcpy(list + len, ",", 1);
-    len++;
+  if (silc_pkcs_list) {
+    silc_dlist_start(silc_pkcs_list);
+    while ((entry = silc_dlist_get(silc_pkcs_list)) != SILC_LIST_END) {
+      len += strlen(entry->name);
+      list = silc_realloc(list, len + 1);
+      
+      memcpy(list + (len - strlen(entry->name)), 
+            entry->name, strlen(entry->name));
+      memcpy(list + len, ",", 1);
+      len++;
+    }
+    list[len - 1] = 0;
   }
 
-  list[len - 1] = 0;
-
   return list;
 }
 
 /* Returns the length of the key */
 
-unsigned int silc_pkcs_get_key_len(SilcPKCS self)
+uint32 silc_pkcs_get_key_len(SilcPKCS pkcs)
 {
-  return self->key_len;
+  return pkcs->key_len;
 }
 
 /* Returns SILC style public key */
 
-unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, unsigned int *len)
+unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, uint32 *len)
 {
   return pkcs->pkcs->get_public_key(pkcs->context, len);
 }
 
 /* Returns SILC style private key */
 
-unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, unsigned int *len)
+unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, uint32 *len)
 {
   return pkcs->pkcs->get_private_key(pkcs->context, len);
 }
 
-/* Sets public key */
-/* XXX rewrite */
+/* Sets public key from SilcPublicKey. */
+
+uint32 silc_pkcs_public_key_set(SilcPKCS pkcs, SilcPublicKey public_key)
+{
+  pkcs->key_len = pkcs->pkcs->set_public_key(pkcs->context, public_key->pk, 
+                                            public_key->pk_len);
+  return pkcs->key_len;
+}
+
+/* Sets public key from data. */
 
-int silc_pkcs_set_public_key(SilcPKCS pkcs, unsigned char *pk, 
-                            unsigned int pk_len)
+uint32 silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk,
+                                    uint32 pk_len)
 {
-  return pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len);
+  pkcs->key_len = pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len);
+  return pkcs->key_len;
 }
 
-/* Sets private key */
+/* Sets private key from SilcPrivateKey. */
 
-int silc_pkcs_set_private_key(SilcPKCS pkcs, unsigned char *prv, 
-                             unsigned int prv_len)
+int silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key)
+{
+  return pkcs->pkcs->set_private_key(pkcs->context, private_key->prv, 
+                                    private_key->prv_len);
+}
+
+/* Sets private key from data. */
+
+int silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv,
+                                  uint32 prv_len)
 {
   return pkcs->pkcs->set_private_key(pkcs->context, prv, prv_len);
 }
 
-/* Saves public key into file */
+/* Encrypts */
+
+int silc_pkcs_encrypt(SilcPKCS pkcs, unsigned char *src, uint32 src_len,
+                     unsigned char *dst, uint32 *dst_len)
+{
+  return pkcs->pkcs->encrypt(pkcs->context, src, src_len, dst, dst_len);
+}
+
+/* Decrypts */
+
+int silc_pkcs_decrypt(SilcPKCS pkcs, unsigned char *src, uint32 src_len,
+                     unsigned char *dst, uint32 *dst_len)
+{
+  return pkcs->pkcs->decrypt(pkcs->context, src, src_len, dst, dst_len);
+}
+
+/* Generates signature */
+
+int silc_pkcs_sign(SilcPKCS pkcs, unsigned char *src, uint32 src_len,
+                  unsigned char *dst, uint32 *dst_len)
+{
+  return pkcs->pkcs->sign(pkcs->context, src, src_len, dst, dst_len);
+}
+
+/* Verifies signature */
+
+int silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature, 
+                    uint32 signature_len, unsigned char *data, 
+                    uint32 data_len)
+{
+  return pkcs->pkcs->verify(pkcs->context, signature, signature_len, 
+                           data, data_len);
+}
+
+/* Generates signature with hash. The hash is signed. */
+
+int silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash,
+                            unsigned char *src, uint32 src_len,
+                            unsigned char *dst, uint32 *dst_len)
+{
+  unsigned char hashr[32];
+  uint32 hash_len;
+  int ret;
+
+  silc_hash_make(hash, src, src_len, hashr);
+  hash_len = hash->hash->hash_len;
+
+  SILC_LOG_HEXDUMP(("Hash"), hashr, hash_len);
+
+  ret = pkcs->pkcs->sign(pkcs->context, hashr, hash_len, dst, dst_len);
+  memset(hashr, 0, sizeof(hashr));
+
+  return ret;
+}
 
-int silc_pkcs_save_public_key(SilcPKCS pkcs, char *filename,
-                             unsigned char *pk, unsigned int pk_len)
+/* Verifies signature with hash. The `data' is hashed and verified against
+   the `signature'. */
+
+int silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash, 
+                              unsigned char *signature, 
+                              uint32 signature_len, 
+                              unsigned char *data, 
+                              uint32 data_len)
+{
+  unsigned char hashr[32];
+  uint32 hash_len;
+  int ret;
+
+  silc_hash_make(hash, data, data_len, hashr);
+  hash_len = hash->hash->hash_len;
+
+  SILC_LOG_HEXDUMP(("Hash"), hashr, hash_len);
+
+  ret = pkcs->pkcs->verify(pkcs->context, signature, signature_len, 
+                          hashr, hash_len);
+  memset(hashr, 0, sizeof(hashr));
+
+  return ret;
+}
+
+/* Encodes and returns SILC public key identifier. If some of the 
+   arguments is NULL those are not encoded into the identifier string.
+   Protocol says that at least username and host must be provided. */
+
+char *silc_pkcs_encode_identifier(char *username, char *host, char *realname,
+                                 char *email, char *org, char *country)
 {
   SilcBuffer buf;
-  int ret = TRUE;
+  char *identifier;
+  uint32 len, tlen = 0;
+
+  if (!username || !host)
+    return NULL;
+
+  len = (username ? strlen(username) : 0) +
+       (host     ? strlen(host)     : 0) +
+       (realname ? strlen(realname) : 0) +
+       (email    ? strlen(email)    : 0) +
+       (org      ? strlen(org)      : 0) +
+       (country  ? strlen(country)  : 0);
+  
+  if (len < 3)
+    return NULL;
+
+  len += 3 + 5 + 5 + 4 + 4 + 4;
+  buf = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buf, len);
+
+  if (username) {
+    silc_buffer_format(buf,
+                      SILC_STR_UI32_STRING("UN="),
+                      SILC_STR_UI32_STRING(username),
+                      SILC_STR_END);
+    silc_buffer_pull(buf, 3 + strlen(username));
+    tlen = 3 + strlen(username); 
+  }
+    
+  if (host) {
+    silc_buffer_format(buf,
+                      SILC_STR_UI32_STRING(", "),
+                      SILC_STR_UI32_STRING("HN="),
+                      SILC_STR_UI32_STRING(host),
+                      SILC_STR_END);
+    silc_buffer_pull(buf, 5 + strlen(host));
+    tlen += 5 + strlen(host); 
+  }
+
+  if (realname) {
+    silc_buffer_format(buf,
+                      SILC_STR_UI32_STRING(", "),
+                      SILC_STR_UI32_STRING("RN="),
+                      SILC_STR_UI32_STRING(realname),
+                      SILC_STR_END);
+    silc_buffer_pull(buf, 5 + strlen(realname));
+    tlen += 5 + strlen(realname); 
+  }
+
+  if (email) {
+    silc_buffer_format(buf,
+                      SILC_STR_UI32_STRING(", "),
+                      SILC_STR_UI32_STRING("E="),
+                      SILC_STR_UI32_STRING(email),
+                      SILC_STR_END);
+    silc_buffer_pull(buf, 4 + strlen(email));
+    tlen += 4 + strlen(email); 
+  }
+
+  if (org) {
+    silc_buffer_format(buf,
+                      SILC_STR_UI32_STRING(", "),
+                      SILC_STR_UI32_STRING("O="),
+                      SILC_STR_UI32_STRING(org),
+                      SILC_STR_END);
+    silc_buffer_pull(buf, 4 + strlen(org));
+    tlen += 4 + strlen(org); 
+  }
+
+  if (country) {
+    silc_buffer_format(buf,
+                      SILC_STR_UI32_STRING(", "),
+                      SILC_STR_UI32_STRING("C="),
+                      SILC_STR_UI32_STRING(country),
+                      SILC_STR_END);
+    silc_buffer_pull(buf, 4 + strlen(country));
+    tlen += 4 + strlen(country); 
+  }
+
+  silc_buffer_push(buf, buf->data - buf->head);
+  identifier = silc_calloc(tlen + 1, sizeof(*identifier));
+  memcpy(identifier, buf->data, tlen);
+  silc_buffer_free(buf);
+
+  return identifier;
+}
+
+/* Decodes the provided `identifier' and returns allocated context for
+   the identifier. */
 
-  buf = silc_buffer_alloc(strlen(pkcs->pkcs->name) + 2 + pk_len
-                         + strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) 
-                         + strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
+SilcPublicKeyIdentifier silc_pkcs_decode_identifier(char *identifier)
+{
+  SilcPublicKeyIdentifier ident;
+  char *cp, *item;
+  int len;
+
+  ident = silc_calloc(1, sizeof(*ident));
+
+  cp = identifier;
+  while (cp) {
+    len = strcspn(cp, ",");
+    if (len - 1 >= 0 && cp[len - 1] == '\\') {
+      while (cp) {
+       cp += len + 1;
+       len = strcspn(cp, ",") + len;
+       if (len - 1 >= 0 && cp[len - 1] != '\\')
+         break;
+      }
+    }
+
+    item = silc_calloc(len + 1, sizeof(char));
+    memcpy(item, cp, len);
+
+    if (strstr(item, "UN="))
+      ident->username = strdup(item + strcspn(cp, "=") + 1);
+    else if (strstr(item, "HN="))
+      ident->host = strdup(item + strcspn(cp, "=") + 1);
+    else if (strstr(item, "RN="))
+      ident->realname = strdup(item + strcspn(cp, "=") + 1);
+    else if (strstr(item, "E="))
+      ident->email = strdup(item + strcspn(cp, "=") + 1);
+    else if (strstr(item, "O="))
+      ident->org = strdup(item + strcspn(cp, "=") + 1);
+    else if (strstr(item, "C="))
+      ident->country = strdup(item + strcspn(cp, "=") + 1);
+    
+    cp += len;
+    if (strlen(cp) == 0)
+      cp = NULL;
+    else
+      cp += 1;
+    
+    if (item)
+      silc_free(item);
+  }
+
+  return ident;
+}
 
+/* Free's decoded public key identifier context. Call this to free the
+   context returned by the silc_pkcs_decode_identifier. */
+
+void silc_pkcs_free_identifier(SilcPublicKeyIdentifier identifier)
+{
+  silc_free(identifier->username);
+  silc_free(identifier->host);
+  silc_free(identifier->realname);
+  silc_free(identifier->email);
+  silc_free(identifier->org);
+  silc_free(identifier->country);
+  silc_free(identifier);
+}
+
+/* Allocates SILC style public key formed from sent arguments. All data
+   is duplicated. */
+
+SilcPublicKey silc_pkcs_public_key_alloc(char *name, char *identifier,
+                                        unsigned char *pk, 
+                                        uint32 pk_len)
+{
+  SilcPublicKey public_key;
+
+  public_key = silc_calloc(1, sizeof(*public_key));
+  public_key->len = 4 + 2 + strlen(name) + 2 + strlen(identifier) + pk_len;
+  public_key->name = strdup(name);
+  public_key->identifier = strdup(identifier);
+  public_key->pk_len = pk_len;
+  public_key->pk = silc_calloc(pk_len, sizeof(*public_key->pk));
+  memcpy(public_key->pk, pk, pk_len);
+
+  return public_key;
+}
+
+/* Free's public key */
+
+void silc_pkcs_public_key_free(SilcPublicKey public_key)
+{
+  if (public_key) {
+    silc_free(public_key->name);
+    silc_free(public_key->identifier);
+    silc_free(public_key->pk);
+    silc_free(public_key);
+  }
+}
+
+/* Allocates SILC private key formed from sent arguments. All data is
+   duplicated. */
+
+SilcPrivateKey silc_pkcs_private_key_alloc(char *name, unsigned char *prv,
+                                          uint32 prv_len)
+{
+  SilcPrivateKey private_key;
+
+  private_key = silc_calloc(1, sizeof(*private_key));
+  private_key->name = strdup(name);
+  private_key->prv_len = prv_len;
+  private_key->prv = silc_calloc(prv_len, sizeof(*private_key->prv));
+  memcpy(private_key->prv, prv, prv_len);
+
+  return private_key;
+}
+
+/* Free's private key */
+
+void silc_pkcs_private_key_free(SilcPrivateKey private_key)
+{
+  if (private_key) {
+    silc_free(private_key->name);
+    silc_free(private_key->prv);
+    silc_free(private_key);
+  }
+}
+
+/* Encodes SILC style public key from SilcPublicKey. Returns the encoded
+   data. */
+
+unsigned char *
+silc_pkcs_public_key_encode(SilcPublicKey public_key, uint32 *len)
+{
+  SilcBuffer buf;
+  unsigned char *ret;
+
+  buf = silc_buffer_alloc(public_key->len);
   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
 
   silc_buffer_format(buf,
-                    SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_BEGIN),
-                    SILC_STR_UI32_STRING(pkcs->pkcs->name),
-                    SILC_STR_UI_SHORT(pk_len),
+                    SILC_STR_UI_INT(public_key->len),
+                    SILC_STR_UI_SHORT(strlen(public_key->name)),
+                    SILC_STR_UI32_STRING(public_key->name),
+                    SILC_STR_UI_SHORT(strlen(public_key->identifier)),
+                    SILC_STR_UI32_STRING(public_key->identifier),
+                    SILC_STR_UI_XNSTRING(public_key->pk, 
+                                         public_key->pk_len),
+                    SILC_STR_END);
+  if (len)
+    *len = public_key->len;
+
+  ret = silc_calloc(buf->len, sizeof(*ret));
+  memcpy(ret, buf->data, buf->len);
+  silc_buffer_free(buf);
+
+  return ret;
+}
+
+/* Encodes SILC style public key. Returns the encoded data. */
+
+unsigned char *
+silc_pkcs_public_key_data_encode(unsigned char *pk, uint32 pk_len,
+                                char *pkcs, char *identifier, 
+                                uint32 *len)
+{
+  SilcBuffer buf;
+  unsigned char *ret;
+  uint32 totlen;
+
+  totlen = 4 + 2 + strlen(pkcs) + 2 + strlen(identifier) + pk_len;
+  buf = silc_buffer_alloc(totlen);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+  silc_buffer_format(buf,
+                    SILC_STR_UI_INT(totlen),
+                    SILC_STR_UI_SHORT(strlen(pkcs)),
+                    SILC_STR_UI32_STRING(pkcs),
+                    SILC_STR_UI_SHORT(strlen(identifier)),
+                    SILC_STR_UI32_STRING(identifier),
                     SILC_STR_UI_XNSTRING(pk, pk_len),
-                    SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_END),
                     SILC_STR_END);
+  if (len)
+    *len = totlen;
 
-  /* Save into a file */
-  if (silc_file_write(filename, buf->data, buf->len)) {
-    ret = FALSE;
-    goto out;
+  ret = silc_calloc(buf->len, sizeof(*ret));
+  memcpy(ret, buf->data, buf->len);
+  silc_buffer_free(buf);
+
+  return ret;
+}
+
+/* Decodes SILC style public key. Returns TRUE if the decoding was
+   successful. Allocates new public key as well. */
+
+int silc_pkcs_public_key_decode(unsigned char *data, uint32 data_len,
+                               SilcPublicKey *public_key)
+{
+  SilcBuffer buf;
+  SilcPKCS alg;
+  uint16 pkcs_len, identifier_len;
+  uint32 totlen, key_len;
+  unsigned char *pkcs_name = NULL, *ident = NULL, *key_data = NULL;
+  int ret;
+
+  buf = silc_buffer_alloc(data_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  silc_buffer_put(buf, data, data_len);
+
+  /* Get length */
+  ret = silc_buffer_unformat(buf,
+                            SILC_STR_UI_INT(&totlen),
+                            SILC_STR_END);
+  if (ret == -1) {
+    silc_buffer_free(buf);
+    return FALSE;
+  }
+
+  if (totlen != data_len) {
+    silc_buffer_free(buf);
+    return FALSE;
+  }
+
+  /* Get algorithm name and identifier */
+  silc_buffer_pull(buf, 4);
+  ret =
+    silc_buffer_unformat(buf,
+                        SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&ident, &identifier_len),
+                        SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  if (pkcs_len < 1 || identifier_len < 3 || 
+      pkcs_len + identifier_len > totlen)
+    goto err;
+
+  /* See if we support this algorithm (check only if PKCS are registered) */
+  if (silc_pkcs_list && !silc_pkcs_is_supported(pkcs_name)) {
+    SILC_LOG_DEBUG(("Unknown PKCS %s", pkcs_name));
+    goto err;
+  }
+
+  /* Protocol says that at least UN and HN must be provided as identifier,
+     check for these. */
+  if (!strstr(ident, "UN=") && !strstr(ident, "HN=")) {
+    SILC_LOG_DEBUG(("The public does not have the required UN= and HN= "
+                   "identifiers"));
+    goto err;
+  }
+
+  /* Get key data. We assume that rest of the buffer is key data. */
+  silc_buffer_pull(buf, 2 + pkcs_len + 2 + identifier_len);
+  key_len = buf->len;
+  ret = silc_buffer_unformat(buf,
+                            SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  /* Try to set the key. If this fails the key must be malformed. This
+     code assumes that the PKCS routine checks the format of the key. 
+     (check only if PKCS are registered) */
+  if (silc_pkcs_list) {
+    silc_pkcs_alloc(pkcs_name, &alg);
+    if (!alg->pkcs->set_public_key(alg->context, key_data, key_len))
+      goto err;
+    silc_pkcs_free(alg);
   }
+  
+  if (public_key) {
+    *public_key = silc_calloc(1, sizeof(**public_key));
+    (*public_key)->len = totlen;
+    (*public_key)->name = pkcs_name;
+    (*public_key)->identifier = ident;
+    (*public_key)->pk = key_data;
+    (*public_key)->pk_len = key_len;
+  }
+
+  silc_buffer_free(buf);
+  return TRUE;
 
- out:
+ err:
+  if (pkcs_name)
+    silc_free(pkcs_name);
+  if (ident)
+    silc_free(ident);
+  if (key_data)
+    silc_free(key_data);
   silc_buffer_free(buf);
+  return FALSE;
+}
+
+/* Compares two public keys and returns TRUE if they are same key, and
+   FALSE if they are not same. */
+
+bool silc_pkcs_public_key_compare(SilcPublicKey key1, SilcPublicKey key2)
+{
+  if (key1 == key2)
+    return TRUE;
+
+  if (key1->len == key2->len &&
+      key1->name && key2->name && key1->identifier && key2->identifier &&
+      !strcmp(key1->name, key2->name) &&
+      !strcmp(key1->identifier, key2->identifier) &&
+      !memcmp(key1->pk, key2->pk, key1->pk_len) &&
+      key1->pk_len == key2->pk_len)
+    return TRUE;
+
+  return FALSE;
+}
+
+/* Encodes SILC private key from SilcPrivateKey. Returns the encoded data. */
+
+unsigned char *
+silc_pkcs_private_key_encode(SilcPrivateKey private_key, uint32 *len)
+{
+  SilcBuffer buf;
+  unsigned char *ret;
+  uint32 totlen;
+
+  totlen = 2 + strlen(private_key->name) + private_key->prv_len;
+  buf = silc_buffer_alloc(totlen);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+  silc_buffer_format(buf,
+                    SILC_STR_UI_SHORT(strlen(private_key->name)),
+                    SILC_STR_UI32_STRING(private_key->name),
+                    SILC_STR_UI_XNSTRING(private_key->prv, 
+                                         private_key->prv_len),
+                    SILC_STR_END);
+  if (len)
+    *len = totlen;
+
+  ret = silc_calloc(buf->len, sizeof(*ret));
+  memcpy(ret, buf->data, buf->len);
+  silc_buffer_free(buf);
+
   return ret;
 }
 
-/* XXX The buffer should be encrypted */
-/* XXX rewrite */
+/* Encodes SILC private key. Returns the encoded data. */
+
+unsigned char *
+silc_pkcs_private_key_data_encode(unsigned char *prv, uint32 prv_len,
+                                 char *pkcs, uint32 *len)
+{
+  SilcBuffer buf;
+  unsigned char *ret;
+  uint32 totlen;
+
+  totlen = 2 + strlen(pkcs) + prv_len;
+  buf = silc_buffer_alloc(totlen);
+  silc_buffer_pull_tail(buf, totlen);
+
+  silc_buffer_format(buf,
+                    SILC_STR_UI_SHORT(strlen(pkcs)),
+                    SILC_STR_UI32_STRING(pkcs),
+                    SILC_STR_UI_XNSTRING(prv, prv_len),
+                    SILC_STR_END);
+  if (len)
+    *len = totlen;
+
+  ret = silc_calloc(buf->len, sizeof(*ret));
+  memcpy(ret, buf->data, buf->len);
+  silc_buffer_free(buf);
+
+  return ret;
+}
+
+/* Decodes SILC style public key. Returns TRUE if the decoding was
+   successful. Allocates new private key as well. */
+
+int silc_pkcs_private_key_decode(unsigned char *data, uint32 data_len,
+                                SilcPrivateKey *private_key)
+{
+  SilcBuffer buf;
+  SilcPKCS alg;
+  uint16 pkcs_len;
+  uint32 key_len;
+  unsigned char *pkcs_name = NULL, *key_data = NULL;
+  int ret;
+
+  buf = silc_buffer_alloc(data_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  silc_buffer_put(buf, data, data_len);
+
+  /* Get algorithm name and identifier */
+  ret = 
+    silc_buffer_unformat(buf,
+                        SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
+                        SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  if (pkcs_len < 1 || pkcs_len > buf->truelen)
+    goto err;
+
+  /* See if we support this algorithm (check only if PKCS are registered). */
+  if (silc_pkcs_list && !silc_pkcs_is_supported(pkcs_name)) {
+    SILC_LOG_DEBUG(("Unknown PKCS `%s'", pkcs_name));
+    goto err;
+  }
+
+  /* Get key data. We assume that rest of the buffer is key data. */
+  silc_buffer_pull(buf, 2 + pkcs_len);
+  key_len = buf->len;
+  ret = silc_buffer_unformat(buf,
+                            SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
+                            SILC_STR_END);
+  if (ret == -1)
+    goto err;
+
+  /* Try to set the key. If this fails the key must be malformed. This
+     code assumes that the PKCS routine checks the format of the key. 
+     (check only if PKCS are registered) */
+  if (silc_pkcs_list) {
+    silc_pkcs_alloc(pkcs_name, &alg);
+    if (!alg->pkcs->set_private_key(alg->context, key_data, key_len))
+      goto err;
+    silc_pkcs_free(alg);
+  }
+  
+  if (private_key) {
+    *private_key = silc_calloc(1, sizeof(**private_key));
+    (*private_key)->name = pkcs_name;
+    (*private_key)->prv = key_data;
+    (*private_key)->prv_len = key_len;
+  }
+
+  silc_buffer_free(buf);
+  return TRUE;
+
+ err:
+  if (pkcs_name)
+    silc_free(pkcs_name);
+  if (key_data)
+    silc_free(key_data);
+  silc_buffer_free(buf);
+  return FALSE;
+}
+
+/* Internal routine to save public key */
 
-int silc_pkcs_save_private_key(SilcPKCS pkcs, char *filename,
-                              unsigned char *prv, unsigned int prv_len,
-                              char *passphrase)
+static int silc_pkcs_save_public_key_internal(char *filename,
+                                             unsigned char *data,
+                                             uint32 data_len,
+                                             uint32 encoding)
 {
   SilcBuffer buf;
-  int ret = TRUE;
+  uint32 len;
+
+  switch(encoding) {
+  case SILC_PKCS_FILE_BIN:
+    break;
+  case SILC_PKCS_FILE_PEM:
+    data = silc_encode_pem_file(data, data_len);
+    data_len = strlen(data);
+    break;
+  }
 
-  buf = silc_buffer_alloc(strlen(pkcs->pkcs->name) + 2 + prv_len
-                         + strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) 
-                         + strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
+  len = data_len + (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
+                   strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
+  buf = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+  silc_buffer_format(buf,
+                    SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_BEGIN),
+                    SILC_STR_UI_XNSTRING(data, data_len),
+                    SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_END),
+                    SILC_STR_END);
+
+  /* Save into file */
+  if (silc_file_writefile(filename, buf->data, buf->len)) {
+    silc_buffer_free(buf);
+    return FALSE;
+  }
+
+  silc_buffer_free(buf);
+  return TRUE;
+}
+
+/* Saves public key into file */
+
+int silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
+                             uint32 encoding)
+{
+  unsigned char *data;
+  uint32 data_len;
+
+  data = silc_pkcs_public_key_encode(public_key, &data_len);
+  return silc_pkcs_save_public_key_internal(filename, data, data_len,
+                                           encoding);
+}
+
+/* Saves public key into file */
+
+int silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
+                                  uint32 data_len,
+                                  uint32 encoding)
+{
+  return silc_pkcs_save_public_key_internal(filename, data, data_len,
+                                           encoding);
+}
+
+/* Internal routine to save private key. */
+
+static int silc_pkcs_save_private_key_internal(char *filename,
+                                              unsigned char *data,
+                                              uint32 data_len,
+                                              uint32 encoding)
+{
+  SilcBuffer buf;
+  uint32 len;
+
+  switch(encoding) {
+  case SILC_PKCS_FILE_BIN:
+    break;
+  case SILC_PKCS_FILE_PEM:
+    data = silc_encode_pem_file(data, data_len);
+    data_len = strlen(data);
+    break;
+  }
+
+  len = data_len + (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
+                   strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
+  buf = silc_buffer_alloc(len);
   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
 
   silc_buffer_format(buf,
                     SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_BEGIN),
-                    SILC_STR_UI32_STRING(pkcs->pkcs->name),
-                    SILC_STR_UI_SHORT(prv_len),
-                    SILC_STR_UI_XNSTRING(prv, prv_len),
+                    SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_END),
                     SILC_STR_END);
 
   /* Save into a file */
-  if (silc_file_write(filename, buf->data, buf->len)) {
-    ret = FALSE;
-    goto out;
+  if (silc_file_writefile_mode(filename, buf->data, buf->len, 0600)) {
+    silc_buffer_free(buf);
+    return FALSE;
   }
 
- out:
   silc_buffer_free(buf);
-  return ret;
+  return TRUE;
 }
 
-/* Loads public key from file and allocates new PKCS object and
-   sets the loaded key into it. */
+/* Saves private key into file. */
+/* XXX The buffer should be encrypted if passphrase is provided. */
 
-int silc_pkcs_load_public_key(char *filename, SilcPKCS *ret_pkcs)
+int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, 
+                              unsigned char *passphrase,
+                              uint32 encoding)
 {
+  unsigned char *data;
+  uint32 data_len;
 
+  data = silc_pkcs_private_key_encode(private_key, &data_len);
+  return silc_pkcs_save_private_key_internal(filename, data, data_len,
+                                            encoding);
+}
+
+/* Saves private key into file. */
+/* XXX The buffer should be encrypted if passphrase is provided. */
+
+int silc_pkcs_save_private_key_data(char *filename, unsigned char *data, 
+                                   uint32 data_len,
+                                   unsigned char *passphrase,
+                                   uint32 encoding)
+{
+  return silc_pkcs_save_private_key_internal(filename, data, data_len,
+                                            encoding);
+}
+
+/* Loads public key from file and allocates new public key. Returns TRUE
+   is loading was successful. */
+
+int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
+                             uint32 encoding)
+{
+  unsigned char *cp, *old, *data, byte;
+  uint32 i, data_len, len;
+
+  old = data = silc_file_readfile(filename, &data_len);
+  if (!data)
+    return FALSE;
+
+  /* Check start of file and remove header from the data. */
+  len = strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN);
+  cp = data;
+  for (i = 0; i < len; i++) {
+    byte = cp[0];
+    cp++;
+    if (byte != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i]) {
+      memset(old, 0, data_len);
+      silc_free(old);
+      return FALSE;
+    }
+  }
+  data = cp;
+
+  /* Decode public key */
+  if (public_key) {
+    len = data_len - (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
+                     strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
+
+    switch(encoding) {
+    case SILC_PKCS_FILE_BIN:
+      break;
+    case SILC_PKCS_FILE_PEM:
+      data = silc_decode_pem(data, len, &len);
+      memset(old, 0, data_len);
+      silc_free(old);
+      old = data; 
+      data_len = len;
+      break;
+    }
+
+    if (!data || !silc_pkcs_public_key_decode(data, len, public_key)) {
+      memset(old, 0, data_len);
+      silc_free(old);
+      return FALSE;
+    }
+  }
+
+  memset(old, 0, data_len);
+  silc_free(old);
   return TRUE;
 }
 
-/* Loads private key from file and allocates new PKCS object and
-   sets the loaded key into it. */
+/* Load private key from file and allocates new private key. Returns TRUE
+   if loading was successful. */
+/* XXX Should support encrypted private key files */
 
-int silc_pkcs_load_private_key(char *filename, SilcPKCS *ret_pkcs)
+int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
+                              uint32 encoding)
 {
+  unsigned char *cp, *old, *data, byte;
+  uint32 i, data_len, len;
+
+  old = data = silc_file_readfile(filename, &data_len);
+  if (!data)
+    return FALSE;
+
+  /* Check start of file and remove header from the data. */
+  len = strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN);
+  cp = data;
+  for (i = 0; i < len; i++) {
+    byte = cp[0];
+    cp++;
+    if (byte != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i]) {
+      memset(old, 0, data_len);
+      silc_free(old);
+      return FALSE;
+    }
+  }
+  data = cp;
+
+  /* Decode private key */
+  if (private_key) {
+    len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
+                     strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
+
+    switch(encoding) {
+    case SILC_PKCS_FILE_BIN:
+      break;
+    case SILC_PKCS_FILE_PEM:
+      data = silc_decode_pem(data, len, &len);
+      break;
+    }
+
+    if (!data || !silc_pkcs_private_key_decode(data, len, private_key)) {
+      memset(old, 0, data_len);
+      silc_free(old);
+      return FALSE;
+    }
+  }
 
+  memset(old, 0, data_len);
+  silc_free(old);
   return TRUE;
 }
index 9d7fd82da58cfd8c2ba3bcec2bcd2cbf3c3d1901..57968af9deff967e081a52bebb14cee0eb138d5d 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 /* The default SILC PKCS (Public Key Cryptosystem) object to represent
    any PKCS in SILC. */
 typedef struct SilcPKCSObjectStruct {
-  unsigned char *name;
-  void *data_context;
-
-  int (*init)(void *, unsigned int, SilcRng);
+  char *name;
+  int (*init)(void *, uint32, SilcRng);
   void (*clear_keys)(void *);
-  unsigned char *(*get_public_key)(void *, unsigned int *);
-  unsigned char *(*get_private_key)(void *, unsigned int *);
-  int (*set_public_key)(void *, unsigned char *, unsigned int);
-  int (*set_private_key)(void *, unsigned char *, unsigned int);
-  unsigned int (*context_len)();
-  unsigned int (*data_context_len)();
-  int (*set_arg)(void *, void *, int, SilcInt);
-  int (*encrypt)(void *, unsigned char *, unsigned int,
-                unsigned char *, unsigned int *);
-  int (*decrypt)(void *, unsigned char *, unsigned int,
-                unsigned char *, unsigned int *);
-  int (*sign)(void *, unsigned char *, unsigned int,
-             unsigned char *, unsigned int *);
-  int (*verify)(void *, unsigned char *, unsigned int,
-               unsigned char *, unsigned int);
+  unsigned char *(*get_public_key)(void *, uint32 *);
+  unsigned char *(*get_private_key)(void *, uint32 *);
+  uint32 (*set_public_key)(void *, unsigned char *, uint32);
+  int (*set_private_key)(void *, unsigned char *, uint32);
+  uint32 (*context_len)();
+  int (*encrypt)(void *, unsigned char *, uint32,
+                unsigned char *, uint32 *);
+  int (*decrypt)(void *, unsigned char *, uint32,
+                unsigned char *, uint32 *);
+  int (*sign)(void *, unsigned char *, uint32,
+             unsigned char *, uint32 *);
+  int (*verify)(void *, unsigned char *, uint32,
+               unsigned char *, uint32);
 } SilcPKCSObject;
 
 /* The main SILC PKCS structure. Use SilcPKCS instead of SilcPKCSStruct.
@@ -51,13 +47,40 @@ typedef struct SilcPKCSObjectStruct {
 typedef struct SilcPKCSStruct {
   void *context;
   SilcPKCSObject *pkcs;
-  unsigned int key_len;
+  uint32 key_len;
 
-  unsigned int (*get_key_len)(struct SilcPKCSStruct *);
+  uint32 (*get_key_len)(struct SilcPKCSStruct *);
 } *SilcPKCS;
 
-/* List of all PKCS in SILC. */
-extern SilcPKCSObject silc_pkcs_list[];
+/* SILC style public key object. Public key is read from file to this
+   object. Public keys received from network must be in this format as 
+   well. */
+typedef struct {
+  uint32 len;
+  char *name;
+  char *identifier;
+  unsigned char *pk;
+  uint32 pk_len;
+} *SilcPublicKey;
+
+/* SILC style private key object. Private key is read from file to this
+   object. */
+typedef struct {
+  char *name;
+  unsigned char *prv;
+  uint32 prv_len;
+} *SilcPrivateKey;
+
+/* Decoded SILC Public Key identifier. Note that some of the fields 
+   may be NULL. */
+typedef struct {
+  char *username;
+  char *host;
+  char *realname;
+  char *email;
+  char *org;
+  char *country;
+} *SilcPublicKeyIdentifier;
 
 /* Public and private key file headers */
 #define SILC_PKCS_PUBLIC_KEYFILE_BEGIN "-----BEGIN SILC PUBLIC KEY-----\n"
@@ -65,6 +88,20 @@ extern SilcPKCSObject silc_pkcs_list[];
 #define SILC_PKCS_PRIVATE_KEYFILE_BEGIN "-----BEGIN SILC PRIVATE KEY-----\n"
 #define SILC_PKCS_PRIVATE_KEYFILE_END "\n-----END SILC PRIVATE KEY-----\n"
 
+/* Public and private key file encoding types */
+#define SILC_PKCS_FILE_BIN 0
+#define SILC_PKCS_FILE_PEM 1
+
+/* Marks for all PKCS in silc. This can be used in silc_pkcs_unregister
+   to unregister all PKCS at once. */
+#define SILC_ALL_PKCS ((SilcPKCSObject *)1)
+
+/* Static list of PKCS for silc_pkcs_register_default(). */
+extern DLLAPI SilcPKCSObject silc_default_pkcs[];
+
+/* Default PKXS in the SILC protocol */
+#define SILC_DEFAULT_PKCS "rsa"
+
 /* Macros */
 
 /* Macros used to implement the SILC PKCS API */
@@ -79,86 +116,137 @@ extern SilcPKCSObject silc_pkcs_list[];
    would look something like this:
 
    #define SILC_PKCS_API_INIT(pkcs) \
-   inline int silc_##pkcs##_init(void *context, unsigned int keylen, \
+   inline int silc_##pkcs##_init(void *context, uint32 keylen, \
                                  void *p1, void *p2)
 
    Now we wouldn't have to send the SilcRng object since the primes are 
    provided as arguments. To send them as void * they could actually be 
-   used as in anyway for real (MP_INT (SilcInt) or even something else 
+   used as in anyway for real (MP_INT (SilcMPInt) or even something else 
    (the pointer could be kludged to be something else in the module))
    (Plus, the SilcRng object management in prime generation would be
    simpler and better what it is now (in silcprimegen.c, that is)).
 */
 
 #define SILC_PKCS_API_INIT(pkcs) \
-int silc_##pkcs##_init(void *context, unsigned int keylen, \
+int silc_##pkcs##_init(void *context, uint32 keylen, \
                       SilcRng rng)
 #define SILC_PKCS_API_CLEAR_KEYS(pkcs) \
 void silc_##pkcs##_clear_keys(void *context)
 #define SILC_PKCS_API_GET_PUBLIC_KEY(pkcs) \
 unsigned char *silc_##pkcs##_get_public_key(void *context, \
-                                            unsigned int *ret_len)
+                                            uint32 *ret_len)
 #define SILC_PKCS_API_GET_PRIVATE_KEY(pkcs) \
 unsigned char *silc_##pkcs##_get_private_key(void *context, \
-                                             unsigned int *ret_len)
+                                             uint32 *ret_len)
 #define SILC_PKCS_API_SET_PUBLIC_KEY(pkcs) \
-int silc_##pkcs##_set_public_key(void *context, unsigned char *key_data, \
-                                 unsigned int key_len)
+uint32 silc_##pkcs##_set_public_key(void *context, unsigned char *key_data, \
+                                    uint32 key_len)
 #define SILC_PKCS_API_SET_PRIVATE_KEY(pkcs) \
 int silc_##pkcs##_set_private_key(void *context, unsigned char *key_data, \
-                                  unsigned int key_len)
+                                  uint32 key_len)
 #define SILC_PKCS_API_CONTEXT_LEN(pkcs) \
-unsigned int silc_##pkcs##_context_len()
-#define SILC_PKCS_API_DATA_CONTEXT_LEN(pkcs) \
-unsigned int silc_##pkcs##_data_context_len()
-#define SILC_PKCS_API_SET_ARG(pkcs) \
-int silc_##pkcs##_set_arg(void *context, \
-                         void *data_context, \
-                         int argnum, \
-                         SilcInt val)
+uint32 silc_##pkcs##_context_len()
 #define SILC_PKCS_API_ENCRYPT(pkcs) \
 int silc_##pkcs##_encrypt(void *context, \
                          unsigned char *src, \
-                         unsigned int src_len, \
+                         uint32 src_len, \
                          unsigned char *dst, \
-                         unsigned int *dst_len)
+                         uint32 *dst_len)
 #define SILC_PKCS_API_DECRYPT(pkcs) \
 int silc_##pkcs##_decrypt(void *context, \
                          unsigned char *src, \
-                         unsigned int src_len, \
+                         uint32 src_len, \
                          unsigned char *dst, \
-                         unsigned int *dst_len)
+                         uint32 *dst_len)
 #define SILC_PKCS_API_SIGN(pkcs) \
 int silc_##pkcs##_sign(void *context, \
                       unsigned char *src, \
-                      unsigned int src_len, \
+                      uint32 src_len, \
                       unsigned char *dst, \
-                      unsigned int *dst_len)
+                      uint32 *dst_len)
 #define SILC_PKCS_API_VERIFY(pkcs) \
 int silc_##pkcs##_verify(void *context, \
                         unsigned char *signature, \
-                        unsigned int signature_len, \
+                        uint32 signature_len, \
                         unsigned char *data, \
-                        unsigned int data_len)
+                        uint32 data_len)
 
 /* Prototypes */
-int silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs);
+bool silc_pkcs_register(SilcPKCSObject *pkcs);
+bool silc_pkcs_unregister(SilcPKCSObject *pkcs);
+bool silc_pkcs_register_default(void);
+bool silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs);
 void silc_pkcs_free(SilcPKCS pkcs);
 int silc_pkcs_is_supported(const unsigned char *name);
-char *silc_pkcs_get_supported();
-unsigned int silc_pkcs_get_key_len(SilcPKCS self);
-unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, unsigned int *len);
-unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, unsigned int *len);
-int silc_pkcs_set_public_key(SilcPKCS pkcs, unsigned char *pk, 
-                            unsigned int pk_len);
-int silc_pkcs_set_private_key(SilcPKCS pkcs, unsigned char *prv, 
-                             unsigned int prv_len);
-int silc_pkcs_save_public_key(SilcPKCS pkcs, char *filename,
-                             unsigned char *pk, unsigned int pk_len);
-int silc_pkcs_save_private_key(SilcPKCS pkcs, char *filename,
-                              unsigned char *prv, unsigned int prv_len,
-                              char *passphrase);
-int silc_pkcs_load_public_key(char *filename, SilcPKCS *ret_pkcs);
-int silc_pkcs_load_private_key(char *filename, SilcPKCS *ret_pkcs);
+char *silc_pkcs_get_supported(void);
+uint32 silc_pkcs_get_key_len(SilcPKCS self);
+unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, uint32 *len);
+unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, uint32 *len);
+uint32 silc_pkcs_public_key_set(SilcPKCS pkcs, SilcPublicKey public_key);
+uint32 silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk,
+                                    uint32 pk_len);
+int silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key);
+int silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv,
+                                  uint32 prv_len);
+int silc_pkcs_encrypt(SilcPKCS pkcs, unsigned char *src, uint32 src_len,
+                     unsigned char *dst, uint32 *dst_len);
+int silc_pkcs_decrypt(SilcPKCS pkcs, unsigned char *src, uint32 src_len,
+                     unsigned char *dst, uint32 *dst_len);
+int silc_pkcs_sign(SilcPKCS pkcs, unsigned char *src, uint32 src_len,
+                  unsigned char *dst, uint32 *dst_len);
+int silc_pkcs_verify(SilcPKCS pkcs, unsigned char *signature, 
+                    uint32 signature_len, unsigned char *data, 
+                    uint32 data_len);
+int silc_pkcs_sign_with_hash(SilcPKCS pkcs, SilcHash hash,
+                            unsigned char *src, uint32 src_len,
+                            unsigned char *dst, uint32 *dst_len);
+int silc_pkcs_verify_with_hash(SilcPKCS pkcs, SilcHash hash, 
+                              unsigned char *signature, 
+                              uint32 signature_len, 
+                              unsigned char *data, 
+                              uint32 data_len);
+char *silc_pkcs_encode_identifier(char *username, char *host, char *realname,
+                                 char *email, char *org, char *country);
+SilcPublicKeyIdentifier silc_pkcs_decode_identifier(char *identifier);
+void silc_pkcs_free_identifier(SilcPublicKeyIdentifier identifier);
+SilcPublicKey silc_pkcs_public_key_alloc(char *name, char *identifier,
+                                        unsigned char *pk, 
+                                        uint32 pk_len);
+void silc_pkcs_public_key_free(SilcPublicKey public_key);
+SilcPrivateKey silc_pkcs_private_key_alloc(char *name, unsigned char *prv,
+                                          uint32 prv_len);
+void silc_pkcs_private_key_free(SilcPrivateKey private_key);
+unsigned char *
+silc_pkcs_public_key_encode(SilcPublicKey public_key, uint32 *len);
+unsigned char *
+silc_pkcs_public_key_data_encode(unsigned char *pk, uint32 pk_len,
+                                char *pkcs, char *identifier, 
+                                uint32 *len);
+int silc_pkcs_public_key_decode(unsigned char *data, uint32 data_len,
+                               SilcPublicKey *public_key);
+bool silc_pkcs_public_key_compare(SilcPublicKey key1, SilcPublicKey key2);
+unsigned char *
+silc_pkcs_private_key_encode(SilcPrivateKey private_key, uint32 *len);
+unsigned char *
+silc_pkcs_private_key_data_encode(unsigned char *prv, uint32 prv_len,
+                                 char *pkcs, uint32 *len);
+int silc_pkcs_private_key_decode(unsigned char *data, uint32 data_len,
+                                SilcPrivateKey *private_key);
+int silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
+                             uint32 encoding);
+int silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
+                                  uint32 data_len,
+                                  uint32 encoding);
+int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, 
+                              unsigned char *passphrase,
+                              uint32 encoding);
+int silc_pkcs_save_private_key_data(char *filename, unsigned char *data, 
+                                   uint32 data_len,
+                                   unsigned char *passphrase,
+                                   uint32 encoding);
+int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
+                             uint32 encoding);
+int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
+                              uint32 encoding);
 
 #endif
index c7deb1b482f84a7cfd9d29984afbd883dc7695f7..7e103264ab721b9b00685d0d5a5a7d8ed195e75f 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
+/* $Id$ */
 /*
  * Created: Sun Mar  9 00:09:18 1997
  *
- * This RNG is based on Secure Shell's random number generator.
- */
-/* XXX: Some operations block resulting slow initialization.
- * XXX: I have some pending changes to make this better. */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
+ * The original RNG was based on Secure Shell's random number generator
+ * by Tatu Ylönen and was used as reference when programming this RNG.  
+ * This RNG has been rewritten twice since the creation.
  */
 
 #include "silcincludes.h"
 
+#ifndef WIN32
+#ifdef HAVE_GETSID
+extern pid_t getsid (pid_t __pid);
+#endif
+
+#ifdef HAVE_GETPGID
+extern pid_t getpgid (pid_t __pid);
+#endif
+#endif
+
 #undef SILC_RNG_DEBUG
-/* #define SILC_RNG_DEBUG */
+/*#define SILC_RNG_DEBUG*/
+
+/* Number of states to fetch data from pool. */
+#define SILC_RNG_STATE_NUM 4
+
+/* Byte size of the random data pool. */
+#define SILC_RNG_POOLSIZE 1024
+
+static uint32 silc_rng_get_position(SilcRng rng);
+static void silc_rng_stir_pool(SilcRng rng);
+static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos);
+static void silc_rng_exec_command(SilcRng rng, char *command);
+static void silc_rng_get_hard_noise(SilcRng rng);
+static void silc_rng_get_medium_noise(SilcRng rng);
+static void silc_rng_get_soft_noise(SilcRng rng);
 
 /* 
    SILC SilcRng State context.
@@ -47,8 +64,8 @@
    from the same point of the pool. Short description of the fields
    following.
 
-   unsigned int low
-   unsigned int pos
+   uint32 low
+   uint32 pos
 
        The index for the random pool buffer. Lowest and current
        positions.
@@ -60,8 +77,8 @@
 
 */
 typedef struct SilcRngStateContext {
-  unsigned int low;
-  unsigned int pos;
+  uint32 low;
+  uint32 pos;
   struct SilcRngStateContext *next;
 } *SilcRngState;
 
@@ -95,12 +112,21 @@ typedef struct SilcRngStateContext {
        random pool. This is allocated when RNG object is allocated and
        free'd when RNG object is free'd.
 
+   uint8 threshhold
+
+       Threshhold to indicate when it is required to acquire more
+       noise from the environment.  More soft noise is acquired after
+       64 bits of output and hard noise every 160 bits of output.
+
 */
 typedef struct SilcRngObjectStruct {
   unsigned char pool[SILC_RNG_POOLSIZE];
   unsigned char key[64];
   SilcRngState state;
   SilcHash sha1;
+  uint8 threshhold;
+  char *devrandom;
+  int fd_devurandom;
 } SilcRngObject;
 
 /* Allocates new RNG object. */
@@ -112,15 +138,18 @@ SilcRng silc_rng_alloc()
   SILC_LOG_DEBUG(("Allocating new RNG object"));
 
   new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    SILC_LOG_ERROR(("Could not allocate new RNG object"));
-    return NULL;
-  }
+  new->fd_devurandom = -1;
 
   memset(new->pool, 0, sizeof(new->pool));
   memset(new->key, 0, sizeof(new->key));
   new->state = NULL;
-  silc_hash_alloc("sha1", &new->sha1);
+  if (!silc_hash_alloc("sha1", &new->sha1)) {
+    silc_free(new);
+    SILC_LOG_ERROR(("Could not allocate sha1 hash, probably not registered"));
+    return NULL;
+  }
+
+  new->devrandom = strdup("/dev/random");
 
   return new;
 }
@@ -132,7 +161,12 @@ void silc_rng_free(SilcRng rng)
   if (rng) {
     memset(rng->pool, 0, sizeof(rng->pool));
     memset(rng->key, 0, sizeof(rng->key));
-    silc_free(rng->sha1);
+    silc_hash_free(rng->sha1);
+    silc_free(rng->devrandom);
+
+    if (rng->fd_devurandom != -1)
+      close(rng->fd_devurandom);
+
     silc_free(rng);
   }
 }
@@ -151,21 +185,17 @@ void silc_rng_init(SilcRng rng)
   SILC_LOG_DEBUG(("Initializing RNG object"));
 
   /* Initialize the states for the RNG. */
-  rng->state = silc_malloc(sizeof(*rng->state));
+  rng->state = silc_calloc(1, sizeof(*rng->state));
   rng->state->low = 0;
   rng->state->pos = 8;
   rng->state->next = NULL;
   first = rng->state;
   for (i = SILC_RNG_STATE_NUM - 1; i >= 1; i--) {
-    next = silc_malloc(sizeof(*rng->state));
+    next = silc_calloc(1, sizeof(*rng->state));
     next->low = 
       (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM));
     next->pos =
       (i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM)) + 8;
-#if 0
-    next->pos = sizeof(rng->pool) - 
-      ((i * (sizeof(rng->pool) / SILC_RNG_STATE_NUM))) + 8;
-#endif
     next->next = rng->state;
     rng->state = next;
   }
@@ -178,39 +208,71 @@ void silc_rng_init(SilcRng rng)
   silc_rng_get_soft_noise(rng);
   silc_rng_get_medium_noise(rng);
   silc_rng_get_hard_noise(rng);
+  silc_rng_get_soft_noise(rng);
+  silc_free(rng->devrandom);
+  rng->devrandom = strdup("/dev/urandom");
 }
 
 /* This function gets 'soft' noise from environment. */
 
-void silc_rng_get_soft_noise(SilcRng rng)
+static void silc_rng_get_soft_noise(SilcRng rng)
 {
+#ifndef SILC_WIN32
   struct tms ptime;
+#endif
+  uint32 pos;
   
+  pos = silc_rng_get_position(rng);
+
   silc_rng_xor(rng, clock(), 0);
+#ifndef SILC_WIN32
+#ifdef HAVE_GETPID
   silc_rng_xor(rng, getpid(), 1);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 2);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 3);
+#ifdef HAVE_GETPGID
+  silc_rng_xor(rng, getpgid(getpid()) << 8, 2);
+  silc_rng_xor(rng, getpgid(getpid()) << 8, 3);
+#endif
   silc_rng_xor(rng, getgid(), 4);
+#endif
+#ifdef HAVE_GETPGRP
   silc_rng_xor(rng, getpgrp(), 5);
-  silc_rng_xor(rng, getsid(getpid() << 16), 6);
+#endif
+#ifdef HAVE_GETSID
+  silc_rng_xor(rng, getsid(getpid()) << 16, 6);
+#endif
   silc_rng_xor(rng, times(&ptime), 7);
   silc_rng_xor(rng, ptime.tms_utime, 8);
-  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 9);
-  silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), 10);
-  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), 11);
-  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), 12);
-  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), 13);
-  silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), 14);
-  silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), 15);
-  silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), 16);
-  silc_rng_xor(rng, (ptime.tms_stime << 8), 17);
-  silc_rng_xor(rng, clock() << 4, 18);
-  silc_rng_xor(rng, getpgid(getpid() << 8), 19);
-  silc_rng_xor(rng, getpgrp(), 20);
-  silc_rng_xor(rng, getsid(getpid() << 16), 21);
-  silc_rng_xor(rng, times(&ptime), 22);
-  silc_rng_xor(rng, ptime.tms_utime, 23);
-  silc_rng_xor(rng, getpgrp(), 24);
+  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_stime + ptime.tms_cutime), pos++);
+  silc_rng_xor(rng, (ptime.tms_utime + ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_cutime ^ ptime.tms_cstime), pos++);
+  silc_rng_xor(rng, (ptime.tms_utime ^ ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_stime ^ ptime.tms_cutime), pos++);
+  silc_rng_xor(rng, (ptime.tms_cutime + ptime.tms_stime), pos++);
+  silc_rng_xor(rng, (ptime.tms_stime << 8), pos++);
+#endif
+  silc_rng_xor(rng, clock() << 4, pos++);
+#ifndef SILC_WIN32
+#ifdef HAVE_GETPGID
+  silc_rng_xor(rng, getpgid(getpid()) << 8, pos++);
+#endif
+#ifdef HAVE_GETPGRP
+  silc_rng_xor(rng, getpgrp(), pos++);
+#endif
+#ifdef HAVE_SETSID
+  silc_rng_xor(rng, getsid(getpid()) << 16, pos++);
+#endif
+  silc_rng_xor(rng, times(&ptime), pos++);
+  silc_rng_xor(rng, ptime.tms_utime, pos++);
+#ifdef HAVE_GETPGRP
+  silc_rng_xor(rng, getpgrp(), pos++);
+#endif
+#endif
+
+#ifdef SILC_RNG_DEBUG
+  SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
 
   /* Stir random pool */
   silc_rng_stir_pool(rng);
@@ -218,50 +280,57 @@ void silc_rng_get_soft_noise(SilcRng rng)
 
 /* This function gets noise from different commands */
 
-void silc_rng_get_medium_noise(SilcRng rng)
+static void silc_rng_get_medium_noise(SilcRng rng)
 {
-  silc_rng_exec_command(rng, "ps -lefaww 2> /dev/null");
-  silc_rng_exec_command(rng, "ls -afiln 2> /dev/null");
-  silc_rng_exec_command(rng, "ps -asww 2> /dev/null");
+  silc_rng_exec_command(rng, "ps -leaww 2> /dev/null");
+  silc_rng_exec_command(rng, "ls -afiln ~ 2> /dev/null");
   silc_rng_exec_command(rng, "ls -afiln /proc 2> /dev/null");
-  /*
-  silc_rng_exec_command(rng, "ps -ef 2> /dev/null");
-  silc_rng_exec_command(rng, "ls -alin /dev 2> /dev/null");
-  */
+  silc_rng_exec_command(rng, "ps -axww 2> /dev/null");
+
+#ifdef SILC_RNG_DEBUG
+  SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
 }
 
 /* This function gets 'hard' noise from environment. This tries to
    get the noise from /dev/random if available. */
 
-void silc_rng_get_hard_noise(SilcRng rng)
+static void silc_rng_get_hard_noise(SilcRng rng)
 {
-  char buf[32];
+#ifndef SILC_WIN32
+  unsigned char buf[32];
   int fd, len, i;
   
-  /* Get noise from /dev/random if available */
-  fd = open("/dev/random", O_RDONLY);
+  /* Get noise from /dev/[u]random if available */
+  fd = open(rng->devrandom, O_RDONLY);
   if (fd < 0)
     return;
 
   fcntl(fd, F_SETFL, O_NONBLOCK);
 
-  for (i = 0; i < 8; i++) {
+  for (i = 0; i < 2; i++) {
     len = read(fd, buf, sizeof(buf));
     if (len <= 0)
       goto out;
     silc_rng_add_noise(rng, buf, len);
   }
 
+#ifdef SILC_RNG_DEBUG
+  SILC_LOG_HEXDUMP(("pool"), rng->pool, sizeof(rng->pool));
+#endif
+
  out:
   close(fd);
   memset(buf, 0, sizeof(buf));
+#endif
 }
 
 /* Execs command and gets noise from its output */
 
-void silc_rng_exec_command(SilcRng rng, char *command)
+static void silc_rng_exec_command(SilcRng rng, char *command)
 {
-  char buf[2048];
+#ifndef SILC_WIN32
+  unsigned char buf[1024];
   FILE *fd;
   int i;
   int c;
@@ -285,17 +354,17 @@ void silc_rng_exec_command(SilcRng rng, char *command)
   pclose(fd);
   
   /* Add the buffer into random pool */
-  silc_rng_add_noise(rng, buf, strlen(buf));
+  silc_rng_add_noise(rng, buf, i);
   memset(buf, 0, sizeof(buf));
+#endif
 }
 
 /* This function adds the contents of the buffer as noise into random 
    pool. After adding the noise the pool is stirred. */
 
-void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, 
-                       unsigned int len)
+void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len)
 {
-  unsigned int i, pos;
+  uint32 i, pos;
 
   pos = silc_rng_get_position(rng);
 
@@ -312,7 +381,7 @@ void silc_rng_add_noise(SilcRng rng, unsigned char *buffer,
 
 /* XOR's data into the pool */
 
-void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos)
+static void silc_rng_xor(SilcRng rng, uint32 val, unsigned int pos)
 {
   assert(rng != NULL);
   rng->pool[pos] ^= val + val;
@@ -321,13 +390,13 @@ void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos)
 /* This function stirs the random pool by encrypting buffer in CFB 
    (cipher feedback) mode with SHA1 algorithm. */
 
-void silc_rng_stir_pool(SilcRng rng)
+static void silc_rng_stir_pool(SilcRng rng)
 {
   int i;
-  unsigned long iv[5];
+  uint32 iv[5];
 
   /* Get the IV */
-  memcpy(iv, &rng->pool[SILC_RNG_POOLSIZE - 256], sizeof(iv));
+  memcpy(iv, &rng->pool[16], sizeof(iv));
 
   /* First CFB pass */
   for (i = 0; i < SILC_RNG_POOLSIZE; i += 5) {
@@ -358,10 +427,10 @@ void silc_rng_stir_pool(SilcRng rng)
 /* Returns next position where data is fetched from the pool or
    put to the pool. */
 
-unsigned int silc_rng_get_position(SilcRng rng)
+static uint32 silc_rng_get_position(SilcRng rng)
 {
   SilcRngState next;
-  unsigned int pos;
+  uint32 pos;
 
   next = rng->state->next;
 
@@ -370,7 +439,7 @@ unsigned int silc_rng_get_position(SilcRng rng)
     rng->state->pos = rng->state->low;
 
 #ifdef SILC_RNG_DEBUG
-    fprintf(stderr, "state: %p: low: %d, pos: %d\n", 
+    fprintf(stderr, "state: %p: low: %lu, pos: %lu\n", 
            rng->state, rng->state->low, rng->state->pos);
 #endif
 
@@ -379,19 +448,31 @@ unsigned int silc_rng_get_position(SilcRng rng)
   return pos;
 }
 
-/* returns random byte. Every two byte is from pools low or high state. */
+/* Returns random byte. */
 
 unsigned char silc_rng_get_byte(SilcRng rng)
 {
+  rng->threshhold++;
+
+  /* Get more soft noise after 64 bits threshhold */
+  if (rng->threshhold >= 8)
+    silc_rng_get_soft_noise(rng);
+
+  /* Get hard noise after 160 bits threshhold, zero the threshhold. */
+  if (rng->threshhold >= 20) {
+    rng->threshhold = 0;
+    silc_rng_get_hard_noise(rng);
+  }
+
   return rng->pool[silc_rng_get_position(rng)];
 }
 
 /* Returns 16 bit random number */
 
-unsigned short silc_rng_get_rn16(SilcRng rng)
+uint16 silc_rng_get_rn16(SilcRng rng)
 {
   unsigned char rn[2];
-  unsigned short num;
+  uint16 num;
 
   rn[0] = silc_rng_get_byte(rng);
   rn[1] = silc_rng_get_byte(rng);
@@ -402,10 +483,10 @@ unsigned short silc_rng_get_rn16(SilcRng rng)
 
 /* Returns 32 bit random number */
 
-unsigned int silc_rng_get_rn32(SilcRng rng)
+uint32 silc_rng_get_rn32(SilcRng rng)
 {
   unsigned char rn[4];
-  unsigned short num;
+  uint16 num;
 
   rn[0] = silc_rng_get_byte(rng);
   rn[1] = silc_rng_get_byte(rng);
@@ -418,17 +499,122 @@ unsigned int silc_rng_get_rn32(SilcRng rng)
 
 /* Returns random number string. Returned string is in HEX format. */
 
-unsigned char *silc_rng_get_rn_string(SilcRng rng, unsigned int len)
+unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len)
 {
   int i;
   unsigned char *string;
 
   string = silc_calloc((len * 2 + 1), sizeof(unsigned char));
-  if (string == NULL)
-    return NULL;
 
   for (i = 0; i < len; i++)
     sprintf(string + 2 * i, "%02x", silc_rng_get_byte(rng));
 
   return string;
 }
+
+/* Returns random number binary data. */
+
+unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len)
+{
+  int i;
+  unsigned char *data;
+
+  data = silc_calloc(len + 1, sizeof(*data));
+
+  for (i = 0; i < len; i++)
+    data[i] = silc_rng_get_byte(rng);
+
+  return data;
+}
+
+/* Global RNG. This is global RNG that application can initialize so
+   that any part of code anywhere can use RNG without having to allocate
+   new RNG object everytime.  If this is not initialized then these routines
+   will fail.  Note: currently in SILC applications always initialize this. */
+
+SilcRng global_rng = NULL;
+
+/* Initialize global RNG. If `rng' is provided it is set as the global
+   RNG object (it can be allocated by the application for example). */
+
+int silc_rng_global_init(SilcRng rng)
+{
+  if (rng)
+    global_rng = rng;
+  else
+    global_rng = silc_rng_alloc();
+
+  return TRUE;
+}
+
+/* Uninitialize global RNG */
+
+int silc_rng_global_uninit()
+{
+  if (global_rng) {
+    silc_rng_free(global_rng);
+    global_rng = NULL;
+  }
+
+  return TRUE;
+}
+
+/* These are analogous to the functions above. */
+
+unsigned char silc_rng_global_get_byte()
+{
+  return global_rng ? silc_rng_get_byte(global_rng) : 0;
+}
+
+/* Return random byte as fast as possible. Reads from /dev/urandom if
+   available. If not then return from normal RNG (not so fast). */
+
+unsigned char silc_rng_global_get_byte_fast()
+{
+#ifndef SILC_WIN32
+  unsigned char buf[1];
+
+  if (!global_rng)
+    return 0;
+
+  if (global_rng->fd_devurandom == -1) {
+    global_rng->fd_devurandom = open("/dev/urandom", O_RDONLY);
+    if (global_rng < 0)
+      return silc_rng_global_get_byte();
+    fcntl(global_rng->fd_devurandom, F_SETFL, O_NONBLOCK);
+  }
+
+  if (read(global_rng->fd_devurandom, buf, sizeof(buf)) < 0)
+    return silc_rng_global_get_byte();
+
+  return buf[0];
+#else
+  return silc_rng_global_get_byte();
+#endif
+}
+
+uint16 silc_rng_global_get_rn16()
+{
+  return global_rng ? silc_rng_get_rn16(global_rng) : 0;
+}
+
+uint32 silc_rng_global_get_rn32()
+{
+  return global_rng ? silc_rng_get_rn32(global_rng) : 0;
+}
+
+unsigned char *silc_rng_global_get_rn_string(uint32 len)
+{
+  return global_rng ? silc_rng_get_rn_string(global_rng, len) : NULL;
+}
+
+unsigned char *silc_rng_global_get_rn_data(uint32 len)
+{
+  return global_rng ? silc_rng_get_rn_data(global_rng, len) : NULL;
+}
+
+void silc_rng_global_add_noise(unsigned char *buffer, uint32 len)
+{
+  if (global_rng)
+    silc_rng_add_noise(global_rng, buffer, len);
+}
index c35f864974d9a16ca2eba54923204f03df988a33..ef52b03a49f9a31e620dba153ee7208ca34419e3 100644 (file)
 /*
 
-  silcSilcRng.h
-
+  silcrng.h
+  COPYRIGHT
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
+  Copyright (C) 1997 - 2001 Pekka Riikonen
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
-
 */
 
+/****h* silccrypt/SilcRNGAPI
+ *
+ * DESCRIPTION
+ *
+ * SILC Random Number Generator is cryptographically strong pseudo random
+ * number generator. It is used to generate all the random numbers needed
+ * in the SILC sessions. All key material and other sources needing random
+ * numbers use this generator.
+ *
+ * The RNG has a random pool of 1024 bytes of size that provides the actual
+ * random numbers for the application. The pool is initialized when the
+ * RNG is allocated and initialized with silc_rng_alloc and silc_rng_init
+ * functions, respectively. 
+ *
+ *
+ * Random Pool Initialization
+ *
+ * The RNG's random pool is the source of all random output data. The pool is
+ * initialized with silc_rng_init and application can reseed it at any time
+ * by calling the silc_rng_add_noise function.
+ *
+ * The initializing phase attempts to set the random pool in a state that it
+ * is impossible to learn the input data to the RNG or any random output
+ * data. This is achieved by acquiring noise from various system sources. The
+ * first source is called to provide "soft noise". This noise is various
+ * data from system's processes. The second source is called to provide
+ * "medium noise". This noise is various output data from executed commands.
+ * Usually the commands are Unix `ps' and `ls' commands with various options.
+ * The last source is called to provide "hard noise" and is noise from
+ * system's /dev/random, if it exists.
+ *
+ *
+ * Stirring the Random Pool
+ *
+ * Every time data is acquired from any source, the pool is stirred. The
+ * stirring process performs an CFB (cipher feedback) encryption with SHA1
+ * algorithm to the entire random pool. First it acquires an IV (Initial
+ * Vector) from the constant (random) location of the pool and performs
+ * the first CFB pass. Then it acquires a new encryption key from variable
+ * location of the pool and performs the second CFB pass. The encryption
+ * key thus is always acquired from unguessable data.
+ *
+ * The encryption process to the entire random pool assures that it is
+ * impossible to learn the input data to the random pool without breaking the
+ * encryption process. This would effectively mean breaking the SHA1 hash
+ * function. The encryption process also assures that each random output from
+ * the random pool is secured with cryptographically strong function, the
+ * SHA1 in this case.
+ *
+ * The random pool can be restirred by the application at any point by
+ * calling the silc_rng_add_noise function. This function adds new noise to
+ * the pool and then stirs the entire pool.
+ *
+ *
+ * Stirring Threshholds
+ *
+ * The random pool has two threshholds that controls when the random pool
+ * needs more new noise and requires restirring. As previously mentioned, the
+ * application may do this by calling the silc_rng_add_noise. However, the
+ * RNG performs this also automatically.
+ *
+ * The first threshhold gets soft noise from system and stirs the random pool.
+ * The threshhold is reached after 64 bits of random data has been fetched
+ * from the RNG. After the 64 bits, the soft noise acquiring and restirring
+ * process is performed every 8 bits of random output data until the second
+ * threshhold is reached.
+ *
+ * The second threshhold gets hard noise from system and stirs the random
+ * pool. The threshhold is reached after 160 bits of random output. After the
+ * noise is acquired (from /dev/urandom) the random pool is stirred and the
+ * threshholds are set to zero. The process is repeated again after 64 bits of
+ * output for first threshhold and after 160 bits of output for the second
+ * threshhold.
+ *
+ *
+ * Internal State of the Random Pool
+ *
+ * The random pool has also internal state that provides several variable
+ * distinct points to the random pool where the data is fetched. The state
+ * changes every 8 bits of output data and it is guaranteed that the fetched
+ * 8 bits of data is from distinct location compared to the previous 8 bits.
+ * It is also guaranteed that the internal state never wraps before
+ * restirring the entire random pool. The internal state means that the data
+ * is not fetched linearly from the pool, eg. starting from zero and wrapping
+ * at the end of the pool. The internal state is not dependent of any random
+ * data in the pool. The internal states are initialized (by default the pool
+ * is splitted to four different sections (states)) at the RNG
+ * initialization phase. The state's current position is added linearly and
+ * wraps at the the start of the next state. The states provides the distinct
+ * locations.
+ *
+ *
+ * Security Considerations
+ *
+ * The security of this random number generator, like of any other RNG's,
+ * depends of the initial state of the RNG. The initial state of the random
+ * number generators must be unknown to an adversary. This means that after
+ * the RNG is initialized it is required that the input data to the RNG and
+ * the output data to the application has no correlation of any kind that
+ * could be used to compromise the acquired random numbers or any future
+ * random numbers. 
+ *
+ * It is, however, clear that the correlation exists but it needs to be
+ * hard to solve for an adversary. To accomplish this the input data to the
+ * random number generator needs to be secret. Usually this is impossible to
+ * achieve. That is why SILC's RNG acquires the noise from three different
+ * sources and provides for the application an interface to add more noise at
+ * any time. The first source ("soft noise") is known to the adversary but
+ * requires exact timing to get all of the input data. However, getting only
+ * partial data is easy. The second source ("medium noise") depends on the
+ * place of execution of the application. Getting at least partial data is
+ * easy but securing for example the user's home directory from outside access
+ * makes it harder. The last source ("hard noise") is considered to be the
+ * most secure source of data. An adversary is not considered to have any
+ * access on this data. This of course greatly depends on the operating system.
+ *
+ * These three sources are considered to be adequate since the random pool is
+ * relatively large and the output of each bit of the random pool is secured
+ * by cryptographically secure function, the SHA1 in CFB mode encryption.
+ * Furthermore the application may provide other random data, such as random
+ * key strokes or mouse movement to the RNG. However, it is recommended that
+ * the application would not be the single point of source for the RNG, in
+ * either intializing or reseeding phases later in the session. Good solution
+ * is probably to use both, the application's seeds and the RNG's own
+ * sources, equally.
+ *
+ * The RNG must also assure that any old or future random numbers are not
+ * compromised if an adversary would learn the initial input data (or any
+ * input data for that matter). The SILC's RNG provides good protection for
+ * this even if the some of the input bits would be compromised for old or
+ * future random numbers. The RNG reinitalizes (reseeds) itself using the
+ * threshholds after every 64 and 160 bits of output. This is considered to be
+ * adequate even if some of the bits would get compromised. Also, the
+ * applications that use the RNG usually fetches at least 256 bits from the
+ * RNG. This means that everytime RNG is accessed both of the threshholds are
+ * reached. This should mean that the RNG is never too long in an compromised
+ * state and recovers as fast as possible.
+ *
+ * Currently the SILC's RNG does not use random seed files to store some
+ * random data for future initializing. This is important and must be
+ * implemented in the future.
+ *
+ * The caller must be cautios when using this RNG with native WIN32 system.
+ * The RNG most likely is impossible to set in unguessable state just by
+ * using the RNG's input data sources.  On WIN32 it is stronly suggested
+ * that caller would add more random noise after the initialization of the
+ * RNG using the silc_rng_add_noise function.  For example, random mouse
+ * movements may be used.
+ *
+ ***/
+
 #ifndef SILCRNG_H
 #define SILCRNG_H
 
 /* Forward declaration. Actual object is in source file. */
 typedef struct SilcRngObjectStruct *SilcRng;
 
-/* Number of states to fetch data from pool. */
-#define SILC_RNG_STATE_NUM 4
-
-/* Byte size of the random data pool. */
-#define SILC_RNG_POOLSIZE 1024
-
 /* Prototypes */
 SilcRng silc_rng_alloc();
 void silc_rng_free(SilcRng rng);
 void silc_rng_init(SilcRng rng);
-void silc_rng_get_soft_noise(SilcRng rng);
-void silc_rng_get_medium_noise(SilcRng rng);
-void silc_rng_get_hard_noise(SilcRng rng);
-void silc_rng_exec_command(SilcRng rng, char *command);
-void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, 
-                       unsigned int len);
-void silc_rng_xor(SilcRng rng, unsigned int val, unsigned int pos);
-void silc_rng_stir_pool(SilcRng rng);
-unsigned int silc_rng_get_position(SilcRng rng);
 unsigned char silc_rng_get_byte(SilcRng rng);
-unsigned short silc_rng_get_rn16(SilcRng rng);
-unsigned int silc_rng_get_rn32(SilcRng rng);
-unsigned char *silc_rng_get_rn_string(SilcRng rng, unsigned int len);
+uint16 silc_rng_get_rn16(SilcRng rng);
+uint32 silc_rng_get_rn32(SilcRng rng);
+unsigned char *silc_rng_get_rn_string(SilcRng rng, uint32 len);
+unsigned char *silc_rng_get_rn_data(SilcRng rng, uint32 len);
+void silc_rng_add_noise(SilcRng rng, unsigned char *buffer, uint32 len);
+
+int silc_rng_global_init(SilcRng rng);
+int silc_rng_global_uninit();
+unsigned char silc_rng_global_get_byte();
+unsigned char silc_rng_global_get_byte_fast();
+uint16 silc_rng_global_get_rn16();
+uint32 silc_rng_global_get_rn32();
+unsigned char *silc_rng_global_get_rn_string(uint32 len);
+unsigned char *silc_rng_global_get_rn_data(uint32 len);
+void silc_rng_global_add_noise(unsigned char *buffer, uint32 len);
 
 #endif
diff --git a/lib/silccrypt/tests/inst b/lib/silccrypt/tests/inst
deleted file mode 100644 (file)
index 06485bd..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-gcc -I.. \
--I../../../includes -I../../silccore \
--I../.. -I../../silccore -I../../silcmath \
--I../../silcmath/gmp-2.0.2 -I../../silcske -I../../silcsim \
--Wall -finline-functions \
--o test_rsa test_rsa.c -L../.. -lsilc
-
diff --git a/lib/silccrypt/tests/inst_aes b/lib/silccrypt/tests/inst_aes
new file mode 100644 (file)
index 0000000..67855a3
--- /dev/null
@@ -0,0 +1,7 @@
+gcc -I.. \
+-I../../../includes -I../../silccore -I../../trq -I../../silcske \
+-I../.. -I../../silccore -I../../silcmath -I../../silcutil \
+-I../../silcmath/gmp -I../../silcske -I../../silcsim \
+-Wall -finline-functions \
+-o test_aes test_aes.c -L../.. -lsilc
+
diff --git a/lib/silccrypt/tests/inst_rsa b/lib/silccrypt/tests/inst_rsa
deleted file mode 100644 (file)
index 06485bd..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-gcc -I.. \
--I../../../includes -I../../silccore \
--I../.. -I../../silccore -I../../silcmath \
--I../../silcmath/gmp-2.0.2 -I../../silcske -I../../silcsim \
--Wall -finline-functions \
--o test_rsa test_rsa.c -L../.. -lsilc
-
index 9837a5da69b81b7c35ab0818351dc6faea9b70fc..5e035349271832d41b4d1944a913242453bcd51b 100644 (file)
@@ -1,7 +1,7 @@
 gcc -I.. \
--I../../../includes -I../../silccore \
--I../.. -I../../silccore -I../../silcmath \
--I../../silcmath/gmp-2.0.2 -I../../silcske -I../../silcsim \
+-I../../../includes -I../../silccore -I../../trq -I../../silcske \
+-I../.. -I../../silccore -I../../silcmath -I../../silcutil \
+-I../../silcmath/gmp -I../../silcske -I../../silcsim \
 -Wall -finline-functions \
 -o test_twofish test_twofish.c -L../.. -lsilc
 
diff --git a/lib/silccrypt/tests/insth b/lib/silccrypt/tests/insth
deleted file mode 100644 (file)
index 30e225d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-gcc -I../ 
--I../../../includes -I../../silccore \
--I../
--Wall -finline-functions
--o test_rsa test_rsa.c -L../.. -lsilc
-
diff --git a/lib/silccrypt/tests/test_aes.c b/lib/silccrypt/tests/test_aes.c
new file mode 100644 (file)
index 0000000..7f7d81b
--- /dev/null
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "silcincludes.h"
+
+#include "aes.h"
+
+int main()
+{
+       int i;
+       unsigned char key[256];
+       unsigned char plain[256];
+       unsigned char plain2[256];
+       unsigned char cipher[256];
+       unsigned char iv[256];
+       void *context;
+
+       memset(&key, 0, sizeof(key));
+       memset(&plain, 0, sizeof(plain));
+       memset(&plain2, 0, sizeof(plain2));
+       memset(&cipher, 0, sizeof(cipher));
+       memset(&iv, 0, sizeof(iv));
+
+       context = malloc(silc_aes_context_len());
+
+       fprintf(stderr, "\nKey:\n");
+       for (i = 0; i < (sizeof(key) / 2); i += 2) {
+               fprintf(stderr, "%02x%02x ", key[i], key[i+1]);
+       }
+
+       fprintf(stderr, "\nSetting key\n");
+       silc_aes_set_key(context, key, 256);
+
+       fprintf(stderr, "\nPlaintext:\n");
+       for (i = 0; i < (sizeof(plain) / 2); i += 2) {
+               plain[i] = i;
+               plain[i+1] = i+1;
+               fprintf(stderr, "%02x%02x ", plain[i], plain[i+1]);
+       }
+
+       fprintf(stderr, "\n\nEncrypting\n");
+       silc_aes_encrypt_cbc(context, plain, cipher, 256, iv);
+
+       fprintf(stderr, "Ciphertext:\n");
+       for (i = 0; i < (sizeof(cipher)/2); i += 2) {
+               fprintf(stderr, "%02x", cipher[i]);
+               fprintf(stderr, "%02x ", cipher[i+1]);
+       }
+
+       memset(&iv, 0, sizeof(iv));
+
+       fprintf(stderr, "\n\nDecrypting\n");
+       silc_aes_decrypt_cbc(context, cipher, plain2, 256, iv);
+
+       fprintf(stderr, "Decryptedtext:\n");
+       for (i = 0; i < (sizeof(plain2)/2); i += 2) {
+               fprintf(stderr, "%02x", plain2[i]);
+               fprintf(stderr, "%02x ", plain2[i+1]);
+       }
+       fprintf(stderr, "\nDone\n");
+
+       return 0;
+}
diff --git a/lib/silccrypt/tests/test_rijndael.c b/lib/silccrypt/tests/test_rijndael.c
deleted file mode 100644 (file)
index ac07f54..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-
-main()
-{
-       int i, k;
-       unsigned char key[256];
-       unsigned char plain[256];
-       unsigned char plain2[256];
-       unsigned char cipher[256];
-       memset(&key, 0, sizeof(key));
-       memset(&plain, 0, sizeof(plain));
-       memset(&plain2, 0, sizeof(plain2));
-       memset(&cipher, 0, sizeof(cipher));
-
-       fprintf(stderr, "\nKey:\n");
-       for (i = 0; i < sizeof(key) / 2; i++) {
-               key[i] = i;
-               key[i+1] = i+1;
-               fprintf(stderr, "%02x%02x ", key[i], key[i+1]);
-       }
-
-       fprintf(stderr, "\nSetting key\n");
-       set_key(key, 128);
-
-       fprintf(stderr, "\nPlaintext:\n");
-       for (i = 0; i < sizeof(plain) / 2; i++) {
-               plain[i] = i;
-               plain[i+1] = i+1;
-               fprintf(stderr, "%02x%02x ", plain[i], plain[i+1]);
-       }
-
-       fprintf(stderr, "Encrypting\n");
-       encrypt(plain, cipher);
-
-       fprintf(stderr, "\nCiphertext:\n");
-       for (i = 0; i < sizeof(cipher); i++) {
-               fprintf(stderr, "%02x", cipher[i]);
-       }
-
-       fprintf(stderr, "Decrypting\n");
-       decrypt(cipher, plain2);
-
-       fprintf(stderr, "\nDecryptedtext:\n");
-       for (i = 0; i < sizeof(plain2); i++) {
-               fprintf(stderr, "%02x", plain2[i]);
-       }
-
-}
diff --git a/lib/silccrypt/tests/test_rsa.c b/lib/silccrypt/tests/test_rsa.c
deleted file mode 100644 (file)
index d8ff4df..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "silcincludes.h"
-#include "rsa.h"
-#include "rsa_internal.h"
-
-void testi(SilcRng rng, void *context)
-{
-        char *numbuf;
-        unsigned int bytes;
-        unsigned int i;
-        MP_INT tnum;            /* number we'll encrypt */
-        MP_INT test;            /* en/decrypted result of tnum */
-       RsaKey *key = (RsaKey *)context;
-       int bits = 1024;        
-
-        numbuf = (char *)malloc((bits / 3) + 1);
-        bytes = bits / 10;
-            
-        mpz_init(&tnum);
-        mpz_init(&test);
-        
-        fprintf(stderr, "\nTesting encryption and decryption ... ");
-
-        for(i = 0; i < bytes; i++)
-            sprintf(numbuf + 2 * i, "%02x", silc_rng_get_byte(rng));
-        
-        mpz_set_str(&tnum, numbuf, 16);
-
-        /* empty buffer */
-        memset(numbuf, 0, bits / 3);
-        free(numbuf);
-
-        /* make tnum smaller than n */
-        mpz_div_ui(&tnum, &tnum, 10);
-        /* encrypt */
-        rsa_en_de_crypt(&test, &tnum, &key->e, &key->n);
-        /* decrypt */
-        rsa_en_de_crypt(&test, &test, &key->d, &key->n);
-        /* see if decrypted result is same than the original one is */
-        if (mpz_cmp(&test, &tnum) != 0) {
-            fprintf(stderr, "Error in encryption and decryption!\n");
-            return -1;
-        }
-
-        mpz_clear(&tnum);
-        mpz_clear(&test);
-
-        fprintf(stderr, "Keys are Ok.\n");
-}
-
-int main()
-{
-       SilcPKCS pkcs;
-       SilcRng rng;
-       unsigned char *pk, *prv;
-       unsigned int pk_len, prv_len;
-       unsigned char *src, *dst, *new;
-       unsigned int src_len, dst_len, new_len;
-       SilcInt tnum, test;
-
-       silc_pkcs_alloc("rsa", &pkcs);
-
-       rng = silc_rng_alloc();
-       silc_rng_init(rng);
-       silc_math_primegen_init();
-
-       pkcs->pkcs->init(pkcs->context, 1024, rng);
-       
-       pk = silc_pkcs_get_public_key(pkcs, &pk_len);
-       prv = silc_pkcs_get_public_key(pkcs, &prv_len);
-
-       src = "PEKKA RIIKONEN";
-       src_len = 5;
-       dst = silc_calloc(200, sizeof(unsigned char));
-       pkcs->pkcs->encrypt(pkcs->context, src, src_len, dst, &dst_len);
-
-       SILC_LOG_HEXDUMP(("src"), src, src_len);
-       SILC_LOG_HEXDUMP(("dst"), dst, dst_len);
-
-       new = silc_calloc(200, sizeof(unsigned char));
-       pkcs->pkcs->decrypt(pkcs->context, dst, dst_len, new, &new_len);
-
-       SILC_LOG_HEXDUMP(("new"), new, new_len);
-
-       testi(rng, pkcs->context);
-
-       return 0;
-}
index 4467e0e5a7dc9813c6c6bf4c43e1ffd17f61e67a..cb5daec4b30d86fbb6a8dc447af5c2460528372b 100644 (file)
@@ -1,60 +1,62 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include "silcincludes.h"
+
+#include "twofish.h"
 
 int main()
 {
-       int i, k, l;
+       int i;
        unsigned char key[256];
        unsigned char plain[256];
        unsigned char plain2[256];
        unsigned char cipher[256];
+       unsigned char iv[256];
+       void *context;
+
        memset(&key, 0, sizeof(key));
        memset(&plain, 0, sizeof(plain));
        memset(&plain2, 0, sizeof(plain2));
        memset(&cipher, 0, sizeof(cipher));
+       memset(&iv, 0, sizeof(iv));
+
+       context = malloc(silc_twofish_context_len());
 
        fprintf(stderr, "\nKey:\n");
-       for (i = 0; i < (sizeof(plain) / 2); i++) {
-               key[i] = i;
-               key[i+1] = i+1;
+       for (i = 0; i < (sizeof(key) / 2); i += 2) {
                fprintf(stderr, "%02x%02x ", key[i], key[i+1]);
        }
 
        fprintf(stderr, "\nSetting key\n");
-       set_key(key, 128);
+       silc_twofish_set_key(context, key, 256);
 
        fprintf(stderr, "\nPlaintext:\n");
-       for (i = 0; i < (sizeof(plain) / 2); i++) {
+       for (i = 0; i < (sizeof(plain) / 2); i += 2) {
                plain[i] = i;
                plain[i+1] = i+1;
                fprintf(stderr, "%02x%02x ", plain[i], plain[i+1]);
        }
 
        fprintf(stderr, "\n\nEncrypting\n");
+       silc_twofish_encrypt_cbc(context, plain, cipher, 256, iv);
+
        fprintf(stderr, "Ciphertext:\n");
-       l = 0;
-       for (k = 0; k < 8; k++) {
-               encrypt(&plain[l], &cipher[l]);
-               for (i = 0; i < 16; i++) {
-                       fprintf(stderr, "%02x", cipher[l+i]);
-                       fprintf(stderr, "%02x ", cipher[l+i+1]);
-               }
-               l += 16;
+       for (i = 0; i < (sizeof(cipher)/2); i += 2) {
+               fprintf(stderr, "%02x", cipher[i]);
+               fprintf(stderr, "%02x ", cipher[i+1]);
        }
 
+       memset(&iv, 0, sizeof(iv));
+
        fprintf(stderr, "\n\nDecrypting\n");
+       silc_twofish_decrypt_cbc(context, cipher, plain2, 256, iv);
 
        fprintf(stderr, "Decryptedtext:\n");
-       l = 0;
-       for (k = 0; k < 8; k++) {
-               decrypt(&cipher[l], &plain2[l]);
-               for (i = 0; i < 16; i++) {
-                       fprintf(stderr, "%02x", plain2[l+i]);
-                       fprintf(stderr, "%02x ", plain2[l+i+1]);
-               }
-               l += 16;
+       for (i = 0; i < (sizeof(plain2)/2); i += 2) {
+               fprintf(stderr, "%02x", plain2[i]);
+               fprintf(stderr, "%02x ", plain2[i+1]);
        }
-       fprintf(stderr, "\nAll done.\n");
+       fprintf(stderr, "\nDone\n");
 
        return 0;
 }
index 08d3d510aaab371ed459a214cb75add7d4eda4ef..c4cf7c8629d42b6c879f0cbb7e6bed29a095e163 100644 (file)
@@ -40,6 +40,7 @@ Mean:          378 cycles =    67.8 mbits/sec
 */
 
 #include "silcincludes.h"
+#include "twofish_internal.h"
 #include "twofish.h"
 
 /* 
@@ -50,8 +51,12 @@ Mean:          378 cycles =    67.8 mbits/sec
 
 SILC_CIPHER_API_SET_KEY(twofish)
 {
-  twofish_set_key((TwofishContext *)context, (unsigned int *)key, keylen);
-  return 1;
+  uint32 k[8];
+
+  SILC_GET_WORD_KEY(key, k, keylen);
+  twofish_set_key((TwofishContext *)context, k, keylen);
+
+  return TRUE;
 }
 
 /* Sets the string as a new key for the cipher. The string is first
@@ -59,15 +64,7 @@ SILC_CIPHER_API_SET_KEY(twofish)
 
 SILC_CIPHER_API_SET_KEY_WITH_STRING(twofish)
 {
-  /*  unsigned char key[md5_hash_len];
-  SilcMarsContext *ctx = (SilcMarsContext *)context;
-
-  make_md5_hash(string, &key);
-  memcpy(&ctx->key, mars_set_key(&key, keylen), keylen);
-  memset(&key, 'F', sizeoof(key));
-  */
-
-  return 1;
+  return FALSE;
 }
 
 /* Returns the size of the cipher context. */
@@ -82,36 +79,22 @@ SILC_CIPHER_API_CONTEXT_LEN(twofish)
 
 SILC_CIPHER_API_ENCRYPT_CBC(twofish)
 {
-  unsigned int *in, *out, *tiv;
-  unsigned int tmp[4];
+  uint32 tiv[4];
   int i;
 
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
+  SILC_CBC_GET_IV(tiv, iv);
 
-  tmp[0] = in[0] ^ tiv[0];
-  tmp[1] = in[1] ^ tiv[1];
-  tmp[2] = in[2] ^ tiv[2];
-  tmp[3] = in[3] ^ tiv[3];
-  twofish_encrypt((TwofishContext *)context, tmp, out);
-  in += 4;
-  out += 4;
+  SILC_CBC_ENC_PRE(tiv, src);
+  twofish_encrypt((TwofishContext *)context, tiv, tiv);
+  SILC_CBC_ENC_POST(tiv, dst, src);
 
   for (i = 16; i < len; i += 16) {
-    tmp[0] = in[0] ^ out[0 - 4];
-    tmp[1] = in[1] ^ out[1 - 4];
-    tmp[2] = in[2] ^ out[2 - 4];
-    tmp[3] = in[3] ^ out[3 - 4];
-    twofish_encrypt((TwofishContext *)context, tmp, out);
-    in += 4;
-    out += 4;
+    SILC_CBC_ENC_PRE(tiv, src);
+    twofish_encrypt((TwofishContext *)context, tiv, tiv);
+    SILC_CBC_ENC_POST(tiv, dst, src);
   }
 
-  tiv[0] = out[0 - 4];
-  tiv[1] = out[1 - 4];
-  tiv[2] = out[2 - 4];
-  tiv[3] = out[3 - 4];
+  SILC_CBC_PUT_IV(tiv, iv);
 
   return TRUE;
 }
@@ -121,49 +104,23 @@ SILC_CIPHER_API_ENCRYPT_CBC(twofish)
 
 SILC_CIPHER_API_DECRYPT_CBC(twofish)
 {
-  unsigned int *tiv, *in, *out;
-  unsigned int tmp[4], tmp2[4];
+  uint32 tmp[4], tmp2[4], tiv[4];
   int i;
 
-  in = (unsigned int *)src;
-  out = (unsigned int *)dst;
-  tiv = (unsigned int *)iv;
-
-  tmp[0] = in[0];
-  tmp[1] = in[1];
-  tmp[2] = in[2];
-  tmp[3] = in[3];
-  twofish_decrypt((TwofishContext *)context, in, out);
-  out[0] ^= tiv[0];
-  out[1] ^= tiv[1];
-  out[2] ^= tiv[2];
-  out[3] ^= tiv[3];
-  in += 4;
-  out += 4;
+  SILC_CBC_GET_IV(tiv, iv);
+
+  SILC_CBC_DEC_PRE(tmp, src);
+  twofish_decrypt((TwofishContext *)context, tmp, tmp2);
+  SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
 
   for (i = 16; i < len; i += 16) {
-    tmp2[0] = tmp[0];
-    tmp2[1] = tmp[1];
-    tmp2[2] = tmp[2];
-    tmp2[3] = tmp[3];
-    tmp[0] = in[0];
-    tmp[1] = in[1];
-    tmp[2] = in[2];
-    tmp[3] = in[3];
-    twofish_decrypt((TwofishContext *)context, in, out);
-    out[0] ^= tmp2[0];
-    out[1] ^= tmp2[1];
-    out[2] ^= tmp2[2];
-    out[3] ^= tmp2[3];
-    in += 4;
-    out += 4;
+    SILC_CBC_DEC_PRE(tmp, src);
+    twofish_decrypt((TwofishContext *)context, tmp, tmp2);
+    SILC_CBC_DEC_POST(tmp2, dst, src, tmp, tiv);
   }
-
-  tiv[0] = tmp[0];
-  tiv[1] = tmp[1];
-  tiv[2] = tmp[2];
-  tiv[3] = tmp[3];
-
+  
+  SILC_CBC_PUT_IV(tiv, iv);
+  
   return TRUE;
 }
 
@@ -464,27 +421,27 @@ u4byte mds_rem(u4byte p0, u4byte p1)
 
     for(i = 0; i < 8; ++i)
     {
-        t = p1 >> 24;   // get most significant coefficient
+        t = p1 >> 24;   /* get most significant coefficient */
         
-        p1 = (p1 << 8) | (p0 >> 24); p0 <<= 8;  // shift others up
+        p1 = (p1 << 8) | (p0 >> 24); p0 <<= 8;  /* shift others up */
             
-        // multiply t by a (the primitive element - i.e. left shift)
+        /* multiply t by a (the primitive element - i.e. left shift) */
 
         u = (t << 1); 
         
-        if(t & 0x80)            // subtract modular polynomial on overflow
+        if(t & 0x80)            /* subtract modular polynomial on overflow */
         
             u ^= G_MOD; 
 
-        p1 ^= t ^ (u << 16);    // remove t * (a * x^2 + 1)  
+        p1 ^= t ^ (u << 16);    /* remove t * (a * x^2 + 1) */
 
-        u ^= (t >> 1);          // form u = a * t + t / a = t * (a + 1 / a); 
+        u ^= (t >> 1);          /* form u = a * t + t / a = t * (a + 1 / a); */
         
-        if(t & 0x01)            // add the modular polynomial on underflow
+        if(t & 0x01)            /* add the modular polynomial on underflow */
         
             u ^= G_MOD >> 1;
 
-        p1 ^= (u << 24) | (u << 8); // remove t * (a + 1/a) * (x^3 + x)
+        p1 ^= (u << 24) | (u << 8); /* remove t * (a + 1/a) * (x^3 + x) */
     }
 
     return p1;
index 32e35dfecaee570edeb170cf31a489772d5591aa..6b96b302b388f7863702ce5bac14e29e72401521 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
 
 #ifndef TWOFISH_H
 #define TWOFISH_H
 
-#include "twofish_internal.h"
-
 /* 
  * SILC Crypto API for Twofish
  */
diff --git a/lib/silcmath/DIRECTORY b/lib/silcmath/DIRECTORY
new file mode 100644 (file)
index 0000000..04056a3
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+@LIBRARY=SILC Math Library
+@FILENAME=silcmathlib.html
+@LINK=silcmath.html:SILC Math API
+@LINK=silcmp.html:SILC MP API
+-->
+
+<BIG><B>SILC Math Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC Math Library provides arbitrary precision artichmetic routines for
+public key cryptosystems, prime number generation routines, and other
+math utility functions for applications.
+
+<BR /><BR />
+@LINKS@
index 436ad72d221e5788591da4ddca6888965b9c63e3..d1e75ab6d8af9347233e767a305880d34cd064d5 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-SUBDIRS = gmp-3.0.1
+if SILC_MP_NSS_MPI
+SUBDIRS = mpi
+else
+SUBDIRS =
+endif
 
 noinst_LIBRARIES = libsilcmath.a
 
+if SILC_MP_NSS_MPI
+MP_SOURCE = mp_mpi.c
+else
+MP_SOURCE = mp_gmp.c
+endif
+
 libsilcmath_a_SOURCES = \
        silcprimegen.c \
-       modinv.c
+       modinv.c \
+       mpbin.c \
+       $(MP_SOURCE)
+
+if SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       mp_gmp.h        \
+       mp_mpi.h        \
+       silcmath.h      \
+       silcmp.h
+endif
 
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../silccrypt -I../silccore -I../silcske \
-       -I../silcsim -I../.. -I../../includes \
-       -I./gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
index 1a60f7b32380f53a5608826031fb175edd0435ab..7863923e4bf7dc76c88163d91812bd638255a3fd 100644 (file)
   GNU General Public License for more details.
 
 */
+/* $Id$ */
 
 #include "silcincludes.h"
 
 /* Table for finding multiplicative inverse */
 typedef struct {
-  SilcInt x;
+  SilcMPInt x;
 } ModInv;
 
 #define plus1  (i == 2 ? 0 : i + 1)
@@ -46,11 +47,11 @@ typedef struct {
    not needed by the algorithm so it does not have to be included.)
 */
 
-void silc_mp_modinv(SilcInt *inv, SilcInt *a, SilcInt *n)
+void silc_mp_modinv(SilcMPInt *inv, SilcMPInt *a, SilcMPInt *n)
 {
   int i;
-  SilcInt y;
-  SilcInt x;
+  SilcMPInt y;
+  SilcMPInt x;
   
   ModInv g[3];
   ModInv v[3];
@@ -58,11 +59,15 @@ void silc_mp_modinv(SilcInt *inv, SilcInt *a, SilcInt *n)
   /* init MP vars */
   silc_mp_init(&y);
   silc_mp_init(&x);
-  silc_mp_init_set_ui(&v[0].x, 0L);            /* v(0) = 0 */
-  silc_mp_init_set_ui(&v[1].x, 1L);            /* v(1) = 1 */
+  silc_mp_init(&v[0].x);
+  silc_mp_init(&v[1].x);
+  silc_mp_set_ui(&v[0].x, 0L);         /* v(0) = 0 */
+  silc_mp_set_ui(&v[1].x, 1L);         /* v(1) = 1 */
   silc_mp_init(&v[2].x);
-  silc_mp_init_set(&g[0].x, n);                        /* g(0) = n */
-  silc_mp_init_set(&g[1].x, a);                        /* g(1) = a */
+  silc_mp_init(&g[0].x);
+  silc_mp_init(&g[1].x);
+  silc_mp_set(&g[0].x, n);                     /* g(0) = n */
+  silc_mp_set(&g[1].x, a);             /* g(1) = a */
   silc_mp_init(&g[2].x);
   
   i = 1;
@@ -85,12 +90,12 @@ void silc_mp_modinv(SilcInt *inv, SilcInt *a, SilcInt *n)
   /* clear the vars */
   memset(&g, 0, sizeof(g));
   memset(&v, 0, sizeof(v));
-  silc_mp_clear(&y);
-  silc_mp_clear(&x);
-  silc_mp_clear(&g[0].x);
-  silc_mp_clear(&g[1].x);
-  silc_mp_clear(&g[2].x);
-  silc_mp_clear(&v[0].x);
-  silc_mp_clear(&v[1].x);
-  silc_mp_clear(&v[2].x);
+  silc_mp_uninit(&y);
+  silc_mp_uninit(&x);
+  silc_mp_uninit(&g[0].x);
+  silc_mp_uninit(&g[1].x);
+  silc_mp_uninit(&g[2].x);
+  silc_mp_uninit(&v[0].x);
+  silc_mp_uninit(&v[1].x);
+  silc_mp_uninit(&v[2].x);
 }
diff --git a/lib/silcmath/mp_gmp.c b/lib/silcmath/mp_gmp.c
new file mode 100644 (file)
index 0000000..d4f62c3
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+
+  mp_gmp.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include <gmp.h>
+
+void silc_mp_init(SilcMPInt *mp)
+{
+  mpz_init(mp);
+}
+
+void silc_mp_uninit(SilcMPInt *mp)
+{
+  mpz_clear(mp);
+}
+
+size_t silc_mp_size(SilcMPInt *mp)
+{
+  return mpz_size(mp);
+}
+
+size_t silc_mp_sizeinbase(SilcMPInt *mp, int base)
+{
+  return mpz_sizeinbase(mp, base);
+}
+
+void silc_mp_set(SilcMPInt *dst, SilcMPInt *src)
+{
+  mpz_set(dst, src);
+}
+
+void silc_mp_set_ui(SilcMPInt *dst, uint32 ui)
+{
+  mpz_set_ui(dst, ui);
+}
+
+void silc_mp_set_si(SilcMPInt *dst, int32 si)
+{
+  mpz_set_si(dst, si);
+}
+
+void silc_mp_set_str(SilcMPInt *dst, const char *str, int base)
+{
+  mpz_set_str(dst, str, base);
+}
+
+uint32 silc_mp_get_ui(SilcMPInt *mp)
+{
+  return (uint32)mpz_get_ui(mp);
+}
+
+char *silc_mp_get_str(char *str, SilcMPInt *mp, int base)
+{
+  return mpz_get_str(str, base, mp);
+}
+
+void silc_mp_add(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_add(dst, mp1, mp2);
+}
+
+void silc_mp_add_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mpz_add_ui(dst, mp1, ui);
+}
+
+void silc_mp_sub(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_sub(dst, mp1, mp2);
+}
+
+void silc_mp_sub_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mpz_sub_ui(dst, mp1, ui);
+}
+
+void silc_mp_mul(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_mul(dst, mp1, mp2);
+}
+
+void silc_mp_mul_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mpz_mul_ui(dst, mp1, ui);
+}
+
+void silc_mp_mul_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp)
+{
+  mpz_mul_2exp(dst, mp1, exp);
+}
+
+void silc_mp_sqrt(SilcMPInt *dst, SilcMPInt *src)
+{
+  mpz_sqrt(dst, src);
+}
+
+void silc_mp_div(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_div(dst, mp1, mp2);
+}
+
+void silc_mp_div_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mpz_div_ui(dst, mp1, ui);
+}
+
+void silc_mp_div_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+                   SilcMPInt *mp2)
+{
+  if (q && r)
+    mpz_fdiv_qr(q, r, mp1, mp2);
+  if (q && !r)
+    mpz_div(q, mp1, mp2);
+  if (!q && r)
+    mpz_mod(r, mp1, mp2);
+}
+
+void silc_mp_div_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp)
+{
+  mpz_fdiv_q_2exp(dst, mp1, exp);
+}
+
+void silc_mp_div_2exp_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+                        uint32 exp)
+{
+  if (q)
+    mpz_fdiv_q_2exp(q, mp1, exp);
+  if (r)
+    mpz_fdiv_r_2exp(r, mp1, exp);
+}
+
+void silc_mp_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_mod(dst, mp1, mp2);
+}
+
+void silc_mp_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mpz_mod_ui(dst, mp1, ui);
+}
+
+void silc_mp_mod_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mpz_mod_2exp(dst, mp1, ui);
+}
+
+void silc_mp_pow(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp)
+{
+  uint32 uiexp = mpz_get_ui(exp);
+  mpz_pow_ui(dst, mp1, uiexp);
+}
+
+void silc_mp_pow_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp)
+{
+  mpz_pow_ui(dst, mp1, exp);
+}
+
+void silc_mp_pow_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp, 
+                    SilcMPInt *mod)
+{
+  mpz_powm(dst, mp1, exp, mod);
+}
+
+void silc_mp_pow_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp, 
+                       SilcMPInt *mod)
+{
+  mpz_powm_ui(dst, mp1, exp, mod);
+}
+
+void silc_mp_gcd(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_gcd(dst, mp1, mp2);
+}
+
+void silc_mp_gcdext(SilcMPInt *g, SilcMPInt *s, SilcMPInt *t, SilcMPInt *mp1,
+                   SilcMPInt *mp2)
+{
+  mpz_gcdext(g, s, t, mp1, mp2);
+}
+
+int silc_mp_cmp(SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  return mpz_cmp(mp1, mp2);
+}
+
+int silc_mp_cmp_si(SilcMPInt *mp1, int32 si)
+{
+  return mpz_cmp_si(mp1, si);
+}
+
+int silc_mp_cmp_ui(SilcMPInt *mp1, uint32 ui)
+{
+  return mpz_cmp_ui(mp1, ui);
+}
+
+void silc_mp_abs(SilcMPInt *dst, SilcMPInt *src)
+{
+  mpz_abs(dst, src);
+}
+
+void silc_mp_neg(SilcMPInt *dst, SilcMPInt *src)
+{
+  mpz_neg(dst, src);
+}
+
+void silc_mp_and(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_and(dst, mp1, mp2);
+}
+
+void silc_mp_or(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_ior(dst, mp1, mp2);
+}
+
+void silc_mp_xor(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpz_xor(dst, mp1, mp2);
+}
similarity index 79%
rename from lib/silcmath/modinv.h
rename to lib/silcmath/mp_gmp.h
index 4a8cfeb6dce4a345c1da98e88df67ad0bd2c053e..14ae512573af1f79435c9683f24b04eedb7f994c 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  modinv.h
-  
+  mp_gmp.h
+
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
-#ifndef MODINV_H
-#define MODINV_H
+#ifndef MP_GMP_H
+#define MP_GMP_H
+
+#include <gmp.h>
 
-void silc_mp_modinv(SilcInt *inv, SilcInt *a, SilcInt *n);
+#define SILC_MP_INT MP_INT
 
 #endif
diff --git a/lib/silcmath/mp_mpi.c b/lib/silcmath/mp_mpi.c
new file mode 100644 (file)
index 0000000..513f559
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+
+  mp_mpi.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "mpi.h"
+#include "mplogic.h"
+
+void silc_mp_init(SilcMPInt *mp)
+{
+  (void)mp_init(mp);
+}
+
+void silc_mp_uninit(SilcMPInt *mp)
+{
+  (void)mp_clear(mp);
+}
+
+size_t silc_mp_size(SilcMPInt *mp)
+{
+  return mp_raw_size(mp);
+}
+
+size_t silc_mp_sizeinbase(SilcMPInt *mp, int base)
+{
+  size_t sib = mp_radix_size(mp, base);
+  if (sib > 2)
+    sib -= 2;                  /* XXX This is actually wrong since
+                                  this might produce wrong balue.
+                                  But, it looks like MPI always returns
+                                  correct value plus one, whereas
+                                  GMP returns always the right value. */
+  return sib;
+}
+
+void silc_mp_set(SilcMPInt *dst, SilcMPInt *src)
+{
+  (void)mp_copy(src, dst);
+}
+
+void silc_mp_set_ui(SilcMPInt *dst, uint32 ui)
+{
+  mp_set(dst, ui);
+}
+
+void silc_mp_set_si(SilcMPInt *dst, int32 si)
+{
+  (void)mp_set_int(dst, si);
+}
+
+void silc_mp_set_str(SilcMPInt *dst, const char *str, int base)
+{
+  (void)mp_read_variable_radix(dst, str, base);
+}
+
+uint32 silc_mp_get_ui(SilcMPInt *mp)
+{
+  return (uint32)MP_DIGIT(mp, 0);
+}
+
+char *silc_mp_get_str(char *str, SilcMPInt *mp, int base)
+{
+  if (mp_toradix(mp, str, base) != MP_OKAY)
+    return NULL;
+  return str;
+}
+
+void silc_mp_add(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  (void)mp_add(mp1, mp2, dst);
+}
+
+void silc_mp_add_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mp_add_d(mp1, ui, dst);
+}
+
+void silc_mp_sub(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  (void)mp_sub(mp1, mp2, dst);
+}
+
+void silc_mp_sub_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  (void)mp_sub_d(mp1, (mp_digit)ui, dst);
+}
+
+void silc_mp_mul(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  (void)mp_mul(mp1, mp2, dst);
+}
+
+void silc_mp_mul_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  (void)mp_mul_d(mp1, (mp_digit)ui, dst);
+}
+
+void silc_mp_mul_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp)
+{
+  SilcMPInt tmp;
+  silc_mp_init(&tmp);
+  (void)mp_2expt(&tmp, (mp_digit)exp);
+  (void)mp_mul(mp1, &tmp, dst);
+  silc_mp_uninit(&tmp);
+}
+
+void silc_mp_sqrt(SilcMPInt *dst, SilcMPInt *src)
+{
+  (void)mp_sqrt(src, dst);
+}
+
+void silc_mp_div(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  (void)mp_div(mp1, mp2, dst, NULL);
+}
+
+void silc_mp_div_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  (void)mp_div_d(mp1, (mp_digit)ui, dst, NULL);
+}
+
+void silc_mp_div_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+                   SilcMPInt *mp2)
+{
+  (void)mp_div(mp1, mp2, q, r);
+}
+
+void silc_mp_div_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp)
+{
+  SilcMPInt tmp;
+  silc_mp_init(&tmp);
+  (void)mp_2expt(&tmp, (mp_digit)exp);
+  (void)mp_div(mp1, &tmp, dst, NULL);
+  silc_mp_uninit(&tmp);
+}
+
+void silc_mp_div_2exp_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+                        uint32 exp)
+{
+  if (q) {
+    (void)mp_2expt(q, (mp_digit)exp);
+    (void)mp_div(mp1, q, q, r);
+  }
+}
+
+void silc_mp_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  (void)mp_mod(mp1, mp2, dst);
+}
+
+void silc_mp_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  mp_digit uidst;
+  (void)mp_mod_d(mp1, (mp_digit)ui, &uidst);
+  mp_set(dst, uidst);
+}
+
+void silc_mp_mod_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui)
+{
+  SilcMPInt tmp;
+  silc_mp_init(&tmp);
+  (void)mp_2expt(&tmp, (mp_digit)ui);
+  (void)mp_mod(mp1, &tmp, dst);
+  silc_mp_uninit(&tmp);
+}
+
+void silc_mp_pow(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp)
+{
+  (void)mp_expt(mp1, exp, dst);
+}
+
+void silc_mp_pow_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp)
+{
+  (void)mp_expt_d(mp1, (mp_digit)exp, dst);
+}
+
+void silc_mp_pow_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp, 
+                    SilcMPInt *mod)
+{
+  (void)mp_exptmod(mp1, exp, mod, dst);
+}
+
+void silc_mp_pow_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp, 
+                       SilcMPInt *mod)
+{
+  (void)mp_exptmod_d(mp1, (mp_digit)exp, mod, dst);
+}
+
+void silc_mp_gcd(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  (void)mp_gcd(mp1, mp2, dst);
+}
+
+void silc_mp_gcdext(SilcMPInt *g, SilcMPInt *s, SilcMPInt *t, SilcMPInt *mp1,
+                   SilcMPInt *mp2)
+{
+  (void)mp_xgcd(mp1, mp2, g, s, t);
+}
+
+int silc_mp_cmp(SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  return mp_cmp(mp1, mp2);
+}
+
+int silc_mp_cmp_si(SilcMPInt *mp1, int32 si)
+{
+  return mp_cmp_int(mp1, (long)si);
+}
+
+int silc_mp_cmp_ui(SilcMPInt *mp1, uint32 ui)
+{
+  return mp_cmp_d(mp1, ui);
+}
+
+void silc_mp_abs(SilcMPInt *dst, SilcMPInt *src)
+{
+  mp_abs(src, dst);
+}
+
+void silc_mp_neg(SilcMPInt *dst, SilcMPInt *src)
+{
+  mp_neg(src, dst);
+}
+
+void silc_mp_and(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpl_and(mp1, mp2, dst);
+}
+
+void silc_mp_or(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpl_or(mp1, mp2, dst);
+}
+
+void silc_mp_xor(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2)
+{
+  mpl_xor(mp1, mp2, dst);
+}
similarity index 66%
rename from lib/silcmath/silcprimegen.h
rename to lib/silcmath/mp_mpi.h
index d34ba67fa19fb6b617cbf403ff280bfeac7f6a31..54614a30bb9661d3480c59d2a814215ab5ac4f7a 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcprimegen.h
-  
+  mp_mpi.h
+
   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
-#ifndef SILCPRIMEGEN_H
-#define SILCPRIMEGEN_H
+#ifndef MP_MPI_H
+#define MP_MPI_H
+
+#include "mpi.h"
+#include "mplogic.h"
 
-int silc_math_gen_prime(SilcInt *prime, unsigned int bits, int verbose);
-int silc_math_prime_test(SilcInt *p);
-void silc_math_primegen_init();
-void silc_math_primegen_uninit();
+#define SILC_MP_INT mp_int
 
 #endif
diff --git a/lib/silcmath/mpbin.c b/lib/silcmath/mpbin.c
new file mode 100644 (file)
index 0000000..8074f2b
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+
+  mpbin.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2000 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Encodes MP integer into binary data. Returns allocated data that
+   must be free'd by the caller. If `len' is provided the destination
+   buffer is allocated that large. If zero then the size is approximated. */
+
+unsigned char *silc_mp_mp2bin(SilcMPInt *val, uint32 len,
+                             uint32 *ret_len)
+{
+  int i;
+  uint32 size;
+  unsigned char *ret;
+  SilcMPInt tmp;
+
+  size = (len ? len : ((silc_mp_sizeinbase(val, 2) + 7) / 8));
+  ret = silc_calloc(size, sizeof(*ret));
+  
+  silc_mp_init(&tmp);
+  silc_mp_set(&tmp, val);
+
+  for (i = size; i > 0; i--) {
+    ret[i - 1] = (unsigned char)(silc_mp_get_ui(&tmp) & 0xff);
+    silc_mp_div_2exp(&tmp, &tmp, 8);
+  }
+
+  silc_mp_uninit(&tmp);
+
+  if (ret_len)
+    *ret_len = size;
+
+  return ret;
+}
+
+/* Samve as above but does not allocate any memory.  The encoded data is
+   returned into `dst' and it's length to the `ret_len'. */
+
+void silc_mp_mp2bin_noalloc(SilcMPInt *val, unsigned char *dst,
+                           uint32 dst_len)
+{
+  int i;
+  uint32 size = dst_len;
+  SilcMPInt tmp;
+
+  silc_mp_init(&tmp);
+  silc_mp_set(&tmp, val);
+
+  for (i = size; i > 0; i--) {
+    dst[i - 1] = (unsigned char)(silc_mp_get_ui(&tmp) & 0xff);
+    silc_mp_div_2exp(&tmp, &tmp, 8);
+  }
+
+  silc_mp_uninit(&tmp);
+}
+
+/* Decodes binary data into MP integer. The integer sent as argument
+   must be initialized. */
+
+void silc_mp_bin2mp(unsigned char *data, uint32 len, SilcMPInt *ret)
+{
+  int i;
+
+  silc_mp_set_ui(ret, 0);
+
+  for (i = 0; i < len; i++) {
+    silc_mp_mul_2exp(ret, ret, 8);
+    silc_mp_add_ui(ret, ret, data[i]);
+  }
+}
diff --git a/lib/silcmath/silcmath.h b/lib/silcmath/silcmath.h
new file mode 100644 (file)
index 0000000..9096d4b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+
+  silcmath.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcmath/SilcMathAPI
+ *
+ * DESCRIPTION
+ *
+ * SILC Math interface includes various utility functions such as
+ * prime generation, and conversion routines. See the silcmp.h for the
+ * SILC MP interface.
+ *
+ ***/
+
+#ifndef SILCMATH_H
+#define SILCMATH_H
+
+/****f* silcmath/SilcMathAPI/silc_math_gen_prime
+ *
+ * SYNOPSIS
+ *
+ *    int silc_math_gen_prime(SilcMPInt *prime, uint32 bits, bool verbose);
+ *
+ * DESCRIPTION
+ *
+ *    Find appropriate prime. It generates a number by taking random bytes. 
+ *    It then tests the number that it's not divisible by any of the small 
+ *    primes and then it performs Fermat's prime test. I thank Rieks Joosten 
+ *    (r.joosten@pijnenburg.nl) for such a good help with prime tests. 
+ *
+ *    If argument verbose is TRUE this will display some status information
+ *    about the progress of generation.
+ *
+ ***/
+bool silc_math_gen_prime(SilcMPInt *prime, uint32 bits, bool verbose);
+
+/****f* silcmath/SilcMathAPI/silc_math_prime_test
+ *
+ * SYNOPSIS
+ *
+ *    int silc_math_prime_test(SilcMPInt *p);
+ *
+ * DESCRIPTION
+ *
+ *    Performs primality testings for given number. Returns TRUE if the 
+ *    number is probably a prime.
+ *
+ ***/
+bool silc_math_prime_test(SilcMPInt *p);
+
+#endif
index 0b64218fdeddc0801ece3ce8194ac800926e3317..2fb53dec6ef09b23edb0a5a1a05d5802e65f1e90 100644 (file)
 /*
 
   silcmp.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
-
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
-
 */
 
+/****h* silcmath/SilcMPAPI
+ *
+ * DESCRIPTION
+ *
+ * SILC MP Library Interface. This interface defines the arbitrary
+ * precision arithmetic routines for SILC. Currently the actual routines
+ * are implemented separately, usually by some other MP library. The
+ * interface is generic but is mainly intended for crypto usage. This
+ * interface is used by SILC routines that needs big numbers, such as
+ * RSA implementation, Diffie-Hellman implementation etc.
+ *
+ ***/
+
 #ifndef SILCMP_H
 #define SILCMP_H
 
-#include "gmp.h"
-
-/* SILC MP library definitions. We use GNU MP library as default
-   MP library. However, to make possible future changes easier (SILC 
-   might have its own MP library in the future) we implement our own 
-   MP API with simple macros. */ 
-
-typedef MP_INT SilcInt;
-#define silc_mp_abs(a, b) mpz_abs((a), (b))
-#define silc_mp_add(a, b, c) mpz_add((a), (b), (c))
-#define silc_mp_add_ui(a, b, c) mpz_add_ui((a), (b), (c))
-#define silc_mp_and(a, b, c) mpz_and((a), (b), (c))
-#define silc_mp_cdiv_q(a, b, c) mpz_cdiv_q((a), (b), (c))
-#define silc_mp_cdiv_q_ui(a, b, c) mpz_cdiv_q_ui((a), (b), (c))
-#define silc_mp_cdiv_r(a, b, c) mpz_cdiv_r((a), (b), (c))
-#define silc_mp_cdiv_r_ui(a, b, c) mpz_cdiv_r_ui((a), (b), (c))
-#define silc_mp_cdiv_ui(a, b) mpz_cdiv_ui((a), (b))
-#define silc_mp_clear(a) mpz_clear((a))
-#define silc_mp_clrbit(a, b) mpz_clrbit((a), (b))
-#define silc_mp_cmp(a, b) mpz_cmp((a), (b))
-#define silc_mp_cmp_si(a, b) mpz_cmp_si((a), (b))
-#define silc_mp_cmp_ui(a, b) mpz_cmp_ui((a), (b))
-#define silc_mp_com(a, b) mpz_com((a), (b))
-#define silc_mp_divexact(a, b, c) mpz_divexact((a), (b), (c))
-#define silc_mp_div(a, b, c) mpz_div((a), (b), (c))
-#define silc_mp_div_ui(a, b, c) mpz_div_ui((a), (b), (c))
-#define silc_mp_fdiv_ui(a, b) mpz_fdiv_ui((a), (b))
-#define silc_mp_fdiv_q(a, b, c) mpz_fdiv_q((a), (b), (c))
-#define silc_mp_fdiv_q_2exp(a, b, c) mpz_fdiv_q_2exp((a), (b), (c))
-#define silc_mp_fdiv_q_ui(a, b, c) mpz_fdiv_q_ui((a), (b), (c))
-#define silc_mp_fdiv_qr(a, b, c, d) mpz_fdiv_qr((a), (b), (c), (d))
-#define silc_mp_fdiv_qr_ui(a, b, c, d) mpz_fdiv_qr_ui((a), (b), (c), (d))
-#define silc_mp_fdiv_r(a, b, c) mpz_fdiv_r((a), (b), (c))
-#define silc_mp_fdiv_r_2exp(a, b, c) mpz_fdiv_r_2exp((a), (b), (c))
-#define silc_mp_fdiv_r_ui(a, b, c) mpz_fdiv_r_ui((a), (b), (c))
-#define silc_mp_fdiv_ui(a, b) mpz_fdiv_ui((a), (b))
-#define silc_mp_gcd(a, b, c) mpz_gcd((a), (b), (c))
-#define silc_mp_gcd_ui(a, b, c) mpz_gcd_ui((a), (b), (c))
-#define silc_mp_gcdext(a, b, c, d, e) mpz_gcdext((a), (b), (c), (d), (e))
-#define silc_mp_get_ui(a) mpz_get_ui((a))
-#define silc_mp_init(a) mpz_init((a))
-#define silc_mp_init_set(a, b) mpz_init_set((a), (b))
-#define silc_mp_init_set_d(a, b) mpz_init_set_d((a), (b))
-#define silc_mp_init_set_si(a, b) mpz_init_set_si((a), (b))
-#define silc_mp_init_set_str(a, b, c) mpz_init_set_str((a), (b), (c))
-#define silc_mp_init_set_ui(a, b) mpz_init_set_ui((a), (b))
-#define silc_mp_invert(a, b, c) mpz_invert((a), (b), (c))
-#define silc_mp_ior(a, b, c) mpz_ior((a), (b), (c))
-#define silc_mp_mod(a, b, c) mpz_mod((a), (b), (c))
-#define silc_mp_mod_2exp(a, b, c) mpz_mod_2exp((a), (b), (c))
-#define silc_mp_mod_ui(a, b, c) mpz_mod_ui((a), (b), (c))
-#define silc_mp_mul(a, b, c) mpz_mul((a), (b), (c))
-#define silc_mp_mul_2exp(a, b, c) mpz_mul_2exp((a), (b), (c))
-#define silc_mp_mul_ui(a, b, c) mpz_mul_ui((a), (b), (c))
-#define silc_mp_neg(a, b) mpz_neg((a), (b))
-#define silc_mp_pow_ui(a, b, c) mpz_pow_ui((a), (b), (c))
-#define silc_mp_powm(a, b, c, d) mpz_powm((a), (b), (c), (d))
-#define silc_mp_powm_ui(a, b, c, d) mpz_powm_ui((a), (b), (c), (d))
-#define silc_mp_probab_prime_p(a, b) mpz_probab_prime_p((a), (b))
-#define silc_mp_set(a, b) mpz_set((a), (b))
-#define silc_mp_set_d(a, b) mpz_set_d((a), (b))
-#define silc_mp_set_f(a, b) mpz_set_f((a), (b))
-#define silc_mp_set_q(a, b) mpz_set_q((a), (b))
-#define silc_mp_set_si(a, b) mpz_set_si((a), (b))
-#define silc_mp_set_str(a, b, c) mpz_set_str((a), (b), (c))
-#define silc_mp_set_ui(a, b) mpz_set_ui((a), (b))
-#define silc_mp_setbit(a, b) mpz_setbit((a), (b))
-#define silc_mp_size(a) mpz_size((a))
-#define silc_mp_sizeinbase(a, b) mpz_sizeinbase((a), (b))
-#define silc_mp_sqrt(a, b) mpz_sqrt((a), (b))
-#define silc_mp_sqrtrem(a, b, c) mpz_sqrtrem((a), (b), (c))
-#define silc_mp_sub(a, b, c) mpz_sub((a), (b), (c))
-#define silc_mp_sub_ui(a, b, c) mpz_sub_ui((a), (b), (c))
-#define silc_mp_tdiv_ui(a, b) mpz_tdiv_ui((a), (b))
-#define silc_mp_tdiv_q(a, b, c) mpz_tdiv_q((a), (b), (c))
-#define silc_mp_tdiv_q_2exp(a, b, c) mpz_tdiv_q_2exp((a), (b), (c))
-#define silc_mp_tdiv_q_ui(a, b, c) mpz_tdiv_q_ui((a), (b), (c))
-#define silc_mp_tdiv_qr(a, b, c, d) mpz_tdiv_qr((a), (b), (c), (d))
-#define silc_mp_tdiv_qr_ui(a, b, c, d) mpz_tdiv_qr_ui((a), (b), (c), (d))
-#define silc_mp_tdiv_r(a, b, c) mpz_tdiv_r((a), (b), (c))
-#define silc_mp_tdiv_r_2exp(a, b, c) mpz_tdiv_r_2exp((a), (b), (c))
-#define silc_mp_tdiv_r_ui(a, b, c) mpz_tdiv_r_ui((a), (b), (c))
-#define silc_mp_tdiv_ui(a, b) mpz_tdiv_ui((a), (b))
-#define silc_mp_ui_pow_ui(a, b, c) mpz_ui_pow_ui((a), (b), (c))
-#define silc_mp_get_str(a, b, c) mpz_get_str((a), (b), (c))
-#define silc_mp_out_str(a, b, c) mpz_out_str((a), (b), (c))
+#ifdef SILC_MP_GMP
+#include "mp_gmp.h"            /* SILC_MP_GMP */
+#else
+#include "mp_mpi.h"            /* SILC_MP_NSS_MPI */
+#endif
+
+/****d* silcmath/SilcMPAPI/SilcMPInt
+ *
+ * NAME
+ *
+ *    typedef SILC_MP_INT SilcMPInt;
+ *
+ * DESCRIPTION
+ *
+ *    The SILC MP Integer definition. This is the actual MP integer.
+ *    The type is defined as SILC_MP_INT as it is implementation specific
+ *    and is unknown to the application.
+ *
+ * SOURCE
+ */
+typedef SILC_MP_INT SilcMPInt;
+/***/
+
+/****f* silcmath/SilcMPAPI/silc_mp_init
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_init(SilcMPInt mp);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes the SilcMPInt *that is the actual MP Integer.
+ *    This must be called before any of the silc_mp_ routines can be
+ *    used. The integer is uninitialized with the silc_mp_uninit function.
+ *
+ ***/
+void silc_mp_init(SilcMPInt *mp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_uninit
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_uninit(SilcMPInt *mp);
+ *
+ * DESCRIPTION
+ *
+ *    Uninitializes the MP Integer.
+ *
+ ***/
+void silc_mp_uninit(SilcMPInt *mp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_size
+ *
+ * SYNOPSIS
+ *
+ *    size_t silc_mp_size(SilcMPInt *mp);
+ *
+ * DESCRIPTION
+ *
+ *    Return the precision size of the integer `mp'.
+ *
+ ***/
+size_t silc_mp_size(SilcMPInt *mp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_sizeinbase
+ *
+ * SYNOPSIS
+ *
+ *    size_t silc_mp_sizeinbase(SilcMPInt *mp, int base);
+ *
+ * DESCRIPTION
+ *
+ *    Return the size of the integer in base `base'. Note that this size
+ *    is probably only an approximation.  However, it is guaranteed that
+ *    the returned size is always at least the size of the integer, however,
+ *    it may be larger.
+ *
+ ***/
+size_t silc_mp_sizeinbase(SilcMPInt *mp, int base);
+
+/****f* silcmath/SilcMPAPI/silc_mp_set
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_set(SilcMPInt *dst, SilcMPInt *src);
+ *
+ * DESCRIPTION
+ *
+ *    Set `dst' integer from `src' integer. The `dst' must already be
+ *    initialized.
+ *
+ ***/
+void silc_mp_set(SilcMPInt *dst, SilcMPInt *src);
+
+/****f* silcmath/SilcMPAPI/silc_mp_set_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_set_ui(SilcMPInt *dst, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Set `dst' integer from unsigned word `ui'. The `dst' must already be
+ *    initialized.
+ *
+ ***/
+void silc_mp_set_ui(SilcMPInt *dst, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_set_si
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_set_si(SilcMPInt *dst, int32 si);
+ *
+ * DESCRIPTION
+ *
+ *    Set `dst' integer from single word `si'. The `dst' must
+ *    already be initialized.
+ *
+ ***/
+void silc_mp_set_si(SilcMPInt *dst, int32 si);
+
+/****f* silcmath/SilcMPAPI/silc_mp_set_str
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_set_str(SilcMPInt *dst, const char *str, int base);
+ *
+ * DESCRIPTION
+ *
+ *    Set `dst' integer from string `str' of base `base'. The `dst' must
+ *    already be initialized.
+ *
+ ***/
+void silc_mp_set_str(SilcMPInt *dst, const char *str, int base);
+
+/****f* silcmath/SilcMPAPI/silc_mp_get_ui
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_mp_get_ui(SilcMPInt *mp);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the least significant unsigned word from `mp'.
+ *
+ ***/
+uint32 silc_mp_get_ui(SilcMPInt *mp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_get_str
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_get_str(char *str, SilcMPInt *mp, int base);
+ *
+ * DESCRIPTION
+ *
+ *    Converts integer `mp' into a string of base `base'. The `str'
+ *    must already have space allocated. The function returns the same
+ *    as `str' or NULL on error.
+ *
+ ***/
+char *silc_mp_get_str(char *str, SilcMPInt *mp, int base);
+
+/****f* silcmath/SilcMPAPI/silc_mp_add
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_add(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Add two integers `mp1' and `mp2' and save the result to `dst'.
+ *
+ ***/
+void silc_mp_add(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_add_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_add_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Add two integers `mp1' and unsigned word `ui' and save the result
+ *    to `dst'.
+ *
+ ***/
+void silc_mp_add_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_sub
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_sub(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Subtract two integers `mp1' and `mp2' and save the result to `dst'.
+ *
+ ***/
+void silc_mp_sub(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_sub_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_sub_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Subtract integers `mp1' and unsigned word `ui' and save the result
+ *    to `dst'.
+ *
+ ***/
+void silc_mp_sub_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mul
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mul(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Multiply two integers `mp1' and `mp2' and save the result to `dst'.
+ *
+ ***/
+void silc_mp_mul(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mul_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mul_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Multiply integer `mp1' and unsigned word `ui' and save the result
+ *    to `dst'.
+ *
+ ***/
+void silc_mp_mul_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mul_2exp
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mul_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp);
+ *
+ * DESCRIPTION
+ *
+ *    Multiply integers `mp1' with 2 ** `exp' and save the result to 
+ *    `dst'. This is equivalent to dst = mp1 * (2 ^ exp).
+ *
+ ***/
+void silc_mp_mul_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_sqrt
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_sqrt(SilcMPInt *dst, SilcMPInt *src);
+ *
+ * DESCRIPTION
+ *
+ *    Compute square root of floor(sqrt(src)) and save the result to `dst'.
+ *
+ ***/
+void silc_mp_sqrt(SilcMPInt *dst, SilcMPInt *src);
+
+/****f* silcmath/SilcMPAPI/silc_mp_div
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_div(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Divide the `mp1' and `mp2' and save the result to the `dst'. This
+ *    is equivalent to dst = mp1 / mp2;
+ *
+ ***/
+void silc_mp_div(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_div_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_div_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Divide the `mp1' and unsigned word `ui' and save the result to the
+ *    `dst'. This is equivalent to dst = mp1 / ui;
+ *
+ ***/
+void silc_mp_div_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_div_qr
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_div_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+ *                        SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Divide the `mp1' and `mp2' and save the quotient to the `q' and
+ *    the remainder to the `r'.  This is equivalent to the q = mp1 / mp2, 
+ *    r = mp1 mod mp2 (or mp1 = mp2 * q + r). If the `q' or `r' is NULL
+ *    then the operation is omitted.
+ *
+ ***/
+void silc_mp_div_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+                   SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_div_2exp
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_div_2exp(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Divide the `mp1' with 2 ** `exp' and save the result to `dst'.
+ *    This is equivalent to dst = mp1 / (2 ^ exp).
+ *
+ ***/
+void silc_mp_div_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_div_2exp_qr
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_div_2exp_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+ *                             uint32 exp);
+ *
+ * DESCRIPTION
+ *
+ *    Divide the `mp1' with 2 ** `exp' and save the quotient to `q' and
+ *    the remainder to `r'. This is equivalent to q = mp1 / (2 ^ exp),
+ *    r = mp1 mod (2 ^ exp). If the `q' or `r' is NULL then the operation
+ *    is omitted.
+ *
+ ***/
+void silc_mp_div_2exp_qr(SilcMPInt *q, SilcMPInt *r, SilcMPInt *mp1, 
+                        uint32 exp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mod
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Mathematical MOD function. Produces the remainder of `mp1' and `mp2'
+ *    and saves the result to `dst'. This is equivalent to dst = mp1 mod mp2.
+ *    The same result can also be get with silc_mp_div_qr as that function
+ *    returns the remainder as well.
+ *
+ ***/
+void silc_mp_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mod_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Mathematical MOD function. Produces the remainder of `mp1' and 
+ *    unsigned word `ui' and saves the result to `dst'. This is equivalent
+ *    to dst = mp1 mod ui.
+ *
+ ***/
+void silc_mp_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mod_2exp
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mod_2exp(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Computes the remainder of `mp1' with 2 ** `exp' and saves the
+ *    result to `dst'. This is equivalent to dst = mp1 mod (2 ^ exp).
+ *    The same result can also be get with silc_mp_div_2exp_qr as that
+ *    function returns the remainder as well.
+ *
+ ***/
+void silc_mp_mod_2exp(SilcMPInt *dst, SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_pow
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_pow(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp);
+ *
+ * DESCRIPTION
+ *
+ *    Compute `mp1' ** `exp' and save the result to `dst'. This is
+ *    equivalent to dst = mp1 ^ exp.
+ *
+ ***/
+void silc_mp_pow(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_pow_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_pow_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp);
+ *
+ * DESCRIPTION
+ *
+ *    Compute `mp1' ** `exp' and save the result to `dst'. This is
+ *    equivalent to dst = mp1 ^ exp.
+ *
+ ***/
+void silc_mp_pow_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp);
+
+/****f* silcmath/SilcMPAPI/silc_mp_pow_mod
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_pow_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp, 
+ *                         SilcMPInt *mod);
+ *
+ * DESCRIPTION
+ *
+ *    Compute (`mp1' ** `exp') mod `mod' and save the result to `dst'.
+ *    This is equivalent to dst = (mp1 ^ exp) mod mod.
+ *
+ ***/
+void silc_mp_pow_mod(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *exp, 
+                    SilcMPInt *mod);
+
+/****f* silcmath/SilcMPAPI/silc_mp_pow_mod_ui
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_pow_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp, 
+ *                            SilcMPInt *mod);
+ *
+ * DESCRIPTION
+ *
+ *    Compute (`mp1' ** `exp') mod `mod' and save the result to `dst'.
+ *    This is equivalent to dst = (mp1 ^ exp) mod mod.
+ *
+ ***/
+void silc_mp_pow_mod_ui(SilcMPInt *dst, SilcMPInt *mp1, uint32 exp, 
+                       SilcMPInt *mod);
+
+/****f* silcmath/SilcMPAPI/silc_mp_modinv
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_modinv(SilcMPInt *inv, SilcMPInt *a, SilcMPInt *n);
+ *
+ * DESCRIPTION
+ *
+ *    Find multiplicative inverse using Euclid's extended algorithm. 
+ *    Computes inverse such that a * inv mod n = 1, where 0 < a < n. 
+ *    Algorithm goes like this:
+ *  
+ *    g(0) = n    v(0) = 0
+ *    g(1) = a    v(1) = 1
+ * 
+ *    y = g(i-1) / g(i)
+ *    g(i+1) = g(i-1) - y * g(i) = g(i)-1 mod g(i)
+ *    v(i+1) = v(i-1) - y * v(i)
+ * 
+ *    do until g(i) = 0, then inverse = v(i-1). If inverse is negative then n, 
+ *    is added to inverse making it positive again. (Sometimes the algorithm 
+ *    has a variable u defined too and it behaves just like v, except that 
+ *    initalize values are swapped (i.e. u(0) = 1, u(1) = 0). However, u is 
+ *    not needed by the algorithm so it does not have to be included.)
+ *
+ ***/
+void silc_mp_modinv(SilcMPInt *inv, SilcMPInt *a, SilcMPInt *n);
+
+/****f* silcmath/SilcMPAPI/silc_mp_gcd
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_gcd(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Calculate the greatest common divisor of the integers `mp1' and `mp2'
+ *    and save the result to `dst'.
+ *
+ ***/
+void silc_mp_gcd(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_gcdext
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_gcdext(SilcMPInt *g, SilcMPInt *s, SilcMPInt *t, 
+ *                        SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Calculate the extended greatest common divisor `g', `s' and `t' such
+ *    that g = mp1 * s + mp2 * + t.
+ *
+ ***/
+void silc_mp_gcdext(SilcMPInt *g, SilcMPInt *s, SilcMPInt *t, SilcMPInt *mp1,
+                   SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_cmp
+ *
+ * SYNOPSIS
+ *
+ *    int silc_mp_cmp(SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Compare `mp1' and `mp2'. Returns posivite, zero, or negative
+ *    if `mp1' > `mp2', `mp1' == `mp2', or `mp1' < `mp2', respectively.
+ *
+ ***/
+int silc_mp_cmp(SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_cmp_si
+ *
+ * SYNOPSIS
+ *
+ *    int silc_mp_cmp_si(SilcMPInt *mp1, int32 si);
+ *
+ * DESCRIPTION
+ *
+ *    Compare `mp1' and single word `si'. Returns posivite, zero, or negative
+ *    if `mp1' > `si', `mp1' == `si', or `mp1' < `si', respectively.
+ *
+ ***/
+int silc_mp_cmp_si(SilcMPInt *mp1, int32 si);
+
+/****f* silcmath/SilcMPAPI/silc_mp_cmp_ui
+ *
+ * SYNOPSIS
+ *
+ *    int silc_mp_cmp_ui(SilcMPInt *mp1, uint32 ui);
+ *
+ * DESCRIPTION
+ *
+ *    Compare `mp1' and unsigned word `ui'. Returns posivite, zero, or 
+ *    negative if `mp1' > `ui', `mp1' == `ui', or `mp1' < `ui', 
+ *    respectively.
+ *
+ ***/
+int silc_mp_cmp_ui(SilcMPInt *mp1, uint32 ui);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mp2bin
+ *
+ * SYNOPSIS
+ *
+ *    unsigned char *silc_mp_mp2bin(SilcMPInt *val, uint32 len, 
+ *                                  uint32 *ret_len);
+ *
+ * DESCRIPTION
+ *
+ *    Encodes MP integer into binary data. Returns allocated data that
+ *    must be free'd by the caller. If `len' is provided the destination
+ *    buffer is allocated that large. If zero then the size is approximated.
+ *
+ ***/
+unsigned char *silc_mp_mp2bin(SilcMPInt *val, uint32 len, 
+                             uint32 *ret_len);
+
+/****f* silcmath/SilcMPAPI/silc_mp_mp2bin_noalloc
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_mp2bin_noalloc(SilcMPInt *val, unsigned char *dst,
+ *                                uint32 dst_len);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_mp_mp2bin but does not allocate any memory.  The
+ *    encoded data is returned into `dst' and it's length to the `ret_len'.
+ *
+ ***/
+void silc_mp_mp2bin_noalloc(SilcMPInt *val, unsigned char *dst,
+                           uint32 dst_len);
+
+/****f* silcmath/SilcMPAPI/silc_mp_bin2mp
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_bin2mp(unsigned char *data, uint32 len, 
+ *                        SilcMPInt *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Decodes binary data into MP integer. The integer sent as argument
+ *    must be initialized.
+ *
+ ***/
+void silc_mp_bin2mp(unsigned char *data, uint32 len, SilcMPInt *ret);
+
+/****f* silcmath/SilcMPAPI/silc_mp_abs
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_abs(SilcMPInt *src, SilcMPInt *dst);
+ *
+ * DESCRIPTION
+ *
+ *    Assign the absolute value of `src' to `dst'.
+ *
+ ***/
+void silc_mp_abs(SilcMPInt *dst, SilcMPInt *src);
+
+/****f* silcmath/SilcMPAPI/silc_mp_neg
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_neg(SilcMPInt *dst, SilcMPInt *src);
+ *
+ * DESCRIPTION
+ *
+ *    Negate `src' and save the result to `dst'.
+ *
+ ***/
+void silc_mp_neg(SilcMPInt *dst, SilcMPInt *src);
+
+/****f* silcmath/SilcMPAPI/silc_mp_and
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_and(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Logical and operator. The result is saved to `dst'.
+ *
+ ***/
+void silc_mp_and(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_or
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_or(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Logical inclusive OR operator. The result is saved to `dst'.
+ *
+ ***/
+void silc_mp_or(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+
+/****f* silcmath/SilcMPAPI/silc_mp_xor
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mp_xor(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
+ *
+ * DESCRIPTION
+ *
+ *    Logical exclusive OR operator. The result is saved to `dst'.
+ *
+ ***/
+void silc_mp_xor(SilcMPInt *dst, SilcMPInt *mp1, SilcMPInt *mp2);
 
 #endif
index aaa1fc1169dc03f5b4e70615203c7abccb561125..27ed090d0357e76da2c998183549a293f773f8a0 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * Created: Mon Dec  8 16:35:37 GMT+0200 1997
- */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:51  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* Created: Mon Dec  8 16:35:37 GMT+0200 1997 */
+/* $Id$ */
 
 #include "silcincludes.h"
 
-/* XXX This must be temporary solution!! yucky! */
-/* Global random pool used for all prime generation. All primes generated
-   in SILC uses this same pool. Before primes can be generated one must
-   call silc_math_primegen_init. */
-static SilcRng primegen_rng;
-
 /* 
    Fixed primetable for small prime division. We use this primetable to 
    test if possible prime is divisible any of these. Primetable is NULL 
@@ -68,7 +53,7 @@ static SilcRng primegen_rng;
 
 */
 
-static unsigned int primetable[] =
+static uint32 primetable[] =
 {
   2, 3, 5, 7, 11, 13, 17, 19,
   23, 29, 31, 37, 41, 43, 47, 53,
@@ -210,31 +195,37 @@ static unsigned int primetable[] =
    If argument verbose is TRUE this will display some status information
    about the progress of generation. */
 
-int silc_math_gen_prime(SilcInt *prime, unsigned int bits, int verbose)
+bool silc_math_gen_prime(SilcMPInt *prime, uint32 bits, bool verbose)
 {
-  unsigned char *numbuf;
-  unsigned int i, b, k;
-  unsigned int *spmods;
-  SilcInt r, base, tmp, tmp2, oprime;
-
-  /* XXX */
-  assert(primegen_rng != NULL);
+  unsigned char *numbuf = NULL;
+  uint32 i, b, k;
+  uint32 *spmods;
+  SilcMPInt r, base, tmp, tmp2, oprime;
 
   silc_mp_init(&r);
-  silc_mp_init_set_ui(&base, 2);
+  silc_mp_init(&base);
   silc_mp_init(&tmp);
   silc_mp_init(&tmp2);
   silc_mp_init(&oprime);
 
+  silc_mp_set_ui(&base, 2);
+
   SILC_LOG_DEBUG(("Generating new prime"));
 
-  /* Get random number */
-  numbuf = silc_rng_get_rn_string(primegen_rng, (bits / 8));
-  if (!numbuf)
-    return FALSE;
+  /* Get random number and assure that the first digit is not zero since
+     our conversion routines does not like the first digit being zero. */
+  do {
+    if (numbuf) {
+      memset(numbuf, 0, (bits / 8));
+      silc_free(numbuf);
+    }
+    numbuf = silc_rng_global_get_rn_string((bits / 8));
+    if (!numbuf)
+      return FALSE;
+  } while (numbuf[0] == '0');
 
   /* Convert into MP and set the size */
-  silc_mp_set_str(prime, numbuf, 16);
+  silc_mp_set_str(prime, numbuf, 16);  
   silc_mp_mod_2exp(prime, prime, bits);
 
   /* Empty buffer */
@@ -243,11 +234,11 @@ int silc_math_gen_prime(SilcInt *prime, unsigned int bits, int verbose)
 
   /* Number could be even number, so we'll make it odd. */
   silc_mp_set_ui(&tmp, 1);
-  silc_mp_ior(prime, prime, &tmp);             /* OR operator */
+  silc_mp_or(prime, prime, &tmp);              /* OR operator */
 
   /* Init modulo table with the prime candidate and the primes
      in the primetable. */
-  spmods = silc_malloc(sizeof(primetable) * sizeof(unsigned int));
+  spmods = silc_calloc(1, sizeof(primetable) * sizeof(uint32));
   for (i = 0; primetable[i] != 0; i++) {
     silc_mp_mod_ui(&tmp, prime, primetable[i]);
     spmods[i] = silc_mp_get_ui(&tmp);
@@ -281,7 +272,7 @@ int silc_math_gen_prime(SilcInt *prime, unsigned int bits, int verbose)
     /* Does the prime pass the Fermat's prime test.
      * r = 2 ^ p mod p, if r == 2, then p is probably a prime. 
      */
-    silc_mp_powm(&r, &base, &oprime, &oprime);
+    silc_mp_pow_mod(&r, &base, &oprime, &oprime);
     if (silc_mp_cmp_ui(&r, 2) != 0) {
       if (verbose) {
        printf(".");
@@ -307,11 +298,11 @@ int silc_math_gen_prime(SilcInt *prime, unsigned int bits, int verbose)
   }
 
   silc_free(spmods);
-  silc_mp_clear(&r);
-  silc_mp_clear(&base);
-  silc_mp_clear(&tmp);
-  silc_mp_clear(&tmp2);
-  silc_mp_clear(&oprime);
+  silc_mp_uninit(&r);
+  silc_mp_uninit(&base);
+  silc_mp_uninit(&tmp);
+  silc_mp_uninit(&tmp2);
+  silc_mp_uninit(&oprime);
 
   return TRUE;
 }
@@ -319,14 +310,15 @@ int silc_math_gen_prime(SilcInt *prime, unsigned int bits, int verbose)
 /* Performs primality testings for given number. Returns TRUE if the 
    number is probably a prime. */
 
-int silc_math_prime_test(SilcInt *p)
+bool silc_math_prime_test(SilcMPInt *p)
 {
-  SilcInt r, base, tmp;
+  SilcMPInt r, base, tmp;
   int i, ret = 0;
   
   silc_mp_init(&r);
   silc_mp_init(&tmp);
-  silc_mp_init_set_ui(&base, 2);
+  silc_mp_init(&base);
+  silc_mp_set_ui(&base, 2);
   
   SILC_LOG_DEBUG(("Testing probability of prime"));
 
@@ -343,13 +335,13 @@ int silc_math_prime_test(SilcInt *p)
   /* Does the prime pass the Fermat's prime test.
    * r = 2 ^ p mod p, if r == 2, then p is probably a prime.
    */
-  silc_mp_powm(&r, &base, p, p);
+  silc_mp_pow_mod(&r, &base, p, p);
   if (silc_mp_cmp_ui(&r, 2) != 0)
     ret = -1;
 
-  silc_mp_clear(&r);
-  silc_mp_clear(&tmp);
-  silc_mp_clear(&base);
+  silc_mp_uninit(&r);
+  silc_mp_uninit(&tmp);
+  silc_mp_uninit(&base);
   
   if (ret)
     return FALSE;
@@ -357,24 +349,3 @@ int silc_math_prime_test(SilcInt *p)
   /* Number is probably a prime */
   return TRUE;
 }
-
-/* XXX This must temporary solution!! */
-/* Initializes the random pool used to generated primes */
-
-void silc_math_primegen_init()
-{
-  SILC_LOG_DEBUG(("Start"));
-
-  if (primegen_rng == NULL) {
-    primegen_rng = silc_rng_alloc();
-    silc_rng_init(primegen_rng);
-  }
-}
-
-/* XXX This must temporary solution!! */
-/* Uninitializes random pool */
-
-void silc_math_primegen_uninit()
-{
-  silc_rng_free(primegen_rng);
-}
diff --git a/lib/silcsftp/DIRECTORY b/lib/silcsftp/DIRECTORY
new file mode 100644 (file)
index 0000000..87c2b2f
--- /dev/null
@@ -0,0 +1,19 @@
+<!--
+@LIBRARY=SILC SFTP Library
+@FILENAME=silcsftplib.html
+@LINK=silcsftp.html:SILC SFTP API
+@LINK=silcsftp_fs.html:SILC SFTP Filesystems
+-->
+
+<BIG><B>SILC SFTP Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC SFTP Library provides SSH File Transfer Protocol client and server
+implementation.  The SFTP protocol is the mandatory file transfer protocol
+used for file transfers in the SILC protocol.  The same interface is used
+for SFTP client and SFTP server.
+
+<BR /><BR />
+@LINKS@
diff --git a/lib/silcsftp/Makefile.am b/lib/silcsftp/Makefile.am
new file mode 100644 (file)
index 0000000..d4f3ffc
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+noinst_LIBRARIES = libsilcsftp.a
+
+libsilcsftp_a_SOURCES =        \
+       sftp_client.c           \
+       sftp_server.c           \
+       sftp_util.c             \
+       sftp_fs_memory.c
+
+if SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       silcsftp.h      \
+       silcsftp_fs.h
+endif
+
+EXTRA_DIST = *.h tests
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcsftp/sftp_client.c b/lib/silcsftp/sftp_client.c
new file mode 100644 (file)
index 0000000..89cc6fc
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+
+  sftp_client.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "sftp_util.h"
+
+/* Request context. Every request will allocate this context and set
+   the correct callback function according the `type' field. */
+typedef struct SilcSFTPRequestStruct {
+  uint32 id;
+  SilcSFTPPacket type;
+  SilcSFTPStatusCallback status;
+  SilcSFTPHandleCallback handle;
+  SilcSFTPDataCallback data;
+  SilcSFTPNameCallback name;
+  SilcSFTPAttrCallback attr;
+  SilcSFTPExtendedCallback extended;
+  void *context;
+  struct SilcSFTPRequestStruct *next;
+} *SilcSFTPRequest;
+
+/* SFTP client context */
+typedef struct {
+  SilcSocketConnection sock;
+  SilcSFTPSendPacketCallback send_packet;
+  void *send_context;
+  SilcSFTPVersionCallback version;
+  void *version_context;
+  uint32 id;
+  SilcList requests;
+  SilcBuffer packet;
+} *SilcSFTPClient;
+
+/* File handle */
+struct SilcSFTPHandleStruct {
+  unsigned char *data;
+  uint32 data_len;
+};
+
+/* Creates SilcSFTPHandle and returns pointer to it. The caller must free
+   the context. */
+
+static SilcSFTPHandle silc_sftp_handle_create(unsigned char *data,
+                                             uint32 data_len)
+{
+  SilcSFTPHandle handle;
+
+  handle = silc_calloc(1, sizeof(*handle));
+  handle->data = silc_calloc(data_len, sizeof(*handle->data));
+  memcpy(handle->data, data, data_len);
+  handle->data_len = data_len;
+
+  return handle;
+}
+
+/* Deletes the handle indicated by the `handle'. */
+
+static void silc_sftp_handle_delete(SilcSFTPHandle handle)
+{
+  silc_free(handle->data);
+  silc_free(handle);
+}
+
+/* Returns the handle data of the `handle' to the `data' pointer. */
+
+static void silc_sftp_handle_get(SilcSFTPHandle handle, 
+                                const unsigned char **data,
+                                uint32 *data_len)
+{
+  *data = (const unsigned char *)handle->data;
+  *data_len = handle->data_len;
+}
+
+/* General routine to send SFTP packet to the SFTP server. */
+
+static void silc_sftp_send_packet(SilcSFTPClient sftp,
+                                 SilcSFTPPacket type, 
+                                 uint32 len, ...)
+{
+  SilcBuffer tmp;
+  va_list vp;
+
+  va_start(vp, len);
+  tmp = silc_sftp_packet_encode_vp(type, sftp->packet, len, vp);
+  va_end(vp);
+  if (!tmp)
+    return;
+  sftp->packet = tmp;
+
+  SILC_LOG_HEXDUMP(("SFTP packet to server"), sftp->packet->data, 
+                  sftp->packet->len);
+
+  /* Send the packet */
+  (*sftp->send_packet)(sftp->sock, sftp->packet, sftp->send_context);
+
+  /* Clear packet */
+  sftp->packet->data = sftp->packet->tail = sftp->packet->head;
+  sftp->packet->len = 0;
+}
+
+/* Finds request by request ID. */
+
+static SilcSFTPRequest silc_sftp_find_request(SilcSFTPClient sftp, uint32 id)
+{
+  SilcSFTPRequest req;
+
+  SILC_LOG_DEBUG(("Finding request ID: %d", id));
+
+  silc_list_start(sftp->requests);
+  while ((req = silc_list_get(sftp->requests)) != SILC_LIST_END) {
+    if (req->id == id)
+      return req;
+  }
+
+  SILC_LOG_DEBUG(("Unknown request ID"));
+
+  return NULL;
+}
+
+/* Function used to call the request callback indicated by the `req'. The
+   `status' will be sent to the callback function as the status of the
+   operation. The variable argument list includes the status and req->type
+   specific data. */
+
+static void silc_sftp_call_request(SilcSFTPClient sftp, 
+                                  SilcSFTPRequest req, 
+                                  SilcSFTPPacket type,
+                                  SilcSFTPStatus status, ...)
+{
+  va_list vp;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  va_start(vp, status);
+
+  switch (req->type) {
+  case SILC_SFTP_READ:
+    {
+      /* Data returned */
+      unsigned char *data;
+      uint32 data_len;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->data)
+         (*req->data)((SilcSFTP)sftp, status, NULL, 0, req->context);
+       break;
+      }
+
+      data = (unsigned char *)va_arg(vp, unsigned char *);
+      data_len = (uint32)va_arg(vp, uint32);
+
+      if (req->data)
+       (*req->data)((SilcSFTP)sftp, status, (const unsigned char *)data, 
+                    data_len, req->context);
+    }    
+    break;
+
+  case SILC_SFTP_OPEN:
+  case SILC_SFTP_OPENDIR:
+    {
+      /* Handle returned */
+      SilcSFTPHandle handle;
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->handle)
+         (*req->handle)((SilcSFTP)sftp, status, NULL, req->context);
+       break;
+      }
+
+      hdata = (unsigned char *)va_arg(vp, unsigned char *);
+      hdata_len = (uint32)va_arg(vp, uint32);
+      handle = silc_sftp_handle_create(hdata, hdata_len);
+
+      if (req->handle)
+       (*req->handle)((SilcSFTP)sftp, status, handle, req->context);
+    }
+    break;
+
+  case SILC_SFTP_CLOSE:
+  case SILC_SFTP_WRITE:
+  case SILC_SFTP_REMOVE:
+  case SILC_SFTP_RENAME:
+  case SILC_SFTP_MKDIR:
+  case SILC_SFTP_RMDIR:
+  case SILC_SFTP_SETSTAT:
+  case SILC_SFTP_FSETSTAT:
+  case SILC_SFTP_SYMLINK:
+    {
+      /* Status returned */
+      char *message, *language_tag;
+
+      message = (char *)va_arg(vp, char *);
+      language_tag = (char *)va_arg(vp, char *);
+
+      if (req->status)
+       (*req->status)((SilcSFTP)sftp, status, (const char *)message, 
+                      (const char *)language_tag, req->context);
+    }
+    break;
+
+  case SILC_SFTP_STAT:
+  case SILC_SFTP_LSTAT:
+  case SILC_SFTP_FSTAT:
+    {
+      /* Attributes returned */
+      SilcSFTPAttributes attr;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->attr)
+         (*req->attr)((SilcSFTP)sftp, status, NULL, req->context);
+       break;
+      }
+
+      attr = (SilcSFTPAttributes)va_arg(vp, SilcSFTPAttributes);
+
+      if (req->attr)
+       (*req->attr)((SilcSFTP)sftp, status, (const SilcSFTPAttributes)attr, 
+                    req->context);
+    }
+    break;
+
+  case SILC_SFTP_READDIR:
+  case SILC_SFTP_REALPATH:
+  case SILC_SFTP_READLINK:
+    {
+      /* Name(s) returned */
+      SilcSFTPName name;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->name)
+         (*req->name)((SilcSFTP)sftp, status, NULL, req->context);
+       break;
+      }
+
+      name = (SilcSFTPName)va_arg(vp, SilcSFTPName);
+
+      if (req->name)
+       (*req->name)((SilcSFTP)sftp, status, name, req->context);
+    }
+    break;
+
+  case SILC_SFTP_EXTENDED:
+    {
+      /* Extended reply returned */
+      unsigned char *data;
+      uint32 data_len;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       if (req->extended)
+         (*req->extended)((SilcSFTP)sftp, status, NULL, 0, req->context);
+       break;
+      }
+
+      data = (unsigned char *)va_arg(vp, unsigned char *);
+      data_len = (uint32)va_arg(vp, uint32);
+
+      if (req->extended)
+       (*req->extended)((SilcSFTP)sftp, status, (const unsigned char *)data, 
+                        data_len, req->context);
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  /* Remove this request */
+  silc_list_del(sftp->requests, req);
+  silc_free(req);
+
+  va_end(vp);
+}
+
+/* Starts SFTP client by associating the socket connection `sock' to the
+   created SFTP client context.  The version callback indicated by the
+   `callback' will be called after the SFTP session has been started
+   and server has returned the version of the protocol.  The SFTP client
+   context is returned in the callback too.  This returns the allocated
+   SFTP client context or NULL on error. */
+
+SilcSFTP silc_sftp_client_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context,
+                               SilcSFTPVersionCallback callback,
+                               void *context)
+{
+  SilcSFTPClient sftp;
+
+  if (!send_packet)
+    return NULL;
+
+  sftp = silc_calloc(1, sizeof(*sftp));
+  sftp->sock = sock;
+  sftp->send_packet = send_packet;
+  sftp->send_context = send_context;
+  sftp->version = callback;
+  sftp->version_context = context;
+  silc_list_init(sftp->requests, struct SilcSFTPRequestStruct, next);
+
+  /* Send the SFTP session initialization to the server */
+  silc_sftp_send_packet(sftp, SILC_SFTP_INIT, 4, 
+                       SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
+                       SILC_STR_END);
+
+  return (SilcSFTP)sftp;
+}
+
+/* Shutdown's the SFTP client.  The caller is responsible of closing
+   the associated socket connection.  The SFTP context is freed and is
+   invalid after this function returns. */
+
+void silc_sftp_client_shutdown(SilcSFTP context)
+{
+  SilcSFTPClient sftp = (SilcSFTPClient)context;
+
+  if (sftp->packet)
+    silc_buffer_free(sftp->packet);
+  silc_free(sftp);
+}
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+
+void silc_sftp_client_receive_process(SilcSFTP context,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet)
+{
+  SilcSFTPClient sftp = (SilcSFTPClient)context;
+  SilcSFTPRequest req;
+  SilcSFTPPacket type;
+  const unsigned char *payload = NULL;
+  uint32 payload_len;
+  int ret;
+  SilcBufferStruct buf;
+  uint32 id;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Parse the packet */
+  type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
+                                &payload_len);
+  if (!type)
+    return;
+
+  silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
+
+  switch (type) {
+  case SILC_SFTP_VERSION:
+    {
+      SilcSFTPVersion version;
+
+      SILC_LOG_DEBUG(("Version packet"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&version),
+                                SILC_STR_END);
+      if (ret < 0) {
+       (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_FAILURE, 0, 
+                        sftp->version_context);
+       break;
+      }
+
+      /* Call the callback */
+      (*sftp->version)((SilcSFTP)sftp, SILC_SFTP_STATUS_OK, version, 
+                      sftp->version_context);
+    }
+    break;
+
+  case SILC_SFTP_STATUS:
+    {
+      uint32 status;
+      char *message = NULL, *language_tag = NULL;
+
+      SILC_LOG_DEBUG(("Status packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_INT(&status),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      if (status != SILC_SFTP_STATUS_OK) {
+       silc_buffer_pull(&buf, 8);
+       ret = silc_buffer_unformat(&buf,
+                                  SILC_STR_UI32_STRING_ALLOC(&message),
+                                  SILC_STR_UI32_STRING_ALLOC(&language_tag),
+                                  SILC_STR_END);
+       if (ret < 0)
+         break;
+
+       silc_buffer_push(&buf, 8);
+      }
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req) {
+       silc_free(message);
+       silc_free(language_tag);
+       break;
+      }
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, status, message, language_tag);
+
+      silc_free(message);
+      silc_free(language_tag);
+    }
+    break;
+
+  case SILC_SFTP_HANDLE:
+    {
+      unsigned char *handle = NULL;
+      uint32 handle_len;
+
+      SILC_LOG_DEBUG(("Handle packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&handle, 
+                                                      &handle_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, 
+                            handle, handle_len);
+    }
+    break;
+
+  case SILC_SFTP_DATA:
+    {
+      unsigned char *data = NULL;
+      uint32 data_len = 0;
+
+      SILC_LOG_DEBUG(("Data packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&data, &data_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, 
+                            data, data_len);
+    }
+    break;
+
+  case SILC_SFTP_NAME:
+    {
+      uint32 count;
+      SilcSFTPName name = NULL;
+
+      SILC_LOG_DEBUG(("Name packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_INT(&count),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      silc_buffer_pull(&buf, 8);
+      name = silc_sftp_name_decode(count, &buf);
+      if (!name)
+       break;
+      silc_buffer_push(&buf, 8);
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, name);
+      silc_sftp_name_free(name);
+    }
+    break;
+
+  case SILC_SFTP_ATTRS:
+    {
+      SilcSFTPAttributes attr = NULL;
+      unsigned char *data;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Attributes packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_XNSTRING(&data, buf.len - 4),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      silc_buffer_set(&tmpbuf, data, buf.len - 4);
+      attr = silc_sftp_attr_decode(&tmpbuf);
+      if (!attr)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, attr);
+    }
+    break;
+
+  case SILC_SFTP_EXTENDED_REPLY:
+    {
+      unsigned char *data = NULL;
+
+      SILC_LOG_DEBUG(("Extended reply packet"));
+
+      ret = silc_buffer_unformat(&buf, 
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI_XNSTRING(&data, buf.len - 4),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Get request */
+      req = silc_sftp_find_request(sftp, id);
+      if (!req)
+       break;
+
+      /* Call the callback */
+      silc_sftp_call_request(sftp, req, type, SILC_SFTP_STATUS_OK, 
+                            data, buf.len - 4);
+    }
+    break;
+
+  default:
+    break;
+  }
+}
+
+void silc_sftp_open(SilcSFTP sftp, 
+                   const char *filename,
+                   SilcSFTPFileOperation pflags,
+                   SilcSFTPAttributes attrs,
+                   SilcSFTPHandleCallback callback,
+                   void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  SilcBuffer attrs_buf;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Open request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_OPEN;
+  req->handle = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + strlen(filename) + 4 + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(filename)),
+                       SILC_STR_UI32_STRING(filename),
+                       SILC_STR_UI_INT(pflags),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data, 
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_close(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Close request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_CLOSE;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_read(SilcSFTP sftp,
+                   SilcSFTPHandle handle,
+                   uint64 offset, 
+                   uint32 len,
+                   SilcSFTPDataCallback callback,
+                   void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len2 = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Read request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_READ;
+  req->data = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len2 = 4 + 4 + hdata_len + 8 + 4;
+
+  silc_sftp_send_packet(client, req->type, len2, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_UI_INT64(offset),
+                       SILC_STR_UI_INT(len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_write(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    uint64 offset,
+                    const unsigned char *data,
+                    uint32 data_len,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Write request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_WRITE;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len + 8 + 4 + data_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_UI_INT64(offset),
+                       SILC_STR_UI_INT(data_len),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_remove(SilcSFTP sftp,
+                     const char *filename,
+                     SilcSFTPStatusCallback callback,
+                     void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Remove request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_REMOVE;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(filename);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(filename)),
+                       SILC_STR_UI32_STRING(filename),
+                       SILC_STR_END);
+}
+
+void silc_sftp_rename(SilcSFTP sftp,
+                     const char *oldname,
+                     const char *newname,
+                     SilcSFTPStatusCallback callback,
+                     void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Rename request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_RENAME;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(oldname) + 4 + strlen(newname);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(oldname)),
+                       SILC_STR_UI32_STRING(oldname),
+                       SILC_STR_UI_INT(strlen(newname)),
+                       SILC_STR_UI32_STRING(newname),
+                       SILC_STR_END);
+}
+
+void silc_sftp_mkdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttributes attrs,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  SilcBuffer attrs_buf;
+
+  SILC_LOG_DEBUG(("Mkdir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_MKDIR;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + strlen(path) + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data,
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_rmdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPStatusCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Rmdir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_RMDIR;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_opendir(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPHandleCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Opendir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_OPENDIR;
+  req->handle = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_readdir(SilcSFTP sftp,
+                      SilcSFTPHandle handle,
+                      SilcSFTPNameCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Readdir request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_READDIR;
+  req->name = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_stat(SilcSFTP sftp,
+                   const char *path,
+                   SilcSFTPAttrCallback callback,
+                   void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Stat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_STAT;
+  req->attr = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_lstat(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttrCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Lstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_LSTAT;
+  req->attr = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_fstat(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPAttrCallback callback,
+                    void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Fstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_FSTAT;
+  req->attr = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  len = 4 + 4 + hdata_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+void silc_sftp_setstat(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPAttributes attrs,
+                      SilcSFTPStatusCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  SilcBuffer attrs_buf;
+
+  SILC_LOG_DEBUG(("Setstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_SETSTAT;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + strlen(path) + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data,
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_fsetstat(SilcSFTP sftp,
+                       SilcSFTPHandle handle,
+                       SilcSFTPAttributes attrs,
+                       SilcSFTPStatusCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+  SilcBuffer attrs_buf;
+  const unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Fsetstat request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_FSETSTAT;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  silc_sftp_handle_get(handle, &hdata, &hdata_len);
+  attrs_buf = silc_sftp_attr_encode(attrs);
+  len = 4 + 4 + hdata_len + attrs_buf->len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_UI_XNSTRING(attrs_buf->data,
+                                            attrs_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attrs_buf);
+}
+
+void silc_sftp_readlink(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Readlink request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_READLINK;
+  req->name = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_symlink(SilcSFTP sftp,
+                      const char *linkpath,
+                      const char *targetpath,
+                      SilcSFTPStatusCallback callback,
+                      void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Symlink request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_SYMLINK;
+  req->status = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(linkpath) + 4 + strlen(targetpath);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(linkpath)),
+                       SILC_STR_UI32_STRING(linkpath),
+                       SILC_STR_UI_INT(strlen(targetpath)),
+                       SILC_STR_UI32_STRING(targetpath),
+                       SILC_STR_END);
+}
+
+void silc_sftp_realpath(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Realpath request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_REALPATH;
+  req->name = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(path);
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(path)),
+                       SILC_STR_UI32_STRING(path),
+                       SILC_STR_END);
+}
+
+void silc_sftp_extended(SilcSFTP sftp,
+                       const char *request,
+                       const unsigned char *data,
+                       uint32 data_len,
+                       SilcSFTPExtendedCallback callback,
+                       void *context)
+{
+  SilcSFTPClient client = (SilcSFTPClient)sftp;
+  SilcSFTPRequest req;
+  uint32 len = 0;
+
+  SILC_LOG_DEBUG(("Extended request"));
+
+  req = silc_calloc(1, sizeof(*req));
+  req->id = client->id++;
+  req->type = SILC_SFTP_WRITE;
+  req->extended = callback;
+  req->context = context;
+  silc_list_add(client->requests, req);
+
+  len = 4 + 4 + strlen(request) + data_len;
+
+  silc_sftp_send_packet(client, req->type, len, 
+                       SILC_STR_UI_INT(req->id),
+                       SILC_STR_UI_INT(strlen(request)),
+                       SILC_STR_UI32_STRING(request),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
diff --git a/lib/silcsftp/sftp_fs_memory.c b/lib/silcsftp/sftp_fs_memory.c
new file mode 100644 (file)
index 0000000..b2c3d84
--- /dev/null
@@ -0,0 +1,1044 @@
+/*
+
+  sftp_fs_memory.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+/* XXX TODO Win32 support */
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "silcsftp_fs.h"
+#include "sftp_util.h"
+
+#define DIR_SEPARATOR "/"
+
+struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory;
+
+/* Memory filesystem entry */
+typedef struct MemFSEntryStruct {
+  char *name;                       /* Name of the entry */
+  char *data;                      /* Data of the entry */
+  bool directory;                  /* TRUE if this is directory */
+  SilcSFTPFSMemoryPerm perm;       /* Permissions */
+  struct MemFSEntryStruct **entry;  /* Files and sub-directories */
+  uint32 entry_count;              /* Number of files and sub-directories */
+  struct MemFSEntryStruct *parent;  /* non-NULL if `directory' is TRUE,
+                                      includes parent directory. */
+  unsigned long created;           /* Time of creation */
+} *MemFSEntry;
+
+/* File handle. */
+typedef struct {
+  uint32 handle;                   /* Handle index */
+  int fd;                          /* Real file handle */
+  MemFSEntry entry;                /* Filesystem entry */
+} *MemFSFileHandle;
+
+/* Memory filesystem */
+typedef struct {
+  MemFSEntry root;                 /* Root of the filesystem hierarchy */
+  SilcSFTPFSMemoryPerm root_perm;
+  MemFSFileHandle *handles;        /* Open file handles */
+  uint32 handles_count;
+} *MemFS;
+
+/* Generates absolute path from relative path that may include '.' and '..'
+   in the path. */
+
+static char *mem_expand_path(MemFSEntry root, const char *path)
+{
+  if (!strstr(path, "./") && !strstr(path, "../") &&
+      !strstr(path, "/..") && !strstr(path, "/."))
+    return strdup(path);
+
+  /* XXX TODO */
+  return NULL;
+}
+
+/* Add `entry' to directory `dir'. */
+
+static bool mem_add_entry(MemFSEntry dir, MemFSEntry entry,
+                         bool check_perm)
+{
+  int i;
+
+  /* Must be both write and exec permissions */
+  if (check_perm && 
+      !((dir->perm & SILC_SFTP_FS_PERM_WRITE) && 
+       (dir->perm & SILC_SFTP_FS_PERM_EXEC)))
+    return FALSE;
+
+  if (!dir->entry) {
+    dir->entry = silc_calloc(3, sizeof(*entry));
+    dir->entry[0] = entry;
+    dir->entry_count = 3;
+    entry->created = time(0);
+    return TRUE;
+  }
+
+  for (i = 0; i < dir->entry_count; i++) {
+    if (dir->entry[i])
+      continue;
+
+    dir->entry[i] = entry;
+    entry->created = time(0);
+    return TRUE;
+  }
+
+  dir->entry = silc_realloc(dir->entry, sizeof(*dir->entry) *
+                           (dir->entry_count + 3));
+  for (i = dir->entry_count + 1; i < dir->entry_count + 3; i++)
+    dir->entry[i] = NULL;
+  dir->entry[dir->entry_count] = entry;
+  dir->entry_count += 3;
+  entry->created = time(0);
+
+  return TRUE;
+}
+
+/* Removes entry `entry' and all entries under it recursively. */
+
+static bool mem_del_entry(MemFSEntry entry, bool check_perm)
+{
+  int i;
+
+  /* Directories cannot be removed from remote access */
+  if (check_perm)
+    return FALSE;
+
+  silc_free(entry->name);
+  silc_free(entry->data);
+
+  /* Delete all entries recursively under this entry */
+  for (i = 0; i < entry->entry_count; i++) {
+    if (entry->entry[i]) {
+      if (!mem_del_entry(entry->entry[i], FALSE))
+       return FALSE;
+    }
+  }
+  silc_free(entry->entry);
+
+  /* Remove from parent */
+  if (entry->parent) {
+    for (i = 0; i < entry->parent->entry_count; i++) {
+      if (entry->parent->entry[i] == entry) {
+       entry->parent->entry[i] = NULL;
+       break;
+      }
+    }
+  }
+
+  silc_free(entry);
+
+  return TRUE;
+}
+
+/* Finds first occurence of entry named `name' under the directory `dir'. 
+   This does not check subdirectories recursively. */
+
+static MemFSEntry mem_find_entry(MemFSEntry dir, const char *name,
+                                uint32 name_len)
+{
+  int i;
+
+  for (i = 0; i < dir->entry_count; i++) {
+    if (!dir->entry[i])
+      continue;
+
+    if (!strncmp(name, dir->entry[i]->name, name_len))
+      return dir->entry[i];
+  }
+
+  return NULL;
+}
+
+/* Finds the entry by the `path' which may include full path or
+   relative path. */
+
+static MemFSEntry mem_find_entry_path(MemFSEntry dir, const char *p)
+{
+  MemFSEntry entry = NULL;
+  int len;
+  char *path, *cp;
+
+  cp = path = mem_expand_path(dir, p);
+
+  if (strlen(cp) == 1 && cp[0] == '/')
+    return dir;
+
+  if (cp[0] == '/')
+    cp++;
+  len = strcspn(cp, DIR_SEPARATOR);
+  while (cp && len) {
+    entry = mem_find_entry(dir, cp, len);
+    if (!entry) {
+      silc_free(cp);
+      return NULL;
+    }
+    cp += len;
+    if (!strlen(cp))
+      break;
+    cp++;
+    len = strcspn(cp, DIR_SEPARATOR);
+    dir = entry;
+  }
+
+  silc_free(path);
+  return entry;
+}
+
+/* Deletes entry by the name `name' from the directory `dir'. This does
+   not check subdirectories recursively. */
+
+static bool mem_del_entry_name(MemFSEntry dir, const char *name,
+                              uint32 name_len, bool check_perm)
+{
+  MemFSEntry entry;
+
+  /* Files cannot be removed from remote access */
+  if (check_perm)
+    return FALSE;
+
+  entry = mem_find_entry(dir, name, name_len);
+
+  if (entry)
+    return mem_del_entry(entry, check_perm);
+
+  return FALSE;
+}
+
+/* Create new handle and add it to the list of open handles. */
+
+static MemFSFileHandle mem_create_handle(MemFS fs, int fd, MemFSEntry entry)
+{
+  MemFSFileHandle handle;
+  int i;
+
+  handle = silc_calloc(1, sizeof(*handle));
+  handle->fd = fd;
+  handle->entry = entry;
+
+  if (!fs->handles) {
+    fs->handles = silc_calloc(5, sizeof(*fs->handles));
+    fs->handles[0] = handle;
+    fs->handles_count = 5;
+
+    handle->handle = 0;
+
+    return handle;
+  }
+
+  for (i = 0; i < fs->handles_count; i++) {
+    if (fs->handles[i])
+      continue;
+
+    fs->handles[i] = handle;
+
+    handle->handle = i;
+
+    return handle;
+  }
+
+  fs->handles = silc_realloc(fs->handles, sizeof(*fs->handles) *
+                            (fs->handles_count + 5));
+  for (i = fs->handles_count + 1; i < fs->handles_count + 5; i++)
+    fs->handles[i] = NULL;
+  fs->handles[fs->handles_count] = handle;
+  handle->handle = fs->handles_count;
+  fs->handles_count += 5;
+
+  return handle;
+}
+
+/* Deletes the handle and remove it from the open handle list. */
+
+static bool mem_del_handle(MemFS fs, MemFSFileHandle handle)
+{
+  if (handle->handle > fs->handles_count)
+    return FALSE;
+
+  if (!fs->handles[handle->handle])
+    return FALSE;
+
+  if (fs->handles[handle->handle] == handle) {
+    fs->handles[handle->handle] = NULL;
+    if (handle->fd != -1)
+      close(handle->fd);
+    silc_free(handle);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/* Find handle by handle index. */
+
+static MemFSFileHandle mem_find_handle(MemFS fs, uint32 handle)
+{
+  if (handle > fs->handles_count)
+    return NULL;
+
+  if (!fs->handles[handle])
+    return NULL;
+
+  if (fs->handles[handle]->handle != handle)
+    return NULL;
+
+  return fs->handles[handle];
+}
+
+/* Allocates memory filesystem context and returns the context.  The
+   context can be given as argument to the silc_sftp_server_start function.
+   The context must be freed by the caller using the function
+   silc_sftp_fs_memory_free. The `perm' is the permissions for the root
+   directory of the filesystem (/ dir). */
+
+SilcSFTPFilesystem silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm)
+{
+  SilcSFTPFilesystem filesystem;
+  MemFS fs;
+
+  fs = silc_calloc(1, sizeof(*fs));
+  fs->root = silc_calloc(1, sizeof(*fs->root));
+  fs->root->perm = perm;
+  fs->root_perm = perm;
+  fs->root->directory = TRUE;
+  fs->root->name = strdup(DIR_SEPARATOR);
+
+  filesystem = silc_calloc(1, sizeof(*filesystem));
+  filesystem->fs = &silc_sftp_fs_memory;
+  filesystem->fs_context = (void *)fs;
+
+  return filesystem;
+}
+
+/* Frees the memory filesystem context. */
+
+void silc_sftp_fs_memory_free(SilcSFTPFilesystem fs)
+{
+  MemFS memfs = (MemFS)fs->fs_context;
+
+  silc_free(memfs->root);
+  silc_free(memfs);
+}
+
+/* Adds a new directory to the memory filesystem. Returns the directory
+   context that can be used to add for example files to the directory
+   or new subdirectories under the directory. The `dir' is the parent
+   directory of the directory to be added. If this directory is to be
+   added to the root directory the `dir' is NULL.  The `name' is the name
+   of the directory. If error occurs this returns NULL. The caller must
+   not free the returned context. The `perm' will indicate the permissions
+   for the directory and they work in POSIX style. */
+
+void *silc_sftp_fs_memory_add_dir(SilcSFTPFilesystem fs, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *name)
+{
+  MemFS memfs = (MemFS)fs->fs_context;
+  MemFSEntry entry;
+
+  entry = silc_calloc(1, sizeof(*entry));
+  entry->perm = perm;
+  entry->name = strdup(name);
+  entry->directory = TRUE;
+  entry->parent = dir ? dir : memfs->root;
+
+  if (!mem_add_entry(dir ? dir : memfs->root, entry, FALSE))
+    return NULL;
+
+  return entry;
+}
+
+/* Deletes a directory indicated by the `dir'. All files and subdirectories
+   in this directory is also removed.  If the `dir' is NULL then all
+   directories and files are removed from the filesystem. Returns TRUE
+   if the removing was success. This is the only way to remove directories
+   in memory file system. The filesystem does not allow removing directories
+   with remote access using the filesystem access function sftp_rmdir. */
+
+bool silc_sftp_fs_memory_del_dir(SilcSFTPFilesystem fs, void *dir)
+{
+  MemFS memfs = (MemFS)fs->fs_context;
+  bool ret;
+
+  if (dir)
+    return mem_del_entry(dir, FALSE);
+
+  /* Remove from root */
+  ret = mem_del_entry(memfs->root, FALSE);
+
+  memfs->root = silc_calloc(1, sizeof(*memfs->root));
+  memfs->root->perm = memfs->root_perm;
+  memfs->root->directory = TRUE;
+  memfs->root->name = strdup(DIR_SEPARATOR);
+
+  return ret;
+}
+
+/* Adds a new file to the directory indicated by the `dir'.  If the `dir'
+   is NULL the file is added to the root directory. The `filename' is the
+   filename in the directory. The `realpath' is the real filepath in the
+   physical filesystem. It is used to actually access the file from the
+   memory filesystem. The `perm' will indicate the permissions for th e
+   file and they work in POSIX style. Returns TRUE if the file was
+   added to the directory. */
+
+bool silc_sftp_fs_memory_add_file(SilcSFTPFilesystem fs, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *filename,
+                                 const char *realpath)
+{
+  MemFS memfs = (MemFS)fs->fs_context;
+  MemFSEntry entry;
+
+  entry = silc_calloc(1, sizeof(*entry));
+  entry->perm = perm;
+  entry->name = strdup(filename);
+  entry->data = strdup(realpath);
+  entry->directory = FALSE;
+
+  return mem_add_entry(dir ? dir : memfs->root, entry, FALSE);
+}
+
+/* Removes a file indicated by the `filename' from the directory
+   indicated by the `dir'. Returns TRUE if the removing was success. */
+
+bool silc_sftp_fs_memory_del_file(SilcSFTPFilesystem fs, void *dir,
+                                 const char *filename)
+{
+  MemFS memfs = (MemFS)fs->fs_context;
+
+  if (!filename)
+    return FALSE;
+
+  return mem_del_entry_name(dir ? dir : memfs->root, filename, 
+                           strlen(filename), FALSE);
+}
+
+SilcSFTPHandle mem_get_handle(void *context, SilcSFTP sftp,
+                             const unsigned char *data,
+                             uint32 data_len)
+{
+  MemFS fs = (MemFS)context;
+  uint32 handle;
+
+  if (data_len < 4)
+    return NULL;
+
+  SILC_GET32_MSB(handle, data);
+  return (SilcSFTPHandle)mem_find_handle(fs, handle);
+}
+
+unsigned char *mem_encode_handle(void *context, SilcSFTP sftp,
+                                SilcSFTPHandle handle,
+                                uint32 *handle_len)
+{
+  unsigned char *data;
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+
+  data = silc_calloc(4, sizeof(*data));
+  SILC_PUT32_MSB(h->handle, data);
+  *handle_len = 4;
+
+  return data;
+}
+
+void mem_open(void *context, SilcSFTP sftp, 
+             const char *filename,
+             SilcSFTPFileOperation pflags,
+             SilcSFTPAttributes attrs,
+             SilcSFTPHandleCallback callback,
+             void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  MemFSFileHandle handle;
+  int flags = 0, fd;
+
+  /* CREAT and TRUNC not supported */
+  if ((pflags & SILC_SFTP_FXF_CREAT) || (pflags & SILC_SFTP_FXF_TRUNC)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, callback_context);
+    return;
+  }
+
+  /* Find such file */
+  entry = mem_find_entry_path(fs->root, filename);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (entry->directory || !entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Check for reading */
+  if ((pflags & SILC_SFTP_FXF_READ) && 
+      !(entry->perm & SILC_SFTP_FS_PERM_READ)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
+               callback_context);
+    return;
+  }    
+
+  /* Check for writing */
+  if (((pflags & SILC_SFTP_FXF_WRITE) || (pflags & SILC_SFTP_FXF_APPEND)) && 
+      !(entry->perm & SILC_SFTP_FS_PERM_WRITE)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
+               callback_context);
+    return;
+  }
+
+  if ((pflags & SILC_SFTP_FXF_READ) && (pflags & SILC_SFTP_FXF_WRITE))
+    flags = O_RDWR;
+  else if (pflags & SILC_SFTP_FXF_READ)
+    flags = O_RDONLY;
+  else if (pflags & SILC_SFTP_FXF_WRITE)
+    flags = O_WRONLY;
+  if (pflags & SILC_SFTP_FXF_APPEND)
+    flags |= O_APPEND;
+
+  /* Attempt to open the file for real. */
+  fd = open(entry->data + 7, flags, 
+           (attrs->flags & SILC_SFTP_ATTR_PERMISSIONS ?
+            attrs->permissions : 0600));
+  if (fd == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  /* File opened, return handle */
+  handle = mem_create_handle(fs, fd, entry);
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle, 
+             callback_context);
+}
+
+void mem_close(void *context, SilcSFTP sftp,
+              SilcSFTPHandle handle,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  int ret;
+
+  if (h->fd != -1) {
+    ret = close(h->fd);
+    if (ret == -1) {
+      (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL, 
+                 callback_context);
+      return;
+    }
+  }
+
+  mem_del_handle(fs, h);
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
+}
+
+void mem_read(void *context, SilcSFTP sftp,
+             SilcSFTPHandle handle,
+             uint64 offset, 
+             uint32 len,
+             SilcSFTPDataCallback callback,
+             void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  unsigned char *data;
+  int ret;
+
+  if (len > 32768)
+    len = 32768;
+
+  data = silc_malloc(len);
+  lseek(h->fd, (off_t)offset, SEEK_SET);
+
+  /* Attempt to read */
+  ret = read(h->fd, data, len);
+  if (ret <= 0) {
+    if (!ret)
+      (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, 0, callback_context);
+    else
+      (*callback)(sftp, silc_sftp_map_errno(errno), NULL, 0, callback_context);
+    silc_free(data);
+    return;
+  }
+
+  /* Return data */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const unsigned char *)data, 
+             ret, callback_context);
+
+  silc_free(data);
+}
+
+void mem_write(void *context, SilcSFTP sftp,
+              SilcSFTPHandle handle,
+              uint64 offset,
+              const unsigned char *data,
+              uint32 data_len,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  int ret;
+
+  lseek(h->fd, (off_t)offset, SEEK_SET);
+
+  /* Attempt to write */
+  ret = write(h->fd, data, data_len);
+  if (ret <= 0) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, NULL, 
+               callback_context);
+    return;
+  }
+
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, NULL, NULL, callback_context);
+}
+
+void mem_remove(void *context, SilcSFTP sftp,
+               const char *filename,
+               SilcSFTPStatusCallback callback,
+               void *callback_context)
+{
+  /* Remove is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_rename(void *context, SilcSFTP sftp,
+               const char *oldname,
+               const char *newname,
+               SilcSFTPStatusCallback callback,
+               void *callback_context)
+{
+  /* Rename is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_mkdir(void *context, SilcSFTP sftp,
+              const char *path,
+              SilcSFTPAttributes attrs,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  /* Mkdir is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_rmdir(void *context, SilcSFTP sftp,
+              const char *path,
+              SilcSFTPStatusCallback callback,
+              void *callback_context)
+{
+  /* Rmdir is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_opendir(void *context, SilcSFTP sftp,
+                const char *path,
+                SilcSFTPHandleCallback callback,
+                void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  MemFSFileHandle handle;
+
+  if (!path || !strlen(path))
+    path = (const char *)DIR_SEPARATOR;
+
+  /* Find such directory */
+  entry = mem_find_entry_path(fs->root, path);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (!entry->directory) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Must be read permissions to open a directory */
+  if (!(entry->perm & SILC_SFTP_FS_PERM_READ)) {
+    (*callback)(sftp, SILC_SFTP_STATUS_PERMISSION_DENIED, NULL, 
+               callback_context);
+    return;
+  }
+
+  /* Directory opened, return handle */
+  handle = mem_create_handle(fs, 0, entry);
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (SilcSFTPHandle)handle, 
+             callback_context);
+}
+
+void mem_readdir(void *context, SilcSFTP sftp,
+                SilcSFTPHandle handle,
+                SilcSFTPNameCallback callback,
+                void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  MemFSEntry entry;
+  SilcSFTPName name;
+  SilcSFTPAttributes attrs;
+  int i;
+  char long_name[256];
+  unsigned long filesize = 0;
+  char *date;
+  struct stat stats;
+
+  if (!h->entry->directory) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }
+
+  if (h->fd == -1) {
+    (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
+    return;
+  }
+
+  name = silc_calloc(1, sizeof(*name));
+  for (i = h->fd; i < 100 + h->fd; i++) {
+    if (i >= h->entry->entry_count)
+      break;
+
+    entry = h->entry->entry[i];
+    if (!entry)
+      continue;
+
+    filesize = sizeof(*entry);
+    memset(long_name, 0, sizeof(long_name));
+
+    date = ctime(&entry->created);
+    if (strrchr(date, ':'))
+      *strrchr(date, ':') = '\0';
+
+    if (!entry->directory)
+#ifndef SILC_WIN32
+               if (!lstat(entry->data + 7, &stats))
+#else
+               if (!stat(entry->data + 7, &stats))
+#endif
+                       filesize = stats.st_size;
+
+    /* Long name format is:
+       drwx------   1   324210 Apr  8 08:40 mail/
+       1234567890 123 12345678 123456789012 */
+    snprintf(long_name, sizeof(long_name) - 1,
+            "%c%c%c%c------ %3d %8lu %12s %s%s",
+            (entry->directory ? 'd' : '-'),
+            ((entry->perm & SILC_SFTP_FS_PERM_READ) ? 'r' : '-'),
+            ((entry->perm & SILC_SFTP_FS_PERM_WRITE) ? 'w' : '-'),
+            ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? 'x' : '-'),
+            (entry->directory ? (int)entry->entry_count : 1),
+            filesize, date, entry->name,
+            (entry->directory ? "/" : 
+             ((entry->perm & SILC_SFTP_FS_PERM_EXEC) ? "*" : "")));
+
+    /* Add attributes */
+    attrs = silc_calloc(1, sizeof(*attrs));
+    attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                   SILC_SFTP_ATTR_UIDGID);
+    attrs->size = filesize;
+    attrs->uid = 0;                /* We use always 0 UID and GID */
+    attrs->gid = 0;
+    if (!entry->directory) {
+      attrs->flags |= SILC_SFTP_ATTR_ACMODTIME;
+      attrs->atime = stats.st_atime;
+      attrs->mtime = stats.st_mtime;
+    }
+
+    /* Add the name */
+    silc_sftp_name_add(name, entry->name, long_name, attrs);
+  }
+
+  /* If we didn't read all then udpate the index for next read */
+  if (i >= h->entry->entry_count)
+    h->fd = -1;
+  else
+    h->fd = i;
+
+  /* If names was not found then return EOF. */
+  if (name->count == 0) {
+    (*callback)(sftp, SILC_SFTP_STATUS_EOF, NULL, callback_context);
+    silc_sftp_name_free(name);
+    return;
+  }
+
+  /* Return name(s) */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPName)name,
+             callback_context);
+
+  silc_sftp_name_free(name);
+}
+
+void mem_stat(void *context, SilcSFTP sftp,
+             const char *path,
+             SilcSFTPAttrCallback callback,
+             void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  SilcSFTPAttributes attrs;
+  int ret;
+  struct stat stats;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  /* Find such directory */
+  entry = mem_find_entry_path(fs->root, path);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (entry->directory || !entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Get real stat */
+  ret = stat(entry->data + 7, &stats);
+  if (ret == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  attrs = silc_calloc(1, sizeof(*attrs));
+  attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                 SILC_SFTP_ATTR_UIDGID |
+                 SILC_SFTP_ATTR_ACMODTIME);
+  attrs->size = stats.st_size;
+  attrs->uid = 0;                  /* We use always 0 UID and GID */
+  attrs->gid = 0;
+  attrs->atime = stats.st_atime;
+  attrs->mtime = stats.st_mtime;
+
+  /* Return attributes */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
+             callback_context);
+
+  silc_sftp_attr_free(attrs);
+}
+
+void mem_lstat(void *context, SilcSFTP sftp,
+              const char *path,
+              SilcSFTPAttrCallback callback,
+              void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  MemFSEntry entry;
+  SilcSFTPAttributes attrs;
+  int ret;
+  struct stat stats;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  /* Find such directory */
+  entry = mem_find_entry_path(fs->root, path);
+  if (!entry) {
+    (*callback)(sftp, SILC_SFTP_STATUS_NO_SUCH_FILE, NULL, callback_context);
+    return;
+  }
+
+  if (entry->directory || !entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Get real stat */
+#ifndef SILC_WIN32
+  ret = lstat(entry->data + 7, &stats);
+#else
+  ret = stat(entry->data + 7, &stats);
+#endif
+  if (ret == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  attrs = silc_calloc(1, sizeof(*attrs));
+  attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                 SILC_SFTP_ATTR_UIDGID |
+                 SILC_SFTP_ATTR_ACMODTIME);
+  attrs->size = stats.st_size;
+  attrs->uid = 0;                  /* We use always 0 UID and GID */
+  attrs->gid = 0;
+  attrs->atime = stats.st_atime;
+  attrs->mtime = stats.st_mtime;
+
+  /* Return attributes */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
+             callback_context);
+
+  silc_sftp_attr_free(attrs);
+}
+
+void mem_fstat(void *context, SilcSFTP sftp,
+              SilcSFTPHandle handle,
+              SilcSFTPAttrCallback callback,
+              void *callback_context)
+{
+  MemFSFileHandle h = (MemFSFileHandle)handle;
+  SilcSFTPAttributes attrs;
+  int ret;
+  struct stat stats;
+
+  if (h->entry->directory || !h->entry->data) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }    
+
+  /* Get real stat */
+  ret = fstat(h->fd, &stats);
+  if (ret == -1) {
+    (*callback)(sftp, silc_sftp_map_errno(errno), NULL, callback_context);
+    return;
+  }
+
+  attrs = silc_calloc(1, sizeof(*attrs));
+  attrs->flags = (SILC_SFTP_ATTR_SIZE |
+                 SILC_SFTP_ATTR_UIDGID |
+                 SILC_SFTP_ATTR_ACMODTIME);
+  attrs->size = stats.st_size;
+  attrs->uid = 0;                  /* We use always 0 UID and GID */
+  attrs->gid = 0;
+  attrs->atime = stats.st_atime;
+  attrs->mtime = stats.st_mtime;
+
+  /* Return attributes */
+  (*callback)(sftp, SILC_SFTP_STATUS_OK, (const SilcSFTPAttributes)attrs, 
+             callback_context);
+
+  silc_sftp_attr_free(attrs);
+}
+     
+void mem_setstat(void *context, SilcSFTP sftp,
+                const char *path,
+                SilcSFTPAttributes attrs,
+                SilcSFTPStatusCallback callback,
+                void *callback_context)
+{
+  /* Setstat is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_fsetstat(void *context, SilcSFTP sftp,
+                 SilcSFTPHandle handle,
+                 SilcSFTPAttributes attrs,
+                 SilcSFTPStatusCallback callback,
+                 void *callback_context)
+{
+  /* Fsetstat is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_readlink(void *context, SilcSFTP sftp,
+                 const char *path,
+                 SilcSFTPNameCallback callback,
+                 void *callback_context)
+{
+  /* Readlink is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL,
+             callback_context);
+}
+
+void mem_symlink(void *context, SilcSFTP sftp,
+                const char *linkpath,
+                const char *targetpath,
+                SilcSFTPStatusCallback callback,
+                void *callback_context)
+{
+  /* Symlink is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, NULL, 
+             callback_context);
+}
+
+void mem_realpath(void *context, SilcSFTP sftp,
+                 const char *path,
+                 SilcSFTPNameCallback callback,
+                 void *callback_context)
+{
+  MemFS fs = (MemFS)context;
+  char *realpath;
+  SilcSFTPName name;
+
+  if (!path || !strlen(path))
+    path = (const char *)strdup("/");
+
+  realpath = mem_expand_path(fs->root, path);
+  if (!realpath) {
+    (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, NULL, callback_context);
+    return;
+  }
+
+  name = silc_calloc(1, sizeof(*name));
+  name->filename = silc_calloc(1, sizeof(*name->filename));
+  name->filename[0] = realpath;
+  name->long_filename = silc_calloc(1, sizeof(*name->long_filename));
+  name->long_filename[0] = realpath;
+  name->attrs = silc_calloc(1, sizeof(*name->attrs));
+  name->attrs[0] = silc_calloc(1, sizeof(*name->attrs[0]));
+  name->count = 1;
+
+  (*callback)(sftp, SILC_SFTP_STATUS_FAILURE, (const SilcSFTPName)name, 
+             callback_context);
+
+  silc_sftp_name_free(name);
+}
+
+void mem_extended(void *context, SilcSFTP sftp,
+                 const char *request,
+                 const unsigned char *data,
+                 uint32 data_len,
+                 SilcSFTPExtendedCallback callback,
+                 void *callback_context)
+{
+  /* Extended is not supported */
+  (*callback)(sftp, SILC_SFTP_STATUS_OP_UNSUPPORTED, NULL, 0, 
+             callback_context);
+}
+
+struct SilcSFTPFilesystemOpsStruct silc_sftp_fs_memory = {
+  mem_get_handle,
+  mem_encode_handle,
+  mem_open,
+  mem_close,
+  mem_read,
+  mem_write,
+  mem_remove,
+  mem_rename,
+  mem_mkdir,
+  mem_rmdir,
+  mem_opendir,
+  mem_readdir,
+  mem_stat,
+  mem_lstat,
+  mem_fstat,
+  mem_setstat,
+  mem_fsetstat,
+  mem_readlink,
+  mem_symlink,
+  mem_realpath,
+  mem_extended
+};
diff --git a/lib/silcsftp/sftp_server.c b/lib/silcsftp/sftp_server.c
new file mode 100644 (file)
index 0000000..09f636c
--- /dev/null
@@ -0,0 +1,1028 @@
+/*
+
+  sftp_server.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "silcsftp_fs.h"
+#include "sftp_util.h"
+
+/* SFTP Server context */
+typedef struct {
+  SilcSocketConnection sock;
+  SilcSFTPSendPacketCallback send_packet;
+  void *send_context;
+  SilcSFTPMonitors monitors;
+  SilcSFTPMonitor monitor;
+  void *monitor_context;
+  SilcSFTPFilesystem fs;
+  SilcBuffer packet;
+} *SilcSFTPServer;
+
+/* General routine to send SFTP packet to the SFTP client. */
+
+static void silc_sftp_send_packet(SilcSFTPServer sftp,
+                                 SilcSFTPPacket type, 
+                                 uint32 len, ...)
+{
+  SilcBuffer tmp;
+  va_list vp;
+
+  va_start(vp, len);
+  tmp = silc_sftp_packet_encode_vp(type, sftp->packet, len, vp);
+  va_end(vp);
+  if (!tmp)
+    return;
+  sftp->packet = tmp;
+
+  SILC_LOG_HEXDUMP(("SFTP packet to client"), sftp->packet->data, 
+                  sftp->packet->len);
+
+  /* Send the packet */
+  (*sftp->send_packet)(sftp->sock, sftp->packet, sftp->send_context);
+
+  /* Clear packet */
+  sftp->packet->data = sftp->packet->tail = sftp->packet->head;
+  sftp->packet->len = 0;
+}
+
+/* Sends error to the client */
+
+static void silc_sftp_send_error(SilcSFTPServer sftp,
+                                SilcSFTPStatus status,
+                                uint32 id)
+{
+  SILC_LOG_DEBUG(("Send error %d", status));
+
+  silc_sftp_send_packet(sftp, SILC_SFTP_STATUS, 16,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(status),
+                       SILC_STR_UI_INT(0),      /* Error */
+                       SILC_STR_UI_INT(0),      /* Language tag */
+                       SILC_STR_END);
+}
+
+/* Status callback */
+
+static void silc_sftp_server_status(SilcSFTP sftp,
+                                   SilcSFTPStatus status,
+                                   const char *message,
+                                   const char *language_tag,
+                                   void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  int mlen, llen;
+
+  SILC_LOG_DEBUG(("Status callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+  
+  if (!message)
+    message = "";
+  if (!language_tag)
+    language_tag = "";
+  mlen = strlen(message);
+  llen = strlen(language_tag);
+
+  silc_sftp_send_packet(server, SILC_SFTP_STATUS, 16 + mlen + llen,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(status),
+                       SILC_STR_UI_INT(mlen),
+                       SILC_STR_UI32_STRING(message),
+                       SILC_STR_UI_INT(llen),
+                       SILC_STR_UI32_STRING(language_tag),
+                       SILC_STR_END);
+}
+
+/* Handle callback */
+
+static void silc_sftp_server_handle(SilcSFTP sftp,
+                                   SilcSFTPStatus status,
+                                   SilcSFTPHandle handle,
+                                   void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  unsigned char *hdata;
+  uint32 hdata_len;
+
+  SILC_LOG_DEBUG(("Handle callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  hdata = server->fs->fs->sftp_encode_handle(server->fs->fs_context, sftp,
+                                            handle, &hdata_len);
+  if (!hdata) {
+    silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_HANDLE, 8 + hdata_len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(hdata_len),
+                       SILC_STR_UI_XNSTRING(hdata, hdata_len),
+                       SILC_STR_END);
+}
+
+/* Data callback */
+
+static void silc_sftp_server_data(SilcSFTP sftp,
+                                 SilcSFTPStatus status,
+                                 const unsigned char *data,
+                                 uint32 data_len,
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+
+  SILC_LOG_DEBUG(("Data callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_DATA, 8 + data_len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_INT(data_len),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
+
+/* Name callback */
+
+static void silc_sftp_server_name(SilcSFTP sftp,
+                                 SilcSFTPStatus status,
+                                 const SilcSFTPName name,
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  SilcBuffer namebuf;
+
+  SILC_LOG_DEBUG(("Name callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  namebuf = silc_sftp_name_encode(name);
+  if (!namebuf) {
+    silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_NAME, 4 + namebuf->len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_XNSTRING(namebuf->data, namebuf->len),
+                       SILC_STR_END);
+}
+
+/* Attributes callback */
+
+static void silc_sftp_server_attr(SilcSFTP sftp,
+                                 SilcSFTPStatus status,
+                                 const SilcSFTPAttributes attrs,
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+  SilcBuffer attr_buf;
+
+  SILC_LOG_DEBUG(("Attr callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  attr_buf = silc_sftp_attr_encode(attrs);
+
+  silc_sftp_send_packet(server, SILC_SFTP_ATTRS, 4 + attr_buf->len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_XNSTRING(attr_buf->data, attr_buf->len),
+                       SILC_STR_END);
+
+  silc_buffer_free(attr_buf);
+}
+
+/* Extended callback */
+
+static void silc_sftp_server_extended(SilcSFTP sftp,
+                                     SilcSFTPStatus status,
+                                     const unsigned char *data,
+                                     uint32 data_len,
+                                     void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  uint32 id = (uint32)context;
+
+  SILC_LOG_DEBUG(("Extended callback"));
+  SILC_LOG_DEBUG(("Request ID: %d", id));
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    silc_sftp_send_error(server, status, id);
+    return;
+  }
+
+  silc_sftp_send_packet(server, SILC_SFTP_EXTENDED, 4 + data_len,
+                       SILC_STR_UI_INT(id),
+                       SILC_STR_UI_XNSTRING(data, data_len),
+                       SILC_STR_END);
+}
+
+/* Starts SFTP server by associating the socket connection `sock' to the
+   created SFTP server context.  This function returns the allocated
+   SFTP client context or NULL on error. The `send_packet' is called
+   by the library when it needs to send a packet. The `fs' is the
+   structure containing filesystem access callbacks. */
+
+SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context,
+                               SilcSFTPFilesystem fs)
+{
+  SilcSFTPServer server;
+
+  server = silc_calloc(1, sizeof(*server));
+  server->sock = sock;
+  server->send_packet = send_packet;
+  server->send_context = send_context;
+  server->fs = fs;
+
+  SILC_LOG_DEBUG(("Starting SFTP server %p", server));
+
+  return (SilcSFTP)server;
+}
+
+/* Shutdown's the SFTP server.  The caller is responsible of closing
+   the associated socket connection.  The SFTP context is freed and is
+   invalid after this function returns. */
+
+void silc_sftp_server_shutdown(SilcSFTP sftp)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+
+  SILC_LOG_DEBUG(("Stopping SFTP server %p", server));
+
+  if (server->packet)
+    silc_buffer_free(server->packet);
+  silc_free(server);
+}
+
+/* Sets monitor callback */
+
+void silc_sftp_server_set_monitor(SilcSFTP sftp,
+                                 SilcSFTPMonitors monitors,
+                                 SilcSFTPMonitor monitor, 
+                                 void *context)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  server->monitors = monitors;
+  server->monitor = monitor;
+  server->monitor_context = context;
+}
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+
+void silc_sftp_server_receive_process(SilcSFTP sftp,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet)
+{
+  SilcSFTPServer server = (SilcSFTPServer)sftp;
+  SilcSFTPPacket type;
+  char *filename = NULL, *path = NULL;
+  const unsigned char *payload = NULL;
+  uint32 payload_len;
+  int ret;
+  SilcBufferStruct buf;
+  uint32 id;
+  SilcSFTPAttributes attrs;
+  SilcSFTPHandle handle;
+  SilcSFTPMonitorDataStruct mdata;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Parse the packet */
+  type = silc_sftp_packet_decode(packet->buffer, (unsigned char **)&payload, 
+                                &payload_len);
+  if (!type)
+    return;
+
+  silc_buffer_set(&buf, (unsigned char *)payload, payload_len);
+
+  memset(&mdata, 0, sizeof(mdata));
+
+  switch (type) {
+  case SILC_SFTP_INIT:
+    {
+      SilcSFTPVersion version;
+
+      SILC_LOG_DEBUG(("Init request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&version),
+                                SILC_STR_END);
+      if (ret < 0)
+       break;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_INIT && server->monitor) {
+       mdata.version = version;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_INIT, &mdata,
+                          server->monitor_context);
+      }
+
+      silc_sftp_send_packet(server, SILC_SFTP_VERSION, 4,
+                           SILC_STR_UI_INT(SILC_SFTP_PROTOCOL_VERSION),
+                           SILC_STR_END);
+    }
+    break;
+
+  case SILC_SFTP_OPEN:
+    {
+      SilcSFTPFileOperation pflags;
+      unsigned char *attr_buf;
+      uint32 attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Open request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&filename),
+                                SILC_STR_UI_INT(&pflags),
+                                SILC_STR_UI32_NSTRING(&attr_buf, 
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_OPEN && server->monitor) {
+       mdata.name = filename;
+       mdata.pflags = pflags;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_OPEN, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Open operation */
+      server->fs->fs->sftp_open(server->fs->fs_context, sftp, filename, pflags,
+                               attrs, silc_sftp_server_handle, (void *)id);
+
+      silc_free(filename);
+      silc_sftp_attr_free(attrs);
+    }
+    break;
+
+  case SILC_SFTP_CLOSE:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      SILC_LOG_DEBUG(("Close request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
+                                              (const unsigned char *)hdata,
+                                              hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_CLOSE && server->monitor) {
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_CLOSE, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Close operation */
+      server->fs->fs->sftp_close(server->fs->fs_context, sftp, handle,
+                                silc_sftp_server_status, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_READ:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+      uint64 offset;
+      uint32 len;
+
+      SILC_LOG_DEBUG(("Read request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_UI_INT64(&offset),
+                                SILC_STR_UI_INT(&len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
+                                              (const unsigned char *)hdata,
+                                              hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_READ && server->monitor) {
+       mdata.offset = offset;
+       mdata.data_len = len;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_READ, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Read operation */
+      server->fs->fs->sftp_read(server->fs->fs_context, sftp, 
+                               handle, offset, len,
+                               silc_sftp_server_data, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_WRITE:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+      uint64 offset;
+      unsigned char *data;
+      uint32 data_len;
+
+      SILC_LOG_DEBUG(("Read request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_UI_INT64(&offset),
+                                SILC_STR_UI32_NSTRING(&data, 
+                                                      &data_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
+                                              (const unsigned char *)hdata,
+                                              hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_WRITE && server->monitor) {
+       mdata.offset = offset;
+       mdata.data_len = data_len;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_WRITE, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Write operation */
+      server->fs->fs->sftp_write(server->fs->fs_context, sftp, handle, offset, 
+                                (const unsigned char *)data, data_len,
+                                silc_sftp_server_status, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_REMOVE:
+    {
+      SILC_LOG_DEBUG(("Remove request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&filename),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_REMOVE && server->monitor) {
+       mdata.name = filename;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_REMOVE, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Remove operation */
+      server->fs->fs->sftp_remove(server->fs->fs_context, sftp, filename,
+                                 silc_sftp_server_status, (void *)id);
+
+      silc_free(filename);
+    }
+    break;
+
+  case SILC_SFTP_RENAME:
+    {
+      char *newname = NULL;
+
+      SILC_LOG_DEBUG(("Rename request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&filename),
+                                SILC_STR_UI32_STRING_ALLOC(&newname),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_RENAME && server->monitor) {
+       mdata.name = filename;
+       mdata.name2 = newname;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_RENAME, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Rename operation */
+      server->fs->fs->sftp_rename(server->fs->fs_context, sftp, 
+                                 filename, newname,
+                                 silc_sftp_server_status, (void *)id);
+
+      silc_free(filename);
+      silc_free(newname);
+    }
+    break;
+
+  case SILC_SFTP_MKDIR:
+    {
+      unsigned char *attr_buf;
+      uint32 attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Mkdir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_UI32_NSTRING(&attr_buf,
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_MKDIR && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_MKDIR, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Mkdir operation */
+      server->fs->fs->sftp_mkdir(server->fs->fs_context, sftp, path, attrs,
+                                silc_sftp_server_status, (void *)id);
+
+      silc_sftp_attr_free(attrs);
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_RMDIR:
+    {
+      SILC_LOG_DEBUG(("Rmdir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_RMDIR && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_RMDIR, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Rmdir operation */
+      server->fs->fs->sftp_rmdir(server->fs->fs_context, sftp, path,
+                                silc_sftp_server_status, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_OPENDIR:
+    {
+      SILC_LOG_DEBUG(("Opendir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_OPENDIR && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_OPENDIR, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Opendir operation */
+      server->fs->fs->sftp_opendir(server->fs->fs_context, sftp, path,
+                                  silc_sftp_server_handle, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_READDIR:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      SILC_LOG_DEBUG(("Readdir request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
+                                              (const unsigned char *)hdata,
+                                              hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_READDIR && server->monitor) {
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_READDIR, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Readdir operation */
+      server->fs->fs->sftp_readdir(server->fs->fs_context, sftp, handle,
+                                  silc_sftp_server_name, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_STAT:
+    {
+      SILC_LOG_DEBUG(("Stat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_STAT && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_STAT, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Stat operation */
+      server->fs->fs->sftp_stat(server->fs->fs_context, sftp, path,
+                               silc_sftp_server_attr, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_LSTAT:
+    {
+      SILC_LOG_DEBUG(("Lstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_LSTAT && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_LSTAT, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Lstat operation */
+      server->fs->fs->sftp_lstat(server->fs->fs_context, sftp, path,
+                                silc_sftp_server_attr, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_FSTAT:
+    {
+      unsigned char *hdata;
+      uint32 hdata_len;
+
+      SILC_LOG_DEBUG(("Fstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Get the handle */
+      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
+                                              (const unsigned char *)hdata,
+                                              hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_FSTAT && server->monitor) {
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_FSTAT, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Fstat operation */
+      server->fs->fs->sftp_fstat(server->fs->fs_context, sftp, handle,
+                                silc_sftp_server_attr, (void *)id);
+    }
+    break;
+
+  case SILC_SFTP_SETSTAT:
+    {
+      unsigned char *attr_buf;
+      uint32 attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Setstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_UI32_NSTRING(&attr_buf,
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_SETSTAT && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_SETSTAT, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Setstat operation */
+      server->fs->fs->sftp_setstat(server->fs->fs_context, sftp, path, attrs,
+                                  silc_sftp_server_status, (void *)id);
+
+      silc_sftp_attr_free(attrs);
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_FSETSTAT:
+    {
+      unsigned char *hdata, *attr_buf;
+      uint32 hdata_len, attr_len = 0;
+      SilcBufferStruct tmpbuf;
+
+      SILC_LOG_DEBUG(("Fsetstat request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_NSTRING(&hdata, 
+                                                      &hdata_len),
+                                SILC_STR_UI32_NSTRING(&attr_buf,
+                                                      &attr_len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      if (attr_len) {
+       silc_buffer_set(&tmpbuf, attr_buf, attr_len);
+       attrs = silc_sftp_attr_decode(&tmpbuf);
+      } else {
+       attrs = silc_calloc(1, sizeof(*attrs));
+      }
+
+      /* Get the handle */
+      handle = server->fs->fs->sftp_get_handle(server->fs->fs_context, sftp,
+                                              (const unsigned char *)hdata,
+                                              hdata_len);
+      if (!handle) {
+       silc_sftp_send_error(server, SILC_SFTP_STATUS_NO_SUCH_FILE, id);
+       break;
+      }
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_FSETSTAT && server->monitor) {
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_FSETSTAT, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Fsetstat operation */
+      server->fs->fs->sftp_fsetstat(server->fs->fs_context, sftp, 
+                                   handle, attrs,
+                                   silc_sftp_server_status, (void *)id);
+
+      silc_sftp_attr_free(attrs);
+    }
+    break;
+
+  case SILC_SFTP_READLINK:
+    {
+      SILC_LOG_DEBUG(("Readlink request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_READLINK && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_READLINK, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Readlink operation */
+      server->fs->fs->sftp_readlink(server->fs->fs_context, sftp, path,
+                                   silc_sftp_server_name, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_SYMLINK:
+    {
+      char *target = NULL;
+
+      SILC_LOG_DEBUG(("Symlink request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_UI32_STRING_ALLOC(&target),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_SYMLINK && server->monitor) {
+       mdata.name = path;
+       mdata.name2 = target;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_SYMLINK, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Symlink operation */
+      server->fs->fs->sftp_symlink(server->fs->fs_context, sftp, path, target,
+                                  silc_sftp_server_status, (void *)id);
+
+      silc_free(path);
+      silc_free(target);
+    }
+    break;
+
+  case SILC_SFTP_REALPATH:
+    {
+      SILC_LOG_DEBUG(("Realpath request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&path),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_REALPATH && server->monitor) {
+       mdata.name = path;
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_REALPATH, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Realpath operation */
+      server->fs->fs->sftp_realpath(server->fs->fs_context, sftp, path,
+                                   silc_sftp_server_name, (void *)id);
+
+      silc_free(path);
+    }
+    break;
+
+  case SILC_SFTP_EXTENDED:
+    {
+      char *request = NULL;
+      unsigned char *data;
+      uint32 data_len;
+
+      SILC_LOG_DEBUG(("Extended request"));
+
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_INT(&id),
+                                SILC_STR_UI32_STRING_ALLOC(&request),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+
+      data_len = 8 + strlen(request);
+      silc_buffer_pull(&buf, data_len);
+      ret = silc_buffer_unformat(&buf,
+                                SILC_STR_UI_XNSTRING(&data, buf.len),
+                                SILC_STR_END);
+      if (ret < 0)
+       goto failure;
+      data_len = buf.len;
+
+      /* Call monitor */
+      if (server->monitors & SILC_SFTP_MONITOR_EXTENDED && server->monitor) {
+       (*server->monitor)(sftp, SILC_SFTP_MONITOR_EXTENDED, &mdata,
+                          server->monitor_context);
+      }
+
+      /* Extended operation */
+      server->fs->fs->sftp_extended(server->fs->fs_context, sftp, 
+                                   request, data, data_len,
+                                   silc_sftp_server_extended, (void *)id);
+
+      silc_free(request);
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  return;
+
+ failure:
+  silc_sftp_send_error(server, SILC_SFTP_STATUS_FAILURE, id);
+}
diff --git a/lib/silcsftp/sftp_util.c b/lib/silcsftp/sftp_util.c
new file mode 100644 (file)
index 0000000..5e3c526
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+
+  sftp_util.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+#include "sftp_util.h"
+
+/* Encodes a SFTP packet of type `packet' of length `len'. The variable
+   argument list is encoded as data payload to the buffer. Returns the
+   encoded packet or NULL on error. The caller must free the returned
+   buffer. If `packet_buf' is non-NULL then the new packet data is put
+   to that buffer instead of allocating new one.  If the new data cannot
+   fit to `packet_buf' will be reallocated. */
+
+SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, 
+                                  SilcBuffer packet_buf, uint32 len, ...)
+{
+  SilcBuffer buffer;
+  va_list vp;
+
+  va_start(vp, len);
+  buffer = silc_sftp_packet_encode_vp(packet, packet_buf, len, vp);
+  va_end(vp);
+
+  return buffer;
+}
+
+/* Same as silc_sftp_packet_encode but takes the variable argument list
+   pointer as argument. */
+
+SilcBuffer silc_sftp_packet_encode_vp(SilcSFTPPacket packet, 
+                                     SilcBuffer packet_buf, uint32 len, 
+                                     va_list vp)
+{
+  SilcBuffer buffer;
+  bool dyn;
+  int ret;
+
+  if (packet_buf) {
+    if (packet_buf->truelen < 4 + 1 + len)
+      packet_buf = silc_buffer_realloc(packet_buf, 4 + 1 + len);
+
+    buffer = packet_buf;
+    dyn = FALSE;
+  } else {
+    buffer = silc_buffer_alloc(4 + 1 + len);
+    dyn = TRUE;
+  }
+
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_INT(len),
+                    SILC_STR_UI_CHAR(packet),
+                    SILC_STR_END);
+  silc_buffer_pull(buffer, 5);
+
+  ret = silc_buffer_format_vp(buffer, vp);
+  if (ret < 0) {
+    if (dyn)
+      silc_buffer_free(buffer);
+    return NULL;
+  }
+
+  silc_buffer_push(buffer, 5);
+
+  return buffer;
+}
+
+/* Decodes the SFTP packet data `packet' and return the SFTP packet type.
+   The payload of the packet is returned to the `payload' pointer. Returns
+   0 if error occurred during decoding. */
+
+SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
+                                      unsigned char **payload,
+                                      uint32 *payload_len)
+{
+  uint32 len;
+  uint8 type;
+  int ret;
+
+  ret = silc_buffer_unformat(packet,
+                            SILC_STR_UI_INT(&len),
+                            SILC_STR_UI_CHAR(&type),
+                            SILC_STR_END);
+  if (ret < 0)
+    return 0;
+
+  if (type < SILC_SFTP_INIT || type > SILC_SFTP_EXTENDED_REPLY)
+    return 0;
+
+  if (len > (packet->len - 5))
+    return 0;
+
+  silc_buffer_pull(packet, 5);
+  ret = silc_buffer_unformat(packet, 
+                            SILC_STR_UI_XNSTRING(payload, len),
+                            SILC_STR_END);
+  if (ret < 0)
+    return 0;
+
+  silc_buffer_push(packet, 5);
+
+  *payload_len = len;
+
+  return (SilcSFTPPacket)type;
+}
+
+/* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
+   The caller must free the buffer. */
+
+SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr)
+{
+  SilcBuffer buffer;
+  int i, ret, len = 4;
+
+  if (attr->flags & SILC_SFTP_ATTR_SIZE)
+    len += 8;
+  if (attr->flags & SILC_SFTP_ATTR_UIDGID)
+    len += 8;
+  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS)
+    len += 4;
+  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME)
+    len += 8;
+  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
+    len += 4;
+    for (i = 0; i < attr->extended_count; i++) {
+      len += 8;
+      len += attr->extended_type[i]->len;
+      len += attr->extended_data[i]->len;
+    }
+  }
+
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  silc_buffer_format(buffer, 
+                    SILC_STR_UI_INT(attr->flags), 
+                    SILC_STR_END);
+  silc_buffer_pull(buffer, 4);
+
+  if (attr->flags & SILC_SFTP_ATTR_SIZE) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT64(attr->size), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->uid), 
+                      SILC_STR_UI_INT(attr->gid), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->permissions), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 4);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->atime), 
+                      SILC_STR_UI_INT(attr->mtime), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
+    silc_buffer_format(buffer, 
+                      SILC_STR_UI_INT(attr->extended_count), 
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, 4);
+
+    for (i = 0; i < attr->extended_count; i++) {
+      ret = 
+       silc_buffer_format(buffer, 
+                          SILC_STR_UI_INT(attr->extended_type[i]->len),
+                          SILC_STR_UI_XNSTRING(attr->extended_type[i]->data,
+                                               attr->extended_type[i]->len),
+                          SILC_STR_UI_INT(attr->extended_data[i]->len),
+                          SILC_STR_UI_XNSTRING(attr->extended_data[i]->data,
+                                               attr->extended_data[i]->len),
+                          SILC_STR_END);
+      silc_buffer_pull(buffer, ret);
+    }
+  }
+
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
+/* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
+   attributes that the caller must free or NULL on error. */
+
+SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer)
+{
+  SilcSFTPAttributes attr;
+
+  attr = silc_calloc(1, sizeof(*attr));
+
+  if (silc_buffer_unformat(buffer, 
+                          SILC_STR_UI_INT(&attr->flags), 
+                          SILC_STR_END) < 0)
+    goto out;
+
+  silc_buffer_pull(buffer, 4);
+
+  if (attr->flags & SILC_SFTP_ATTR_SIZE) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT64(&attr->size), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_UIDGID) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->uid), 
+                            SILC_STR_UI_INT(&attr->gid), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_PERMISSIONS) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->permissions), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 4);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_ACMODTIME) {
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->atime), 
+                            SILC_STR_UI_INT(&attr->mtime), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 8);
+  }
+
+  if (attr->flags & SILC_SFTP_ATTR_EXTENDED) {
+    int i;
+
+    if (silc_buffer_unformat(buffer, 
+                            SILC_STR_UI_INT(&attr->extended_count), 
+                            SILC_STR_END) < 0)
+      goto out;
+
+    silc_buffer_pull(buffer, 4);
+
+    attr->extended_type = silc_calloc(attr->extended_count, 
+                                     sizeof(*attr->extended_type));
+    attr->extended_data = silc_calloc(attr->extended_count, 
+                                     sizeof(*attr->extended_data));
+    for (i = 0; i < attr->extended_count; i++) {
+      unsigned char *tmp, *tmp2;
+      uint32 tmp_len, tmp2_len;
+
+      if (silc_buffer_unformat(buffer, 
+                              SILC_STR_UI32_NSTRING(&tmp, &tmp_len),
+                              SILC_STR_UI32_NSTRING(&tmp2, &tmp2_len),
+                              SILC_STR_END) < 0)
+       goto out;
+
+      attr->extended_type[i] = silc_buffer_alloc(tmp_len);
+      attr->extended_data[i] = silc_buffer_alloc(tmp2_len);
+      silc_buffer_put(attr->extended_type[i], tmp, tmp_len);
+      silc_buffer_put(attr->extended_data[i], tmp2, tmp2_len);
+
+      silc_buffer_pull(buffer, tmp_len + 4 + tmp2_len + 4);
+    }
+  }
+
+  return attr;
+
+ out:
+  silc_sftp_attr_free(attr);
+  return NULL;
+}
+
+/* Frees the attributes context and its internals. */
+
+void silc_sftp_attr_free(SilcSFTPAttributes attr)
+{
+  int i;
+
+  for (i = 0; i < attr->extended_count; i++) {
+    silc_buffer_free(attr->extended_type[i]);
+    silc_buffer_free(attr->extended_data[i]);
+  }
+  silc_free(attr->extended_type);
+  silc_free(attr->extended_data);
+  silc_free(attr);
+}
+
+/* Adds an entry to the `name' context. */
+
+void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
+                       const char *long_name, SilcSFTPAttributes attrs)
+{
+  name->filename = silc_realloc(name->filename, sizeof(*name->filename) *
+                               (name->count + 1));
+  name->long_filename = silc_realloc(name->long_filename, 
+                                    sizeof(*name->long_filename) *
+                                    (name->count + 1));
+  name->attrs = silc_realloc(name->attrs, sizeof(*name->attrs) *
+                            (name->count + 1));
+
+  name->filename[name->count] = strdup(short_name);
+  name->long_filename[name->count] = strdup(long_name);
+  name->attrs[name->count] = attrs;
+  name->count++;
+}
+
+/* Encodes the SilcSFTPName to a buffer and returns the allocated buffer. 
+   The caller must free the buffer. */
+
+SilcBuffer silc_sftp_name_encode(SilcSFTPName name)
+{
+  SilcBuffer buffer;
+  int i, len = 4;
+  SilcBuffer *attr_buf;
+
+  attr_buf = silc_calloc(name->count, sizeof(*attr_buf));
+  for (i = 0; i < name->count; i++) {
+    len += (8 + strlen(name->filename[i]) + strlen(name->long_filename[i]));
+    attr_buf[i] = silc_sftp_attr_encode(name->attrs[i]);
+    len += attr_buf[i]->len;
+  }
+
+  buffer = silc_buffer_alloc(len);
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
+
+  silc_buffer_format(buffer,
+                    SILC_STR_UI_INT(name->count),
+                    SILC_STR_END);
+  silc_buffer_pull(buffer, 4);
+
+  for (i = 0; i < name->count; i++) {
+    len =
+      silc_buffer_format(buffer,
+                        SILC_STR_UI_INT(strlen(name->filename[i])),
+                        SILC_STR_UI32_STRING(name->filename[i]),
+                        SILC_STR_UI_INT(strlen(name->long_filename[i])),
+                        SILC_STR_UI32_STRING(name->long_filename[i]),
+                        SILC_STR_UI_XNSTRING(attr_buf[i]->data,
+                                             attr_buf[i]->len),
+                        SILC_STR_END);
+
+    silc_buffer_pull(buffer, len);
+    silc_free(attr_buf[i]);
+  }
+  silc_free(attr_buf);
+
+  silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}
+
+/* Decodes a SilcSFTPName structure from the `buffer' that must include
+   `count' many name, longname and attribute values. Returns the allocated
+   structure or NULL on error. */
+
+SilcSFTPName silc_sftp_name_decode(uint32 count, SilcBuffer buffer)
+{
+  SilcSFTPName name;
+  int i;
+  int ret;
+
+  name = silc_calloc(1, sizeof(*name));
+  name->filename = silc_calloc(count, sizeof(*name->filename));
+  name->long_filename = silc_calloc(count, sizeof(*name->filename));
+  name->attrs = silc_calloc(count, sizeof(*name->attrs));
+  name->count = count;
+
+  for (i = 0; i < count; i++) {
+    ret = 
+      silc_buffer_unformat(buffer,
+                          SILC_STR_UI32_STRING_ALLOC(&name->filename[i]),
+                          SILC_STR_UI32_STRING_ALLOC(&name->long_filename[i]),
+                          SILC_STR_END);
+    if (ret < 0) {
+      silc_sftp_name_free(name);
+      return NULL;
+    }
+
+    silc_buffer_pull(buffer, ret);
+
+    /* Decode attributes, this will pull the `buffer' to correct place
+       for next round automatically. */
+    name->attrs[i] = silc_sftp_attr_decode(buffer);
+  }
+
+  return name;
+}
+
+/* Frees the name context and its internals. */
+
+void silc_sftp_name_free(SilcSFTPName name)
+{
+  int i;
+
+  for (i = 0; i < name->count; i++) {
+    silc_free(name->filename[i]);
+    silc_free(name->long_filename[i]);
+    silc_sftp_attr_free(name->attrs[i]);
+  }
+
+  silc_free(name->filename);
+  silc_free(name->long_filename);
+  silc_free(name->attrs);
+  silc_free(name);
+}
+
+/* Maps errno to SFTP status message. */
+
+SilcSFTPStatus silc_sftp_map_errno(int err)
+{
+  SilcSFTPStatus ret;
+
+  switch (err) {
+  case 0:
+    ret = SILC_SFTP_STATUS_OK;
+    break;
+  case ENOENT:
+  case ENOTDIR:
+  case EBADF:
+    ret = SILC_SFTP_STATUS_NO_SUCH_FILE;
+    break;
+  case EPERM:
+  case EACCES:
+  case EFAULT:
+    ret = SILC_SFTP_STATUS_PERMISSION_DENIED;
+    break;
+  case ENAMETOOLONG:
+  case EINVAL:
+    ret = SILC_SFTP_STATUS_BAD_MESSAGE;
+    break;
+  default:
+    ret = SILC_SFTP_STATUS_FAILURE;
+    break;
+  }
+
+  return ret;
+}
diff --git a/lib/silcsftp/sftp_util.h b/lib/silcsftp/sftp_util.h
new file mode 100644 (file)
index 0000000..f8e9c30
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+
+  sftp_util.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SFTP_UTIL_H
+#define SFTP_UTIL_H
+
+typedef uint32 SilcSFTPPacket;
+
+/* SFTP packet types */
+#define SILC_SFTP_INIT               1
+#define SILC_SFTP_VERSION            2
+#define SILC_SFTP_OPEN               3
+#define SILC_SFTP_CLOSE              4
+#define SILC_SFTP_READ               5
+#define SILC_SFTP_WRITE              6
+#define SILC_SFTP_LSTAT              7
+#define SILC_SFTP_FSTAT              8
+#define SILC_SFTP_SETSTAT            9
+#define SILC_SFTP_FSETSTAT           10
+#define SILC_SFTP_OPENDIR            11
+#define SILC_SFTP_READDIR            12
+#define SILC_SFTP_REMOVE             13
+#define SILC_SFTP_MKDIR              14
+#define SILC_SFTP_RMDIR              15
+#define SILC_SFTP_REALPATH           16
+#define SILC_SFTP_STAT               17
+#define SILC_SFTP_RENAME             18
+#define SILC_SFTP_READLINK           19
+#define SILC_SFTP_SYMLINK            20
+#define SILC_SFTP_STATUS             101
+#define SILC_SFTP_HANDLE             102
+#define SILC_SFTP_DATA               103
+#define SILC_SFTP_NAME               104
+#define SILC_SFTP_ATTRS              105
+#define SILC_SFTP_EXTENDED           200
+#define SILC_SFTP_EXTENDED_REPLY     201
+
+/* SFTP attributes flags */
+#define SILC_SFTP_ATTR_SIZE          0x00000001
+#define SILC_SFTP_ATTR_UIDGID        0x00000002
+#define SILC_SFTP_ATTR_PERMISSIONS   0x00000004
+#define SILC_SFTP_ATTR_ACMODTIME     0x00000008
+#define SILC_SFTP_ATTR_EXTENDED      0x80000000
+
+/* Encodes a SFTP packet of type `packet' of length `len'. The variable
+   argument list is encoded as data payload to the buffer. Returns the
+   encoded packet or NULL on error. The caller must free the returned
+   buffer. If `packet_buf' is non-NULL then the new packet data is put
+   to that buffer instead of allocating new one.  If the new data cannot
+   fit to `packet_buf' will be reallocated. */
+SilcBuffer silc_sftp_packet_encode(SilcSFTPPacket packet, 
+                                  SilcBuffer packet_buf, uint32 len, ...);
+
+/* Same as silc_sftp_packet_encode but takes the variable argument list
+   pointer as argument. */
+SilcBuffer silc_sftp_packet_encode_vp(SilcSFTPPacket packet, 
+                                     SilcBuffer packet_buf, uint32 len, 
+                                     va_list vp);
+
+/* Decodes the SFTP packet data `data' and return the SFTP packet type.
+   The payload of the packet is returned to the `payload' pointer. Returns
+   NULL if error occurred during decoding. */
+SilcSFTPPacket silc_sftp_packet_decode(SilcBuffer packet,
+                                      unsigned char **payload,
+                                      uint32 *payload_len);
+
+/* Encodes the SFTP attributes to a buffer and returns the allocated buffer.
+   The caller must free the buffer. */
+SilcBuffer silc_sftp_attr_encode(SilcSFTPAttributes attr);
+
+/* Decodes SilcSFTPAttributes from the buffer `buffer'. Returns the allocated
+   attributes that the caller must free or NULL on error. */
+SilcSFTPAttributes silc_sftp_attr_decode(SilcBuffer buffer);
+
+/* Frees the attributes context and its internals. */
+void silc_sftp_attr_free(SilcSFTPAttributes attr);
+
+/* Adds an entry to the `name' context. */
+void silc_sftp_name_add(SilcSFTPName name, const char *short_name,
+                       const char *long_name, SilcSFTPAttributes attrs);
+
+/* Encodes the SilcSFTPName to a buffer and returns the allocated buffer. 
+   The caller must free the buffer. */
+SilcBuffer silc_sftp_name_encode(SilcSFTPName name);
+
+/* Decodes a SilcSFTPName structure from the `buffer' that must include
+   `count' many name, longname and attribute values. Returns the allocated
+   structure or NULL on error. */
+SilcSFTPName silc_sftp_name_decode(uint32 count, SilcBuffer buffer);
+
+/* Frees the name context and its internals. */
+void silc_sftp_name_free(SilcSFTPName name);
+
+/* Maps errno to SFTP status message. */
+SilcSFTPStatus silc_sftp_map_errno(int err);
+
+#endif /* SFTP_UTIL_H */
diff --git a/lib/silcsftp/silcsftp.h b/lib/silcsftp/silcsftp.h
new file mode 100644 (file)
index 0000000..c817b8f
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+
+  silcsftp.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCSFTP_H
+#define SILCSFTP_H
+
+/****h* silcsftp/SilcSFTPAPI
+ *
+ * DESCRIPTION
+ *
+ *    SILC SFTP Interface is the implementation of the SSH File Transfer
+ *    Protocol.  The interface defines the SFTP client and the SFTP server.
+ *    The SFTP is the mandatory file transfer protocol in the SILC protocol.
+ *    The SFTP server implementation is filesystem independent and generic
+ *    interface is defined to represent filesystem access.
+ *
+ *    The SilcSFTP context is the actual SFTP client or SFTP server, and
+ *    each SFTP session (associated to a socket connection) must create
+ *    own SFTP context.
+ *
+ ***/
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTP
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSFTPStruct *SilcSFTP;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual SFTP client and SFTP server, and is
+ *    allocated by silc_sftp_client_start or silc_sftp_server_start and
+ *    given as argument usually to all silc_sftp_* functions.  It is freed
+ *    by the silc_sftp_client_shutdown or silc_sftp_server_shutdown 
+ *    functions.
+ *
+ ***/
+typedef struct SilcSFTPStruct *SilcSFTP;
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPVersion
+ *
+ * NAME
+ * 
+ *    typedef uint32 SilcSFTPVersion;
+ *
+ * DESCRIPTION
+ *
+ *    SFTP Version type.
+ *
+ * SOURCE
+ */
+typedef uint32 SilcSFTPVersion;
+/***/
+
+/* SFTP protocol version */
+#define SILC_SFTP_PROTOCOL_VERSION       3
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPStatus
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPStatus
+ *
+ * DESCRIPTION
+ *
+ *    SFTP protocol status types.  These enumerations is used to indicate
+ *    the status of request.  The server can send these to the client when
+ *    client has requested an operation.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_STATUS_OK                  = 0,  /* Operation successful */
+  SILC_SFTP_STATUS_EOF                 = 1,  /* No more data available */
+  SILC_SFTP_STATUS_NO_SUCH_FILE        = 2,  /* File does not exist */
+  SILC_SFTP_STATUS_PERMISSION_DENIED   = 3,  /* No sufficient permissions */
+  SILC_SFTP_STATUS_FAILURE             = 4,  /* Operation failed */
+  SILC_SFTP_STATUS_BAD_MESSAGE         = 5,  /* Bad message received */
+  SILC_SFTP_STATUS_NO_CONNECTION       = 6,  /* No connection to server */
+  SILC_SFTP_STATUS_CONNECTION_LOST     = 7,  /* Connection lost to server */
+  SILC_SFTP_STATUS_OP_UNSUPPORTED      = 8,  /* Operation unsupported */
+} SilcSFTPStatus;
+/***/
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPFileOperation
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPFileOperation
+ *
+ * DESCRIPTION
+ *
+ *    SFTP protocol file operation flags.  These enumerations can be used
+ *    by the client when client is opening an file, to indicate how it
+ *    would like to open the file.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_FXF_READ           = 0x00000001, /* Reading */
+  SILC_SFTP_FXF_WRITE          = 0x00000002, /* Writing */
+  SILC_SFTP_FXF_APPEND         = 0x00000004, /* Appending to end of file */
+  SILC_SFTP_FXF_CREAT          = 0x00000008, /* Create if doesn't exist */
+  SILC_SFTP_FXF_TRUNC          = 0x00000010, /* Truncate if exists */
+  SILC_SFTP_FXF_EXCL           = 0x00000020, /* Don't create if exists */
+} SilcSFTPFileOperation;
+/***/
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPAttributes
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } *SilcSFTPAttributes, SilcSFTPAttributesStruct;
+ *
+ * DESCRIPTION
+ *
+ *    SFTP File attributes structure represents the attributes for a file.
+ *    This structure can be used by the client to send attributes to the 
+ *    server, and by server to return file attributes to the client.
+ *
+ ***/
+typedef struct {
+  uint32 flags;                        /* Flags to indicate present attributes */
+  uint64 size;                 /* Sife of the file in bytes */
+  uint32 uid;                  /* Unix user ID */
+  uint32 gid;                  /* Unix group ID */
+  uint32 permissions;          /* POSIX file permission bitmask */
+  uint32 atime;                        /* Access time of file */
+  uint32 mtime;                        /* Modification time of file */
+
+  uint32 extended_count;       /* Extended type and data count */
+  SilcBuffer *extended_type;
+  SilcBuffer *extended_data;
+} *SilcSFTPAttributes, SilcSFTPAttributesStruct;
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPName
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } *SilcSFTPName, SilcSFTPNameStruct
+ *
+ * DESCRIPTION
+ *
+ *    SFTP Name structure represents the name reply received from the server.
+ *    It includes the returned file(s) short and long file names and
+ *    attributes for the file(s).  This is returned by the server for
+ *    example when reading the contents of a directory.
+ *
+ ***/
+typedef struct  {
+  char **filename;
+  char **long_filename;
+  SilcSFTPAttributes *attrs;
+  uint32 count;                        /* Number of files */
+} *SilcSFTPName, SilcSFTPNameStruct;
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPHandle
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSFTPHandleStruct *SilcSFTPHandle;
+ *
+ * DESCRIPTION
+ *
+ *    This context represents an open file handle and is allocated by
+ *    the library.  The application receives this context in the
+ *    SilcSFTPHandleCallback function.
+ *
+ ***/
+typedef struct SilcSFTPHandleStruct *SilcSFTPHandle;
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPSendPacketCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPSendPacketCallback)(SilcSocketConnection sock,
+ *                                               SilcBuffer packet, 
+ *                                               void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Packet sending callback. The caller of this interface will provide this
+ *    function for the library. The libary will call this function everytime
+ *    it needs to send a packet to the socket connection indicated by the
+ *    `sock'. 
+ *
+ ***/
+typedef void (*SilcSFTPSendPacketCallback)(SilcSocketConnection sock,
+                                          SilcBuffer packet, void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPVersionCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPVersionCallback)(SilcSFTP sftp,
+ *                                            SilcSFTPStatus status,
+ *                                            SilcSFTPVersion version,
+ *                                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Version callback is called at the protocol initialization phase when
+ *    the server returns the version of the protocol. The `version' indicates
+ *    the version of the protocol.
+ *
+ ***/
+typedef void (*SilcSFTPVersionCallback)(SilcSFTP sftp,
+                                       SilcSFTPStatus status,
+                                       SilcSFTPVersion version,
+                                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPStatusCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPStatusCallback)(SilcSFTP sftp,
+ *                                           SilcSFTPStatus status,
+ *                                           const char *message,
+ *                                           const char *language_tag,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Status callback is called every time server returns a status packet
+ *    for a request the client has made. The `status' indicates the type
+ *    of the status.  The `message' is optional error message received from
+ *    the server, in language indicated by the `language_tag'.  Both of
+ *    these pointers may be NULL.
+ *
+ ***/
+typedef void (*SilcSFTPStatusCallback)(SilcSFTP sftp,
+                                      SilcSFTPStatus status,
+                                      const char *message,
+                                      const char *language_tag,
+                                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPHandleCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPHandleCallback)(SilcSFTP sftp,
+ *                                           SilcSFTPStatus status,
+ *                                           SilcSFTPHandle handle,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Handle callback is called when the server returns a handle to the
+ *    client as a result of some request client has made.  The `handle'
+ *    is the file handle and the application can use it to perform file
+ *    operations for the handle. Each of the returned handle must be
+ *    also closed at some point with silc_sftp_close.
+ *
+ ***/
+typedef void (*SilcSFTPHandleCallback)(SilcSFTP sftp,
+                                      SilcSFTPStatus status,
+                                      SilcSFTPHandle handle,
+                                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPDataCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPDataCallback)(SilcSFTP sftp,
+ *                                         SilcSFTPStatus status,
+ *                                         const unsigned char *data,
+ *                                         uint32 data_len,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Data callback is called when data packet is received from the server.
+ *    This is called for example when application is reading a file from
+ *    the server.  The `data' is the raw data of length of `data_len'.
+ *
+ ***/
+typedef void (*SilcSFTPDataCallback)(SilcSFTP sftp,
+                                    SilcSFTPStatus status,
+                                    const unsigned char *data,
+                                    uint32 data_len,
+                                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPNameCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPNameCallback)(SilcSFTP sftp,
+ *                                         SilcSFTPStatus status,
+ *                                         const SilcSFTPName name,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Name callback is called when directory is being read by the client.
+ *    The server returns one or more file names in one reply.  These file
+ *    names are saved in the `filename' structures with their short and
+ *    long name format, and with file attributes.
+ *
+ ***/
+typedef void (*SilcSFTPNameCallback)(SilcSFTP sftp,
+                                    SilcSFTPStatus status,
+                                    const SilcSFTPName name,
+                                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPAttrCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPAttrCallback)(SilcSFTP sftp,
+ *                                         SilcSFTPStatus status,
+ *                                         const SilcSFTPAttributes attrs,
+ *                                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Attributes callback is called when the server returns the attributes
+ *    for a file the client has requested.  The attributes are saved in
+ *    the `attrs' structure.
+ *
+ ***/
+typedef void (*SilcSFTPAttrCallback)(SilcSFTP sftp,
+                                    SilcSFTPStatus status,
+                                    const SilcSFTPAttributes attrs,
+                                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPExtendedCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPExtendedCallback)(SilcSFTP sftp,
+ *                                             SilcSFTPStatus status,
+ *                                             const unsigned char *data,
+ *                                             uint32 data_len,
+ *                                             void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Extended request callback is called when client sends extended
+ *    request to the server. The `data' is arbitrary data returned by the
+ *    server and its encoding is the extended request specific.
+ *
+ ***/
+typedef void (*SilcSFTPExtendedCallback)(SilcSFTP sftp,
+                                        SilcSFTPStatus status,
+                                        const unsigned char *data,
+                                        uint32 data_len,
+                                        void *context);
+
+
+/* SFTP Client Interface */
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_client_start
+ *
+ * SYNOPSIS
+ *
+ *    SilcSFTP silc_sftp_client_start(SilcSocketConnection sock,
+ *                                    SilcSFTPSendPacketCallback send_packet,
+ *                                    void *send_context,
+ *                                    SilcSFTPVersionCallback callback,
+ *                                    void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Starts SFTP client by associating the socket connection `sock' to the
+ *    created SFTP client context.  The version callback indicated by the
+ *    `callback' will be called after the SFTP session has been started
+ *    and server has returned the version of the protocol.  The SFTP client
+ *    context is returned in the callback too.  This returns the allocated
+ *    SFTP client context or NULL on error.
+ *
+ ***/
+SilcSFTP silc_sftp_client_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context,
+                               SilcSFTPVersionCallback callback,
+                               void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_client_shutdown
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_client_shutdown(SilcSFTP sftp);
+ *
+ * DESCRIPTION
+ *
+ *    Shutdown's the SFTP client.  The caller is responsible of closing
+ *    the associated socket connection.  The SFTP context is freed and is
+ *    invalid after this function returns.
+ *
+ ***/
+void silc_sftp_client_shutdown(SilcSFTP sftp);
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+void silc_sftp_client_receive_process(SilcSFTP sftp,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_open
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_open(SilcSFTP sftp, 
+ *                        const char *filename,
+ *                        SilcSFTPFileOperation pflags,
+ *                        SilcSFTPAttributes attrs,
+ *                        SilcSFTPHandleCallback callback,
+ *                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Open a file indicated by the `filename' with flags indicated by the
+ *    `pflags', and with attributes indicated by the `attsr'.  Calls the
+ *    `callback' to return the opened file handle.
+ *
+ ***/
+void silc_sftp_open(SilcSFTP sftp, 
+                   const char *filename,
+                   SilcSFTPFileOperation pflags,
+                   SilcSFTPAttributes attrs,
+                   SilcSFTPHandleCallback callback,
+                   void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_close
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_close(SilcSFTP sftp,
+ *                         SilcSFTPHandle handle,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Closes the file indicated by the file handle `handle'.  Calls the
+ *    `callback' to indicate the status of the closing.
+ *
+ ***/
+void silc_sftp_close(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_read
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_read(SilcSFTP sftp,
+ *                        SilcSFTPHandle handle,
+ *                        uint64 offset, 
+ *                        uint32 len,
+ *                        SilcSFTPDataCallback callback,
+ *                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Reads data from the file indicated by the file handle `handle' starting
+ *    from the offset of `offset' at most `len' bytes.  The `callback' is
+ *    called to return the read data.
+ *
+ ***/
+void silc_sftp_read(SilcSFTP sftp,
+                   SilcSFTPHandle handle,
+                   uint64 offset, 
+                   uint32 len,
+                   SilcSFTPDataCallback callback,
+                   void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_write
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_write(SilcSFTP sftp,
+ *                         SilcSFTPHandle handle,
+ *                         uint64 offset,
+ *                         const unsigned char *data,
+ *                         uint32 data_len,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Writes to a file indicated by the file handle `handle' starting from
+ *    offset of `offset' at most `data_len' bytes of `data'.  The `callback' 
+ *    is called to indicate the status of the writing.
+ *
+ ***/
+void silc_sftp_write(SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    uint64 offset,
+                    const unsigned char *data,
+                    uint32 data_len,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_remove
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_remove(SilcSFTP sftp,
+ *                          const char *filename,
+ *                          SilcSFTPStatusCallback callback,
+ *                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Removes a file indicated by the `filename'.  Calls the `callback'
+ *    to indicate the status of the removing.
+ *
+ ***/
+void silc_sftp_remove(SilcSFTP sftp,
+                     const char *filename,
+                     SilcSFTPStatusCallback callback,
+                     void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_rename
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_rename(SilcSFTP sftp,
+ *                          const char *oldname,
+ *                          const char *newname,
+ *                          SilcSFTPStatusCallback callback,
+ *                          void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Renames a file indicated by the `oldname' to the name `newname'.  The
+ *    `callback' is called to indicate the status of the renaming.
+ *
+ ***/
+void silc_sftp_rename(SilcSFTP sftp,
+                     const char *oldname,
+                     const char *newname,
+                     SilcSFTPStatusCallback callback,
+                     void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_mkdir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_mkdir(SilcSFTP sftp,
+ *                         const char *path,
+ *                         SilcSFTPAttributes attrs,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Creates a new directory indicated by the `path' with attributes indicated
+ *    by the `attrs'. The `callback' is called to indicate the status of the
+ *    creation.
+ *
+ ***/
+void silc_sftp_mkdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttributes attrs,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_rmdir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_rmdir(SilcSFTP sftp,
+ *                         const char *path,
+ *                         SilcSFTPStatusCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Removes a directory indicated by the `path' and calls the `callback'
+ *    to indicate the status of the removal.
+ *
+ ***/
+void silc_sftp_rmdir(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPStatusCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_opendir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_opendir(SilcSFTP sftp,
+ *                           const char *path,
+ *                           SilcSFTPHandleCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Opens a directory indicated by the `path'.  The `callback' is called
+ *    to return the opened file handle.
+ *
+ ***/
+void silc_sftp_opendir(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPHandleCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_readdir
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_readdir(SilcSFTP sftp,
+ *                           SilcSFTPHandle handle,
+ *                           SilcSFTPNameCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Reads the contents of the directory indicated by the `handle' and
+ *    calls the `callback' to return the read file(s) from the directory.
+ *
+ ***/
+void silc_sftp_readdir(SilcSFTP sftp,
+                      SilcSFTPHandle handle,
+                      SilcSFTPNameCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_stat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_stat(SilcSFTP sftp,
+ *                        const char *path,
+ *                        SilcSFTPAttrCallback callback,
+ *                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets the file attributes for a file indicated by the `path'. This
+ *    will follow symbolic links also. Calls the `callback' to return the
+ *    file attributes.
+ *
+ ***/
+void silc_sftp_stat(SilcSFTP sftp,
+                   const char *path,
+                   SilcSFTPAttrCallback callback,
+                   void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_lstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_lstat(SilcSFTP sftp,
+ *                         const char *path,
+ *                         SilcSFTPAttrCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets the file attributes for a file indicated by the `path'. This
+ *    will not follow symbolic links. Calls the `callback' to return the
+ *    file attributes
+ *
+ ***/
+void silc_sftp_lstat(SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttrCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_fstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_fstat(SilcSFTP fstp,
+ *                         SilcSFTPHandle handle,
+ *                         SilcSFTPAttrCallback callback,
+ *                         void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Gets a file attributes for a opened file indicated by the `handle'.
+ *    Calls the `callback' to return the file attributes.
+ *
+ ***/
+void silc_sftp_fstat(SilcSFTP fstp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPAttrCallback callback,
+                    void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_setstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_setstat(SilcSFTP sftp,
+ *                           const char *path,
+ *                           SilcSFTPAttributes attrs,
+ *                           SilcSFTPStatusCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Sets a file attributes to a file indicated by the `path' with the
+ *    attributes indicated by the `attrs'.  Calls the `callback' to indicate
+ *    the status of the setting.
+ *
+ ***/
+void silc_sftp_setstat(SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPAttributes attrs,
+                      SilcSFTPStatusCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_fsetstat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_fsetstat(SilcSFTP sftp,
+ *                            SilcSFTPHandle handle,
+ *                            SilcSFTPAttributes attrs,
+ *                            SilcSFTPStatusCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Sets a file attributes to a opened file indicated by the `handle' with
+ *    the attributes indicated by the `attrs'.  Calls the `callback' to
+ *    indicate the status of the setting.
+ *
+ ***/
+void silc_sftp_fsetstat(SilcSFTP sftp,
+                       SilcSFTPHandle handle,
+                       SilcSFTPAttributes attrs,
+                       SilcSFTPStatusCallback callback,
+                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_readlink
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_readlink(SilcSFTP sftp,
+ *                            const char *path,
+ *                            SilcSFTPNameCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Reads the target of a symbolic link indicated by the `path'.  The
+ *    `callback' is called to return the target of the symbolic link.
+ *
+ ***/
+void silc_sftp_readlink(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_symlink
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_symlink(SilcSFTP sftp,
+ *                           const char *linkpath,
+ *                           const char *targetpath,
+ *                           SilcSFTPStatusCallback callback,
+ *                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Creates a new symbolic link indicated by the `linkpath' to the target
+ *    indicated by the `targetpath'.  The `callback' is called to indicate
+ *    the status of creation.
+ *
+ ***/
+void silc_sftp_symlink(SilcSFTP sftp,
+                      const char *linkpath,
+                      const char *targetpath,
+                      SilcSFTPStatusCallback callback,
+                      void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_realpath
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_realpath(SilcSFTP sftp,
+ *                            const char *path,
+ *                            SilcSFTPNameCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Canonicalizes the path indicated by the `path' to a absolute path.
+ *    The `callback' is called to return the absolute path.
+ *
+ ***/
+void silc_sftp_realpath(SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_extended
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_extended(SilcSFTP sftp,
+ *                            const char *request,
+ *                            const unsigned char *data,
+ *                            uint32 data_len,
+ *                            SilcSFTPExtendedCallback callback,
+ *                            void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Performs an extended operation indicated by the `request' with 
+ *    optional extended operation data indicated by the `data'.  The callback
+ *    is called to return any data associated with the extended request.
+ *
+ ***/
+void silc_sftp_extended(SilcSFTP sftp,
+                       const char *request,
+                       const unsigned char *data,
+                       uint32 data_len,
+                       SilcSFTPExtendedCallback callback,
+                       void *context);
+
+
+/* SFTP Server Interface */
+
+#include "silcsftp_fs.h"
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_server_start
+ *
+ * SYNOPSIS
+ *
+ *    SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
+ *                                    SilcSFTPSendPacketCallback send_packet,
+ *                                    void *send_context, 
+ *                                    SilcSFTPFilesystem fs);
+ *
+ * DESCRIPTION
+ *
+ *    Starts SFTP server by associating the socket connection `sock' to the
+ *    created SFTP server context.  This function returns the allocated
+ *    SFTP client context or NULL on error. The `send_packet' is called
+ *    by the library when it needs to send a packet. The `fs' is the
+ *    filesystem context allocated by the application.
+ *
+ ***/
+SilcSFTP silc_sftp_server_start(SilcSocketConnection sock,
+                               SilcSFTPSendPacketCallback send_packet,
+                               void *send_context, 
+                               SilcSFTPFilesystem fs);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_server_shutdown
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_server_shutdown(SilcSFTP sftp);
+ *
+ * DESCRIPTION
+ *
+ *    Shutdown's the SFTP server.  The caller is responsible of closing
+ *    the associated socket connection.  The SFTP context is freed and is
+ *    invalid after this function returns.
+ *
+ ***/
+void silc_sftp_server_shutdown(SilcSFTP sftp);
+
+/****d* silcsftp/SilcSFTPAPI/SilcSFTPMonitors
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPMonitors;
+ *
+ * DESCRIPTION
+ *
+ *    SFTP server monitor types. These can be masked together to monitor
+ *    various client requests.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_MONITOR_INIT        = 0x0001,
+  SILC_SFTP_MONITOR_OPEN        = 0x0002,
+  SILC_SFTP_MONITOR_CLOSE       = 0x0004,
+  SILC_SFTP_MONITOR_READ        = 0x0008,
+  SILC_SFTP_MONITOR_WRITE       = 0x0010,
+  SILC_SFTP_MONITOR_REMOVE      = 0x0020,
+  SILC_SFTP_MONITOR_RENAME      = 0x0040,
+  SILC_SFTP_MONITOR_MKDIR       = 0x0080,
+  SILC_SFTP_MONITOR_RMDIR       = 0x0100,
+  SILC_SFTP_MONITOR_OPENDIR     = 0x0200,
+  SILC_SFTP_MONITOR_READDIR     = 0x0400,
+  SILC_SFTP_MONITOR_STAT        = 0x0800,
+  SILC_SFTP_MONITOR_LSTAT       = 0x1000,
+  SILC_SFTP_MONITOR_FSTAT       = 0x2000,
+  SILC_SFTP_MONITOR_SETSTAT     = 0x4000,
+  SILC_SFTP_MONITOR_FSETSTAT    = 0x8000,
+  SILC_SFTP_MONITOR_READLINK    = 0x10000,
+  SILC_SFTP_MONITOR_SYMLINK     = 0x20000,
+  SILC_SFTP_MONITOR_REALPATH    = 0x40000,
+  SILC_SFTP_MONITOR_EXTENDED    = 0x80000,
+} SilcSFTPMonitors;
+/***/
+
+/****s* silcsftp/SilcSFTPAPI/SilcSFTPMonitorData
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } *SilcSFTPMonitorData, SilcSFTPMonitorDataStruct;
+ *
+ * DESCRIPTION
+ *
+ *    This structure includes the monitor type specific data.  The
+ *    application can check what the client has requested from this
+ *    structure.
+ *
+ * SOURCE
+ */
+typedef struct {
+  SilcSFTPVersion version;     /* _INIT */
+  char *name;                  /* _OPEN, _REMOVE, _RENAME, _MKDIR,
+                                  _RMDIR, _OPENDIR, _STAT, _LSTAT,
+                                  _SETSTAT, _READLINK, _SYMLINK, _REALPATH */
+  char *name2;                 /* _RENAME, _SYMLINK */
+  SilcSFTPFileOperation pflags;        /* _OPEN */
+  uint64 offset;               /* _READ, _WRITE */
+  uint32 data_len;             /* _READ, _WRITE */
+  SilcSFTPName names;          /* _READDIR, _READLINK, _REALPATH */
+} *SilcSFTPMonitorData, SilcSFTPMonitorDataStruct;
+/***/
+
+/****f* silcsftp/SilcSFTPAPI/SilcSFTPMonitor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSFTPMonitor)(SilcSFTP sftp
+ *                                    SilcSFTPMonitors type,
+ *                                    const SilcSFTPMonitorData data,
+ *                                    void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Monitor callback that is called when an specified request is
+ *    received from client.  The `type' is the requested type that
+ *    was being monitored.
+ *
+ ***/
+typedef void (*SilcSFTPMonitor)(SilcSFTP sftp,
+                               SilcSFTPMonitors type,
+                               const SilcSFTPMonitorData data,
+                               void *context);
+
+/****f* silcsftp/SilcSFTPAPI/silc_sftp_server_set_monitor
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_server_set_monitor(SilcSFTP sftp,
+ *                                      SilcSFTPMonitors monitors,
+ *                                      SilcSFTPMonitor monitor, 
+ *                                      void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Sets monitor callback to monitor various request sent by an client.
+ *    When request that has been set in the `monitors' is received the
+ *    monitor callback will be called to notify the caller.
+ *
+ ***/
+void silc_sftp_server_set_monitor(SilcSFTP sftp,
+                                 SilcSFTPMonitors monitors,
+                                 SilcSFTPMonitor monitor, 
+                                 void *context);
+
+/* Function that is called to process the incmoing SFTP packet. */
+/* XXX Some day this will go away and we have automatic receive callbacks
+   for SilcSocketConnection API or SilcPacketContext API. */
+void silc_sftp_server_receive_process(SilcSFTP sftp,
+                                     SilcSocketConnection sock,
+                                     SilcPacketContext *packet);
+
+#endif /* SILCSFTP_H */
diff --git a/lib/silcsftp/silcsftp_fs.h b/lib/silcsftp/silcsftp_fs.h
new file mode 100644 (file)
index 0000000..6144dc0
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+
+  silcsftp_fs.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCSFTP_FS_H
+#define SILCSFTP_FS_H
+
+/****h* silcsftp/SilcSFTPFSAPI
+ *
+ * DESCRIPTION
+ *
+ *    SILC SFTP Filesystem interface defines filesystems for the SFTP server
+ *    usage.  The filesystems may be for example virtual memory filesystem
+ *    or real filesystem access.
+ *
+ *    Currently only implemented filesystem is memory file system.
+ *
+ *    Memory Filesystem:
+ *
+ *    Memory filesystem is a virtual filesystem which provides safe access
+ *    to files without actually revealing the underlaying physical filesystem
+ *    hierarchy or real filenames. Virtual directories can be added to the
+ *    filesystem and freely create filesystem hierarchy. The directories
+ *    can have subdirectories and files. The filesystem also provides limited
+ *    status information for files.  The files in the filesystem are
+ *    virtual but they include the path to the real file.  The real path
+ *    includes always a schema which indicates where the file really is
+ *    available.  The only supported schema currently is "file://".  In
+ *    the future it could support various others like "http://" and "ldap://".
+ *
+ *    The filesystem also provides security and permission handling for
+ *    directories and files.  Normal POSIX style permissions can be set
+ *    giving thus rights to reading, writing and/or executing.  They behave
+ *    same way as defined in POSIX.  It is also guaranteed that if the
+ *    writing to a file is not allowed in the memory filesystem, but it is
+ *    allowed in real physical filesystem the file still cannot be written.
+ *    However, the real physical filesystem permissions still matter, for
+ *    example if writing is enabled in the memory filesystem but it is not
+ *    enabled on physical filesystem, the file cannot be written.
+ *
+ *    The directories cannot be removed from remote access using the
+ *    filesystem access function sftp_rmdir.  This is because the filesystem
+ *    is one-user filesystem and differentiating between users is not 
+ *    possible.  Thus, it would allow anyone to remove directories and
+ *    their contents.  Removing directories is possible only locally using
+ *    the silc_sftp_fs_memory_del_dir function.  The same thing is with
+ *    removing files as well.  Files too can be removed only locally using
+ *    the silc_sftp_fs_memory_del_file function.  Also, files can not ever
+ *    be executed from remote access.
+ *
+ *    Also some of the file operation flags are not supported, such as 
+ *    SILC_SFTP_FXF_CREAT, SILC_SFTP_FXF_TRUNC and SILC_SFTP_FXF_EXCL
+ *    since they would require access to a real filesystem file which does
+ *    not exist yet, or would mean destroying the file.  However, the
+ *    SILC_SFTP_FXF_WRITE is supported since the file aready exists.
+ *
+ *    The memory filesystem does not provide symbolic links.
+ *
+ ***/
+
+/****s* silcsftp/SilcSFTPFSAPI/SilcSFTPFilesystemOps
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSFTPFilesystemOpsStruct { ... } 
+ *                     *SilcSFTPFilesystemOps;
+ *
+ * DESCRIPTION
+ *
+ *    This structure defines the generic filesystem access.  When the
+ *    filesystem is accessed these functions are called to do the requested
+ *    filesystem operation.  The level that implements the actual filesystem
+ *    must fill this structure with the callback functions providing the
+ *    access to the filesystem.
+ *
+ * SOURCE
+ */
+typedef struct SilcSFTPFilesystemOpsStruct {
+  /* Find a file handle by the file handle data indicated by the `data'. 
+     If the handle is not found this returns NULL. */
+  SilcSFTPHandle (*sftp_get_handle)(void *context, SilcSFTP sftp,
+                                   const unsigned char *data,
+                                   uint32 data_len);
+
+  /* Return encoded handle of `handle' or NULL on error. The caller
+     must free the returned buffer. */
+  unsigned char *(*sftp_encode_handle)(void *context, SilcSFTP sftp,
+                                      SilcSFTPHandle handle,
+                                      uint32 *handle_len);
+
+  /* Open a file indicated by the `filename' with flags indicated by the
+     `pflags', and with attributes indicated by the `attr'.  Calls the
+     `callback' to return the opened file handle. */
+  void (*sftp_open)(void *context, SilcSFTP sftp, 
+                   const char *filename, 
+                   SilcSFTPFileOperation pflags,
+                   SilcSFTPAttributes attr,
+                   SilcSFTPHandleCallback callback,
+                   void *callback_context);
+
+  /* Closes the file indicated by the file handle `handle'.  Calls the
+     `callback' to indicate the status of the closing. */
+  void (*sftp_close)(void *context, SilcSFTP sftp, 
+                    SilcSFTPHandle handle,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Reads data from the file indicated by the file handle `handle' starting
+     from the offset of `offset' at most `len' bytes.  The `callback' is
+     called to return the read data. */
+  void (*sftp_read)(void *context, SilcSFTP sftp,
+                   SilcSFTPHandle handle, 
+                   uint64 offset, 
+                   uint32 len,
+                   SilcSFTPDataCallback callback,
+                   void *callback_context);
+
+  /* Writes to a file indicated by the file handle `handle' starting from
+     offset of `offset' at most `data_len' bytes of `data'.  The `callback' 
+     is called to indicate the status of the writing. */
+  void (*sftp_write)(void *context, SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    uint64 offset,
+                    const unsigned char *data,
+                    uint32 data_len,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Removes a file indicated by the `filename'.  Calls the `callback'
+     to indicate the status of the removing. */
+  void (*sftp_remove)(void *context, SilcSFTP sftp,
+                     const char *filename,
+                     SilcSFTPStatusCallback callback,
+                     void *callback_context);
+
+  /* Renames a file indicated by the `oldname' to the name `newname'.  The
+     `callback' is called to indicate the status of the renaming. */
+  void (*sftp_rename)(void *context, SilcSFTP sftp,
+                     const char *oldname,
+                     const char *newname,
+                     SilcSFTPStatusCallback callback,
+                     void *callback_context);
+
+  /* Creates a new directory indicated by the `path' with attributes indicated
+     by the `attrs'. The `callback' is called to indicate the status of the
+     creation. */
+  void (*sftp_mkdir)(void *context, SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttributes attrs,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Removes a directory indicated by the `path' and calls the `callback'
+     to indicate the status of the removal. */
+  void (*sftp_rmdir)(void *context, SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPStatusCallback callback,
+                    void *callback_context);
+
+  /* Opens a directory indicated by the `path'.  The `callback' is called
+     to return the opened file handle. */
+  void (*sftp_opendir)(void *context, SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPHandleCallback callback,
+                      void *callback_context);
+
+  /* Reads the contents of the directory indicated by the `handle' and
+     calls the `callback' to return the read file(s) from the directory. */
+  void (*sftp_readdir)(void *context, SilcSFTP sftp,
+                      SilcSFTPHandle handle,
+                      SilcSFTPNameCallback callback,
+                      void *callback_context);
+
+  /* Gets the file attributes for a file indicated by the `path'. This
+     will follow symbolic links also. Calls the `callback' to return the
+     file attributes. */
+  void (*sftp_stat)(void *context, SilcSFTP sftp,
+                   const char *path,
+                   SilcSFTPAttrCallback callback,
+                   void *callback_context);
+
+  /* Gets the file attributes for a file indicated by the `path'. This
+     will not follow symbolic links. Calls the `callback' to return the
+     file attributes. */
+  void (*sftp_lstat)(void *context, SilcSFTP sftp,
+                    const char *path,
+                    SilcSFTPAttrCallback callback,
+                    void *callback_context);
+
+  /* Gets a file attributes for a opened file indicated by the `handle'.
+     Calls the `callback' to return the file attributes. */
+  void (*sftp_fstat)(void *context, SilcSFTP sftp,
+                    SilcSFTPHandle handle,
+                    SilcSFTPAttrCallback callback,
+                    void *callback_context);
+  
+  /* Sets a file attributes to a file indicated by the `path' with the
+     attributes indicated by the `attrs'.  Calls the `callback' to indicate
+     the status of the setting. */
+  void (*sftp_setstat)(void *context, SilcSFTP sftp,
+                      const char *path,
+                      SilcSFTPAttributes attrs,
+                      SilcSFTPStatusCallback callback,
+                      void *callback_context);
+
+  /* Sets a file attributes to a opened file indicated by the `handle' with
+     the attributes indicated by the `attrs'.  Calls the `callback' to
+     indicate the status of the setting. */
+  void (*sftp_fsetstat)(void *context, SilcSFTP sftp,
+                       SilcSFTPHandle handle,
+                       SilcSFTPAttributes attrs,
+                       SilcSFTPStatusCallback callback,
+                       void *callback_context);
+
+  /* Reads the target of a symbolic link indicated by the `path'.  The
+     `callback' is called to return the target of the symbolic link. */
+  void (*sftp_readlink)(void *context, SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *callback_context);
+
+  /* Creates a new symbolic link indicated by the `linkpath' to the target
+     indicated by the `targetpath'.  The `callback' is called to indicate
+     the status of creation. */
+  void (*sftp_symlink)(void *context, SilcSFTP sftp,
+                      const char *linkpath,
+                      const char *targetpath,
+                      SilcSFTPStatusCallback callback,
+                      void *callback_context);
+
+  /* Canonicalizes the path indicated by the `path' to a absolute path.
+     The `callback' is called to return the absolute path. */
+  void (*sftp_realpath)(void *context, SilcSFTP sftp,
+                       const char *path,
+                       SilcSFTPNameCallback callback,
+                       void *callback_context);
+
+  /* Performs an extended operation indicated by the `request' with 
+     optional extended operation data indicated by the `data'.  The callback
+     is called to return any data associated with the extended request. */
+  void (*sftp_extended)(void *context, SilcSFTP sftp,
+                       const char *request,
+                       const unsigned char *data,
+                       uint32 data_len,
+                       SilcSFTPExtendedCallback callback,
+                       void *callback_context);
+} *SilcSFTPFilesystemOps;
+/****/
+
+/****s* silcsftp/SilcSFTPFSAPI/SilcSFTPFilesystem
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } *SilcSFTPFilesystem;
+ *
+ * DESCRIPTION
+ *
+ *    This context is allocated and returned by all filesystem allocation
+ *    routines.  The returned context is given as argument to the
+ *    silc_sftp_server_start function.  The caller must also free the
+ *    context after the SFTP server is shutdown.
+ *
+ * SOURCE
+ */
+typedef struct {
+  SilcSFTPFilesystemOps fs;
+  void *fs_context;
+} *SilcSFTPFilesystem;
+/***/
+
+/* Memory filesystem */
+
+/****d* silcsftp/SilcSFTPFSAPI/SilcSFTPFSMemoryPerm
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSFTPFSMemoryPerm;
+ *
+ * DESCRIPTION
+ *
+ *    Memory filesystem permission definition.  These enumerations can
+ *    be used to set the permission mask for directories and files.
+ *    The permissions behave in POSIX style.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SFTP_FS_PERM_READ    = 0x0001,    /* Reading allowed */
+  SILC_SFTP_FS_PERM_WRITE   = 0x0002,   /* Writing allowed */
+  SILC_SFTP_FS_PERM_EXEC    = 0x0004,   /* Execution allowed */
+} SilcSFTPFSMemoryPerm;
+/***/
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_alloc
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates memory filesystem context and returns the context.  The
+ *    context can be given as argument to the silc_sftp_server_start
+ *    function. The context must be freed by the caller using the function
+ *    silc_sftp_fs_memory_free. The `perm' is the permissions for the root
+ *    directory of the filesystem (/ dir).
+ *
+ ***/
+SilcSFTPFilesystem silc_sftp_fs_memory_alloc(SilcSFTPFSMemoryPerm perm);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_sftp_fs_memory_free(void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the memory filesystem context.
+ *
+ ***/
+void silc_sftp_fs_memory_free(SilcSFTPFilesystem fs);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_add_dir
+ *
+ * SYNOPSIS
+ *
+ *    void *silc_sftp_fs_memory_add_dir(void *context, void *dir,
+ *                                      SilcSFTPFSMemoryPerm perm,
+ *                                      const char *name);
+ *
+ * DESCRIPTION
+ *
+ *    Adds a new directory to the memory filesystem. Returns the directory
+ *    context that can be used to add for example files to the directory
+ *    or new subdirectories under the directory. The `dir' is the parent
+ *    directory of the directory to be added. If this directory is to be
+ *    added to the root directory the `dir' is NULL.  The `name' is the name
+ *    of the directory. If error occurs this returns NULL. The `perm' will 
+ *    indicate the permissions for the directory and they work in POSIX
+ *    style. 
+ *
+ ***/
+void *silc_sftp_fs_memory_add_dir(SilcSFTPFilesystem fs, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *name);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_del_dir
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_sftp_fs_memory_del_dir(SilcSFTPFilesystem fs, void *dir);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes a directory indicated by the `dir'. All files and
+ *    subdirectories in this directory is also removed.  If the `dir' is
+ *    NULL then all directories and files are removed from the filesystem.
+ *    Returns TRUE if the removing was success. This is the only way to
+ *    remove directories in memory file system. The filesystem does not
+ *    allow removing directories with remote access using the filesystem
+ *    access function sftp_rmdir.
+ *
+ ***/
+bool silc_sftp_fs_memory_del_dir(SilcSFTPFilesystem fs, void *dir);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_add_file
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_sftp_fs_memory_add_file(SilcSFTPFilesystem fs, void *dir,
+ *                                      SilcSFTPFSMemoryPerm perm,
+ *                                      const char *filename,
+ *                                      const char *realpath);
+ *
+ * DESCRIPTION
+ *
+ *    Adds a new file to the directory indicated by the `dir'.  If the `dir'
+ *    is NULL the file is added to the root directory. The `filename' is the
+ *    filename in the directory. The `realpath' is the real filepath in the
+ *    physical filesystem. The real path must include the schema to
+ *    indicate where the file is actually located.  The only supported
+ *    schema currently is "file://".  It is used to actually access the fil
+ *    from the memory filesystem. The `perm' will indicate the permissions
+ *    for the file and they work in POSIX style. Returns TRUE if the file
+ *    was added to the directory.
+ *
+ ***/
+bool silc_sftp_fs_memory_add_file(SilcSFTPFilesystem fs, void *dir,
+                                 SilcSFTPFSMemoryPerm perm,
+                                 const char *filename,
+                                 const char *realpath);
+
+/****f* silcsftp/SilcSFTPFSAPI/silc_sftp_fs_memory_del_file
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_sftp_fs_memory_del_file(SilcSFTPFilesystem fs, void *dir,
+ *                                      const char *filename);
+ *
+ * DESCRIPTION
+ *
+ *    Removes a file indicated by the `filename' from the directory
+ *    indicated by the `dir'. Returns TRUE if the removing was success. This
+ *    is the only way to remove files in the filesystem.  The filesystem does
+ *    not allow removing files with remote access using the filesystem
+ *    access function sftp_remove.
+ *
+ ***/
+bool silc_sftp_fs_memory_del_file(SilcSFTPFilesystem fs, void *dir,
+                                 const char *filename);
+
+#endif /* SILCSFTP_FS_H */
diff --git a/lib/silcsftp/tests/Makefile.am b/lib/silcsftp/tests/Makefile.am
new file mode 100644 (file)
index 0000000..4df0565
--- /dev/null
@@ -0,0 +1,28 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+bin_PROGRAMS = sftp_server sftp_client
+sftp_server_SOURCES = sftp_server.c
+sftp_client_SOURCES = sftp_client.c
+
+LIBS = $(SILC_COMMON_LIBS)
+LDADD = -L.. -L../.. -lsilc
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcsftp/tests/sftp_client.c b/lib/silcsftp/tests/sftp_client.c
new file mode 100644 (file)
index 0000000..9b2e796
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+
+  sftp_client.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+
+typedef struct {
+  SilcSchedule schedule;
+  SilcSocketConnection sock;
+  SilcSFTP sftp;
+} *Client;
+
+Client gclient;
+
+char *dir;
+char *file;
+bool opendir;
+uint64 offset;
+
+static void sftp_name(SilcSFTP sftp, SilcSFTPStatus status,
+                     const SilcSFTPName name, void *context);
+static void sftp_handle(SilcSFTP sftp, SilcSFTPStatus status,
+                       SilcSFTPHandle handle, void *context);
+static void sftp_data(SilcSFTP sftp, SilcSFTPStatus status,
+                     const unsigned char *data, uint32 data_len,
+                     void *context);
+
+static void send_packet(SilcSocketConnection sock,
+                       SilcBuffer packet, void *context)
+{
+  Client client = (Client)context;
+  SilcPacketContext packetdata;
+  int ret;
+
+  memset(&packetdata, 0, sizeof(packetdata));
+  packetdata.type = SILC_PACKET_FTP;
+  packetdata.truelen = packet->len + SILC_PACKET_HEADER_LEN;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, 0);
+  silc_packet_send_prepare(sock,
+                          SILC_PACKET_HEADER_LEN,
+                          packetdata.padlen,
+                          packet->len);
+  packetdata.buffer = sock->outbuf;
+  silc_buffer_put(sock->outbuf, packet->data, packet->len);
+  silc_packet_assemble(&packetdata, NULL);
+  ret = silc_packet_send(sock, TRUE);
+  if (ret != -2)
+    return;
+
+  silc_schedule_set_listen_fd(client->schedule, sock->sock, 
+                             (SILC_TASK_READ | SILC_TASK_WRITE));
+  SILC_SET_OUTBUF_PENDING(sock);
+}
+
+static bool packet_parse(SilcPacketParserContext *parser, void *context)
+{
+  Client client = (Client)parser->context;
+  SilcSocketConnection sock = parser->sock;
+  SilcPacketContext *packet = parser->packet;
+  int ret;
+  
+  ret = silc_packet_parse(packet, NULL);
+  assert(packet->type == SILC_PACKET_FTP);
+
+  silc_sftp_client_receive_process(client->sftp, sock, packet);
+
+  return TRUE;
+}
+
+SILC_TASK_CALLBACK(packet_process)
+{
+  Client client = (Client)context;
+  SilcSocketConnection sock = client->sock;
+  int ret;
+
+  if (type == SILC_TASK_WRITE) {
+    if (sock->outbuf->data - sock->outbuf->head)
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    ret = silc_packet_send(sock, TRUE);
+    if (ret < 0)
+      return;
+
+    silc_schedule_set_listen_fd(client->schedule, fd, SILC_TASK_READ);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+    return;
+  }
+
+  if (type == SILC_TASK_READ) {
+    ret = silc_packet_receive(sock);
+    if (ret < 0)
+      return;
+
+    if (ret == 0) {
+      silc_net_close_connection(sock->sock);
+      silc_socket_free(sock);
+      exit(0);
+    }
+
+    silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, 
+                               packet_parse, client);
+  }
+}
+
+static void sftp_data(SilcSFTP sftp, SilcSFTPStatus status,
+                     const unsigned char *data, uint32 data_len,
+                     void *context)
+{
+  SilcSFTPHandle handle = (SilcSFTPHandle)context;
+  int debug = silc_debug;
+
+  if (status != SILC_SFTP_STATUS_OK) {
+    SilcSFTPAttributesStruct attrs;
+
+    fprintf(stderr, "Status %d\n", status);
+
+    if (!strcmp(file, "/sftp/sftp_server.c"))
+      return;
+
+    /* Open another file */
+    opendir = FALSE;
+    memset(&attrs, 0, sizeof(attrs));
+    file = "/sftp/sftp_server.c";
+    fprintf(stderr, "Opening file %s\n", file);
+    offset = 0;
+    silc_sftp_open(sftp, file, SILC_SFTP_FXF_READ,
+                  &attrs, sftp_handle, gclient);
+    return;
+  }
+
+  if (!debug)
+    silc_debug = 1;
+  SILC_LOG_HEXDUMP(("data"), (unsigned char *)data, data_len);
+  silc_debug = debug;
+
+  offset += data_len;
+
+  /* Attempt to read more */
+  fprintf(stderr, "Reading more of file %s\n", file);
+  silc_sftp_read(sftp, handle, offset, 2048, sftp_data, handle);
+}
+
+static void sftp_name(SilcSFTP sftp, SilcSFTPStatus status,
+                     const SilcSFTPName name, void *context)
+{
+  Client client = (Client)context;
+  int i;
+
+  SILC_LOG_DEBUG(("Name"));
+  fprintf(stderr, "Status %d\n", status);
+
+  fprintf(stderr, "Directory: %s\n", dir);
+  for (i = 0; i < name->count; i++) {
+    fprintf(stderr, "%s\n", name->long_filename[i]);
+  }
+
+  if (!strcmp(dir, "sftp")) {
+    SilcSFTPAttributesStruct attrs;
+
+    /* open */
+    opendir = FALSE;
+    memset(&attrs, 0, sizeof(attrs));
+    file = "passwd";
+    fprintf(stderr, "Opening file %s\n", file);
+    offset = 0;
+    silc_sftp_open(sftp, file, SILC_SFTP_FXF_READ,
+                  &attrs, sftp_handle, client);
+    return;
+  }
+
+  if (!strcmp(dir, "/"))
+    dir = "sftp";
+
+  fprintf(stderr, "Opening %s\n", dir);
+
+  /* opendir */
+  opendir = TRUE;
+  silc_sftp_opendir(sftp, dir, sftp_handle, client);
+}
+
+static void sftp_handle(SilcSFTP sftp, SilcSFTPStatus status,
+                       SilcSFTPHandle handle, void *context)
+{
+  Client client = (Client)context;
+
+  SILC_LOG_DEBUG(("Handle"));
+  fprintf(stderr, "Status %d\n", status);
+  if (status != SILC_SFTP_STATUS_OK)
+    return;
+
+  if (opendir) {
+    fprintf(stderr, "Reading %s\n", dir);
+    /* Readdir */
+    silc_sftp_readdir(sftp, handle, sftp_name, client);
+  } else {
+    fprintf(stderr, "Reading file %s\n", file);
+
+    /* Read */
+    silc_sftp_read(sftp, handle, 0, 2048, sftp_data, handle);
+  }
+}
+
+static void sftp_version(SilcSFTP sftp, SilcSFTPStatus status,
+                        SilcSFTPVersion version, void *context)
+{
+  Client client = (Client)context;
+  fprintf(stderr, "Version: %d\n", (int)version);
+
+  SILC_LOG_DEBUG(("Version"));
+  fprintf(stderr, "Status %d\n", status);
+
+  /* opendir */
+  dir = "/";
+  fprintf(stderr, "Opening %s\n", dir);
+  opendir = TRUE;
+  silc_sftp_opendir(sftp, dir, sftp_handle, client);
+}
+
+int main(int argc, char **argv)
+{
+  Client client = silc_calloc(1, sizeof(*client));
+  int sock;
+
+  gclient = client;
+
+  if (argc > 1 && !strcmp(argv[1], "-d")) {
+    silc_debug = 1;
+    silc_debug_hexdump = 1;
+    silc_log_set_debug_string("");
+  }
+
+  client->schedule = silc_schedule_init(100);
+  if (!client->schedule)
+    return -1;
+
+  /* Connecto to server */
+  sock = silc_net_create_connection(NULL, 5000, "127.0.0.1");
+  if (sock < 0)
+    return -1;
+  silc_socket_alloc(sock, 0, NULL, &client->sock);
+  silc_schedule_task_add(client->schedule, sock,
+                        packet_process, client, 0, 0,
+                        SILC_TASK_GENERIC, SILC_TASK_PRI_NORMAL);
+
+  /* Start SFTP session */
+  client->sftp = silc_sftp_client_start(client->sock, send_packet, client,
+                                       sftp_version, client);
+
+  silc_schedule(client->schedule);
+  return 0;
+}
diff --git a/lib/silcsftp/tests/sftp_server.c b/lib/silcsftp/tests/sftp_server.c
new file mode 100644 (file)
index 0000000..bc5ac02
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+
+  sprp_server.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#include "silcincludes.h"
+#include "silcsftp.h"
+
+typedef struct {
+  SilcSchedule schedule;
+  int sock;
+  SilcSFTPFilesystem fs;
+  SilcSocketConnection socks[100];
+  SilcSFTP sftp[100];
+} *Server;
+
+static void send_packet(SilcSocketConnection sock,
+                       SilcBuffer packet, void *context)
+{
+  Server server = (Server)context;
+  SilcPacketContext packetdata;
+  int ret;
+
+  memset(&packetdata, 0, sizeof(packetdata));
+  packetdata.type = SILC_PACKET_FTP;
+  packetdata.truelen = packet->len + SILC_PACKET_HEADER_LEN;
+  packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, 0);
+  silc_packet_send_prepare(sock,
+                          SILC_PACKET_HEADER_LEN,
+                          packetdata.padlen,
+                          packet->len);
+  packetdata.buffer = sock->outbuf;
+  silc_buffer_put(sock->outbuf, packet->data, packet->len);
+  silc_packet_assemble(&packetdata, NULL);
+  ret = silc_packet_send(sock, TRUE);
+  if (ret != -2)
+    return;
+
+  silc_schedule_set_listen_fd(server->schedule, sock->sock, 
+                             (SILC_TASK_READ | SILC_TASK_WRITE));
+  SILC_SET_OUTBUF_PENDING(sock);
+}
+
+static bool packet_parse(SilcPacketParserContext *parser, void *context)
+{
+  Server server = (Server)parser->context;
+  SilcSocketConnection sock = parser->sock;
+  SilcPacketContext *packet = parser->packet;
+  int ret;
+  
+  ret = silc_packet_parse(packet, NULL);
+  assert(packet->type == SILC_PACKET_FTP);
+
+  silc_sftp_server_receive_process(server->sftp[sock->sock], sock, packet);
+
+  return TRUE;
+}
+
+SILC_TASK_CALLBACK(packet_process)
+{
+  Server server = (Server)context;
+  SilcSocketConnection sock = server->socks[fd];
+  int ret;
+
+  if (!sock)
+    return;
+
+  if (type == SILC_TASK_WRITE) {
+    if (sock->outbuf->data - sock->outbuf->head)
+      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    ret = silc_packet_send(sock, TRUE);
+    if (ret < 0)
+      return;
+
+    silc_schedule_set_listen_fd(server->schedule, fd, SILC_TASK_READ);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+    return;
+  }
+
+  if (type == SILC_TASK_READ) {
+    ret = silc_packet_receive(sock);
+    if (ret < 0)
+      return;
+
+    if (ret == 0) {
+      silc_net_close_connection(sock->sock);
+      silc_schedule_unset_listen_fd(server->schedule, sock->sock);
+      server->socks[sock->sock] = NULL;
+      silc_socket_free(sock);
+      return;
+    }
+
+    silc_packet_receive_process(sock, FALSE, NULL, NULL, 0, packet_parse, 
+                               server);
+  }
+}
+
+SILC_TASK_CALLBACK(accept_connection)
+{
+  Server server = (Server)context;
+  SilcSocketConnection sc;
+  int sock;
+
+  sock = silc_net_accept_connection(server->sock);
+  if (sock < 0)
+    exit(1);
+
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  silc_socket_alloc(sock, 0, NULL, &sc);
+  server->socks[sock] = sc;
+  server->sftp[sock] = 
+    silc_sftp_server_start(sc, send_packet, server,
+                          server->fs);
+  silc_schedule_task_add(server->schedule, sock, packet_process,
+                        server, 0, 0, SILC_TASK_GENERIC,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+int main()
+{
+  Server server = silc_calloc(1, sizeof(*server));
+  void *dir;
+
+  silc_debug = 1;
+
+  server->schedule = silc_schedule_init(100);
+  if (!server->schedule)
+    return -1;
+
+  server->sock = silc_net_create_server(5000, NULL);
+  if (server->sock < 0)
+    return -1;
+
+  /* Make test filesystem hierarchy */
+
+  server->fs = silc_sftp_fs_memory_alloc((SILC_SFTP_FS_PERM_READ |
+                                         SILC_SFTP_FS_PERM_WRITE));
+  dir =
+    silc_sftp_fs_memory_add_dir(server->fs, NULL, (SILC_SFTP_FS_PERM_READ |
+                                                  SILC_SFTP_FS_PERM_WRITE |
+                                                  SILC_SFTP_FS_PERM_EXEC),
+                               "sftp");
+  silc_sftp_fs_memory_add_file(server->fs, NULL, SILC_SFTP_FS_PERM_READ,
+                              "passwd", "file:///etc/passwd");
+  silc_sftp_fs_memory_add_file(server->fs, NULL, (SILC_SFTP_FS_PERM_READ |
+                                                 SILC_SFTP_FS_PERM_WRITE),
+                              "writeme", "file://./writeme-test");
+  silc_sftp_fs_memory_add_file(server->fs, dir, SILC_SFTP_FS_PERM_READ,
+                              "shadow", "file:///etc/shadow");
+  silc_sftp_fs_memory_add_file(server->fs, dir, SILC_SFTP_FS_PERM_READ,
+                              "sftp_server.c", "file://sftp_server.c");
+  silc_sftp_fs_memory_add_dir(server->fs, dir, (SILC_SFTP_FS_PERM_READ |
+                                               SILC_SFTP_FS_PERM_WRITE |
+                                               SILC_SFTP_FS_PERM_EXEC),
+                              "Mail");
+  silc_sftp_fs_memory_add_file(server->fs, NULL, SILC_SFTP_FS_PERM_EXEC,
+                              "testi", "file://sftp_client.c");
+
+  silc_schedule_task_add(server->schedule, server->sock, 
+                        accept_connection, server, 0, 0,
+                        SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
+  silc_schedule(server->schedule);
+
+  return 0;
+}
index dc920e8d78e392088c694abe5c4770f2916108e9..1608b73a333b64448622ddcce265e17d918d4e51 100644 (file)
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
+if SILC_SIM
 noinst_LIBRARIES = libsilcsim.a
+else
+noinst_LIBRARIES = 
+endif
 
 libsilcsim_a_SOURCES = \
        silcsim.c \
        silcsimutil.c
 
-SIM_CFLAGS = -fPIC -shared
-
-SIM_MODULES_DIR = modules
-
-SUBDIRS = modules
-
 #
 # SILC Ciphers to be compiled as modules
 #
@@ -39,9 +37,10 @@ SIM_CIPHER_OBJS = \
         rc5.o \
         rc6.o \
         mars.o \
-        rijndael.o \
+        aes.o \
         rsa.o \
-        twofish.o
+        twofish.o \
+       cast.o
 
 #
 # SILC Hash Functions to be compiled as modules
@@ -50,26 +49,26 @@ SIM_HASH_OBJS = \
        md5.o \
        sha1.o
 
+if SILC_SIM
 all: $(SIM_CIPHER_OBJS) $(SIM_HASH_OBJS)
+endif
 
 $(SIM_CIPHER_OBJS): ../silccrypt/libsilccrypt.a
-       rm -rf $*.c $*.o
-       $(LN_S) $(srcdir)/../silccrypt/$*.c
-       $(COMPILE) $(SIM_CFLAGS) $*.c -o $(SIM_MODULES_DIR)/$*.sim.so
-       $(LN_S) $(srcdir)/$(SIM_MODULES_DIR)/$*.sim.so $*.o
-       rm -rf $*.c
+       $(LTCOMPILE) -c $(srcdir)/../silccrypt/$*.c
+       $(LIBTOOL) --mode=link $(LINK) -rpath $(silc_modulesdir) -o lib$*.la $*.lo
+       cd $(srcdir) && $(LN_S) -f $(srcdir)/.libs/lib$*.so $(srcdir)/$*.sim.so
 
 $(SIM_HASH_OBJS): ../silccrypt/libsilccrypt.a
-       rm -rf $*.c $*.o
-       $(LN_S) $(srcdir)/../silccrypt/$*.c
-       $(COMPILE) $(SIM_CFLAGS) $*.c -o $(SIM_MODULES_DIR)/$*.sim.so
-       $(LN_S) $(srcdir)/$(SIM_MODULES_DIR)/$*.sim.so $*.o
-       rm -rf $*.c
+       $(LTCOMPILE) -c $(srcdir)/../silccrypt/$*.c
+       $(LIBTOOL) --mode=link $(LINK) -rpath $(silc_modulesdir) -o lib$*.la $*.lo
+       cd $(srcdir) && $(LN_S) -f $(srcdir)/.libs/lib$*.so $(srcdir)/$*.sim.so
+
+CLEANFILES = *.sim.so *.la
 
-CLEANFILES = $(SIM_MODULES_DIR)/*.sim.so
+if SILC_DIST_TOOLKIT
+include_HEADERS = silcsim.h silcsimutil.h
+endif
 
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../silccrypt -I../silcmath -I../silcske \
-       -I../silccore -I../.. -I../../includes \
-       -I../silcmath/gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcsim/modules/Makefile.in b/lib/silcsim/modules/Makefile.in
deleted file mode 100644 (file)
index 1fa3227..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-# Makefile.in generated automatically by automake 1.3 from Makefile.am
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-#
-#  Makefile.am
-#
-#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-#
-#  Copyright (C) 2000 Pekka Riikonen
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-
-
-SHELL = /bin/sh
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-prefix = @prefix@
-exec_prefix = @exec_prefix@
-
-bindir = @bindir@
-sbindir = @sbindir@
-libexecdir = @libexecdir@
-datadir = @datadir@
-sysconfdir = @sysconfdir@
-sharedstatedir = @sharedstatedir@
-localstatedir = @localstatedir@
-libdir = @libdir@
-infodir = @infodir@
-mandir = @mandir@
-includedir = @includedir@
-oldincludedir = /usr/include
-
-DISTDIR =
-
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-
-top_builddir = ../../..
-
-ACLOCAL = @ACLOCAL@
-AUTOCONF = @AUTOCONF@
-AUTOMAKE = @AUTOMAKE@
-AUTOHEADER = @AUTOHEADER@
-
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-transform = @program_transform_name@
-
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_alias = @build_alias@
-build_triplet = @build@
-host_alias = @host_alias@
-host_triplet = @host@
-target_alias = @target_alias@
-target_triplet = @target@
-CC = @CC@
-LN_S = @LN_S@
-MAKEINFO = @MAKEINFO@
-PACKAGE = @PACKAGE@
-RANLIB = @RANLIB@
-VERSION = @VERSION@
-
-AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
-mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
-CONFIG_HEADER = ../../../includes/silcdefs.h
-CONFIG_CLEAN_FILES = 
-DIST_COMMON =  Makefile.am Makefile.in
-
-
-DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
-
-TAR = tar
-GZIP = --best
-all: Makefile
-
-.SUFFIXES:
-$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
-       cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/silcsim/modules/Makefile
-
-Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
-       cd $(top_builddir) \
-         && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
-
-tags: TAGS
-TAGS:
-
-
-distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
-
-subdir = lib/silcsim/modules
-
-distdir: $(DISTFILES)
-       @for file in $(DISTFILES); do \
-         d=$(srcdir); \
-         test -f $(distdir)/$$file \
-         || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
-         || cp -p $$d/$$file $(distdir)/$$file; \
-       done
-info:
-dvi:
-check: all
-       $(MAKE)
-installcheck:
-install-exec: 
-       @$(NORMAL_INSTALL)
-
-install-data: 
-       @$(NORMAL_INSTALL)
-
-install: install-exec install-data all
-       @:
-
-uninstall: 
-
-install-strip:
-       $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install
-installdirs:
-
-
-mostlyclean-generic:
-       -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
-
-clean-generic:
-       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
-       -rm -f Makefile $(DISTCLEANFILES)
-       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
-       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-
-maintainer-clean-generic:
-       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-mostlyclean:  mostlyclean-generic
-
-clean:  clean-generic mostlyclean
-
-distclean:  distclean-generic clean
-       -rm -f config.status
-
-maintainer-clean:  maintainer-clean-generic distclean
-       @echo "This command is intended for maintainers to use;"
-       @echo "it deletes files that may require special tools to rebuild."
-
-.PHONY: tags distdir info dvi installcheck install-exec install-data \
-install uninstall all installdirs mostlyclean-generic distclean-generic \
-clean-generic maintainer-clean-generic clean mostlyclean distclean \
-maintainer-clean
-
-
-all:
-       -cd ..
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
index 5afc108db31dec3f049f21e96a43ed0b1250643d..ab9b3433615c6923d212c040773869922626aa46 100644 (file)
@@ -108,6 +108,7 @@ int silc_sim_close(SilcSimContext *sim)
 
   /* Close the library */
   dlclose(sim->handle);
+  sim->handle = NULL;
 
   return TRUE;
 }
diff --git a/lib/silcske/DIRECTORY b/lib/silcske/DIRECTORY
new file mode 100644 (file)
index 0000000..9c65583
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+@LIBRARY=SILC Key Exchange Library
+@FILENAME=silcskelib.html
+@LINK=silcske.html:SILC SKE API
+@LINK=silcske_status.html:SILC SKE Status
+-->
+
+<BIG><B>SILC Key Exchange Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC Key Exchange (SKE) Library, is an implementation of the SKE protocol.
+It provides the key exchange protocol for all SILC applications.
+
+<BR /><BR />
+@LINKS@
+
index dcc56ed7e0eaca0aad3f92fe59cac0df1c13b7d4..b233c42da5ccf4c94b80589315d98524e1f49462 100644 (file)
@@ -25,8 +25,14 @@ libsilcske_a_SOURCES = \
        payload.c \
         groups.c
 
+if SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       groups.h        \
+       payload.h       \
+       silcske.h       \
+       silcske_status.h
+endif
+
 EXTRA_DIST = *.h
 
-INCLUDES = -I. -I.. -I../silccrypt -I../silccore \
-       -I../silcsim -I../silcmath -I../.. -I../../includes \
-        -I../silcmath/gmp-3.0.1
+include $(top_srcdir)/Makefile.defines.in
index a37b9db3dbf4c53176d88f612ea7889580ff2cfa..c5a947e8866cd96c01304e76efef8b339717e21c 100644 (file)
@@ -2,9 +2,9 @@
 
   groups.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 #include "groups_internal.h"
@@ -36,50 +29,50 @@ const struct SilcSKEDiffieHellmanGroupDefStruct silc_ske_groups[] =
 {
   /* 1024 bits modulus (Mandatory group) */
   { 1, "diffie-hellman-group1",
-    "0x"
-    "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
-    "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
-    "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
-    "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
-    "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381"
-    "FFFFFFFF FFFFFFFF",
-    "0x"
-    "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
-    "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
-    "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
-    "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
-    "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F67329C0"
-    "FFFFFFFF FFFFFFFF",
-    "0x2" },
+
+    "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+    "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+    "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+    "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+    "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+    "FFFFFFFFFFFFFFFF",
+
+    "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68"
+    "948127044533E63A0105DF531D89CD9128A5043CC71A026E"
+    "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122"
+    "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6"
+    "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0"
+    "FFFFFFFFFFFFFFFF",
+    "2" },
 
   /* 1536 bits modulus (Optional group) */
   { 2, "diffie-hellman-group2",
-    "0x"
-    "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1"
-    "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD"
-    "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245"
-    "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED"
-    "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D"
-    "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F"
-    "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D"
-    "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF",
-    "0x"
-    "7FFFFFFF FFFFFFFF E487ED51 10B4611A 62633145 C06E0E68"
-    "94812704 4533E63A 0105DF53 1D89CD91 28A5043C C71A026E"
-    "F7CA8CD9 E69D218D 98158536 F92F8A1B A7F09AB6 B6A8E122"
-    "F242DABB 312F3F63 7A262174 D31BF6B5 85FFAE5B 7A035BF6"
-    "F71C35FD AD44CFD2 D74F9208 BE258FF3 24943328 F6722D9E"
-    "E1003E5C 50B1DF82 CC6D241B 0E2AE9CD 348B1FD4 7E9267AF"
-    "C1B2AE91 EE51D6CB 0E3179AB 1042A95D CF6A9483 B84B4B36"
-    "B3861AA7 255E4C02 78BA3604 6511B993 FFFFFFFF FFFFFFFF",
-    "0x2" },
+
+    "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+    "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+    "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+    "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+    "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+    "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+    "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+    "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",
+
+    "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68"
+    "948127044533E63A0105DF531D89CD9128A5043CC71A026E"
+    "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122"
+    "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6"
+    "F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9E"
+    "E1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AF"
+    "C1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36"
+    "B3861AA7255E4C0278BA36046511B993FFFFFFFFFFFFFFFF",
+    "2" },
 
   { 0, NULL, NULL, NULL }
 };
 
 /* Returns Diffie Hellman group by group number */
 
-SilcSKEStatus silc_ske_get_group_by_number(int number,
+SilcSKEStatus silc_ske_group_get_by_number(int number,
                                           SilcSKEDiffieHellmanGroup *ret)
 {
   int i;
@@ -101,9 +94,9 @@ SilcSKEStatus silc_ske_get_group_by_number(int number,
     silc_mp_init(&group->group);
     silc_mp_init(&group->group_order);
     silc_mp_init(&group->generator);
-    silc_mp_set_str(&group->group, silc_ske_groups[i].group, 0);
-    silc_mp_set_str(&group->group_order, silc_ske_groups[i].group_order, 0);
-    silc_mp_set_str(&group->generator, silc_ske_groups[i].generator, 0);
+    silc_mp_set_str(&group->group, silc_ske_groups[i].group, 16);
+    silc_mp_set_str(&group->group_order, silc_ske_groups[i].group_order, 16);
+    silc_mp_set_str(&group->generator, silc_ske_groups[i].generator, 16);
     
     *ret = group;
   }
@@ -113,7 +106,7 @@ SilcSKEStatus silc_ske_get_group_by_number(int number,
 
 /* Returns Diffie Hellman group by name */
 
-SilcSKEStatus silc_ske_get_group_by_name(const char *name,
+SilcSKEStatus silc_ske_group_get_by_name(const char *name,
                                         SilcSKEDiffieHellmanGroup *ret)
 {
   int i;
@@ -135,9 +128,9 @@ SilcSKEStatus silc_ske_get_group_by_name(const char *name,
     silc_mp_init(&group->group);
     silc_mp_init(&group->group_order);
     silc_mp_init(&group->generator);
-    silc_mp_set_str(&group->group, silc_ske_groups[i].group, 0);
-    silc_mp_set_str(&group->group_order, silc_ske_groups[i].group_order, 0);
-    silc_mp_set_str(&group->generator, silc_ske_groups[i].generator, 0);
+    silc_mp_set_str(&group->group, silc_ske_groups[i].group, 16);
+    silc_mp_set_str(&group->group_order, silc_ske_groups[i].group_order, 16);
+    silc_mp_set_str(&group->generator, silc_ske_groups[i].generator, 16);
     
     *ret = group;
   }
@@ -145,6 +138,16 @@ SilcSKEStatus silc_ske_get_group_by_name(const char *name,
   return SILC_SKE_STATUS_OK;
 }
 
+/* Free group */
+
+void silc_ske_group_free(SilcSKEDiffieHellmanGroup group)
+{
+  silc_mp_uninit(&group->group);
+  silc_mp_uninit(&group->group_order);
+  silc_mp_uninit(&group->generator);
+  silc_free(group);
+}
+
 /* Returns comma separated list of supported groups */
 
 char *silc_ske_get_supported_groups()
@@ -167,3 +170,17 @@ char *silc_ske_get_supported_groups()
 
   return list;
 }
+
+/* Returns the number of the `group'. */
+
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group)
+{
+  return group->number;
+}
+
+/* Returns the name of the `group'. */
+
+const char *silc_ske_group_get_name(SilcSKEDiffieHellmanGroup group)
+{
+  return group->name;
+}
index 29e33be4d080d74a4141c677355ce26c7c24c1a5..724bc3d5f86f1dec73ab9c022f485e6024a78e44 100644 (file)
@@ -2,9 +2,9 @@
 
   groups.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
+/****h* silcske/SilcSKEGroups
+ *
+ * DESCRIPTION
+ *
+ * This interface defines the Diffie Hellman group management and utility
+ * functions for the SKE.  They can be used find DH groups by group number,
+ * and group name.  These routines are used during the SKE session.
+ *
+ ***/
+
 #ifndef GROUPS_H
 #define GROUPS_H
 
 #include "silcske_status.h"
-#include "groups_internal.h"
 
-/* Forward declaration */
+/****s* silcske/SilcSKEGroups/SilcSKEDiffieHellmanGroup
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSKEDiffieHellmanGroupStruct 
+ *                     *SilcSKEDiffieHellmanGroup;
+ *
+ * DESCRIPTION
+ *
+ *    This context represents one Diffie Hellman group, and is returned
+ *    by the utility functions for finding correct groups.  The context
+ *    is freed by calling the silc_ske_group_free function.
+ *
+ ***/
 typedef struct SilcSKEDiffieHellmanGroupStruct *SilcSKEDiffieHellmanGroup;
 
-/* List of defined groups. */
-extern const struct SilcSKEDiffieHellmanGroupDefStruct silc_ske_groups[];
-
 /* Prototypes */
-SilcSKEStatus silc_ske_get_group_by_number(int number,
+
+/****f* silcske/SilcSKEGroups/silc_ske_group_get_by_number
+ *
+ * SYNOPSIS
+ *
+ *    SilcSKEStatus 
+ *    silc_ske_group_get_by_number(int number,
+ *                                 SilcSKEDiffieHellmanGroup *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the Diffie Hellman group into the `ret' pointer by
+ *    group number indicated by the `number'.  Returns error status
+ *    if the group was not found.
+ *
+ ***/
+SilcSKEStatus silc_ske_group_get_by_number(int number,
                                           SilcSKEDiffieHellmanGroup *ret);
-SilcSKEStatus silc_ske_get_group_by_name(const char *name,
+
+/****f* silcske/SilcSKEGroups/silc_ske_group_get_by_name
+ *
+ * SYNOPSIS
+ *
+ *    SilcSKEStatus 
+ *    silc_ske_get_group_by_name(const char *name,
+ *                               SilcSKEDiffieHellmanGroup *ret);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the Diffie Hellman group into the `ret' pointer by
+ *    group name indicated by the `name'.  Returns error status
+ *    if the group was not found.
+ *
+ ***/
+SilcSKEStatus silc_ske_group_get_by_name(const char *name,
                                         SilcSKEDiffieHellmanGroup *ret);
+
+/****f* silcske/SilcSKEGroups/silc_ske_group_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_ske_group_free(SilcSKEDiffieHellmanGroup group);
+ *
+ * DESCRIPTION
+ *
+ *    Free the Diffie Hellman group indicated by the `group'.
+ *
+ ***/
+void silc_ske_group_free(SilcSKEDiffieHellmanGroup group);
+
+/****f* silcske/SilcSKEGroups/silc_ske_get_supported_groups
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_ske_get_supported_groups();
+ *
+ * DESCRIPTION
+ *
+ *    Returns a comma separated list of support Diffie Hellman groups.
+ *    This can be used to get the list of supported groups for SKE
+ *    packets.
+ *
+ ***/
 char *silc_ske_get_supported_groups();
 
+/****f* silcske/SilcSKEGroups/silc_ske_group_get_number
+ *
+ * SYNOPSIS
+ *
+ *    int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group);
+ *
+ * DESCRIPTION
+ *
+ *    Return the group number of the group indicated by the `group'.
+ *
+ ***/
+int silc_ske_group_get_number(SilcSKEDiffieHellmanGroup group);
+
+/****f* silcske/SilcSKEGroups/silc_ske_group_get_name
+ *
+ * SYNOPSIS
+ *
+ *    const char *silc_ske_group_get_name(SilcSKEDiffieHellmanGroup group);
+ *
+ * DESCRIPTION
+ *
+ *    Return the group name of the group indicated by the `group'.
+ *
+ ***/
+const char *silc_ske_group_get_name(SilcSKEDiffieHellmanGroup group);
+
 #endif
index 015c125eed8cd2911d9d12f9b171bedf4a100380..eb62758365bc6434ac12b1775db3a4ed6adb1cf7 100644 (file)
@@ -2,9 +2,9 @@
 
   groups_internal.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -34,9 +34,12 @@ struct SilcSKEDiffieHellmanGroupDefStruct {
 struct SilcSKEDiffieHellmanGroupStruct {
   int number;
   char *name;
-  SilcInt group;
-  SilcInt group_order;
-  SilcInt generator;
+  SilcMPInt group;
+  SilcMPInt group_order;
+  SilcMPInt generator;
 };
 
+/* List of defined groups. */
+extern const struct SilcSKEDiffieHellmanGroupDefStruct silc_ske_groups[];
+
 #endif
index 45c9c7cc5c0fb0661cd6345ff09f35c740fd9d22..c2b64be0536a87a3c3f5b1641b8a18290976748d 100644 (file)
@@ -2,9 +2,9 @@
 
   payload.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/* XXX TODO: This is not optimized version and should be optimized! 
-   Use *_ALLOC buffer formatting in payload decodings! */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
-#include "payload_internal.h"
-
-/* Temporary buffer used in payload decoding */
-unsigned char buf[16384];
 
 /* Encodes Key Exchange Start Payload into a SILC Buffer to be sent
    to the other end. */
@@ -42,44 +29,49 @@ SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
                                            SilcBuffer *return_buffer)
 {
   SilcBuffer buf;
+  int ret;
 
   SILC_LOG_DEBUG(("Encoding KE Start Payload"));
 
   if (!payload)
     return SILC_SKE_STATUS_ERROR;
 
-  /* Allocate channel payload buffer. */
   buf = silc_buffer_alloc(payload->len);
-  if (!buf) {
-    SILC_LOG_ERROR(("Could not allocate encode buffer"));
-    return SILC_SKE_STATUS_ERROR;
-  }
-
-  silc_buffer_pull_tail(buf, payload->len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
 
   /* Encode the payload */
-  silc_buffer_format(buf,
-                    SILC_STR_UI_CHAR(0),        /* RESERVED field */
-                    SILC_STR_UI_CHAR(payload->flags),
-                    SILC_STR_UI_SHORT(payload->len),
-                    SILC_STR_UI_XNSTRING(payload->cookie, 
-                                         payload->cookie_len),
-                    SILC_STR_UI_SHORT(payload->ke_grp_len),
-                    SILC_STR_UI_XNSTRING(payload->ke_grp_list,
-                                         payload->ke_grp_len),
-                    SILC_STR_UI_SHORT(payload->pkcs_alg_len),
-                    SILC_STR_UI_XNSTRING(payload->pkcs_alg_list,
-                                         payload->pkcs_alg_len),
-                    SILC_STR_UI_SHORT(payload->enc_alg_len),
-                    SILC_STR_UI_XNSTRING(payload->enc_alg_list,
-                                         payload->enc_alg_len),
-                    SILC_STR_UI_SHORT(payload->hash_alg_len),
-                    SILC_STR_UI_XNSTRING(payload->hash_alg_list,
-                                         payload->hash_alg_len),
-                    SILC_STR_UI_SHORT(payload->comp_alg_len),
-                    SILC_STR_UI_XNSTRING(payload->comp_alg_list,
-                                         payload->comp_alg_len),
-                    SILC_STR_END);
+  ret = silc_buffer_format(buf,
+                          SILC_STR_UI_CHAR(0),        /* RESERVED field */
+                          SILC_STR_UI_CHAR(payload->flags),
+                          SILC_STR_UI_SHORT(payload->len),
+                          SILC_STR_UI_XNSTRING(payload->cookie, 
+                                               payload->cookie_len),
+                          SILC_STR_UI_SHORT(payload->version_len),
+                          SILC_STR_UI_XNSTRING(payload->version, 
+                                               payload->version_len),
+                          SILC_STR_UI_SHORT(payload->ke_grp_len),
+                          SILC_STR_UI_XNSTRING(payload->ke_grp_list,
+                                               payload->ke_grp_len),
+                          SILC_STR_UI_SHORT(payload->pkcs_alg_len),
+                          SILC_STR_UI_XNSTRING(payload->pkcs_alg_list,
+                                               payload->pkcs_alg_len),
+                          SILC_STR_UI_SHORT(payload->enc_alg_len),
+                          SILC_STR_UI_XNSTRING(payload->enc_alg_list,
+                                               payload->enc_alg_len),
+                          SILC_STR_UI_SHORT(payload->hash_alg_len),
+                          SILC_STR_UI_XNSTRING(payload->hash_alg_list,
+                                               payload->hash_alg_len),
+                          SILC_STR_UI_SHORT(payload->hmac_alg_len),
+                          SILC_STR_UI_XNSTRING(payload->hmac_alg_list,
+                                               payload->hmac_alg_len),
+                          SILC_STR_UI_SHORT(payload->comp_alg_len),
+                          SILC_STR_UI_XNSTRING(payload->comp_alg_list,
+                                               payload->comp_alg_len),
+                          SILC_STR_END);
+  if (ret == -1) {
+    silc_buffer_free(buf);
+    return SILC_SKE_STATUS_ERROR;
+  }
 
   /* Return the encoded buffer */
   *return_buffer = buf;
@@ -100,23 +92,42 @@ silc_ske_payload_start_decode(SilcSKE ske,
   SilcSKEStartPayload *payload;
   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
   unsigned char tmp;
-  int len, len2;
+  int ret;
 
   SILC_LOG_DEBUG(("Decoding Key Exchange Start Payload"));
 
   SILC_LOG_HEXDUMP(("KE Start Payload"), buffer->data, buffer->len);
 
   payload = silc_calloc(1, sizeof(*payload));
-  memset(buf, 0, sizeof(buf));
+  payload->cookie_len = SILC_SKE_COOKIE_LEN;
 
-  /* Parse the entire payload */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_CHAR(&tmp),     /* RESERVED Field */
-                      SILC_STR_UI_CHAR(&payload->flags),
-                      SILC_STR_UI_SHORT(&payload->len),
-                      SILC_STR_UI_XNSTRING(&buf, SILC_SKE_COOKIE_LEN),
-                      SILC_STR_UI_SHORT(&payload->ke_grp_len),
-                      SILC_STR_END);
+  /* Parse start of the payload */
+  ret = 
+    silc_buffer_unformat(buffer,
+                        SILC_STR_UI_CHAR(&tmp),     /* RESERVED Field */
+                        SILC_STR_UI_CHAR(&payload->flags),
+                        SILC_STR_UI_SHORT(&payload->len),
+                        SILC_STR_UI_XNSTRING_ALLOC(&payload->cookie, 
+                                                   payload->cookie_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->version,
+                                                    &payload->version_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->ke_grp_list,
+                                                    &payload->ke_grp_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->pkcs_alg_list,
+                                                    &payload->pkcs_alg_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->enc_alg_list,
+                                                    &payload->enc_alg_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->hash_alg_list,
+                                                    &payload->hash_alg_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->hmac_alg_list,
+                                                    &payload->hmac_alg_len),
+                        SILC_STR_UI16_NSTRING_ALLOC(&payload->comp_alg_list,
+                                                    &payload->comp_alg_len),
+                        SILC_STR_END);
+  if (ret == -1) {
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
 
   if (tmp != 0) {
     SILC_LOG_DEBUG(("Bad reserved field"));
@@ -130,110 +141,6 @@ silc_ske_payload_start_decode(SilcSKE ske,
     goto err;
   }
 
-  if (payload->ke_grp_len < 1) {
-    SILC_LOG_DEBUG(("Bad payload length"));
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
-    goto err;
-  }
-
-  len2 = len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 2;
-  silc_buffer_pull(buffer, len);
-
-  /* Copy cookie from payload */
-  payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, 
-                               sizeof(unsigned char));
-  payload->cookie_len = SILC_SKE_COOKIE_LEN;
-  memcpy(payload->cookie, buf, SILC_SKE_COOKIE_LEN);
-  memset(buf, 0, sizeof(buf));
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, payload->ke_grp_len),
-                      SILC_STR_UI_SHORT(&payload->pkcs_alg_len),
-                      SILC_STR_END);
-
-  if (payload->pkcs_alg_len < 1) {
-    SILC_LOG_DEBUG(("Bad payload length"));
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
-    goto err;
-  }
-
-  len2 += len = payload->ke_grp_len + 2;
-  silc_buffer_pull(buffer, len);
-
-  /* Copy KE groups from payload */
-  payload->ke_grp_list = silc_calloc(payload->ke_grp_len + 1, 
-                                    sizeof(unsigned char));
-  memcpy(payload->ke_grp_list, buf, payload->ke_grp_len);
-  memset(buf, 0, sizeof(buf));
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, payload->pkcs_alg_len),
-                      SILC_STR_UI_SHORT(&payload->enc_alg_len),
-                      SILC_STR_END);
-
-  if (payload->enc_alg_len < 1) {
-    SILC_LOG_DEBUG(("Bad payload length"));
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
-    goto err;
-  }
-
-  len2 += len = payload->pkcs_alg_len + 2;
-  silc_buffer_pull(buffer, len);
-
-  /* Copy PKCS algs from payload */
-  payload->pkcs_alg_list = silc_calloc(payload->pkcs_alg_len + 1, 
-                                      sizeof(unsigned char));
-  memcpy(payload->pkcs_alg_list, buf, payload->pkcs_alg_len);
-  memset(buf, 0, sizeof(buf));
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, payload->enc_alg_len),
-                      SILC_STR_UI_SHORT(&payload->hash_alg_len),
-                      SILC_STR_END);
-
-  if (payload->hash_alg_len < 1) {
-    SILC_LOG_DEBUG(("Bad payload length"));
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
-    goto err;
-  }
-
-  len2 += len = payload->enc_alg_len + 2;
-  silc_buffer_pull(buffer, len);
-
-  /* Copy encryption algs from payload */
-  payload->enc_alg_list = silc_calloc(payload->enc_alg_len + 1, 
-                                     sizeof(unsigned char));
-  memcpy(payload->enc_alg_list, buf, payload->enc_alg_len);
-  memset(buf, 0, sizeof(buf));
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, payload->hash_alg_len),
-                      SILC_STR_UI_SHORT(&payload->comp_alg_len),
-                      SILC_STR_END);
-
-  len2 += len = payload->hash_alg_len + 2;
-  silc_buffer_pull(buffer, len);
-
-  /* Copy hash algs from payload */
-  payload->hash_alg_list = silc_calloc(payload->hash_alg_len + 1, 
-                                      sizeof(unsigned char));
-  memcpy(payload->hash_alg_list, buf, payload->hash_alg_len);
-  memset(buf, 0, sizeof(buf));
-
-  if (payload->comp_alg_len) {
-    silc_buffer_unformat(buffer,
-                        SILC_STR_UI_XNSTRING(&buf, payload->comp_alg_len),
-                        SILC_STR_END);
-
-    /* Copy compression algs from payload */
-    payload->comp_alg_list = silc_calloc(payload->comp_alg_len + 1, 
-                                        sizeof(unsigned char));
-    memcpy(payload->comp_alg_list, buf, payload->comp_alg_len);
-    memset(buf, 0, sizeof(buf));
-  }
-
-  silc_buffer_push(buffer, len2);
-
   /* Return the payload */
   *return_payload = payload;
 
@@ -242,6 +149,7 @@ silc_ske_payload_start_decode(SilcSKE ske,
  err:
   silc_ske_payload_start_free(payload);
 
+  ske->status = status;
   return status;
 }
 
@@ -250,278 +158,161 @@ silc_ske_payload_start_decode(SilcSKE ske,
 void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
 {
   if (payload) {
-    if (payload->cookie)
-      silc_free(payload->cookie);
-    if (payload->ke_grp_list)
-      silc_free(payload->ke_grp_list);
-    if (payload->pkcs_alg_list)
-      silc_free(payload->pkcs_alg_list);
-    if (payload->enc_alg_list)
-      silc_free(payload->enc_alg_list);
-    if (payload->hash_alg_list)
-      silc_free(payload->hash_alg_list);
-    if (payload->comp_alg_list)
-      silc_free(payload->comp_alg_list);
+    silc_free(payload->cookie);
+    silc_free(payload->version);
+    silc_free(payload->ke_grp_list);
+    silc_free(payload->pkcs_alg_list);
+    silc_free(payload->enc_alg_list);
+    silc_free(payload->hash_alg_list);
+    silc_free(payload->hmac_alg_list);
+    silc_free(payload->comp_alg_list);
     silc_free(payload);
   }
 }
 
-/* Encodes Key Exchange 1 Payload into a SILC Buffer to be sent
-   to the other end. */
+/* Encodes Key Exchange Payload into a SILC Buffer to be sent to the other
+   end. */
 
-SilcSKEStatus silc_ske_payload_one_encode(SilcSKE ske,
-                                         SilcSKEOnePayload *payload,
-                                         SilcBuffer *return_buffer)
+SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
+                                        SilcSKEKEPayload *payload,
+                                        SilcBuffer *return_buffer)
 {
   SilcBuffer buf;
-  unsigned char *e_str;
-  unsigned short e_len;
+  unsigned char *x_str;
+  uint32 x_len;
+  int ret;
 
-  SILC_LOG_DEBUG(("Encoding KE Payload"));
+  SILC_LOG_DEBUG(("Encoding KE Payload"));
 
   if (!payload)
     return SILC_SKE_STATUS_ERROR;
 
-  /* Encode the integer into HEX string */
-  e_len = silc_mp_sizeinbase(&payload->e, 16);
-  e_str = silc_calloc(e_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(e_str, 16, &payload->e);
-
-  /* Allocate channel payload buffer. The length of the buffer
-     is 2 + e. */
-  buf = silc_buffer_alloc(e_len + 2);
-  if (!buf) {
-    SILC_LOG_ERROR(("Could not allocate encode buffer"));
+  if (ske->start_payload && 
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
+      !payload->sign_data) {
+    SILC_LOG_DEBUG(("Signature data is missing"));
     return SILC_SKE_STATUS_ERROR;
   }
 
-  silc_buffer_pull_tail(buf, e_len + 2);
-
-  /* Encode the payload */
-  silc_buffer_format(buf, 
-                    SILC_STR_UI_SHORT(e_len + 2),
-                    SILC_STR_UI_XNSTRING(e_str, e_len),
-                    SILC_STR_END);
-
-  /* Return encoded buffer */
-  *return_buffer = buf;
-
-  memset(e_str, 'F', e_len);
-  silc_free(e_str);
-
-  return SILC_SKE_STATUS_OK;
-}
-
-/* Parses the Key Exchange 1 Payload. Parsed data is returned
-   to allocated payload structure. */
-
-SilcSKEStatus silc_ske_payload_one_decode(SilcSKE ske,
-                                         SilcBuffer buffer,
-                                         SilcSKEOnePayload **return_payload)
-{
-  SilcSKEOnePayload *payload;
-  SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
-  unsigned short e_len;
-
-  SILC_LOG_DEBUG(("Decoding Key Exchange 1 Payload"));
-
-  SILC_LOG_HEXDUMP(("KE 1 Payload"), buffer->data, buffer->len);
-
-  payload = silc_calloc(1, sizeof(*payload));
-
-  memset(buf, 0, sizeof(buf));
-
-  /* Parse the payload */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&e_len),
-                      SILC_STR_END);
-                      
-  if (e_len < 1) {
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
-    goto err;
-  }
-
-  if (e_len != buffer->len) {
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
-    goto err;
-  }
-
-  /* Length includes the length field length as well. Remove it. */
-  e_len -= 2;
-
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(NULL),
-                      SILC_STR_UI_XNSTRING(&buf, e_len),
-                      SILC_STR_END);
-
-  /* Decode the HEX string to integer */
-  silc_mp_init(&payload->e);
-  silc_mp_set_str(&payload->e, buf, 16);
-  memset(buf, 0, sizeof(buf));
-
-  /* Return the payload */
-  *return_payload = payload;
-
-  return SILC_SKE_STATUS_OK;
-
- err:
-  silc_free(payload);
-
-  return status;
-}
-
-/* Free's KE1 Payload */
-
-void silc_ske_payload_one_free(SilcSKEOnePayload *payload)
-{
-  if (payload) {
-    silc_mp_clear(&payload->e);
-    silc_free(payload);
-  }
-}
-
-/* Encodes Key Exchange 2 Payload into a SILC Buffer to be sent
-   to the other end. */
-
-SilcSKEStatus silc_ske_payload_two_encode(SilcSKE ske,
-                                         SilcSKETwoPayload *payload,
-                                         SilcBuffer *return_buffer)
-{
-  SilcBuffer buf;
-  unsigned char *f_str;
-  unsigned int f_len;
-  unsigned int len;
-
-  SILC_LOG_DEBUG(("Encoding KE 2 Payload"));
-
-  if (!payload)
-    return SILC_SKE_STATUS_ERROR;
-
-  /* Encode the integer into HEX string */
-  f_len = silc_mp_sizeinbase(&payload->f, 16);
-  f_str = silc_calloc(f_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(f_str, 16, &payload->f);
+  /* Encode the integer into binary data */
+  x_str = silc_mp_mp2bin(&payload->x, 0, &x_len);
 
   /* Allocate channel payload buffer. The length of the buffer
-     is 2 + 2 + public key + 2 + f + 2 + signature. */
-  len = payload->pk_len + 2 + 2 + f_len + 2 + payload->sign_len + 2;
-  buf = silc_buffer_alloc(len);
-  if (!buf) {
-    SILC_LOG_ERROR(("Could not allocate encode buffer"));
-    return SILC_SKE_STATUS_ERROR;
-  }
-
-  silc_buffer_pull_tail(buf, len);
+     is 4 + public key + 2 + x + 2 + signature. */
+  buf = silc_buffer_alloc(4 + payload->pk_len + 2 + x_len + 
+                         2 + payload->sign_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
 
   /* Encode the payload */
-  silc_buffer_format(buf, 
-                    SILC_STR_UI_SHORT(payload->pk_len + 4),
-                    SILC_STR_UI_SHORT(payload->pk_type),
-                    SILC_STR_UI_XNSTRING(payload->pk_data, 
-                                         payload->pk_len),
-                    SILC_STR_UI_SHORT(f_len + 2),
-                    SILC_STR_UI_XNSTRING(f_str, f_len),
-                    SILC_STR_UI_SHORT(payload->sign_len + 2),
-                    SILC_STR_UI_XNSTRING(payload->sign_data, 
-                                         payload->sign_len),
-                    SILC_STR_END);
+  ret = silc_buffer_format(buf, 
+                          SILC_STR_UI_SHORT(payload->pk_len),
+                          SILC_STR_UI_SHORT(payload->pk_type),
+                          SILC_STR_UI_XNSTRING(payload->pk_data, 
+                                               payload->pk_len),
+                          SILC_STR_UI_SHORT(x_len),
+                          SILC_STR_UI_XNSTRING(x_str, x_len),
+                          SILC_STR_UI_SHORT(payload->sign_len),
+                          SILC_STR_UI_XNSTRING(payload->sign_data, 
+                                               payload->sign_len),
+                          SILC_STR_END);
+  if (ret == -1) {
+    memset(x_str, 'F', x_len);
+    silc_free(x_str);
+    silc_buffer_free(buf);
+    return SILC_SKE_STATUS_ERROR;
+  }
 
   /* Return encoded buffer */
   *return_buffer = buf;
 
-  memset(f_str, 'F', f_len);
-  silc_free(f_str);
+  SILC_LOG_HEXDUMP(("KE Payload"), buf->data, buf->len);
+
+  memset(x_str, 'F', x_len);
+  silc_free(x_str);
 
   return SILC_SKE_STATUS_OK;
 }
 
-/* Parses the Key Exchange 2 Payload. Parsed data is returned
-   to allocated payload structure. */
+/* Parses the Key Exchange Payload. Parsed data is returned to allocated
+   payload structure. */
 
-SilcSKEStatus silc_ske_payload_two_decode(SilcSKE ske,
-                                         SilcBuffer buffer,
-                                         SilcSKETwoPayload **return_payload)
+SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
+                                        SilcBuffer buffer,
+                                        SilcSKEKEPayload **return_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
-  SilcSKETwoPayload *payload;
-  unsigned short f_len;
-  unsigned int tot_len = 0, len2;
+  SilcSKEKEPayload *payload;
+  unsigned char *x = NULL;
+  uint16 x_len;
+  uint32 tot_len = 0, len2;
+  int ret;
 
-  SILC_LOG_DEBUG(("Decoding Key Exchange Payload"));
+  SILC_LOG_DEBUG(("Decoding Key Exchange Payload"));
 
-  SILC_LOG_HEXDUMP(("KE Payload"), buffer->data, buffer->len);
+  SILC_LOG_HEXDUMP(("KE Payload"), buffer->data, buffer->len);
 
   payload = silc_calloc(1, sizeof(*payload));
-  memset(buf, 0, sizeof(buf));
 
   len2 = buffer->len;
 
-  /* Parse the payload */
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_SHORT(&payload->pk_len),
-                      SILC_STR_UI_SHORT(&payload->pk_type),
-                      SILC_STR_END);
+  /* Parse start of the payload */
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI_SHORT(&payload->pk_len),
+                            SILC_STR_UI_SHORT(&payload->pk_type),
+                            SILC_STR_END);
+  if (ret == -1) {
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
 
-  if (payload->pk_len < 5) {
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
+  if (payload->pk_type == 0) {
+    status = SILC_SKE_STATUS_BAD_PAYLOAD;
     goto err;
   }
 
-  tot_len += payload->pk_len;
+  tot_len += payload->pk_len + 4;
 
-  payload->pk_len -= 4;
+  /* Parse PK data and the signature */
   silc_buffer_pull(buffer, 4);
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, payload->pk_len),
-                      SILC_STR_UI_SHORT(&f_len),
-                      SILC_STR_END);
-
-  if (f_len < 3) {
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
+  ret = silc_buffer_unformat(buffer,
+                            SILC_STR_UI_XNSTRING_ALLOC(&payload->pk_data,
+                                                       payload->pk_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&x, &x_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&payload->sign_data, 
+                                                        &payload->sign_len),
+                            SILC_STR_END);
+  if (ret == -1) {
+    status = SILC_SKE_STATUS_ERROR;
     goto err;
   }
 
-  tot_len += f_len;
-
-  payload->pk_data = silc_calloc(payload->pk_len + 1, 
-                                sizeof(unsigned char));
-  memcpy(payload->pk_data, buf, payload->pk_len);
-  memset(buf, 0, sizeof(buf));
+  tot_len += x_len + 2;
+  tot_len += payload->sign_len + 2;
 
-  f_len -= 2;
-  silc_buffer_pull(buffer, payload->pk_len + 2);
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, f_len),
-                      SILC_STR_UI_SHORT(&payload->sign_len),
-                      SILC_STR_END);
-
-  if (payload->sign_len < 3) {
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
+  if (x_len < 3) {
+    status = SILC_SKE_STATUS_BAD_PAYLOAD;
     goto err;
   }
 
-  tot_len += payload->sign_len;
+  if (ske->start_payload && 
+      (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
+      (payload->sign_len < 3 || !payload->sign_data)) {
+    SILC_LOG_DEBUG(("The signature data is missing - both parties are "
+                   "required to do authentication"));
+    status = SILC_SKE_STATUS_BAD_PAYLOAD;
+    goto err;
+  }
 
   if (tot_len != len2) {
-    status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
+    status = SILC_SKE_STATUS_BAD_PAYLOAD;
     goto err;
   }
   
-  /* Decode the HEX string to integer */
-  silc_mp_init(&payload->f);
-  silc_mp_set_str(&payload->f, buf, 16);
-  memset(buf, 0, sizeof(buf));
-
-  payload->sign_len -= 2;
-  silc_buffer_pull(buffer, f_len + 2);
-  silc_buffer_unformat(buffer,
-                      SILC_STR_UI_XNSTRING(&buf, payload->sign_len),
-                      SILC_STR_END);
-
-  payload->sign_data = silc_calloc(payload->sign_len + 1, 
-                                sizeof(unsigned char));
-  memcpy(payload->sign_data, buf, payload->sign_len);
-  memset(buf, 0, sizeof(buf));
+  /* Decode the binary data to integer */
+  silc_mp_init(&payload->x);
+  silc_mp_bin2mp(x, x_len, &payload->x);
+  memset(x, 0, sizeof(x_len));
+  silc_free(x);
 
   /* Return the payload */
   *return_payload = payload;
@@ -529,25 +320,22 @@ SilcSKEStatus silc_ske_payload_two_decode(SilcSKE ske,
   return SILC_SKE_STATUS_OK;
 
  err:
-  if (payload->pk_data)
-    silc_free(payload->pk_data);
-  if (payload->sign_data)
-    silc_free(payload->sign_data);
+  silc_free(payload->pk_data);
+  silc_free(payload->sign_data);
+  silc_free(x);
   silc_free(payload);
-
+  ske->status = status;
   return status;
 }
 
-/* Free's KE2 Payload */
+/* Free's KE Payload */
 
-void silc_ske_payload_two_free(SilcSKETwoPayload *payload)
+void silc_ske_payload_ke_free(SilcSKEKEPayload *payload)
 {
   if (payload) {
-    if (payload->pk_data)
-      silc_free(payload->pk_data);
-    if (payload->sign_data)
-      silc_free(payload->sign_data);
-    silc_mp_clear(&payload->f);
+    silc_free(payload->pk_data);
+    silc_mp_uninit(&payload->x);
+    silc_free(payload->sign_data);
     silc_free(payload);
   }
 }
index fa88e4155acec2b21d6cc9800dbf28089cce74d3..ba9000422af3b94eee0696f4a6bc1950df471bae 100644 (file)
@@ -2,9 +2,9 @@
 
   payload.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 #define PAYLOAD_H
 
 #include "silcske_status.h"
-#include "payload_internal.h"
+
+/* Forward declarations */
+typedef struct SilcSKEStartPayloadStruct SilcSKEStartPayload;
+typedef struct SilcSKEKEPayloadStruct SilcSKEKEPayload;
+
+/* SILC Key Exchange Start Payload */
+struct SilcSKEStartPayloadStruct {
+  unsigned char flags;
+  uint16 len;
+
+  unsigned char *cookie;
+  uint16 cookie_len;
+
+  unsigned char *version;
+  uint16 version_len;
+
+  uint16 ke_grp_len;
+  unsigned char *ke_grp_list;
+
+  uint16 pkcs_alg_len;
+  unsigned char *pkcs_alg_list;
+
+  uint16 enc_alg_len;
+  unsigned char *enc_alg_list;
+  
+  uint16 hash_alg_len;
+  unsigned char *hash_alg_list;
+
+  uint16 hmac_alg_len;
+  unsigned char *hmac_alg_list;
+
+  uint16 comp_alg_len;
+  unsigned char *comp_alg_list;
+};
+
+/* SILC Key Exchange Payload */
+struct SilcSKEKEPayloadStruct {
+  uint16 pk_len;
+  unsigned char *pk_data;
+  uint16 pk_type;
+
+  SilcMPInt x;
+
+  uint16 sign_len;
+  unsigned char *sign_data;
+};
 
 /* Prototypes */
 SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
@@ -33,19 +78,12 @@ silc_ske_payload_start_decode(SilcSKE ske,
                              SilcBuffer buffer,
                              SilcSKEStartPayload **return_payload);
 void silc_ske_payload_start_free(SilcSKEStartPayload *payload);
-SilcSKEStatus silc_ske_payload_one_encode(SilcSKE ske,
-                                         SilcSKEOnePayload *payload,
-                                         SilcBuffer *return_buffer);
-SilcSKEStatus silc_ske_payload_one_decode(SilcSKE ske,
-                                         SilcBuffer buffer,
-                                         SilcSKEOnePayload **return_payload);
-void silc_ske_payload_one_free(SilcSKEOnePayload *payload);
-SilcSKEStatus silc_ske_payload_two_encode(SilcSKE ske,
-                                         SilcSKETwoPayload *payload,
-                                         SilcBuffer *return_buffer);
-SilcSKEStatus silc_ske_payload_two_decode(SilcSKE ske,
-                                         SilcBuffer buffer,
-                                         SilcSKETwoPayload **return_payload);
-void silc_ske_payload_two_free(SilcSKETwoPayload *payload);
+SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
+                                        SilcSKEKEPayload *payload,
+                                        SilcBuffer *return_buffer);
+SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
+                                        SilcBuffer buffer,
+                                        SilcSKEKEPayload **return_payload);
+void silc_ske_payload_ke_free(SilcSKEKEPayload *payload);
 
 #endif
diff --git a/lib/silcske/payload_internal.h b/lib/silcske/payload_internal.h
deleted file mode 100644 (file)
index f866f46..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-
-  payload_internal.h
-
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 2000 Pekka Riikonen
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-*/
-
-#ifndef PAYLOAD_INTERNAL_H
-#define PAYLOAD_INTERNAL_H
-
-/* SILC Key Exchange Start Payload */
-typedef struct {
-  unsigned char flags;
-  unsigned int len;
-
-  unsigned char *cookie;
-  unsigned short cookie_len;
-
-  unsigned short ke_grp_len;
-  unsigned char *ke_grp_list;
-
-  unsigned short pkcs_alg_len;
-  unsigned char *pkcs_alg_list;
-
-  unsigned short enc_alg_len;
-  unsigned char *enc_alg_list;
-  
-  unsigned short hash_alg_len;
-  unsigned char *hash_alg_list;
-
-  unsigned short comp_alg_len;
-  unsigned char *comp_alg_list;
-} SilcSKEStartPayload;
-
-/* SILC Key Exchange 1 Payload */
-typedef struct {
-  SilcInt e;
-} SilcSKEOnePayload;
-
-/* SILC Key Exchange 2 Payload */
-typedef struct {
-  unsigned short pk_len;
-  unsigned char *pk_data;
-  unsigned short pk_type;
-
-  SilcInt f;
-
-  unsigned short sign_len;
-  unsigned char *sign_data;
-} SilcSKETwoPayload;
-
-#endif
index 8d36c09653dcc1e5d58e4776bd37786ebc8fdba2..a6b23c857918cbf090182b3cfc8f86644c266f7d 100644 (file)
@@ -2,9 +2,9 @@
 
   silcske.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
-#include "payload_internal.h"
+#include "silcske.h"
 #include "groups_internal.h"
 
+/* Structure to hold all SKE callbacks. */
+struct SilcSKECallbacksStruct {
+  SilcSKESendPacketCb send_packet;
+  SilcSKECb payload_receive;
+  SilcSKEVerifyCb verify_key;
+  SilcSKECb proto_continue;
+  SilcSKECheckVersion check_version;
+  void *context;
+};
+
 /* Allocates new SKE object. */
 
 SilcSKE silc_ske_alloc()
@@ -39,10 +42,8 @@ SilcSKE silc_ske_alloc()
   SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
 
   ske = silc_calloc(1, sizeof(*ske));
-  if (!ske) {
-    SILC_LOG_ERROR(("Could not allocate new SKE object"));
-    return NULL;
-  }
+  ske->status = SILC_SKE_STATUS_OK;
+  ske->users = 1;
 
   return ske;
 }
@@ -51,6 +52,13 @@ SilcSKE silc_ske_alloc()
 
 void silc_ske_free(SilcSKE ske)
 {
+  ske->users--;
+  if (ske->users > 0) {
+    SILC_LOG_DEBUG(("Key Exchange set to FREED status"));
+    ske->status = SILC_SKE_STATUS_FREED;
+    return;
+  }
+
   SILC_LOG_DEBUG(("Freeing Key Exchange object"));
 
   if (ske) {
@@ -58,55 +66,110 @@ void silc_ske_free(SilcSKE ske)
     if (ske->start_payload)
       silc_ske_payload_start_free(ske->start_payload);
 
-    /* Free KE1 payload */
+    /* Free KE payload */
     if (ske->ke1_payload)
-      silc_ske_payload_one_free(ske->ke1_payload);
-
-    /* Free KE2 payload */
+      silc_ske_payload_ke_free(ske->ke1_payload);
     if (ske->ke2_payload)
-      silc_ske_payload_two_free(ske->ke2_payload);
+      silc_ske_payload_ke_free(ske->ke2_payload);
 
     /* Free rest */
     if (ske->prop) {
       if (ske->prop->group)
-       silc_free(ske->prop->group);
+       silc_ske_group_free(ske->prop->group);
       if (ske->prop->pkcs)
        silc_pkcs_free(ske->prop->pkcs);
       if (ske->prop->cipher)
        silc_cipher_free(ske->prop->cipher);
       if (ske->prop->hash)
        silc_hash_free(ske->prop->hash);
+      if (ske->prop->hmac)
+       silc_hmac_free(ske->prop->hmac);
       silc_free(ske->prop);
     }
     if (ske->start_payload_copy)
       silc_buffer_free(ske->start_payload_copy);
-    if (ske->pk)
-      silc_free(ske->pk);
-    silc_mp_clear(&ske->x);
-    silc_mp_clear(&ske->KEY);
-    if (ske->hash)
-      silc_free(ske->hash);
+    if (ske->x) {
+      silc_mp_uninit(ske->x);
+      silc_free(ske->x);
+    }
+    if (ske->KEY) {
+      silc_mp_uninit(ske->KEY);
+      silc_free(ske->KEY);
+    }
+    silc_free(ske->hash);
+    silc_free(ske->callbacks);
     silc_free(ske);
   }
 }
 
+/* Sets the callback functions for the SKE session. 
+
+   The `send_packet' callback is a function that sends the packet to
+   network. The SKE library will call it at any time packet needs to
+   be sent to the remote host. 
+
+   The `payload_receive' callback is called when the remote host's Key
+   Exchange Start Payload has been processed.  The payload is saved
+   to ske->start_payload if the application would need it.  The application
+   must also provide the payload to the next state of the SKE.
+
+   The `verify_key' callback is called to verify the received public key
+   or certificate.  The verification process is most likely asynchronous.
+   That is why the application must call the completion callback when the
+   verification process has been completed. The library then calls the user
+   callback (`proto_continue'), if it is provided to indicate that the SKE
+   protocol may continue. 
+   
+   The `proto_continue' callback is called to indicate that it is
+   safe to continue the execution of the SKE protocol after executing
+   an asynchronous operation, such as calling the `verify_key' callback
+   function, which is asynchronous. The application should check the
+   ske->status in this function to check whether it is Ok to continue
+   the execution of the protocol.
+
+   The `check_version' callback is called to verify the remote host's
+   version. The application may check its own version against the remote
+   host's version and determine whether supporting the remote host
+   is possible. 
+
+   The `context' is passed as argument to all of the above callback
+   functions. */
+
+void silc_ske_set_callbacks(SilcSKE ske,
+                           SilcSKESendPacketCb send_packet,
+                           SilcSKECb payload_receive,
+                           SilcSKEVerifyCb verify_key,
+                           SilcSKECb proto_continue,
+                           SilcSKECheckVersion check_version,
+                           void *context)
+{
+  if (ske->callbacks)
+    silc_free(ske->callbacks);
+  ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
+  ske->callbacks->send_packet = send_packet;
+  ske->callbacks->payload_receive = payload_receive;
+  ske->callbacks->verify_key = verify_key;
+  ske->callbacks->proto_continue = proto_continue;
+  ske->callbacks->check_version = check_version;
+  ske->callbacks->context = context;
+}
+
 /* Starts the SILC Key Exchange protocol for initiator. The connection
    to the remote end must be established before calling this function
    and the connecting socket must be sent as argument. This function
-   creates the Key Exchange Start Paload which includes all our
+   creates the Key Exchange Start Payload which includes all our
    configured security properties. This payload is then sent to the
    remote end for further processing. This payload must be sent as
    argument to the function, however, it must not be encoded
-   already, it is done by this function.
+   already, it is done by this function. The caller must not free
+   the `start_payload' since the SKE library will save it.
 
    The packet sending is done by calling a callback function. Caller
    must provide a routine to send the packet. */
 
 SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
                                       SilcSocketConnection sock,
-                                      SilcSKEStartPayload *start_payload,
-                                      SilcSKESendPacketCb send_packet,
-                                      void *context)
+                                      SilcSKEStartPayload *start_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
@@ -124,10 +187,12 @@ SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
   /* Take a copy of the payload buffer for future use. It is used to
      compute the HASH value. */
   ske->start_payload_copy = silc_buffer_copy(payload_buf);
+  ske->start_payload = start_payload;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
@@ -140,9 +205,7 @@ SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
    sent in the silc_ske_initiator_start function. */
 
 SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske, 
-                                        SilcBuffer start_payload,
-                                        SilcSKECb callback,
-                                        void *context)
+                                        SilcBuffer start_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcSKEStartPayload *payload;
@@ -153,8 +216,35 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
 
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, start_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK)
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    silc_ske_payload_start_free(ske->start_payload);
+    return status;
+  }
+
+  /* Check that the cookie is returned unmodified */
+  if (memcmp(ske->start_payload->cookie, payload->cookie,
+            ske->start_payload->cookie_len)) {
+    SILC_LOG_DEBUG(("Responder modified our cookie and it must not do it"));
+    ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
+    silc_ske_payload_start_free(ske->start_payload);
     return status;
+  }
+
+  /* Check version string */
+  if (ske->callbacks->check_version) {
+    status = ske->callbacks->check_version(ske, payload->version, 
+                                          payload->version_len,
+                                          ske->callbacks->context);
+    if (status != SILC_SKE_STATUS_OK) {
+      ske->status = status;
+      silc_ske_payload_start_free(ske->start_payload);
+      return status;
+    }
+  }
+
+  /* Free our KE Start Payload context, we don't need it anymore. */
+  silc_ske_payload_start_free(ske->start_payload);
 
   /* Take the selected security properties into use while doing
      the key exchange. This is used only while doing the key 
@@ -162,7 +252,7 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
      the callback function. */
   ske->prop = prop = silc_calloc(1, sizeof(*prop));
   prop->flags = payload->flags;
-  status = silc_ske_get_group_by_name(payload->ke_grp_list, &group);
+  status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
@@ -183,11 +273,17 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     goto err;
   }
 
+  if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
+    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
+    goto err;
+  }
+
+  /* Save remote's KE Start Payload */
   ske->start_payload = payload;
 
   /* Return the received payload by calling the callback function. */
-  if (callback)
-    (*callback)(ske, context);
+  if (ske->callbacks->payload_receive)
+    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
 
   return status;
 
@@ -195,7 +291,7 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
   if (payload)
     silc_ske_payload_start_free(payload);
 
-  silc_free(group);
+  silc_ske_group_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -203,149 +299,219 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     silc_cipher_free(prop->cipher);
   if (prop->hash)
     silc_hash_free(prop->hash);
+  if (prop->hmac)
+    silc_hmac_free(prop->hmac);
   silc_free(prop);
   ske->prop = NULL;
 
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
 /* This function creates random number x, such that 1 < x < q and 
    computes e = g ^ x mod p and sends the result to the remote end in 
-   Key Exchange Payload. */
+   Key Exchange Payload. */
 
 SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
-                                        SilcSKESendPacketCb send_packet,
-                                        void *context)
+                                        SilcPublicKey public_key,
+                                        SilcPrivateKey private_key)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt x, e;
-  SilcSKEOnePayload *payload;
+  SilcMPInt *x;
+  SilcSKEKEPayload *payload;
+  uint32 pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Create the random number x, 1 < x < q. */
-  silc_mp_init(&x);
+  x = silc_calloc(1, sizeof(*x));
+  silc_mp_init(x);
   status = 
-    silc_ske_create_rnd(ske, ske->prop->group->group_order,
+    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       &x);
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = status;
     return status;
   }
 
+  /* Encode the result to Key Exchange Payload. */
+
+  payload = silc_calloc(1, sizeof(*payload));
+  ske->ke1_payload = payload;
+
   SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
 
   /* Do the Diffie Hellman computation, e = g ^ x mod p */
-  silc_mp_init(&e);
-  silc_mp_powm(&e, &ske->prop->group->generator, &x, 
-              &ske->prop->group->group);
-  
-  /* Encode the result to Key Exchange 1 Payload. */
-  payload = silc_calloc(1, sizeof(*payload));
-  payload->e = e;
-  status = silc_ske_payload_one_encode(ske, payload, &payload_buf);
+  silc_mp_init(&payload->x);
+  silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x, 
+                 &ske->prop->group->group);
+
+  /* Get public key */
+  if (public_key) {
+    payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
+    if (!payload->pk_data) {
+      silc_mp_uninit(x);
+      silc_free(x);
+      silc_mp_uninit(&payload->x);
+      silc_free(payload);
+      ske->status = SILC_SKE_STATUS_OK;
+      return ske->status;
+    }
+    payload->pk_len = pk_len;
+  }
+  payload->pk_type = SILC_SKE_PK_TYPE_SILC;
+
+  /* Compute signature data if we are doing mutual authentication */
+  if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+    unsigned char hash[32], sign[1024];
+    uint32 hash_len, sign_len;
+
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
+    SILC_LOG_DEBUG(("Computing HASH_i value"));
+
+    /* Compute the hash value */
+    memset(hash, 0, sizeof(hash));
+    silc_ske_make_hash(ske, hash, &hash_len, TRUE);
+
+    SILC_LOG_DEBUG(("Signing HASH_i value"));
+    
+    /* Sign the hash value */
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv, 
+                                  private_key->prv_len);
+    silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
+    payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
+    memcpy(payload->sign_data, sign, sign_len);
+    memset(sign, 0, sizeof(sign));
+    payload->sign_len = sign_len;
+  }
+
+  status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
-    silc_mp_clear(&e);
+    silc_mp_uninit(x);
+    silc_free(x);
+    silc_mp_uninit(&payload->x);
+    silc_free(payload->pk_data);
     silc_free(payload);
+    ske->status = status;
     return status;
   }
 
-  ske->ke1_payload = payload;
   ske->x = x;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_1, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, 
+                                  SILC_PACKET_KEY_EXCHANGE_1, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
   return status;
 }
 
-/* Receives Key Exchange 2 Payload from responder consisting responders
-   public key, f, and signature. This function verifies the public key,
-   computes the secret shared key and verifies the signature. */
+/* An initiator finish final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke2_payload,
-                                       SilcSKECb callback,
-                                       void *context)
+static void silc_ske_initiator_finish_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKETwoPayload *payload;
-  SilcInt KEY;
+  SilcSKEKEPayload *payload;
   unsigned char hash[32];
-  unsigned int hash_len;
+  uint32 hash_len;
+  SilcPublicKey public_key = NULL;
+
+  /* If the SKE was freed during the async call then free it really now,
+     otherwise just decrement the reference counter. */
+  if (ske->status == SILC_SKE_STATUS_FREED) {
+    silc_ske_free(ske);
+    return;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  /* If the caller returns PENDING status SKE library will assume that
+     the caller will re-call this callback when it is not anymore in
+     PENDING status. */
+  if (status == SILC_SKE_STATUS_PENDING)
+    return;
 
-  /* Decode the payload */
-  status = silc_ske_payload_two_decode(ske, ke2_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK)
-    return status;
-  ske->ke2_payload = payload;
+  ske->users--;
+  payload = ske->ke2_payload;
 
-  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+  /* If the status is an error then the public key that was verified
+     by the caller is not authentic. */
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    if (ske->callbacks->proto_continue)
+      ske->callbacks->proto_continue(ske, ske->callbacks->context);
+    return;
+  }
 
-  /* Compute the shared secret key */
-  silc_mp_init(&KEY);
-  silc_mp_powm(&KEY, &payload->f, &ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
+  if (payload->pk_data) {
+    /* Decode the public key */
+    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
+                                    &public_key)) {
+      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      if (ske->callbacks->proto_continue)
+       ske->callbacks->proto_continue(ske, ske->callbacks->context);
+      return;
+    }
 
-  SILC_LOG_DEBUG(("Verifying public key"));
+    SILC_LOG_DEBUG(("Public key is authentic"));
 
-  /* Verify the public key */ /* XXX */
-  status = silc_ske_verify_public_key(ske, payload->pk_data, 
-                                     payload->pk_len);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
-  
-  SILC_LOG_DEBUG(("Public key is authentic"));
+    /* Compute the hash value */
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
 
-  /* Compute the hash value */
-  status = silc_ske_make_hash(ske, hash, &hash_len);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+    memcpy(ske->hash, hash, hash_len);
+    ske->hash_len = hash_len;
 
-  ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-  memcpy(ske->hash, hash, hash_len);
-  ske->hash_len = hash_len;
+    SILC_LOG_DEBUG(("Verifying signature (HASH)"));
 
-  SILC_LOG_DEBUG(("Verifying signature"));
-
-  /* Verify signature */
-  silc_pkcs_set_public_key(ske->prop->pkcs, payload->pk_data, payload->pk_len);
-  if (ske->prop->pkcs->pkcs->verify(ske->prop->pkcs->context,
-                                   payload->sign_data, payload->sign_len,
-                                   hash, hash_len) == FALSE) {
-
-    SILC_LOG_DEBUG(("Signature don't match"));
+    /* Verify signature */
+    silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
+    if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, 
+                        payload->sign_len, hash, hash_len) == FALSE) {
+      
+      SILC_LOG_DEBUG(("Signature don't match"));
+      
+      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      goto err;
+    }
 
-    status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-    goto err;
+    SILC_LOG_DEBUG(("Signature is Ok"));
+    
+    silc_pkcs_public_key_free(public_key);
+    memset(hash, 'F', hash_len);
   }
 
-  SILC_LOG_DEBUG(("Signature is Ok"));
+  ske->status = SILC_SKE_STATUS_OK;
 
-  memset(hash, 'F', hash_len);
+  /* Call the callback. The caller may now continue the SKE protocol. */
+  if (ske->callbacks->proto_continue)
+    ske->callbacks->proto_continue(ske, ske->callbacks->context);
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
-
-  return status;
+  return;
 
  err:
-  memset(hash, 'F', hash_len);
-  silc_ske_payload_two_free(payload);
+  memset(hash, 'F', sizeof(hash));
+  silc_ske_payload_ke_free(payload);
+  ske->ke2_payload = NULL;
+
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
 
-  silc_mp_clear(&ske->KEY);
+  if (public_key)
+    silc_pkcs_public_key_free(public_key);
 
   if (ske->hash) {
     memset(ske->hash, 'F', hash_len);
@@ -353,9 +519,98 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
     ske->hash = NULL;
   }
 
+  if (status == SILC_SKE_STATUS_OK)
+    ske->status = SILC_SKE_STATUS_ERROR;
+
+  ske->status = status;
+
+  /* Call the callback. */
+  if (ske->callbacks->proto_continue)
+    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+}
+
+/* Receives Key Exchange Payload from responder consisting responders
+   public key, f, and signature. This function verifies the public key,
+   computes the secret shared key and verifies the signature. 
+
+   The `callback' will be called to indicate that the caller may
+   continue with the SKE protocol.  The caller must not continue
+   before the SKE libary has called that callback.  If this function
+   returns an error the callback will not be called.  It is called
+   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
+   However, note that when the library calls the callback the ske->status
+   may be error.
+
+   This calls the `verify_key' callback to verify the received public
+   key or certificate. If the `verify_key' is provided then the remote
+   must send public key and it is considered to be an error if remote 
+   does not send its public key. If caller is performing a re-key with
+   SKE then the `verify_key' is usually not provided when it is not also
+   required for the remote to send its public key. */
+
+SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
+                                       SilcBuffer ke_payload)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *payload;
+  SilcMPInt *KEY;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode the payload */
+  status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    return status;
+  }
+  ske->ke2_payload = payload;
+
+  if (!payload->pk_data && ske->callbacks->verify_key) {
+    SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
+                   "even though we require it"));
+    ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+    goto err;
+  }
+
+  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+
+  /* Compute the shared secret key */
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
+  ske->KEY = KEY;
+
+  if (payload->pk_data && ske->callbacks->verify_key) {
+    SILC_LOG_DEBUG(("Verifying public key"));
+    
+    ske->users++;
+    (*ske->callbacks->verify_key)(ske, payload->pk_data, payload->pk_len,
+                                payload->pk_type, ske->callbacks->context,
+                                silc_ske_initiator_finish_final, NULL);
+    
+    /* We will continue to the final state after the public key has
+       been verified by the caller. */
+    return SILC_SKE_STATUS_PENDING;
+  }
+
+  /* Continue to final state */
+  ske->users++;
+  silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
+
+  return SILC_SKE_STATUS_OK;
+
+ err:
+  silc_ske_payload_ke_free(payload);
+  ske->ke2_payload = NULL;
+
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
+
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -367,9 +622,9 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
 
 SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
                                       SilcSocketConnection sock,
+                                      char *version,
                                       SilcBuffer start_payload,
-                                      SilcSKECb callback,
-                                      void *context)
+                                      bool mutual_auth)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
@@ -381,24 +636,35 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
 
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, start_payload, &remote_payload);
-  if (status != SILC_SKE_STATUS_OK)
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
     return status;
+  }
 
   /* Take a copy of the payload buffer for future use. It is used to
      compute the HASH value. */
   ske->start_payload_copy = silc_buffer_copy(start_payload);
 
+  /* Force the mutual authentication flag if we want to do it. */
+  if (mutual_auth) {
+    SILC_LOG_DEBUG(("Force mutual authentication"));
+    remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
+  }
+
   /* Parse and select the security properties from the payload */
   payload = silc_calloc(1, sizeof(*payload));
-  status = silc_ske_select_security_properties(ske, payload, remote_payload);
+  status = silc_ske_select_security_properties(ske, version,
+                                              payload, remote_payload);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   ske->start_payload = payload;
 
   /* Call the callback function. */
-  if (callback)
-    (*callback)(ske, context);
+  if (ske->callbacks->payload_receive)
+    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
+
+  silc_ske_payload_start_free(remote_payload);
 
   return status;
 
@@ -411,6 +677,7 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -418,14 +685,12 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
    encoded into Key Exchange Start Payload and sent to the initiator. */
 
 SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske, 
-                                        SilcSKEStartPayload *start_payload,
-                                        SilcSKESendPacketCb send_packet,
-                                        void *context)
+                                        SilcSKEStartPayload *start_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
   SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group;
+  SilcSKEDiffieHellmanGroup group = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
@@ -433,7 +698,7 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
      only for this negotiation and will be free'd after KE is over. */
   ske->prop = prop = silc_calloc(1, sizeof(*prop));
   prop->flags = start_payload->flags;
-  status = silc_ske_get_group_by_name(start_payload->ke_grp_list, &group);
+  status = silc_ske_group_get_by_name(start_payload->ke_grp_list, &group);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
@@ -457,21 +722,29 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
     goto err;
   }
 
+  if (silc_hmac_alloc(start_payload->hmac_alg_list, NULL,
+                     &prop->hmac) == FALSE) {
+    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
+    goto err;
+  }
+
   /* Encode the payload */
   status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
   return status;
 
  err:
-  silc_free(group);
+  if (group)
+    silc_ske_group_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -479,154 +752,299 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
     silc_cipher_free(prop->cipher);
   if (prop->hash)
     silc_hash_free(prop->hash);
+  if (prop->hmac)
+    silc_hmac_free(prop->hmac);
   silc_free(prop);
   ske->prop = NULL;
 
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
-/* This function receives the Key Exchange 1 Payload from the initiator.
-   After processing the payload this then selects random number x,
-   such that 1 < x < q and computes f = g ^ x mod p. This then puts
-   the result f to a Key Exchange 2 Payload which is later processed
-   in ske_responder_finish function. The callback function should
-   not touch the payload (it should merely call the ske_responder_finish
-   function). */
+/* An responder phase 2 final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke1_payload,
-                                        SilcSKECb callback,
-                                        void *context)
+static void silc_ske_responder_phase2_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEOnePayload *one_payload;
-  SilcSKETwoPayload *two_payload;
-  SilcInt x, f;
+  SilcSKEKEPayload *recv_payload, *send_payload;
+  SilcMPInt *x;
+
+  /* If the SKE was freed during the async call then free it really now,
+     otherwise just decrement the reference counter. */
+  if (ske->status == SILC_SKE_STATUS_FREED) {
+    silc_ske_free(ske);
+    return;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  /* If the caller returns PENDING status SKE library will assume that
+     the caller will re-call this callback when it is not anymore in
+     PENDING status. */
+  if (status == SILC_SKE_STATUS_PENDING)
+    return;
 
-  /* Decode Key Exchange 1 Payload */
-  status = silc_ske_payload_one_decode(ske, ke1_payload, &one_payload);
-  if (status != SILC_SKE_STATUS_OK)
-    return status;
+  ske->users--;
+  recv_payload = ske->ke1_payload;
+
+  /* If the status is an error then the public key that was verified
+     by the caller is not authentic. */
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    if (ske->callbacks->proto_continue)
+      ske->callbacks->proto_continue(ske, ske->callbacks->context);
+    return;
+  }
+
+  /* The public key verification was performed only if the Mutual
+     Authentication flag is set. */
+  if (ske->start_payload && 
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+    SilcPublicKey public_key = NULL;
+    unsigned char hash[32];
+    uint32 hash_len;
+
+    /* Decode the public key */
+    if (!silc_pkcs_public_key_decode(recv_payload->pk_data, 
+                                    recv_payload->pk_len, 
+                                    &public_key)) {
+      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      if (ske->callbacks->proto_continue)
+       ske->callbacks->proto_continue(ske, ske->callbacks->context);
+      return;
+    }
+
+    SILC_LOG_DEBUG(("Public key is authentic"));
+
+    /* Compute the hash value */
+    status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
+    if (status != SILC_SKE_STATUS_OK) {
+      ske->status = status;
+      if (ske->callbacks->proto_continue)
+       ske->callbacks->proto_continue(ske, ske->callbacks->context);
+      return;
+    }
+
+    SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
+    
+    /* Verify signature */
+    silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
+    if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data, 
+                        recv_payload->sign_len, hash, hash_len) == FALSE) {
+      
+      SILC_LOG_DEBUG(("Signature don't match"));
+      
+      ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      if (ske->callbacks->proto_continue)
+       ske->callbacks->proto_continue(ske, ske->callbacks->context);
+      return;
+    }
+    
+    SILC_LOG_DEBUG(("Signature is Ok"));
+    
+    silc_pkcs_public_key_free(public_key);
+    memset(hash, 'F', hash_len);
+  }
 
   /* Create the random number x, 1 < x < q. */
-  silc_mp_init(&x);
+  x = silc_calloc(1, sizeof(*x));
+  silc_mp_init(x);
   status = 
-    silc_ske_create_rnd(ske, ske->prop->group->group_order,
+    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       &x);
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
-    return status;
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = status;
+    if (ske->callbacks->proto_continue)
+      ske->callbacks->proto_continue(ske, ske->callbacks->context);
+    return;
   }
 
+  /* Save the results for later processing */
+  send_payload = silc_calloc(1, sizeof(*send_payload));
+  ske->x = x;
+  ske->ke2_payload = send_payload;
+
   SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
 
   /* Do the Diffie Hellman computation, f = g ^ x mod p */
-  silc_mp_init(&f);
-  silc_mp_powm(&f, &ske->prop->group->generator, &x, 
-              &ske->prop->group->group);
+  silc_mp_init(&send_payload->x);
+  silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x, 
+                 &ske->prop->group->group);
   
-  /* Save the results for later processing */
-  two_payload = silc_calloc(1, sizeof(*two_payload));
-  two_payload->f = f;
-  ske->x = x;
-  ske->ke1_payload = one_payload;
-  ske->ke2_payload = two_payload;
+  /* Call the callback. The caller may now continue with the SKE protocol. */
+  ske->status = SILC_SKE_STATUS_OK;
+  if (ske->callbacks->proto_continue)
+    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+}
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+/* This function receives the Key Exchange Payload from the initiator.
+   This also performs the mutual authentication if required. Then, this 
+   function first generated a random number x, such that 1 < x < q
+   and computes f = g ^ x mod p. This then puts the result f to a Key
+   Exchange Payload. 
+
+   The `callback' will be called to indicate that the caller may
+   continue with the SKE protocol.  The caller must not continue
+   before the SKE libary has called that callback.  If this function
+   returns an error the callback will not be called.  It is called
+   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
+   However, note that when the library calls the callback the ske->status
+   may be error.
+
+   This calls the `verify_key' callback to verify the received public
+   key or certificate if the Mutual Authentication flag is set. If the
+   `verify_key' is provided then the remote must send public key and it
+   is considered to be an error if remote does not send its public key. */
 
-  return status;
+SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
+                                        SilcBuffer ke_payload)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *recv_payload;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode Key Exchange Payload */
+  status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    return status;
+  }
+
+  ske->ke1_payload = recv_payload;
+
+  /* Verify the received public key and verify the signature if we are
+     doing mutual authentication. */
+  if (ske->start_payload && 
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
+    
+    if (!recv_payload->pk_data && ske->callbacks->verify_key) {
+      SILC_LOG_DEBUG(("Remote end did not send its public key (or "
+                     "certificate), even though we require it"));
+      ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+      return status;
+    }
+
+    if (recv_payload->pk_data && ske->callbacks->verify_key) {
+      SILC_LOG_DEBUG(("Verifying public key"));
+
+      ske->users++;
+      (*ske->callbacks->verify_key)(ske, recv_payload->pk_data, 
+                                   recv_payload->pk_len,
+                                   recv_payload->pk_type, 
+                                   ske->callbacks->context,
+                                   silc_ske_responder_phase2_final, NULL);
+
+      /* We will continue to the final state after the public key has
+        been verified by the caller. */
+      return SILC_SKE_STATUS_PENDING;
+    }
+  }
+
+  /* Continue to final state */
+  ske->users++;
+  silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
+
+  return SILC_SKE_STATUS_OK;
 }
 
-/* This function computes the secret shared key KEY = e ^ x mod p, and, 
-   a hash value to be signed and sent to the other end. This then
-   encodes Key Exchange 2 Payload and sends it to the other end. */
+/* This functions generates the secret key KEY = e ^ x mod p, and, a hash
+   value to be signed and sent to the other end. This then encodes Key
+   Exchange Payload and sends it to the other end. */
 
 SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
-                                       unsigned char *pk,
-                                       unsigned int pk_len,
-                                       unsigned char *prv,
-                                       unsigned int prv_len,
-                                       SilcSKEPKType pk_type,
-                                       SilcSKESendPacketCb send_packet,
-                                       void *context)
+                                       SilcPublicKey public_key,
+                                       SilcPrivateKey private_key,
+                                       SilcSKEPKType pk_type)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt KEY;
-  unsigned char hash[32], sign[256];
-  unsigned int hash_len, sign_len;
+  SilcMPInt *KEY;
+  unsigned char hash[32], sign[1024], *pk;
+  uint32 hash_len, sign_len, pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
   SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
 
   /* Compute the shared secret key */
-  silc_mp_init(&KEY);
-  silc_mp_powm(&KEY, &ske->ke1_payload->e, &ske->x, 
-              &ske->prop->group->group);
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x, 
+                 &ske->prop->group->group);
   ske->KEY = KEY;
 
-  SILC_LOG_DEBUG(("Getting public key"));
+  if (public_key && private_key) {
+    SILC_LOG_DEBUG(("Getting public key"));
+    
+    /* Get the public key */
+    pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+    if (!pk) {
+      status = SILC_SKE_STATUS_ERROR;
+      goto err;
+    }
+    ske->ke2_payload->pk_data = pk;
+    ske->ke2_payload->pk_len = pk_len;
+    
+    SILC_LOG_DEBUG(("Computing HASH value"));
+    
+    /* Compute the hash value */
+    memset(hash, 0, sizeof(hash));
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
 
-  /* Get the public key */
-  ske->ke2_payload->pk_data = silc_calloc(pk_len, sizeof(unsigned char));
-  memcpy(ske->ke2_payload->pk_data, pk, pk_len);
-  ske->ke2_payload->pk_len = pk_len;
+    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
+    memcpy(ske->hash, hash, hash_len);
+    ske->hash_len = hash_len;
+    
+    SILC_LOG_DEBUG(("Signing HASH value"));
+    
+    /* Sign the hash value */
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv, 
+                                  private_key->prv_len);
+    silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
+    ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
+    memcpy(ske->ke2_payload->sign_data, sign, sign_len);
+    memset(sign, 0, sizeof(sign));
+    ske->ke2_payload->sign_len = sign_len;
+  }
   ske->ke2_payload->pk_type = pk_type;
 
-  SILC_LOG_DEBUG(("Computing HASH value"));
-
-  /* Compute the hash value */
-  memset(hash, 0, sizeof(hash));
-  status = silc_ske_make_hash(ske, hash, &hash_len);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
-
-  ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-  memcpy(ske->hash, hash, hash_len);
-  ske->hash_len = hash_len;
-
-  SILC_LOG_DEBUG(("Signing HASH value"));
-
-  /* Sign the hash value */
-  silc_pkcs_set_private_key(ske->prop->pkcs, prv, prv_len);
-  ske->prop->pkcs->pkcs->sign(ske->prop->pkcs->context,
-                             hash, hash_len,
-                             sign, &sign_len);
-  ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
-  memcpy(ske->ke2_payload->sign_data, sign, sign_len);
-  memset(sign, 0, sizeof(sign));
-  ske->ke2_payload->sign_len = sign_len;
-
-  /* Encode the Key Exchange 2 Payload */
-  status = silc_ske_payload_two_encode(ske, ske->ke2_payload,
-                                      &payload_buf);
+  /* Encode the Key Exchange Payload */
+  status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
+                                     &payload_buf);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_2, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, 
+                                  SILC_PACKET_KEY_EXCHANGE_2,
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
   return status;
 
  err:
-  silc_mp_clear(&ske->KEY);
-  silc_ske_payload_two_free(ske->ke2_payload);
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
+  silc_ske_payload_ke_free(ske->ke2_payload);
 
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -634,31 +1052,32 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
    must not be called until the keys are processed like the protocol
    defines. This function is for both initiator and responder. */
 
-SilcSKEStatus silc_ske_end(SilcSKE ske,
-                          SilcSKESendPacketCb send_packet,
-                          void *context)
+SilcSKEStatus silc_ske_end(SilcSKE ske)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer packet;
 
   SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc(1);
-  packet->len = 0;
+  packet = silc_buffer_alloc(4);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_INT((uint32)SILC_SKE_STATUS_OK),
+                    SILC_STR_END);
 
-  if (send_packet)
-    (*send_packet)(ske, packet, SILC_PACKET_SUCCESS, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_SUCCESS, 
+                                  ske->callbacks->context);
 
-  return status;
+  silc_buffer_free(packet);
+
+  return SILC_SKE_STATUS_OK;
 }
 
 /* Aborts the Key Exchange protocol. This is called if error occurs
    while performing the protocol. The status argument is the error
    status and it is sent to the remote end. */
 
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
-                            SilcSKESendPacketCb send_packet,
-                            void *context)
+SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
 {
   SilcBuffer packet;
 
@@ -667,11 +1086,12 @@ SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
   packet = silc_buffer_alloc(4);
   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
   silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(status),
+                    SILC_STR_UI_INT((uint32)status),
                     SILC_STR_END);
 
-  if (send_packet)
-    (*send_packet)(ske, packet, SILC_PACKET_FAILURE, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_FAILURE, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(packet);
 
@@ -687,23 +1107,29 @@ SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
 
 SilcSKEStatus 
 silc_ske_assemble_security_properties(SilcSKE ske,
+                                     unsigned char flags,
+                                     char *version,
                                      SilcSKEStartPayload **return_payload)
 {
   SilcSKEStartPayload *rp;
+  int i;
 
   SILC_LOG_DEBUG(("Assembling KE Start Payload"));
 
   rp = silc_calloc(1, sizeof(*rp));
 
-  /* XXX */
   /* Set flags */
-  rp->flags = 0;
+  rp->flags = flags;
 
-  /* XXX */
-  /* Cookie */
-  rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
+  /* Set random cookie */
+  rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
+  for (i = 0; i < SILC_SKE_COOKIE_LEN; i++)
+    rp->cookie[i] = silc_rng_get_byte(ske->rng);
   rp->cookie_len = SILC_SKE_COOKIE_LEN;
-  memcpy(rp->cookie, "1234567890123456", SILC_SKE_COOKIE_LEN);
+
+  /* Put version */
+  rp->version = strdup(version);
+  rp->version_len = strlen(version);
 
   /* Get supported Key Exhange groups */
   rp->ke_grp_list = silc_ske_get_supported_groups();
@@ -721,15 +1147,20 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   rp->hash_alg_list = silc_hash_get_supported();
   rp->hash_alg_len = strlen(rp->hash_alg_list);
 
+  /* Get supported HMACs */
+  rp->hmac_alg_list = silc_hmac_get_supported();
+  rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+
   /* XXX */
   /* Get supported compression algorithms */
-  rp->comp_alg_list = "";
+  rp->comp_alg_list = strdup("");
   rp->comp_alg_len = 0;
 
   rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 
+    2 + rp->version_len +
     2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len + 
     2 + rp->enc_alg_len + 2 + rp->hash_alg_len + 
-    2 + rp->comp_alg_len;
+    2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
 
   *return_payload = rp;
 
@@ -741,9 +1172,11 @@ silc_ske_assemble_security_properties(SilcSKE ske,
 
 SilcSKEStatus 
 silc_ske_select_security_properties(SilcSKE ske,
+                                   char *version,
                                    SilcSKEStartPayload *payload,
                                    SilcSKEStartPayload *remote_payload)
 {
+  SilcSKEStatus status;
   SilcSKEStartPayload *rp;
   char *cp;
   int len;
@@ -752,14 +1185,29 @@ silc_ske_select_security_properties(SilcSKE ske,
 
   rp = remote_payload;
 
+  /* Check version string */
+  if (ske->callbacks->check_version) {
+    status = ske->callbacks->check_version(ske, rp->version, 
+                                          rp->version_len,
+                                          ske->callbacks->context);
+    if (status != SILC_SKE_STATUS_OK) {
+      ske->status = status;
+      return status;
+    }
+  }
+
   /* Flags are returned unchanged. */
   payload->flags = rp->flags;
 
-  /* XXX Cookie check?? */
+  /* Take cookie, we must return it to sender unmodified. */
   payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
   payload->cookie_len = SILC_SKE_COOKIE_LEN;
   memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN);
 
+  /* Put our version to our reply */
+  payload->version = strdup(version);
+  payload->version_len = strlen(version);
+
   /* Get supported Key Exchange groups */
   cp = rp->ke_grp_list;
   if (cp && strchr(cp, ',')) {
@@ -772,7 +1220,7 @@ silc_ske_select_security_properties(SilcSKE ske,
 
       SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
 
-      if (silc_ske_get_group_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
+      if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
        SILC_LOG_DEBUG(("Found KE group `%s'", item));
 
        payload->ke_grp_len = len;
@@ -972,6 +1420,64 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->hash_alg_list = strdup(rp->hash_alg_list);
   }
 
+  /* Get supported HMACs */
+  cp = rp->hmac_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
+
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      memcpy(item, cp, len);
+
+      SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
+
+      if (silc_hmac_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+
+       payload->hmac_alg_len = len;
+       payload->hmac_alg_list = item;
+       break;
+      }
+
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
+
+      if (item)
+       silc_free(item);
+    }
+
+    if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
+      SILC_LOG_DEBUG(("Could not find supported HMAC"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload->pkcs_alg_list);
+      silc_free(payload->enc_alg_list);
+      silc_free(payload->hash_alg_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_UNKNOWN_HMAC;
+    }
+  } else {
+
+    if (!rp->hmac_alg_len) {
+      SILC_LOG_DEBUG(("HMAC not defined in payload"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload->pkcs_alg_list);
+      silc_free(payload->enc_alg_list);
+      silc_free(payload->hash_alg_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    }
+
+    SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
+                   rp->hmac_alg_list));
+
+    payload->hmac_alg_len = rp->hmac_alg_len;
+    payload->hmac_alg_list = strdup(rp->hmac_alg_list);
+  }
+
 #if 0
   /* Get supported compression algorithms */
   cp = rp->hash_alg_list;
@@ -1018,9 +1524,10 @@ silc_ske_select_security_properties(SilcSKE ske,
 #endif
 
   payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 
+    2 + payload->version_len + 
     2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len + 
     2 + payload->enc_alg_len + 2 + payload->hash_alg_len + 
-    2 + payload->comp_alg_len;
+    2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
 
   return SILC_SKE_STATUS_OK;
 }
@@ -1028,9 +1535,9 @@ silc_ske_select_security_properties(SilcSKE ske,
 /* Creates random number such that 1 < rnd < n and at most length
    of len bits. The rnd sent as argument must be initialized. */
 
-SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n, 
-                                 unsigned int len, 
-                                 SilcInt *rnd)
+SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, 
+                                 uint32 len, 
+                                 SilcMPInt *rnd)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   unsigned char *string;
@@ -1038,17 +1545,19 @@ SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n,
   SILC_LOG_DEBUG(("Creating random number"));
 
   /* Get the random number as string */
-  string = silc_rng_get_rn_string(ske->rng, (len / 8));
+  string = silc_rng_get_rn_data(ske->rng, (len / 8));
+  if (!string)
+    return SILC_SKE_STATUS_ERROR;
 
   /* Decode the string into a MP integer */
-  silc_mp_set_str(rnd, string, 16);
+  silc_mp_bin2mp(string, (len / 8), rnd);
   silc_mp_mod_2exp(rnd, rnd, len);
 
   /* Checks */
   if (silc_mp_cmp_ui(rnd, 1) < 0)
     status = SILC_SKE_STATUS_ERROR;
 
-  if (silc_mp_cmp(rnd, &n) >= 0)
+  if (silc_mp_cmp(rnd, n) >= 0)
     status = SILC_SKE_STATUS_ERROR;
 
   memset(string, 'F', (len / 8));
@@ -1057,127 +1566,186 @@ SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n,
   return status;
 }
 
-/* XXX TODO */
-
-SilcSKEStatus silc_ske_verify_public_key(SilcSKE ske, 
-                                        unsigned char *pubkey,
-                                        unsigned int pubkey_len)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-
-  return status;
-}
-
-/* Creates a hash value HASH as defined in the SKE protocol. */
+/* Creates a hash value HASH as defined in the SKE protocol. If the
+   `initiator' is TRUE then this function is used to create the HASH_i
+   hash value defined in the protocol. If it is FALSE then this is used
+   to create the HASH value defined by the protocol. */
 
 SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
                                 unsigned char *return_hash,
-                                unsigned int *return_hash_len)
+                                uint32 *return_hash_len,
+                                int initiator)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer buf;
   unsigned char *e, *f, *KEY;
-  unsigned int e_len, f_len, KEY_len;
+  uint32 e_len, f_len, KEY_len;
+  int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
-  e_len = silc_mp_sizeinbase(&ske->ke1_payload->e, 16);
-  e = silc_calloc(e_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(e, 16, &ske->ke1_payload->e);
-
-  f_len = silc_mp_sizeinbase(&ske->ke2_payload->f, 16);
-  f = silc_calloc(f_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(f, 16, &ske->ke2_payload->f);
-
-  KEY_len = silc_mp_sizeinbase(&ske->KEY, 16);
-  KEY = silc_calloc(KEY_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(KEY, 16, &ske->KEY);
+  if (initiator == FALSE) {
+    e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
+    f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len);
+    KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
+    
+    /* Format the buffer used to compute the hash value */
+    /* XXX Backward support for 0.6.1 */
+    if (ske->backward_version == 1) {
+      SILC_LOG_DEBUG(("*********** Using old KE payload"));
+      buf = silc_buffer_alloc(ske->start_payload_copy->len + 
+                             ske->ke2_payload->pk_len + e_len + 
+                             f_len + KEY_len);
+      silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+      ret = 
+       silc_buffer_format(buf,
+                          SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
+                                               ske->start_payload_copy->len),
+                          SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, 
+                                               ske->ke2_payload->pk_len),
+                          SILC_STR_UI_XNSTRING(e, e_len),
+                          SILC_STR_UI_XNSTRING(f, f_len),
+                          SILC_STR_UI_XNSTRING(KEY, KEY_len),
+                          SILC_STR_END);
+    } else {
+      /* Initiator is not required to send its public key */
+      SILC_LOG_DEBUG(("*********** Using new KE payload"));
+      buf = silc_buffer_alloc(ske->start_payload_copy->len + 
+                             ske->ke2_payload->pk_len + 
+                             ske->ke1_payload->pk_len + 
+                             e_len + f_len + KEY_len);
+      silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+
+      if (!ske->ke1_payload->pk_data) {
+       ret = 
+         silc_buffer_format(buf,
+                            SILC_STR_UI_XNSTRING(ske->start_payload_copy->
+                                                 data,
+                                                 ske->start_payload_copy->
+                                                 len),
+                            SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, 
+                                                 ske->ke2_payload->pk_len),
+                            SILC_STR_UI_XNSTRING(e, e_len),
+                            SILC_STR_UI_XNSTRING(f, f_len),
+                            SILC_STR_UI_XNSTRING(KEY, KEY_len),
+                            SILC_STR_END);
+      } else {
+       ret = 
+         silc_buffer_format(buf,
+                            SILC_STR_UI_XNSTRING(ske->start_payload_copy->
+                                                 data,
+                                                 ske->start_payload_copy->
+                                                 len),
+                            SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data, 
+                                                 ske->ke2_payload->pk_len),
+                            SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data, 
+                                                 ske->ke1_payload->pk_len),
+                            SILC_STR_UI_XNSTRING(e, e_len),
+                            SILC_STR_UI_XNSTRING(f, f_len),
+                            SILC_STR_UI_XNSTRING(KEY, KEY_len),
+                            SILC_STR_END);
+      }
+    }
+    if (ret == -1) {
+      silc_buffer_free(buf);
+      memset(e, 0, e_len);
+      memset(f, 0, f_len);
+      memset(KEY, 0, KEY_len);
+      silc_free(e);
+      silc_free(f);
+      silc_free(KEY);
+      return SILC_SKE_STATUS_ERROR;
+    }
 
-  buf = silc_buffer_alloc(ske->start_payload_copy->len + 
-                         ske->pk_len + e_len + f_len + KEY_len);
-  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+    memset(e, 0, e_len);
+    memset(f, 0, f_len);
+    memset(KEY, 0, KEY_len);
+    silc_free(e);
+    silc_free(f);
+    silc_free(KEY);
+  } else {
+    e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
 
-  /* Format the buffer used to compute the hash value */
-  silc_buffer_format(buf,
-                    SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
-                                         ske->start_payload_copy->len),
-                    SILC_STR_UI_XNSTRING(ske->pk, ske->pk_len),
-                    SILC_STR_UI_XNSTRING(e, e_len),
-                    SILC_STR_UI_XNSTRING(f, f_len),
-                    SILC_STR_UI_XNSTRING(KEY, KEY_len),
-                    SILC_STR_END);
+    buf = silc_buffer_alloc(ske->start_payload_copy->len + 
+                           ske->ke1_payload->pk_len + e_len);
+    silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+    
+    /* Format the buffer used to compute the hash value */
+    ret = 
+      silc_buffer_format(buf,
+                        SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
+                                             ske->start_payload_copy->len),
+                        SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data, 
+                                             ske->ke1_payload->pk_len),
+                        SILC_STR_UI_XNSTRING(e, e_len),
+                        SILC_STR_END);
+    if (ret == -1) {
+      silc_buffer_free(buf);
+      memset(e, 0, e_len);
+      silc_free(e);
+      return SILC_SKE_STATUS_ERROR;
+    }
 
-#if 0
-  SILC_LOG_HEXDUMP(("Hash buffer"), buf->data, buf->len);
-#endif
+    memset(e, 0, e_len);
+    silc_free(e);
+  }
 
   /* Make the hash */
   silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
   *return_hash_len = ske->prop->hash->hash->hash_len;
 
-  SILC_LOG_HEXDUMP(("Hash"), return_hash, *return_hash_len);
+  if (initiator == FALSE) {
+    SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
+  } else {
+    SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len);
+  }
 
   silc_buffer_free(buf);
-  memset(e, 0, e_len);
-  memset(f, 0, f_len);
-  memset(KEY, 0, KEY_len);
-  silc_free(e);
-  silc_free(f);
-  silc_free(KEY);
 
   return status;
 }
 
-/* Processes negotiated key material as protocol specifies. This returns
-   the actual keys to be used in the SILC. */
+/* Processes the provided key material `data' as the SILC protocol 
+   specification defines. */
 
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
-                                           unsigned int req_iv_len,
-                                           unsigned int req_enc_key_len,
-                                           unsigned int req_hmac_key_len,
-                                           SilcSKEKeyMaterial *key)
+SilcSKEStatus 
+silc_ske_process_key_material_data(unsigned char *data,
+                                  uint32 data_len,
+                                  uint32 req_iv_len,
+                                  uint32 req_enc_key_len,
+                                  uint32 req_hmac_key_len,
+                                  SilcHash hash,
+                                  SilcSKEKeyMaterial *key)
 {
-  int i, klen;
   SilcBuffer buf;
-  SilcInt tmp;
-  unsigned char *tmpbuf;
-  unsigned char hash[32];
-  unsigned int hash_len = ske->prop->hash->hash->hash_len;
-  unsigned int enc_key_len = req_enc_key_len / 8;
+  unsigned char hashd[32];
+  uint32 hash_len = req_hmac_key_len;
+  uint32 enc_key_len = req_enc_key_len / 8;
 
   SILC_LOG_DEBUG(("Start"));
 
-  silc_mp_init_set(&tmp, &ske->KEY);
-
-  klen = silc_mp_size(&tmp);
-
-  /* Format the KEY material into binary data */
-  tmpbuf = silc_calloc(klen, sizeof(unsigned char));
-  for (i = klen; i > 0; i--) {
-    tmpbuf[i - 1] = (unsigned char)(silc_mp_get_ui(&tmp) & 0xff);
-    silc_mp_fdiv_q_2exp(&tmp, &tmp, 8);
-  }
-
-  buf = silc_buffer_alloc(1 + klen + hash_len);
+  if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
+    return SILC_SKE_STATUS_ERROR;
 
+  buf = silc_buffer_alloc(1 + data_len);
   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
   silc_buffer_format(buf,
                     SILC_STR_UI_CHAR(0),
-                    SILC_STR_UI_XNSTRING(tmpbuf, klen),
-                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_END);
 
   /* Take IVs */
-  memset(hash, 0, sizeof(hash));
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 0;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
-  memcpy(key->send_iv, hash, req_iv_len);
-  memset(hash, 0, sizeof(hash));
+  memcpy(key->send_iv, hashd, req_iv_len);
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 1;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
-  memcpy(key->receive_iv, hash, req_iv_len);
+  memcpy(key->receive_iv, hashd, req_iv_len);
   key->iv_len = req_iv_len;
 
   /* Take the encryption keys. If requested key size is more than
@@ -1193,34 +1761,36 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     if (enc_key_len > (3 * hash_len))
       return SILC_SKE_STATUS_ERROR;
     
+    /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, k1);
+    silc_hash_make(hash, buf->data, buf->len, k1);
     
-    /* XXX */
-    dist = silc_buffer_alloc(hash_len * 3);
-    
-    silc_buffer_pull_tail(dist, klen + hash_len);
+    /* Take second round */
+    dist = silc_buffer_alloc(data_len + hash_len);
+    silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                      SILC_STR_UI_XNSTRING(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
                       SILC_STR_END);
-    
     memset(k2, 0, sizeof(k2));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k2);
-    
-    silc_buffer_pull(dist, klen + hash_len);
+    silc_hash_make(hash, dist->data, dist->len, k2);
+
+    /* Take third round */
+    dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull_tail(dist, hash_len);
+    silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
                       SILC_STR_END);
-    silc_buffer_push(dist, klen + hash_len);
-    
+    silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k3);
-    
+    silc_hash_make(hash, dist->data, dist->len, k3);
+
+    /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
     memcpy(dtmp, k1, hash_len);
     memcpy(dtmp + hash_len, k2, hash_len);
-    memcpy(dtmp + hash_len, k3, hash_len);
+    memcpy(dtmp + hash_len + hash_len, k3, hash_len);
 
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->send_enc_key, dtmp, enc_key_len);
@@ -1234,10 +1804,10 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     silc_buffer_free(dist);
   } else {
     /* Take normal hash as key */
-    memset(hash, 0, sizeof(hash));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+    memset(hashd, 0, sizeof(hashd));
+    silc_hash_make(hash, buf->data, buf->len, hashd);
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
-    memcpy(key->send_enc_key, hash, enc_key_len);
+    memcpy(key->send_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
   }
 
@@ -1251,34 +1821,36 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     if (enc_key_len > (3 * hash_len))
       return SILC_SKE_STATUS_ERROR;
     
+    /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, k1);
-    
-    /* XXX */
-    dist = silc_buffer_alloc(hash_len * 3);
+    silc_hash_make(hash, buf->data, buf->len, k1);
     
-    silc_buffer_pull_tail(dist, klen + hash_len);
+    /* Take second round */
+    dist = silc_buffer_alloc(data_len + hash_len);
+    silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                      SILC_STR_UI_XNSTRING(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
                       SILC_STR_END);
-    
     memset(k2, 0, sizeof(k2));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, dist->len, k2);
     
-    silc_buffer_pull(dist, klen + hash_len);
+    /* Take third round */
+    dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull_tail(dist, hash_len);
+    silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
                       SILC_STR_END);
-    silc_buffer_push(dist, klen + hash_len);
-    
+    silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k3);
-    
+    silc_hash_make(hash, dist->data, dist->len, k3);
+
+    /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
     memcpy(dtmp, k1, hash_len);
     memcpy(dtmp + hash_len, k2, hash_len);
-    memcpy(dtmp + hash_len, k3, hash_len);
+    memcpy(dtmp + hash_len + hash_len, k3, hash_len);
 
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->receive_enc_key, dtmp, enc_key_len);
@@ -1292,23 +1864,136 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     silc_buffer_free(dist);
   } else {
     /* Take normal hash as key */
-    memset(hash, 0, sizeof(hash));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+    memset(hashd, 0, sizeof(hashd));
+    silc_hash_make(hash, buf->data, buf->len, hashd);
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
-    memcpy(key->receive_enc_key, hash, enc_key_len);
+    memcpy(key->receive_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
   }
 
-  /* Take HMAC key */
-  memset(hash, 0, sizeof(hash));
+  /* Take HMAC keys */
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 4;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
-  key->hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
-  memcpy(key->hmac_key, hash, req_hmac_key_len);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
+  key->send_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
+  memcpy(key->send_hmac_key, hashd, req_hmac_key_len);
+  memset(hashd, 0, sizeof(hashd));
+  buf->data[0] = 5;
+  silc_hash_make(hash, buf->data, buf->len, hashd);
+  key->receive_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
+  memcpy(key->receive_hmac_key, hashd, req_hmac_key_len);
   key->hmac_key_len = req_hmac_key_len;
+  memset(hashd, 0, sizeof(hashd));
+
+  silc_buffer_free(buf);
+
+  return SILC_SKE_STATUS_OK;
+}
+
+/* Processes negotiated key material as protocol specifies. This returns
+   the actual keys to be used in the SILC. */
+
+SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
+                                           uint32 req_iv_len,
+                                           uint32 req_enc_key_len,
+                                           uint32 req_hmac_key_len,
+                                           SilcSKEKeyMaterial *key)
+{
+  SilcSKEStatus status;
+  SilcBuffer buf;
+  unsigned char *tmpbuf;
+  uint32 klen;
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
+
+  buf = silc_buffer_alloc(klen + ske->hash_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  silc_buffer_format(buf,
+                    SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_END);
+
+  /* Process the key material */
+  status = silc_ske_process_key_material_data(buf->data, buf->len,
+                                             req_iv_len, req_enc_key_len,
+                                             req_hmac_key_len, 
+                                             ske->prop->hash, key);
 
   memset(tmpbuf, 0, klen);
   silc_free(tmpbuf);
+  silc_buffer_free(buf);
 
-  return SILC_SKE_STATUS_OK;
+  return status;
+}
+
+/* Free key material structure */
+
+void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+{
+  if (!key)
+    return;
+
+  if (key->send_iv)
+    silc_free(key->send_iv);
+  if (key->receive_iv)
+    silc_free(key->receive_iv);
+  if (key->send_enc_key) {
+    memset(key->send_enc_key, 0, key->enc_key_len / 8);
+    silc_free(key->send_enc_key);
+  }
+  if (key->receive_enc_key) {
+    memset(key->receive_enc_key, 0, key->enc_key_len / 8);
+    silc_free(key->receive_enc_key);
+  }
+  if (key->send_hmac_key) {
+    memset(key->send_hmac_key, 0, key->hmac_key_len);
+    silc_free(key->send_hmac_key);
+  }
+  if (key->receive_hmac_key) {
+    memset(key->receive_hmac_key, 0, key->hmac_key_len);
+    silc_free(key->receive_hmac_key);
+  }
+  silc_free(key);
+}
+
+const char *silc_ske_status_string[] = 
+{
+  /* Official */
+  "Ok",
+  "Unkown error occurred",
+  "Bad payload in packet",
+  "Unsupported group",
+  "Unsupported cipher",
+  "Unsupported PKCS",
+  "Unsupported hash function",
+  "Unsupported HMAC",
+  "Unsupported public key (or certificate)",
+  "Incorrect signature",
+  "Bad or unsupported version",
+  "Invalid cookie",
+
+  /* Other errors */
+  "Pending",
+  "Remote did not provide public key",
+  "Key exchange protocol is not active",
+  "Bad reserved field in packet",
+  "Bad payload length in packet",
+  "Incorrect hash",
+
+  NULL
+};
+
+/* Maps status to readable string and returns the string. If string is not
+   found and empty character string ("") is returned. */
+
+const char *silc_ske_map_status(SilcSKEStatus status)
+{
+  int i;
+
+  for (i = 0; silc_ske_status_string[i]; i++)
+    if (status == i)
+      return silc_ske_status_string[i];
+
+  return "";
 }
index e429b70f3eaaaebd48f851c40d68c7ca51d20331..85c78657e9c02362284c621baf1858aabf0b291c 100644 (file)
@@ -2,9 +2,9 @@
 
   silcske.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -29,40 +29,84 @@ typedef struct SilcSKEStruct *SilcSKE;
 /* Forward declaration for security properties. */
 typedef struct SilcSKESecurityPropertiesStruct *SilcSKESecurityProperties;
 
+/* Forward declaration for SKE callbacks structure. */
+typedef struct SilcSKECallbacksStruct *SilcSKECallbacks;
+
+/* Supported Public Key Types, defined by the protocol */
+typedef enum {
+  SILC_SKE_PK_TYPE_SILC    = 1,        /* Mandatory type */
+  /* Optional types. These are not implemented currently */
+  SILC_SKE_PK_TYPE_SSH2    = 2,
+  SILC_SKE_PK_TYPE_X509V3  = 3,
+  SILC_SKE_PK_TYPE_OPENPGP = 4,
+  SILC_SKE_PK_TYPE_SPKI    = 5
+} SilcSKEPKType;
+
 /* Packet sending callback. Caller of the SKE routines must provide
-   a routine to send packets to negotiation parties. */
+   a routine to send packets to negotiation parties. See the
+   silc_ske_set_callbacks for more information. */
 typedef void (*SilcSKESendPacketCb)(SilcSKE ske, SilcBuffer packet, 
                                    SilcPacketType type, void *context);
 
 /* Generic SKE callback function. This is called in various SKE
    routines. The SilcSKE object sent as argument provides all the data
-   callers routine might need (payloads etc). */
+   callers routine might need (payloads etc). This is usually called
+   to indicate that the application may continue the execution of the
+   SKE protocol. The application should check the ske->status in this
+   callback function. This callback is also called when Start Payload
+   is processed. See silc_ske_set_callbacks function for more information. */
 typedef void (*SilcSKECb)(SilcSKE ske, void *context);
 
-/* Supported Public Key Types, defined by the protocol */
-typedef enum {
-  SILC_SKE_PK_TYPE_SILC = 1,   /* Mandatory type */
-  /* Optional types. These are not implemented currently
-  SILC_SKE_PK_TYPE_SSH2 = 2,
-  SILC_SKE_PK_TYPE_X509V3 = 3,
-  SILC_SKE_PK_TYPE_OPENPGP = 4,
-  SILC_SKE_PK_TYPE_SPKI = 5
-  */
-} SilcSKEPKType;
+/* Completion callback that will be called when the public key
+   has been verified.  The `status' will indicate whether the public
+   key were trusted or not. If the `status' is PENDING then the status
+   is not considered to be available at this moment. In this case the
+   SKE libary will assume that the caller will call this callback again
+   when the status is available. See silc_ske_set_callbacks for more
+   information. */
+typedef void (*SilcSKEVerifyCbCompletion)(SilcSKE ske,
+                                         SilcSKEStatus status,
+                                         void *context);
+
+/* Callback function used to verify the received public key or certificate. 
+   The verification process is most likely asynchronous. That's why the
+   application must call the `completion' callback when the verification
+   process has been completed. The library then calls the user callback
+   (SilcSKECb), if it was provided for the function that takes this callback
+   function as argument, to indicate that the SKE protocol may continue. 
+   See silc_ske_set_callbacks for more information. */
+typedef void (*SilcSKEVerifyCb)(SilcSKE ske, 
+                               unsigned char *pk_data,
+                               uint32 pk_len,
+                               SilcSKEPKType pk_type,
+                               void *context,
+                               SilcSKEVerifyCbCompletion completion,
+                               void *completion_context);
+
+/* Callback function used to check the version of the remote SKE server.
+   The SKE library will call this function so that the appliation may
+   check its version against the remote host's version. This returns
+   SILC_SKE_STATUS_OK if the version string is Ok, and returns
+   SILC_SKE_STATUS_BAD_VERSION if the version was not acceptable. */
+typedef SilcSKEStatus (*SilcSKECheckVersion)(SilcSKE ske, 
+                                            unsigned char *version, 
+                                            uint32 len, void *context);
 
 /* Context passed to key material processing function. The function
    returns the processed key material into this structure. */
 typedef struct {
   unsigned char *send_iv;
   unsigned char *receive_iv;
-  unsigned int iv_len;
+  uint32 iv_len;
   unsigned char *send_enc_key;
   unsigned char *receive_enc_key;
-  unsigned int enc_key_len;
-  unsigned char *hmac_key;
-  unsigned int hmac_key_len;
+  uint32 enc_key_len;
+  unsigned char *send_hmac_key;
+  unsigned char *receive_hmac_key;
+  uint32 hmac_key_len;
 } SilcSKEKeyMaterial;
 
+/* Length of cookie in Start Payload */
 #define SILC_SKE_COOKIE_LEN 16
 
 #include "groups.h"
@@ -70,9 +114,10 @@ typedef struct {
 
 /* Security Property Flags. */
 typedef enum {
-  SILC_SKE_SP_FLAG_NONE = (1L << 0),
-  SILC_SKE_SP_FLAG_NO_REPLY = (1L << 1),
-  SILC_SKE_SP_FLAG_PFS = (1L << 2),
+  SILC_SKE_SP_FLAG_NONE      = 0x00,
+  SILC_SKE_SP_FLAG_NO_REPLY  = 0x01,
+  SILC_SKE_SP_FLAG_PFS       = 0x02,
+  SILC_SKE_SP_FLAG_MUTUAL    = 0x04,
 } SilcSKESecurityPropertyFlag;
 
 /* Security Properties negotiated between key exchange parties. This
@@ -85,7 +130,8 @@ struct SilcSKESecurityPropertiesStruct {
   SilcPKCS pkcs;
   SilcCipher cipher;
   SilcHash hash;
-  /* XXX SilcCompression comp; */
+  SilcHmac hmac;
+  /* XXX SilcZip comp; */
 };
 
 struct SilcSKEStruct {
@@ -98,28 +144,23 @@ struct SilcSKEStruct {
   /* Key Exchange payloads filled during key negotiation with
      remote data. Responder may save local data here as well. */
   SilcSKEStartPayload *start_payload;
-  SilcSKEOnePayload *ke1_payload;
-  SilcSKETwoPayload *ke2_payload;
+  SilcSKEKEPayload *ke1_payload;
+  SilcSKEKEPayload *ke2_payload;
 
   /* Temporary copy of the KE Start Payload used in the
      HASH computation. */
   SilcBuffer start_payload_copy;
 
-  /* If initiator, this is responders public key. If responder this
-     is our own public key. */
-  unsigned char *pk;
-  unsigned int pk_len;
-
   /* Random number x, 1 < x < q. This is the secret exponent
      used in Diffie Hellman computations. */
-  SilcInt x;
+  SilcMPInt *x;
   
   /* The secret shared key */
-  SilcInt KEY;
+  SilcMPInt *KEY;
   
   /* The hash value HASH of the key exchange */
   unsigned char *hash;
-  unsigned int hash_len;
+  uint32 hash_len;
 
   /* Random Number Generator. This is set by the caller and must
      be free'd by the caller. */
@@ -128,73 +169,87 @@ struct SilcSKEStruct {
   /* Pointer to the what ever user data. This is set by the caller
      and is not touched by the SKE. The caller must also free this one. */
   void *user_data;
+
+  /* Current status of SKE */
+  SilcSKEStatus status;
+
+  /* Reference counter. This is used when SKE library is performing async
+     operations, like public key verification. */
+  int users;
+
+  /* SKE callbacks. */
+  SilcSKECallbacks callbacks;
+
+  /* Backwards support version indicator */
+  uint32 backward_version;
 };
 
 /* Prototypes */
 SilcSKE silc_ske_alloc();
 void silc_ske_free(SilcSKE ske);
+void silc_ske_set_callbacks(SilcSKE ske,
+                           SilcSKESendPacketCb send_packet,
+                           SilcSKECb payload_receive,
+                           SilcSKEVerifyCb verify_key,
+                           SilcSKECb proto_continue,
+                           SilcSKECheckVersion check_version,
+                           void *context);
 SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
                                       SilcSocketConnection sock,
-                                      SilcSKEStartPayload *start_payload,
-                                      SilcSKESendPacketCb send_packet,
-                                      void *context);
+                                      SilcSKEStartPayload *start_payload);
 SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske, 
-                                        SilcBuffer start_payload,
-                                        SilcSKECb callback,
-                                        void *context);
+                                        SilcBuffer start_payload);
 SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
-                                        SilcSKESendPacketCb send_packet,
-                                        void *context);
+                                        SilcPublicKey public_key,
+                                        SilcPrivateKey private_key);
 SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke2_payload,
-                                       SilcSKECb callback,
-                                       void *context);
+                                       SilcBuffer ke_payload);
 SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
                                       SilcSocketConnection sock,
+                                      char *version,
                                       SilcBuffer start_payload,
-                                      SilcSKECb callback,
-                                      void *context);
+                                      bool mutual_auth);
 SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske, 
-                                        SilcSKEStartPayload *start_payload,
-                                        SilcSKESendPacketCb send_packet,
-                                        void *context);
+                                        SilcSKEStartPayload *start_payload);
 SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke1_payload,
-                                        SilcSKECb callback,
-                                        void *context);
+                                        SilcBuffer ke_payload);
 SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
-                                       unsigned char *pk,
-                                       unsigned int pk_len,
-                                       unsigned char *prv,
-                                       unsigned int prv_len,
-                                       SilcSKEPKType pk_type,
-                                       SilcSKESendPacketCb send_packet,
-                                       void *context);
-SilcSKEStatus silc_ske_end(SilcSKE ske,
-                          SilcSKESendPacketCb send_packet,
-                          void *context);
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
-                            SilcSKESendPacketCb send_packet,
-                            void *context);
+                                       SilcPublicKey public_key,
+                                       SilcPrivateKey private_key,
+                                       SilcSKEPKType pk_type);
+SilcSKEStatus silc_ske_end(SilcSKE ske);
+SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status);
 SilcSKEStatus 
 silc_ske_assemble_security_properties(SilcSKE ske,
+                                     unsigned char flags,
+                                     char *version,
                                      SilcSKEStartPayload **return_payload);
 SilcSKEStatus 
 silc_ske_select_security_properties(SilcSKE ske,
+                                   char *version,
                                    SilcSKEStartPayload *payload,
                                    SilcSKEStartPayload *remote_payload);
-SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n, 
-                                 unsigned int len, 
-                                 SilcInt *rnd);
-SilcSKEStatus silc_ske_verify_public_key(SilcSKE ske, 
-                                        unsigned char *pubkey,
-                                        unsigned int pubkey_len);
+SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, 
+                                 uint32 len, 
+                                 SilcMPInt *rnd);
 SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
                                 unsigned char *return_hash,
-                                unsigned int *return_hash_len);
+                                uint32 *return_hash_len,
+                                int initiator);
+SilcSKEStatus 
+silc_ske_process_key_material_data(unsigned char *data,
+                                  uint32 data_len,
+                                  uint32 req_iv_len,
+                                  uint32 req_enc_key_len,
+                                  uint32 req_hmac_key_len,
+                                  SilcHash hash,
+                                  SilcSKEKeyMaterial *key);
 SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
-                                           unsigned int req_iv_len,
-                                           unsigned int req_enc_key_len,
-                                           unsigned int req_hmac_key_len,
+                                           uint32 req_iv_len,
+                                           uint32 req_enc_key_len,
+                                           uint32 req_hmac_key_len,
                                            SilcSKEKeyMaterial *key);
+void silc_ske_free_key_material(SilcSKEKeyMaterial *key);
+const char *silc_ske_map_status(SilcSKEStatus status);
+
 #endif
index f512354f1179aee5c0a0cdcaa0ba50546a2768cd..f223354bc50f7ef87cac7d3d6c9d3ce28f71b0bc 100644 (file)
@@ -2,9 +2,9 @@
 
   silcske_status.h
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 /* Status flags returned by all SKE routines */
 typedef enum {
   /* These are defined by the protocol */
-  SILC_SKE_STATUS_OK = 0,
-  SILC_SKE_STATUS_ERROR = 1,
-  SILC_SKE_STATUS_BAD_PAYLOAD = 2,
-  SILC_SKE_STATUS_UNKNOWN_GROUP = 3,
-  SILC_SKE_STATUS_UNKNOWN_CIPHER = 4,
-  SILC_SKE_STATUS_UNKNOWN_PKCS = 5,
-  SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION = 6,
-  SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY = 7,
-  SILC_SKE_STATUS_INCORRECT_SIGNATURE = 8,
-
+  SILC_SKE_STATUS_OK                     = 0,
+  SILC_SKE_STATUS_ERROR                  = 1,
+  SILC_SKE_STATUS_BAD_PAYLOAD            = 2,
+  SILC_SKE_STATUS_UNKNOWN_GROUP          = 3,
+  SILC_SKE_STATUS_UNKNOWN_CIPHER         = 4,
+  SILC_SKE_STATUS_UNKNOWN_PKCS           = 5,
+  SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION  = 6,
+  SILC_SKE_STATUS_UNKNOWN_HMAC           = 7,
+  SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY = 8,
+  SILC_SKE_STATUS_INCORRECT_SIGNATURE    = 9,
+  SILC_SKE_STATUS_BAD_VERSION            = 10,
+  SILC_SKE_STATUS_INVALID_COOKIE         = 11,
+
+  SILC_SKE_STATUS_PENDING,
+  SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED,
   SILC_SKE_STATUS_KEY_EXCHANGE_NOT_ACTIVE,
   SILC_SKE_STATUS_BAD_RESERVED_FIELD,
   SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH,
   SILC_SKE_STATUS_INCORRECT_HASH,
-  SILC_SKE_STATUS_INCORRECT_PUBLIC_KEY,
+  SILC_SKE_STATUS_FREED,
 } SilcSKEStatus;
 
+extern const char *silc_ske_status_string[];
+
 #endif
diff --git a/lib/silcutil/DIRECTORY b/lib/silcutil/DIRECTORY
new file mode 100644 (file)
index 0000000..c02522f
--- /dev/null
@@ -0,0 +1,35 @@
+<!--
+@LIBRARY=SILC Utility Library
+@FILENAME=silcutillib.html
+@LINK=silcbuffer.html:SILC Buffer API
+@LINK=silcbuffmt.html:SILC Buffer Format API
+@LINK=silcbufutil.html:SILC Buffer Utility API
+@LINK=silchashtable.html:SILC Hash Table API
+@LINK=silclog.html:SILC Log and Debug API
+@LINK=silcmemory.html:SILC Memory API
+@LINK=silcmutex.html:SILC Mutex API
+@LINK=silcthread.html:SILC Thread API
+@LINK=silcnet.html:SILC Net API
+@LINK=silcschedule.html:SILC Schedule API
+@LINK=silcsockconn.html:SILC Socket Connection API
+@LINK=silcprotocol.html:SILC Protocol API
+@LINK=silcutil.html:SILC Util API
+@LINK=silczip.html:SILC Zip API
+@LINK=silclist.html:SILC List API
+@LINK=silcdlist.html:SILC Dynamic List API
+-->
+
+<BIG><B>SILC Utility Library</B></BIG>
+<BR /><BR />
+<B>Introduction</B>
+
+<BR /><BR />
+SILC Utility Library provides various utility routines for the applications.
+For example, it provides the application's main loop, called the SILC
+Scheduler.  It can handle all kinds of tasks, like socket connections and
+timeout tasks.  The SILC Utility Library also provides various buffer
+management routines.  All of these routines work on multiple platforms
+such as Unix and WIN32.
+
+<BR /><BR />
+@LINKS@
diff --git a/lib/silcutil/Makefile.am b/lib/silcutil/Makefile.am
new file mode 100644 (file)
index 0000000..07fb946
--- /dev/null
@@ -0,0 +1,66 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2000 - 2002 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; version 2 of the License.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+if SILC_WIN32
+SUBDIRS=win32
+else
+if SILC_EPOC
+SUBDIRS=epoc
+else
+SUBDIRS=unix
+endif
+endif
+
+noinst_LIBRARIES = libsilcutil.a
+
+libsilcutil_a_SOURCES = \
+       silcbuffmt.c \
+       silcconfig.c \
+       silclog.c \
+       silcmemory.c \
+       silcnet.c \
+       silcschedule.c \
+       silcutil.c \
+       silchashtable.c \
+       silcsockconn.c  \
+       silcprotocol.c
+
+if SILC_DIST_TOOLKIT
+include_HEADERS =      \
+       silcbuffer.h    \
+       silcbuffmt.h    \
+       silcbufutil.h   \
+       silcconfig.h    \
+       silchashtable.h \
+       silclog.h       \
+       silcmemory.h    \
+       silcmutex.h     \
+       silcnet.h       \
+       silcschedule.h  \
+       silcsockconn.h  \
+       silcprotocol.h  \
+       silcthread.h    \
+       silclist.h      \
+       silcdlist.h     \
+       silcutil.h
+endif
+
+EXTRA_DIST = *.h
+
+include $(top_srcdir)/Makefile.defines.in
similarity index 74%
rename from lib/silccore/silcbuffer.h
rename to lib/silcutil/silcbuffer.h
index 179c3d0e0567065917c4d402e077eef0887ad61d..b91d6cb67ad50f79b658818bddeea43ebd5a3d6e 100644 (file)
@@ -17,6 +17,8 @@
   GNU General Public License for more details.
 
 */
+/* $Id$ */
+/* Optimized buffer managing routines.  These are short inline functions. */
 
 #ifndef SILCBUFFER_H
 #define SILCBUFFER_H
    the allocated data area. Following short description of the fields
    of the buffer.
 
-   unsigned int truelen;
+   uint32 truelen;
 
        True length of the buffer. This is set at the allocation of the
        buffer and it should not be touched after that. This field should
        be considered read-only.
 
-   unsigned int len;
+   uint32 len;
 
        Length of the currently valid data area. Tells the length of the 
        data at the buffer. This is set to zero at the allocation of the
     Currently valid data area is considered to be the main data area in
     the buffer. However, the entire buffer is of course valid data and can
     be used as such. Usually head section of the buffer includes different
-    kind of headers or similiar. Data section includes the main data of
+    kind of headers or similar. Data section includes the main data of
     the buffer. Tail section can be seen as a reserve space of the data
     section. Tail section can be pulled towards end thus the data section
     becomes larger.
 
 */
 
-typedef struct SilcBufferStruct {
-  unsigned int truelen;
-  unsigned int len;
+typedef struct {
+  uint32 truelen;
+  uint32 len;
   unsigned char *head;
   unsigned char *data;
   unsigned char *tail;
   unsigned char *end;
-
-  /* Method functions. */
-  unsigned char *(*pull)(struct SilcBufferStruct *, unsigned int);
-  unsigned char *(*push)(struct SilcBufferStruct *, unsigned int);
-  unsigned char *(*pull_tail)(struct SilcBufferStruct *, unsigned int);
-  unsigned char *(*push_tail)(struct SilcBufferStruct *, unsigned int);
-  unsigned char *(*put)(struct SilcBufferStruct *, unsigned char *, 
-                       unsigned int);
-  unsigned char *(*put_head)(struct SilcBufferStruct *, unsigned char *, 
-                            unsigned int);
-  unsigned char *(*put_tail)(struct SilcBufferStruct *, unsigned char *, 
-                            unsigned int);
-} SilcBufferObject;
-
-typedef SilcBufferObject *SilcBuffer;
+} *SilcBuffer, SilcBufferStruct;
 
 /* Macros */
 
@@ -138,12 +126,52 @@ typedef SilcBufferObject *SilcBuffer;
    the buffer area to the end of the buffer. */
 #define SILC_BUFFER_END(x) ((x)->end - (x)->head)
 
-#ifndef SILC_DEBUG             /* When we are not doing debugging we use
-                                  optimized inline buffer functions. */
-/* 
- * Optimized buffer managing routines.  These are short inline
- * functions.
- */
+/* Inline functions */
+
+static inline
+SilcBuffer silc_buffer_alloc(uint32 len)
+{
+  SilcBuffer sb;
+
+  /* Allocate new SilcBuffer */
+  sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
+
+  /* Allocate the actual data area */
+  sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
+
+  /* Set pointers to the new buffer */
+  sb->truelen = len;
+  sb->data = sb->head;
+  sb->tail = sb->head;
+  sb->end = sb->head + sb->truelen;
+
+  return sb;
+}
+
+/* Free's a SilcBuffer */
+
+static inline
+void silc_buffer_free(SilcBuffer sb)
+{
+  if (sb) {
+    memset(sb->head, 'F', sb->truelen);
+    silc_free(sb->head);
+    silc_free(sb);
+  }
+}
+
+/* Sets the `data' and `data_len' to the buffer pointer sent as argument.
+   The data area is automatically set to the `data_len'. This function
+   can be used to set the data to static buffer without needing any
+   memory allocations. The `data' will not be copied to the buffer. */
+
+static inline
+void silc_buffer_set(SilcBuffer sb, unsigned char *data, uint32 data_len)
+{
+  sb->data = sb->head = data;
+  sb->tail = sb->end = data + data_len;
+  sb->len = sb->truelen = data_len;
+}
 
 /* Pulls current data area towards end. The length of the currently
    valid data area is also decremented. Returns pointer to the data
@@ -162,12 +190,14 @@ typedef SilcBufferObject *SilcBuffer;
            ^
 */
 
-extern inline 
-unsigned char *silc_buffer_pull(SilcBuffer sb, unsigned int len)
+static inline 
+unsigned char *silc_buffer_pull(SilcBuffer sb, uint32 len)
 {
   unsigned char *old_data = sb->data;
 
-  assert(len <= (sb->tail - sb->data));
+#ifdef SILC_DEBUG
+  assert(len <= (uint32)(sb->tail - sb->data));
+#endif
 
   sb->data += len;
   sb->len -= len;
@@ -192,12 +222,14 @@ unsigned char *silc_buffer_pull(SilcBuffer sb, unsigned int len)
               ^
 */
 
-extern inline 
-unsigned char *silc_buffer_push(SilcBuffer sb, unsigned int len)
+static inline 
+unsigned char *silc_buffer_push(SilcBuffer sb, uint32 len)
 {
   unsigned char *old_data = sb->data;
 
+#ifdef SILC_DEBUG
   assert((sb->data - len) >= sb->head);
+#endif
 
   sb->data -= len;
   sb->len += len;
@@ -222,12 +254,14 @@ unsigned char *silc_buffer_push(SilcBuffer sb, unsigned int len)
                         ^
 */
 
-extern inline 
-unsigned char *silc_buffer_pull_tail(SilcBuffer sb, unsigned int len)
+static inline 
+unsigned char *silc_buffer_pull_tail(SilcBuffer sb, uint32 len)
 {
   unsigned char *old_tail = sb->tail;
 
-  assert((sb->end - sb->tail) >= len);
+#ifdef SILC_DEBUG
+  assert((uint32)(sb->end - sb->tail) >= len);
+#endif
 
   sb->tail += len;
   sb->len += len;
@@ -252,12 +286,14 @@ unsigned char *silc_buffer_pull_tail(SilcBuffer sb, unsigned int len)
                             ^
 */
 
-extern inline
-unsigned char *silc_buffer_push_tail(SilcBuffer sb, unsigned int len)
+static inline
+unsigned char *silc_buffer_push_tail(SilcBuffer sb, uint32 len)
 {
   unsigned char *old_tail = sb->tail;
 
+#ifdef SILC_DEBUG
   assert((sb->tail - len) >= sb->data);
+#endif
 
   sb->tail -= len;
   sb->len -= len;
@@ -276,13 +312,15 @@ unsigned char *silc_buffer_push_tail(SilcBuffer sb, unsigned int len)
    Puts data to the head section. 
 */
 
-extern inline
+static inline
 unsigned char *silc_buffer_put_head(SilcBuffer sb, 
-                                   unsigned char *data,
-                                   unsigned int len)
+                                   const unsigned char *data,
+                                   uint32 len)
 {
-  assert((sb->data - sb->head) >= len);
-  return memcpy(sb->head, data, len);
+#ifdef SILC_DEBUG
+  assert((uint32)(sb->data - sb->head) >= len);
+#endif
+  return (unsigned char *)memcpy(sb->head, data, len);
 }
 
 /* Puts data at the start of the valid data area. Returns a pointer 
@@ -296,13 +334,15 @@ unsigned char *silc_buffer_put_head(SilcBuffer sb,
            Puts data to the data section.
 */
 
-extern inline
+static inline
 unsigned char *silc_buffer_put(SilcBuffer sb, 
-                              unsigned char *data,
-                              unsigned int len)
+                              const unsigned char *data,
+                              uint32 len)
 {
-  assert((sb->tail - sb->data) >= len);
-  return memcpy(sb->data, data, len);
+#ifdef SILC_DEBUG
+  assert((uint32)(sb->tail - sb->data) >= len);
+#endif
+  return (unsigned char *)memcpy(sb->data, data, len);
 }
 
 /* Puts data at the tail of the buffer. Returns pointer to the copied
@@ -316,34 +356,15 @@ unsigned char *silc_buffer_put(SilcBuffer sb,
                            Puts data to the tail section.
 */
 
-extern inline
+static inline
 unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
-                                   unsigned char *data,
-                                   unsigned int len)
+                                   const unsigned char *data,
+                                   uint32 len)
 {
-  assert((sb->end - sb->tail) >= len);
-  return memcpy(sb->tail, data, len);
-}
-
-#endif /* !SILC_DEBUG */
-
-/* Prototypes */
-SilcBuffer silc_buffer_alloc(unsigned int len);
-void silc_buffer_free(SilcBuffer sb);
 #ifdef SILC_DEBUG
-unsigned char *silc_buffer_pull(SilcBuffer sb, unsigned int len);
-unsigned char *silc_buffer_push(SilcBuffer sb, unsigned int len);
-unsigned char *silc_buffer_pull_tail(SilcBuffer sb, unsigned int len);
-unsigned char *silc_buffer_push_tail(SilcBuffer sb, unsigned int len);
-unsigned char *silc_buffer_put_head(SilcBuffer sb, 
-                                   unsigned char *data,
-                                   unsigned int len);
-unsigned char *silc_buffer_put(SilcBuffer sb, 
-                              unsigned char *data,
-                              unsigned int len);
-unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
-                                   unsigned char *data,
-                                   unsigned int len);
+  assert((uint32)(sb->end - sb->tail) >= len);
 #endif
+  return (unsigned char *)memcpy(sb->tail, data, len);
+}
 
 #endif
similarity index 58%
rename from lib/silccore/silcbuffmt.c
rename to lib/silcutil/silcbuffmt.c
index 2e7955c520080f19e16acf4fa56a81c2f1d9a780..86edee11b1da29fe418a4e9500f8e06e53a16780 100644 (file)
   GNU General Public License for more details.
 
 */
-/* XXX: These routines needs to be made more stable as these can crash
-   if the data (for unformatting for example) is malformed or the buffer
-   is too short. Must be fixed. There are some other obvious bugs as
-   well. */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 
+/* Macro to check whether there is enough free space to add the
+   required amount of data. For unformatting this means that there must
+   be the data that is to be extracted. */
+#define HAS_SPACE(x, req)                      \
+do {                                           \
+  if (req > (x)->len)                          \
+    goto fail;                                 \
+} while(0)
+
 /* Formats the arguments sent and puts them into the buffer sent as
    argument. The buffer must be initialized beforehand and it must have
    enough free space to include the formatted data. If this function
 int silc_buffer_format(SilcBuffer dst, ...)
 {
   va_list ap;
-  SilcBufferParamType fmt;
-  unsigned char *start_ptr = dst->data;
+  int ret;
 
   va_start(ap, dst);
+  ret = silc_buffer_format_vp(dst, ap);
+  va_end(ap);
+
+  return ret;
+}
+
+int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
+{
+  SilcBufferParamType fmt;
+  unsigned char *start_ptr = dst->data;
+  int len;
 
   /* Parse the arguments by formatting type. */
   while(1) {
@@ -54,14 +62,16 @@ int silc_buffer_format(SilcBuffer dst, ...)
     switch(fmt) {
     case SILC_BUFFER_PARAM_SI8_CHAR:
       {
-       char x = va_arg(ap, char);
+       char x = (char)va_arg(ap, int);
+       HAS_SPACE(dst, 1);
        silc_buffer_put(dst, &x, 1);
        silc_buffer_pull(dst, 1);
        break;
       }
     case SILC_BUFFER_PARAM_UI8_CHAR:
       {
-       unsigned char x = va_arg(ap, unsigned char);
+       unsigned char x = (unsigned char)va_arg(ap, int);
+       HAS_SPACE(dst, 1);
        silc_buffer_put(dst, &x, 1);
        silc_buffer_pull(dst, 1);
        break;
@@ -69,7 +79,8 @@ int silc_buffer_format(SilcBuffer dst, ...)
     case SILC_BUFFER_PARAM_SI16_SHORT:
       {
        unsigned char xf[2];
-       short x = va_arg(ap, short);
+       int16 x = (int16)va_arg(ap, int);
+       HAS_SPACE(dst, 2);
        SILC_PUT16_MSB(x, xf);
        silc_buffer_put(dst, xf, 2);
        silc_buffer_pull(dst, 2);
@@ -78,7 +89,8 @@ int silc_buffer_format(SilcBuffer dst, ...)
     case SILC_BUFFER_PARAM_UI16_SHORT:
       {
        unsigned char xf[2];
-       unsigned short x = va_arg(ap, unsigned short);
+       uint16 x = (uint16)va_arg(ap, int);
+       HAS_SPACE(dst, 2);
        SILC_PUT16_MSB(x, xf);
        silc_buffer_put(dst, xf, 2);
        silc_buffer_pull(dst, 2);
@@ -87,7 +99,8 @@ int silc_buffer_format(SilcBuffer dst, ...)
     case SILC_BUFFER_PARAM_SI32_INT:
       {
        unsigned char xf[4];
-       int x = va_arg(ap, int);
+       int32 x = va_arg(ap, int32);
+       HAS_SPACE(dst, 4);
        SILC_PUT32_MSB(x, xf);
        silc_buffer_put(dst, xf, 4);
        silc_buffer_pull(dst, 4);
@@ -96,31 +109,59 @@ int silc_buffer_format(SilcBuffer dst, ...)
     case SILC_BUFFER_PARAM_UI32_INT:
       {
        unsigned char xf[4];
-       unsigned int x = va_arg(ap, unsigned int);
+       uint32 x = va_arg(ap, uint32);
+       HAS_SPACE(dst, 4);
        SILC_PUT32_MSB(x, xf);
        silc_buffer_put(dst, xf, 4);
        silc_buffer_pull(dst, 4);
        break;
       }
+    case SILC_BUFFER_PARAM_SI64_INT:
+      {
+       unsigned char xf[8];
+       int64 x = va_arg(ap, int64);
+       HAS_SPACE(dst, 8);
+       SILC_PUT64_MSB(x, xf);
+       silc_buffer_put(dst, xf, 8);
+       silc_buffer_pull(dst, 8);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI64_INT:
+      {
+       unsigned char xf[8];
+       uint64 x = va_arg(ap, uint64);
+       HAS_SPACE(dst, 8);
+       SILC_PUT64_MSB(x, xf);
+       silc_buffer_put(dst, xf, 8);
+       silc_buffer_pull(dst, 8);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI8_STRING:
     case SILC_BUFFER_PARAM_UI16_STRING:
     case SILC_BUFFER_PARAM_UI32_STRING:
+    case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
       {
        unsigned char *x = va_arg(ap, unsigned char *);
-       silc_buffer_put(dst, x, strlen(x));
-       silc_buffer_pull(dst, strlen(x));
+       uint32 tmp_len = strlen(x);
+       HAS_SPACE(dst, tmp_len);
+       silc_buffer_put(dst, x, tmp_len);
+       silc_buffer_pull(dst, tmp_len);
        break;
       }
+    case SILC_BUFFER_PARAM_UI8_NSTRING:
     case SILC_BUFFER_PARAM_UI16_NSTRING:
     case SILC_BUFFER_PARAM_UI32_NSTRING:
     case SILC_BUFFER_PARAM_UI_XNSTRING:
+    case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
     case SILC_BUFFER_PARAM_UI32_NSTRING_ALLOC:
     case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
       {
        unsigned char *x = va_arg(ap, unsigned char *);
-       unsigned int len = va_arg(ap, unsigned int);
+       uint32 len = va_arg(ap, uint32);
+       HAS_SPACE(dst, len);
        silc_buffer_put(dst, x, len);
        silc_buffer_pull(dst, len);
        break;
@@ -129,7 +170,7 @@ int silc_buffer_format(SilcBuffer dst, ...)
       goto ok;
       break;
     default:
-      SILC_LOG_ERROR(("Bad buffer formatting type `%d'. Could not "
+      SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
                      "format the data.", fmt));
       goto fail;
       break;
@@ -137,13 +178,14 @@ int silc_buffer_format(SilcBuffer dst, ...)
   }
 
  fail:
-  SILC_LOG_ERROR(("Error occured while formatting data"));
-  return FALSE;
+  SILC_LOG_DEBUG(("Error occured while formatting data"));
+  return -1;
 
  ok:
   /* Push the buffer back to where it belongs. */
-  silc_buffer_push(dst, dst->data - start_ptr);
-  return dst->len;
+  len = dst->data - start_ptr;
+  silc_buffer_push(dst, len);
+  return len;
 }
 
 /* Unformats the buffer sent as argument. The unformatted data is returned
@@ -154,12 +196,21 @@ int silc_buffer_format(SilcBuffer dst, ...)
 int silc_buffer_unformat(SilcBuffer src, ...)
 {
   va_list ap;
+  int ret;
+
+  va_start(ap, src);
+  ret = silc_buffer_unformat_vp(src, ap);
+  va_end(ap);
+  
+  return ret;
+}
+
+int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
+{
   SilcBufferParamType fmt;
   unsigned char *start_ptr = src->data;
   int len = 0;
 
-  va_start(ap, src);
-
   /* Parse the arguments by formatting type. */
   while(1) {
     fmt = va_arg(ap, SilcBufferParamType);
@@ -168,6 +219,7 @@ int silc_buffer_unformat(SilcBuffer src, ...)
     case SILC_BUFFER_PARAM_SI8_CHAR:
       {
        char *x = va_arg(ap, char *);
+       HAS_SPACE(src, 1);
        if (x)
          *x = src->data[0];
        silc_buffer_pull(src, 1);
@@ -176,6 +228,7 @@ int silc_buffer_unformat(SilcBuffer src, ...)
     case SILC_BUFFER_PARAM_UI8_CHAR:
       {
        unsigned char *x = va_arg(ap, unsigned char *);
+       HAS_SPACE(src, 1);
        if (x)
          *x = src->data[0];
        silc_buffer_pull(src, 1);
@@ -183,7 +236,8 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       }
     case SILC_BUFFER_PARAM_SI16_SHORT:
       {
-       short *x = va_arg(ap, short *);
+       int16 *x = va_arg(ap, int16 *);
+       HAS_SPACE(src, 2);
        if (x)
          SILC_GET16_MSB(*x, src->data);
        silc_buffer_pull(src, 2);
@@ -191,7 +245,8 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       }
     case SILC_BUFFER_PARAM_UI16_SHORT:
       {
-       unsigned short *x = va_arg(ap, unsigned short *);
+       uint16 *x = va_arg(ap, uint16 *);
+       HAS_SPACE(src, 2);
        if (x)
          SILC_GET16_MSB(*x, src->data);
        silc_buffer_pull(src, 2);
@@ -199,7 +254,8 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       }
     case SILC_BUFFER_PARAM_SI32_INT:
       {
-       int *x = va_arg(ap, int *);
+       int32 *x = va_arg(ap, int32 *);
+       HAS_SPACE(src, 4);
        if (x)
          SILC_GET32_MSB(*x, src->data);
        silc_buffer_pull(src, 4);
@@ -207,38 +263,81 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       }
     case SILC_BUFFER_PARAM_UI32_INT:
       {
-       unsigned int *x = va_arg(ap, unsigned int *);
+       uint32 *x = va_arg(ap, uint32 *);
+       HAS_SPACE(src, 4);
        if (x)
          SILC_GET32_MSB(*x, src->data);
        silc_buffer_pull(src, 4);
        break;
       }
+    case SILC_BUFFER_PARAM_SI64_INT:
+      {
+       int64 *x = va_arg(ap, int64 *);
+       HAS_SPACE(src, 8);
+       if (x)
+         SILC_GET64_MSB(*x, src->data);
+       silc_buffer_pull(src, 8);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI64_INT:
+      {
+       uint64 *x = va_arg(ap, uint64 *);
+       HAS_SPACE(src, 8);
+       if (x)
+         SILC_GET64_MSB(*x, src->data);
+       silc_buffer_pull(src, 8);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI8_STRING:
+      {
+       uint8 len2;
+       unsigned char **x = va_arg(ap, unsigned char **);
+       HAS_SPACE(src, 1);
+       len2 = (uint8)src->data[0];
+       silc_buffer_pull(src, 1);
+       HAS_SPACE(src, len2);
+       if (x)
+         *x = src->data;
+       silc_buffer_pull(src, len2);
+       break;
+      }
     case SILC_BUFFER_PARAM_UI16_STRING:
       {
-       unsigned short len2;
+       uint16 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
+       HAS_SPACE(src, 2);
        SILC_GET16_MSB(len2, src->data);
        silc_buffer_pull(src, 2);
-       if ((len2 > src->len))
-         goto fail;
-       if (len2 < 1)
-         break;
+       HAS_SPACE(src, len2);
        if (x)
-         memcpy(x, src->data, len2);
+         *x = src->data;
+       silc_buffer_pull(src, len2);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI8_STRING_ALLOC:
+      {
+       uint8 len2;
+       unsigned char **x = va_arg(ap, unsigned char **);
+       HAS_SPACE(src, 1);
+       len2 = (uint8)src->data[0];
+       silc_buffer_pull(src, 1);
+       HAS_SPACE(src, len2);
+       if (x && len2) {
+         *x = silc_calloc(len2 + 1, sizeof(unsigned char));
+         memcpy(*x, src->data, len2);
+       }
        silc_buffer_pull(src, len2);
        break;
       }
     case SILC_BUFFER_PARAM_UI16_STRING_ALLOC:
       {
-       unsigned short len2;
+       uint16 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
+       HAS_SPACE(src, 2);
        SILC_GET16_MSB(len2, src->data);
        silc_buffer_pull(src, 2);
-       if ((len2 > src->len))
-         goto fail;
-       if (len2 < 1)
-         break;
-       if (x) {
+       HAS_SPACE(src, len2);
+       if (x && len2) {
          *x = silc_calloc(len2 + 1, sizeof(unsigned char));
          memcpy(*x, src->data, len2);
        }
@@ -247,68 +346,94 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       }
     case SILC_BUFFER_PARAM_UI32_STRING:
       {
-       unsigned int len2;
+       uint32 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
+       HAS_SPACE(src, 4);
        SILC_GET32_MSB(len2, src->data);
        silc_buffer_pull(src, 4);
-       if ((len2 > src->len))
-         goto fail;
-       if (len2 < 1)
-         break;
+       HAS_SPACE(src, len2);
        if (x)
-         memcpy(x, src->data, len2);
+         *x = src->data;
        silc_buffer_pull(src, len2);
        break;
       }
     case SILC_BUFFER_PARAM_UI32_STRING_ALLOC:
       {
-       unsigned int len2;
+       uint32 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
+       HAS_SPACE(src, 4);
        SILC_GET32_MSB(len2, src->data);
        silc_buffer_pull(src, 4);
-       if ((len2 > src->len))
-         goto fail;
-       if (len2 < 1)
-         break;
-       if (x) {
+       HAS_SPACE(src, len2);
+       if (x && len2) {
          *x = silc_calloc(len2 + 1, sizeof(unsigned char));
          memcpy(*x, src->data, len2);
        }
        silc_buffer_pull(src, len2);
        break;
       }
+    case SILC_BUFFER_PARAM_UI8_NSTRING:
+      {
+       uint8 len2;
+       unsigned char **x = va_arg(ap, unsigned char **);
+       uint8 *len = va_arg(ap, uint8 *);
+       HAS_SPACE(src, 1);
+       len2 = (uint8)src->data[0];
+       silc_buffer_pull(src, 1);
+       HAS_SPACE(src, len2);
+       if (len)
+         *len = len2;
+       if (x)
+         *x = src->data;
+       silc_buffer_pull(src, len2);
+       break;
+      }
     case SILC_BUFFER_PARAM_UI16_NSTRING:
       {
-       unsigned short len2;
+       uint16 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
-       unsigned short *len = va_arg(ap, unsigned short *);
+       uint16 *len = va_arg(ap, uint16 *);
+       HAS_SPACE(src, 2);
        SILC_GET16_MSB(len2, src->data);
        silc_buffer_pull(src, 2);
-       if ((len2 > src->len))
-         break;
-       if (len2 < 1)
-         break;
+       HAS_SPACE(src, len2);
        if (len)
          *len = len2;
        if (x)
-         memcpy(x, src->data, len2);
+         *x = src->data;
+       silc_buffer_pull(src, len2);
+       break;
+      }
+    case SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC:
+      {
+       uint8 len2;
+       unsigned char **x = va_arg(ap, unsigned char **);
+       uint8 *len = va_arg(ap, uint8 *);
+       HAS_SPACE(src, 1);
+       len2 = (uint8)src->data[0];
+       silc_buffer_pull(src, 1);
+       HAS_SPACE(src, len2);
+       if (len)
+         *len = len2;
+       if (x && len2) {
+         *x = silc_calloc(len2 + 1, sizeof(unsigned char));
+         memcpy(*x, src->data, len2);
+       }
        silc_buffer_pull(src, len2);
        break;
       }
     case SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC:
       {
-       unsigned short len2;
+       uint16 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
-       unsigned short *len = va_arg(ap, unsigned short *);
+       uint16 *len = va_arg(ap, uint16 *);
+       HAS_SPACE(src, 2);
        SILC_GET16_MSB(len2, src->data);
        silc_buffer_pull(src, 2);
-       if ((len2 > src->len))
-         break;
-       if (len2 < 1)
-         break;
+       HAS_SPACE(src, len2);
        if (len)
          *len = len2;
-       if (x) {
+       if (x && len2) {
          *x = silc_calloc(len2 + 1, sizeof(unsigned char));
          memcpy(*x, src->data, len2);
        }
@@ -317,39 +442,39 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       }
     case SILC_BUFFER_PARAM_UI32_NSTRING:
       {
-       unsigned int len2;
+       uint32 len2;
        unsigned char **x = va_arg(ap, unsigned char **);
-       unsigned int *len = va_arg(ap, unsigned int *);
+       uint32 *len = va_arg(ap, uint32 *);
+       HAS_SPACE(src, 4);
        SILC_GET32_MSB(len2, src->data);
        silc_buffer_pull(src, 4);
-       if ((len2 > src->len))
-         goto fail;
-       if (len2 < 1)
-         break;
+       HAS_SPACE(src, len2);
        if (len)
          *len = len2;
        if (x)
-         memcpy(x, src->data, len2);
+         *x = src->data;
        silc_buffer_pull(src, len2);
        break;
       }
-    case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
+    case SILC_BUFFER_PARAM_UI_XNSTRING:
       {
        unsigned char **x = va_arg(ap, unsigned char **);
-       unsigned int len = va_arg(ap, unsigned int);
-       if (len && x) {
-         *x = silc_calloc(len + 1, sizeof(unsigned char));
-         memcpy(*x, src->data, len);
-       }
+       uint32 len = va_arg(ap, uint32);
+       HAS_SPACE(src, len);
+       if (len && x)
+         *x = src->data;
        silc_buffer_pull(src, len);
        break;
       }
-    case SILC_BUFFER_PARAM_UI_XNSTRING:
+    case SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC:
       {
        unsigned char **x = va_arg(ap, unsigned char **);
-       unsigned int len = va_arg(ap, unsigned int);
-       if (len && x)
-         memcpy(x, src->data, len);
+       uint32 len = va_arg(ap, uint32);
+       HAS_SPACE(src, len);
+       if (len && x) {
+         *x = silc_calloc(len + 1, sizeof(unsigned char));
+         memcpy(*x, src->data, len);
+       }
        silc_buffer_pull(src, len);
        break;
       }
@@ -357,7 +482,7 @@ int silc_buffer_unformat(SilcBuffer src, ...)
       goto ok;
       break;
     default:
-      SILC_LOG_ERROR(("Bad buffer formatting type `%d'. Could not "
+      SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
                      "format the data.", fmt));
       goto fail;
       break;
@@ -365,8 +490,8 @@ int silc_buffer_unformat(SilcBuffer src, ...)
   }
 
  fail:
-  SILC_LOG_ERROR(("Error occured while unformatting buffer"));
-  return FALSE;
+  SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
+  return -1;
 
  ok:
   /* Push the buffer back to the start. */
similarity index 60%
rename from lib/silccore/silcbuffmt.h
rename to lib/silcutil/silcbuffmt.h
index 6d9acaeb736327deab7ee84a069cafd441006661..8b281eedf663a9f723e4665d69e634d11955748f 100644 (file)
 
 */
 
+/****h* silcutil/SilcBufferFormatAPI
+ *
+ * DESCRIPTION
+ *
+ *    SILC Buffer Format API provides a few functions for formatting
+ *    various different data types into a buffer, and retrieving
+ *    various data from buffer into specific data types.  It is usefull
+ *    to format for example packets and later unformat them.
+ *
+ ***/
+
 #ifndef SILCBUFFMT_H
 #define SILCBUFFMT_H
 
    _SI_ = signed
    _UI_ = unsigned
 
+  Any XXX_STRING_ALLOC types will allocate space for the data and
+  memcpy the data to the pointer sent as argument (in unformatting).
+
+  Any XXX_STRING will not allocate or copy any data.  Instead it
+  will set the pointer to the data.  Note that the data pointer 
+  returned (in unformatting) must not be freed.
+
 */
 typedef enum {
   SILC_BUFFER_PARAM_SI8_CHAR,
@@ -37,16 +55,23 @@ typedef enum {
   SILC_BUFFER_PARAM_SI32_INT,
   SILC_BUFFER_PARAM_UI32_INT,
 
-  SILC_BUFFER_PARAM_UI16_STRING,
-  SILC_BUFFER_PARAM_UI16_STRING_ALLOC,
-  SILC_BUFFER_PARAM_UI32_STRING,
-  SILC_BUFFER_PARAM_UI32_STRING_ALLOC,
-  SILC_BUFFER_PARAM_UI16_NSTRING,
-  SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC,
-  SILC_BUFFER_PARAM_UI32_NSTRING,
-  SILC_BUFFER_PARAM_UI32_NSTRING_ALLOC,
-  SILC_BUFFER_PARAM_UI_XNSTRING,
-  SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC,
+  SILC_BUFFER_PARAM_SI64_INT,
+  SILC_BUFFER_PARAM_UI64_INT,
+
+  SILC_BUFFER_PARAM_UI8_STRING,         /* No copy */
+  SILC_BUFFER_PARAM_UI8_STRING_ALLOC,  /* Alloc + memcpy */
+  SILC_BUFFER_PARAM_UI16_STRING,        /* No copy */
+  SILC_BUFFER_PARAM_UI16_STRING_ALLOC, /* Alloc + memcpy */
+  SILC_BUFFER_PARAM_UI32_STRING,       /* No copy */
+  SILC_BUFFER_PARAM_UI32_STRING_ALLOC, /* Alloc + memcpy */
+  SILC_BUFFER_PARAM_UI8_NSTRING,       /* No copy */
+  SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC, /* Alloc + memcpy */
+  SILC_BUFFER_PARAM_UI16_NSTRING,      /* No copy */
+  SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC,        /* Alloc + memcpy */
+  SILC_BUFFER_PARAM_UI32_NSTRING,      /* No copy */
+  SILC_BUFFER_PARAM_UI32_NSTRING_ALLOC,        /* Alloc + memcpy */
+  SILC_BUFFER_PARAM_UI_XNSTRING,       /* No copy */
+  SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC, /* Alloc + memcpy */
 
   SILC_BUFFER_PARAM_END
 } SilcBufferParamType;
@@ -66,28 +91,39 @@ typedef enum {
 #define SILC_STR_SI_CHAR(x) SILC_BUFFER_PARAM_SI8_CHAR, (x)
 #define SILC_STR_UI_CHAR(x) SILC_BUFFER_PARAM_UI8_CHAR, (x)
 
-/* Signed/unsigned short
+/* Signed/uint16
 
    Formatting:    SILC_STR_SI_SHORT(short)
-                  SILC_STR_UI_SHORT(unsigned short)
+                  SILC_STR_UI_SHORT(uint16)
    Unformatting:  SILC_STR_SI_SHORT(short *)
-                  SILC_STR_UI_SHORT(unsigned short *)
+                  SILC_STR_UI_SHORT(uint16 *)
 
 */
 #define SILC_STR_SI_SHORT(x) SILC_BUFFER_PARAM_SI16_SHORT, (x)
 #define SILC_STR_UI_SHORT(x) SILC_BUFFER_PARAM_UI16_SHORT, (x)
 
-/* Signed/unsigned int
+/* Signed/uint32
 
    Formatting:    SILC_STR_SI_INT(int)
-                  SILC_STR_UI_INT(unsigned int)
+                  SILC_STR_UI_INT(uint32)
    Unformatting:  SILC_STR_SI_INT(int *)
-                  SILC_STR_UI_INT(unsigned int *)
+                  SILC_STR_UI_INT(uint32 *)
 
 */
 #define SILC_STR_SI_INT(x) SILC_BUFFER_PARAM_SI32_INT, (x)
 #define SILC_STR_UI_INT(x) SILC_BUFFER_PARAM_UI32_INT, (x)
 
+/* Signed/uint64. 
+
+   Formatting:    SILC_STR_SI_INT64(int)
+                  SILC_STR_UI_INT64(uint32)
+   Unformatting:  SILC_STR_SI_INT64(int *)
+                  SILC_STR_UI_INT64(uint32 *)
+
+*/
+#define SILC_STR_SI_INT64(x) SILC_BUFFER_PARAM_SI64_INT, (x)
+#define SILC_STR_UI_INT64(x) SILC_BUFFER_PARAM_UI64_INT, (x)
+
 /* Unsigned NULL terminated string. Note that the string must be
    NULL terminated because strlen() will be used to get the length of
    the string. 
@@ -116,6 +152,8 @@ typedef enum {
    as argument in unformatting.
 
 */
+#define SILC_STR_UI8_STRING(x) SILC_BUFFER_PARAM_UI8_STRING, (x)
+#define SILC_STR_UI8_STRING_ALLOC(x) SILC_BUFFER_PARAM_UI8_STRING_ALLOC, (x)
 #define SILC_STR_UI16_STRING(x) SILC_BUFFER_PARAM_UI16_STRING, (x)
 #define SILC_STR_UI16_STRING_ALLOC(x) SILC_BUFFER_PARAM_UI16_STRING_ALLOC, (x)
 #define SILC_STR_UI32_STRING(x) SILC_BUFFER_PARAM_UI32_STRING, (x)
@@ -123,8 +161,8 @@ typedef enum {
 
 /* Unsigned string. Second argument is the length of the string.
 
-   Formatting:    SILC_STR_UI32_NSTRING(unsigned char *, unsigned int)
-   Unformatting:  SILC_STR_UI32_NSTRING(unsigned char **, unsigned int *)
+   Formatting:    SILC_STR_UI32_NSTRING(unsigned char *, uint32)
+   Unformatting:  SILC_STR_UI32_NSTRING(unsigned char **, uint32 *)
 
    Unformatting procedure will check for length of the string from the
    buffer before trying to get the string out. Thus, one *must* format the
@@ -151,6 +189,9 @@ typedef enum {
    as argument in unformatting.
 
 */
+#define SILC_STR_UI8_NSTRING(x, l) SILC_BUFFER_PARAM_UI8_NSTRING, (x), (l)
+#define SILC_STR_UI8_NSTRING_ALLOC(x, l) \
+  SILC_BUFFER_PARAM_UI8_NSTRING_ALLOC, (x), (l)
 #define SILC_STR_UI16_NSTRING(x, l) SILC_BUFFER_PARAM_UI16_NSTRING, (x), (l)
 #define SILC_STR_UI16_NSTRING_ALLOC(x, l) \
   SILC_BUFFER_PARAM_UI16_NSTRING_ALLOC, (x), (l)
@@ -162,7 +203,7 @@ typedef enum {
    the string.
 
    Formatting:    This is equal to using *_NSTRING
-   Unformatting:  SILC_STR_UI_XNSTRING(unsigned char **, unsigned int)
+   Unformatting:  SILC_STR_UI_XNSTRING(unsigned char **, uint32)
 
    This type can be used to take arbitrary length string from the buffer
    by sending the requested amount of bytes as argument. This differs
@@ -180,12 +221,66 @@ typedef enum {
 #define SILC_STR_UI_XNSTRING_ALLOC(x, l) \
   SILC_BUFFER_PARAM_UI_XNSTRING_ALLOC, (x), (l)
 
-/* Marks end of the argument list. This must the at the end of the
+/* Marks end of the argument list. This must be at the end of the
    argument list or error will occur. */
 #define SILC_STR_END SILC_BUFFER_PARAM_END
 
 /* Prototypes */
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_format
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_format(SilcBuffer dst, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list.  Returns -1 on error
+ *    and the length of the formatted buffer otherwise.
+ *
+ ***/
 int silc_buffer_format(SilcBuffer dst, ...);
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_unformat
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_unformat(SilcBuffer src, ...);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list.  Returns -1 on error
+ *    and the length of the formatted buffer otherwise.
+ *
+ ***/
 int silc_buffer_unformat(SilcBuffer src, ...);
 
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_format_vp
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_format_vp(SilcBuffer dst, va_list vp);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list indicated by the `ap'.
+ *    Returns -1 on error and the length of the formatted buffer otherwise.
+ *
+ ***/
+int silc_buffer_format_vp(SilcBuffer dst, va_list ap);
+
+/****f* silcutil/SilcBufferFormatAPI/silc_buffer_unformat_vp
+ *
+ * SYNOPSIS
+ *
+ *    int silc_buffer_unformat_vp(SilcBuffer src, va_list vp);
+ *
+ * DESCRIPTION
+ *
+ *    Formats a buffer from a variable argument list indicated by the `ap'.
+ *    Returns -1 on error and the length of the formatted buffer otherwise.
+ *
+ ***/
+int silc_buffer_unformat_vp(SilcBuffer src, va_list ap);
+
 #endif
similarity index 66%
rename from lib/silccore/silcbufutil.h
rename to lib/silcutil/silcbufutil.h
index acbbcaf47cc1b5a7b278f2ae88437dd3ec55957f..ecf99e03c978ccd21e7c460342af912fca78bef2 100644 (file)
 #ifndef SILCBUFUTIL_H
 #define SILCBUFUTIL_H
 
-#ifndef SILC_DEBUG             /* When we are not doing debugging we use
-                                  optimized inline buffer functions. */
+#include "silcbuffer.h"
 
 /* Clears and initialiazes the buffer to the state as if it was just
    allocated by silc_buffer_alloc. */
 
-extern inline
+static inline
 void silc_buffer_clear(SilcBuffer sb)
 {
   memset(sb->head, 0, sb->truelen);
@@ -40,7 +39,7 @@ void silc_buffer_clear(SilcBuffer sb)
    currently valid data area, nothing more. Use silc_buffer_clone to
    copy entire buffer. */
 
-extern inline
+static inline
 SilcBuffer silc_buffer_copy(SilcBuffer sb)
 {
   SilcBuffer sb_new;
@@ -56,7 +55,7 @@ SilcBuffer silc_buffer_copy(SilcBuffer sb)
    everything from the source buffer. The result is exact clone of
    the original buffer. */
 
-extern inline
+static inline
 SilcBuffer silc_buffer_clone(SilcBuffer sb)
 {
   SilcBuffer sb_new;
@@ -64,20 +63,38 @@ SilcBuffer silc_buffer_clone(SilcBuffer sb)
   sb_new = silc_buffer_alloc(sb->truelen);
   silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
   silc_buffer_put(sb_new, sb->head, sb->truelen);
-  sb_new->data = sb_new->head + sb->len;
-  sb_new->tail = sb_new->head + (sb->end - sb->tail);
+  sb_new->data = sb_new->head + (sb->data - sb->head);
+  sb_new->tail = sb_new->data + sb->len;
   sb_new->len = sb->len;
 
   return sb_new;
 }
 
-#endif /* !SILC_DEBUG */
+/* Reallocates buffer. Old data is saved into the new buffer. Returns
+   new SilcBuffer pointer. The buffer is exact clone of the old one
+   except that there is now more space at the end of buffer. */
 
-/* Prototypes */
-#ifdef SILC_DEBUG
-void silc_buffer_clear(SilcBuffer sb);
-SilcBuffer silc_buffer_copy(SilcBuffer sb);
-SilcBuffer silc_buffer_clone(SilcBuffer sb);
-#endif
+static inline
+SilcBuffer silc_buffer_realloc(SilcBuffer sb, uint32 newsize)
+{
+  SilcBuffer sb_new;
+
+  if (!sb)
+    return silc_buffer_alloc(newsize);
+
+  if (newsize <= sb->truelen)
+    return sb;
+
+  sb_new = silc_buffer_alloc(newsize);
+  silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
+  silc_buffer_put(sb_new, sb->head, sb->truelen);
+  sb_new->data = sb_new->head + (sb->data - sb->head);
+  sb_new->tail = sb_new->data + sb->len;
+  sb_new->len = sb->len;
+
+  silc_buffer_free(sb);
+
+  return sb_new;
+}
 
 #endif
diff --git a/lib/silcutil/silcconfig.c b/lib/silcutil/silcconfig.c
new file mode 100644 (file)
index 0000000..188ffa2
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+
+  silcconfig.c
+
+  Author: Johnny Mnemonic <johnny@themnemonic.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* limit debug logging verbosity */
+#if 0
+#define SILC_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SILC_CONFIG_DEBUG(fmt)
+#endif
+
+/* this is the option struct and currently it is only used internally to
+ * the module and other structs. */
+typedef struct SilcConfigOptionStruct {
+  char *name;                  /* *lowercase* name of the option */
+  SilcConfigType type;         /* important: the type of the returned value */
+  SilcConfigCallback cb;       /* the value handler */
+  const SilcConfigTable *subtable; /* used if type is SILC_CONFIG_ARG_BLOCK */
+  void *context;               /* context passed to the callback function */
+  struct SilcConfigOptionStruct *next;
+} SilcConfigOption;
+
+/* unique for each config file (and included ones) */
+struct SilcConfigFileObject {
+  char *filename; /* the original filename opened */
+  int level;   /* parsing level, how many nested silc_config_main we have */
+  char *base;  /* this is a fixed pointer to the base location */
+  char *p;     /* the Parser poitner */
+  uint32 len;  /* fixed length of the whole file */
+  uint32 line; /* current parsing line, strictly linked to p */
+  bool included; /* wether this file is main or included */
+};
+
+/* We need the entity to base our block-style parsing on */
+struct SilcConfigEntityObject {
+  SilcConfigOption *opts;      /* known options list */
+  SilcConfigFile *file;                /* parsing file object */
+};
+
+/* access error descriptions only with silc_config_strerror() */
+static char *errorstrs[] = {
+  "-OK",                                       /* SILC_CONFIG_OK */
+  "-SILENT",                                   /* SILC_CONFIG_ESILENT */
+  "Invalid syntax",                            /* SILC_CONFIG_EGENERIC */
+  "Internal error! Please report this bug",    /* SILC_CONFIG_EINTERNAL */
+  "Can't open specified file",                 /* SILC_CONFIG_ECANTOPEN */
+  "Expected open-brace '{'",                   /* SILC_CONFIG_EOPENBRACE */
+  "Missing close-brace '}'",                   /* SILC_CONFIG_ECLOSEBRACE */
+  "Invalid data type",                         /* SILC_CONFIG_ETYPE */
+  "Unknown option",                            /* SILC_CONFIG_EBADOPTION */
+  "Invalid text",                              /* SILC_CONFIG_EINVALIDTEXT */
+  "Double option specification",               /* SILC_CONFIG_EDOUBLE */
+  "Expected data but not found",               /* SILC_CONFIG_EEXPECTED */
+  "Expected '='",                              /* SILC_CONFIG_EEXPECTEDEQUAL */
+  "Unexpected data",                           /* SILC_CONFIG_EUNEXPECTED */
+  "Missing needed fields",                     /* SILC_CONFIG_EMISSFIELDS */
+  "Missing ';'",                               /* SILC_CONFIG_EMISSCOLON */
+};
+
+/* return string describing SilcConfig's error code */
+char *silc_config_strerror(int errnum)
+{
+  if ((errnum < 0) || (errnum >= sizeof(errorstrs)/sizeof(*errorstrs)) ||
+    (errorstrs[errnum] == NULL)) {
+    char *defret = "-INVALIDERROR";
+    return defret;
+  }
+  return errorstrs[errnum];
+}
+
+/* Begin of internal SilcConfig's text util functions */
+
+/* Points the first non-space character */
+static void my_trim_spaces(SilcConfigFile *file)
+{
+  register char *r = file->p;
+  while (isspace(*r))
+    if (*r++ == '\n') file->line++;
+  file->p = r;
+}
+/* Skips the current line until newline (lf or cr) */
+static void my_skip_line(SilcConfigFile *file)
+{
+  register char *r = file->p;
+  while (*r && (*r != '\n') && (*r != '\r')) r++;
+  file->p = (*r ? r + 1 : r);
+  file->line++;
+}
+/* Obtains a text token from the current position until first separator.
+ * a separator is any non alphanumeric character nor "_" or "-" */
+static char *my_next_token(SilcConfigFile *file, char *to)
+{
+  register char *o;
+  my_trim_spaces(file);
+  o = file->p;
+  while (isalnum(*o) || (*o == '_') || (*o == '-'))
+    *to++ = *o++;
+  *to = '\0';
+  file->p = o;
+  return to;
+}
+/* Obtains a string from the current position. The only difference from
+ * next_token() is that quoted-strings are also accepted */
+static char *my_get_string(SilcConfigFile *file, char *to)
+{
+  char *o;
+  my_trim_spaces(file);
+  o = file->p;
+  if (*o == '"') {
+    char *quot = strchr(++o, '"');
+    int len = quot - o;
+    if (!quot) { /* XXX FIXME: gotta do something here */
+      printf("Bullshit, missing matching \"");
+      exit(1);
+    }
+    if (len <= 0)
+      *to = '\0';
+    else {
+      strncpy(to, o, len);
+      to[len] = '\0';
+    }
+    /* update stream pointer */
+    file->p = quot + 1;
+    return to;
+  }
+  /* we don't need quote parsing, fall-back to token extractor */
+  my_next_token(file, to);
+  return to;
+};
+/* Skips all comment lines and spaces lines until first useful character */
+static void my_skip_comments(SilcConfigFile *file)
+{
+  while (1) {
+    my_trim_spaces(file);
+    if (*file->p != '#') return;
+    my_skip_line(file);
+  }
+}
+
+/* End of internal text functions
+ * Next section contains SilcConfig internal config utils */
+
+/* find an option in the list by name and returns its pointer */
+static SilcConfigOption *silc_config_find_option(SilcConfigEntity ent,
+       const char *name)
+{
+  SilcConfigOption *tmp;
+  for (tmp = ent->opts; tmp; tmp = tmp->next) {
+    if (!strcasecmp(tmp->name, name))
+      return tmp;
+  }
+  return NULL;
+}
+/* ... */
+static void *silc_config_marshall(SilcConfigType type, const char *val)
+{
+  void *pt;
+  int val_int;
+  bool val_bool;
+  char *val_tmp;
+  uint32 val_size;
+
+  switch (type) {
+    case SILC_CONFIG_ARG_TOGGLE:
+      if (!strcasecmp(val, "yes") || !strcasecmp(val, "true") ||
+               !strcasecmp(val, "on") || !strcasecmp(val, "1")) {
+       val_bool = TRUE;
+      }
+      else if (!strcasecmp(val, "no") || !strcasecmp(val, "false") ||
+               !strcasecmp(val, "off") || !strcasecmp(val, "0")) {
+       val_bool = FALSE;
+      }
+      else
+       return NULL;
+      pt = silc_calloc(1, sizeof(val_bool));
+      *(bool *)pt = (bool) val_bool;
+      return pt;
+    case SILC_CONFIG_ARG_INT:
+      val_int = (int) strtol(val, &val_tmp, 0);
+      if (*val_tmp) /* error converting string */
+       return NULL;
+      pt = silc_calloc(1, sizeof(val_int));
+      *(int *)pt = val_int;
+      return pt;
+    case SILC_CONFIG_ARG_SIZE:
+      val_size = (uint32) strtol(val, &val_tmp, 0);
+      if (val == val_tmp)
+       return NULL; /* really wrong, there must be at least one digit */
+      /* Search for a designator */
+      switch (tolower(val_tmp[0])) {
+       case '\0': /* None */
+         break;
+       case 'k': /* Kilobytes */
+         val_size *= (uint32) 1024;
+         break;
+       case 'm': /* Megabytes */
+         val_size *= (uint32) (1024 * 1024);
+         break;
+       case 'g':
+         val_size *= (uint32) (1024 * 1024 * 1024);
+         break;
+       default:
+         return NULL;
+      }
+      /* the string must die here */
+      if (val_tmp[1])
+       return NULL;
+      pt = silc_calloc(1, sizeof(val_size));
+      *(uint32 *)pt = val_size;
+      return pt;
+    case SILC_CONFIG_ARG_STR: /* the only difference between STR and STRE is */
+      if (!val[0])           /* that STR cannot be empty, while STRE can.  */
+       return NULL;
+    case SILC_CONFIG_ARG_STRE:
+      pt = (void *) strdup(val);
+      return pt;
+    /* following types are not supposed to have a return value */
+    case SILC_CONFIG_ARG_BLOCK:
+    case SILC_CONFIG_ARG_NONE:
+      return NULL;
+    default:
+      return NULL;
+  }
+
+  return NULL;
+}
+
+/* End of internal functions */
+
+
+/* Tries to open the config file and returns a valid SilcConfigFile object
+ * or NULL if failed */
+
+SilcConfigFile *silc_config_open(char *configfile)
+{
+  char *buffer;
+  uint32 filelen;
+  SilcConfigFile *ret;
+
+  if (!(buffer = silc_file_readfile(configfile, &filelen)))
+    return NULL;
+
+  ret = (SilcConfigFile *) silc_calloc(1, sizeof(*ret));
+  ret->filename = strdup(configfile);
+  ret->base = ret->p = buffer;
+  ret->len = filelen;
+  ret->line = 1; /* line count, start from first line */
+  return ret;
+}
+
+/* Frees a file object */
+
+void silc_config_close(SilcConfigFile *file)
+{
+  if (file) {
+    /* XXX FIXME: this check could probably be removed later */
+    uint32 my_len = (uint32) (strchr(file->base, EOF) - file->base);
+    SILC_CONFIG_DEBUG(("file=0x%x name=\"%s\" level=%d line=%lu", (uint32) file,
+                       file->filename, file->level, file->line));
+    if (my_len != file->len) {
+      fprintf(stderr, "FATAL ERROR: saved len and current len does not match!\n");
+      abort();
+    }
+    silc_free(file->filename);
+    memset(file->base, 'F', file->len);
+    silc_free(file->base);
+    memset(file, 'F', sizeof(*file));
+    silc_free(file);
+  }
+}
+
+/* initializes a SilcConfigEntity pointer allocation */
+
+SilcConfigEntity silc_config_init(SilcConfigFile *file)
+{
+  SilcConfigEntity ret;
+  if (!file)
+    return NULL;
+  SILC_CONFIG_DEBUG(("Allocating new config entity"));
+  ret = (SilcConfigEntity) silc_calloc(1, sizeof(*ret));
+  ret->file = file;
+  return ret;
+};
+
+/* Returns the original filename of the object file */
+
+char *silc_config_get_filename(SilcConfigFile *file)
+{
+  if (file)
+    return file->filename;
+  return NULL;
+}
+
+/* Returns the current line that file parsing arrived at */
+
+uint32 silc_config_get_line(SilcConfigFile *file)
+{
+  if (file)
+    return file->line;
+  return 0;
+}
+
+/* Returns a pointer to the beginning of the requested line.  If the line
+ * was not found, NULL is returned */
+
+char *silc_config_read_line(SilcConfigFile *file, uint32 line)
+{
+  register char *p;
+  int len;
+  char *ret, *endbuf;
+
+  if (!file || (line <= 0))
+    return NULL;
+  for (p = file->base; *p && (*p != EOF); p++) {
+    if (line <= 1)
+      goto found;
+    if (*p == '\n')
+      line--;
+  }
+  return NULL;
+
+ found:
+  if ((endbuf = strchr(p, '\n'))) {
+    len = endbuf - p;
+    ret = silc_calloc(len, sizeof(*ret));
+    strncpy(ret, p, len);
+    ret[len] = '\0';
+  }
+  else {
+    ret = silc_calloc(strlen(p), sizeof(*ret));
+    strcpy(ret, p);
+  }
+  return ret;
+}
+
+/* Convenience function to read the current parsed line */
+
+char *silc_config_read_current_line(SilcConfigFile *file)
+{
+  return silc_config_read_line(file, file->line);
+}
+
+/* (Private) destroy a SilcConfigEntity */
+
+static void silc_config_destroy(SilcConfigEntity ent)
+{
+  SilcConfigOption *oldopt, *nextopt;
+  SILC_CONFIG_DEBUG(("Freeing config entity [ent=0x%x] [opts=0x%x]",
+                       (uint32) ent, (uint32) ent->opts));
+  for (oldopt = ent->opts; oldopt; oldopt = nextopt) {
+    nextopt = oldopt->next;
+    memset(oldopt->name, 'F', strlen(oldopt->name) + 1);
+    silc_free(oldopt->name);
+    memset(oldopt, 'F', sizeof(*oldopt));
+    silc_free(oldopt);
+  }
+  memset(ent, 'F', sizeof(*ent));
+  silc_free(ent);
+}
+
+/* Registers a new option in the specified entity */
+
+void silc_config_register(SilcConfigEntity ent, const char *name,
+                         SilcConfigType type, SilcConfigCallback cb,
+                         const SilcConfigTable *subtable, void *context)
+{
+  SilcConfigOption *newopt;
+  SILC_CONFIG_DEBUG(("Register new option=\"%s\" type=%u cb=0x%08x context=0x%08x",
+               name, type, (uint32) cb, (uint32) context));
+
+  if (!ent || !name)
+    return;
+  /* if we are registering a block, make sure there is a specified sub-table */
+  if ((type == SILC_CONFIG_ARG_BLOCK) && !subtable)
+    return;
+  /* refuse special tag */
+  if (!strcasecmp(name, "include"))
+    return;
+  if (silc_config_find_option(ent, name)) {
+    fprintf(stderr, "Internal Error: Option double registered\n");
+    abort();
+  }
+
+  /* allocate and append the new option */
+  newopt = (SilcConfigOption *) silc_calloc(1, sizeof(*newopt));
+  newopt->name = strdup(name);
+  newopt->type = type;
+  newopt->cb = cb;
+  newopt->subtable = subtable;
+  newopt->context = context;
+
+  if (!ent->opts)
+    ent->opts = newopt;
+  else {
+    SilcConfigOption *tmp;
+    for (tmp = ent->opts; tmp->next; tmp = tmp->next);
+    tmp->next = newopt;
+  }
+}
+
+/* Register a new option table in the specified config entity */
+
+void silc_config_register_table(SilcConfigEntity ent,
+                               const SilcConfigTable table[], void *context)
+{
+  int i;
+  if (!ent || !table) return;
+  SILC_CONFIG_DEBUG(("Registering table"));
+  /* FIXME: some potability checks needed */
+  for (i = 0; table[i].name; i++) {
+    silc_config_register(ent, table[i].name, table[i].type,
+                        table[i].callback, table[i].subtable, context);
+  }
+}
+
+/* ... */
+
+static int silc_config_main_internal(SilcConfigEntity ent)
+{
+  SilcConfigFile *file = ent->file;
+  char **p = &file->p;
+
+  /* loop throught statements */
+  while (1) {
+    char buf[255];
+    SilcConfigOption *thisopt;
+
+    /* makes it pointing to the next interesting char */
+    my_skip_comments(file);
+    /* got eof? */
+    if (**p == '\0' || **p == EOF) {
+      if (file->level > 1) /* cannot get eof in a sub-level! */
+       return SILC_CONFIG_EEXPECTED;
+      goto finish;
+    }
+    /* check if we completed this (sub) section (it doesn't matter if this
+     * is the main section) */
+    if (**p == '}') {
+      if (file->level < 2) /* can't be! must be at least one sub-block */
+       return SILC_CONFIG_EUNEXPECTED;
+      (*p)++;
+      goto finish;
+    }
+    //SILC_LOG_HEXDUMP(("Preparing lookup at line=%lu", file->line), *p, 16);
+
+    /* obtain the keyword */
+    my_next_token(file, buf);
+    SILC_CONFIG_DEBUG(("Looking up keyword=\"%s\" [line=%lu]", buf, file->line));
+
+    /* handle special directive */
+    if (!strcasecmp(buf, "include")) {
+      int ret;
+      SilcConfigFile *inc_file;
+      SilcConfigEntity inc_ent;
+
+      my_trim_spaces(file); /* prepare next char */
+
+      /* Now trying to include the specified file.  The included file will
+       * be allowed to include sub-files but it will preserve the block-level
+       * of the including block. Note that the included file won't be allowed
+       * to raise the block level of the including block. */
+
+      my_get_string(file, buf); /* get the filename */
+      SILC_LOG_DEBUG(("Including file \"%s\"", buf));
+      /* before getting on, check if this row is REALLY complete */
+      if (*(*p)++ != ';')
+       return SILC_CONFIG_EMISSCOLON;
+
+      /* open the file and start the parsing */
+      inc_file = silc_config_open(buf);
+      if (!inc_file) /* does it point a valid filename? */
+        return SILC_CONFIG_ECANTOPEN;
+      inc_file->included = TRUE;
+
+      /* create a new entity and hack it to use the same options */
+      inc_ent = silc_config_init(inc_file);
+      inc_ent->opts = ent->opts;
+      ret = silc_config_main(inc_ent);
+
+      /* Cleanup.
+       * If the included file returned an error, the application will probably
+       * want to output some kind of error message. Because of this, we can't
+       * destroy THIS file object. The hack works this way: The application
+       * expects to destroy the originally created object file, so we'll swap
+       * the original file with the included file. */
+      if (ret) {
+        SilcConfigFile tmp_file;
+        SILC_CONFIG_DEBUG(("SWAPPING FILE OBJECTS"));
+        memcpy(&tmp_file, inc_file, sizeof(tmp_file));
+        memcpy(inc_file, file, sizeof(tmp_file));
+        silc_config_close(inc_file);
+        memcpy(file, &tmp_file, sizeof(tmp_file));
+        return ret;
+      }
+      /* otherwise if no errors encoured, continue normally */
+      silc_config_close(inc_file);
+      continue; /* this one is handled */
+    }
+
+    /* we have a registered option (it can also be a sub-block) */
+    thisopt = silc_config_find_option(ent, buf);
+    if (!thisopt)
+      return SILC_CONFIG_EBADOPTION;
+
+    my_trim_spaces(file); /* prepare next char */
+
+    /* option type is a block? */
+    if (thisopt->type == SILC_CONFIG_ARG_BLOCK) {
+      int ret;
+      SilcConfigEntity sub_ent;
+
+      SILC_CONFIG_DEBUG(("Entering sub-block"));
+      if (*(*p)++ != '{')
+       return SILC_CONFIG_EOPENBRACE;
+      /* build the new entity for this sub-block */
+      sub_ent = silc_config_init(ent->file);
+      /* use the previous specified table to describe this block's options */
+      silc_config_register_table(sub_ent, thisopt->subtable, thisopt->context);
+      /* run this block! */
+      ret = silc_config_main(sub_ent);
+      SILC_CONFIG_DEBUG(("Returned from sub-block [ret=%d]", ret));
+
+      if (ret) /* now check the result */
+       return ret;
+
+      /* now call block clean-up callback (if any) */
+      if (thisopt->cb) {
+       SILC_CONFIG_DEBUG(("Now calling clean-up callback (if any)"));
+       thisopt->cb(thisopt->type, thisopt->name, file->line,
+                   NULL, thisopt->context);
+      }
+      /* Do we want ';' to be mandatory after close brace? */
+      if (*(*p)++ != ';')
+       return SILC_CONFIG_EMISSCOLON;
+    }
+    else if (thisopt->type == SILC_CONFIG_ARG_NONE) {
+      /* before getting on, check if this row is REALLY complete */
+      if (*(*p)++ != ';')
+       return SILC_CONFIG_EMISSCOLON;
+      SILC_CONFIG_DEBUG(("Triggering callback for none"));
+      if (thisopt->cb) {
+       thisopt->cb(thisopt->type, thisopt->name, file->line,
+                   NULL, thisopt->context);
+      }
+    }
+    else {
+      void *pt;
+      int ret;
+
+      if (*(*p)++ != '=')
+       return SILC_CONFIG_EEXPECTEDEQUAL;
+
+      my_get_string(file, buf); /* get the option argument */
+      SILC_CONFIG_DEBUG(("With argument=\"%s\"", buf));
+
+      /* before getting on, check if this row is REALLY complete */
+      if (*(*p)++ != ';')
+       return SILC_CONFIG_EMISSCOLON;
+
+      /* convert the option argument to the right format */
+      pt = silc_config_marshall(thisopt->type, buf);
+      if (!pt)
+       return SILC_CONFIG_EINVALIDTEXT;
+      if (thisopt->cb) {
+       ret = thisopt->cb(thisopt->type, thisopt->name, file->line,
+                         pt, thisopt->context);
+       if (ret) {
+         SILC_CONFIG_DEBUG(("Callback refused the value [ret=%d]", ret));
+         return ret;
+       }
+      }
+      silc_free(pt);
+    }
+    continue;
+
+ finish:
+    break;
+  }
+
+  return SILC_CONFIG_OK;
+}
+
+/* ... */
+
+int silc_config_main(SilcConfigEntity ent)
+{
+  SilcConfigFile *file = ent->file;
+  int ret;
+
+  /* don't silently accept a NULL entity */
+  if (!ent) {
+    ret = SILC_CONFIG_EGENERIC;
+    goto main_cleanup;
+  }
+
+  /* call the real main and store the result */
+  file->level++;
+  SILC_CONFIG_DEBUG(("[Lev=%d] Entering config parsing core", file->level));
+  ret = silc_config_main_internal(ent);
+  SILC_CONFIG_DEBUG(("[Lev=%d] Quitting main [ret=%d]", file->level, ret));
+  if (!file->level) /* when swap happens, we could close a file twice */
+    goto main_end;
+  file->level--;
+
+  /* If this file was included don't destroy the options set because it is
+   * the same of the including block. Although if this entity is in a
+   * sub-block created inside the included file, this options set must be
+   * destroyed. */
+ main_cleanup:
+  if ((file->level != 0) || (file->included != TRUE))
+    silc_config_destroy(ent);
+
+ main_end:
+  return ret;
+}
diff --git a/lib/silcutil/silcconfig.h b/lib/silcutil/silcconfig.h
new file mode 100644 (file)
index 0000000..87db1cf
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+
+  silcconfig.h
+
+  Author: Johnny Mnemonic <johnny@themnemonic.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcConfigAPI
+ *
+ * DESCRIPTION
+ *
+ * The SILC Config util library is based on two main objects, SilcConfigFile
+ * (or File object) and SilcConfigEntity (or Entity).  The File objects are
+ * structs directly corresponding to the real files in the filesystem, while
+ * Entities are a little more abstract.
+ * An Entity is composed by delimited area on a File object (it can take the
+ * whole File object or just part of it), plus a group of known options.
+ *
+ * In order to parse this file, first you need to create a File object with
+ * the silc_config_open() function, and then you need to create the Entity
+ * with the silc_config_init() function.
+ * Now you can use the newly created Entity to register a group of expected
+ * known options and sub-blocks, and then you can call the main parsing loop
+ * with the silc_config_main() function.
+ * When silc_config_main() will return, if some error encoured the object file
+ * will point to the file that caused this error (this can be different from
+ * the originally opened file if it contained `Include' directives).  If no
+ * errors encoured then the File objects will still point to the original
+ * file.
+ * While silc_config_main() will take care of destroying Entities before
+ * returning, you need to take care that the File object you created is freed
+ * with the silc_config_close() function.
+ *
+ * The SILC Config library won't take care about storing the values contained
+ * in the config file.  You must take care about it with the callback
+ * functions.
+ *
+ * The config file syntax is pretty straightforward.  All lines starting
+ * with `#' will be skipped, while sub-blocks are delimited by braces (see
+ * the example below).
+ * Options with argument must have the `=' character between the option
+ * name and the value.  Simple words and numbers does not require quoting.
+ * There is a special built-in directive "Include" which allows you to include
+ * another config file in the point the directive is.  You can also Include
+ * inside a sub-block body, in this case when parsing the included config file
+ * it will be assumed that we are within this block, and the included file
+ * won't be allowed to close his root block.
+ *
+ * Example:
+ *
+ *    cipher {
+ *       name = aes-256-cbc;
+ *       module = "aes.sim.so";
+ *       key_length = 32;       # usually the default is just fine
+ *       block_length = 16;
+ *    };
+ *    Include "/etc/silc/hash_funcs.conf";
+ *
+ ***/
+
+#ifndef SILCCONFIG_H
+#define SILCCONFIG_H
+
+/****d* silcutil/SilcConfigAPI/errno
+ *
+ * NAME
+ *
+ *    enum { ... } - describe a SILC Config error
+ *
+ * DESCRIPTION
+ *
+ *    The virtual integer `errno' is returned by the silc_config_main()
+ *    function and indicates what went wrong.
+ *    You can convert it to the corresponding error string with the function
+ *    silc_config_strerror().
+ *
+ * SOURCE
+ */
+enum {
+  SILC_CONFIG_OK,              /* OK */
+  SILC_CONFIG_ESILENT,         /* Error defined by callback function */
+  SILC_CONFIG_EGENERIC,                /* Invalid syntax */
+  SILC_CONFIG_EINTERNAL,       /* Internal Error (caused by developer) */
+  SILC_CONFIG_ECANTOPEN,       /* Can't open specified file */
+  SILC_CONFIG_EOPENBRACE,      /* Expected open-brace '{' */
+  SILC_CONFIG_ECLOSEBRACE,     /* Missing close-brace '}' */
+  SILC_CONFIG_ETYPE,           /* Invalid data type */
+  SILC_CONFIG_EBADOPTION,      /* Unknown option */
+  SILC_CONFIG_EINVALIDTEXT,    /* Invalid text */
+  SILC_CONFIG_EDOUBLE,         /* Double option specification */
+  SILC_CONFIG_EEXPECTED,       /* Expected data but not found */
+  SILC_CONFIG_EEXPECTEDEQUAL,  /* Expected '=' */
+  SILC_CONFIG_EUNEXPECTED,     /* Unexpected data */
+  SILC_CONFIG_EMISSFIELDS,     /* Missing needed fields */
+  SILC_CONFIG_EMISSCOLON,      /* Missing ';' */
+};
+/***/
+
+/****d* silcutil/SilcConfigAPI/SilcConfigType
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcConfigType;
+ *
+ * DESCRIPTION
+ *
+ *    This identifies the parameter type that an option has. This parameter
+ *    is very important because the callback's *val pointer points to a
+ *    memory location containing the previously specified data type.
+ *    For example, if you specified an option with an integer parameter
+ *    callback's *val will be a pointer to an integer.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_CONFIG_ARG_TOGGLE,      /* TOGGLE on,off; yes,no; true, false; */
+  SILC_CONFIG_ARG_INT,         /* callback wants an integer */
+  SILC_CONFIG_ARG_STR,         /* callback expects \0-terminated str */
+  SILC_CONFIG_ARG_STRE,                /* same as above, but can also be empty */
+  SILC_CONFIG_ARG_BLOCK,       /* this is a sub-block */
+  SILC_CONFIG_ARG_SIZE,                /* like int, but accepts suffixes kMG */
+  SILC_CONFIG_ARG_NONE,                /* does not expect any args */
+} SilcConfigType;
+/***/
+
+/****f* silcutil/SilcConfigAPI/SilcConfigCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef int (*SilcConfigCallback)(SilcConfigType type, const char *name,
+ *                                      uint32 line, void *val, void *context);
+ * DESCRIPTION
+ *
+ *    This is the callback prototype for the options handler.  The pointer
+ *    `val' points to a location of type described by `type'.  `name' points
+ *    to a null-terminated string with the name of the option which triggered
+ *    this callback, that is stated at line `line'.  `context' is the
+ *    user-specified context provided when this option was registered.
+ *
+ ***/
+typedef int (*SilcConfigCallback)(SilcConfigType type, const char *name,
+                                 uint32 line, void *val, void *context);
+
+/****s* silcutil/SilcConfigAPI/SilcConfigTable
+ *
+ * SYNOPSIS
+ *
+ *    typedef struct { ... } SilcConfigTable;
+ *
+ * DESCRIPTION
+ *
+ *    SILC Config table defines an easy and quick way of registering options
+ *    in an entity. The function silc_config_register_table() will take as
+ *    argument a SilcConfigTable array terminated by a NULL struct, it is
+ *    important thus, that the `name' field of the terminating struct is set
+ *    to NULL.
+ *
+ *    char *name
+ *
+ *       The option name lowercase. The matching is always case-insensitive,
+ *       but for convention the option specification must always be lowercase.
+ *
+ *    SilcConfigType type
+ *
+ *       This specifies what kind of parameter this option expects.  The
+ *       special cases SILC_CONFIG_ARG_BLOCK tells SILC Config that this is
+ *       not a normal option but the name of a sub-block of the current
+ *       block (there is no limit to the number of nested blocks allowed).
+ *
+ *    SilcConfigCallback callback
+ *
+ *       Normally this is the value handler of the current option. If this
+ *       field is set to NULL then the value is silently discarded. Useful
+ *       for example to support deprecated options.
+ *
+ *    SilcConfigTable *subtable
+ *
+ *       If the `type' field is set to SILC_CONFIG_ARG_BLOCK, then this field
+ *       must point to a valid sub-table NULL-terminated array. If `type' is
+ *       something else, this valued is unused.
+ *
+ ***/
+typedef struct SilcConfigTableStruct {
+  char *name;
+  SilcConfigType type;
+  SilcConfigCallback callback;
+  const struct SilcConfigTableStruct *subtable;
+} SilcConfigTable;
+
+/****s* silcutil/SilcConfigAPI/SilcConfigFile
+ *
+ * SYNOPSIS
+ *
+ *    typedef struct { ... } SilcConfigFile;
+ *
+ * DESCRIPTION
+ *
+ *    A File object holds the data contained in a previously loaded file by
+ *    the silc_config_open() function.
+ *    This is an internally allocated struct and must be used only with the
+ *    helper functions.
+ *
+ ***/
+typedef struct SilcConfigFileObject SilcConfigFile;
+
+/****s* silcutil/SilcConfigAPI/SilcConfigEntity
+ *
+ * SYNOPSIS
+ *
+ *    typedef struct { ... } SilcConfigEntity;
+ *
+ * DESCRIPTION
+ *
+ *    The SILC Config is based on config entities.  An entity contains the
+ *    SilcConfigFile object we are parsing and the registered options.
+ *
+ ***/
+typedef struct SilcConfigEntityObject *SilcConfigEntity;
+
+/* Macros */
+
+/****d* silcutil/SilcConfigAPI/SILC_CONFIG_CALLBACK
+ *
+ * NAME
+ *
+ *    #define SILC_CONFIG_CALLBACK ...
+ *
+ * DESCRIPTION
+ *
+ *    Generic macro to define SilcConfigCallback functions. This defines a
+ *    static function with name `func' as a config callback function.
+ *
+ * SOURCE
+ */
+#define SILC_CONFIG_CALLBACK(func)                             \
+static int func(SilcConfigType type, const char *name,         \
+               uint32 line, void *val, void *context)
+/***/
+
+/* Prototypes */
+
+/****f* silcutil/SilcConfigAPI/silc_config_open
+ *
+ * SYNOPSIS
+ *
+ *    SilcConfigFile *silc_config_open(char *configfile);
+ *
+ * DESCRIPTION
+ *
+ *    Tries to open the config file `configfile' and returns a valid File
+ *    object on success, or NULL on failure.
+ *    An File object created this way must be destroyed with the function
+ *    silc_config_close().
+ *
+ ***/
+SilcConfigFile *silc_config_open(char *configfile);
+
+/****f* silcutil/SilcConfigAPI/silc_config_close
+ *
+ * SYNOPSIS
+ *
+ *    void silc_config_close(SilcConfigFile *file);
+ *
+ * DESCRIPTION
+ *
+ *    Closes and frees the File object `file', which must have been returned
+ *    by a previous call to silc_config_open().  Otherwise, or if
+ *    this function has already been called before for the same File object,
+ *    undefined behaviour occurs.
+ *    If `file' is NULL, no operation is performed.
+ *
+ ***/
+void silc_config_close(SilcConfigFile *file);
+
+/****f* silcutil/SilcConfigAPI/silc_config_init
+ *
+ * SYNOPSIS
+ *
+ *    SilcConfigEntity silc_config_init(SilcConfigFile *file);
+ *
+ * DESCRIPTION
+ *
+ *    Creates an Entity pointing to the valid File object `file', which must
+ *    be returned by a previous call to silc_config_open(), otherwise NULL
+ *    is returned.
+ *    Entities will be automatically destroyed after the call to the
+ *    silc_config_main() function, because of this no uninit functions are
+ *    provided.
+ *
+ ***/
+SilcConfigEntity silc_config_init(SilcConfigFile *file);
+
+/****f* silcutil/SilcConfigAPI/silc_config_strerror
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_config_strerror(int errnum);
+ *
+ * DESCRIPTION
+ *
+ *    The silc_config_strerror() function returns a string describing the
+ *    error code passed in the argument `errnum'.
+ *
+ ***/
+char *silc_config_strerror(int errnum);
+
+/****f* silcutil/SilcConfigAPI/silc_config_get_filename
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_config_get_filename(SilcConfigFile *file);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the original filename of the object file.
+ *    The returned pointer points to internally allocated storage and must
+ *    not be freed, modified or stored.
+ *
+ ***/
+char *silc_config_get_filename(SilcConfigFile *file);
+
+/****f* silcutil/SilcConfigAPI/silc_config_get_line
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_config_get_line(SilcConfigFile *file);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current line that file parsing arrived at.
+ *
+ ***/
+uint32 silc_config_get_line(SilcConfigFile *file);
+
+/****f* silcutil/SilcConfigAPI/silc_config_read_line
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_config_read_line(SilcConfigFile *file, uint32 line);
+ *
+ * DESCRIPTION
+ *
+ *    Returns a dynamically allocated null-terminated buffer containing the
+ *    line `line' of `file'.
+ *    The returned pointer must be freed when it's not needed any longer.
+ *
+ * SEE ALSO
+ *    silc_config_read_current_line
+ *
+ ***/
+char *silc_config_read_line(SilcConfigFile *file, uint32 line);
+
+/****f* silcutil/SilcConfigAPI/silc_config_read_current_line
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_config_read_current_line(SilcConfigFile *file);
+ *
+ * DESCRIPTION
+ *
+ *    Returns a dynamically allocated buffer containing the line that the
+ *    parser stopped at.  This is a convenience function for
+ *    silc_config_read_line.
+ *    The returned pointer must be freed when it's not needed any longer.
+ *
+ ***/
+char *silc_config_read_current_line(SilcConfigFile *file);
+
+/****f* silcutil/SilcConfigAPI/silc_config_register
+ *
+ * SYNOPSIS
+ *
+ *    void silc_config_register(SilcConfigEntity ent, const char *name,
+ *                              SilcConfigType type, SilcConfigCallback cb,
+ *                              const SilcConfigTable *subtable,
+ *                              void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Register option `name' in the entity `ent'. If `cb' is not NULL, it
+ *    will be called with the *val pointer pointing to an internally
+ *    allocated storage of type described by `type'.
+ *    If `type' is SILC_CONFIG_ARG_BLOCK, then `subtable' must be a valid
+ *    pointer to a SilcConfigTable array specified the options in the
+ *    sub-block.
+ *
+ * SEE ALSO
+ *    silc_config_register_table
+ *
+ ***/
+void silc_config_register(SilcConfigEntity ent, const char *name,
+                         SilcConfigType type, SilcConfigCallback cb,
+                         const SilcConfigTable *subtable, void *context);
+
+/****f* silcutil/SilcConfigAPI/silc_config_register_table
+ *
+ * SYNOPSIS
+ *
+ *    void silc_config_register_table(SilcConfigEntity ent,
+ *                                    const SilcConfigTable table[],
+ *                                    void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Register the tableset of options `table' automatically in the entity
+ *    `ent'.  If defined in the table, the callback functions will be called
+ *    all with the same context `context'.
+ *    The `table' array must be terminated with an entry with the name field
+ *    set to NULL.
+ *
+ * SEE ALSO
+ *    SilcConfigTable
+ *
+ ***/
+void silc_config_register_table(SilcConfigEntity ent,
+                               const SilcConfigTable table[], void *context);
+
+/****f* silcutil/SilcConfigAPI/silc_config_main
+ *
+ * SYNOPSIS
+ *
+ *    int silc_config_main(SilcConfigEntity ent);
+ *
+ * DESCRIPTION
+ *
+ *    Enter the main parsing loop. When this function returns the parsing
+ *    is finished in the current block (and sub-blocks).
+ *    When this function exits, the entity is already destroyed, because
+ *    of this you should set it to NULL right after the function call.
+ *
+ ***/
+int silc_config_main(SilcConfigEntity ent);
+
+#endif /* !SILCCONFIG_H */
diff --git a/lib/silcutil/silcdlist.h b/lib/silcutil/silcdlist.h
new file mode 100644 (file)
index 0000000..ef4a9d6
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+
+  silcdlist.h
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILDCLIST_H
+#define SILDCLIST_H
+
+#include "silclist.h"
+
+/*
+   SILC Dynamic List API
+
+   SILC Dynamic List API can be used to add opaque contexts to list that will
+   automatically allocate list entries.  Normal SILC List API cannot be used
+   for this purpose because in that case the context passed to the list must
+   be defined as list structure already.  This is not the case in SilcDList.
+
+   This is slower than SilcList because this requires one extra memory 
+   allocation when adding new entries to the list.  The context is probably
+   allocated already and the new list entry requires one additional memory 
+   allocation.  The memory allocation and free'ing is done automatically in
+   the API and does not show to the caller.
+
+   I left sorting functions out because I don't know whether we need them.
+   If needed, just copy them from silclist.h
+
+*/
+
+/* SilcDList object. This is the actual SilcDList object that is used by
+   application. Application defines this object and adds context's to this
+   list with functions defined below. */
+typedef struct {
+  SilcList list;
+} *SilcDList;
+
+/* SilcDListEntry structure, one entry in the list. This MUST NOT be used
+   directly by the application. */
+typedef struct SilcDListEntryStruct {
+  void *context;
+  struct SilcDListEntryStruct *next;
+} *SilcDListEntry;
+
+/* Initializes SilcDList. */
+
+static inline
+SilcDList silc_dlist_init()
+{
+  SilcDList list;
+
+  list = (SilcDList)silc_calloc(1, sizeof(*list));
+  silc_list_init(list->list, struct SilcDListEntryStruct, next);
+
+  return list;
+}
+
+/* Uninits and free's all memory. Must be called to free memory. Does NOT
+   free the contexts saved by caller. */
+
+static inline
+void silc_dlist_uninit(SilcDList list)
+{
+  if (list) {
+    SilcDListEntry e;
+    silc_list_start(list->list);
+    while ((e = (SilcDListEntry)silc_list_get(list->list)) != SILC_LIST_END) {
+      silc_list_del(list->list, e);
+      silc_free(e);
+    }
+    silc_free(list);
+  }
+}
+
+/* Return the number of entries in the list */
+
+static inline
+int silc_dlist_count(SilcDList list)
+{
+  return silc_list_count(list->list);
+}
+
+/* Set the start of the list. This prepares the list for traversing entries
+   from the start of the list. */
+
+static inline
+void silc_dlist_start(SilcDList list)
+{
+  silc_list_start(list->list);
+}
+
+/* Adds new entry to the list. This is the default function to add new
+   entries to the list. */
+
+static inline
+void silc_dlist_add(SilcDList list, void *context)
+{
+  SilcDListEntry e = (SilcDListEntry)silc_calloc(1, sizeof(*e));
+  e->context = context;
+  silc_list_add(list->list, e);
+}
+
+/* Remove entry from the list. Returns < 0 on error, 0 otherwise. */
+
+static inline
+void silc_dlist_del(SilcDList list, void *context)
+{
+  SilcDListEntry e;
+
+  silc_list_start(list->list);
+  while ((e = (SilcDListEntry)silc_list_get(list->list)) != SILC_LIST_END) {
+    if (e->context == context) {
+      silc_list_del(list->list, e);
+      silc_free(e);
+      break;
+    }
+  }
+}
+
+/* Returns current entry from the list and moves the list pointer forward
+   so that calling this next time returns the next entry from the list. This
+   can be used to traverse the list. Return SILC_LIST_END when the entire
+   list has ben traversed. Later, silc_list_start must be called again when 
+   re-starting list traversing. Example:
+
+   // Traverse the list from the beginning to the end 
+   silc_dlist_start(list)
+   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
+     ...
+   }
+   
+*/
+static inline
+void *silc_dlist_get(SilcDList list)
+{
+  SilcDListEntry e = (SilcDListEntry)silc_list_get(list->list);
+  if (e != SILC_LIST_END)
+    return e->context;
+  return SILC_LIST_END;
+}
+
+#endif
diff --git a/lib/silcutil/silchashtable.c b/lib/silcutil/silchashtable.c
new file mode 100644 (file)
index 0000000..8cd5530
--- /dev/null
@@ -0,0 +1,897 @@
+/*
+
+  silchashtable.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* Implementation of collision resistant hash table. This is a hash table
+   that provides a reliable (what you add stays there, and duplicate keys
+   are allowed) with as fast reference to the key as possible. If duplicate
+   keys are a lot in the hash table the lookup gets slower of course. 
+   However, this is reliable and no data is lost at any point. If you know
+   that you never have duplicate keys then this is as fast as any simple
+   hash table. */
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silchashtable.h"
+
+/* Define to 1 if you want hash table debug enabled */
+#define SILC_HASH_TABLE_DEBUG 0
+
+#if SILC_HASH_TABLE_DEBUG == 1
+#define SILC_HT_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SILC_HT_DEBUG(fmt)
+#endif
+
+/* Default size of the hash table (index to prime table) */
+#define SILC_HASH_TABLE_SIZE 3
+
+/* Produce the index by hashing the key */
+#define SILC_HASH_TABLE_HASH(f, c) \
+  ((f)(key, (c)) % primesize[ht->table_size])
+
+/* Check whether need to rehash */
+#define SILC_HASH_REHASH_INC \
+  (ht->auto_rehash && (ht->entry_count / 2) > primesize[ht->table_size])
+#define SILC_HASH_REHASH_DEC \
+  (ht->auto_rehash && (ht->entry_count * 2) < primesize[ht->table_size] && \
+   ht->entry_count > primesize[SILC_HASH_TABLE_SIZE])
+
+/* One entry in the hash table. Includes the key and the associated
+   context. The `next' pointer is non-NULL if two (or more) different
+   keys hashed to same value.  The pointer is the pointer to the next
+   entry. */
+typedef struct SilcHashTableEntryStruct {
+  void *key;
+  void *context;
+  struct SilcHashTableEntryStruct *next;
+} *SilcHashTableEntry;
+
+/* Hash table. */
+struct SilcHashTableStruct {
+  SilcHashTableEntry *table;
+  uint32 table_size;
+  uint32 entry_count;
+  SilcHashFunction hash;
+  SilcHashCompare compare;
+  SilcHashDestructor destructor;
+  void *hash_user_context;
+  void *compare_user_context;
+  void *destructor_user_context;
+  bool auto_rehash;
+};
+
+/* Prime sizes for the hash table. The size of the table will always
+   be one of these. */
+const uint32 primesize[42] = 
+{
+  1, 3, 5, 11, 17, 37, 67, 109, 131, 163, 257, 367, 521, 823, 1031, 
+  1237, 2053, 2777, 4099, 6247, 8209, 14057, 16411, 21089, 32771, 47431,
+  65537, 106721, 131101, 262147, 360163, 524309, 810343, 1048583, 2097169,
+  4194319, 6153409, 8388617, 13845163, 16777259, 33554467, 67108879
+};
+
+/* Find appropriate size for the hash table. The size will be a prime. */
+
+static uint32 silc_hash_table_primesize(uint32 size, uint32 *index)
+{
+  int i;
+
+  for (i = 0; i < sizeof(primesize); i++)
+    if (primesize[i] >= size) {
+      *index = i;
+      SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index]));
+      return primesize[i];
+    }
+
+  *index = i - 1;
+  SILC_HT_DEBUG(("sizeof of the hash table is %d", primesize[*index]));
+  return primesize[i - 1];
+}
+
+/* Internal routine to find entry in the hash table by `key'. Returns
+   the previous entry (if exists) as well. */
+
+static inline SilcHashTableEntry *
+silc_hash_table_find_internal(SilcHashTable ht, void *key,
+                             SilcHashTableEntry *prev_entry,
+                             SilcHashFunction hash, void *hash_user_context,
+                             SilcHashCompare compare, 
+                             void *compare_user_context)
+{
+  SilcHashTableEntry *entry, prev = NULL;
+  uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+  SILC_HT_DEBUG(("index %d key %p", i, key));
+
+  entry = &ht->table[i];
+  if (compare) {
+    while (*entry && !compare((*entry)->key, key, compare_user_context)) {
+      prev = *entry;
+      entry = &(*entry)->next;
+    }
+  } else {
+    while (*entry && (*entry)->key != key) {
+      prev = *entry;
+      entry = &(*entry)->next;
+    }
+  }
+
+  *prev_entry = prev;
+  return entry;
+}
+
+/* Internal routine to find entry in the hash table by `key' and `context'.
+   Returns the previous entry (if exists) as well. */
+
+static inline SilcHashTableEntry *
+silc_hash_table_find_internal_context(SilcHashTable ht, void *key,
+                                     void *context,
+                                     SilcHashTableEntry *prev_entry,
+                                     SilcHashFunction hash, 
+                                     void *hash_user_context,
+                                     SilcHashCompare compare, 
+                                     void *compare_user_context)
+{
+  SilcHashTableEntry *entry, prev = NULL;
+  uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+  SILC_HT_DEBUG(("index %d key %p context %p", i, key, context));
+
+  entry = &ht->table[i];
+  if (ht->compare) {
+    while (*entry) {
+      if (compare((*entry)->key, key, compare_user_context) &&
+         (*entry)->context == context)
+       break;
+      prev = *entry;
+      entry = &(*entry)->next;
+    }
+  } else {
+    while (*entry) {
+      if ((*entry)->key == key && (*entry)->context == context)
+       break;
+      prev = *entry;
+      entry = &(*entry)->next;
+    }
+  }
+
+  *prev_entry = prev;
+  return entry;
+}
+
+/* Internal routine to find entry in the hash table by `key'. */
+
+static inline SilcHashTableEntry *
+silc_hash_table_find_internal_simple(SilcHashTable ht, void *key,
+                                    SilcHashFunction hash,
+                                    void *hash_user_context,
+                                    SilcHashCompare compare,
+                                    void *compare_user_context)
+{
+  SilcHashTableEntry *entry;
+  uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+  SILC_HT_DEBUG(("index %d key %p", i, key));
+
+  entry = &ht->table[i];
+  if (compare) {
+    while (*entry && !compare((*entry)->key, key, compare_user_context))
+      entry = &(*entry)->next;
+  } else {
+    while (*entry && (*entry)->key != key)
+      entry = &(*entry)->next;
+  }
+
+  return entry;
+}
+
+/* Internal routine to find all keys by `key'. This may return multiple
+   entries if multiple entries with same key exists. With specific
+   hash and comparison functions. */
+
+static inline void
+silc_hash_table_find_internal_all(SilcHashTable ht, void *key, 
+                                 SilcHashFunction hash,
+                                 void *hash_user_context,
+                                 SilcHashCompare compare,
+                                 void *compare_user_context,
+                                 SilcHashForeach foreach,
+                                 void *foreach_user_context)
+{
+  SilcHashTableEntry *entry, *tmp;
+  bool auto_rehash;
+  uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+  SILC_HT_DEBUG(("index %d key %p", i, key));
+
+  /* Disallow auto rehashing while going through the table since we call
+     the `foreach' function which could alter the table. */
+  auto_rehash = ht->auto_rehash;
+  ht->auto_rehash = FALSE;
+
+  entry = &ht->table[i];
+  if (compare) {
+    while (*entry) {
+      if (compare((*entry)->key, key, compare_user_context)) {
+       tmp = &(*entry)->next;
+       foreach((*entry)->key, (*entry)->context, foreach_user_context);
+       entry = tmp;
+       continue;
+      }
+      entry = &(*entry)->next;
+    }
+  } else {
+    while (*entry) {
+      if ((*entry)->key == key) {
+       tmp = &(*entry)->next;
+       foreach((*entry)->key, (*entry)->context, foreach_user_context);
+       entry = tmp;
+       continue;
+      }
+      entry = &(*entry)->next;
+    }
+  }
+
+  ht->auto_rehash = auto_rehash;
+}
+
+/* Internal routine to add new key to the hash table */
+
+static inline void
+silc_hash_table_add_internal(SilcHashTable ht, void *key, void *context,
+                            SilcHashFunction hash, 
+                            void *hash_user_context)
+{
+  SilcHashTableEntry *entry;
+  uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+  SILC_HT_DEBUG(("index %d key %p", i, key));
+
+  entry = &ht->table[i];
+  if (*entry) {
+    /* The entry exists already. We have a collision, add it to the
+       list to avoid collision. */
+    SilcHashTableEntry e, tmp;
+
+    e = *entry;
+    tmp = e->next;
+    while (tmp) {
+      e = tmp;
+      tmp = tmp->next;
+    }
+
+    SILC_HT_DEBUG(("Collision; adding new key to list"));
+
+    e->next = silc_calloc(1, sizeof(*e->next));
+    e->next->key = key;
+    e->next->context = context;
+    ht->entry_count++;
+  } else {
+    /* New key */
+    SILC_HT_DEBUG(("New key"));
+    *entry = silc_calloc(1, sizeof(**entry));
+    (*entry)->key = key;
+    (*entry)->context = context;
+    ht->entry_count++;
+  }
+
+  if (SILC_HASH_REHASH_INC)
+    silc_hash_table_rehash(ht, 0);
+}
+
+/* Internal routine to replace old key with new one (if it exists) */
+
+static inline void
+silc_hash_table_replace_internal(SilcHashTable ht, void *key, void *context,
+                                SilcHashFunction hash, 
+                                void *hash_user_context)
+{
+  SilcHashTableEntry *entry;
+  uint32 i = SILC_HASH_TABLE_HASH(hash, hash_user_context);
+
+  SILC_HT_DEBUG(("index %d key %p", i, key));
+
+  entry = &ht->table[i];
+  if (*entry) {
+    /* The entry exists already. We have a collision, replace the old
+       key and context. */
+    if (ht->destructor)
+      ht->destructor((*entry)->key, (*entry)->context, 
+                    ht->destructor_user_context);
+  } else {
+    /* New key */
+    *entry = silc_calloc(1, sizeof(**entry));
+    ht->entry_count++;
+  }
+
+  (*entry)->key = key;
+  (*entry)->context = context;
+
+  if (SILC_HASH_REHASH_INC)
+    silc_hash_table_rehash(ht, 0);
+}
+
+/* Allocates new hash table and returns it.  If the `table_size' is not
+   zero then the hash table size is the size provided. If zero then the
+   default size will be used. Note that if the `table_size' is provided
+   it should be a prime. The `hash', `compare' and `destructor' are
+   the hash function, the key comparison function and key and context
+   destructor function, respectively. The `hash' is mandatory, the others
+   are optional. */
+
+SilcHashTable silc_hash_table_alloc(uint32 table_size, 
+                                   SilcHashFunction hash,
+                                   void *hash_user_context,
+                                   SilcHashCompare compare,
+                                   void *compare_user_context,
+                                   SilcHashDestructor destructor,
+                                   void *destructor_user_context,
+                                   bool auto_rehash)
+{
+  SilcHashTable ht;
+  uint32 size_index = SILC_HASH_TABLE_SIZE;
+
+  if (!hash)
+    return NULL;
+
+  ht = silc_calloc(1, sizeof(*ht));
+  ht->table = silc_calloc(table_size ? silc_hash_table_primesize(table_size,
+                                                                &size_index) :
+                         primesize[SILC_HASH_TABLE_SIZE],
+                         sizeof(*ht->table));
+  ht->table_size = size_index;
+  ht->hash = hash;
+  ht->compare = compare;
+  ht->destructor = destructor;
+  ht->hash_user_context = hash_user_context;
+  ht->compare_user_context = compare_user_context;
+  ht->destructor_user_context = destructor_user_context;
+  ht->auto_rehash = auto_rehash;
+
+  return ht;
+}
+
+/* Frees the hash table. The destructor function provided in the
+   silc_hash_table_alloc will be called for all keys in the hash table. */
+
+void silc_hash_table_free(SilcHashTable ht)
+{
+  SilcHashTableEntry e, tmp;
+  int i;
+
+  for (i = 0; i < primesize[ht->table_size]; i++) {
+    e = ht->table[i];
+    while (e) {
+      if (ht->destructor)
+       ht->destructor(e->key, e->context, ht->destructor_user_context);
+      tmp = e;
+      e = e->next;
+      silc_free(tmp);
+    }
+  }
+
+  silc_free(ht->table);
+  silc_free(ht);
+}
+
+/* Returns the size of the hash table */
+
+uint32 silc_hash_table_size(SilcHashTable ht)
+{
+  return primesize[ht->table_size];
+}
+
+/* Returns the number of the entires in the hash table. If there is more
+   entries in the table thatn the size of the hash table calling the
+   silc_hash_table_rehash is recommended. */
+
+uint32 silc_hash_table_count(SilcHashTable ht)
+{
+  return ht->entry_count;
+}
+
+/* Adds new entry to the hash table. The `key' is hashed using the
+   hash function and the both `key' and `context' will be saved to the
+   hash table. This function quarantees that the entry is always added
+   to the hash table reliably (it is collision resistant). */
+
+void silc_hash_table_add(SilcHashTable ht, void *key, void *context)
+{
+  silc_hash_table_add_internal(ht, key, context, ht->hash, 
+                              ht->hash_user_context);
+}
+
+/* Same as above but with specific hash function and user context. */
+
+void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
+                            SilcHashFunction hash, void *hash_user_context)
+{
+  silc_hash_table_add_internal(ht, key, context, hash, hash_user_context);
+}
+
+/* Same as above but if the `key' already exists in the hash table
+   the old key and the old context will be replace with the `key' and
+   the `context. The destructor function will be called for the
+   replaced key and context. */
+
+void silc_hash_table_replace(SilcHashTable ht, void *key, void *context)
+{
+  silc_hash_table_replace_internal(ht, key, context, ht->hash, 
+                                  ht->hash_user_context);
+}
+
+/* Same as above but with specific hash function. */
+
+void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context,
+                                SilcHashFunction hash, 
+                                void *hash_user_context)
+{
+  silc_hash_table_replace_internal(ht, key, context, hash, hash_user_context);
+}
+
+/* Removes the entry from the hash table by the provided `key'. This will
+   call the destructor funtion for the found entry. Return TRUE if the
+   entry was removed successfully and FALSE otherwise. */
+
+bool silc_hash_table_del(SilcHashTable ht, void *key)
+{
+  SilcHashTableEntry *entry, prev, e;
+
+  entry = silc_hash_table_find_internal(ht, key, &prev,
+                                       ht->hash, ht->hash_user_context,
+                                       ht->compare, ht->compare_user_context);
+  if (*entry == NULL)
+    return FALSE;
+
+  e = *entry;
+
+  if (!prev && e->next)
+    *entry = e->next;
+  if (!prev && e->next == NULL)
+    *entry = NULL;
+  if (prev)
+    prev->next = NULL;
+  if (prev && e->next)
+    prev->next = e->next;
+
+  if (ht->destructor)
+    ht->destructor(e->key, e->context, ht->destructor_user_context);
+  silc_free(e);
+
+  ht->entry_count--;
+
+  if (SILC_HASH_REHASH_DEC)
+    silc_hash_table_rehash(ht, 0);
+
+  return TRUE;
+}
+
+/* Same as above but with specific hash and compare functions. */
+
+bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
+                            SilcHashFunction hash, 
+                            void *hash_user_context,
+                            SilcHashCompare compare, 
+                            void *compare_user_context,
+                            SilcHashDestructor destructor,
+                            void *destructor_user_context)
+{
+  SilcHashTableEntry *entry, prev, e;
+
+  entry = silc_hash_table_find_internal(ht, key, &prev,
+                                       hash ? hash : ht->hash,
+                                       hash_user_context ? hash_user_context :
+                                       ht->hash_user_context,
+                                       compare ? compare : ht->compare,
+                                       compare_user_context ? 
+                                       compare_user_context :
+                                       ht->compare_user_context);
+  if (*entry == NULL)
+    return FALSE;
+
+  e = *entry;
+
+  if (!prev && e->next)
+    *entry = e->next;
+  if (!prev && e->next == NULL)
+    *entry = NULL;
+  if (prev)
+    prev->next = NULL;
+  if (prev && e->next)
+    prev->next = e->next;
+
+  if (destructor) {
+    destructor(e->key, e->context, destructor_user_context);
+  } else {
+    if (ht->destructor)
+      ht->destructor(e->key, e->context, ht->destructor_user_context);
+  }
+  silc_free(e);
+
+  ht->entry_count--;
+
+  if (SILC_HASH_REHASH_DEC)
+    silc_hash_table_rehash(ht, 0);
+
+  return TRUE;
+}
+
+/* Same as above but verifies that the context associated with the `key'
+   matches the `context'. This is handy to use with hash tables that may
+   have duplicate keys. In that case the `context' may be used to check
+   whether the correct entry is being deleted. */
+
+bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, 
+                                   void *context)
+{
+  SilcHashTableEntry *entry, prev, e;
+
+  entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
+                                               ht->hash, 
+                                               ht->hash_user_context,
+                                               ht->compare,
+                                               ht->compare_user_context);
+  if (*entry == NULL)
+    return FALSE;
+
+  e = *entry;
+
+  if (!prev && e->next)
+    *entry = e->next;
+  if (!prev && e->next == NULL)
+    *entry = NULL;
+  if (prev)
+    prev->next = NULL;
+  if (prev && e->next)
+    prev->next = e->next;
+
+  if (ht->destructor)
+    ht->destructor(e->key, e->context, ht->destructor_user_context);
+  silc_free(e);
+
+  ht->entry_count--;
+
+  if (SILC_HASH_REHASH_DEC)
+    silc_hash_table_rehash(ht, 0);
+
+  return TRUE;
+}
+
+/* Same as above but with specific hash and compare functions. */
+
+bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, 
+                                       void *context,
+                                       SilcHashFunction hash, 
+                                       void *hash_user_context,
+                                       SilcHashCompare compare, 
+                                       void *compare_user_context,
+                                       SilcHashDestructor destructor,
+                                       void *destructor_user_context)
+{
+  SilcHashTableEntry *entry, prev, e;
+
+  entry = silc_hash_table_find_internal_context(ht, key, context, &prev,
+                                               hash ? hash : ht->hash,
+                                               hash_user_context ? 
+                                               hash_user_context :
+                                               ht->hash_user_context,
+                                               compare ? 
+                                               compare : ht->compare,
+                                               compare_user_context ? 
+                                               compare_user_context :
+                                               ht->compare_user_context);
+  if (*entry == NULL)
+    return FALSE;
+
+  e = *entry;
+
+  if (!prev && e->next)
+    *entry = e->next;
+  if (!prev && e->next == NULL)
+    *entry = NULL;
+  if (prev)
+    prev->next = NULL;
+  if (prev && e->next)
+    prev->next = e->next;
+
+  if (destructor) {
+    destructor(e->key, e->context, destructor_user_context);
+  } else {
+    if (ht->destructor)
+      ht->destructor(e->key, e->context, ht->destructor_user_context);
+  }
+  silc_free(e);
+
+  ht->entry_count--;
+
+  if (SILC_HASH_REHASH_DEC)
+    silc_hash_table_rehash(ht, 0);
+
+  return TRUE;
+}
+
+/* Finds the entry in the hash table by the provided `key' as fast as
+   possible. Return TRUE if the entry was found and FALSE otherwise. 
+   The found entry is returned to the `ret_key' and `ret_context',
+   respectively. If the `ret_key and `ret_context' are NULL then this
+   maybe used only to check whether given key exists in the table. */
+
+bool silc_hash_table_find(SilcHashTable ht, void *key,
+                         void **ret_key, void **ret_context)
+{
+  SilcHashTableEntry *entry;
+
+  entry = silc_hash_table_find_internal_simple(ht, key, ht->hash, 
+                                              ht->hash_user_context,
+                                              ht->compare, 
+                                              ht->compare_user_context);
+  if (*entry == NULL)
+    return FALSE;
+
+  if (ret_key)
+    *ret_key = (*entry)->key;
+  if (ret_context)
+    *ret_context = (*entry)->context;
+
+  return TRUE;
+}
+
+/* Same as above but with specified hash and comparison functions. */
+
+bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
+                             void **ret_key, void **ret_context,
+                             SilcHashFunction hash, 
+                             void *hash_user_context,
+                             SilcHashCompare compare, 
+                             void *compare_user_context)
+{
+  SilcHashTableEntry *entry;
+
+  entry = silc_hash_table_find_internal_simple(ht, key,
+                                              hash ? hash : ht->hash, 
+                                              hash_user_context ? 
+                                              hash_user_context :
+                                              ht->hash_user_context,
+                                              compare ? compare :
+                                              ht->compare, 
+                                              compare_user_context ?
+                                              compare_user_context :
+                                              ht->compare_user_context);
+  if (*entry == NULL)
+    return FALSE;
+
+  if (ret_key)
+    *ret_key = (*entry)->key;
+  if (ret_context)
+    *ret_context = (*entry)->context;
+
+  return TRUE;
+}
+
+/* As the hash table is collision resistant it is possible to save duplicate
+   keys to the hash table. This function can be used to find all keys
+   and contexts from the hash table that are found using the `key'. The
+   `foreach' is called for every found key. */
+
+void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
+                                 SilcHashForeach foreach, void *user_context)
+{
+  silc_hash_table_find_internal_all(ht, key, ht->hash, ht->hash_user_context,
+                                   ht->compare, ht->compare_user_context,
+                                   foreach, user_context);
+}
+
+/* Same as above but with specific hash and comparison functions. */
+
+void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
+                                     SilcHashFunction hash, 
+                                     void *hash_user_context,
+                                     SilcHashCompare compare, 
+                                     void *compare_user_context,
+                                     SilcHashForeach foreach, 
+                                     void *foreach_user_context)
+{
+  silc_hash_table_find_internal_all(ht, key,
+                                   hash ? hash : ht->hash, 
+                                   hash_user_context ? 
+                                   hash_user_context :
+                                   ht->hash_user_context,
+                                   compare ? compare :
+                                   ht->compare, 
+                                   compare_user_context ?
+                                   compare_user_context :
+                                   ht->compare_user_context,
+                                   foreach, foreach_user_context);
+}
+
+/* Traverse all entrys in the hash table and call the `foreach' for
+   every entry with the `user_context' context. */
+
+void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
+                            void *user_context)
+{
+  SilcHashTableEntry e, tmp;
+  int i;
+  bool auto_rehash;
+
+  if (!foreach)
+    return;
+
+  auto_rehash = ht->auto_rehash;
+  ht->auto_rehash = FALSE;
+  for (i = 0; i < primesize[ht->table_size]; i++) {
+    e = ht->table[i];
+    while (e) {
+      /* Entry may become invalid inside the `foreach' */
+      tmp = e->next;
+      foreach(e->key, e->context, user_context);
+      e = tmp;
+    }
+  }
+  ht->auto_rehash = auto_rehash;
+}
+
+/* Rehashs the hash table. The size of the new hash table is provided
+   as `new_size'. If the `new_size' is zero then this routine will make
+   the new table of a suitable size. Note that this operation may be
+   very slow. */
+
+void silc_hash_table_rehash(SilcHashTable ht, uint32 new_size)
+{
+  int i;
+  SilcHashTableEntry *table, e, tmp;
+  uint32 table_size, size_index;
+
+  SILC_HT_DEBUG(("Start"));
+
+  if (new_size)
+    silc_hash_table_primesize(new_size, &size_index);
+  else
+    silc_hash_table_primesize(ht->entry_count, &size_index);
+
+  if (size_index == ht->table_size)
+    return;
+
+  SILC_HT_DEBUG(("Rehashing"));
+
+  /* Take old hash table */
+  table = ht->table;
+  table_size = ht->table_size;
+
+  /* Allocate new table */
+  ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
+  ht->table_size = size_index;
+  ht->entry_count = 0;
+
+  /* Rehash */
+  for (i = 0; i < primesize[table_size]; i++) {
+    e = table[i];
+    while (e) {
+      silc_hash_table_add(ht, e->key, e->context);
+      tmp = e;
+      e = e->next;
+
+      /* Remove old entry */
+      silc_free(tmp);
+    }
+  }
+
+  /* Remove old table */
+  silc_free(table);
+}
+
+/* Same as above but with specific hash function. */
+
+void silc_hash_table_rehash_ext(SilcHashTable ht, uint32 new_size,
+                               SilcHashFunction hash, 
+                               void *hash_user_context)
+{
+  int i;
+  SilcHashTableEntry *table, e, tmp;
+  uint32 table_size, size_index;
+
+  SILC_HT_DEBUG(("Start"));
+
+  if (new_size)
+    silc_hash_table_primesize(new_size, &size_index);
+  else
+    silc_hash_table_primesize(ht->entry_count, &size_index);
+
+  if (size_index == ht->table_size)
+    return;
+
+  SILC_HT_DEBUG(("Rehashing"));
+
+  /* Take old hash table */
+  table = ht->table;
+  table_size = ht->table_size;
+
+  /* Allocate new table */
+  ht->table = silc_calloc(primesize[size_index], sizeof(*ht->table));
+  ht->table_size = size_index;
+  ht->entry_count = 0;
+
+  /* Rehash */
+  for (i = 0; i < primesize[table_size]; i++) {
+    e = table[i];
+    while (e) {
+      silc_hash_table_add_ext(ht, e->key, e->context, hash, 
+                             hash_user_context);
+      tmp = e;
+      e = e->next;
+
+      /* Remove old entry */
+      silc_free(tmp);
+    }
+  }
+
+  /* Remove old table */
+  silc_free(table);
+}
+
+/* Prepares the `htl' list structure sent as argument to be used in the
+   hash table traversing with the silc_hash_table_get. Usage:
+   SilcHashTableList htl; silc_hash_table_list(ht, &htl); */
+
+void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl)
+{
+  htl->ht = ht;
+  htl->entry = NULL;
+  htl->index = 0;
+  htl->auto_rehash = ht->auto_rehash;
+
+  /* Disallow rehashing of the table while traversing the table */
+  ht->auto_rehash = FALSE;
+}
+
+/* Resets the `htl' SilcHashTableList. */
+
+void silc_hash_table_list_reset(SilcHashTableList *htl)
+{
+  /* Set back the original auto rehash value to the table */
+  htl->ht->auto_rehash = htl->auto_rehash;
+}
+
+/* Returns always the next entry in the hash table into the `key' and
+   `context' and TRUE.  If this returns FALSE then there are no anymore
+   any entrys. Usage: while (silc_hash_table_get(&htl, &key, &context)) */
+
+bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context)
+{
+  SilcHashTableEntry entry = (SilcHashTableEntry)htl->entry;
+
+  if (!htl->ht->entry_count)
+    return FALSE;
+
+  while (!entry && htl->index < primesize[htl->ht->table_size]) {
+    entry = htl->ht->table[htl->index];
+    htl->index++;
+  }
+
+  if (!entry)
+    return FALSE;
+
+  htl->entry = entry->next;
+
+  if (key)
+    *key = entry->key;
+  if (context)
+    *context = entry->context;
+
+  return TRUE;
+}
diff --git a/lib/silcutil/silchashtable.h b/lib/silcutil/silchashtable.h
new file mode 100644 (file)
index 0000000..9c4c823
--- /dev/null
@@ -0,0 +1,657 @@
+/*
+
+  silchashtable.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcHashTableAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of collision resistant hash table. This is a hash table
+ * that provides a reliable (what you add there stays there, and duplicate
+ * keys are allowed) with as fast reference to the key as possible. If
+ * there are a lot of duplicate keys in the hash table the lookup gets
+ * slower of course. However, this is reliable and no data is lost at any
+ * point. If you know that you never have duplicate keys then this is as
+ * fast as any simple hash table.
+ *
+ * The interface provides many ways to search the hash table including
+ * an extended interface where caller can specify its own hash and comparison
+ * functions.
+ *
+ * There are two ways tro traverse the entire hash table if this feature
+ * is needed. There exists a foreach function that calls a foreach
+ * callback for each entry in the hash table. Other way is to use
+ * SilcHashTableList structure and traverse the hash table inside while()
+ * using the list structure. Both are equally fast.
+ *
+ ***/
+
+#ifndef SILCHASHTABLE_H
+#define SILCHASHTABLE_H
+
+/****s* silcutil/SilcHashTableAPI/SilcHashTable
+ *
+ * NAME
+ * 
+ *    typedef struct SilcHashTableStruct *SilcHashTable;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual hash table and is allocated
+ *    by silc_hash_table_alloc and given as argument usually to
+ *    all silc_hash_table_* functions.  It is freed by the
+ *    silc_hash_table_free function.
+ *
+ ***/
+typedef struct SilcHashTableStruct *SilcHashTable;
+
+/****s* silcutil/SilcHashTableAPI/SilcHashTableList
+ *
+ * NAME
+ * 
+ *    typedef struct SilcHashTableListStruct SilcHashTableList;
+ *
+ * DESCRIPTION
+ *
+ *    This structure is used to tarverse the hash table. This structure
+ *    is given as argument to the silc_hash_table_list function to 
+ *    initialize it and then used to traverse the hash table with the
+ *    silc_hash_table_get function. It needs not be allocated or freed.
+ *
+ * EXAMPLE
+ *
+ *    SilcHashTableList htl;
+ *    silc_hash_table_list(hash_table, &htl);
+ *    while (silc_hash_table_get(&htl, (void *)&key, (void *)&context))
+ *      ...
+ *    silc_hash_table_list_reset(&htl);
+ *
+ * SOURCE
+ */
+typedef struct SilcHashTableListStruct SilcHashTableList;
+
+/* List structure to traverse the hash table. */
+struct SilcHashTableListStruct {
+  SilcHashTable ht;
+  void *entry;
+  uint32 index;
+  bool auto_rehash;
+};
+/***/
+
+/****f* silcutil/SilcHashTableAPI/SilcHashFunction
+ *
+ * SYNOPSIS
+ *
+ *    typedef uint32 (*SilcHashFunction)(void *key, void *user_context);
+ *
+ * DESCRIPTION
+ *
+ *    A type for the hash function. This function is used to hash the
+ *    provided key value `key' and return the index for the hash table.
+ *    The `user_context' is application specific context and is delivered
+ *    to the callback.
+ *
+ ***/
+typedef uint32 (*SilcHashFunction)(void *key, void *user_context);
+
+/****f* silcutil/SilcHashTableAPI/SilcHashCompare
+ *
+ * SYNOPSIS
+ *
+ *    typedef bool (*SilcHashCompare)(void *key1, void *key2, 
+ *                                    void *user_context);
+ *
+ * DESCRIPTION
+ *
+ *    A comparison funtion that is called to compare the two keys `key1' and
+ *    `key2'. If they are equal this must return TRUE or FALSE otherwise.
+ *    The application provides this function when allocating a new hash table.
+ *    The `user_context' is application specific context and is delivered
+ *    to the callback.
+ *
+ ***/
+typedef bool (*SilcHashCompare)(void *key1, void *key2, void *user_context);
+
+/****f* silcutil/SilcHashTableAPI/SilcHashDestructor
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcHashDestructor)(void *key, void *context, 
+ *                                       void *user_context);
+ *
+ * DESCRIPTION
+ *
+ *    A destructor callback that the library will call to destroy the 
+ *    `key' and `context'.  The appliation provides the function when
+ *    allocating a new hash table. The `user_context' is application
+ *    specific context and is delivered to the callback.
+ *
+ ***/
+typedef void (*SilcHashDestructor)(void *key, void *context, 
+                                  void *user_context);
+
+/****f* silcutil/SilcHashTableAPI/SilcHashForeach
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcHashForeach)(void *key, void *context, 
+ *                                    void *user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Foreach function. This is called when traversing the entrys in the
+ *    hash table using silc_hash_table_foreach. The `user_context' is
+ *    application specific context and is delivered to the callback.
+ *
+ ***/
+typedef void (*SilcHashForeach)(void *key, void *context, void *user_context);
+
+/* Simple hash table interface */
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_alloc
+ *
+ * SYNOPSIS
+ *
+ *    SilcHashTable silc_hash_table_alloc(uint32 table_size, 
+ *                                        SilcHashFunction hash,
+ *                                        void *hash_user_context,
+ *                                        SilcHashCompare compare,
+ *                                        void *compare_user_context,
+ *                                        SilcHashDestructor destructor,
+ *                                        void *destructor_user_context,
+ *                                        bool auto_rehash);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates new hash table and returns it.  If the `table_size' is not
+ *    zero then the hash table size is the size provided. If zero then the
+ *    default size will be used. Note that if the `table_size' is provided
+ *    it should be a prime. The `hash', `compare' and `destructor' are
+ *    the hash function, the key comparison function and key and context
+ *    destructor function, respectively. The `hash' is mandatory, the others
+ *    are optional.
+ *
+ ***/
+SilcHashTable silc_hash_table_alloc(uint32 table_size, 
+                                   SilcHashFunction hash,
+                                   void *hash_user_context,
+                                   SilcHashCompare compare,
+                                   void *compare_user_context,
+                                   SilcHashDestructor destructor,
+                                   void *destructor_user_context,
+                                   bool auto_rehash);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_free(SilcHashTable ht);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the hash table. The destructor function provided in the
+ *    silc_hash_table_alloc will be called for all keys in the hash table.
+ *
+ ***/
+void silc_hash_table_free(SilcHashTable ht);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_size
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_hash_table_size(SilcHashTable ht);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the size of the hash table. This is the true size of the
+ *    hash table.
+ *
+ ***/
+uint32 silc_hash_table_size(SilcHashTable ht);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_count
+ *
+ * SYNOPSIS
+ *
+ *    uint32 silc_hash_table_count(SilcHashTable ht);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the number of the entires in the hash table. If there is more
+ *    entries in the table thatn the size of the hash table calling the
+ *    silc_hash_table_rehash is recommended.
+ *
+ ***/
+uint32 silc_hash_table_count(SilcHashTable ht);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_add
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_add(SilcHashTable ht, void *key, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Adds new entry to the hash table. The `key' is hashed using the
+ *    hash function and the both `key' and `context' will be saved to the
+ *    hash table. This function quarantees that the entry is always added
+ *    to the hash table reliably (it is collision resistant).
+ *
+ ***/
+void silc_hash_table_add(SilcHashTable ht, void *key, void *context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_replace
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_replace(SilcHashTable ht, void *key, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_hash_table_add but if the `key' already exists in the
+ *    hash table the old key and the old context will be replaced with the
+ *    `key' and the `context. The destructor function will be called for the
+ *    replaced key and context.
+ *
+ ***/
+void silc_hash_table_replace(SilcHashTable ht, void *key, void *context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_del
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_del(SilcHashTable ht, void *key);
+ *
+ * DESCRIPTION
+ *
+ *    Removes the entry from the hash table by the provided `key'. This will
+ *    call the destructor funtion for the found entry. Return TRUE if the
+ *    entry was removed successfully and FALSE otherwise.
+ *
+ ***/
+bool silc_hash_table_del(SilcHashTable ht, void *key);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_del_by_context
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, 
+ *                                        void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_hash_table_del but verifies that the context associated
+ *    with the `key' matches the `context'. This is handy to use with hash
+ *    tables that may have duplicate keys. In that case the `context' may
+ *    be used to check whether the correct entry is being deleted.
+ *
+ ***/
+bool silc_hash_table_del_by_context(SilcHashTable ht, void *key, 
+                                   void *context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_find
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_find(SilcHashTable ht, void *key,
+ *                              void **ret_key, void **ret_context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds the entry in the hash table by the provided `key' as fast as
+ *    possible. Return TRUE if the entry was found and FALSE otherwise. 
+ *    The found entry is returned to the `ret_key' and `ret_context',
+ *    respectively. If the `ret_key and `ret_context' are NULL then this
+ *    maybe used only to check whether given key exists in the table.
+ *
+ ***/
+bool silc_hash_table_find(SilcHashTable ht, void *key,
+                         void **ret_key, void **ret_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_find_foreach
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
+ *                                      SilcHashForeach foreach, 
+ *                                      void *user_context);
+ *
+ * DESCRIPTION
+ *
+ *    As the hash table is collision resistant it is possible to save duplicate
+ *    keys to the hash table. This function can be used to find all keys
+ *    and contexts from the hash table that are found using the `key'. The
+ *    `foreach' is called for every found key.
+ *
+ * NOTES
+ *
+ *    The hash table will not be rehashed during the traversing of the table,
+ *    even if the table was marked as auto rehashable.  The caller also must
+ *    not call silc_hash_table_rehash while traversing the table.
+ *
+ ***/
+void silc_hash_table_find_foreach(SilcHashTable ht, void *key,
+                                 SilcHashForeach foreach, void *user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_foreach
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
+ *                                 void *user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Traverse all entrys in the hash table and call the `foreach' for
+ *    every entry with the `user_context' context.
+ *
+ * NOTES
+ *
+ *    The hash table will not be rehashed during the traversing of the table,
+ *    even if the table was marked as auto rehashable.  The caller also must
+ *    not call silc_hash_table_rehash while traversing the table.
+ *
+ ***/
+void silc_hash_table_foreach(SilcHashTable ht, SilcHashForeach foreach,
+                            void *user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_rehash
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_rehash(SilcHashTable ht, uint32 new_size);
+ *
+ * DESCRIPTION
+ *
+ *    Rehashs the hash table. The size of the new hash table is provided
+ *    as `new_size'. If the `new_size' is zero then this routine will make
+ *    the new table of a suitable size. Note that this operation may be
+ *    very slow.
+ *
+ ***/
+void silc_hash_table_rehash(SilcHashTable ht, uint32 new_size);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_list
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl);
+ *
+ * DESCRIPTION
+ *
+ *    Prepares the `htl' SilcHashTableList sent as argument to be used in the
+ *    hash table traversing with the silc_hash_table_get.  After the hash
+ *    table traversing is completed the silc_hash_table_list_reset must be
+ *    called.
+ *
+ * NOTES
+ *
+ *    The hash table will not be rehashed during the traversing of the list,
+ *    even if the table was marked as auto rehashable.  The caller also must
+ *    not call silc_hash_table_rehash while traversing the list.
+ *
+ ***/
+void silc_hash_table_list(SilcHashTable ht, SilcHashTableList *htl);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_list_reset
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_list_reset(SilcHashTableList *htl);
+ *
+ * DESCRIPTION
+ *
+ *    Resets the `htl' SilcHashTableList.  This must be called after the
+ *    hash table traversing is completed.
+ *
+ ***/
+void silc_hash_table_list_reset(SilcHashTableList *htl);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_get
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_get(SilcHashTableList *htl, void **key, 
+ *                             void **context);
+ *
+ * DESCRIPTION
+ *
+ *    Returns always the next entry in the hash table into the `key' and
+ *    `context' and TRUE.  If this returns FALSE then there are no anymore
+ *    any entrys.
+ *
+ ***/
+bool silc_hash_table_get(SilcHashTableList *htl, void **key, void **context);
+
+
+/* Extended hash table interface (same as above but with specific
+   hash and comparison functions). */
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_add_ext
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
+ *                                 SilcHashFunction hash, 
+ *                                 void *hash_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Adds new entry to the hash table. The `key' is hashed using the
+ *    hash function and the both `key' and `context' will be saved to the
+ *    hash table. This function quarantees that the entry is always added
+ *    to the hash table reliably (it is collision resistant).
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *
+ ***/
+void silc_hash_table_add_ext(SilcHashTable ht, void *key, void *context,
+                            SilcHashFunction hash, void *hash_user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_replace_ext
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_replace_ext(SilcHashTable ht, void *key, 
+ *                                     void *context,
+ *                                     SilcHashFunction hash, 
+ *                                     void *hash_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_hash_table_add_ext but if the `key' already exists in the
+ *    hash table the old key and the old context will be replaced with the
+ *    `key' and the `context. The destructor function will be called for the
+ *    replaced key and context.
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *
+ ***/
+void silc_hash_table_replace_ext(SilcHashTable ht, void *key, void *context,
+                                SilcHashFunction hash, 
+                                void *hash_user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_del_ext
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
+ *                                 SilcHashFunction hash, 
+ *                                 void *hash_user_context,
+ *                                 SilcHashCompare compare, 
+ *                                 void *compare_user_context,
+ *                                 SilcHashDestructor destructor,
+ *                                 void *destructor_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Removes the entry from the hash table by the provided `key'. This will
+ *    call the destructor funtion for the found entry. Return TRUE if the
+ *    entry was removed successfully and FALSE otherwise.
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *    The `compare' and `compare_user_context' are application specified
+ *    comparing function. If not provided the hash table's default is used.
+ *    The `destructor' and `destructor_user_context' are application
+ *    specific destructor function.
+ *
+ ***/
+bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
+                            SilcHashFunction hash, 
+                            void *hash_user_context,
+                            SilcHashCompare compare, 
+                            void *compare_user_context,
+                            SilcHashDestructor destructor,
+                            void *destructor_user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_del_by_context_ext
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, 
+ *                                            void *context,
+ *                                            SilcHashFunction hash, 
+ *                                            void *hash_user_context,
+ *                                            SilcHashCompare compare, 
+ *                                            void *compare_user_context,
+ *                                            SilcHashDestructor destructor,
+ *                                            void *destructor_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Same as silc_hash_table_del but verifies that the context associated
+ *    with the `key' matches the `context'. This is handy to use with hash
+ *    tables that may have duplicate keys. In that case the `context' may
+ *    be used to check whether the correct entry is being deleted.
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *    The `compare' and `compare_user_context' are application specified
+ *    comparing function. If not provided the hash table's default is used.
+ *    The `destructor' and `destructor_user_context' are application
+ *    specific destructor function.
+ *
+ ***/
+bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key, 
+                                       void *context,
+                                       SilcHashFunction hash, 
+                                       void *hash_user_context,
+                                       SilcHashCompare compare, 
+                                       void *compare_user_context,
+                                       SilcHashDestructor destructor,
+                                       void *destructor_user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_find_ext
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
+ *                                  void **ret_key, void **ret_context,
+ *                                  SilcHashFunction hash, 
+ *                                  void *hash_user_context,
+ *                                  SilcHashCompare compare, 
+ *                                  void *compare_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Finds the entry in the hash table by the provided `key' as fast as
+ *    possible. Return TRUE if the entry was found and FALSE otherwise. 
+ *    The found entry is returned to the `ret_key' and `ret_context',
+ *    respectively. If the `ret_key and `ret_context' are NULL then this
+ *    maybe used only to check whether given key exists in the table.
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *    The `compare' and `compare_user_context' are application specified
+ *    comparing function. If not provided the hash table's default is used.
+ *
+ ***/
+bool silc_hash_table_find_ext(SilcHashTable ht, void *key,
+                             void **ret_key, void **ret_context,
+                             SilcHashFunction hash, 
+                             void *hash_user_context,
+                             SilcHashCompare compare, 
+                             void *compare_user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_find_foreach_ext
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
+ *                                          SilcHashFunction hash, 
+ *                                          void *hash_user_context,
+ *                                          SilcHashCompare compare, 
+ *                                          void *compare_user_context,
+ *                                          SilcHashForeach foreach, 
+ *                                          void *foreach_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    As the hash table is collision resistant it is possible to save duplicate
+ *    keys to the hash table. This function can be used to find all keys
+ *    and contexts from the hash table that are found using the `key'. The
+ *    `foreach' is called for every found key.
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *    The `compare' and `compare_user_context' are application specified
+ *    comparing function. If not provided the hash table's default is used.
+ *
+ * NOTES
+ *
+ *    The hash table will not be rehashed during the traversing of the table,
+ *    even if the table was marked as auto rehashable.  The caller also must
+ *    not call silc_hash_table_rehash while traversing the table.
+ *
+ ***/
+void silc_hash_table_find_foreach_ext(SilcHashTable ht, void *key,
+                                     SilcHashFunction hash, 
+                                     void *hash_user_context,
+                                     SilcHashCompare compare, 
+                                     void *compare_user_context,
+                                     SilcHashForeach foreach, 
+                                     void *foreach_user_context);
+
+/****f* silcutil/SilcHashTableAPI/silc_hash_table_rehash_ext
+ *
+ * SYNOPSIS
+ *
+ *    void silc_hash_table_rehash_ext(SilcHashTable ht, uint32 new_size,
+ *                                    SilcHashFunction hash, 
+ *                                    void *hash_user_context);
+ *
+ * DESCRIPTION
+ *
+ *    Rehashs the hash table. The size of the new hash table is provided
+ *    as `new_size'. If the `new_size' is zero then this routine will make
+ *    the new table of a suitable size. Note that this operation may be
+ *    very slow.
+ *
+ *    The `hash' and `hash_user_context' are application specified hash
+ *    function. If not provided the hash table's default is used.
+ *
+ ***/
+void silc_hash_table_rehash_ext(SilcHashTable ht, uint32 new_size,
+                               SilcHashFunction hash, 
+                               void *hash_user_context);
+
+#endif
diff --git a/lib/silcutil/silclist.h b/lib/silcutil/silclist.h
new file mode 100644 (file)
index 0000000..6f30934
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+
+  silclist.h
+
+  Author: Timo Sirainen <tss@iki.fi>
+
+  Copyright (C) 2002 Timo Sirainen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcList
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the SilcList interface.  This interface provides
+ * simple linked list.
+ *
+ ***/
+
+#ifndef SILCLIST_H
+#define SILCLIST_H
+
+/****s* silcutil/SilcList/SilcList
+ *
+ * NAME
+ * 
+ *    typedef struct { ... } SilcList;
+ *
+ * DESCRIPTION
+ *
+ *    This is the SilcList context, and is initialized by calling the
+ *    function silc_list_init.
+ *
+ * EXAMPLE
+ *
+ *     SilcList list;
+ *     silc_list_init(list, struct SilcInternalEntryStruct, next);
+ *
+ ***/
+typedef struct {
+  void *head, *tail;
+  void *current;
+  int offset;
+} SilcList;
+
+/****d* silcutil/SilcList/SILC_LIST_END
+ *
+ * NAME
+ * 
+ *    #define SILC_LIST_END ...
+ *
+ * DESCRIPTION
+ *
+ *    Functions return this when the list is invalid or when traversing
+ *    the list there is no more entires in the list.
+ *
+ * SOURCE
+ */
+#define SILC_LIST_END NULL
+/***/
+
+/* Initializes SilcList object. Example:
+
+   SilcList list;
+
+   silc_list_init(list, struct SilcInternalEntryStruct, next);
+
+   Where `list' is the defined list, and second argument is the structure
+   of the entries in the list and last argument is the pointer in the entry
+   structure that is used as list member. SilcInternalEntry might be as 
+   follows:
+
+   struct SilcInternalEntryStruct {
+     char *dummy;
+     struct SilcInternalEntryStruct *next; // The list member pointer
+   };
+
+   The `next' pointer in the structure (or some other pointer for that matter)
+   is given for the silc_list_init as the last argument. This pointer is used
+   by the list routines to link the entries together in the list. Your code
+   should not touch the member pointer manually.
+*/
+
+/****f* silcutil/SilcList/silc_list_init
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_init(list, type, field) ...
+ *
+ * DESCRIPTION
+ *
+ *    This macro initializes the SilcList list.  The `list' is the defined
+ *    list, second argument is the structure of the entries in the list,
+ *    and last argument is the pointer in the entry structure that is used
+ *    as list member.  When using SilcList, you should not touch the
+ *    structure member pointer (the `next' for example) manually.
+ *
+ * EXAMPLE
+ *
+ *    struct SilcInternalEntryStruct {
+ *      char *dummy;
+ *      struct SilcInternalEntryStruct *next; // The list member pointer
+ *    };
+ *
+ *    SilcList list;
+ *    silc_list_init(list, struct SilcInternalEntryStruct, next);
+ *
+ ***/
+#define silc_list_init(list, type, field) \
+  __silc_list_init(&(list), offsetof(type, field))
+
+static inline void __silc_list_init(SilcList *list, int offset)
+{
+  list->head = list->tail = list->current = SILC_LIST_END;
+  list->offset = offset;
+}
+
+#define list_next(list, pos) ((void **) ((char *) (pos) + (list)->offset))
+
+/****f* silcutil/SilcList/silc_list_count
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_count(list) ...
+ *
+ * DESCRIPTION
+ *
+ *    Returns the number of entries in the list indicated by `list'.
+ *
+ ***/
+#define silc_list_count(list) __silc_list_count(&(list))
+static inline int __silc_list_count(SilcList *list)
+{
+  int count = 0;
+  void *pos;
+
+  for (pos = list->head; pos != NULL; pos = *list_next(list, pos))
+    count++;
+
+  return count;
+}
+
+/****f* silcutil/SilcList/silc_list_start
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_start(list) ...
+ *
+ * DESCRIPTION
+ *
+ *    Sets the start of the list.  This prepares the list for traversing
+ *    the entries from the start of the list.
+ *
+ ***/
+#define silc_list_start(list) (list).current = (list).head;
+
+/****f* silcutil/SilcList/silc_list_add
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_add(list, entry) ...
+ *
+ * DESCRIPTION
+ *
+ *    Adds new entry indicated by `entry' to the end of the list indicated 
+ *    by `list'.
+ *
+ ***/
+#define silc_list_add(list, entry) __silc_list_add(&(list), entry)
+static inline void __silc_list_add(SilcList *list, void *data)
+{
+  if (list->head == NULL)
+    list->head = data;
+  else
+    *list_next(list, list->tail) = data;
+
+  list->tail = data;
+  *list_next(list, data) = NULL;
+}
+
+/****f* silcutil/SilcList/silc_list_del
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_del(list, entry) ...
+ *
+ * DESCRIPTION
+ *
+ *    Remove entry indicated by `entry' from the list indicated by `list'.
+ *
+ ***/
+#define silc_list_del(list, data) __silc_list_del(&(list), data)
+static inline void __silc_list_del(SilcList *list, void *data)
+{
+  void **pos, *prev;
+
+  prev = NULL;
+  for (pos = &list->head; *pos != NULL; pos = list_next(list, *pos)) {
+    if (*pos == data) {
+      *pos = *list_next(list, data);
+      if (list->current == data)
+        list->current = *pos;
+      break;
+    }
+
+    prev = *pos;
+  }
+
+  if (data == list->tail)
+    list->tail = prev;
+}
+
+/****f* silcutil/SilcList/silc_list_get
+ *
+ * SYNOPSIS
+ *
+ *    #define silc_list_get(list, entry) ...
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current entry from the list indicated by `list' and
+ *    moves the list pointer forward so that calling this next time will
+ *    return the next entry from the list.  This can be used to traverse
+ *    the list.  Returns SILC_LIST_END when the entire list has been
+ *    tarversed and no additional entries exist in the list. Later,
+ *    silc_list_start must be called again when re-starting the list
+ *    tarversing.
+ *
+ * EXAMPLE
+ *
+ *    // Traverse the list from the beginning to the end 
+ *    silc_list_start(list)
+ *    while ((entry = silc_list_get(list)) != SILC_LIST_END) {
+ *      ...
+ *    }
+ *
+ ***/
+#define silc_list_get(x) __silc_list_get(&(x))
+
+static inline
+void *__silc_list_get(SilcList *list)
+{
+  void *pos;
+
+  pos = list->current;
+  if (pos != NULL)
+    list->current = *list_next(list, pos);
+  return pos;
+}
+
+#endif
diff --git a/lib/silcutil/silclog.c b/lib/silcutil/silclog.c
new file mode 100644 (file)
index 0000000..6095910
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+
+  silclog.c
+
+  Author: Johnny Mnemonic <johnny@themnemonic.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Minimum time delay for log flushing calls (in seconds) */
+#define SILC_LOG_FLUSH_MIN_DELAY 2
+
+/* nice macro for looping through all logs -- makes the code more readable */
+#define SILC_FOREACH_LOG(__x__) for (__x__ = 0; __x__ < SILC_LOG_MAX; __x__++)
+
+/* Our working struct -- at the moment we keep it private, but this could
+ * change in the future */
+struct SilcLogStruct {
+  char *filename;
+  FILE *fp;
+  uint32 maxsize;
+  char *typename;
+  SilcLogType type;
+  SilcLogCb cb;
+  void *context;
+};
+typedef struct SilcLogStruct *SilcLog;
+
+/* These are the known logging channels */
+static struct SilcLogStruct silclogs[SILC_LOG_MAX] = {
+  {NULL, NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
+  {NULL, NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
+  {NULL, NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
+  {NULL, NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
+};
+
+/* If TRUE, log files will be flushed for each log input */
+bool silc_log_quick = FALSE;
+
+/* Set TRUE/FALSE to enable/disable debugging */
+bool silc_debug = FALSE;
+bool silc_debug_hexdump = FALSE;
+
+/* Flush delay */
+long silc_log_flushdelay = 300;
+
+/* Regular pattern matching expression for the debug output */
+char *silc_log_debug_string = NULL;
+
+/* Debug callbacks. If set these are used instead of default ones. */
+static SilcLogDebugCb silc_log_debug_cb = NULL;
+static void *silc_log_debug_context = NULL;
+static SilcLogHexdumpCb silc_log_hexdump_cb = NULL;
+static void *silc_log_hexdump_context = NULL;
+
+/* Did we register already our functions to the scheduler? */
+static bool silc_log_scheduled = FALSE;
+static bool silc_log_no_init = FALSE;
+
+/* This is only needed during starting up -- don't lose any logging */
+static bool silc_log_starting = TRUE;
+
+/* The type wrapper utility. Translates a SilcLogType id to the corresponding
+ * logfile, or NULL if not found. */
+static SilcLog silc_log_find_by_type(SilcLogType type)
+{
+  /* this is not really needed, but i think it's more secure */
+  switch (type) {
+    case SILC_LOG_INFO:
+      return &silclogs[SILC_LOG_INFO];
+    case SILC_LOG_WARNING:
+      return &silclogs[SILC_LOG_WARNING];
+    case SILC_LOG_ERROR:
+      return &silclogs[SILC_LOG_ERROR];
+    case SILC_LOG_FATAL:
+      return &silclogs[SILC_LOG_FATAL];
+    default:
+      return NULL;
+  }
+  return NULL;
+}
+
+/* Given an open log file, checks the size and rotates it if there is a
+ * max size set less then the current size */
+static void silc_log_checksize(SilcLog log)
+{
+  char newname[127];
+  long size;
+
+  if (!log || !log->fp || !log->maxsize)
+    return; /* we are not interested */
+  if ((size = ftell(log->fp)) < 0) {
+    /* OMG, EBADF is here.. we'll try our best.. */
+    FILE *oldfp = log->fp;
+    fclose(oldfp); /* we can discard the error */
+    log->fp = NULL; /* make sure we don't get here recursively */
+    SILC_LOG_ERROR(("Error while checking size of the log file %s, fp=%d",
+                   log->filename, oldfp));
+    return;
+  }
+  if (size < log->maxsize) return;
+
+  /* It's too big */
+  fprintf(log->fp, "[%s] [%s] Cycling log file, over max "
+         "logsize (%lu kilobytes)\n",
+         silc_get_time(), log->typename, log->maxsize / 1024);
+  fflush(log->fp);
+  fclose(log->fp);
+  snprintf(newname, sizeof(newname), "%s.old", log->filename);
+  unlink(newname);
+
+  /* I heard the following syscall may cause portability issues, but I don't
+   * have any other solution since SILC library doesn't provide any other
+   * function like this. -Johnny */
+  rename(log->filename, newname);
+  if (!(log->fp = fopen(log->filename, "w")))
+    SILC_LOG_WARNING(("Couldn't reopen logfile %s for type \"%s\": %s",
+                     log->filename, log->typename, strerror(errno)));
+}
+
+/* Reset a logging channel (close and reopen) */
+
+static bool silc_log_reset(SilcLog log)
+{
+  if (!log) return FALSE;
+  if (log->fp) {
+    fflush(log->fp);
+    fclose(log->fp);
+  }
+  if (!log->filename) return FALSE;
+  if (!(log->fp = fopen(log->filename, "a+"))) {
+    SILC_LOG_WARNING(("Couldn't reset logfile %s for type \"%s\": %s",
+       log->filename, log->typename, strerror(errno)));
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/* Internal timeout callback to flush log channels and check file sizes */
+
+SILC_TASK_CALLBACK(silc_log_fflush_callback)
+{
+  unsigned int u;
+  if (!silc_log_quick) {
+    silc_log_flush_all();
+    SILC_FOREACH_LOG(u)
+      silc_log_checksize(&silclogs[u]);
+  }
+  silc_log_starting = FALSE;
+  if (silc_log_flushdelay < SILC_LOG_FLUSH_MIN_DELAY)
+    silc_log_flushdelay = SILC_LOG_FLUSH_MIN_DELAY;
+  silc_schedule_task_add((SilcSchedule) context, 0, silc_log_fflush_callback,
+                        context, silc_log_flushdelay, 0, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
+}
+
+/* Outputs the log message to the first available channel. Channels are
+ * ordered by importance (see SilcLogType documentation).
+ * More importants channels can be printed on less important ones, but not
+ * vice-versa. */
+
+void silc_log_output(SilcLogType type, char *string)
+{
+  char *typename;
+  FILE *fp;
+  SilcLog log;
+
+  if ((type > SILC_LOG_MAX) || !(log = silc_log_find_by_type(type)))
+    goto end;
+
+  /* If there is a custom callback set, use it and return. */
+  if (log->cb) {
+    if ((*log->cb)(type, string, log->context))
+      goto end;
+  }
+
+  /* save the original typename, because if we redirect the channel we
+   * keep however the original destination channel name */
+  typename = log->typename;
+
+  if (!silc_log_scheduled) {
+    if (silc_log_no_init == FALSE) {
+      fprintf(stderr,
+             "Warning, trying to output without log files initialization, "
+             "log output is going to stderr\n");
+      silc_log_no_init = TRUE;
+    }
+    /* redirect output */
+    fp = stderr;
+    log = NULL;
+    goto found;
+  }
+
+  /* ok, now we have to find an open stream */
+  while (TRUE) {
+    if (log && (fp = log->fp)) goto found;
+    if (type == 0) break;
+    log = silc_log_find_by_type(--type);
+  }
+
+  /* Couldn't find any open stream.. sorry :( */
+  SILC_LOG_DEBUG(("Warning! couldn't find any available log channel!"));
+  goto end;
+
+ found:
+  fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), typename, string);
+  if (silc_log_quick || silc_log_starting) {
+    fflush(fp);
+    if (log)
+      silc_log_checksize(log);
+  }
+
+ end:
+  silc_free(string);
+}
+
+/* returns an internally allocated pointer to a logging channel file */
+char *silc_log_get_file(SilcLogType type)
+{
+  SilcLog log;
+
+  if (!(log = silc_log_find_by_type(type)))
+    return NULL;
+  if (log->fp)
+    return log->filename;
+  return NULL;
+}
+
+/* Set and initialize the specified logging channel. See the API reference */
+bool silc_log_set_file(SilcLogType type, char *filename, uint32 maxsize,
+                      SilcSchedule scheduler)
+{
+  FILE *fp = NULL;
+  SilcLog log;
+
+  log = silc_log_find_by_type(type);
+  if (!log || !scheduler)
+    return FALSE;
+
+  SILC_LOG_DEBUG(("Setting \"%s\" file to %s (max size=%d)",
+                 log->typename, filename, maxsize));
+
+  /* before assuming the new file, make sure we can open it */
+  if (filename) {
+    if (!(fp = fopen(filename, "a+"))) {
+      fprintf(stderr, "warning: couldn't open log file %s: %s\n",
+             filename, strerror(errno));
+      return FALSE;
+    }
+  }
+
+  /* remove old file */
+  if (log->filename) {
+    if (log->fp) {
+      fflush(fp);
+      fclose(fp);
+    }
+    silc_free(log->filename);
+    log->filename = NULL;
+    log->fp = NULL;
+  }
+
+  if (fp) {
+    log->filename = strdup(filename);
+    log->fp = fp;
+    log->maxsize = maxsize;
+  }
+
+  if (silc_log_scheduled)
+    return TRUE;
+
+  /* add schedule hook with a short delay to make sure we'll use right delay */
+  silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback,
+                        (void *) scheduler, 10, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  silc_log_scheduled = TRUE;
+
+  return TRUE;
+}
+
+/* Sets a log callback, set callback to NULL to return to default behaviour */
+
+void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
+{
+  SilcLog log;
+
+  if (!(log = silc_log_find_by_type(type)))
+    return;
+
+  log->cb = cb;
+  log->context = context;
+}
+
+/* Resets log callbacks */
+
+void silc_log_reset_callbacks()
+{
+  unsigned int u;
+  SILC_FOREACH_LOG(u) {
+    silclogs[u].cb = NULL;
+    silclogs[u].context = NULL;
+  }
+}
+
+/* Flushes all opened logging channels */
+
+void silc_log_flush_all() {
+  unsigned int u;
+  SILC_LOG_DEBUG(("Flushing all logs"));
+  SILC_FOREACH_LOG(u) {
+    if (silclogs[u].fp)
+      fflush(silclogs[u].fp);
+  }
+}
+
+/* Resets all known logging channels */
+
+void silc_log_reset_all() {
+  unsigned int u;
+  SILC_LOG_DEBUG(("Resetting all logs"));
+  SILC_FOREACH_LOG(u)
+    silc_log_reset(&silclogs[u]);
+}
+
+/* Outputs the debug message to stderr. */
+
+void silc_log_output_debug(char *file, char *function,
+                          int line, char *string)
+{
+  if (!silc_debug)
+    goto end;
+
+  if (silc_log_debug_string &&
+      !silc_string_regex_match(silc_log_debug_string, file) &&
+      !silc_string_regex_match(silc_log_debug_string, function))
+    goto end;
+
+  if (silc_log_debug_cb) {
+    if ((*silc_log_debug_cb)(file, function, line, string,
+                            silc_log_debug_context))
+      goto end;
+  }
+
+  fprintf(stderr, "%s:%d: %s\n", function, line, string);
+  fflush(stderr);
+
+ end:
+  silc_free(string);
+}
+
+/* Hexdumps a message */
+
+void silc_log_output_hexdump(char *file, char *function,
+                            int line, void *data_in,
+                            uint32 len, char *string)
+{
+  int i, k;
+  int off, pos, count;
+  unsigned char *data = (unsigned char *)data_in;
+
+  if (!silc_debug_hexdump)
+    goto end;
+
+  if (silc_log_debug_string &&
+      !silc_string_regex_match(silc_log_debug_string, file) &&
+      !silc_string_regex_match(silc_log_debug_string, function))
+    goto end;
+
+  if (silc_log_hexdump_cb) {
+    if ((*silc_log_hexdump_cb)(file, function, line, data_in, len, string,
+                              silc_log_hexdump_context))
+      goto end;
+  }
+
+  fprintf(stderr, "%s:%d: %s\n", function, line, string);
+
+  k = 0;
+  pos = 0;
+  count = 16;
+  off = len % 16;
+  while (1) {
+    if (off) {
+      if ((len - pos) < 16 && (len - pos <= len - off))
+       count = off;
+    } else {
+      if (pos == len)
+       count = 0;
+    }
+    if (off == len)
+      count = len;
+
+    if (count)
+      fprintf(stderr, "%08X  ", k++ * 16);
+
+    for (i = 0; i < count; i++) {
+      fprintf(stderr, "%02X ", data[pos + i]);
+
+      if ((i + 1) % 4 == 0)
+       fprintf(stderr, " ");
+    }
+
+    if (count && count < 16) {
+      int j;
+
+      for (j = 0; j < 16 - count; j++) {
+       fprintf(stderr, "   ");
+
+       if ((j + count + 1) % 4 == 0)
+         fprintf(stderr, " ");
+      }
+    }
+
+    for (i = 0; i < count; i++) {
+      char ch;
+
+      if (data[pos] < 32 || data[pos] >= 127)
+       ch = '.';
+      else
+       ch = data[pos];
+
+      fprintf(stderr, "%c", ch);
+      pos++;
+    }
+
+    if (count)
+      fprintf(stderr, "\n");
+
+    if (count < 16)
+      break;
+  }
+
+ end:
+  silc_free(string);
+}
+
+/* Sets debug callbacks */
+
+void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
+                                 void *debug_context,
+                                 SilcLogHexdumpCb hexdump_cb,
+                                 void *hexdump_context)
+{
+  silc_log_debug_cb = debug_cb;
+  silc_log_debug_context = debug_context;
+  silc_log_hexdump_cb = hexdump_cb;
+  silc_log_hexdump_context = hexdump_context;
+}
+
+/* Resets debug callbacks */
+
+void silc_log_reset_debug_callbacks()
+{
+  silc_log_debug_cb = NULL;
+  silc_log_debug_context = NULL;
+  silc_log_hexdump_cb = NULL;
+  silc_log_hexdump_context = NULL;
+}
+
+/* Set current debug string */
+
+void silc_log_set_debug_string(const char *debug_string)
+{
+  silc_free(silc_log_debug_string);
+  if ((strchr(debug_string, '(') &&
+       strchr(debug_string, ')')) ||
+       strchr(debug_string, '$'))
+    silc_log_debug_string = strdup(debug_string);
+  else
+    silc_log_debug_string = silc_string_regexify(debug_string);
+}
diff --git a/lib/silcutil/silclog.h b/lib/silcutil/silclog.h
new file mode 100644 (file)
index 0000000..46db324
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+
+  silclog.h
+
+  Author: Johnny Mnemonic <johnny@themnemonic.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcLogAPI
+ *
+ * DESCRIPTION
+ *
+ * The SILC logging APIs provide a powerful and easy-to-use interface to
+ * the logging system and debugging output.
+ *
+ ***/
+
+#ifndef SILCLOG_H
+#define SILCLOG_H
+
+/****d* silcutil/SilcLogAPI/SilcLogType
+ *
+ * NAME
+ *
+ *    typedef enum { ... } SilcLogType;
+ *
+ * DESCRIPTION
+ *
+ *    This is the main logging channel id. There are currently four known
+ *    logging channels (plus the debugging output channel), and they are
+ *    ordered by importance.
+ *    See the source code for SILC coding conventions about how to choose
+ *    the right output channel.
+ *
+ * SOURCE
+ */
+typedef enum {
+  /* Generic info channel file */
+  SILC_LOG_INFO,
+
+  /* This should be used for warnings and non critical failures */
+  SILC_LOG_WARNING,
+
+  /* Generic error and critical failures messages */
+  SILC_LOG_ERROR,
+
+  /* Fatal messages (usually situations that will lead to a program crash */
+  SILC_LOG_FATAL,
+
+  /* Total number logging channels */
+  SILC_LOG_MAX
+} SilcLogType;
+/***/
+
+/****f* silcutil/SilcLogAPI/SilcLogCb
+ *
+ * SYNOPSIS
+ *
+ *    typedef bool (*SilcLogCb)(SilcLogType type, char *message,
+ *                              void *context);
+ *
+ * DESCRIPTION
+ *
+ *    The logging custom callback function.  The `type' is the channel ID
+ *    that triggered the event, which allows you to use the same callback
+ *    function for multiple logging channels.
+ *    The `message' parameter points to a null-terminated buffer containing
+ *    the received message, while `context' is the caller-specified context.
+ *    The message must not be modified or freed by the callback function.
+ *
+ * SEE ALSO
+ *    silc_log_set_callback
+ *
+ ***/
+typedef bool (*SilcLogCb)(SilcLogType type, char *message, void *context);
+
+/****f* silcutil/SilcLogAPI/SilcLogDebugCb
+ *
+ * SYNOPSIS
+ *
+ *    typedef bool (*SilcLogDebugCb)(char *file, char *function, int line,
+ *                                   char *message, void *context);
+ *
+ * DESCRIPTION
+ *
+ *    The debug logging callback function.  The default behaviour is to
+ *    output messages to stderr.  `file', `function', and `line' are the
+ *    corresponding offsets in the source files.  `message' points to a
+ *    null-terminated buffer containing the debugging message, and `context'
+ *    is the caller-specified context.
+ *    The message must not be modified or freed by the callback function.
+ *    If the function returns TRUE, SilcLog will assume the message as handled
+ *    and won't run its default handler.
+ *
+ * SEE ALSO
+ *    silc_debug, silc_log_set_debug_callbacks
+ *
+ ***/
+typedef bool (*SilcLogDebugCb)(char *file, char *function, int line,
+                              char *message, void *context);
+
+/****f* silcutil/SilcLogAPI/SilcLogHexdumpCb
+ *
+ * SYNOPSIS
+ *
+ *    typedef bool (*SilcDebugHexdumpCb)(char *file, char *function, int line,
+ *                                       unsigned char *data, uint32 data_len,
+ *                                       char *message, void *context;
+ *
+ * DESCRIPTION
+ *
+ *    The hexdump logging callback function.  The default behaviour is to
+ *    print a formatted hexdump to stderr, and is commonly what you would
+ *    like it to be.  `file', `function', and `line' are the corresponding
+ *    offsets in the source files.  `data' is the begin of the buffer that
+ *    should be hexdumped, which is `data_len' bytes long.
+ *    The `message' parameter points to a null-terminated buffer containing
+ *    the received message, while `context' is the caller-specified context.
+ *    The message must not be modified or freed by the callback function.
+ *    If the function returns TRUE, SilcLog will assume the message as handled
+ *    and won't run its default handler.
+ *
+ * SEE ALSO
+ *    silc_debug_hexdump, silc_log_set_debug_callbacks
+ *
+ ***/
+typedef bool (*SilcLogHexdumpCb)(char *file, char *function, int line,
+                                unsigned char *data, uint32 data_len,
+                                char *message, void *context);
+
+/* Global Variables */
+
+/****v* silcutil/SilcLogAPI/silc_log_quick
+ *
+ * NAME
+ *
+ *    bool silc_log_quick -- enable/disable fast logging output
+ *
+ * DESCRIPTION
+ *
+ *    SilcLog makes use of libc stream buffering output, which means that it
+ *    saves HD activity by buffering the logging messages and writing them
+ *    all together every some minutes (default is 5 minutes).
+ *    Setting this variable to TRUE will force SilcLog to write messages to the
+ *    filesystem as soon as they are received. This increases the CPU activity
+ *    notably on bigger servers, but reduces memory usage.
+ *    If you want to change the logging style on-the-fly, make sure to call
+ *    silc_log_flush_all() after setting this variable to TRUE.
+ *
+ ***/
+extern DLLAPI bool silc_log_quick;
+
+/****v* silcutil/SilcLogAPI/silc_log_flushdelay
+ *
+ * NAME
+ *
+ *    long silc_log_flushdelay -- flushing time delay
+ *
+ * DESCRIPTION
+ *
+ *    Sets the logfiles flushing delay in seconds.  As for now, changing this
+ *    value AFTER logfiles initialization won't take effect until previous
+ *    delay time will expire; for example if you change from 300 seconds to
+ *    60 seconds you will have to wait up to 300 seconds for this change to
+ *    take effect.
+ *    This value must be greater than 2 seconds.
+ *
+ ***/
+extern DLLAPI long silc_log_flushdelay;
+
+/****v* silcutil/SilcLogAPI/silc_debug
+ *
+ * NAME
+ *
+ *    bool silc_debug -- enable/disable debugging output
+ *
+ * DESCRIPTION
+ *
+ *    If silc_debug is set to FALSE, debugging functions won't procude any
+ *    output.  This is useful when for example you compile in the debugging
+ *    support but at a certain point you want to send the program in the
+ *    background.
+ *
+ * SEE ALSO
+ *    SILC_LOG_DEBUG
+ *
+ ***/
+extern DLLAPI bool silc_debug;
+
+/****v* silcutil/SilcLogAPI/silc_debug_hexdump
+ *
+ * NAME
+ *
+ *    bool silc_debug_hexdump -- enable/disable debugging output
+ *
+ * DESCRIPTION
+ *
+ *    If silc_debug_hexdump is set to FALSE, debugging functions won't produce
+ *    any output.  This is useful when for example you compile in the debugging
+ *    support but at a certain point you want to send the program in the
+ *    background.
+ *
+ * SEE ALSO
+ *    SILC_LOG_HEXDUMP
+ *
+ ***/
+extern DLLAPI bool silc_debug_hexdump;
+
+/* Macros */
+
+#ifdef WIN32
+#define __FUNCTION__ ""
+#endif
+
+/****d* silcutil/SilcLogAPI/SILC_LOG_INFO
+ *
+ * NAME
+ *
+ *    #define SILC_LOG_INFO(...)
+ *
+ * DESCRIPTION
+ *
+ *    This macro is a wrapper to the main logging function.
+ *    It supports variable argument list formatting, and *automatically*
+ *    appends newline at the end of the string.
+ *
+ * NOTES
+ *
+ *    This macro requires double parenthesis to ensure that the VA list
+ *    formatting would work correctly.
+ *
+ * EXAMPLE
+ *
+ *    SILC_LOG_INFO(("Today i feel %s", core->mood));
+ *
+ * SOURCE
+ */
+#define SILC_LOG_INFO(fmt) silc_log_output(SILC_LOG_INFO, silc_format fmt)
+/***/
+
+/****d* silcutil/SilcLogAPI/SILC_LOG_WARNING
+ *
+ * NAME
+ *
+ *    #define SILC_LOG_WARNING(...)
+ *
+ * DESCRIPTION
+ *
+ *    Wrapper to the WARNING logging channel.
+ *    Please see the SILC_LOG_INFO macro.
+ *
+ * SEE ALSO
+ *    SILC_LOG_INFO
+ *
+ * SOURCE
+ */
+#define SILC_LOG_WARNING(fmt) silc_log_output(SILC_LOG_WARNING, silc_format fmt)
+/***/
+
+/****d* silcutil/SilcLogAPI/SILC_LOG_ERROR
+ *
+ * NAME
+ *
+ *    #define SILC_LOG_ERROR(...)
+ *
+ * DESCRIPTION
+ *
+ *    Wrapper to the ERROR logging channel.
+ *    Please see the SILC_LOG_INFO macro.
+ *
+ * SEE ALSO
+ *    SILC_LOG_INFO
+ *
+ * SOURCE
+ */
+#define SILC_LOG_ERROR(fmt) silc_log_output(SILC_LOG_ERROR, silc_format fmt)
+/***/
+
+/****d* silcutil/SilcLogAPI/SILC_LOG_FATAL
+ *
+ * NAME
+ *
+ *    #define SILC_LOG_FATAL(...)
+ *
+ * DESCRIPTION
+ *
+ *    Wrapper to the FATAL logging channel.
+ *    Please see the SILC_LOG_INFO macro.
+ *
+ * SEE ALSO
+ *    SILC_LOG_INFO
+ *
+ * SOURCE
+ */
+#define SILC_LOG_FATAL(fmt) silc_log_output(SILC_LOG_FATAL, silc_format fmt)
+/***/
+
+/****d* silcutil/SilcLogAPI/SILC_LOG_DEBUG
+ *
+ * NAME
+ *
+ *    #define SILC_LOG_DEBUG(...)
+ *
+ * DESCRIPTION
+ *
+ *    This is a special wrapper to the debugging output (usually stderr).
+ *    The standard behaviour is the same as SILC_LOG_INFO, with the difference
+ *    that this macro also depends on the global define SILC_DEBUG.
+ *    Undefining SILC_DEBUG causes these functions to be defined to an empty
+ *    value, thus removing all debug logging calls from the compiled
+ *    application.
+ *    This macro is also affected by the global variable silc_debug.
+ *
+ * SOURCE
+ */
+#ifdef SILC_DEBUG
+#define SILC_LOG_DEBUG(fmt) silc_log_output_debug(__FILE__, \
+                               __FUNCTION__, \
+                               __LINE__, \
+                               silc_format fmt)
+#else
+#define SILC_LOG_DEBUG(fmt)
+#endif /* SILC_DEBUG */
+/***/
+
+/****d* silcutil/SilcLogAPI/SILC_LOG_HEXDUMP
+ *
+ * NAME
+ *
+ *    #define SILC_LOG_HEXDUMP(...)
+ *
+ * DESCRIPTION
+ *
+ *    This is a special wrapper to the hexdump output function.  This macro
+ *    behaves slightly differently from other logging wrappers.
+ *    The first parameter, is composed by a group of parameters delimited by
+ *    parenthesis.
+ *    The second parameter is a `char *' pointer pointing to the beginning
+ *    of the memory section that should be hexdumped, and the third parameter
+ *    is the length of this memory section.
+ *    Undefining the global SILC_DEBUG define causes these functions to be
+ *    defined to an empty value, thus removing all debug logging calls from
+ *    the compiled application.
+ *    This macro is also affected by the global variable silc_debug_hexdump.
+ *
+ * EXAMPLE
+ *
+ *    SILC_LOG_HEXDUMP(("Outgoing packet [%d], len %d", pckt->seq, pckt->len),
+ *                     pckt->data, pckt->datalen);
+ *
+ * SOURCE
+ */
+#ifdef SILC_DEBUG
+#define SILC_LOG_HEXDUMP(fmt, data, len) silc_log_output_hexdump(__FILE__, \
+                               __FUNCTION__, \
+                               __LINE__, \
+                               (data), (len), \
+                               silc_format fmt)
+#else
+#define SILC_LOG_HEXDUMP(fmt, data, len)
+#endif /* SILC_DEBUG */
+/***/
+
+/* Prototypes */
+
+/****f* silcutil/SilcLogAPI/silc_log_output
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_output(SilcLogType type, char *string);
+ *
+ * DESCRIPTION
+ *
+ *    This is the main function for logging output. Please note that you
+ *    should rather use one of the logging wrapper macros.
+ *    If you really want to use this function, its usage is quite simple.
+ *    The `type' parameter identifies the channel to use, while the `string'
+ *    parameter must be a dynamic allocated (null-terminated) buffer, because
+ *    it will be freed at the end of this function, for internal reasons.
+ *    If there are registered callbacks for the specified type, this function
+ *    will first trigger those callbacks.  The callback functions must NOT
+ *    free or modify the original buffer.
+ *
+ * SEE ALSO
+ *    SILC_LOG_INFO, SILC_LOG_WARNING, SILC_LOG_ERROR, SILC_LOG_FATAL
+ *
+ ***/
+void silc_log_output(SilcLogType type, char *string);
+
+/****f* silcutil/SilcLogAPI/silc_log_get_file
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_log_get_file(SilcLogType type);
+ *
+ * DESCRIPTION
+ *
+ *    Returns the current logging file for the channel `type'.
+ *    If there has been an error during the opening of this channel, NULL
+ *    is returned, even if the file has been previously set with
+ *    silc_log_set_file().
+ *    The returned pointer points to internally allocated storage and must
+ *    not be freed, modified or stored.
+ *
+ ***/
+char *silc_log_get_file(SilcLogType type);
+
+/****f* silcutil/SilcLogAPI/silc_log_set_file
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_log_set_file(SilcLogType type, char *filename, uint32 maxsize,
+ *                           SilcSchedule scheduler);
+ *
+ * DESCRIPTION
+ *
+ *    Sets `filename', which can be maximum `maxsize' bytes long, as the new
+ *    logging file for the channel `type'.  If you specify an illegal filename
+ *    a warning message is printed and FALSE is returned.  In this case
+ *    logging settings are not changed.
+ *    You can disable logging for a channel by specifying NULL filename, the
+ *    maxsize in this case is not important.
+ *    The `scheduler' parameter is needed by the internal logging to allow
+ *    buffered output and thus to save HD activity.
+ *
+ ***/
+bool silc_log_set_file(SilcLogType type, char *filename, uint32 maxsize,
+                      SilcSchedule scheduler);
+
+/****f* silcutil/SilcLogAPI/silc_log_set_callback
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_set_callback(SilcLogType type, SilcLogCb cb,
+ *                               void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Set `cb' as the default callback function for the logging channel
+ *    `type'.  When SilcLog receives a message for this channel, it will
+ *    trigger the callback function.  If the callback function returns TRUE
+ *    SilcLog will assume the input as handled and won't run its default
+ *    handler.
+ *    You can disable/remove a callback by setting it to NULL or calling the
+ *    function silc_log_reset_callbacks.
+ *    If set, the callback function must be in the form described by
+ *    SilcLogCb.
+ *
+ * SEE ALSO
+ *    silc_log_reset_callbacks
+ *
+ ***/
+void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context);
+
+/****f* silcutil/SilcLogAPI/silc_log_reset_callbacks
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_reset_callbacks();
+ *
+ * DESCRIPTION
+ *
+ *    Removes all logging callbacks for normal channels.  This function does
+ *    NOT remove callbacks for debugging channels (debug and hexdump), you
+ *    rather need to call silc_log_set_debug_callbacks() with NULL callbacks.
+ *
+ ***/
+void silc_log_reset_callbacks();
+
+/****f* silcutil/SilcLogAPI/silc_log_flush_all
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_flush_all();
+ *
+ * DESCRIPTION
+ *
+ *    Forces flushing for all logging channels.  This should be called for
+ *    example after receiving special signals.
+ *
+ * SEE ALSO
+ *    silc_log_quick
+ *
+ ***/
+void silc_log_flush_all();
+
+/****f* silcutil/SilcLogAPI/silc_log_reset_all
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_reset_all();
+ *
+ * DESCRIPTION
+ *
+ *    Forces all logging channels to close and reopen their streams.  Useful
+ *    for example after a SIGHUP signal.
+ *    Please note that this function could cause some warning messages if
+ *    some logging channel points to an illegal filename.
+ *
+ ***/
+void silc_log_reset_all();
+
+/****f* silcutil/SilcLogAPI/silc_log_output_debug
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_output_debug(char *file, char *function,
+ *                               int line, char *string);
+ *
+ * DESCRIPTION
+ *
+ *    This is the main function for debug output.  Please note that you should
+ *    rather use the wrapper macro SILC_LOG_DEBUG.
+ *    If you want to use it anyway, the `file', `function', and `line' are the
+ *    corresponding offsets in the source files, while `string' must be a
+ *    dynamic allocated (null-terminated) buffer.
+ *
+ ***/
+void silc_log_output_debug(char *file, char *function,
+                          int line, char *string);
+
+/****f* silcutil/SilcLogAPI/silc_log_output_hexdump
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_output_hexdump(char *file, char *function,
+ *                                 int line, void *data_in,
+ *                                 uint32 len, char *string);
+ *
+ * DESCRIPTION
+ *
+ *    This is the main function for hexdump output.  Please note that you
+ *    should rather use the wrapper macro SILC_LOG_HEXDUMP.
+ *    If you want to use it anyway, the `file', `function', and `line' are the
+ *    corresponding offsets in the source files, `data_in' is the beginning
+ *    of the buffer you wish to hexdump, which is `len' bytes long.
+ *    `string' must be a dynamic allocated (null-terminated) buffer.
+ *
+ ***/
+void silc_log_output_hexdump(char *file, char *function,
+                            int line, void *data_in,
+                            uint32 len, char *string);
+
+/****f* silcutil/SilcLogAPI/silc_log_set_debug_callbacks
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
+ *                                      void *debug_context,
+ *                                      SilcLogHexdumpCb hexdump_cb,
+ *                                      void *hexdump_context);
+ *
+ * DESCRIPTION
+ *
+ *    Sets `debug_cb' as the the default callback function for the debug
+ *    output, that will be called with the `debug_context' parameter.
+ *    When SilcLog receives a debug message, it will trigger the callback
+ *    function.  If the callback function returns TRUE SilcLog will assume
+ *    the input as handled and won't run its default handler.
+ *    `hexdump_cb' and `hexdump_context' works the same way, except that they
+ *    are referred to SILC_LOG_HEXDUMP requests.
+ *    You can disable/remove a callback by setting it to NULL.
+ *    If set, each callback function must be either in the form described by
+ *    SilcLogDebugCb or SilcLogHexdumpCb.
+ *
+ * SEE ALSO
+ *    SilcLogDebugCb,  SilcLogHexdumpCb
+ *
+ ***/
+void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
+                                 void *debug_context,
+                                 SilcLogHexdumpCb hexdump_cb,
+                                 void *hexdump_context);
+
+/****f* silcutil/SilcLogAPI/silc_log_set_debug_string
+ *
+ * SYNOPSIS
+ *
+ *    void silc_log_set_debug_string(const char *debug_string);
+ *
+ * DESCRIPTION
+ *
+ *    Sets `debug_string' as the regexp string for filtering debugging
+ *    output.  The string is copied and it can be modified/destroyed after
+ *    this function call.
+ *
+ ***/
+void silc_log_set_debug_string(const char *debug_string);
+
+#endif /* !SILCLOG_H */
similarity index 64%
rename from lib/silccore/silcmemory.c
rename to lib/silcutil/silcmemory.c
index 03d981e27b209b21091267b9c163b95b499ce545..2df6fee495f03ea02a595f6942c3f63f2603b6e8 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:55  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 
 void *silc_malloc(size_t size)
 {
-#ifdef HAVE_MLOCK
-  void *addr = malloc(size);
-  mlock(addr, size);
+  void *addr;
+  addr = malloc(size);
+  assert(addr != NULL);
   return addr;
-#else
-  return malloc(size);
-#endif
 }
 
 void *silc_calloc(size_t items, size_t size)
 {
-#ifdef HAVE_MLOCK
-  void *addr = calloc(items, size);
-  mlock(addr, size);
+  void *addr;
+  addr = calloc(items, size);
+  assert(addr != NULL);
   return addr;
-#else
-  return calloc(items, size);
-#endif
 }
 
 void *silc_realloc(void *ptr, size_t size)
 {
-#ifdef HAVE_MLOCK
-  void *addr = realloc(ptr, size);
-  mlock(addr, size);
+  void *addr;
+  addr = realloc(ptr, size);
+  assert(addr != NULL);
   return addr;
-#else
-  return realloc(ptr, size);
-#endif
 }
 
 void silc_free(void *ptr)
 {
   free(ptr);
 }
-
-
-
-
-
-
-
similarity index 99%
rename from lib/silccore/silcmemory.h
rename to lib/silcutil/silcmemory.h
index cafba4f74ca66245d8a9e236a8f20cb72748b383..100f511d39e02fa4fb20200704487d95bda78323 100644 (file)
@@ -28,9 +28,3 @@ void *silc_realloc(void *ptr, size_t size);
 void silc_free(void *ptr);
 
 #endif
-
-
-
-
-
-
diff --git a/lib/silcutil/silcmutex.h b/lib/silcutil/silcmutex.h
new file mode 100644 (file)
index 0000000..076b667
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+
+  silcmutex.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcMutexAPI
+ *
+ * DESCRIPTION
+ *
+ * Interface for the SILC Mutex locking implementation. This is platform
+ * independent mutual exclusion interface for applications that need 
+ * concurrency control.
+ *
+ ***/
+
+#ifndef SILCMUTEX_H
+#define SILCMUTEX_H
+
+#ifdef SILC_THREADS
+
+/****s* silcutil/SilcMutexAPI/SilcMutex
+ *
+ * NAME
+ * 
+ *    typedef struct SilcMutexStruct *SilcMutex;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual SILC Mutex and is allocated
+ *    by silc_mutex_alloc and given as argument to all silc_mutex_*
+ *    functions.  It is freed by the silc_mutex_free function.
+ *
+ ***/
+typedef struct SilcMutexStruct *SilcMutex;
+
+/****d* silcutil/SilcMutexAPI/SILC_MUTEX_DEFINE
+ *
+ * NAME
+ * 
+ *    #define SILC_MUTEX_DEFINE(name) ...
+ *
+ * DESCRIPTION
+ *
+ *    This macro is used to define new mutex.  Use this macro in an
+ *    environment that can be compiled with or without the SILC Mutex
+ *    API. This is equivalent to defining SilcMutex `name'; directly.
+ *
+ * SOURCE
+ */
+#define SILC_MUTEX_DEFINE(name) SilcMutex name
+/***/
+
+/****f* silcutil/SilcMutexAPI/silc_mutex_alloc
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_mutex_alloc(SilcMutex *mutex);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates SILC Mutex object.  The mutex object must be allocated
+ *    before it can be used.  It is freed by the silc_mutex_free function.
+ *    This returns TRUE and allocated mutex in to the `mutex' and FALSE
+ *    on error.
+ *
+ ***/
+bool silc_mutex_alloc(SilcMutex *mutex);
+
+/****f* silcutil/SilcMutexAPI/silc_mutex_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mutex_free(SilcMutex mutex);
+ *
+ * DESCRIPTION
+ *
+ *    Free SILC Mutex object and frees all allocated memory.
+ *
+ ***/
+void silc_mutex_free(SilcMutex mutex);
+
+/****f* silcutil/SilcMutexAPI/silc_mutex_lock
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mutex_lock(SilcMutex mutex);
+ *
+ * DESCRIPTION
+ *
+ *    Locks the mutex. If the mutex is locked by another thread the
+ *    current thread will block until the other thread has issued
+ *    silc_mutex_unlock for the mutex.
+ *
+ * NOTES
+ *
+ *    The caller must not call silc_mutex_lock for mutex that has been
+ *    already locked in the current thread.  In this case deadlock will
+ *    occur.
+ *
+ ***/
+void silc_mutex_lock(SilcMutex mutex);
+
+/****f* silcutil/SilcMutexAPI/silc_mutex_unlock
+ *
+ * SYNOPSIS
+ *
+ *    void silc_mutex_unlock(SilcMutex mutex);
+ *
+ * DESCRIPTION
+ *
+ *    Unlocks the mutex and thus releases it for another thread that
+ *    may be waiting for the lock.
+ *
+ * NOTES
+ *
+ *    The caller must not call the silc_mutex_unlock for an unlocked
+ *    mutex or mutex not locked by the current thread.  It is fatal
+ *    error if this occurs.
+ *
+ ***/
+void silc_mutex_unlock(SilcMutex mutex);
+
+#else
+
+#define SILC_MUTEX_DEFINE(name)
+#define silc_mutex_alloc(mutex) (void)0
+#define silc_mutex_free(mutex) (void)0
+#define silc_mutex_lock(mutex) (void)0
+#define silc_mutex_unlock(mutex) (void)0
+
+#endif         /* SILC_THREADS */
+
+#endif
diff --git a/lib/silcutil/silcnet.c b/lib/silcutil/silcnet.c
new file mode 100644 (file)
index 0000000..d85317b
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+
+  silcnet.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcnet.h"
+
+/* Accepts a connection from a particular socket */
+
+int silc_net_accept_connection(int sock)
+{
+  return accept(sock, 0, 0);
+}
+
+/* Sets a option for a socket. */
+
+int silc_net_set_socket_opt(int sock, int level, int option, int on)
+{
+  return setsockopt(sock, level, option, (void *)&on, sizeof(on));
+}
+
+/* Get socket options */
+
+int silc_net_get_socket_opt(int sock, int level, int option, 
+                           void *optval, int *opt_len)
+{
+  return getsockopt(sock, level, option, optval, opt_len);
+}
+
+/* Checks whether IP address sent as argument is valid IPv4 address. */
+
+bool silc_net_is_ip4(const char *addr)
+{
+  int count = 0;
+
+  while (*addr) {
+    if (*addr != '.' && !isdigit(*addr))
+      return FALSE;
+    if (*addr == '.')
+      count++;
+    addr++;
+  }
+
+  if (count != 3)
+    return FALSE;
+  
+  return TRUE;
+}
+
+/* Checks whether IP address sent as argument is valid IPv6 address. */
+
+bool silc_net_is_ip6(const char *addr)
+{
+  /* XXX does this work with all kinds of IPv6 addresses? */
+  while (*addr) {
+    if (*addr != ':' && !isxdigit(*addr))
+      return FALSE;
+    addr++;
+  }
+  
+  return TRUE;
+}
+
+/* Checks whether IP address sent as argument is valid IP address. */
+
+bool silc_net_is_ip(const char *addr)
+{
+  if (silc_net_is_ip4(addr))
+    return TRUE;
+  return silc_net_is_ip6(addr);
+}
+
+/* Internal context for async resolving */
+typedef struct {
+  SilcNetResolveCallback completion;
+  void *context;
+  SilcSchedule schedule;
+  char *input;
+  char *result;
+} *SilcNetResolveContext;
+
+SILC_TASK_CALLBACK(silc_net_resolve_completion)
+{
+  SilcNetResolveContext r = (SilcNetResolveContext)context;
+
+  /* Call the completion callback */
+  if (r->completion)
+    (*r->completion)(r->result, r->context);
+
+  silc_free(r->input);
+  silc_free(r->result);
+  silc_free(r);
+}
+
+/* Thread function to resolve the address for hostname. */
+
+static void *silc_net_gethostbyname_thread(void *context)
+{
+  SilcNetResolveContext r = (SilcNetResolveContext)context;
+  char tmp[64];
+
+  if (silc_net_gethostbyname(r->input, tmp, sizeof(tmp)))
+    r->result = strdup(tmp);
+
+  silc_schedule_task_add(r->schedule, 0, silc_net_resolve_completion, r, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_wakeup(r->schedule);
+  return NULL;
+}
+
+/* Thread function to resolve the hostname for address. */
+
+static void *silc_net_gethostbyaddr_thread(void *context)
+{
+  SilcNetResolveContext r = (SilcNetResolveContext)context;
+  char tmp[256];
+
+  if (silc_net_gethostbyaddr(r->input, tmp, sizeof(tmp)))
+    r->result = strdup(tmp);
+
+  silc_schedule_task_add(r->schedule, 0, silc_net_resolve_completion, r, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_wakeup(r->schedule);
+  return NULL;
+}
+
+/* Resolves IP address for hostname. */
+
+bool silc_net_gethostbyname(const char *name, char *address,
+                           uint32 address_len)
+{
+#ifdef HAVE_IPV6
+  struct addrinfo hints, *ai;
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_socktype = SOCK_STREAM;
+  if (getaddrinfo(name, NULL, &hints, &ai))
+    return FALSE;
+
+  if (getnameinfo(ai->ai_addr, ai->ai_addrlen, address,
+                 address_len, NULL, 0, NI_NUMERICHOST)) {
+    freeaddrinfo(ai);
+    return FALSE;
+  } else {
+    freeaddrinfo(ai);
+  }
+#else
+  struct hostent *hp;
+  struct in_addr ip;
+  char *tmp;
+
+  hp = gethostbyname(name);
+  if (!hp)
+    return FALSE;
+
+  memcpy(&ip.s_addr, hp->h_addr_list[0], 4);
+  tmp = inet_ntoa(ip);
+  if (!tmp)
+    return FALSE;
+  if (address_len < strlen(tmp))
+    return FALSE;
+  memset(address, 0, address_len);
+  strncpy(address, tmp, strlen(tmp));
+#endif
+  
+  return TRUE;
+}
+
+/* Resolves IP address for hostname async. */
+
+void silc_net_gethostbyname_async(const char *name, 
+                                 SilcSchedule schedule,
+                                 SilcNetResolveCallback completion,
+                                 void *context)
+{
+  SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
+
+  r->completion = completion;
+  r->context = context;
+  r->schedule = schedule;
+  r->input = strdup(name);
+
+  silc_thread_create(silc_net_gethostbyname_thread, r, FALSE);
+}
+
+/* Resolves hostname by IP address. */
+
+bool silc_net_gethostbyaddr(const char *addr, char *name, uint32 name_len)
+{
+#ifdef HAVE_IPV6
+  struct addrinfo req, *ai;
+  
+  memset(&req, 0, sizeof(req));
+  req.ai_socktype = SOCK_STREAM;
+  req.ai_flags = AI_CANONNAME;
+  
+  if (getaddrinfo(addr, NULL, &req, &ai))
+    return FALSE;
+  if (getnameinfo(ai->ai_addr, ai->ai_addrlen, name, name_len, NULL, 0, 0)) {
+    freeaddrinfo(ai);
+    return FALSE;
+  }
+  freeaddrinfo(ai);
+#else
+  struct hostent *hp;
+
+  hp = gethostbyaddr(addr, strlen(addr), AF_INET);
+  if (!hp)
+    return FALSE;
+  if (name_len < strlen(hp->h_name))
+    return FALSE;
+  memset(name, 0, name_len);
+  strncpy(name, hp->h_name, strlen(hp->h_name));
+#endif
+  
+  return TRUE;
+}
+
+/* Resolves hostname by IP address async. */
+
+void silc_net_gethostbyaddr_async(const char *addr, 
+                                 SilcSchedule schedule,
+                                 SilcNetResolveCallback completion,
+                                 void *context)
+{
+  SilcNetResolveContext r = silc_calloc(1, sizeof(*r));
+
+  r->completion = completion;
+  r->context = context;
+  r->schedule = schedule;
+  r->input = strdup(addr);
+
+  silc_thread_create(silc_net_gethostbyaddr_thread, r, FALSE);
+}
+
+/* Performs lookups for remote name and IP address. This peforms reverse
+   lookup as well to verify that the IP has FQDN. */
+
+bool silc_net_check_host_by_sock(int sock, char **hostname, char **ip)
+{
+  struct sockaddr_in remote;
+  struct hostent *dest;
+  char *host_ip = NULL;
+  char host_name[1024];
+  int rval, len;
+  int i;
+
+  *hostname = NULL;
+  *ip = NULL;
+
+  SILC_LOG_DEBUG(("Resolving remote hostname and IP address"));
+
+  memset(&remote, 0, sizeof(remote));
+  len = sizeof(remote);
+  rval = getpeername(sock, (struct sockaddr *)&remote, &len);
+  if (rval < 0)
+    return FALSE;
+
+  host_ip = inet_ntoa(remote.sin_addr);
+  if (!host_ip)
+    return FALSE;
+
+  *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
+  memcpy(*ip, host_ip, strlen(host_ip));
+
+  /* Get host by address */
+  dest = gethostbyaddr((char *)&remote.sin_addr, 
+                      sizeof(struct in_addr), AF_INET);
+  if (!dest)
+    return FALSE;
+
+  /* Get same host by name to see that the remote host really is
+     the who it says it is */
+  memset(host_name, 0, sizeof(host_name));
+  memcpy(host_name, dest->h_name, strlen(dest->h_name));
+
+  *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
+  memcpy(*hostname, host_name, strlen(host_name));
+  SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
+
+  dest = gethostbyname(host_name);
+  if (!dest)
+    return FALSE;
+
+  /* Find the address from list */
+  for (i = 0; dest->h_addr_list[i]; i++)
+    if (!memcmp(dest->h_addr_list[i], &remote.sin_addr, 
+               sizeof(struct in_addr)))
+      break;
+  if (!dest->h_addr_list[i])
+    return FALSE;
+
+  silc_free(*ip);
+  *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
+  memcpy(*ip, host_ip, strlen(host_ip));
+  SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
+
+  return TRUE;
+}
+
+/* Performs lookups for local name and IP address. This peforms reverse
+   lookup as well to verify that the IP has FQDN. */
+
+bool silc_net_check_local_by_sock(int sock, char **hostname, char **ip)
+{
+  struct sockaddr_in local;
+  struct hostent *dest;
+  char *host_ip = NULL;
+  char host_name[1024];
+  int rval, len;
+  int i;
+
+  *hostname = NULL;
+  *ip = NULL;
+
+  SILC_LOG_DEBUG(("Resolving local hostname and IP address"));
+
+  memset(&local, 0, sizeof(local));
+  len = sizeof(local);
+  rval = getsockname(sock, (struct sockaddr *)&local, &len);
+  if (rval < 0)
+    return FALSE;
+
+  host_ip = inet_ntoa(local.sin_addr);
+  if (!host_ip)
+    return FALSE;
+
+  *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
+  memcpy(*ip, host_ip, strlen(host_ip));
+
+  /* Get host by address */
+  dest = gethostbyaddr((char *)&local.sin_addr, 
+                      sizeof(struct in_addr), AF_INET);
+  if (!dest)
+    return FALSE;
+
+  /* Get same host by name to see that the local host really is
+     the who it says it is */
+  memset(host_name, 0, sizeof(host_name));
+  memcpy(host_name, dest->h_name, strlen(dest->h_name));
+
+  *hostname = silc_calloc(strlen(host_name) + 1, sizeof(char));
+  memcpy(*hostname, host_name, strlen(host_name));
+  SILC_LOG_DEBUG(("Resolved hostname `%s'", *hostname));
+
+  dest = gethostbyname(host_name);
+  if (!dest)
+    return FALSE;
+
+  /* Find the address from list */
+  for (i = 0; dest->h_addr_list[i]; i++)
+    if (!memcmp(dest->h_addr_list[i], &local.sin_addr, 
+              sizeof(struct in_addr)))
+      break;
+  if (!dest->h_addr_list[i])
+    return FALSE;
+
+  silc_free(*ip);
+  *ip = silc_calloc(strlen(host_ip) + 1, sizeof(char));
+  memcpy(*ip, host_ip, strlen(host_ip));
+  SILC_LOG_DEBUG(("Resolved IP address `%s'", *ip));
+
+  return TRUE;
+}
+
+/* Return remote port by socket. */
+
+uint16 silc_net_get_remote_port(int sock)
+{
+#ifdef HAVE_IPV6
+  struct sockaddr_storage remote;
+  int len;
+  char s[NI_MAXSERV];
+
+  memset(&remote, 0, sizeof(remote));
+  len = sizeof(remote);
+  if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
+    return 0;
+
+  if (getnameinfo((struct sockaddr *)&remote, len, NULL, 0, s, sizeof(s),
+      NI_NUMERICSERV))
+    return 0;
+  
+  return atoi(s);
+#else
+  struct sockaddr_in remote;
+  int len;
+
+  memset(&remote, 0, sizeof(remote));
+  len = sizeof(remote);
+  if (getpeername(sock, (struct sockaddr *)&remote, &len) < 0)
+    return 0;
+
+  return ntohs(remote.sin_port);
+#endif
+}
+
+/* Return local port by socket. */
+
+uint16 silc_net_get_local_port(int sock)
+{
+#ifdef HAVE_IPV6
+  struct sockaddr_storage local;
+  int len;
+  char s[NI_MAXSERV];
+
+  memset(&local, 0, sizeof(local));
+  len = sizeof(local);
+  if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
+    return 0;
+
+  if (getnameinfo((struct sockaddr *)&local, len, NULL, 0, s, sizeof(s),
+      NI_NUMERICSERV))
+    return 0;
+  
+  return atoi(s);
+#else
+  struct sockaddr_in local;
+  int len;
+
+  memset(&local, 0, sizeof(local));
+  len = sizeof(local);
+  if (getsockname(sock, (struct sockaddr *)&local, &len) < 0)
+    return 0;
+
+  return ntohs(local.sin_port);
+#endif
+}
+
+/* Return name of localhost. */
+
+char *silc_net_localhost(void)
+{
+  char hostname[256], ip_addr[64];
+
+  if (gethostname(hostname, sizeof(hostname)))
+    return NULL;
+
+  if (!silc_net_gethostbyname(hostname, ip_addr, sizeof(ip_addr)))
+    return strdup(hostname);
+
+  silc_net_gethostbyaddr(ip_addr, hostname, sizeof(hostname));
+  return strdup(hostname);
+}
+
+/* Returns local IP address */
+
+char *silc_net_localip(void)
+{
+  char hostname[256], ip_addr[64];
+
+  if (gethostname(hostname, sizeof(hostname)))
+    return NULL;
+
+  if (!silc_net_gethostbyname(hostname, ip_addr, sizeof(ip_addr)))
+    return NULL;
+
+  return strdup(ip_addr);
+}
diff --git a/lib/silcutil/silcnet.h b/lib/silcutil/silcnet.h
new file mode 100644 (file)
index 0000000..0e85a1b
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+
+  silcnet.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcNetAPI
+ *
+ * DESCRIPTION
+ *
+ * SILC Net API provides various network routines for applications. It
+ * can be used to create TCP/IP connections and servers. Various utility
+ * functions for resolving various information is also provided.
+ *
+ * On WIN32 systems the SILC Net API must initialized by calling the
+ * silc_net_win32_init and uninitialized when the application ends by
+ * calling the silc_net_win32_uninit function. The initializing must be
+ * done in order to assure that the SILC Net API works correctly.
+ *
+ ***/
+
+#ifndef SILCNET_H
+#define SILCNET_H
+
+/* Prototypes */
+
+/****f* silcutil/SilcNetAPI/silc_net_create_server
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_create_server(int port, char *ip_addr);
+ *
+ * DESCRIPTION
+ *
+ *    This function creates server or daemon or listener or what ever. This
+ *    does not fork a new process, it must be done by the caller if caller
+ *    wants to create a child process. This is used by the SILC server. 
+ *    If argument `ip_addr' is NULL `any' address will be used. Returns 
+ *    the created socket or -1 on error.
+ *
+ ***/
+int silc_net_create_server(int port, const char *ip_addr);
+
+/****f* silcutil/SilcNetAPI/silc_net_close_server
+ *
+ * SYNOPSIS
+ *
+ *    void silc_net_close_server(int sock);
+ *
+ * DESCRIPTION
+ *
+ *    Closes the server by closing the socket connection.
+ *
+ ***/
+void silc_net_close_server(int sock);
+
+/****f* silcutil/SilcNetAPI/silc_net_create_connection
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_create_connection(const char *local_ip, int port, 
+ *                                   const char *host);
+ *
+ * DESCRIPTION
+ *
+ *    Creates a connection (TCP/IP) to a remote host. Returns the connection
+ *    socket or -1 on error. This blocks the process while trying to create
+ *    the connection. If the `local_ip' is not NULL then this will bind
+ *    the `local_ip' address to a port before creating the connection.  If
+ *    it is NULL then this will directly create the connection.
+ *
+ ***/
+int silc_net_create_connection(const char *localhost, int port, 
+                              const char *host);
+
+/****f* silcutil/SilcNetAPI/silc_net_create_connection_async
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_create_connection_async(const char *local_ip, int port, 
+ *                                         const char *host);
+ *
+ * DESCRIPTION
+ *
+ *    Creates a connection (TCP/IP) to a remote host. Returns the connection
+ *    socket or -1 on error. This creates non-blocking socket hence the
+ *    connection returns directly. To get the result of the connect() one
+ *    must select() the socket and read the result after it's ready. If the
+ *    `local_ip' is not NULL then this will bind the `local_ip' address to
+ *    a port before creating the connection.  If it is NULL then this will
+ *    directly create the connection.
+ *
+ ***/
+int silc_net_create_connection_async(const char *local_ip, int port, 
+                                    const char *host);
+
+/****f* silcutil/SilcNetAPI/silc_net_close_connection
+ *
+ * SYNOPSIS
+ *
+ *    void silc_net_close_connection(int sock);
+ *
+ * DESCRIPTION
+ *
+ *    Closes the connection by closing the socket connection.
+ *
+ ***/
+void silc_net_close_connection(int sock);
+
+/****f* silcutil/SilcNetAPI/silc_net_accept_connection
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_accept_connection(int sock);
+ *
+ * DESCRIPTION
+ *
+ *    Accepts a connection from a particular socket.
+ *
+ ***/
+int silc_net_accept_connection(int sock);
+
+/****f* silcutil/SilcNetAPI/silc_net_set_socket_nonblock
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_set_socket_nonblock(int sock);
+ *
+ * DESCRIPTION
+ *
+ *    Sets the socket to non-blocking mode.
+ *
+ ***/
+int silc_net_set_socket_nonblock(int sock);
+
+/****f* silcutil/SilcNetAPI/silc_net_set_socket_opt
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_set_socket_opt(int sock, int level, int option, int on);
+ *
+ * DESCRIPTION
+ *
+ *    Sets a option for a socket.  This function can be used to set
+ *    various options for the socket.  Some of the options might be
+ *    system specific.
+ *
+ ***/
+int silc_net_set_socket_opt(int sock, int level, int option, int on);
+
+/****f* silcutil/SilcNetAPI/silc_net_get_socket_opt
+ *
+ * SYNOPSIS
+ *
+ *    int silc_net_get_socket_opt(int sock, int level, int option, 
+ *                                void *optval, int *opt_len);
+ *
+ * DESCRIPTION
+ *
+ *    Return socket options to the `optval' and `opt_len'.
+ *
+ ***/
+int silc_net_get_socket_opt(int sock, int level, int option, 
+                           void *optval, int *opt_len);
+
+/****f* silcutil/SilcNetAPI/silc_net_is_ip4
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_is_ip4(const char *addr);
+ *
+ * DESCRIPTION
+ *
+ *    Checks whether IP address sent as argument is valid IPv4 address.
+ *
+ ***/
+bool silc_net_is_ip4(const char *addr);
+
+/****f* silcutil/SilcNetAPI/silc_net_is_ip6
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_is_ip6(const char *addr);
+ *
+ * DESCRIPTION
+ *
+ *    Checks whether IP address sent as argument is valid IPv6 address.
+ *
+ ***/
+bool silc_net_is_ip6(const char *addr);
+
+/****f* silcutil/SilcNetAPI/silc_net_is_ip
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_is_ip(const char *addr);
+ *
+ * DESCRIPTION
+ *
+ *    Checks whether IP address sent as argument is valid IP address.
+ *    This supports both IPv4 and IPv6 addresses.
+ *
+ ***/
+bool silc_net_is_ip(const char *addr);
+
+/****f* silcutil/SilcNetAPI/silc_net_addr2bin
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_addr2bin(const char *addr, void *bin, uint32 bin_len);
+ *
+ * DESCRIPTION
+ *
+ *    Converts the IP number string from numbers-and-dots notation to
+ *    binary form in network byte order.  The address can be either
+ *    IPv4 or IPv6 address.
+ *
+ ***/
+bool silc_net_addr2bin(const char *addr, void *bin, uint32 bin_len);
+
+/****f* silcutil/SilcNetAPI/SilcNetResolveCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcNetResolveCallback)(const char *result, 
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    A callback function of this type is called after the asynchronous
+ *    resolving operation has been completed.  This callback is used
+ *    when asynchronously resolving IP addresses and hostnames.
+ *
+ ***/
+typedef void (*SilcNetResolveCallback)(const char *result, void *context);
+
+/****f* silcutil/SilcNetAPI/silc_net_gethostbyname
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_gethostbyname(const char *name, char *address, 
+ *                                uint32 address_len);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves the IP address of the hostname indicated by the `host'.
+ *    This returns TRUE and the IP address of the host, or FALSE
+ *    if the address could not be resolved.  This is synchronous
+ *    function and will block the calling process.
+ *
+ ***/
+bool silc_net_gethostbyname(const char *name, char *address, 
+                           uint32 address_len);
+
+/****f* silcutil/SilcNetAPI/silc_net_gethostbyname_async
+ *
+ * SYNOPSIS
+ *
+ *    void silc_net_gethostbyname_async(const char *name, 
+ *                                      SilcSchedule schedule,
+ *                                      SilcNetResolveCallback completion,
+ *                                      void *context)
+ *
+ * DESCRIPTION
+ *
+ *    Asynchronously resolves the IP address of the hostname indicated
+ *    by the `host'.  This function returns immediately, and the
+ *    `completion' callback will be called after the resolving is
+ *    completed.
+ *
+ ***/
+void silc_net_gethostbyname_async(const char *name, 
+                                 SilcSchedule schedule,
+                                 SilcNetResolveCallback completion,
+                                 void *context);
+
+/****f* silcutil/SilcNetAPI/silc_net_gethostbyaddr
+ *
+ * SYNOPSIS
+ *
+ *   bool silc_net_gethostbyaddr(const char *addr, char *name, 
+ *                               uint32 name_len);
+ *
+ * DESCRIPTION
+ *
+ *    Resolves the hostname for the IP address indicated by the `addr'
+ *    This returns TRUE and the resolved hostname, or FALSE on error.
+ *    The `addr' may be either IPv4 or IPv6 address.  This is
+ *    synchronous function and will block the calling process.
+ *
+ ***/
+bool silc_net_gethostbyaddr(const char *addr, char *name, uint32 name_len);
+
+/****f* silcutil/SilcNetAPI/silc_net_gethostbyaddr_async
+ *
+ * SYNOPSIS
+ *
+ *    void silc_net_gethostbyaddr_async(const char *addr, 
+ *                                      SilcSchedule schedule,
+ *                                      SilcNetResolveCallback completion,
+ *                                      void *context)
+ *
+ * DESCRIPTION
+ *
+ *    Asynchronously resolves the hostname for the IP address indicated
+ *    by the `addr'.  This function returns immediately, and the
+ *    `completion' callback will be called after the resolving is
+ *    completed.
+ *
+ ***/
+void silc_net_gethostbyaddr_async(const char *addr, 
+                                 SilcSchedule schedule,
+                                 SilcNetResolveCallback completion,
+                                 void *context);
+
+/****f* silcutil/SilcNetAPI/silc_net_check_host_by_sock
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_check_host_by_sock(int sock, char **hostname, char **ip);
+ *
+ * DESCRIPTION
+ *
+ *    Performs lookups for remote name and IP address. This peforms reverse
+ *    lookup as well to verify that the IP has FQDN.
+ *
+ ***/
+bool silc_net_check_host_by_sock(int sock, char **hostname, char **ip);
+
+/****f* silcutil/SilcNetAPI/silc_net_check_local_by_sock
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_check_local_by_sock(int sock, char **hostname, char **ip);
+ *
+ * DESCRIPTION
+ *
+ *    Performs lookups for local name and IP address. This peforms reverse
+ *    lookup as well to verify that the IP has FQDN.
+ *
+ ***/
+bool silc_net_check_local_by_sock(int sock, char **hostname, char **ip);
+
+/****f* silcutil/SilcNetAPI/silc_net_get_remote_port
+ *
+ * SYNOPSIS
+ *
+ *    uint16 silc_net_get_remote_port(int sock);
+ *
+ * DESCRIPTION
+ *
+ *    Return remote port by socket.
+ *
+ ***/
+uint16 silc_net_get_remote_port(int sock);
+
+/****f* silcutil/SilcNetAPI/silc_net_get_local_port
+ *
+ * SYNOPSIS
+ *
+ *    uint16 silc_net_get_local_port(int sock);
+ *
+ * DESCRIPTION
+ *
+ *    Return local port by socket.
+ *
+ ***/
+uint16 silc_net_get_local_port(int sock);
+
+/****f* silcutil/SilcNetAPI/silc_net_localhost
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_net_localhost(void);
+ *
+ * DESCRIPTION
+ *
+ *    Return name of localhost.  This will also attempt to resolve
+ *    the real hostname by the local host's IP address.  If unsuccessful
+ *    the first found hostname is returned.
+ *
+ ***/
+char *silc_net_localhost(void);
+
+/****f* silcutil/SilcNetAPI/silc_net_localip
+ *
+ * SYNOPSIS
+ *
+ *    char *silc_net_localip(void)
+ *
+ * DESCRIPTION
+ *
+ *    Return IP of localhost.  
+ *
+ ***/
+char *silc_net_localip(void);
+
+#ifdef WIN32
+
+/****f* silcutil/SilcNetAPI/silc_net_win32_init
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_net_win32_init(void);
+ *
+ * DESCRIPTION
+ *
+ *    This is WIN32 system specific function and is used to initialize
+ *    the network.  This must be called by all WIN32 applications.  It
+ *    is usually called at the application's main() or WinMain() before
+ *    calling any other SILC routine.  The application must also call
+ *    the silc_net_win32_uninit when exiting the application.  Returns
+ *    FALSE on error.  The network will not work if this function returns
+ *    FALSE.
+ *
+ ***/
+bool silc_net_win32_init(void);
+
+/****f* silcutil/SilcNetAPI/silc_net_win32_uninit
+ *
+ * SYNOPSIS
+ *
+ *    void silc_net_win32_init(void);
+ *
+ * DESCRIPTION
+ *
+ *    This is WIN32 system specific function and is used to uninitialize
+ *    the network.  This must be called by all WIN32 applications.  It
+ *    is usually called when the application is exiting.  After calling
+ *    this function the SILC Net API routines will not work anymore.
+ *
+ ***/
+void silc_net_win32_uninit(void);
+
+#endif
+
+#endif
diff --git a/lib/silcutil/silcprotocol.c b/lib/silcutil/silcprotocol.c
new file mode 100644 (file)
index 0000000..3703c30
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+
+  silcprotocol.c
+
+  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/*
+ * Created: Tue Nov 25 19:25:33 GMT+0200 1997
+ */
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcprotocol.h"
+
+/* Dynamically registered protocols */
+SilcProtocolObject *silc_protocol_list = NULL;
+
+/* Dynamically registers new protocol. The protocol is added into protocol
+   list and can be unregistered with silc_protocol_unregister. */
+
+void silc_protocol_register(SilcProtocolType type,
+                           SilcProtocolCallback callback)
+{
+  SilcProtocolObject *new;
+
+  new = silc_calloc(1, sizeof(*new));
+  new->type = type;
+  new->callback = callback;
+
+  if (!silc_protocol_list)
+    silc_protocol_list = new;
+  else {
+    new->next = silc_protocol_list;
+    silc_protocol_list = new;
+  }
+}
+
+/* Unregisters protocol. The unregistering is done by both protocol type
+   and the protocol callback. */
+
+void silc_protocol_unregister(SilcProtocolType type,
+                              SilcProtocolCallback callback)
+{
+  SilcProtocolObject *protocol, *prev;
+
+  protocol = silc_protocol_list;
+  prev = NULL;
+  while (protocol && (protocol->type != type && 
+                      protocol->callback != callback)) {
+    prev = protocol;
+    protocol = protocol->next;
+  }
+
+  if (protocol) {
+    if (prev)
+      prev->next = protocol->next;
+    else
+      silc_protocol_list = protocol->next;
+
+    silc_free(protocol);
+  }
+}
+
+/* Allocates a new protocol object. The new allocated and initialized 
+   protocol is returned to the new_protocol argument. The argument context
+   is the context to be sent as argument for the protocol. The callback
+   argument is the function to be called _after_ the protocol has finished. */
+
+void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
+                        void *context, SilcProtocolFinalCallback callback)
+{
+  SilcProtocolObject *protocol;
+
+  SILC_LOG_DEBUG(("Allocating new protocol type %d", type));
+
+  protocol = silc_protocol_list;
+  while (protocol && protocol->type != type)
+    protocol = protocol->next;
+
+  if (!protocol) {
+    SILC_LOG_ERROR(("Requested protocol does not exists"));
+    *new_protocol = NULL;
+    return;
+  }
+
+  *new_protocol = silc_calloc(1, sizeof(**new_protocol));
+  (*new_protocol)->protocol = protocol;
+  (*new_protocol)->state = SILC_PROTOCOL_STATE_UNKNOWN;
+  (*new_protocol)->context = context;
+  (*new_protocol)->final_callback = callback;
+}
+
+/* Frees a protocol object. */
+
+void silc_protocol_free(SilcProtocol protocol)
+{
+  if (protocol)
+    silc_free(protocol);
+}
+
+/* Executes next state of the protocol. The state must be set before
+   calling this function. */
+
+void silc_protocol_execute(SilcProtocol protocol, SilcSchedule schedule,
+                          long secs, long usecs)
+{
+  if (secs + usecs) 
+    silc_schedule_task_add(schedule, 0, 
+                          protocol->protocol->callback, (void *)protocol, 
+                          secs, usecs, 
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_NORMAL);
+  else
+    protocol->protocol->callback(schedule, 0, 0, (void *)protocol);
+}
+
+/* Executes the final callback of the protocol. */
+
+void silc_protocol_execute_final(SilcProtocol protocol, SilcSchedule schedule)
+{
+  protocol->final_callback(schedule, 0, 0, (void *)protocol);
+}
+
+/* Cancels the execution of the next state of the protocol. */
+
+void silc_protocol_cancel(SilcProtocol protocol, SilcSchedule schedule)
+{
+  silc_schedule_task_del_by_context(schedule, protocol);
+}
diff --git a/lib/silcutil/silcprotocol.h b/lib/silcutil/silcprotocol.h
new file mode 100644 (file)
index 0000000..a6e4463
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+
+  silcprotocol.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1997 - 2000 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silccore/silcprotocol.h
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the protocol handling routines for SILC applications.
+ * These routines allow execution of arbitrary protocols in the application.
+ * New protocols may be registered by type and allocated later by that
+ * type for the execution. The protocols implements a state machine style
+ * execution where each state is executed one after the other. The
+ * application controls these states and their order of execution.
+ * 
+ * After the protocol has been executed, an final callback is called
+ * which the application may use to do post-protocol work or to start
+ * perhaps other protocols. These routines are generic and the actual
+ * protocols, their types, callback and final callbacks functions must
+ * be implemented in the application.
+ *
+ ***/
+
+#ifndef SILCPROTOCOL_H
+#define SILCPROTOCOL_H
+
+/****d* silccore/SilcProtocolAPI/SilcProtocolType
+ *
+ * NAME
+ * 
+ *    typedef unsigned char SilcProtocolType;
+ *
+ * DESCRIPTION
+ *
+ *    Protocol type definition. The protocol types are application
+ *    specific and this is just a generic type for them.
+ *
+ ***/
+typedef unsigned char SilcProtocolType;
+
+/****d* silccore/SilcProtocolAPI/SilcProtocolState
+ *
+ * NAME
+ * 
+ *    typedef unsigned char SilcProtocolState;
+ *
+ * DESCRIPTION
+ *
+ *    Protocol state definition and the defined protocol states. These
+ *    states are the generic states. However, each protocol actually
+ *    implements the states. The state after SILC_PROTOCOL_STATE_START
+ *    would be state 2 in the application. These states can be easily
+ *    used for example inside switch() statement.
+ *
+ * EXAMPLE
+ *
+ *    switch (protocol->state) {
+ *    case SILC_PROTOCOL_STATE_START:
+ *      protocol_starts_here();
+ *    case 2:
+ *      ...
+ *    case 3:
+ *      ...
+ *    case SILC_PROTOCOL_STATE_END:
+ *      protocol_ends_here();
+ *    case SILC_PROTOCOL_STATE_FAILURE:
+ *      remote_end_sent_failure();
+ *    case SILC_PROTOCOL_STATE_ERROR:
+ *      local_error_during_protocol();
+ *    }
+ *
+ * SOURCE
+ */
+typedef unsigned char SilcProtocolState;
+
+/* Protocol states. Do NOT change the values of these states, especially
+   the START state or you break every protocol. */
+#define SILC_PROTOCOL_STATE_UNKNOWN 0
+#define SILC_PROTOCOL_STATE_START 1
+#define SILC_PROTOCOL_STATE_END 252
+#define SILC_PROTOCOL_STATE_FAILURE 253         /* Received failure from remote */
+#define SILC_PROTOCOL_STATE_ERROR 254    /* Local error at our end */
+/***/
+
+/* Type definition for authentication protocol's auth methods. */
+/* XXX strictly speaking this belongs to application */
+typedef unsigned char SilcProtocolAuthMeth;
+
+/****f* silccore/SilcProtocolAPI/SilcProtocolCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef SilcTaskCallback SilcProtocolCallback;
+ *
+ * DESCRIPTION
+ *
+ *    Protocol callback. This callback is set when registering new
+ *    protocol. The callback is called everytime the protocol is executed.
+ *    The `context' delivered to this callback function is the SilcProtocol
+ *    context and needs to be explicitly type casted to SilcProtocol in
+ *    the callback function.
+ *
+ ***/
+typedef SilcTaskCallback SilcProtocolCallback;
+
+/****f* silccore/SilcProtocolAPI/SilcProtocolFinalCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef SilcTaskCallback SilcProtocolFinalCallback;
+ *
+ * DESCRIPTION
+ *
+ *    Final protocol callback. This callback is set when allocating
+ *    protocol for execution. This is called when the protocol has ended.
+ *    The `context' delivered to this callback function is the SilcProtocol
+ *    context and needs to be explicitly type casted to SilcProtocol in
+ *    the callback function.
+ *
+ ***/
+typedef SilcTaskCallback SilcProtocolFinalCallback;
+
+/****s* silccore/SilcProtocolAPI/SilcProtocolObject
+ *
+ * NAME
+ * 
+ *    typedef struct SilcProtocolObjectStruct { ... } SilcProtocolObject;
+ *
+ * DESCRIPTION
+ *
+ *    The object for one protocol. This hold the information of one
+ *    registered protocol. Application must not allocate this type
+ *    directly. It is used by the protocol routines.
+ *
+ *    Short description of the field following:
+ *  
+ *    SilcProtocolType type
+ *
+ *      Protocol type.
+ * 
+ *    SilcProtocolCallback callback;
+ *
+ *      Callback function for the protocol. This is SilcTaskCallback function
+ *      pointer as the protocols in SILC are executed as timeout tasks.
+ *
+ *    struct SilcProtocolObjectStruct *next;
+ *
+ *      Pointer to the next protocol.
+ *
+ ***/
+typedef struct SilcProtocolObjectStruct {
+  SilcProtocolType type;
+  SilcProtocolCallback callback;
+  struct SilcProtocolObjectStruct *next;
+} SilcProtocolObject;
+
+/****s* silccore/SilcProtocolAPI/SilcProtocol
+ *
+ * NAME
+ * 
+ *    typedef struct SilcProtocolStruct { ... } *SilcProtocol;
+ *
+ * DESCRIPTION
+ *
+ *    The actual protocol object. This is allocated by the silc_protocol_alloc
+ *    and holds the information about the current protocol. Information
+ *    such as the current state, execution callback and final callback.
+ *    The context is freed by silc_protocol_free function.
+ *
+ *    Short description of the field following:
+ *
+ *    SilcProtocolObject *protocol
+ *
+ *      This is the pointer to the SilcProtocolObject and holds the
+ *      protocol specific information.
+ *
+ *    SilcProtocolState state
+ *
+ *      Protocol state. The state of the protocol can be changed in the
+ *      callback function.
+ *
+ *    void *context
+ *
+ *      Context to be sent for the callback function. This is usually 
+ *      object for either SILC client or server. However, this abstraction 
+ *      makes it possible that this pointer could be some other object as
+ *      well. Note that the context is not delivered in any callback 
+ *      function. Application can access it through this context.
+ *
+ *    SilcProtocolFinalCallback final_callback;
+ *
+ *      This is a callback function that is called with timeout _after_ the
+ *      protocol has finished or error occurs. If this is NULL, naturally 
+ *      nothing will be executed. Protocol should call this function only at 
+ *      SILC_PROTOCOL_STATE_END and SILC_PROTOCOL_STATE_ERROR states.
+ *
+ ***/
+typedef struct SilcProtocolStruct {
+  SilcProtocolObject *protocol;
+  SilcProtocolState state;
+  void *context;
+  SilcProtocolFinalCallback final_callback;
+} *SilcProtocol;
+
+/* Prototypes */
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_register
+ *
+ * SYNOPSIS
+ *
+ *    void silc_protocol_register(SilcProtocolType type,
+ *                                SilcProtocolCallback callback);
+ *
+ * DESCRIPTION
+ *
+ *    Dynamically registers new protocol. The protocol is added into protocol
+ *    list and can be unregistered with silc_protocol_unregister. The
+ *    `type' is the type of the protocol and is used to identify the
+ *    protocol when allocating it with silc_protocol_alloc. The `callback'
+ *    is the actual protocol function that is called when protocol is
+ *    executed (and it performes the actual protocol). The protocol
+ *    is unregistered by silc_protocol_unregister function.
+ *
+ ***/
+void silc_protocol_register(SilcProtocolType type,
+                           SilcProtocolCallback callback);
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_unregister
+ *
+ * SYNOPSIS
+ *
+ *    void silc_protocol_unregister(SilcProtocolType type,
+ *                                  SilcProtocolCallback callback);
+ *
+ * DESCRIPTION
+ *
+ *    Unregisters protocol. The unregistering is done by both protocol type
+ *    and the protocol callback. Every registered protocol must be 
+ *    unregistered using this function.
+ *
+ ***/
+void silc_protocol_unregister(SilcProtocolType type,
+                              SilcProtocolCallback callback);
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_alloc
+ *
+ * SYNOPSIS
+ *
+ *    void silc_protocol_alloc(SilcProtocolType type, 
+ *                             SilcProtocol *new_protocol,
+ *                             void *context, 
+ *                             SilcProtocolFinalCallback callback);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates a new protocol. The new allocated and initialized 
+ *    protocol is returned to the `new_protocol' argument. The argument
+ *    context `context' is the context to be sent as argument for the
+ *    protocol callback function. The `callback' argument is the function
+ *    to be called after the protocol has finished.
+ *
+ ***/
+void silc_protocol_alloc(SilcProtocolType type, SilcProtocol *new_protocol,
+                        void *context, SilcProtocolFinalCallback callback);
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_protocol_free(SilcProtocol protocol);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the protocol context. This must be called for all allocated
+ *    protocols.
+ *
+ ***/
+void silc_protocol_free(SilcProtocol protocol);
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_execute
+ *
+ * SYNOPSIS
+ *
+ *    void silc_protocol_execute(SilcProtocol protocol, SilcSchedule schedule,
+ *                               long secs, long usecs);
+ *
+ * DESCRIPTION
+ *
+ *    Executes the protocol. This calls the state that has been set.
+ *    The state must be set before calling this function. This is then
+ *    also used to call always the next state after changing the state
+ *    of the protocol. The `schedule' is the application's scheduler.
+ *    It is passed to the protocol callback functions. The `secs' and 
+ *    `usecs' are the timeout before the protocol is executed. If both 
+ *    zero the protocol is executed immediately.
+ *
+ ***/
+void silc_protocol_execute(SilcProtocol protocol, SilcSchedule schedule,
+                          long secs, long usecs);
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_execute_final
+ *
+ * SYNOPSIS
+ *
+ *    void 
+ *    silc_protocol_execute_final(SilcProtocol protocol, 
+ *                               SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Executes the final callback for the protocol. The `schedule' is
+ *    the application's scheduler.. It is passed to the protocol callback
+ *    functions. The final callback is executed immediately.
+ *
+ ***/
+void silc_protocol_execute_final(SilcProtocol protocol, SilcSchedule schedule);
+
+/****f* silccore/SilcProtocolAPI/silc_protocol_cancel
+ *
+ * SYNOPSIS
+ *
+ *    void silc_protocol_cancel(SilcProtocol protocol, SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Cancels the execution of the next state of the protocol. This has
+ *    effect only if the silc_protocol_execute was called with timeout.
+ *    It is guaranteed that if the protocol is cancelled before the timeout
+ *    has elapsed the protocol callback won't be called.
+ *
+ ***/
+void silc_protocol_cancel(SilcProtocol protocol, SilcSchedule schedule);
+
+#endif
diff --git a/lib/silcutil/silcschedule.c b/lib/silcutil/silcschedule.c
new file mode 100644 (file)
index 0000000..1672fdf
--- /dev/null
@@ -0,0 +1,1207 @@
+/*
+
+  silcschedule.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1998 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcschedule_i.h"
+
+/* Forward declarations */
+typedef struct SilcTaskQueueStruct *SilcTaskQueue;
+
+/* System specific routines. Implemented under unix/ and win32/. */
+
+/* System specific select(). Returns same values as normal select(). */
+int silc_select(SilcScheduleFd fds, uint32 fds_count, struct timeval *timeout);
+
+/* Initializes the wakeup of the scheduler. In multi-threaded environment
+   the scheduler needs to be wakenup when tasks are added or removed from
+   the task queues. This will initialize the wakeup for the scheduler.
+   Any tasks that needs to be registered must be registered to the `queue'.
+   It is guaranteed that the scheduler will automatically free any
+   registered tasks in this queue. This is system specific routine. */
+void *silc_schedule_wakeup_init(SilcSchedule schedule);
+
+/* Uninitializes the system specific wakeup. */
+void silc_schedule_wakeup_uninit(void *context);
+
+/* Wakes up the scheduler. This is platform specific routine */
+void silc_schedule_wakeup_internal(void *context);
+
+
+/* Internal task management routines. */
+
+static void silc_task_queue_alloc(SilcTaskQueue *queue);
+static void silc_task_queue_free(SilcTaskQueue queue);
+static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd);
+static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask, 
+                             SilcTaskPriority priority);
+static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first);
+static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
+                                     SilcTaskPriority priority);
+static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task);
+static int silc_schedule_task_timeout_compare(struct timeval *smaller, 
+                                             struct timeval *bigger);
+static void silc_task_del_by_context(SilcTaskQueue queue, void *context);
+static void silc_task_del_by_callback(SilcTaskQueue queue,
+                                     SilcTaskCallback callback);
+static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd);
+
+/* Returns the task queue by task type */
+#define SILC_SCHEDULE_GET_QUEUE(type)                                  \
+  (type == SILC_TASK_FD ? schedule->fd_queue :                         \
+   type == SILC_TASK_TIMEOUT ? schedule->timeout_queue :               \
+   schedule->generic_queue)
+
+/* SILC Task object. Represents one task in the scheduler. */
+struct SilcTaskStruct {
+  uint32 fd;
+  struct timeval timeout;
+  SilcTaskCallback callback;
+  void *context;
+  bool valid;
+  SilcTaskPriority priority;
+  SilcTaskType type;
+
+  /* Pointers forming doubly linked circular list */
+  struct SilcTaskStruct *next;
+  struct SilcTaskStruct *prev;
+};
+
+/* SILC Task Queue object. The queue holds all the tasks in the scheduler.
+   There are always three task queues in the scheduler. One for non-timeout
+   tasks (fd tasks performing tasks over specified file descriptor), 
+   one for timeout tasks and one for generic tasks. */
+struct SilcTaskQueueStruct {
+  SilcTask task;               /* Pointer to all tasks */
+  struct timeval timeout;      /* Current timeout */
+  SILC_MUTEX_DEFINE(lock);     /* Queue's lock */
+};
+
+/* 
+   SILC Scheduler structure.
+
+   This is the actual schedule object in SILC. Both SILC client and server 
+   uses this same scheduler. Actually, this scheduler could be used by any
+   program needing scheduling.
+
+   Following short description of the fields:
+
+   SilcTaskQueue fd_queue
+
+       Task queue hook for non-timeout tasks. Usually this means that these
+       tasks perform different kind of I/O on file descriptors. File 
+       descriptors are usually network sockets but they actually can be
+       any file descriptors. This hook is initialized in silc_schedule_init
+       function. Timeout tasks should not be added to this queue because
+       they will never expire.
+
+   SilcTaskQueue timeout_queue
+
+       Task queue hook for timeout tasks. This hook is reserved specificly
+       for tasks with timeout. Non-timeout tasks should not be added to this
+       queue because they will never get scheduled. This hook is also
+       initialized in silc_schedule_init function.
+
+   SilcTaskQueue generic_queue
+
+       Task queue hook for generic tasks. This hook is reserved specificly
+       for generic tasks, tasks that apply to all file descriptors, except
+       to those that have specificly registered a non-timeout task. This hook
+       is also initialized in silc_schedule_init function.
+
+   SilcScheduleFd fd_list
+
+       List of file descriptors the scheduler is supposed to be listenning.
+       This is updated internally.
+
+   uint32 max_fd
+   uint32 last_fd
+
+       Size of the fd_list list. There can be `max_fd' many tasks in
+       the scheduler at once. The `last_fd' is the last valid entry
+       in the fd_list.
+
+   struct timeval *timeout;
+
+       Pointer to the schedules next timeout. Value of this timeout is
+       automatically updated in the silc_schedule function.
+
+   bool valid
+
+       Marks validity of the scheduler. This is a boolean value. When this
+       is false the scheduler is terminated and the program will end. This
+       set to true when the scheduler is initialized with silc_schedule_init
+       function.
+
+   fd_set in
+   fd_set out
+
+       File descriptor sets for select(). These are automatically managed
+       by the scheduler and should not be touched otherwise.
+
+   void *wakeup
+
+       System specific wakeup context. On multi-threaded environments the
+       scheduler needs to be wakenup (in the thread) when tasks are added
+       or removed. This is initialized by silc_schedule_wakeup_init.
+
+   SILC_MUTEX_DEFINE(lock)
+  
+       Scheduler lock.
+
+*/
+struct SilcScheduleStruct {
+  SilcTaskQueue fd_queue;
+  SilcTaskQueue timeout_queue;
+  SilcTaskQueue generic_queue;
+  SilcScheduleFd fd_list;
+  uint32 max_fd;
+  uint32 last_fd;
+  struct timeval *timeout;
+  bool valid;
+  void *wakeup;
+  SILC_MUTEX_DEFINE(lock);
+  bool is_locked;
+};
+
+/* Initializes the scheduler. This returns the scheduler context that
+   is given as arugment usually to all silc_schedule_* functions.
+   The `max_tasks' indicates the number of maximum tasks that the
+   scheduler can handle. */
+
+SilcSchedule silc_schedule_init(int max_tasks)
+{
+  SilcSchedule schedule;
+
+  SILC_LOG_DEBUG(("Initializing scheduler"));
+
+  schedule = silc_calloc(1, sizeof(*schedule));
+
+  /* Allocate three task queues, one for file descriptor based tasks,
+     one for timeout tasks and one for generic tasks. */
+  silc_task_queue_alloc(&schedule->fd_queue);
+  silc_task_queue_alloc(&schedule->timeout_queue);
+  silc_task_queue_alloc(&schedule->generic_queue);
+
+  if (!max_tasks)
+    max_tasks = 200;
+
+  /* Initialize the scheduler */
+  schedule->fd_list = silc_calloc(max_tasks, sizeof(*schedule->fd_list));
+  schedule->max_fd = max_tasks;
+  schedule->timeout = NULL;
+  schedule->valid = TRUE;
+
+  /* Allocate scheduler lock */
+  silc_mutex_alloc(&schedule->lock);
+
+  /* Initialize the wakeup, for multi-threads support */
+  schedule->wakeup = silc_schedule_wakeup_init(schedule);
+
+  return schedule;
+}
+
+/* Uninitializes the schedule. This is called when the program is ready
+   to end. This removes all tasks and task queues. Returns FALSE if the
+   scheduler could not be uninitialized. This happens when the scheduler
+   is still valid and silc_schedule_stop has not been called. */
+
+bool silc_schedule_uninit(SilcSchedule schedule)
+{
+  SILC_LOG_DEBUG(("Uninitializing scheduler"));
+
+  if (schedule->valid == TRUE)
+    return FALSE;
+
+  /* Unregister all tasks */
+  silc_schedule_task_remove(schedule->fd_queue, SILC_ALL_TASKS);
+  silc_schedule_task_remove(schedule->timeout_queue, SILC_ALL_TASKS);
+  silc_schedule_task_remove(schedule->generic_queue, SILC_ALL_TASKS);
+
+  /* Unregister all task queues */
+  silc_task_queue_free(schedule->fd_queue);
+  silc_task_queue_free(schedule->timeout_queue);
+  silc_task_queue_free(schedule->generic_queue);
+
+  silc_free(schedule->fd_list);
+
+  /* Uninit the wakeup */
+  silc_schedule_wakeup_uninit(schedule->wakeup);
+
+  silc_mutex_free(schedule->lock);
+
+  return TRUE;
+}
+
+/* Stops the schedule even if it is not supposed to be stopped yet. 
+   After calling this, one should call silc_schedule_uninit (after the 
+   silc_schedule has returned). */
+
+void silc_schedule_stop(SilcSchedule schedule)
+{
+  SILC_LOG_DEBUG(("Stopping scheduler"));
+  schedule->valid = FALSE;
+}
+
+/* Executes nontimeout tasks. It then checks whether any of ther fd tasks
+   was signaled by the silc_select. If some task was not signaled then
+   all generic tasks are executed for that task. The generic tasks are
+   never executed for task that has explicit fd task set. */
+/* This holds the schedule->lock and the queue locks. */
+
+static void silc_schedule_dispatch_nontimeout(SilcSchedule schedule)
+{
+  SilcTask task;
+  int i, last_fd = schedule->last_fd;
+  uint32 fd;
+
+  for (i = 0; i <= last_fd; i++) {
+    if (schedule->fd_list[i].events == 0)
+      continue;
+
+    fd = schedule->fd_list[i].fd;
+
+    /* First check whether this fd has task in the fd queue */
+    silc_mutex_lock(schedule->fd_queue->lock);
+    task = silc_task_find(schedule->fd_queue, fd);
+
+    /* If the task was found then execute its callbacks. If not then
+       execute all generic tasks for that fd. */
+    if (task) {
+      /* Validity of the task is checked always before and after
+        execution beacuse the task might have been unregistered
+        in the callback function, ie. it is not valid anymore. */
+
+      /* Is the task ready for reading */
+      if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
+       silc_mutex_unlock(schedule->fd_queue->lock);
+       silc_mutex_unlock(schedule->lock);
+       task->callback(schedule, SILC_TASK_READ, task->fd, task->context);
+       silc_mutex_lock(schedule->lock);
+       silc_mutex_lock(schedule->fd_queue->lock);
+      }
+
+      /* Is the task ready for writing */
+      if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
+       silc_mutex_unlock(schedule->fd_queue->lock);
+       silc_mutex_unlock(schedule->lock);
+       task->callback(schedule, SILC_TASK_WRITE, task->fd, task->context);
+       silc_mutex_lock(schedule->lock);
+       silc_mutex_lock(schedule->fd_queue->lock);
+      }
+
+      if (!task->valid)
+       silc_schedule_task_remove(schedule->fd_queue, task);
+
+      silc_mutex_unlock(schedule->fd_queue->lock);
+    } else {
+      /* Run generic tasks for this fd. */
+
+      silc_mutex_unlock(schedule->fd_queue->lock);
+
+      silc_mutex_lock(schedule->generic_queue->lock);
+      if (!schedule->generic_queue->task) {
+       silc_mutex_unlock(schedule->generic_queue->lock);
+       continue;
+      }
+
+      task = schedule->generic_queue->task;
+      while(1) {
+       /* Validity of the task is checked always before and after
+          execution beacuse the task might have been unregistered
+          in the callback function, ie. it is not valid anymore. */
+
+       /* Is the task ready for reading */                             
+       if (task->valid && schedule->fd_list[i].revents & SILC_TASK_READ) {
+         silc_mutex_unlock(schedule->generic_queue->lock);
+         silc_mutex_unlock(schedule->lock);
+         task->callback(schedule, SILC_TASK_READ, fd, task->context);
+         silc_mutex_lock(schedule->lock);
+         silc_mutex_lock(schedule->generic_queue->lock);
+       }
+
+       /* Is the task ready for writing */                             
+       if (task->valid && schedule->fd_list[i].revents & SILC_TASK_WRITE) {
+         silc_mutex_unlock(schedule->generic_queue->lock);
+         silc_mutex_unlock(schedule->lock);
+         task->callback(schedule, SILC_TASK_WRITE, fd, task->context);
+         silc_mutex_lock(schedule->lock);
+         silc_mutex_lock(schedule->generic_queue->lock);
+       }
+
+       if (!task->valid) {
+         /* Invalid (unregistered) tasks are removed from the
+            task queue. */
+         if (schedule->generic_queue->task == task->next) {
+           silc_schedule_task_remove(schedule->generic_queue, task);
+           silc_mutex_unlock(schedule->generic_queue->lock);
+           break;
+         }
+
+         task = task->next;
+         silc_schedule_task_remove(schedule->generic_queue, task);
+         continue;
+       }
+
+       /* Break if there isn't more tasks in the queue */
+       if (schedule->generic_queue->task == task->next)
+         break;
+
+       task = task->next;
+      }                        
+
+      silc_mutex_unlock(schedule->generic_queue->lock);
+    }
+  }
+}
+
+/* Executes all tasks whose timeout has expired. The task is removed from
+   the task queue after the callback function has returned. Also, invalid
+   tasks are removed here. We don't have to care about priorities because 
+   tasks are already sorted in their priority order at the registration 
+   phase. */
+/* This holds the schedule->lock and the schedule->timeout_queue->lock */
+
+static void silc_schedule_dispatch_timeout(SilcSchedule schedule)
+{
+  SilcTaskQueue queue = schedule->timeout_queue;
+  SilcTask task;
+  struct timeval curtime;
+
+  SILC_LOG_DEBUG(("Running timeout tasks"));
+
+  silc_gettimeofday(&curtime);
+
+  queue = schedule->timeout_queue;
+  if (queue && queue->task) {
+    task = queue->task;
+
+    /* Walk thorugh all tasks in the particular task queue and run all 
+       the expired tasks. */
+    while(1) {
+      /* Execute the task if the timeout has expired */
+      if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
+        if (task->valid) {
+         silc_mutex_unlock(queue->lock);
+         silc_mutex_unlock(schedule->lock);
+         task->callback(schedule, SILC_TASK_EXPIRE, task->fd, task->context);
+         silc_mutex_lock(schedule->lock);
+         silc_mutex_lock(queue->lock);
+       }
+
+        /* Break if there isn't more tasks in the queue */
+       if (queue->task == task->next) {
+         silc_schedule_task_remove(queue, task);
+         break;
+        }
+
+        task = task->next;
+
+        /* Remove the task from queue */
+        silc_schedule_task_remove(queue, task->prev);
+      } else {
+        /* The timeout hasn't expired, check for next one */
+
+        /* Break if there isn't more tasks in the queue */
+        if (queue->task == task->next)
+          break;
+
+        task = task->next;
+      }
+    }
+  }
+}
+
+/* Calculates next timeout for select(). This is the timeout value
+   when at earliest some of the timeout tasks expire. If this is in the
+   past, they will be run now. */
+/* This holds the schedule->lock and the schedule->timeout_queue->lock */
+
+static void silc_schedule_select_timeout(SilcSchedule schedule)
+{
+  SilcTaskQueue queue = schedule->timeout_queue;
+  SilcTask task;
+  struct timeval curtime;
+
+  /* Get the current time */
+  silc_gettimeofday(&curtime);
+  schedule->timeout = NULL;
+
+  /* First task in the task queue has always the smallest timeout. */
+  task = queue->task;
+  while(1) {
+    if (task && task->valid == TRUE) {
+      /* If the timeout is in past, we will run the task and all other
+        timeout tasks from the past. */
+      if (silc_schedule_task_timeout_compare(&task->timeout, &curtime)) {
+       silc_schedule_dispatch_timeout(schedule);
+                                               
+       /* The task(s) has expired and doesn't exist on the task queue
+          anymore. We continue with new timeout. */
+       queue = schedule->timeout_queue;
+       task = queue->task;
+       if (task == NULL || task->valid == FALSE)
+         break;
+      }
+
+      /* Calculate the next timeout for select() */
+      queue->timeout.tv_sec = task->timeout.tv_sec - curtime.tv_sec;
+      queue->timeout.tv_usec = task->timeout.tv_usec - curtime.tv_usec;
+      if (queue->timeout.tv_sec < 0)
+       queue->timeout.tv_sec = 0;
+
+      /* We wouldn't want to go under zero, check for it. */
+      if (queue->timeout.tv_usec < 0) {
+       queue->timeout.tv_sec -= 1;
+       if (queue->timeout.tv_sec < 0)
+         queue->timeout.tv_sec = 0;
+       queue->timeout.tv_usec += 1000000L;
+      }
+
+      /* We've got the timeout value */
+      break;
+    } else {
+      /* Task is not valid, remove it and try next one. */
+      silc_schedule_task_remove(queue, task);
+      task = queue->task;
+      if (queue->task == NULL)
+       break;
+    }
+  }
+
+  /* Save the timeout */
+  if (task) {
+    schedule->timeout = &queue->timeout;
+    SILC_LOG_DEBUG(("timeout: sec=%d, usec=%d", schedule->timeout->tv_sec,
+                   schedule->timeout->tv_usec));
+  }
+}
+
+/* Runs the scheduler once and then returns. */
+
+bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs)
+{
+  struct timeval timeout;
+  int ret;
+
+  SILC_LOG_DEBUG(("In scheduler loop"));
+
+  if (!schedule->is_locked)
+    silc_mutex_lock(schedule->lock);
+
+  /* If the task queues aren't initialized or we aren't valid anymore
+     we will return */
+  if ((!schedule->fd_queue && !schedule->timeout_queue 
+       && !schedule->generic_queue) || schedule->valid == FALSE) {
+    SILC_LOG_DEBUG(("Scheduler not valid anymore, exiting"));
+    if (!schedule->is_locked)
+      silc_mutex_unlock(schedule->lock);
+    return FALSE;
+  }
+
+  /* Calculate next timeout for silc_select(). This is the timeout value
+     when at earliest some of the timeout tasks expire. */
+  silc_mutex_lock(schedule->timeout_queue->lock);
+  silc_schedule_select_timeout(schedule);
+  silc_mutex_unlock(schedule->timeout_queue->lock);
+
+  if (timeout_usecs >= 0) {
+    timeout.tv_sec = 0;
+    timeout.tv_usec = timeout_usecs;
+    schedule->timeout = &timeout;
+  }
+
+  silc_mutex_unlock(schedule->lock);
+
+  /* This is the main select(). The program blocks here until some
+     of the selected file descriptors change status or the selected
+     timeout expires. */
+  SILC_LOG_DEBUG(("Select"));
+  ret = silc_select(schedule->fd_list, schedule->last_fd + 1, 
+                   schedule->timeout);
+
+  silc_mutex_lock(schedule->lock);
+
+  switch (ret) {
+  case -1:
+    /* Error */
+    if (errno == EINTR)
+      break;
+    SILC_LOG_ERROR(("Error in select(): %s", strerror(errno)));
+    break;
+  case 0:
+    /* Timeout */
+    silc_mutex_lock(schedule->timeout_queue->lock);
+    silc_schedule_dispatch_timeout(schedule);
+    silc_mutex_unlock(schedule->timeout_queue->lock);
+    break;
+  default:
+    /* There is some data available now */
+    SILC_LOG_DEBUG(("Running non-timeout tasks"));
+    silc_schedule_dispatch_nontimeout(schedule);
+    break;
+  }
+
+  if (!schedule->is_locked)
+    silc_mutex_unlock(schedule->lock);
+
+  return TRUE;
+}
+
+/* The SILC scheduler. This is actually the main routine in SILC programs.
+   When this returns the program is to be ended. Before this function can
+   be called, one must call silc_schedule_init function. */
+
+void silc_schedule(SilcSchedule schedule)
+{
+  SILC_LOG_DEBUG(("Running scheduler"));
+
+  if (schedule->valid == FALSE) {
+    SILC_LOG_ERROR(("Scheduler is not valid, stopping"));
+    return;
+  }
+
+  silc_mutex_lock(schedule->lock);
+  schedule->is_locked = TRUE;
+
+  /* Start the scheduler loop */
+  while (silc_schedule_one(schedule, -1)) 
+    ;
+
+  silc_mutex_unlock(schedule->lock);
+}
+
+/* Wakes up the scheduler. This is used only in multi-threaded
+   environments where threads may add new tasks or remove old tasks
+   from task queues. This is called to wake up the scheduler in the
+   main thread so that it detects the changes in the task queues.
+   If threads support is not compiled in this function has no effect.
+   Implementation of this function is platform specific. */
+
+void silc_schedule_wakeup(SilcSchedule schedule)
+{
+#ifdef SILC_THREADS
+  SILC_LOG_DEBUG(("Wakeup scheduler"));
+  silc_mutex_lock(schedule->lock);
+  silc_schedule_wakeup_internal(schedule->wakeup);
+  silc_mutex_unlock(schedule->lock);
+#endif
+}
+
+/* Add new task to the scheduler */
+
+SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
+                               SilcTaskCallback callback, void *context, 
+                               long seconds, long useconds, 
+                               SilcTaskType type, 
+                               SilcTaskPriority priority)
+{
+  SilcTask newtask;
+  SilcTaskQueue queue;
+  int timeout = FALSE;
+
+  SILC_LOG_DEBUG(("Registering new task, fd=%d type=%d priority=%d", fd, 
+                 type, priority));
+
+  queue = SILC_SCHEDULE_GET_QUEUE(type);
+    
+  /* If the task is generic task, we check whether this task has already
+     been registered. Generic tasks are registered only once and after that
+     the same task applies to all file descriptors to be registered. */
+  if (type == SILC_TASK_GENERIC) {
+    silc_mutex_lock(queue->lock);
+
+    if (queue->task) {
+      SilcTask task = queue->task;
+      while(1) {
+       if ((task->callback == callback) && (task->context == context)) {
+         SILC_LOG_DEBUG(("Found matching generic task, using the match"));
+         
+         silc_mutex_unlock(queue->lock);
+
+         /* Add the fd to be listened, the task found now applies to this
+            fd as well. */
+         silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
+         return task;
+       }
+       
+       if (queue->task == task->next)
+         break;
+       
+       task = task->next;
+      }
+    }
+
+    silc_mutex_unlock(queue->lock);
+  }
+
+  newtask = silc_calloc(1, sizeof(*newtask));
+  newtask->fd = fd;
+  newtask->context = context;
+  newtask->callback = callback;
+  newtask->valid = TRUE;
+  newtask->priority = priority;
+  newtask->type = type;
+  newtask->next = newtask;
+  newtask->prev = newtask;
+
+  /* Create timeout if marked to be timeout task */
+  if (((seconds + useconds) > 0) && (type == SILC_TASK_TIMEOUT)) {
+    silc_gettimeofday(&newtask->timeout);
+    newtask->timeout.tv_sec += seconds + (useconds / 1000000L);
+    newtask->timeout.tv_usec += (useconds % 1000000L);
+    if (newtask->timeout.tv_usec > 999999L) {
+      newtask->timeout.tv_sec += 1;
+      newtask->timeout.tv_usec -= 1000000L;
+    }
+    timeout = TRUE;
+  }
+
+  /* If the task is non-timeout task we have to tell the scheduler that we
+     would like to have these tasks scheduled at some odd distant future. */
+  if (type != SILC_TASK_TIMEOUT)
+    silc_schedule_set_listen_fd(schedule, fd, SILC_TASK_READ);
+
+  silc_mutex_lock(queue->lock);
+
+  /* Is this first task of the queue? */
+  if (queue->task == NULL) {
+    queue->task = newtask;
+    silc_mutex_unlock(queue->lock);
+    return newtask;
+  }
+
+  if (timeout)
+    newtask = silc_task_add_timeout(queue, newtask, priority);
+  else
+    newtask = silc_task_add(queue, newtask, priority);
+
+  silc_mutex_unlock(queue->lock);
+
+  return newtask;
+}
+
+/* Removes a task from the scheduler */
+
+void silc_schedule_task_del(SilcSchedule schedule, SilcTask task)
+{
+  SilcTaskQueue queue = SILC_SCHEDULE_GET_QUEUE(task->type);
+
+  /* Unregister all tasks */
+  if (task == SILC_ALL_TASKS) {
+    SilcTask next;
+    SILC_LOG_DEBUG(("Unregistering all tasks at once"));
+
+    silc_mutex_lock(queue->lock);
+
+    if (!queue->task) {
+      silc_mutex_unlock(queue->lock);
+      return;
+    }
+
+    next = queue->task;
+    
+    while(1) {
+      if (next->valid)
+       next->valid = FALSE;
+      if (queue->task == next->next)
+       break;
+      next = next->next;
+    }
+
+    silc_mutex_unlock(queue->lock);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Unregistering task"));
+
+  silc_mutex_lock(queue->lock);
+
+  /* Unregister the specific task */
+  if (task->valid)
+    task->valid = FALSE;
+
+  silc_mutex_unlock(queue->lock);
+}
+
+/* Remove task by fd */
+
+void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd)
+{
+  SILC_LOG_DEBUG(("Unregister task by fd %d", fd));
+
+  silc_task_del_by_fd(schedule->timeout_queue, fd);
+  silc_task_del_by_fd(schedule->fd_queue, fd);
+}
+
+/* Remove task by task callback. */
+
+void silc_schedule_task_del_by_callback(SilcSchedule schedule,
+                                       SilcTaskCallback callback)
+{
+  SILC_LOG_DEBUG(("Unregister task by callback"));
+
+  silc_task_del_by_callback(schedule->timeout_queue, callback);
+  silc_task_del_by_callback(schedule->fd_queue, callback);
+  silc_task_del_by_callback(schedule->generic_queue, callback);
+}
+
+/* Remove task by context. */
+
+void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context)
+{
+  SILC_LOG_DEBUG(("Unregister task by context"));
+
+  silc_task_del_by_context(schedule->timeout_queue, context);
+  silc_task_del_by_context(schedule->fd_queue, context);
+  silc_task_del_by_context(schedule->generic_queue, context);
+}
+
+/* Sets a file descriptor to be listened by select() in scheduler. One can
+   call this directly if wanted. This can be called multiple times for
+   one file descriptor to set different iomasks. */
+
+void silc_schedule_set_listen_fd(SilcSchedule schedule,
+                                uint32 fd, SilcTaskEvent iomask)
+{
+  int i;
+  bool found = FALSE;
+
+  silc_mutex_lock(schedule->lock);
+
+  for (i = 0; i < schedule->max_fd; i++)
+    if (schedule->fd_list[i].fd == fd) {
+      schedule->fd_list[i].fd = fd;
+      schedule->fd_list[i].events = iomask;
+      if (i > schedule->last_fd)
+       schedule->last_fd = i;
+      found = TRUE;
+      break;
+    }
+
+  if (!found)
+    for (i = 0; i < schedule->max_fd; i++)
+      if (schedule->fd_list[i].events == 0) {
+       schedule->fd_list[i].fd = fd;
+       schedule->fd_list[i].events = iomask;
+       if (i > schedule->last_fd)
+         schedule->last_fd = i;
+       break;
+      }
+
+  silc_mutex_unlock(schedule->lock);
+}
+
+/* Removes a file descriptor from listen list. */
+
+void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd)
+{
+  int i;
+
+  silc_mutex_lock(schedule->lock);
+
+  SILC_LOG_DEBUG(("Unset listen fd %d", fd));
+
+  for (i = 0; i < schedule->max_fd; i++)
+    if (schedule->fd_list[i].fd == fd) {
+      schedule->fd_list[i].fd = 0;
+      schedule->fd_list[i].events = 0;
+      if (schedule->last_fd == i)
+       schedule->last_fd = schedule->max_fd - 1;
+      break;
+    }
+
+  silc_mutex_unlock(schedule->lock);
+}
+
+/* Allocates a newtask task queue into the scheduler */
+
+static void silc_task_queue_alloc(SilcTaskQueue *queue)
+{
+  *queue = silc_calloc(1, sizeof(**queue));
+  silc_mutex_alloc(&(*queue)->lock);
+}
+
+/* Free's a task queue. */
+
+static void silc_task_queue_free(SilcTaskQueue queue)
+{
+  silc_mutex_free(queue->lock);
+  silc_free(queue);
+}
+
+/* Return task by its fd. */
+
+static SilcTask silc_task_find(SilcTaskQueue queue, uint32 fd)
+{
+  SilcTask next;
+
+  if (!queue->task)
+    return NULL;
+
+  next = queue->task;
+
+  while (1) {
+    if (next->fd == fd)
+      return next;
+    if (queue->task == next->next)
+      return NULL;
+    next = next->next;
+  }
+
+  return NULL;
+}
+
+/* Adds a non-timeout task into the task queue. This function is used
+   by silc_task_register function. Returns a pointer to the registered 
+   task. */
+
+static SilcTask silc_task_add(SilcTaskQueue queue, SilcTask newtask, 
+                             SilcTaskPriority priority)
+{
+  SilcTask task, next, prev;
+
+  /* Take the first task in the queue */
+  task = queue->task;
+
+  switch(priority) {
+  case SILC_TASK_PRI_LOW:
+    /* Lowest priority. The task is added at the end of the list. */
+    prev = task->prev;
+    newtask->prev = prev;
+    newtask->next = task;
+    prev->next = newtask;
+    task->prev = newtask;
+    break;
+  case SILC_TASK_PRI_NORMAL:
+    /* Normal priority. The task is added before lower priority tasks
+       but after tasks with higher priority. */
+    prev = task->prev;
+    while(prev != task) {
+      if (prev->priority > SILC_TASK_PRI_LOW)
+       break;
+      prev = prev->prev;
+    }
+    if (prev == task) {
+      /* There are only lower priorities in the list, we will
+        sit before them and become the first task in the queue. */
+      prev = task->prev;
+      newtask->prev = prev;
+      newtask->next = task;
+      task->prev = newtask;
+      prev->next = newtask;
+
+      /* We are now the first task in queue */
+      queue->task = newtask;
+    } else {
+      /* Found a spot from the list, add the task to the list. */
+      next = prev->next;
+      newtask->prev = prev;
+      newtask->next = next;
+      prev->next = newtask;
+      next->prev = newtask;
+    }
+    break;
+  default:
+    silc_free(newtask);
+    return NULL;
+  }
+
+  return newtask;
+}
+
+/* Return the timeout task with smallest timeout. */
+
+static SilcTask silc_task_get_first(SilcTaskQueue queue, SilcTask first)
+{
+  SilcTask prev, task;
+
+  prev = first->prev;
+
+  if (first == prev)
+    return first;
+
+  task = first;
+  while (1) {
+    if (first == prev)
+      break;
+
+    if (silc_schedule_task_timeout_compare(&prev->timeout, &task->timeout))
+      task = prev;
+
+    prev = prev->prev;
+  }
+
+  return task;
+}
+
+/* Adds a timeout task into the task queue. This function is used by
+   silc_task_register function. Returns a pointer to the registered 
+   task. Timeout tasks are sorted by their timeout value in ascending
+   order. The priority matters if there are more than one task with
+   same timeout. */
+
+static SilcTask silc_task_add_timeout(SilcTaskQueue queue, SilcTask newtask,
+                                     SilcTaskPriority priority)
+{
+  SilcTask task, prev, next;
+
+  /* Take the first task in the queue */
+  task = queue->task;
+
+  /* Take last task from the list */
+  prev = task->prev;
+    
+  switch(priority) {
+  case SILC_TASK_PRI_LOW:
+    /* Lowest priority. The task is added at the end of the list. */
+    while(prev != task) {
+
+      /* If we have longer timeout than with the task head of us
+        we have found our spot. */
+      if (silc_schedule_task_timeout_compare(&prev->timeout, 
+                                            &newtask->timeout))
+       break;
+
+      /* If we are equal size of timeout we will be after it. */
+      if (!silc_schedule_task_timeout_compare(&newtask->timeout, 
+                                             &prev->timeout))
+       break;
+
+      /* We have shorter timeout, compare to next one. */
+      prev = prev->prev;
+    }
+    /* Found a spot from the list, add the task to the list. */
+    next = prev->next;
+    newtask->prev = prev;
+    newtask->next = next;
+    prev->next = newtask;
+    next->prev = newtask;
+    
+    if (prev == task) {
+      /* Check if we are going to be the first task in the queue */
+      if (silc_schedule_task_timeout_compare(&prev->timeout, 
+                                            &newtask->timeout))
+       break;
+      if (!silc_schedule_task_timeout_compare(&newtask->timeout, 
+                                             &prev->timeout))
+       break;
+
+      /* We are now the first task in queue */
+      queue->task = newtask;
+    }
+    break;
+  case SILC_TASK_PRI_NORMAL:
+    /* Normal priority. The task is added before lower priority tasks
+       but after tasks with higher priority. */
+    while(prev != task) {
+
+      /* If we have longer timeout than with the task head of us
+        we have found our spot. */
+      if (silc_schedule_task_timeout_compare(&prev->timeout, 
+                                            &newtask->timeout))
+       break;
+
+      /* If we are equal size of timeout, priority kicks in place. */
+      if (!silc_schedule_task_timeout_compare(&newtask->timeout, 
+                                             &prev->timeout))
+       if (prev->priority >= SILC_TASK_PRI_NORMAL)
+         break;
+
+      /* We have shorter timeout or higher priority, compare to next one. */
+      prev = prev->prev;
+    }
+    /* Found a spot from the list, add the task to the list. */
+    next = prev->next;
+    newtask->prev = prev;
+    newtask->next = next;
+    prev->next = newtask;
+    next->prev = newtask;
+    
+    if (prev == task) {
+      /* Check if we are going to be the first task in the queue */
+      if (silc_schedule_task_timeout_compare(&prev->timeout, 
+                                            &newtask->timeout))
+       break;
+      if (!silc_schedule_task_timeout_compare(&newtask->timeout, 
+                                             &prev->timeout))
+       if (prev->priority >= SILC_TASK_PRI_NORMAL)
+         break;
+
+      /* We are now the first task in queue */
+      queue->task = newtask;
+    }
+    break;
+  default:
+    silc_free(newtask);
+    return NULL;
+  }
+
+  return newtask;
+}
+
+/* Removes (unregisters) a task from particular task queue. This function
+   is used internally by scheduler. This must be called holding the 
+   queue->lock. */
+
+static int silc_schedule_task_remove(SilcTaskQueue queue, SilcTask task)
+{
+  SilcTask first, old, next;
+
+  if (!queue || !task)
+    return FALSE;
+
+  if (!queue->task) {
+    return FALSE;
+  }
+
+  first = queue->task;
+
+  /* Unregister all tasks in queue */
+  if (task == SILC_ALL_TASKS) {
+    SILC_LOG_DEBUG(("Removing all tasks at once"));
+    next = first;
+
+    while(1) {
+      next = next->next;
+      silc_free(next->prev);
+      if (next == first)
+       break;
+    }
+
+    queue->task = NULL;
+    return TRUE;
+  }
+
+  SILC_LOG_DEBUG(("Removing task"));
+
+  /* Unregister the task */
+  old = first;
+  while(1) {
+    if (old == task) {
+      SilcTask prev, next;
+
+      prev = old->prev;
+      next = old->next;
+      prev->next = next;
+      next->prev = prev;
+
+      if (prev == old && next == old)
+       queue->task = NULL;
+      if (queue->task == old)
+       queue->task = silc_task_get_first(queue, next);
+      
+      silc_free(old);
+      return TRUE;
+    }
+    old = old->prev;
+
+    if (old == first) {
+      return FALSE;
+    }
+  }
+}
+
+/* Compare two time values. If the first argument is smaller than the
+   second this function returns TRUE. */
+
+static int silc_schedule_task_timeout_compare(struct timeval *smaller, 
+                                             struct timeval *bigger)
+{
+  if ((smaller->tv_sec < bigger->tv_sec) ||
+      ((smaller->tv_sec == bigger->tv_sec) &&
+       (smaller->tv_usec < bigger->tv_usec)))
+    return TRUE;
+
+  return FALSE;
+}
+
+static void silc_task_del_by_fd(SilcTaskQueue queue, uint32 fd)
+{
+  SilcTask next;
+
+  silc_mutex_lock(queue->lock);
+
+  if (!queue->task) {
+    silc_mutex_unlock(queue->lock);
+    return;
+  }
+
+  next = queue->task;
+
+  while(1) {
+    if (next->fd == fd)
+      next->valid = FALSE;
+    if (queue->task == next->next)
+      break;
+    next = next->next;
+  }
+
+  silc_mutex_unlock(queue->lock);
+}
+
+static void silc_task_del_by_callback(SilcTaskQueue queue,
+                                     SilcTaskCallback callback)
+{
+  SilcTask next;
+
+  silc_mutex_lock(queue->lock);
+
+  if (!queue->task) {
+    silc_mutex_unlock(queue->lock);
+    return;
+  }
+
+  next = queue->task;
+
+  while(1) {
+    if (next->callback == callback)
+      next->valid = FALSE;
+    if (queue->task == next->next)
+      break;
+    next = next->next;
+  }
+
+  silc_mutex_unlock(queue->lock);
+}
+
+static void silc_task_del_by_context(SilcTaskQueue queue, void *context)
+{
+  SilcTask next;
+
+  silc_mutex_lock(queue->lock);
+
+  if (!queue->task) {
+    silc_mutex_unlock(queue->lock);
+    return;
+  }
+
+  next = queue->task;
+
+  while(1) {
+    if (next->context == context)
+      next->valid = FALSE;
+    if (queue->task == next->next)
+      break;
+    next = next->next;
+  }
+
+  silc_mutex_unlock(queue->lock);
+}
diff --git a/lib/silcutil/silcschedule.h b/lib/silcutil/silcschedule.h
new file mode 100644 (file)
index 0000000..45a5def
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+  
+  silcschedule.h
+  COPYRIGHT
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 1998 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/****h* silcutil/SilcScheduleAPI
+ *
+ * DESCRIPTION
+ *
+ * The SILC Scheduler is the heart of any application. The scheduler provides
+ * the application's main loop that can handle incoming data, outgoing data,
+ * timeouts and dispatch different kind of tasks.
+ *
+ * The SILC Scheduler supports file descriptor based tasks, timeout tasks
+ * and generic tasks. File descriptor tasks are tasks that perform some 
+ * operation over the specified file descriptor. These include network 
+ * connections, for example. The timeout tasks are timeouts that are executed
+ * after the specified timeout has elapsed. The generic tasks are tasks that
+ * apply to all registered file descriptors thus providing one task that
+ * applies to many independent connections.
+ *
+ * The SILC Scheduler is designed to be the sole main loop of the application
+ * so that the application does not need any other main loop.  However,
+ * SILC Scheduler does support running the scheduler only once, so that the
+ * scheduler does not block, and thus providing a possiblity that some
+ * external main loop is run over the SILC Scheduler. However, these 
+ * applications are considered to be special cases.
+ *
+ * Typical application first initializes the scheduler and then registers
+ * the very first tasks to the scheduler and then run the scheduler.  After
+ * the scheduler's run function returns the application is considered to be 
+ * ended.
+ *
+ * On WIN32 systems the SILC Scheduler is too designed to work as the main
+ * loop of the GUI application. It can handle all Windows messages and
+ * it dispatches them from the scheduler, and thus makes it possible to
+ * create GUI applications. The scheduler can also handle all kinds of
+ * WIN32 handles, this includes sockets created by the SILC Net API routines,
+ * WSAEVENT handle objects created by Winsock2 routines and arbitrary 
+ * WIN32 HANDLE objects.
+ *
+ * The SILC Scheduler supports multi-threads as well. The actual scheduler
+ * must be run in single-thread but other threads may register new tasks
+ * and unregister old tasks.  However, it is enforced that the actual
+ * task is always run in the main thread.  The scheduler is context based
+ * which makes it possible to allocate several schedulers for one application.
+ * Since the scheduler must be run in single-thread, a multi-threaded
+ * application could be created by allocating own scheduler for each of the
+ * worker threads.
+ *
+ ***/
+
+#ifndef SILCSCHEDULE_H
+#define SILCSCHEDULE_H
+
+/****s* silcutil/SilcScheduleAPI/SilcSchedule
+ *
+ * NAME
+ * 
+ *    typedef struct SilcScheduleStruct *SilcSchedule;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual Scheduler and is allocated by
+ *    the silc_schedule_init funtion.  The context is given as argument
+ *    to all silc_schedule_* functions.  It must be freed by the 
+ *    silc_schedule_uninit function.
+ *
+ ***/
+typedef struct SilcScheduleStruct *SilcSchedule;
+
+/****s* silcutil/SilcScheduleAPI/SilcTask
+ *
+ * NAME
+ * 
+ *    typedef struct SilcTaskStruct *SilcTask;
+ *
+ * DESCRIPTION
+ *
+ *    This object represents one task in the scheduler.  It is allocated
+ *    by the silc_schedule_task_add function and freed by one of the
+ *    silc_schedule_task_del* functions.
+ *
+ ***/
+typedef struct SilcTaskStruct *SilcTask;
+
+/****d* silcutil/SilcScheduleAPI/SilcTaskType
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcTaskType;
+ *
+ * DESCRIPTION
+ *
+ *    SILC has three types of tasks, non-timeout tasks (tasks that perform
+ *    over file descriptors), timeout tasks and generic tasks (tasks that
+ *    apply to every file descriptor). This type is sent as argument for the 
+ *    task registering function, silc_schedule_task_add.
+ *
+ * SOURCE
+ */
+typedef enum {
+  /* File descriptor task that performs some event over file descriptors.
+     These tasks are for example network connections. */
+  SILC_TASK_FD,
+  
+  /* Timeout tasks are tasks that are executed after the specified 
+     time has elapsed. After the task is executed the task is removed
+     automatically from the scheduler. It is safe to re-register the
+     task in task callback. It is also safe to unregister a task in
+     the task callback. */
+  SILC_TASK_TIMEOUT,
+
+  /* Generic tasks are non-timeout tasks and they apply to all file 
+     descriptors, except to those that have explicitly registered a 
+     non-timeout task. These tasks are there to make it simpler and faster 
+     to execute common code that applies to all connections. These are,
+     for example, receiving packets from network and sending packets to
+     network. It doesn't make much sense to register a task that receives
+     a packet from network to every connection when you can have one task
+     that applies to all connections. This is what generic tasks are for.
+     Generic tasks are not bound to any specific file descriptor, however,
+     the correct file descriptor must be passed as argument to task
+     registering function. */
+  SILC_TASK_GENERIC,
+} SilcTaskType;
+/***/
+
+/****d* silcutil/SilcScheduleAPI/SilcTaskEvent
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcTaskEvent;
+ *
+ * DESCRIPTION
+ *
+ *    SILC Task event types.  The event type indicates the occurred
+ *    event of the task.  This type will be given as argument to the
+ *    SilcTaskCallback function to indicate the event for the caller.
+ *    The SILC_TASK_READ and SILC_TASK_WRITE may be set by the caller
+ *    of the silc_schedule_set_listen_fd, if the caller needs to control
+ *    the events for the task. The SILC_TASK_EXPIRE is set always only
+ *    by the scheduler when timeout expires for timeout task.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_TASK_READ      = 0x0001,                 /* Reading */
+  SILC_TASK_WRITE     = 0x0002,                 /* Writing */
+  SILC_TASK_EXPIRE    = 0x0004,                 /* Timeout */
+} SilcTaskEvent;
+/***/
+
+/****d* silcutil/SilcScheduleAPI/SilcTaskPriority
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcTaskPriority;
+ *
+ * DESCRIPTION
+ *
+ *    Task priorities. Tasks may be registered with different priorities.
+ *    This type defines the different task priorities. The priorities
+ *    behaves same for all type of tasks, fd tasks, timeout tasks and
+ *    generic tasks.
+ *
+ * SOURCE
+ */
+typedef enum {
+  /* Lowest priority. The task is scheduled to run after its timeout
+     has expired only and only when every other task with higher priority 
+     has already been run. For non-timeout tasks this priority behaves
+     same way. Life is not fair for tasks with this priority. */
+  SILC_TASK_PRI_LOW,
+
+  /* Normal priority that is used mostly in SILC. This is priority that
+     should always be used unless you specificly need some other priority.
+     The scheduler will run this task as soon as its timeout has expired.
+     For non-timeout tasks this priority behaves same way. Tasks are run 
+     in FIFO (First-In-First-Out) order. */
+  SILC_TASK_PRI_NORMAL,
+} SilcTaskPriority;
+/***/
+
+/****f* silcutil/SilcScheduleAPI/SilcTaskCallback
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcTaskCallback)(SilcSchedule schedule, 
+ *                                     SilcTaskEvent type, uint32 fd, 
+ *                                     void *context);
+ *
+ * DESCRIPTION
+ *
+ *    The task callback function.  This function will be called by the
+ *    scheduler when some event of the task is performed.  For example,
+ *    when data is available from the connection this will be called.
+ *
+ *    The `schedule' is the scheduler context, the `type' is the indicated
+ *    event, the `fd' is the file descriptor of the task and the `context'
+ *    is a caller specified context. If multiple events occurred this
+ *    callback is called separately for all events.
+ *
+ *    To specify task callback function in the application using the
+ *    SILC_TASK_CALLBACK and SILC_TASK_CALLBACK_GLOBAL macros is
+ *    recommended.
+ *
+ ***/
+typedef void (*SilcTaskCallback)(SilcSchedule schedule, SilcTaskEvent type,
+                                uint32 fd, void *context);
+
+/* Macros */
+
+/****d* silcutil/SilcScheduleAPI/SILC_ALL_TASKS
+ *
+ * NAME
+ * 
+ *    #define SILC_ALL_TASKS ...
+ *
+ * DESCRIPTION
+ *
+ *    Marks for all tasks in the scheduler. This can be passed to 
+ *    silc_schedule_task_del function to delete all tasks at once.
+ *
+ * SOURCE
+ */
+#define SILC_ALL_TASKS ((SilcTask)1)
+/***/
+
+/****d* silcutil/SilcScheduleAPI/SILC_TASK_CALLBACK
+ *
+ * NAME
+ * 
+ *    #define SILC_TASK_CALLBACK ...
+ *
+ * DESCRIPTION
+ *
+ *    Generic macro to define task callback functions. This defines a
+ *    static function with name `func' as a task callback function.
+ *
+ * SOURCE
+ */
+#define SILC_TASK_CALLBACK(func)                               \
+static void func(SilcSchedule schedule, SilcTaskEvent type,    \
+                uint32 fd, void *context)
+/***/
+
+/****d* silcutil/SilcScheduleAPI/SILC_TASK_CALLBACK_GLOBAL
+ *
+ * NAME
+ * 
+ *    #define SILC_TASK_CALLBACK_GLOBAL ...
+ *
+ * DESCRIPTION
+ *
+ *    Generic macro to define task callback functions. This defines a
+ *    function with name `func' as a task callback function.  This
+ *    differs from SILC_TASK_CALLBACK in that the defined function is
+ *    not static.
+ *
+ * SOURCE
+ */
+#define SILC_TASK_CALLBACK_GLOBAL(func)                        \
+void func(SilcSchedule schedule, SilcTaskEvent type,   \
+         uint32 fd, void *context)
+/***/
+
+/* Prototypes */
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_init
+ *
+ * SYNOPSIS
+ *
+ *    SilcSchedule silc_schedule_init(int max_tasks);
+ *
+ * DESCRIPTION
+ *
+ *    Initializes the scheduler. This returns the scheduler context that
+ *    is given as argument usually to all silc_schedule_* functions.
+ *    The `max_tasks' indicates the number of maximum tasks that the
+ *    scheduler can handle.
+ *
+ ***/
+SilcSchedule silc_schedule_init(int max_tasks);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_uninit
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_schedule_uninit(SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Uninitializes the scheduler. This is called when the program is ready
+ *    to end. This removes all tasks from the scheduler. Returns FALSE if the
+ *    scheduler could not be uninitialized. This happens when the scheduler
+ *    is still valid and silc_schedule_stop has not been called.
+ *
+ ***/
+bool silc_schedule_uninit(SilcSchedule schedule);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_stop
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_stop(SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Stops the scheduler even if it is not supposed to be stopped yet. 
+ *    After calling this, one must call silc_schedule_uninit (after the 
+ *    silc_schedule has returned).  After this is called it is guaranteed
+ *    that next time the scheduler enters the main loop it will be stopped.
+ *    However, untill it enters the main loop it will not detect that
+ *    it is stopped for example if this is called from another thread.
+ *
+ ***/
+void silc_schedule_stop(SilcSchedule schedule);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule(SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    The SILC scheduler. This is actually the main routine in SILC programs.
+ *    When this returns the program is to be ended. Before this function can
+ *    be called, one must call silc_schedule_init function.
+ *
+ ***/
+void silc_schedule(SilcSchedule schedule);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_one
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_schedule_one(SilcSchedule schedule, int block);
+ *
+ * DESCRIPTION
+ *
+ *    Same as the silc_schedule but runs the scheduler only one round
+ *    and then returns.  This function is handy when the SILC scheduler
+ *    is used inside some other external scheduler, for example.  If
+ *    the `timeout_usecs' is non-negative a timeout will be added to the
+ *    scheduler.  The function will not return in this timeout unless
+ *    some other event occurs.
+ *
+ ***/
+bool silc_schedule_one(SilcSchedule schedule, int timeout_usecs);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_wakeup
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_wakeup(SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Wakes up the scheduler. This is used only in multi-threaded
+ *    environments where threads may add new tasks or remove old tasks
+ *    from the scheduler. This is called to wake up the scheduler in the
+ *    main thread so that it detects the changes in the scheduler.
+ *    If threads support is not compiled in this function has no effect.
+ *    Implementation of this function may be platform specific.
+ *
+ ***/
+void silc_schedule_wakeup(SilcSchedule schedule);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_add
+ *
+ * SYNOPSIS
+ *
+ *    SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
+ *                                    SilcTaskCallback callback, 
+ *                                    void *context, 
+ *                                    long seconds, long useconds, 
+ *                                    SilcTaskType type, 
+ *                                    SilcTaskPriority priority);
+ *
+ * DESCRIPTION
+ *
+ *    Registers a new task to the scheduler. This same function is used
+ *    to register all types of tasks. The `type' argument tells what type
+ *    of the task is. Note that when registering non-timeout tasks one
+ *    should also pass 0 as timeout, as the timeout will be ignored anyway. 
+ *    Also, note, that one cannot register timeout task with 0 timeout.
+ *    There cannot be zero timeouts, passing zero means no timeout is used
+ *    for the task and SILC_TASK_FD is used as default task type in
+ *    this case.
+ *
+ *    The `schedule' is the scheduler context. The `fd' is the file
+ *    descriptor of the task. On WIN32 systems the `fd' is not actual
+ *    file descriptor but some WIN32 event handle. On WIN32 system the `fd'
+ *    may be a socket created by the SILC Net API routines, WSAEVENT object
+ *    created by Winsock2 network routines or arbitrary WIN32 HANDLE object.
+ *    On Unix systems the `fd' is always the real file descriptor.
+ *
+ *    The `callback' is the task callback that will be called when some
+ *    event occurs for this task. The `context' is sent as argument to
+ *    the task `callback' function. For timeout tasks the callback is
+ *    called after the specified timeout has elapsed.
+ *
+ *    If the `type' is SILC_TASK_TIMEOUT then `seconds' and `useconds'
+ *    may be non-zero.  Otherwise they should be zero. The `priority'
+ *    indicates the priority of the task.
+ *
+ *    It is always safe to call this function in any place. New tasks
+ *    may be added also in task callbacks, and in multi-threaded environment
+ *    in other threads as well.
+ *   
+ ***/
+SilcTask silc_schedule_task_add(SilcSchedule schedule, uint32 fd,
+                               SilcTaskCallback callback, void *context, 
+                               long seconds, long useconds, 
+                               SilcTaskType type, 
+                               SilcTaskPriority priority);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_del
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_task_del(SilcSchedule schedule, SilcTask task);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes the `task' from the scheduler indicated by the `schedule'.
+ *    After deleting the task it is guaranteed that the task callback
+ *    will not be called. If the `task' is SILC_ALL_TASKS then all
+ *    tasks is removed from the scheduler.
+ *
+ *    It is safe to call this function in any place. Tasks may be removed
+ *    in task callbacks (including in the task's own task callback) and
+ *    in multi-threaded environment in other threads as well.
+ *
+ ***/
+void silc_schedule_task_del(SilcSchedule schedule, SilcTask task);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_fd
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes a task from the scheduler by the specified `fd'.
+ *
+ *    It is safe to call this function in any place. Tasks may be removed
+ *    in task callbacks (including in the task's own task callback) and
+ *    in multi-threaded environment in other threads as well.
+ *
+ *    Note that generic tasks cannot be deleted using this function
+ *    since generic tasks does not match any specific fd.
+ *
+ ***/
+void silc_schedule_task_del_by_fd(SilcSchedule schedule, uint32 fd);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_callback
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_task_del_by_callback(SilcSchedule schedule,
+ *                                            SilcTaskCallback callback);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes a task from the scheduler by the specified `callback' task
+ *    callback function.
+ *
+ *    It is safe to call this function in any place. Tasks may be removed
+ *    in task callbacks (including in the task's own task callback) and
+ *    in multi-threaded environment in other threads as well.
+ *
+ ***/
+void silc_schedule_task_del_by_callback(SilcSchedule schedule,
+                                       SilcTaskCallback callback);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_task_del_by_context
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_task_del_by_context(SilcSchedule schedule, 
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Deletes a task from the scheduler by the specified `context'.
+ *
+ *    It is safe to call this function in any place. Tasks may be removed
+ *    in task callbacks (including in the task's own task callback) and
+ *    in multi-threaded environment in other threads as well.
+ *
+ ***/
+void silc_schedule_task_del_by_context(SilcSchedule schedule, void *context);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_set_listen_fd
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_set_listen_fd(SilcSchedule schedule, uint32 fd,
+ *                                     SilcTaskEvent mask);
+ *
+ * DESCRIPTION
+ *
+ *    Sets a file descriptor `fd' to be listened by the scheduler for
+ *    `mask' events.  To tell scheduler not to listen anymore for this
+ *    file descriptor call the silc_schedule_unset_listen_fd function.
+ *    When new task is created with silc_schedule_task_add the event
+ *    for the task's fd is initially set to SILC_TASK_READ. If you need
+ *    to control the task's fd's events you must call this function
+ *    whenever you need to change the events. This can be called multiple
+ *    times to change the events.
+ *
+ ***/
+void silc_schedule_set_listen_fd(SilcSchedule schedule, uint32 fd,
+                                SilcTaskEvent mask);
+
+/****f* silcutil/SilcScheduleAPI/silc_schedule_unset_listen_fd
+ *
+ * SYNOPSIS
+ *
+ *    void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd);
+ *
+ * DESCRIPTION
+ *
+ *    Tells the scheduler not to listen anymore for the specified
+ *    file descriptor `fd'. No events will be detected for the `fd'
+ *    after calling this function.
+ *
+ ***/
+void silc_schedule_unset_listen_fd(SilcSchedule schedule, uint32 fd);
+
+#endif
diff --git a/lib/silcutil/silcschedule_i.h b/lib/silcutil/silcschedule_i.h
new file mode 100644 (file)
index 0000000..b3a2c92
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+
+  silcschedule_i.h.
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCSCHEDULE_I_H
+#define SILCSCHEDULE_I_H
+
+#include "silcincludes.h"
+
+/* Schedule FD structure. Includes the file descriptors that the scheduler
+   will listen. This is given as argument to the silc_select function. */
+typedef struct {
+  uint32 fd;                   /* The file descriptor (or handle on WIN32) */
+  uint16 events;               /* Mask of task events, if events is 0 then
+                                  the fd must be omitted. */
+  uint16 revents;              /* Returned events mask */
+} *SilcScheduleFd;
+
+#endif
diff --git a/lib/silcutil/silcsockconn.c b/lib/silcutil/silcsockconn.c
new file mode 100644 (file)
index 0000000..e6a81a5
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+
+  silcsockconn.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Heartbeat context */
+struct SilcSocketConnectionHBStruct {
+  uint32 heartbeat;
+  SilcSocketConnectionHBCb hb_callback;
+  void *hb_context;
+  SilcSchedule schedule;
+  SilcTask hb_task;
+  SilcSocketConnection sock;
+};
+
+/* Internal async host lookup context. */
+typedef struct {
+  SilcSocketHostLookupCb callback;
+  void *context;
+  SilcSchedule schedule;
+  SilcSocketConnection sock;
+  bool port;
+} *SilcSocketHostLookup;
+
+/* Allocates a new socket connection object. The allocated object is 
+   returned to the new_socket argument. */
+
+void silc_socket_alloc(int sock, SilcSocketType type, void *user_data, 
+                      SilcSocketConnection *new_socket)
+{
+  SILC_LOG_DEBUG(("Allocating new socket connection object"));
+
+  /* Set the pointers. Incoming and outgoing data buffers
+     are allocated by the application when they are first used. */
+  *new_socket = silc_calloc(1, sizeof(**new_socket));
+  (*new_socket)->sock = sock;
+  (*new_socket)->type = type;
+  (*new_socket)->user_data = user_data;
+  (*new_socket)->protocol = NULL;
+  (*new_socket)->flags = 0;
+  (*new_socket)->inbuf = NULL;
+  (*new_socket)->outbuf = NULL;
+  (*new_socket)->users++;
+}
+
+/* Free's the Socket connection object. */
+
+void silc_socket_free(SilcSocketConnection sock)
+{
+  sock->users--;
+  SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users + 1,
+                 sock->users));
+  if (sock->users < 1) {
+    silc_buffer_free(sock->inbuf);
+    silc_buffer_free(sock->outbuf);
+    if (sock->hb) {
+      silc_schedule_task_del(sock->hb->schedule, sock->hb->hb_task);
+      silc_free(sock->hb->hb_context);
+      silc_free(sock->hb);
+    }
+
+    silc_free(sock->ip);
+    silc_free(sock->hostname);
+
+    memset(sock, 'F', sizeof(*sock));
+    silc_free(sock);
+  }
+}
+
+/* Increase the reference counter. */
+
+SilcSocketConnection silc_socket_dup(SilcSocketConnection sock)
+{
+  sock->users++;
+  SILC_LOG_DEBUG(("Socket %p refcnt %d->%d", sock, sock->users - 1,
+                 sock->users));
+  return sock;
+}
+
+/* Internal timeout callback to perform heartbeat */
+
+SILC_TASK_CALLBACK(silc_socket_heartbeat)
+{
+  SilcSocketConnectionHB hb = (SilcSocketConnectionHB)context;
+
+  if (!hb->heartbeat)
+    return;
+
+  if (hb->hb_callback)
+    hb->hb_callback(hb->sock, hb->hb_context);
+
+  hb->hb_task = silc_schedule_task_add(hb->schedule, hb->sock->sock, 
+                                      silc_socket_heartbeat,
+                                      context, hb->heartbeat, 0,
+                                      SILC_TASK_TIMEOUT,
+                                      SILC_TASK_PRI_LOW);
+}
+
+/* Sets the heartbeat timeout and prepares the socket for performing
+   heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
+   allocated by the application and will be sent as argument to the
+   `hb_callback' function that is called when the `heartbeat' timeout
+   expires.  The callback `hb_context' won't be touched by the library
+   but will be freed automatically when calling silc_socket_free.  The
+   `schedule' is the application's scheduler. */
+
+void silc_socket_set_heartbeat(SilcSocketConnection sock, 
+                              uint32 heartbeat,
+                              void *hb_context,
+                              SilcSocketConnectionHBCb hb_callback,
+                              SilcSchedule schedule)
+{
+  if (sock->hb) {
+    silc_schedule_task_del(schedule, sock->hb->hb_task);
+    silc_free(sock->hb->hb_context);
+    silc_free(sock->hb);
+  }
+
+  sock->hb = silc_calloc(1, sizeof(*sock->hb));
+  sock->hb->heartbeat = heartbeat;
+  sock->hb->hb_context = hb_context;
+  sock->hb->hb_callback = hb_callback;
+  sock->hb->schedule = schedule;
+  sock->hb->sock = sock;
+  sock->hb->hb_task = silc_schedule_task_add(schedule, sock->sock,
+                                            silc_socket_heartbeat,
+                                            (void *)sock->hb, heartbeat, 0,
+                                            SILC_TASK_TIMEOUT,
+                                            SILC_TASK_PRI_LOW);
+}
+
+/* Finishing timeout callback that will actually call the user specified
+   host lookup callback. This is executed back in the calling thread and
+   not in the lookup thread. */
+
+SILC_TASK_CALLBACK(silc_socket_host_lookup_finish)
+{
+  SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
+
+  SILC_UNSET_HOST_LOOKUP(lookup->sock);
+
+  /* If the reference counter is 1 we know that we are the only one
+     holding the socket and it thus is considered freed. The lookup
+     is cancelled also and we will not call the final callback. */
+  if (lookup->sock->users == 1) {
+    SILC_LOG_DEBUG(("Async host lookup was cancelled"));
+    silc_free(lookup);
+    silc_socket_free(lookup->sock);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Async host lookup finished"));
+
+  silc_socket_free(lookup->sock);
+
+  /* Call the final callback. */
+  if (lookup->callback)
+    lookup->callback(lookup->sock, lookup->context);
+
+  silc_free(lookup);
+}
+
+/* The thread function that performs the actual lookup. */
+
+static void *silc_socket_host_lookup_start(void *context)
+{
+  SilcSocketHostLookup lookup = (SilcSocketHostLookup)context;
+  SilcSocketConnection sock = lookup->sock;
+
+  if (lookup->port)
+    sock->port = silc_net_get_remote_port(sock->sock);
+
+  silc_net_check_host_by_sock(sock->sock, &sock->hostname, &sock->ip);  
+  if (!sock->hostname && sock->ip)
+    sock->hostname = strdup(sock->ip);
+
+  silc_schedule_task_add(lookup->schedule, sock->sock,
+                        silc_socket_host_lookup_finish, lookup, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  silc_schedule_wakeup(lookup->schedule);
+
+  return NULL;
+}
+
+/* Performs asynchronous host name and IP address lookups for the
+   specified socket connection. This may be called when the socket
+   connection is created and the full IP address and fully qualified
+   domain name information is desired. The `callback' with `context'
+   will be called after the lookup is performed. The `schedule'
+   is the application's scheduler which the lookup routine needs. If
+   the socket connection is freed during the lookup the library will
+   automatically cancel the lookup and the `callback' will not be called. */
+
+void silc_socket_host_lookup(SilcSocketConnection sock,
+                            bool port_lookup,
+                            SilcSocketHostLookupCb callback,
+                            void *context,
+                            SilcSchedule schedule)
+{
+  SilcSocketHostLookup lookup;
+
+  SILC_LOG_DEBUG(("Performing async host lookup"));
+
+  lookup = silc_calloc(1, sizeof(*lookup));
+  lookup->sock = silc_socket_dup(sock);        /* Increase reference counter */
+  lookup->callback = callback;
+  lookup->context = context;
+  lookup->schedule = schedule;
+  lookup->port = port_lookup;
+
+  SILC_SET_HOST_LOOKUP(sock);
+  silc_thread_create(silc_socket_host_lookup_start, lookup, FALSE);
+}
diff --git a/lib/silcutil/silcsockconn.h b/lib/silcutil/silcsockconn.h
new file mode 100644 (file)
index 0000000..ff52501
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+  silcsockconn.h
+  Author: Pekka Riikonen <priikone@silnet.org>
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcSocketConnectionAPI
+ *
+ * DESCRIPTION
+ *
+ * Implementation of the Socket Connection object. The SilcSocketConnection
+ * is used by all applications to represent a socket based connection
+ * to the network. The Socket Connection object handles inbound and outbound
+ * data buffers, can perform keepalive actions for the connection and
+ * supports connection based protocols as well.
+ *
+ ***/
+
+#ifndef SILCSOCKCONN_H
+#define SILCSOCKCONN_H
+
+/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnection
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSocketConnectionStruct *SilcSocketConnection;
+ *
+ * DESCRIPTION
+ *
+ *    This context is forward declaration for the SilcSocketConnectionStruct.
+ *    This is allocated by the silc_socket_alloc and freed by the
+ *    silc_socket_free function. The silc_socket_dup can be used to
+ *    increase the reference counter of the context. The data is freed
+ *    by the silc_socket_free function only after the reference counter
+ *    hits zero.
+ *
+ ***/
+typedef struct SilcSocketConnectionStruct *SilcSocketConnection;
+
+/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionHB
+ *
+ * NAME
+ * 
+ *    typedef struct SilcSocketConnectionHB *SilcSocketConnectionHB;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the heartbeat context for the SilcSockeConnection.
+ *    It is meant to hold the keepalive information for the connection.
+ *    This is allocated internally and freed internally by the 
+ *    interface routines.
+ *
+ ***/
+typedef struct SilcSocketConnectionHBStruct *SilcSocketConnectionHB;
+
+/****d* silcutil/SilcSocketConnectionAPI/SilcSocketType
+ *
+ * NAME
+ * 
+ *    typedef enum { ... } SilcSocketType;
+ *
+ * DESCRIPTION
+ *
+ *    Socket types. These identifies the socket connection. There
+ *    are four different types; unknown, client, server and router.
+ *    Unknown connections are connections that hasn't advanced long
+ *    enough so that we might know which type of connection it is.
+ *    It is the applications responsibility to update the type 
+ *    information when it becomes available.
+ *
+ * SOURCE
+ */
+typedef enum {
+  SILC_SOCKET_TYPE_UNKNOWN = 0,
+  SILC_SOCKET_TYPE_CLIENT = 1,
+  SILC_SOCKET_TYPE_SERVER = 2,
+  SILC_SOCKET_TYPE_ROUTER = 3
+} SilcSocketType;
+/***/
+
+/* Socket flags */
+#define SILC_SF_NONE             0
+#define SILC_SF_INBUF_PENDING    1 /* data in inbound buffer */
+#define SILC_SF_OUTBUF_PENDING   2 /* data in outbound buffer */
+#define SILC_SF_DISCONNECTING    3 /* socket disconnecting */
+#define SILC_SF_DISCONNECTED     4 /* socket disconnected */
+#define SILC_SF_HOST_LOOKUP      5 /* performing host lookup for socket */
+#define SILC_SF_DISABLED         6 /* socket connection is disabled,
+                                     no data is sent or received. */
+
+/****s* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionStruct
+ *
+ * NAME
+ * 
+ *    struct SilcSocketConnectionStruct { ... };
+ *
+ * DESCRIPTION
+ *
+ *    This object holds information about the connected sockets to the server.
+ *    This is quite important object since this is referenced by the server all
+ *    the time when figuring out what the connection is supposed to be doing
+ *    and to whom we should send a message. This structure is the structure
+ *    for the SilcSocketConnection forward declaration.
+ *
+ *    Following short description of the fields:
+ *
+ *    int sock
+ *
+ *      The actual connected socket. This is usually saved when accepting
+ *      new connection to the server.
+ *
+ *    SilcSocketType type
+ *
+ *      Type of the socket. This identifies the type of the connection. This
+ *      is mainly used to identify whether the connection is a client or a
+ *      server connection.
+ *
+ *    void *user_data
+ *
+ *      This is a pointer to a data that is is saved here at the same
+ *      time a new connection object is allocated. Usually this is a 
+ *      back-pointer to some important data for fast referencing. For
+ *      SILC server this is a pointer to the ID list and for SILC client
+ *      to object holding active connections (windows).
+ *
+ *    SilcProtocol protocol
+ *
+ *      Protocol object for the socket. Currently only one protocol can be
+ *      executing at a time for a particular socket.
+ *
+ *    uint32 flags
+ *
+ *      Socket flags that indicate the status of the socket. This can
+ *      indicate several different status that can affect the use of the
+ *      socket object.
+ *
+ *    int users
+ *
+ *      Reference counter. When allocated it is set to one (1) and it won't
+ *      be freed until it hits zero (0).
+ *
+ *    char *hostname
+ *    char *ip
+ *    uint16 port
+ *
+ *      Resolved hostname, IP address and port of the connection who owns
+ *      this object.
+ *
+ *    SilcBuffer inbuf
+ *    SilcBuffer outbuf
+ *
+ *      Incoming and outgoing buffers for the particular socket connection.
+ *      Incoming data from the socket is put after decryption in to the
+ *      inbuf buffer and outgoing data after encryption is put to the outbuf
+ *      buffer.
+ *
+ *    SilcSocketConnectionHB hb
+ *
+ *      The heartbeat context.  If NULL, heartbeat is not performed.
+ *
+ ***/
+struct SilcSocketConnectionStruct {
+  int sock;
+  SilcSocketType type;
+  void *user_data;
+  SilcProtocol protocol;
+  uint32 flags;
+  uint8 sock_error;
+  int users;
+
+  char *hostname;
+  char *ip;
+  uint16 port;
+
+  SilcBuffer inbuf;
+  SilcBuffer outbuf;
+
+  SilcSocketConnectionHB hb;
+};
+
+/* Macros */
+
+/* Amount of bytes to be read from the socket connection at once. */
+#define SILC_SOCKET_READ_SIZE 16384
+
+/* Default socket buffer size. */
+#define SILC_SOCKET_BUF_SIZE 1024
+
+/* Generic manipulation of flags */
+#define SF_SET(x, f) (x)->flags |= (1L << (f))
+#define SF_UNSET(x, f) (x)->flags &= ~(1L << (f))
+#define SF_IS(x, f) ((x)->flags & (1L << (f)))
+
+/* Setting/Unsetting flags */
+#define SILC_SET_OUTBUF_PENDING(x) SF_SET((x), SILC_SF_OUTBUF_PENDING)
+#define SILC_SET_INBUF_PENDING(x) SF_SET((x), SILC_SF_INBUF_PENDING)
+#define SILC_SET_DISCONNECTING(x) SF_SET((x), SILC_SF_DISCONNECTING)
+#define SILC_SET_DISCONNECTED(x) SF_SET((x), SILC_SF_DISCONNECTED)
+#define SILC_SET_HOST_LOOKUP(x) SF_SET((x), SILC_SF_HOST_LOOKUP)
+#define SILC_SET_DISABLED(x) SF_SET((x), SILC_SF_HOST_LOOKUP)
+#define SILC_UNSET_OUTBUF_PENDING(x) SF_UNSET((x), SILC_SF_OUTBUF_PENDING)
+#define SILC_UNSET_INBUF_PENDING(x) SF_UNSET((x), SILC_SF_INBUF_PENDING)
+#define SILC_UNSET_DISCONNECTING(x) SF_UNSET((x), SILC_SF_DISCONNECTING)
+#define SILC_UNSET_DISCONNECTED(x) SF_UNSET((x), SILC_SF_DISCONNECTED)
+#define SILC_UNSET_HOST_LOOKUP(x) SF_UNSET((x), SILC_SF_HOST_LOOKUP)
+#define SILC_UNSET_DISABLED(x) SF_UNSET((x), SILC_SF_DISABLED)
+
+/* Checking for flags */
+#define SILC_IS_OUTBUF_PENDING(x) SF_IS((x), SILC_SF_OUTBUF_PENDING)
+#define SILC_IS_INBUF_PENDING(x) SF_IS((x), SILC_SF_INBUF_PENDING)
+#define SILC_IS_DISCONNECTING(x) SF_IS((x), SILC_SF_DISCONNECTING)
+#define SILC_IS_DISCONNECTED(x) SF_IS((x), SILC_SF_DISCONNECTED)
+#define SILC_IS_HOST_LOOKUP(x) SF_IS((x), SILC_SF_HOST_LOOKUP)
+#define SILC_IS_DISABLED(x) SF_IS((x), SILC_SF_DISABLED)
+
+/* Prototypes */
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_alloc
+ *
+ * SYNOPSIS
+ *
+ *    void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
+ *                           SilcSocketConnection *new_socket);
+ *
+ * DESCRIPTION
+ *
+ *    Allocates a new socket connection object. The allocated object is 
+ *    returned to the new_socket argument. The `sock' is the socket
+ *    for the connection, the `type' the initial type of the connection and
+ *    the `user_data' a application specific pointer.
+ *
+ ***/
+void silc_socket_alloc(int sock, SilcSocketType type, void *user_data,
+                      SilcSocketConnection *new_socket);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_free
+ *
+ * SYNOPSIS
+ *
+ *    void silc_socket_free(SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Frees the socket connection context. This frees it only if the
+ *    reference counter of the socket is zero, otherwise it decreases the
+ *    reference counter.
+ *
+ ***/
+void silc_socket_free(SilcSocketConnection sock);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_dup
+ *
+ * SYNOPSIS
+ *
+ *    SilcSocketConnection silc_socket_dup(SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Duplicates the socket context. This actually does not duplicate
+ *    any data, instead this increases the reference counter of the
+ *    context. The reference counter is decreased by calling the
+ *    silc_socket_free function and it frees the data when the counter
+ *    hits zero.
+ *
+ ***/
+SilcSocketConnection silc_socket_dup(SilcSocketConnection sock);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_read
+ *
+ * SYNOPSIS
+ *
+ *    int silc_socket_read(SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Reads data from the socket connection into the incoming data buffer.
+ *    It reads as much as possible from the socket connection. This returns
+ *    amount of bytes read or -1 on error or -2 on case where all of the
+ *    data could not be read at once. Implementation of this function
+ *    may be platform specific.
+ *
+ ***/
+int silc_socket_read(SilcSocketConnection sock);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_write
+ *
+ * SYNOPSIS
+ *
+ *    int silc_socket_write(SilcSocketConnection sock);
+ *
+ * DESCRIPTION
+ *
+ *    Writes data from the outgoing buffer to the socket connection. If the
+ *    data cannot be written at once, it must be written at later time. 
+ *    The data is written from the data section of the buffer, not from head
+ *    or tail section. This automatically pulls the data section towards end
+ *    after writing the data. Implementation of this function may be
+ *    platform specific.
+ *
+ ***/
+int silc_socket_write(SilcSocketConnection sock);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_get_error
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_socket_get_error(SilcSocketConnection sock, char *error,
+ *                               uint32 error_len);
+ *
+ * DESCRIPTION
+ *
+ *    Returns human readable error message into the `error' buffer if
+ *    the socket is in error status.  Returns TRUE if error message was
+ *    written into the buffer and FALSE if there is not socket error.
+ *
+ ***/
+bool silc_socket_get_error(SilcSocketConnection sock, char *error,
+                          uint32 error_len);
+
+/****f* silcutil/SilcSocketConnectionAPI/SilcSocketConnectionHBCb
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSocketConnectionHBCb)(SilcSocketConnection sock,
+ *                                             void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Heartbeat callback function. This is the function in the application
+ *    that this library will call when it is time to send the keepalive
+ *    packet SILC_PACKET_HEARTBEAT.
+ *
+ ***/
+typedef void (*SilcSocketConnectionHBCb)(SilcSocketConnection sock,
+                                        void *context);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_set_heartbeat
+ *
+ * SYNOPSIS
+ *
+ *    void silc_socket_set_heartbeat(SilcSocketConnection sock, 
+ *                                   uint32 heartbeat,
+ *                                   void *hb_context,
+ *                                   SilcSocketConnectionHBCb hb_callback,
+ *                                   SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Sets the heartbeat timeout and prepares the socket for performing
+ *    heartbeat in `heartbeat' intervals (seconds). The `hb_context' is
+ *    allocated by the application and will be sent as argument to the
+ *    `hb_callback' function that is called when the `heartbeat' timeout
+ *    expires.  The callback `hb_context' won't be touched by the library
+ *    but will be freed automatically when calling silc_socket_free.  The
+ *    `schedule' is the application's scheduler.
+ *
+ ***/
+void silc_socket_set_heartbeat(SilcSocketConnection sock, 
+                              uint32 heartbeat,
+                              void *hb_context,
+                              SilcSocketConnectionHBCb hb_callback,
+                              SilcSchedule schedule);
+
+/****f* silcutil/SilcSocketConnectionAPI/SilcSocketHostLookupCb
+ *
+ * SYNOPSIS
+ *
+ *    typedef void (*SilcSocketHostLookupCb)(SilcSocketConnection sock,
+ *                                           void *context);
+ *
+ * DESCRIPTION
+ *
+ *    Asynchronous host lookup callback function that will be called
+ *    when the lookup is performed.
+ *
+ ***/
+typedef void (*SilcSocketHostLookupCb)(SilcSocketConnection sock,
+                                      void *context);
+
+/****f* silcutil/SilcSocketConnectionAPI/silc_socket_host_lookup
+ *
+ * SYNOPSIS
+ *
+ *    void silc_socket_host_lookup(SilcSocketConnection sock,
+ *                                 bool port_lookup,
+ *                                 SilcSocketHostLookupCb callback,
+ *                                 void *context,
+ *                                 SilcSchedule schedule);
+ *
+ * DESCRIPTION
+ *
+ *    Performs asynchronous host name and IP address lookups for the
+ *    specified socket connection. This may be called when the socket
+ *    connection is created and the full IP address and fully qualified
+ *    domain name information is desired. The `callback' with `context'
+ *    will be called after the lookup is performed. The `schedule'
+ *    is the application's scheduler which the lookup routine needs. 
+ *    If the socket connection is freed during the lookup the library
+ *    will automatically cancel the lookup and the `callback' will not be
+ *    called.
+ *
+ *    If `port_lookup' is TRUE then the remote port of the socket 
+ *    connection is resolved. After the information is resolved they
+ *    are accessible using sock->ip and sock->hostname pointers. Note
+ *    that if the both IP and FQDN could not be resolved the sock->hostname
+ *    includes the IP address of the remote host. The resolved port is 
+ *    available in sock->port.
+ *
+ ***/
+void silc_socket_host_lookup(SilcSocketConnection sock,
+                            bool port_lookup,
+                            SilcSocketHostLookupCb callback,
+                            void *context,
+                            SilcSchedule schedule);
+
+#endif
diff --git a/lib/silcutil/silcthread.h b/lib/silcutil/silcthread.h
new file mode 100644 (file)
index 0000000..26493ab
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+
+  silcmutex.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
+  Copyright (C) 2001 Pekka Riikonen
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+/****h* silcutil/SilcThreadAPI
+ *
+ * DESCRIPTION
+ *
+ * Interface for SILC Thread implementation. This is platform independent
+ * interface of threads for applications that need concurrent execution
+ * with the application's main thread. The threads created with this 
+ * interface executes concurrently with the calling thread.
+ *
+ ***/
+
+#ifndef SILCTHREAD_H
+#define SILCTHREAD_H
+
+/* Prototypes */
+
+/****s* silcutil/SilcThreadAPI/SilcThread
+ *
+ * NAME
+ * 
+ *    typedef struct SilcThreadStruct *SilcThread;
+ *
+ * DESCRIPTION
+ *
+ *    This context is the actual SILC Thread and is returned by
+ *    the silc_thread_create functions, and given as arguments to
+ *    some of the silc_thread_* functions. This context and its
+ *    resources are released automatically when the thread exits.
+ *
+ ***/
+typedef void *SilcThread;
+
+/****f* silcutil/SilcThreadAPI/SilcThreadStart
+ *
+ * SYNOPSIS
+ *
+ *    typedef void *(*SilcThreadStart)(void *context);
+ *
+ * DESCRIPTION
+ *
+ *    A callback function that is called when the thread is created
+ *    by the silc_thread_create function.  This returns the return value
+ *    of the thread. If another thread is waiting this thread's
+ *    destruction with silc_thread_wait the returned value is passed
+ *    to that thread. The thread is destroyed when this function
+ *    returns.
+ *
+ ***/
+typedef void *(*SilcThreadStart)(void *context);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_create
+ *
+ * SYNOPSIS
+ *
+ *    SilcThread silc_thread_create(SilcThreadStart start_func, 
+ *                                  void *context, bool waitable);
+ * DESCRIPTION
+ *
+ *    Creates a new thread. The `start_func' with `context' will be
+ *    called if the thread was created. This function returns a pointer
+ *    to the thread or NULL if the thread could not be created.  All
+ *    resources of the returned pointer is freed automatically when the
+ *    thread exits.
+ *
+ *    If the `waitable' is set to TRUE then another thread can wait
+ *    this thread's destruction with silc_thread_wait. If it is set to
+ *    FALSE the thread is not waitable.
+ *
+ * NOTES
+ *
+ *    If the `waitable' is TRUE the thread's resources are not freed
+ *    when it exits until another thread has issued silc_thread_wait.
+ *    If the `waitable' is TRUE then another thread must always issue
+ *    silc_thread_wait to avoid memory leaks.
+ *
+ ***/
+SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
+                             bool waitable);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_exit
+ *
+ * SYNOPSIS
+ *
+ *    void silc_thread_exit(void *exit_value);
+ *
+ * DESCRIPTION
+ *
+ *    Exits the current thread. This can be called to explicitly exit
+ *    the thread with `exit_value'. Another way to exit (destroy) the
+ *    current thread is to return from the SilcThreadStart function
+ *    with exit value. The exit value is passed to another thread if it
+ *    is waiting it with silc_thread_wait function.
+ *
+ ***/
+void silc_thread_exit(void *exit_value);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_self
+ *
+ * SYNOPSIS
+ *
+ *    SilcThread silc_thread_self(void);
+ *
+ * DESCRIPTION
+ *
+ *    Returns a pointer to the current thread.
+ *
+ ***/
+SilcThread silc_thread_self(void);
+
+/****f* silcutil/SilcThreadAPI/silc_thread_wait
+ *
+ * SYNOPSIS
+ *
+ *    bool silc_thread_wait(SilcThread thread, void **exit_value);
+ *
+ * DESCRIPTION
+ *
+ *    Waits until the thread indicated by `thread' finishes. This blocks
+ *    the execution of the current thread. The thread is finished if it
+ *    calls silc_thread_exit or is destroyed naturally. When the thread
+ *    exits its exit value is saved to `exit_value' and TRUE is returned.
+ *    If the `thread' is not waitable this will return immediately with
+ *    FALSE value.
+ *
+ ***/
+bool silc_thread_wait(SilcThread thread, void **exit_value);
+
+#endif
diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c
new file mode 100644 (file)
index 0000000..21d779e
--- /dev/null
@@ -0,0 +1,954 @@
+/*
+
+  silcutil.c 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/*
+ * These are general utility functions that doesn't belong to any specific
+ * group of routines.
+ */
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Opens a file indicated by the filename `filename' with flags indicated
+   by the `flags'. */
+
+int silc_file_open(const char *filename, int flags)
+{
+  int fd;
+
+  fd = open(filename, flags, 0600);
+
+  return fd;
+}
+
+/* Reads data from file descriptor `fd' to `buf'. */
+
+int silc_file_read(int fd, unsigned char *buf, uint32 buf_len)
+{
+  return read(fd, (void *)buf, buf_len);
+}
+
+/* Writes `buffer' of length of `len' to file descriptor `fd. */
+
+int silc_file_write(int fd, const char *buffer, uint32 len)
+{
+  return write(fd, (const void *)buffer, len);
+}
+
+/* Closes file descriptor */
+
+int silc_file_close(int fd)
+{
+  return close(fd);
+}
+
+/* Writes a buffer to the file. */
+
+int silc_file_writefile(const char *filename, const char *buffer, uint32 len)
+{
+  int fd;
+        
+  if ((fd = creat(filename, 0644)) == -1) {
+    SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
+                   strerror(errno)));
+    return -1;
+  }
+  
+  if ((write(fd, buffer, len)) == -1) {
+    SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
+    close(fd);
+    return -1;
+  }
+
+  close(fd);
+  
+  return 0;
+}
+
+/* Writes a buffer to the file.  If the file is created specific mode is
+   set to the file. */
+
+int silc_file_writefile_mode(const char *filename, const char *buffer, 
+                            uint32 len, int mode)
+{
+  int fd;
+        
+  if ((fd = creat(filename, mode)) == -1) {
+    SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
+                   strerror(errno)));
+    return -1;
+  }
+  
+  if ((write(fd, buffer, len)) == -1) {
+    SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
+    close(fd);
+    return -1;
+  }
+
+  close(fd);
+  
+  return 0;
+}
+
+/* Reads a file to a buffer. The allocated buffer is returned. Length of
+   the file read is returned to the return_len argument. */
+
+char *silc_file_readfile(const char *filename, uint32 *return_len)
+{
+  int fd;
+  char *buffer;
+  int filelen;
+
+  fd = silc_file_open(filename, O_RDONLY);
+  if (fd < 0) {
+    if (errno == ENOENT)
+      return NULL;
+    SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
+    return NULL;
+  }
+
+  filelen = lseek(fd, (off_t)0L, SEEK_END);
+  if (filelen < 0) {
+    close(fd);
+    return NULL;
+  }
+  if (lseek(fd, (off_t)0L, SEEK_SET) < 0) {
+    close(fd);
+    return NULL;
+  }
+
+  if (filelen < 0) {
+    SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
+    close(fd);
+    return NULL;
+  }
+  
+  buffer = silc_calloc(filelen + 1, sizeof(char));
+  
+  if ((read(fd, buffer, filelen)) == -1) {
+    memset(buffer, 0, sizeof(buffer));
+    close(fd);
+    SILC_LOG_ERROR(("Cannot read from file %s: %s", filename,
+                    strerror(errno)));
+    return NULL;
+  }
+
+  close(fd);
+  buffer[filelen] = EOF;
+
+  if (return_len)
+    *return_len = filelen;
+
+  return buffer;
+}
+
+/* Returns files size. Returns 0 on error. */
+
+uint64 silc_file_size(const char *filename)
+{
+  int ret;
+  struct stat stats;
+
+#ifndef SILC_WIN32
+  ret = lstat(filename, &stats);
+#else
+  ret = stat(filename, &stats);
+#endif
+  if (ret < 0)
+    return 0;
+
+  return (uint64)stats.st_size;
+}
+
+/* Gets line from a buffer. Stops reading when a newline or EOF occurs.
+   This doesn't remove the newline sign from the destination buffer. The
+   argument begin is returned and should be passed again for the function. */
+
+int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
+{
+  static int start = 0;
+  int i;
+  
+  memset(dest, 0, destlen);
+  
+  if (begin != start)
+    start = 0;
+  
+  i = 0;
+  for ( ; start <= srclen; i++, start++) {
+    if (i > destlen)
+      return -1;
+    
+    dest[i] = src[start];
+    
+    if (dest[i] == EOF) 
+      return EOF;
+    
+    if (dest[i] == '\n') 
+      break;
+  }
+  start++;
+  
+  return start;
+}
+
+/* Checks line for illegal characters. Return -1 when illegal character
+   were found. This is used to check for bad lines when reading data from
+   for example a configuration file. */
+
+int silc_check_line(char *buf) 
+{
+  /* Illegal characters in line */
+  if (strchr(buf, '#')) return -1;
+  if (strchr(buf, '\'')) return -1;
+  if (strchr(buf, '\\')) return -1;
+  if (strchr(buf, '\r')) return -1;
+  if (strchr(buf, '\a')) return -1;
+  if (strchr(buf, '\b')) return -1;
+  if (strchr(buf, '\f')) return -1;
+  
+  /* Empty line */
+  if (buf[0] == '\n')
+    return -1;
+  
+  return 0;
+}
+
+/* Returns current time as string. */
+
+char *silc_get_time()
+{
+  time_t curtime;
+  char *return_time;
+
+  curtime = time(NULL);
+  return_time = ctime(&curtime);
+  return_time[strlen(return_time) - 1] = '\0';
+
+  return return_time;
+}
+
+/* Converts string to capital characters */
+
+char *silc_to_upper(char *string)
+{
+  int i;
+  char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
+
+  for (i = 0; i < strlen(string); i++)
+    ret[i] = toupper(string[i]);
+
+  return ret;
+}
+
+static unsigned char pem_enc[64] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
+   data string. Note: This is originally public domain code and is 
+   still PD. */
+
+char *silc_encode_pem(unsigned char *data, uint32 len)
+{
+  int i, j;
+  uint32 bits, c, char_count;
+  char *pem;
+
+  char_count = 0;
+  bits = 0;
+  j = 0;
+
+  pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
+
+  for (i = 0; i < len; i++) {
+    c = data[i];
+    bits += c;
+    char_count++;
+
+    if (char_count == 3) {
+      pem[j++] = pem_enc[bits  >> 18];
+      pem[j++] = pem_enc[(bits >> 12) & 0x3f];
+      pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
+      pem[j++] = pem_enc[bits & 0x3f];
+      bits = 0;
+      char_count = 0;
+    } else {
+      bits <<= 8;
+    }
+  }
+
+  if (char_count != 0) {
+    bits <<= 16 - (8 * char_count);
+    pem[j++] = pem_enc[bits >> 18];
+    pem[j++] = pem_enc[(bits >> 12) & 0x3f];
+
+    if (char_count == 1) {
+      pem[j++] = '=';
+      pem[j] = '=';
+    } else {
+      pem[j++] = pem_enc[(bits >> 6) & 0x3f];
+      pem[j] = '=';
+    }
+  }
+
+  return pem;
+}
+
+/* Same as above but puts newline ('\n') every 72 characters. */
+
+char *silc_encode_pem_file(unsigned char *data, uint32 data_len)
+{
+  int i, j;
+  uint32 len, cols;
+  char *pem, *pem2;
+
+  pem = silc_encode_pem(data, data_len);
+  len = strlen(pem);
+
+  pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
+
+  for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
+    if (cols == 72) {
+      pem2[i] = '\n';
+      cols = 0;
+      len++;
+      continue;
+    }
+
+    pem2[i] = pem[j++];
+  }
+
+  silc_free(pem);
+  return pem2;
+}
+
+/* Decodes PEM into data. Returns the decoded data. Note: This is
+   originally public domain code and is still PD. */
+
+unsigned char *silc_decode_pem(unsigned char *pem, uint32 pem_len,
+                              uint32 *ret_len)
+{
+  int i, j;
+  uint32 len, c, char_count, bits;
+  unsigned char *data;
+  static char ialpha[256], decoder[256];
+
+  for (i = 64 - 1; i >= 0; i--) {
+    ialpha[pem_enc[i]] = 1;
+    decoder[pem_enc[i]] = i;
+  }
+
+  char_count = 0;
+  bits = 0;
+  j = 0;
+
+  if (!pem_len)
+    len = strlen(pem);
+  else
+    len = pem_len;
+
+  data = silc_calloc(((len * 6) / 8), sizeof(*data));
+
+  for (i = 0; i < len; i++) {
+    c = pem[i];
+
+    if (c == '=')
+      break;
+
+    if (c > 127 || !ialpha[c])
+      continue;
+
+    bits += decoder[c];
+    char_count++;
+
+    if (char_count == 4) {
+      data[j++] = bits >> 16;
+      data[j++] = (bits >> 8) & 0xff;
+      data[j++] = bits & 0xff;
+      bits = 0;
+      char_count = 0;
+    } else {
+      bits <<= 6;
+    }
+  }
+
+  switch(char_count) {
+  case 1:
+    silc_free(data);
+    return NULL;
+    break;
+  case 2:
+    data[j++] = bits >> 10;
+    break;
+  case 3:
+    data[j++] = bits >> 16;
+    data[j++] = (bits >> 8) & 0xff;
+    break;
+  }
+
+  if (ret_len)
+    *ret_len = j;
+
+  return data;
+}
+
+/* Parse userfqdn string which is in user@fqdn format */
+
+bool silc_parse_userfqdn(const char *string, char **left, char **right)
+{
+  uint32 tlen;
+
+  if (!string)
+    return FALSE;
+
+  if (string[0] == '@') {
+    if (left)
+      *left = strdup(string);
+    return TRUE;
+  }
+
+  if (strchr(string, '@')) {
+    tlen = strcspn(string, "@");
+    
+    if (left) {
+      *left = silc_calloc(tlen + 1, sizeof(char));
+      memcpy(*left, string, tlen);
+    }
+    
+    if (right) {
+      *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
+      memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
+    }
+  } else {
+    if (left)
+      *left = strdup(string);
+  }
+
+  return TRUE;
+}
+
+/* Parses command line. At most `max_args' is taken. Rest of the line
+   will be allocated as the last argument if there are more than `max_args'
+   arguments in the line. Note that the command name is counted as one
+   argument and is saved. */
+
+void silc_parse_command_line(unsigned char *buffer, 
+                            unsigned char ***parsed,
+                            uint32 **parsed_lens,
+                            uint32 **parsed_types,
+                            uint32 *parsed_num,
+                            uint32 max_args)
+{
+  int i, len = 0;
+  int argc = 0;
+  const char *cp = buffer;
+  char *tmp;
+
+  *parsed = silc_calloc(1, sizeof(**parsed));
+  *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
+
+  /* Get the command first */
+  len = strcspn(cp, " ");
+  tmp = silc_to_upper((char *)cp);
+  (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
+  memcpy((*parsed)[0], tmp, len);
+  silc_free(tmp);
+  (*parsed_lens)[0] = len;
+  cp += len;
+  while (*cp == ' ')
+    cp++;
+  argc++;
+
+  /* Parse arguments */
+  if (strchr(cp, ' ') || strlen(cp) != 0) {
+    for (i = 1; i < max_args; i++) {
+
+      if (i != max_args - 1)
+       len = strcspn(cp, " ");
+      else
+       len = strlen(cp);
+      while (len && cp[len - 1] == ' ')
+       len--;
+      if (!len)
+       break;
+      
+      *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
+      *parsed_lens = silc_realloc(*parsed_lens, 
+                                 sizeof(**parsed_lens) * (argc + 1));
+      (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
+      memcpy((*parsed)[argc], cp, len);
+      (*parsed_lens)[argc] = len;
+      argc++;
+
+      cp += len;
+      if (strlen(cp) == 0)
+       break;
+      else
+       while (*cp == ' ')
+         cp++;
+    }
+  }
+
+  /* Save argument types. Protocol defines all argument types but
+     this implementation makes sure that they are always in correct
+     order hence this simple code. */
+  *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
+  for (i = 0; i < argc; i++)
+    (*parsed_types)[i] = i;
+
+  *parsed_num = argc;
+}
+
+/* Formats arguments to a string and returns it after allocating memory
+   for it. It must be remembered to free it later. */
+
+char *silc_format(char *fmt, ...)
+{
+  va_list args;
+  static char buf[8192];
+
+  memset(buf, 0, sizeof(buf));
+  va_start(args, fmt);
+  vsnprintf(buf, sizeof(buf) - 1, fmt, args);
+  va_end(args);
+
+  return strdup(buf);
+}
+
+/* Renders ID to suitable to print for example to log file. */
+
+static char rid[256];
+
+char *silc_id_render(void *id, uint16 type)
+{
+  char tmp[100];
+  unsigned char tmps[2];
+
+  memset(rid, 0, sizeof(rid));
+  switch(type) {
+  case SILC_ID_SERVER:
+    {
+      SilcServerID *server_id = (SilcServerID *)id;
+      if (server_id->ip.data_len > 4) {
+#ifdef HAVE_IPV6
+       struct in6_addr ipv6;
+       memmove(&ipv6, server_id->ip.data, sizeof(ipv6));
+       if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
+         strcat(rid, tmp);
+#endif
+      } else {
+       struct in_addr ipv4;
+       memmove(&ipv4.s_addr, server_id->ip.data, 4);
+       strcat(rid, inet_ntoa(ipv4));
+      }
+
+      memset(tmp, 0, sizeof(tmp));
+      snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
+      strcat(rid, tmp);
+      SILC_PUT16_MSB(server_id->rnd, tmps);
+      memset(tmp, 0, sizeof(tmp));
+      snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
+      strcat(rid, tmp);
+    }
+    break;
+  case SILC_ID_CLIENT:
+    {
+      SilcClientID *client_id = (SilcClientID *)id;
+      if (client_id->ip.data_len > 4) {
+#ifdef HAVE_IPV6
+       struct in6_addr ipv6;
+       memmove(&ipv6, client_id->ip.data, sizeof(ipv6));
+       if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
+         strcat(rid, tmp);
+#endif
+      } else {
+       struct in_addr ipv4;
+       memmove(&ipv4.s_addr, client_id->ip.data, 4);
+       strcat(rid, inet_ntoa(ipv4));
+      }
+
+      memset(tmp, 0, sizeof(tmp));
+      snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
+      strcat(rid, tmp);
+      memset(tmp, 0, sizeof(tmp));
+      snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]", 
+              client_id->hash[0], client_id->hash[1],
+              client_id->hash[2], client_id->hash[3]);
+      strcat(rid, tmp);
+    }
+    break;
+  case SILC_ID_CHANNEL:
+    {
+      SilcChannelID *channel_id = (SilcChannelID *)id;
+      if (channel_id->ip.data_len > 4) {
+#ifdef HAVE_IPV6
+       struct in6_addr ipv6;
+       memmove(&ipv6, channel_id->ip.data, sizeof(ipv6));
+       if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp)))
+         strcat(rid, tmp);
+#endif
+      } else {
+       struct in_addr ipv4;
+       memmove(&ipv4.s_addr, channel_id->ip.data, 4);
+       strcat(rid, inet_ntoa(ipv4));
+      }
+
+      memset(tmp, 0, sizeof(tmp));
+      snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
+      strcat(rid, tmp);
+      SILC_PUT16_MSB(channel_id->rnd, tmps);
+      memset(tmp, 0, sizeof(tmp));
+      snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
+      strcat(rid, tmp);
+    }
+    break;
+  }
+
+  return rid;
+}
+
+/* Compares two strings. Strings may include wildcards * and ?.
+   Returns TRUE if strings match. */
+
+int silc_string_compare(char *string1, char *string2)
+{
+  int i;
+  int slen1;
+  int slen2;
+  char *tmpstr1, *tmpstr2;
+
+  if (!string1 || !string2)
+    return FALSE;
+
+  slen1 = strlen(string1);
+  slen2 = strlen(string2);
+
+  /* See if they are same already */
+  if (!strncmp(string1, string2, strlen(string2)))
+    return TRUE;
+
+  if (slen2 < slen1)
+    if (!strchr(string1, '*'))
+      return FALSE;
+  
+  /* Take copies of the original strings as we will change them */
+  tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
+  memcpy(tmpstr1, string1, slen1);
+  tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
+  memcpy(tmpstr2, string2, slen2);
+  
+  for (i = 0; i < slen1; i++) {
+    
+    /* * wildcard. Only one * wildcard is possible. */
+    if (tmpstr1[i] == '*')
+      if (!strncmp(tmpstr1, tmpstr2, i)) {
+       memset(tmpstr2, 0, slen2);
+       strncpy(tmpstr2, tmpstr1, i);
+       break;
+      }
+    
+    /* ? wildcard */
+    if (tmpstr1[i] == '?') {
+      if (!strncmp(tmpstr1, tmpstr2, i)) {
+       if (!(slen1 < i + 1))
+         if (tmpstr1[i + 1] != '?' &&
+             tmpstr1[i + 1] != tmpstr2[i + 1])
+           continue;
+       
+       if (!(slen1 < slen2))
+         tmpstr2[i] = '?';
+      }
+    }
+  }
+  
+  /* if using *, remove it */
+  if (strchr(tmpstr1, '*'))
+    *strchr(tmpstr1, '*') = 0;
+  
+  if (!strcmp(tmpstr1, tmpstr2)) {
+    memset(tmpstr1, 0, slen1);
+    memset(tmpstr2, 0, slen2);
+    silc_free(tmpstr1);
+    silc_free(tmpstr2);
+    return TRUE;
+  }
+  
+  memset(tmpstr1, 0, slen1);
+  memset(tmpstr2, 0, slen2);
+  silc_free(tmpstr1);
+  silc_free(tmpstr2);
+  return FALSE;
+}
+
+/* Basic has function to hash strings. May be used with the SilcHashTable. 
+   Note that this lowers the characters of the string (with tolower()) so
+   this is used usually with nicknames, channel and server names to provide
+   case insensitive keys. */
+
+uint32 silc_hash_string(void *key, void *user_context)
+{
+  char *s = (char *)key;
+  uint32 h = 0, g;
+  
+  while (*s != '\0') {
+    h = (h << 4) + tolower(*s);
+    if ((g = h & 0xf0000000)) {
+      h = h ^ (g >> 24);
+      h = h ^ g;
+    }
+    s++;
+  }
+  
+  return h;
+}
+
+/* Basic hash function to hash integers. May be used with the SilcHashTable. */
+
+uint32 silc_hash_uint(void *key, void *user_context)
+{
+  return *(uint32 *)key;
+}
+
+/* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
+
+uint32 silc_hash_ptr(void *key, void *user_context)
+{
+  return (uint32)key;
+}
+
+/* Hash a ID. The `user_context' is the ID type. */
+
+uint32 silc_hash_id(void *key, void *user_context)
+{
+  SilcIdType id_type = (SilcIdType)(uint32)user_context;
+  uint32 h = 0;
+  int i;
+
+  switch (id_type) {
+  case SILC_ID_CLIENT:
+    {
+      SilcClientID *id = (SilcClientID *)key;
+      uint32 g;
+  
+      /* The client ID is hashed by hashing the hash of the ID 
+        (which is a truncated MD5 hash of the nickname) so that we
+        can access the entry from the cache with both Client ID but
+        with just a hash from the ID as well. */
+
+      for (i = 0; i < sizeof(id->hash); i++) {
+       h = (h << 4) + id->hash[i];
+       if ((g = h & 0xf0000000)) {
+         h = h ^ (g >> 24);
+         h = h ^ g;
+       }
+      }
+
+      return h;
+    }
+    break;
+  case SILC_ID_SERVER:
+    {
+      SilcServerID *id = (SilcServerID *)key;
+      
+      h = id->port * id->rnd;
+      for (i = 0; i < id->ip.data_len; i++)
+       h ^= id->ip.data[i];
+      
+      return h;
+    }
+    break;
+  case SILC_ID_CHANNEL:
+    {
+      SilcChannelID *id = (SilcChannelID *)key;
+      
+      h = id->port * id->rnd;
+      for (i = 0; i < id->ip.data_len; i++)
+       h ^= id->ip.data[i];
+      
+      return h;
+    }
+    break;
+  default:
+    break;
+  }
+
+  return h;
+}
+
+/* Hash binary data. The `user_context' is the data length. */
+
+uint32 silc_hash_data(void *key, void *user_context)
+{
+  uint32 len = (uint32)user_context, h = 0;
+  unsigned char *data = (unsigned char *)key;
+  int i;
+
+  h = (data[0] * data[len - 1] + 1) * len;
+  for (i = 0; i < len; i++)
+    h ^= data[i];
+
+  return h;
+}
+
+/* Compares two strings. May be used as SilcHashTable comparison function. */
+
+bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
+{
+  return !strcasecmp((char *)key1, (char *)key2);
+}
+
+/* Compares two ID's. May be used as SilcHashTable comparison function. 
+   The Client ID's compares only the hash of the Client ID not any other
+   part of the Client ID. Other ID's are fully compared. */
+
+bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
+{
+  SilcIdType id_type = (SilcIdType)(uint32)user_context;
+  return (id_type == SILC_ID_CLIENT ? 
+         SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
+         SILC_ID_COMPARE_TYPE(key1, key2, id_type));
+}
+
+/* Compare two Client ID's entirely and not just the hash from the ID. */
+
+bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
+{
+  return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
+}
+
+/* Compares binary data. May be used as SilcHashTable comparison function. */
+
+bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
+{
+  uint32 len = (uint32)user_context;
+  return !memcmp(key1, key2, len);
+}
+
+/* Parses mode mask and returns the mode as string. */
+
+char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
+{
+  char string[100];
+
+  if (!mode)
+    return NULL;
+
+  memset(string, 0, sizeof(string));
+
+  if (mode & SILC_CHANNEL_MODE_PRIVATE)
+    strncat(string, "p", 1);
+
+  if (mode & SILC_CHANNEL_MODE_SECRET)
+    strncat(string, "s", 1);
+
+  if (mode & SILC_CHANNEL_MODE_PRIVKEY)
+    strncat(string, "k", 1);
+
+  if (mode & SILC_CHANNEL_MODE_INVITE)
+    strncat(string, "i", 1);
+
+  if (mode & SILC_CHANNEL_MODE_TOPIC)
+    strncat(string, "t", 1);
+
+  if (mode & SILC_CHANNEL_MODE_ULIMIT)
+    strncat(string, "l", 1);
+
+  if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
+    strncat(string, "a", 1);
+
+  if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
+    strncat(string, "f", 1);
+
+  if (mode & SILC_CHANNEL_MODE_CIPHER)
+    strncat(string, cipher, strlen(cipher));
+
+  if (mode & SILC_CHANNEL_MODE_HMAC)
+    strncat(string, hmac, strlen(hmac));
+
+  /* Rest of mode is ignored */
+
+  return strdup(string);
+}
+
+/* Parses channel user mode mask and returns te mode as string */
+
+char *silc_client_chumode(uint32 mode)
+{
+  char string[4];
+
+  if (!mode)
+    return NULL;
+
+  memset(string, 0, sizeof(string));
+
+  if (mode & SILC_CHANNEL_UMODE_CHANFO)
+    strncat(string, "f", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_CHANOP)
+    strncat(string, "o", 1);
+
+  return strdup(string);
+}
+
+/* Parses channel user mode and returns it as special mode character. */
+
+char *silc_client_chumode_char(uint32 mode)
+{
+  char string[4];
+
+  if (!mode)
+    return NULL;
+
+  memset(string, 0, sizeof(string));
+
+  if (mode & SILC_CHANNEL_UMODE_CHANFO)
+    strncat(string, "*", 1);
+
+  if (mode & SILC_CHANNEL_UMODE_CHANOP)
+    strncat(string, "@", 1);
+
+  return strdup(string);
+}
+
+/* Creates fingerprint from data, usually used with SHA1 digests */
+
+char *silc_fingerprint(const unsigned char *data, uint32 data_len)
+{
+  char fingerprint[64], *cp;
+  int i;
+
+  memset(fingerprint, 0, sizeof(fingerprint));
+  cp = fingerprint;
+  for (i = 0; i < data_len; i++) {
+    snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
+    cp += 2;
+    
+    if ((i + 1) % 2 == 0)
+      snprintf(cp++, sizeof(fingerprint), " ");
+
+    if ((i + 1) % 10 == 0)
+      snprintf(cp++, sizeof(fingerprint), " ");
+  }
+  i--;
+  if ((i + 1) % 2 == 0)
+    cp[-2] = 0;
+  if ((i + 1) % 10 == 0)
+    cp[-1] = 0;
+  
+  return strdup(fingerprint);
+}
diff --git a/lib/silcutil/silcutil.h b/lib/silcutil/silcutil.h
new file mode 100644 (file)
index 0000000..a8e2add
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+
+  silcutil.h 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+
+#ifndef SILCUTIL_H
+#define SILCUTIL_H
+
+/* Prototypes */
+int silc_file_open(const char *filename, int flags);
+int silc_file_read(int fd, unsigned char *buf, uint32 buf_len);
+int silc_file_write(int fd, const char *buffer, uint32 len);
+int silc_file_close(int fd);
+char *silc_file_readfile(const char *filename, uint32 *return_len);
+int silc_file_writefile(const char *filename, const char *buffer, uint32 len);
+int silc_file_writefile_mode(const char *filename, const char *buffer, 
+                            uint32 len, int mode);
+uint64 silc_file_size(const char *filename);
+int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin);
+int silc_check_line(char *buf);
+char *silc_get_time();
+char *silc_to_upper(char *string);
+char *silc_encode_pem(unsigned char *data, uint32 len);
+char *silc_encode_pem_file(unsigned char *data, uint32 data_len);
+unsigned char *silc_decode_pem(unsigned char *pem, uint32 pem_len,
+                              uint32 *ret_len);
+bool silc_parse_userfqdn(const char *string, char **left, char **right);
+void silc_parse_command_line(unsigned char *buffer, 
+                            unsigned char ***parsed,
+                            uint32 **parsed_lens,
+                            uint32 **parsed_types,
+                            uint32 *parsed_num,
+                            uint32 max_args);
+char *silc_format(char *fmt, ...);
+char *silc_id_render(void *id, uint16 type);
+int silc_string_compare(char *string1, char *string2);
+char *silc_string_regexify(const char *string);
+int silc_string_regex_match(const char *regex, const char *string);
+int silc_string_match(const char *string1, const char *string2);
+char *silc_get_username();
+char *silc_get_real_name();
+uint32 silc_hash_string(void *key, void *user_context);
+uint32 silc_hash_uint(void *key, void *user_context);
+uint32 silc_hash_ptr(void *key, void *user_context);
+uint32 silc_hash_id(void *key, void *user_context);
+uint32 silc_hash_data(void *key, void *user_context);
+bool silc_hash_string_compare(void *key1, void *key2, void *user_context);
+bool silc_hash_id_compare(void *key1, void *key2, void *user_context);
+bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context);
+bool silc_hash_data_compare(void *key1, void *key2, void *user_context);
+char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac);
+char *silc_client_chumode(uint32 mode);
+char *silc_client_chumode_char(uint32 mode);
+int silc_gettimeofday(struct timeval *p);
+char *silc_fingerprint(const unsigned char *data, uint32 data_len);
+
+#endif
diff --git a/lib/silcutil/symbian/silcepocsockconn.cpp b/lib/silcutil/symbian/silcepocsockconn.cpp
new file mode 100644 (file)
index 0000000..99853dc
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+
+  silcepocsockconn.cpp 
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Writes data from encrypted buffer to the socket connection. If the
+   data cannot be written at once, it will be written later with a timeout. 
+   The data is written from the data section of the buffer, not from head
+   or tail section. This automatically pulls the data section towards end
+   after writing the data. */
+
+int silc_socket_write(SilcSocketConnection sock)
+{
+  /* XXX */
+}
+
+/* Reads data from the socket connection into the incoming data buffer.
+   It reads as much as possible from the socket connection. This returns
+   amount of bytes read or -1 on error or -2 on case where all of the
+   data could not be read at once. */
+
+int silc_socket_read(SilcSocketConnection sock)
+{
+  /* XXX */
+}
+
+/* Returns human readable socket error message. These are copied from the
+   PuTTY. */
+
+#define PUT_ERROR(s) 
+do {
+  if (strlen(s) > err_len)
+    return FALSE;
+  memset(error, 0, error_len);
+  memcpy(error, s, strlen(s));
+  return TRUE;
+} while(0)
+
+bool silc_socket_get_error(SilcSocketConnection sock, char *error,
+                          uint32 error_len)
+{
+  if (sock->sock_error == KErrNone)
+    return FALSE;
+
+  switch (sock->sock_error) {
+  case KErrNotFound:
+    PUT_ERROR("Item not found. (NotFound)");
+  case KErrGeneral:
+    PUT_ERROR("Uncategorized error. (General)");
+  case KErrCancel:
+    PUT_ERROR("Operation cancelled. (Cancel)");
+  case KErrNoMemory:
+    PUT_ERROR("A memory allocation failed. (NoMemory)");
+  case KErrNotSupported:
+    PUT_ERROR("A function is not supported in a given context. "
+             "(NotSupported)");
+  case KErrArgument:
+    PUT_ERROR("An argument is out of range. (Argument)");
+  case KErrBadHandle:
+    PUT_ERROR("Bad handle. (BadHandle)");
+  case KErrOverflow:
+    PUT_ERROR("Overflow. (Overflow)");
+  case KErrUnderflow:
+    PUT_ERROR("Underflow. (Underflow)");
+  case KErrAlreadyExists:
+    PUT_ERROR("The resource already exists. (AlreadyExists)");
+  case KErrPathNotFound:
+    PUT_ERROR("In the context of file operations, the path was "
+             "not found. (PathNotFound)");
+  case KErrDied:
+    PUT_ERROR("A handle refers to a thread which has died (Died)");
+  case KErrInUse:
+    PUT_ERROR("A requested resource is already in use. (InUse)");
+  case KErrServerTerminated:
+    PUT_ERROR("A client/server operation cannot execute, because the "
+             "server has terminated. (ServerTerminated)");
+  case KErrServerBusy:
+    PUT_ERROR("A client/server operation cannot execute, because the server "
+             "is busy. (ServerBusy)");
+  case KErrNotReady:
+    PUT_ERROR("Resource not ready. Not initialized, or has no power. "
+             "(NotReady)");
+  case KErrUnknown:
+    PUT_ERROR("A device is of unknown type. (Unknown)");
+  case KErrCorrupt:
+    PUT_ERROR("Corrupted. (Corrupt)");
+  case KErrAccessDenied:
+    PUT_ERROR("Access denied. (AccessDenied)");
+  case KErrLocked:
+    PUT_ERROR("The operation cannot be performed, because the resource "
+             "is locked. (Locked)");
+  case KErrWrite:
+    PUT_ERROR("During a file write operation, not all the data could "
+             "be written. (Write)");
+  case KErrDisMounted:
+    PUT_ERROR("A volume which was to be used for a file system operation "
+             "has been dismounted. (DisMounted)");
+  case KErrEof:
+    PUT_ERROR("End of file has been reached. (Eof)");
+  case KErrDiskFull:
+    PUT_ERROR("A write operation could not complete, because the disk "
+             "was full. (DiskFull)");
+  case KErrBadDriver:
+    PUT_ERROR("A driver DLL was of the wrong type. (BadDriver)");
+  case KErrBadName:
+    PUT_ERROR("Name did not conform with the required syntax. (BadName)");
+  case KErrCommsLineFail:
+    PUT_ERROR("The communication line failed. (CommsLineFail)");
+  case KErrCommsFrame:
+    PUT_ERROR("A frame error occurred in a communications operation. "
+             "(CommsFrame)");
+  case KErrCommsOverrun:
+    PUT_ERROR("An overrun was detected by a communications driver. "
+             "(CommsOverrun)");
+  case KErrCommsParity:
+    PUT_ERROR("A parity error occurred in communications. (CommsParity)");
+  case KErrTimedOut:
+    PUT_ERROR("An operation timed out. (TimedOut)");
+  case KErrCouldNotConnect:
+    PUT_ERROR("A session could not connect. (CouldNotConnect)");
+  case KErrCouldNotDisconnect:
+    PUT_ERROR("A session could not disconnect. (CouldNotDisconnect)");
+  case KErrDisconnected:
+    PUT_ERROR("The required session was disconnected. (Disconnected)");
+  case KErrBadLibraryEntryPoint:
+    PUT_ERROR("A library entry point was not of the required type. "
+             "(BadLibraryEntryPoint)");
+  case KErrBadDescriptor:
+    PUT_ERROR("A non-descriptor parameter was passed. (BadDescriptor)");
+  case KErrAbort:
+    PUT_ERROR("An operation was aborted (Abort)");
+  case KErrTooBig:
+    PUT_ERROR("A number was too big (TooBig)");
+  case KErrDivideByZero:
+    PUT_ERROR("A divide-by-zero operation was attempted. (DivideByZero)");
+  case KErrBadPower:
+    PUT_ERROR("Insufficient power was available to complete an operation. "
+             "(BadPower)");
+  case KErrWouldBlock:
+    PUT_ERROR("Network error: Resource temporarily unavailable (WouldBlock)");
+  case KErrNetUnreach:
+    PUT_ERROR("Network unreachable. (NetUnreach)");
+  case KErrHostUnreach:
+    PUT_ERROR("Host unreachable. (HostUnreach)");
+  case KErrNoProtocolOpt:
+    PUT_ERROR("No such protocol option. (NoProtocolOpt)");
+  case KErrUrgentData:
+    PUT_ERROR("Urgent data arrived. (UrgentData)");
+  case KInvalSocket:
+    PUT_ERROR("Got NULL sokcet.");
+  }
+
+  return FALSE;
+}
diff --git a/lib/silcutil/symbian/silcepocthread.cpp b/lib/silcutil/symbian/silcepocthread.cpp
new file mode 100644 (file)
index 0000000..12ad59c
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+
+  silcepocthread.cpp
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+#ifdef SILC_THREADS
+
+/* Thread structure for EPOC */
+typedef struct {
+  RThread *thread;
+  SilcThreadStart start_func;
+  void *context;
+  bool waitable;
+} *SilcEpocThread;
+
+/* The actual thread function */
+
+TInt silc_thread_epoc_start(TAny *context)
+{
+  SilcEpocThread thread = (SilcEpocThread)context;
+
+  thread->start_func(thread->context);
+  silc_thread_exit(NULL);
+
+  return 0;
+}
+
+SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
+                             bool waitable)
+{
+#ifdef SILC_THREADS
+  SilcEpocThread thread;
+  TInt ret;
+
+  SILC_LOG_DEBUG(("Creating new thread"));
+
+  thread = silc_calloc(1, sizeof(*thread));
+  thread->start_func = start_func;
+  thread->context = context;
+  thread->waitable = waitable;
+
+  /* Create the thread */
+  /* XXX Unique name should be given for the thread */
+  thread->thread = new RThread();
+  ret = thread->thread->Create(NULL, silc_thread_epoc_start, 0, 0, 0,
+                              (TAny *)thread, EOwnerProcess);
+  if (ret != KErrNone) {
+    SILC_LOG_ERROR(("Could not create new thread"));
+    delete thread->thread;
+    silc_free(thread);
+    return NULL;
+  }
+
+  return (SilcThread)thread;
+#else
+  /* Call thread callback immediately */
+  (*start_func)(context);
+  return NULL;
+#endif
+}
+
+void silc_thread_exit(void *exit_value)
+{
+#ifdef SILC_THREADS
+  /* XXX */
+#endif
+}
+
+SilcThread silc_thread_self(void)
+{
+#ifdef SILC_THREADS
+  /* XXX */
+  return NULL;
+#else
+  return NULL;
+#endif
+}
+
+bool silc_thread_wait(SilcThread thread, void **exit_value)
+{
+#ifdef SILC_THREADS
+  /* XXX */
+  return TRUE;
+#else
+  return FALSE;
+#endif
+}
diff --git a/lib/silcutil/symbian/silcepocutil.cpp b/lib/silcutil/symbian/silcepocutil.cpp
new file mode 100644 (file)
index 0000000..b43eda9
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+
+  silcepocutil.cpp
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* XXX TODO or use GNU regex if it compiles */
+char *silc_string_regexify(const char *string)
+{
+  return strdup(string);
+}
+
+char *silc_string_regex_combine(const char *string1, const char *string2)
+{
+  return strdup(string1);
+}
+
+int silc_string_regex_match(const char *regex, const char *string)
+{
+  return TRUE;
+}
+
+int silc_string_match(const char *string1, const char *string2)
+{
+  return TRUE;
+}
+
+/* Return current time to struct timeval. */
+
+int silc_gettimeofday(struct timeval *p)
+{
+  return gettimeofday(p, NULL);
+}
diff --git a/lib/silcutil/unix/Makefile.am b/lib/silcutil/unix/Makefile.am
new file mode 100644 (file)
index 0000000..1b5b1c8
--- /dev/null
@@ -0,0 +1,31 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+noinst_LIBRARIES = libsilcunixutil.a
+
+libsilcunixutil_a_SOURCES =    \
+       silcunixschedule.c      \
+       silcunixnet.c           \
+       silcunixutil.c          \
+       silcunixsockconn.c      \
+       silcunixmutex.c         \
+       silcunixthread.c
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcutil/unix/silcunixmutex.c b/lib/silcutil/unix/silcunixmutex.c
new file mode 100644 (file)
index 0000000..1b531b0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+
+  silcunixmutex.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+#ifdef SILC_THREADS
+
+/* SILC Mutex structure */
+struct SilcMutexStruct {
+  pthread_mutex_t mutex;
+};
+
+bool silc_mutex_alloc(SilcMutex *mutex)
+{
+  *mutex = silc_calloc(1, sizeof(**mutex));
+  if (*mutex == NULL)
+    return FALSE;
+
+  pthread_mutex_init(&(*mutex)->mutex, NULL);
+  return TRUE;
+}
+
+void silc_mutex_free(SilcMutex mutex)
+{
+  pthread_mutex_destroy(&mutex->mutex);
+  silc_free(mutex);
+}
+
+void silc_mutex_lock(SilcMutex mutex)
+{
+  if (pthread_mutex_lock(&mutex->mutex))
+    assert(FALSE);
+}
+
+void silc_mutex_unlock(SilcMutex mutex)
+{
+  if (pthread_mutex_unlock(&mutex->mutex))
+    assert(FALSE);
+}
+
+#endif /* SILC_THREADS */
diff --git a/lib/silcutil/unix/silcunixnet.c b/lib/silcutil/unix/silcunixnet.c
new file mode 100644 (file)
index 0000000..d1f6384
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+
+  silcunixnet.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcnet.h"
+
+#ifdef HAVE_IPV6
+#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ?   \
+  sizeof(so.sin6) : sizeof(so.sin))
+#else
+#define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
+#endif
+
+typedef union {
+  struct sockaddr sa;
+  struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+  struct sockaddr_in6 sin6;
+#endif
+} SilcSockaddr;
+
+static bool silc_net_set_sockaddr(SilcSockaddr *addr, const char *ip_addr,
+                                 int port)
+{
+  int len;
+
+  memset(addr, 0, sizeof(*addr));
+
+  /* Check for IPv4 and IPv6 addresses */
+  if (ip_addr) {
+    if (!silc_net_is_ip(ip_addr)) {
+      SILC_LOG_ERROR(("%s is not IP address", ip_addr));
+      return FALSE;
+    }
+
+    if (silc_net_is_ip4(ip_addr)) {
+      /* IPv4 address */
+      len = sizeof(addr->sin.sin_addr);
+      silc_net_addr2bin(ip_addr, 
+                       (unsigned char *)&addr->sin.sin_addr.s_addr, len);
+      addr->sin.sin_family = AF_INET;
+      addr->sin.sin_port = port ? htons(port) : 0;
+    } else {
+#ifdef HAVE_IPV6
+      /* IPv6 address */
+      len = sizeof(addr->sin6.sin6_addr);
+      silc_net_addr2bin(ip_addr, 
+                       (unsigned char *)&addr->sin6.sin6_addr, len);
+      addr->sin6.sin6_family = AF_INET6;
+      addr->sin6.sin6_port = port ? htons(port) : 0;
+#else
+      SILC_LOG_ERROR(("IPv6 support is not compiled in"));
+      return FALSE;
+#endif
+    }
+  } else {
+    /* Any address */
+    addr->sin.sin_family = AF_INET;
+    addr->sin.sin_addr.s_addr = INADDR_ANY;
+    if (port)
+      addr->sin.sin_port = htons(port);
+  }
+
+  return TRUE;
+}
+
+/* This function creates server or daemon or listener or what ever. This
+   does not fork a new process, it must be done by the caller if caller
+   wants to create a child process. This is used by the SILC server. 
+   If argument `ip_addr' is NULL `any' address will be used. Returns 
+   the created socket or -1 on error. */
+
+int silc_net_create_server(int port, const char *ip_addr)
+{
+  int sock, rval;
+  SilcSockaddr server;
+
+  SILC_LOG_DEBUG(("Creating a new server listener"));
+
+  /* Set sockaddr for server */
+  if (!silc_net_set_sockaddr(&server, ip_addr, port))
+    return -1;
+
+  /* Create the socket */
+  sock = socket(server.sin.sin_family, SOCK_STREAM, 0);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
+    return -1;
+  }
+
+  /* Set the socket options */
+  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+  if (rval < 0) {
+    SILC_LOG_ERROR(("Cannot set socket options: %s", strerror(errno)));
+    return -1;
+  }
+
+  /* Bind the server socket */
+  rval = bind(sock, &server.sa, SIZEOF_SOCKADDR(server));
+  if (rval < 0) {
+    SILC_LOG_DEBUG(("Cannot bind socket: %s", strerror(errno)));
+    return -1;
+  }
+
+  /* Specify that we are listenning */
+  rval = listen(sock, 5);
+  if (rval < 0) {
+    SILC_LOG_ERROR(("Cannot set socket listenning: %s", strerror(errno)));
+    return -1;
+  }
+
+  /* Set the server socket to non-blocking mode */
+  silc_net_set_socket_nonblock(sock);
+
+  SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
+
+  return sock;
+}
+
+/* Closes the server by closing the socket connection. */
+
+void silc_net_close_server(int sock)
+{
+  shutdown(sock, 2);
+  close(sock);
+
+  SILC_LOG_DEBUG(("Server socket closed"));
+}
+
+/* Creates a connection (TCP/IP) to a remote host. Returns the connection
+   socket or -1 on error. This blocks the process while trying to create
+   the connection. */
+
+int silc_net_create_connection(const char *local_ip, int port, 
+                              const char *host)
+{
+  int sock, rval;
+  char ip_addr[64];
+  SilcSockaddr desthost;
+
+  SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
+
+  /* Do host lookup */
+  if (!silc_net_gethostbyname(host, ip_addr, sizeof(ip_addr))) {
+    SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
+                   "IP address", host));
+    return -1;
+  }
+
+  /* Set sockaddr for this connection */
+  if (!silc_net_set_sockaddr(&desthost, ip_addr, port))
+    return -1;
+
+  /* Create the connection socket */
+  sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
+    return -1;
+  }
+
+  /* Bind to the local address if provided */
+  if (local_ip) {
+    SilcSockaddr local;
+
+    /* Set sockaddr for local listener, and try to bind it. */
+    if (silc_net_set_sockaddr(&local, local_ip, 0))
+      bind(sock, &local.sa, sizeof(local));
+  }
+
+  /* Connect to the host */
+  rval = connect(sock, &desthost.sa, sizeof(desthost));
+  if (rval < 0) {
+    SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
+    shutdown(sock, 2);
+    close(sock);
+    return -1;
+  }
+
+  /* Set appropriate options */
+#if defined(TCP_NODELAY)
+  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
+#endif
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
+
+  SILC_LOG_DEBUG(("Connection created"));
+
+  return sock;
+}
+
+/* Creates a connection (TCP/IP) to a remote host. Returns the connection
+   socket or -1 on error. This creates non-blocking socket hence the
+   connection returns directly. To get the result of the connect() one
+   must select() the socket and read the result after it's ready. */
+
+int silc_net_create_connection_async(const char *local_ip, int port, 
+                                    const char *host)
+{
+  int sock, rval;
+  char ip_addr[64];
+  SilcSockaddr desthost;
+
+  SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
+                 host, port));
+
+  /* Do host lookup */
+  if (!silc_net_gethostbyname(host, ip_addr, sizeof(ip_addr))) {
+    SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
+                   "IP address", host));
+    return -1;
+  }
+
+  /* Set sockaddr for this connection */
+  if (!silc_net_set_sockaddr(&desthost, ip_addr, port))
+    return -1;
+
+  /* Create the connection socket */
+  sock = socket(desthost.sin.sin_family, SOCK_STREAM, 0);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Cannot create socket: %s", strerror(errno)));
+    return -1;
+  }
+
+  /* Bind to the local address if provided */
+  if (local_ip) {
+    SilcSockaddr local;
+
+    /* Set sockaddr for local listener, and try to bind it. */
+    if (silc_net_set_sockaddr(&local, local_ip, 0))
+      bind(sock, &local.sa, sizeof(local));
+  }
+
+  /* Set the socket to non-blocking mode */
+  silc_net_set_socket_nonblock(sock);
+
+  /* Connect to the host */
+  rval = connect(sock, &desthost.sa, sizeof(desthost));
+  if (rval < 0) {
+    if (errno !=  EINPROGRESS) {
+      SILC_LOG_ERROR(("Cannot connect to remote host: %s", strerror(errno)));
+      shutdown(sock, 2);
+      close(sock);
+      return -1;
+    }
+  }
+
+  /* Set appropriate options */
+#if defined(TCP_NODELAY)
+  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
+#endif
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
+
+  SILC_LOG_DEBUG(("Connection operation in progress"));
+
+  return sock;
+}
+
+/* Closes the connection by closing the socket connection. */
+
+void silc_net_close_connection(int sock)
+{
+  close(sock);
+}
+
+/* Set's the socket to non-blocking mode. */
+
+int silc_net_set_socket_nonblock(int sock)
+{
+  return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
+}
+
+/* Converts the IP number string from numbers-and-dots notation to
+   binary form. */
+
+bool silc_net_addr2bin(const char *addr, void *bin, uint32 bin_len)
+{
+  int ret = 0;
+
+  if (silc_net_is_ip4(addr)) {
+    /* IPv4 address */
+    struct in_addr tmp;
+    ret = inet_aton(addr, &tmp);
+    if (bin_len < 4)
+      return FALSE;
+    
+    memcpy(bin, (unsigned char *)&tmp.s_addr, 4);
+#ifdef HAVE_IPV6
+  } else {
+    /* IPv6 address */
+    if (bin_len < 16)
+      return FALSE;
+
+    ret = inet_pton(AF_INET6, addr, &bin);
+#endif /* HAVE_IPV6 */
+  }
+
+  return ret != 0;
+}
diff --git a/lib/silcutil/unix/silcunixschedule.c b/lib/silcutil/unix/silcunixschedule.c
new file mode 100644 (file)
index 0000000..78a366a
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+
+  silcunixschedule.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1998 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcschedule_i.h"
+
+/* Calls normal select() system call. */
+
+int silc_select(SilcScheduleFd fds, uint32 fds_count, struct timeval *timeout)
+{
+  fd_set in, out;
+  int ret, i, max_fd = 0;
+
+  FD_ZERO(&in);
+  FD_ZERO(&out);
+
+  for (i = 0; i < fds_count; i++) {
+    if (!fds[i].events)
+      continue;
+
+    if (fds[i].fd > max_fd)
+      max_fd = fds[i].fd;
+
+    if (fds[i].events & SILC_TASK_READ)
+      FD_SET(fds[i].fd, &in);
+    if (fds[i].events & SILC_TASK_WRITE)
+      FD_SET(fds[i].fd, &out);
+
+    fds[i].revents = 0;
+  }
+
+  ret = select(max_fd + 1, &in, &out, NULL, timeout);
+  if (ret <= 0)
+    return ret;
+
+  for (i = 0; i < fds_count; i++) {
+    if (!fds[i].events)
+      continue;
+
+    if (FD_ISSET(fds[i].fd, &in))
+      fds[i].revents |= SILC_TASK_READ;
+    if (FD_ISSET(fds[i].fd, &out))
+      fds[i].revents |= SILC_TASK_WRITE;
+  }
+
+  return ret;
+}
+
+#ifdef SILC_THREADS
+
+/* Internal wakeup context. */
+typedef struct {
+  int wakeup_pipe[2];
+  SilcTask wakeup_task;
+} *SilcUnixWakeup;
+
+SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
+{
+  SilcUnixWakeup wakeup = (SilcUnixWakeup)context;
+  unsigned char c;
+
+  read(wakeup->wakeup_pipe[0], &c, 1);
+}
+
+#endif /* SILC_THREADS */
+
+/* Initializes the wakeup of the scheduler. In multi-threaded environment
+   the scheduler needs to be wakenup when tasks are added or removed from
+   the task queues. This will initialize the wakeup for the scheduler.
+   Any tasks that needs to be registered must be registered to the `queue'.
+   It is quaranteed that the scheduler will automatically free any
+   registered tasks in this queue. This is system specific routine. */
+
+void *silc_schedule_wakeup_init(SilcSchedule schedule)
+{
+#ifdef SILC_THREADS
+  SilcUnixWakeup wakeup;
+
+  wakeup = silc_calloc(1, sizeof(*wakeup));
+
+  if (pipe(wakeup->wakeup_pipe)) {
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  wakeup->wakeup_task = 
+    silc_schedule_task_add(schedule, wakeup->wakeup_pipe[0],
+                          silc_schedule_wakeup_cb, wakeup,
+                          0, 0, SILC_TASK_FD, 
+                          SILC_TASK_PRI_NORMAL);
+  if (!wakeup->wakeup_task) {
+    close(wakeup->wakeup_pipe[0]);
+    close(wakeup->wakeup_pipe[1]);
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  return (void *)wakeup;
+#endif
+  return NULL;
+}
+
+/* Uninitializes the system specific wakeup. */
+
+void silc_schedule_wakeup_uninit(void *context)
+{
+#ifdef SILC_THREADS
+  SilcUnixWakeup wakeup = (SilcUnixWakeup)context;
+
+  if (!wakeup)
+    return;
+
+  close(wakeup->wakeup_pipe[0]);
+  close(wakeup->wakeup_pipe[1]);
+  silc_free(wakeup);
+#endif
+}
+
+/* Wakes up the scheduler */
+
+void silc_schedule_wakeup_internal(void *context)
+{
+#ifdef SILC_THREADS
+  SilcUnixWakeup wakeup = (SilcUnixWakeup)context;
+
+  if (!wakeup)
+    return;
+
+  write(wakeup->wakeup_pipe[1], "!", 1);
+#endif
+}
diff --git a/lib/silcutil/unix/silcunixsockconn.c b/lib/silcutil/unix/silcunixsockconn.c
new file mode 100644 (file)
index 0000000..f6d8744
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+
+  silcunixsockconn.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Writes data from encrypted buffer to the socket connection. If the
+   data cannot be written at once, it will be written later with a timeout. 
+   The data is written from the data section of the buffer, not from head
+   or tail section. This automatically pulls the data section towards end
+   after writing the data. */
+
+int silc_socket_write(SilcSocketConnection sock)
+{
+  int ret = 0;
+  int fd = sock->sock;
+  SilcBuffer src = sock->outbuf;
+
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
+  SILC_LOG_DEBUG(("Writing data to socket %d", fd));
+
+  if (src->len > 0) {
+    ret = write(fd, src->data, src->len);
+    if (ret < 0) {
+      if (errno == EAGAIN) {
+       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
+       return -2;
+      }
+      SILC_LOG_DEBUG(("Cannot write to socket: %s", strerror(errno)));
+      sock->sock_error = errno;
+      return -1;
+    }
+
+    silc_buffer_pull(src, ret);
+  }
+
+  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
+
+  return ret;
+}
+
+/* Reads data from the socket connection into the incoming data buffer.
+   It reads as much as possible from the socket connection. This returns
+   amount of bytes read or -1 on error or -2 on case where all of the
+   data could not be read at once. */
+
+int silc_socket_read(SilcSocketConnection sock)
+{
+  int len = 0;
+  unsigned char buf[SILC_SOCKET_READ_SIZE];
+  int fd = sock->sock;
+
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
+  SILC_LOG_DEBUG(("Reading data from socket %d", fd));
+
+  /* Read the data from the socket. */
+  len = read(fd, buf, sizeof(buf));
+  if (len < 0) {
+    if (errno == EAGAIN || errno == EINTR) {
+      SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
+      return -2;
+    }
+    SILC_LOG_DEBUG(("Cannot read from socket: %d:%s", fd, strerror(errno)));
+    sock->sock_error = errno;
+    return -1;
+  }
+
+  if (!len)
+    return 0;
+
+  /* Insert the data to the buffer. */
+
+  if (!sock->inbuf)
+    sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
+  
+  /* If the data does not fit to the buffer reallocate it */
+  if ((sock->inbuf->end - sock->inbuf->tail) < len)
+    sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen + 
+                                     (len * 2));
+  silc_buffer_put_tail(sock->inbuf, buf, len);
+  silc_buffer_pull_tail(sock->inbuf, len);
+
+  SILC_LOG_DEBUG(("Read %d bytes", len));
+
+  return len;
+}
+
+/* Returns human readable socket error message */
+
+bool silc_socket_get_error(SilcSocketConnection sock, char *error,
+                          uint32 error_len)
+{
+  char *err;
+
+  if (!sock->sock_error)
+    return FALSE;
+
+  err = strerror(sock->sock_error);
+  if (strlen(err) > error_len)
+    return FALSE;
+
+  memset(error, 0, error_len);
+  memcpy(error, err, strlen(err));
+  return TRUE;
+}
diff --git a/lib/silcutil/unix/silcunixthread.c b/lib/silcutil/unix/silcunixthread.c
new file mode 100644 (file)
index 0000000..575c54a
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+
+  silcunixthread.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
+                             bool waitable)
+{
+#ifdef SILC_THREADS
+  pthread_attr_t attr;
+  pthread_t thread;
+  int ret;
+
+  SILC_LOG_DEBUG(("Creating new thread"));
+
+  if (!start_func)
+    return NULL;
+
+  if (pthread_attr_init(&attr)) {
+    SILC_LOG_ERROR(("Thread error: %s", strerror(errno)));
+    return NULL;
+  }
+
+  if (pthread_attr_setdetachstate(&attr,
+                                 waitable ? PTHREAD_CREATE_JOINABLE : 
+                                 PTHREAD_CREATE_DETACHED)) {
+    SILC_LOG_ERROR(("Thread error: %s", strerror(errno)));
+    pthread_attr_destroy(&attr);
+    return NULL;
+  }
+
+  ret = pthread_create(&thread, &attr, (void * (*)(void *))start_func, 
+                      context);
+  if (ret) {
+    SILC_LOG_ERROR(("Thread error: %s", strerror(errno)));
+    pthread_attr_destroy(&attr);
+    return NULL;
+  }
+
+  pthread_attr_destroy(&attr);
+
+  SILC_LOG_DEBUG(("Created thread %p", (SilcThread)thread));
+
+  return (SilcThread)thread;
+#else
+  /* Call thread callback immediately */
+  (*start_func)(context);
+  return NULL;
+#endif
+}
+
+void silc_thread_exit(void *exit_value)
+{
+#ifdef SILC_THREADS
+  pthread_exit(exit_value);
+#endif
+}
+
+SilcThread silc_thread_self(void)
+{
+#ifdef SILC_THREADS
+  pthread_t self = pthread_self();
+  return (SilcThread)self;
+#else
+  return NULL;
+#endif
+}
+
+bool silc_thread_wait(SilcThread thread, void **exit_value)
+{
+#ifdef SILC_THREADS
+  SILC_LOG_DEBUG(("Waiting for thread %p", thread));
+  if (!pthread_join(*(pthread_t *)thread, exit_value))
+    return TRUE;
+  return FALSE;
+#else
+  return FALSE;
+#endif
+}
diff --git a/lib/silcutil/unix/silcunixutil.c b/lib/silcutil/unix/silcunixutil.c
new file mode 100644 (file)
index 0000000..cab2086
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+
+  silcunixutil.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/*
+ * These are general utility functions that doesn't belong to any specific
+ * group of routines.
+ */
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* XXX lib/contrib/regex.c might cmopile on WIN32 as well */
+
+/* Inspects the `string' for wildcards and returns regex string that can
+   be used by the GNU regex library. A comma (`,') in the `string' means
+   that the string is list. */
+
+char *silc_string_regexify(const char *string)
+{
+  int i, len, count;
+  char *regex;
+
+  len = strlen(string);
+  count = 4;
+  for (i = 0; i < len; i++)
+    if (string[i] == '*' || string[i] == '?')
+      count++;
+
+  regex = silc_calloc(len + count, sizeof(*regex));
+
+  count = 0;
+  regex[count] = '(';
+  count++;
+
+  for (i = 0; i < len; i++) {
+    if (string[i] == '*' || string[i] == '?') {
+      regex[count] = '.';
+      count++;
+    } else if (string[i] == ',') {
+      if (i + 1 == len)
+       continue;
+      regex[count] = '|';
+      count++;
+      continue;
+    }
+
+    regex[count] = string[i];
+    count++;
+  }
+
+  regex[count++] = ')';
+  regex[count] = '$';
+
+  return regex;
+}
+
+/* Combines two regex strings into one regex string so that they can be
+   used as one by the GNU regex library. The `string2' is combine into
+   the `string1'. */
+
+char *silc_string_regex_combine(const char *string1, const char *string2)
+{
+  char *tmp;
+  int len1, len2;
+
+  len1 = strlen(string1);
+  len2 = strlen(string2);
+
+  tmp = silc_calloc(2 + len1 + len2, sizeof(*tmp));
+  strncat(tmp, string1, len1 - 2);
+  strncat(tmp, "|", 1);
+  strncat(tmp, string2 + 1, len2 - 1);
+
+  return tmp;
+}
+
+/* Matches the two strings and returns TRUE if the strings match. */
+
+int silc_string_regex_match(const char *regex, const char *string)
+{
+  regex_t preg;
+  int ret = FALSE;
+  
+  if (regcomp(&preg, regex, REG_NOSUB | REG_EXTENDED) < 0)
+    return FALSE;
+
+  if (regexec(&preg, string, 0, NULL, 0) == 0)
+    ret = TRUE;
+
+  regfree(&preg);
+
+  return ret;
+}
+
+/* Do regex match to the two strings `string1' and `string2'. If the
+   `string2' matches the `string1' this returns TRUE. */
+
+int silc_string_match(const char *string1, const char *string2)
+{
+  char *s1;
+  int ret = FALSE;
+
+  if (!string1 || !string2)
+    return ret;
+
+  s1 = silc_string_regexify(string1);
+  ret = silc_string_regex_match(s1, string2);
+  silc_free(s1);
+
+  return ret;
+}
+
+/* Returns the username of the user. If the global variable LOGNAME
+   does not exists we will get the name from the password file. */
+
+char *silc_get_username()
+{
+  char *logname = NULL;
+  
+  logname = getenv("LOGNAME");
+  if (!logname) {
+    logname = getlogin();
+    if (!logname) {
+      struct passwd *pw;
+
+      pw = getpwuid(getuid());
+      if (!pw) {
+       fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
+       return NULL;
+      }
+      
+      logname = pw->pw_name;
+    }
+  }
+  
+  return strdup(logname);
+}                          
+
+/* Returns the real name of ther user. */
+
+char *silc_get_real_name()
+{
+  char *realname = NULL;
+  struct passwd *pw;
+    
+  pw = getpwuid(getuid());
+  if (!pw) {
+    fprintf(stderr, "silc_get_username: %s\n", strerror(errno));
+    return NULL;
+  }
+
+  if (strchr(pw->pw_gecos, ','))
+    *strchr(pw->pw_gecos, ',') = 0;
+
+  realname = strdup(pw->pw_gecos);
+
+  return realname;
+}
+
+/* Return current time to struct timeval. */
+
+int silc_gettimeofday(struct timeval *p)
+{
+  return gettimeofday(p, NULL);
+}
diff --git a/lib/silcutil/win32/Makefile.am b/lib/silcutil/win32/Makefile.am
new file mode 100644 (file)
index 0000000..57b8c23
--- /dev/null
@@ -0,0 +1,31 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2001 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+noinst_LIBRARIES = libsilcwin32util.a
+
+libsilcwin32util_a_SOURCES =   \
+       silcwin32net.c          \
+       silcwin32schedule.c     \
+       silcwin32sockconn.c     \
+       silcwin32util.c         \
+       silcwin32mutex.c        \
+       silcwin32thread.c
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/lib/silcutil/win32/silcwin32mutex.c b/lib/silcutil/win32/silcwin32mutex.c
new file mode 100644 (file)
index 0000000..0a186f5
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+
+  silcwin32mutex.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+#ifdef SILC_THREADS
+
+/* SILC Mutex structure */
+struct SilcMutexStruct {
+  HANDLE mutex;
+};
+
+bool silc_mutex_alloc(SilcMutex *mutex)
+{
+  *mutex = silc_calloc(1, sizeof(**mutex));
+  (*mutex)->mutex = CreateMutex(NULL, FALSE, NULL);
+  if (!(*mutex)->mutex) {
+    silc_free(*mutex);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+void silc_mutex_free(SilcMutex mutex)
+{
+  CloseHandle(mutex->mutex);
+  silc_free(mutex);
+}
+
+void silc_mutex_lock(SilcMutex mutex)
+{
+  WaitForSingleObject(mutex->mutex, INFINITE);
+}
+
+void silc_mutex_unlock(SilcMutex mutex)
+{
+  ReleaseMutex(mutex->mutex);
+}
+
+#endif /* SILC_THREADS */
diff --git a/lib/silcutil/win32/silcwin32net.c b/lib/silcutil/win32/silcwin32net.c
new file mode 100644 (file)
index 0000000..928a40d
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+
+  silcwin32net.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+/* XXX IPv6 support missing */
+
+#include "silcincludes.h"
+#include "silcnet.h"
+
+/* This function creates server or daemon or listener or what ever. This
+   does not fork a new process, it must be done by the caller if caller
+   wants to create a child process. This is used by the SILC server. 
+   If argument `ip_addr' is NULL `any' address will be used. Returns 
+   the created socket or -1 on error. */
+
+int silc_net_create_server(int port, const char *ip_addr)
+{
+  SOCKET sock;
+  int rval;
+  struct sockaddr_in server;
+  int len = sizeof(server.sin_addr);
+
+  SILC_LOG_DEBUG(("Creating a new server listener"));
+
+  /* Create the socket */
+  sock = socket(PF_INET, SOCK_STREAM, 0);
+  if (sock == INVALID_SOCKET) {
+    SILC_LOG_ERROR(("Cannot create socket"));
+    return -1;
+  }
+
+  /* Set the socket options */
+  rval = silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+  if (rval != 0) {
+    SILC_LOG_ERROR(("Cannot set socket options"));
+    closesocket(sock);
+    return -1;
+  }
+
+  /* Set the socket information for bind() */
+  memset(&server, 0, sizeof(server));
+  server.sin_family = AF_INET;
+  if (port)
+    server.sin_port = htons(port);
+
+  /* Convert IP address to network byte order */
+  if (ip_addr)
+    silc_net_addr2bin(ip_addr, (unsigned char *)&server.sin_addr.s_addr, len);
+  else
+    server.sin_addr.s_addr = INADDR_ANY;
+
+  /* Bind the server socket */
+  rval = bind(sock, (struct sockaddr *)&server, sizeof(server));
+  if (rval != 0) {
+    SILC_LOG_ERROR(("Cannot bind socket"));
+    closesocket(sock);
+    return -1;
+  }
+
+  /* Specify that we are listenning */
+  rval = listen(sock, 5);
+  if (rval != 0) {
+    SILC_LOG_ERROR(("Cannot set socket listenning"));
+    closesocket(sock);
+    return -1;
+  }
+
+  SILC_LOG_DEBUG(("Server listener created, fd=%d", sock));
+
+  return sock;
+}
+
+/* Closes the server by closing the socket connection. */
+
+void silc_net_close_server(int sock)
+{
+  shutdown(sock, 2);
+  closesocket(sock);
+
+  SILC_LOG_DEBUG(("Server socket closed"));
+}
+
+/* Creates a connection (TCP/IP) to a remote host. Returns the connection
+   socket or -1 on error. This blocks the process while trying to create
+   the connection. */
+
+int silc_net_create_connection(const char *local_ip, int port, 
+                              const char *host)
+{
+  SOCKET sock;
+  int rval, err;
+  struct hostent *dest;
+  struct sockaddr_in desthost;
+
+  SILC_LOG_DEBUG(("Creating connection to host %s port %d", host, port));
+
+  /* Do host lookup */
+  dest = gethostbyname(host);
+  if (!dest) {
+    SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
+                   "IP address", host));
+    return -1;
+  }
+
+  /* Set socket information */
+  memset(&desthost, 0, sizeof(desthost));
+  desthost.sin_port = htons(port);
+  desthost.sin_family = AF_INET;
+  memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
+
+  /* Create the connection socket */
+  sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock == INVALID_SOCKET) {
+    SILC_LOG_ERROR(("Cannot create socket"));
+    return -1;
+  }
+
+  /* Connect to the host */
+  rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
+  err = WSAGetLastError();
+  if (rval == SOCKET_ERROR && err != WSAEWOULDBLOCK) {
+    SILC_LOG_ERROR(("Cannot connect to remote host"));
+    shutdown(sock, 2);
+    closesocket(sock);
+    return -1;
+  }
+
+  /* Set appropriate options */
+#if defined(TCP_NODELAY)
+  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
+#endif
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
+
+  SILC_LOG_DEBUG(("Connection created"));
+
+  return sock;
+}
+
+/* Creates a connection (TCP/IP) to a remote host. Returns the connection
+   socket or -1 on error. This creates non-blocking socket hence the
+   connection returns directly. To get the result of the connect() one
+   must select() the socket and read the result after it's ready. */
+
+int silc_net_create_connection_async(const char *local_ip, int port, 
+                                    const char *host)
+{
+  SOCKET sock;
+  int rval, err;
+  struct hostent *dest;
+  struct sockaddr_in desthost;
+
+  SILC_LOG_DEBUG(("Creating connection (async) to host %s port %d", 
+                 host, port));
+
+  /* Do host lookup */
+  dest = gethostbyname(host);
+  if (!dest) {
+    SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
+                   "IP address", host));
+    return -1;
+  }
+
+  /* Set socket information */
+  memset(&desthost, 0, sizeof(desthost));
+  desthost.sin_port = htons(port);
+  desthost.sin_family = AF_INET;
+  memcpy(&desthost.sin_addr, dest->h_addr_list[0], sizeof(desthost.sin_addr));
+
+  /* Create the connection socket */
+  sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock == INVALID_SOCKET) {
+    SILC_LOG_ERROR(("Cannot create socket"));
+    return -1;
+  }
+
+  /* Connect to the host */
+  rval = connect(sock, (struct sockaddr *)&desthost, sizeof(desthost));
+  err = WSAGetLastError();
+  if (rval == SOCKET_ERROR && err != WSAEWOULDBLOCK) {
+    SILC_LOG_ERROR(("Cannot connect to remote host"));
+    shutdown(sock, 2);
+    closesocket(sock);
+    return -1;
+  }
+
+  /* Set socket to nonblocking mode */
+  silc_net_set_socket_nonblock(sock);
+
+  /* Set appropriate options */
+#if defined(TCP_NODELAY)
+  silc_net_set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1);
+#endif
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_KEEPALIVE, 1);
+
+  SILC_LOG_DEBUG(("Connection created"));
+
+  return sock;
+}
+
+/* Closes the connection by closing the socket connection. */
+
+void silc_net_close_connection(int sock)
+{
+  closesocket(sock);
+}
+
+/* Converts the IP number string from numbers-and-dots notation to
+   binary form. */
+
+bool silc_net_addr2bin(const char *addr, void *bin, uint32 bin_len)
+{
+  unsigned long ret;
+
+  ret = inet_addr(addr);
+
+  if (bin_len < 4)
+    return FALSE;
+
+  memcpy(bin, (unsigned char *)&ret, 4);
+  return ret != INADDR_NONE;
+}
+
+/* Set socket to non-blocking mode. */
+
+int silc_net_set_socket_nonblock(int sock)
+{
+  unsigned long on = 1;
+  return ioctlsocket(sock, FIONBIO, &on);
+}
+
+/* Init Winsock2. */
+
+bool silc_net_win32_init(void)
+{
+  int ret, sopt = SO_SYNCHRONOUS_NONALERT;
+  WSADATA wdata;
+  WORD ver = MAKEWORD(1, 1);
+
+  ret = WSAStartup(ver, &wdata);
+  if (ret)
+    return FALSE;
+
+  /* Allow using the SOCKET's as file descriptors so that we can poll
+     them with SILC Scheduler. */
+  ret = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&sopt,
+                  sizeof(sopt));
+  if (ret)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Uninit Winsock2 */
+
+void silc_net_win32_uninit(void)
+{
+  WSACleanup();
+}
diff --git a/lib/silcutil/win32/silcwin32schedule.c b/lib/silcutil/win32/silcwin32schedule.c
new file mode 100644 (file)
index 0000000..4c83514
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+
+  silcwin32schedule.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+#include "silcschedule_i.h"
+
+/* Our "select()" for WIN32. This mimics the behaviour of select() system
+   call. It does not call the Winsock's select() though. Its functions
+   are derived from GLib's g_poll() and from some old Xemacs's sys_select().
+
+   This makes following assumptions, which I don't know whether they
+   are correct or not:
+
+   o SILC_TASK_WRITE is ignored, if set this will return immediately.
+   o If all arguments except timeout are NULL then this will register
+     a timeout with SetTimer and will wait just for Windows messages
+     with WaitMessage.
+   o MsgWaitForMultipleObjects is used to wait all kind of events, this
+     includes SOCKETs and Windows messages.
+   o All Windows messages are dispatched from this function.
+   o The Operating System has Winsock 2.
+
+   References:
+
+   o http://msdn.microsoft.com/library/default.asp?
+     url=/library/en-us/winui/hh/winui/messques_77zk.asp 
+   o http://msdn.microsoft.com/library/default.asp?
+     url=/library/en-us/winsock/hh/winsock/apistart_9g1e.asp
+   o http://msdn.microsoft.com/library/default.asp?
+     url=/library/en-us/dnmgmt/html/msdn_getpeek.asp
+   o http://developer.novell.com/support/winsock/doc/toc.htm
+
+*/
+
+int silc_select(SilcScheduleFd fds, uint32 fds_count, struct timeval *timeout)
+{
+  HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+  DWORD ready, curtime, timeo;
+  int nhandles = 0, i;
+  MSG msg;
+
+  if (fds_count > MAXIMUM_WAIT_OBJECTS)
+    fds_count = MAXIMUM_WAIT_OBJECTS;
+
+  for (i = 0; i < fds_count; i++) {
+    if (!fds[i].events)
+      continue;
+
+    if (fds[i].events & SILC_TASK_READ)
+      handles[nhandles++] = (HANDLE)fds[i].fd;
+
+    /* If writing then just set the bit and return */
+    if (fds[i].events & SILC_TASK_WRITE) {
+      fds[i].revents = SILC_TASK_WRITE;
+      return 1;
+    }
+
+    fds[i].revents = 0;
+  }
+
+  timeo = (timeout ? (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000) :
+          INFINITE);
+
+  /* If we have nothing to wait and timeout is set then register a timeout
+     and wait just for windows messages. */
+  if (nhandles == 0 && timeout) {
+    UINT timer = SetTimer(NULL, 0, timeo, NULL);
+    curtime = GetTickCount();
+    while (timer) {
+      WaitMessage();
+
+      while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+       if (msg.message == WM_TIMER) {
+         KillTimer(NULL, timer);
+         return 0;
+       }
+       TranslateMessage(&msg); 
+       DispatchMessage(&msg); 
+      }
+
+      KillTimer(NULL, timer);
+      if (timeo != INFINITE) {
+       timeo -= GetTickCount() - curtime;
+       if (timeo < 0)
+         timeo = 0;
+      }
+      timer = SetTimer(NULL, 0, timeo, NULL);
+    }
+  }
+
+ retry:
+  curtime = GetTickCount();
+  ready = MsgWaitForMultipleObjects(nhandles, handles, FALSE, timeo, 
+                                   QS_ALLINPUT);
+
+  if (ready == WAIT_FAILED) {
+    /* Wait failed with error */
+    SILC_LOG_WARNING(("WaitForMultipleObjects() failed"));
+    return -1;
+  } else if (ready >= WAIT_ABANDONED_0 &&
+            ready < WAIT_ABANDONED_0 + nhandles) {
+    /* Signal abandoned */
+    SILC_LOG_WARNING(("WaitForMultipleObjects() failed (ABANDONED)"));
+    return -1;
+  } else if (ready == WAIT_TIMEOUT) {
+    /* Timeout */
+    return 0;
+  } else if (ready == WAIT_OBJECT_0 + nhandles) {
+    /* Windows messages. The MSDN online says that if the application
+       creates a window then its main loop (and we're assuming that
+       it is our SILC Scheduler) must handle the Windows messages, so do
+       it here as the MSDN suggests. */
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+      TranslateMessage(&msg); 
+      DispatchMessage(&msg); 
+    }
+
+    /* If timeout is set then we must update the timeout since we won't
+       return and we will give the wait another try. */
+    if (timeo != INFINITE) {
+      timeo -= GetTickCount() - curtime;
+      if (timeo < 0)
+       timeo = 0;
+    }
+
+    /* Give the wait another try */
+   goto retry;
+  } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) {
+    /* Some other event, like SOCKET or something. */
+
+    /* Go through all fds even though only one was set. This is to avoid
+       starvation of high numbered fds. */
+    ready -= WAIT_OBJECT_0;
+    do {
+      for (i = 0; i < fds_count; i++) {
+       if (!fds[i].events)
+         continue;
+       
+       if (fds[i].fd == (int)handles[ready]) {
+         fds[i].revents |= SILC_TASK_READ;
+         break;
+       }
+      }
+
+      /* Check the status of the next handle and set its fd to the fd
+        set if data is available. */
+      while (++ready < fds_count)
+       if (WaitForSingleObject(handles[ready], 0) == WAIT_OBJECT_0)
+         break;
+    } while (ready < fds_count);
+
+    return i + 1;
+  }
+
+  return -1;
+}
+
+#ifdef SILC_THREADS
+
+/* Internal wakeup context. */
+typedef struct {
+  HANDLE wakeup_sema;
+  SilcTask wakeup_task;
+} *SilcWin32Wakeup;
+
+SILC_TASK_CALLBACK(silc_schedule_wakeup_cb)
+{
+  /* Nothing */
+}
+
+#endif /* SILC_THREADS */
+
+/* Initializes the wakeup of the scheduler. In multi-threaded environment
+   the scheduler needs to be wakenup when tasks are added or removed from
+   the task queues. This will initialize the wakeup for the scheduler.
+   Any tasks that needs to be registered must be registered to the `queue'.
+   It is guaranteed that the scheduler will automatically free any
+   registered tasks in this queue. This is system specific routine. */
+
+void *silc_schedule_wakeup_init(SilcSchedule schedule)
+{
+#ifdef SILC_THREADS
+  SilcWin32Wakeup wakeup;
+
+  wakeup = silc_calloc(1, sizeof(*wakeup));
+
+  wakeup->wakeup_sema = CreateSemaphore(NULL, 0, 100, NULL);
+  if (!wakeup->wakeup_sema) {
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  wakeup->wakeup_task = 
+    silc_schedule_task_add(schedule, (int)wakeup->wakeup_sema,
+                          silc_schedule_wakeup_cb, wakeup,
+                          0, 0, SILC_TASK_FD, 
+                          SILC_TASK_PRI_NORMAL);
+  if (!wakeup->wakeup_task) {
+    CloseHandle(wakeup->wakeup_sema);
+    silc_free(wakeup);
+    return NULL;
+  }
+
+  return (void *)wakeup;
+#else
+  return NULL;
+#endif
+}
+
+/* Uninitializes the system specific wakeup. */
+
+void silc_schedule_wakeup_uninit(void *context)
+{
+#ifdef SILC_THREADS
+  SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
+
+  if (!wakeup)
+    return;
+
+  CloseHandle(wakeup->wakeup_sema);
+  silc_free(wakeup);
+#endif
+}
+
+/* Wakes up the scheduler */
+
+void silc_schedule_wakeup_internal(void *context)
+{
+#ifdef SILC_THREADS
+  SilcWin32Wakeup wakeup = (SilcWin32Wakeup)context;
+
+  if (!wakeup)
+    return;
+
+  ReleaseSemaphore(wakeup->wakeup_sema, 1, NULL);
+#endif
+}
diff --git a/lib/silcutil/win32/silcwin32sockconn.c b/lib/silcutil/win32/silcwin32sockconn.c
new file mode 100644 (file)
index 0000000..c63a77e
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+
+  silcwin32sockconn.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 1997 - 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* Writes data from encrypted buffer to the socket connection. If the
+   data cannot be written at once, it will be written later with a timeout. 
+   The data is written from the data section of the buffer, not from head
+   or tail section. This automatically pulls the data section towards end
+   after writing the data. */
+
+int silc_socket_write(SilcSocketConnection sock)
+{
+  int ret = 0, err;
+  SOCKET fd = sock->sock;
+  SilcBuffer src = sock->outbuf;
+
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
+  SILC_LOG_DEBUG(("Writing data to socket %d", fd));
+
+  if (src->len > 0) {
+    ret = send(fd, src->data, src->len,  0);
+    if (ret == SOCKET_ERROR) {
+      err = WSAGetLastError();
+      if (err == WSAEWOULDBLOCK) {
+       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
+       return -2;
+      }
+      SILC_LOG_ERROR(("Cannot write to socket: %d", (int)fd));
+      sock->sock_error = err;
+      return -1;
+    }
+
+    silc_buffer_pull(src, ret);
+  }
+
+  SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
+
+  return ret;
+}
+
+/* Reads data from the socket connection into the incoming data buffer.
+   It reads as much as possible from the socket connection. This returns
+   amount of bytes read or -1 on error or -2 on case where all of the
+   data could not be read at once. */
+
+int silc_socket_read(SilcSocketConnection sock)
+{
+  int len = 0, err;
+  unsigned char buf[SILC_SOCKET_READ_SIZE];
+  SOCKET fd = sock->sock;
+  int argp;
+
+  if (SILC_IS_DISABLED(sock))
+    return -1;
+
+  SILC_LOG_DEBUG(("Reading data from socket %d", fd));
+
+  /* Check whether there is data available, without calling recv(). */
+  ioctlsocket(fd, FIONREAD, (unsigned long *)&argp);
+  if (argp == 0) {
+    /* Is this kludge or what? Without this thing this contraption
+       does not work at all!?. */
+    SleepEx(1, TRUE);
+    SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
+    return -2;
+  }
+
+  /* Read the data from the socket. */
+  len = recv(fd, buf, sizeof(buf), 0);
+  if (len == SOCKET_ERROR) {
+    err = WSAGetLastError();
+    if (err == WSAEWOULDBLOCK || err == WSAEINTR) {
+      SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
+      return -2;
+    }
+    SILC_LOG_ERROR(("Cannot read from socket: %d", (int)fd));
+    sock->sock_error = err;
+    return -1;
+  }
+
+  if (!len)
+    return 0;
+
+  /* Insert the data to the buffer. */
+
+  if (!sock->inbuf)
+    sock->inbuf = silc_buffer_alloc(SILC_SOCKET_BUF_SIZE);
+  
+  /* If the data does not fit to the buffer reallocate it */
+  if ((sock->inbuf->end - sock->inbuf->tail) < len)
+    sock->inbuf = silc_buffer_realloc(sock->inbuf, sock->inbuf->truelen + 
+                                     (len * 2));
+  silc_buffer_put_tail(sock->inbuf, buf, len);
+  silc_buffer_pull_tail(sock->inbuf, len);
+
+  SILC_LOG_DEBUG(("Read %d bytes", len));
+
+  return len;
+}
+
+/* Returns human readable socket error message */
+
+bool silc_socket_get_error(SilcSocketConnection sock, char *error,
+                          uint32 error_len)
+{
+  /* XXX TODO */
+  return FALSE;
+}
diff --git a/lib/silcutil/win32/silcwin32thread.c b/lib/silcutil/win32/silcwin32thread.c
new file mode 100644 (file)
index 0000000..6e6a909
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+
+  silcwin32thread.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* These routines are based on GLib's WIN32 gthread implementation and
+   thus credits should go there. */
+/* $Id$ */
+
+#include "silcincludes.h"
+
+#ifdef SILC_THREADS
+
+/* Thread structure for WIN32 */
+typedef struct {
+  HANDLE thread;
+  SilcThreadStart start_func;
+  void *context;
+  bool waitable;
+} *SilcWin32Thread;
+
+static DWORD silc_thread_tls;
+
+/* Actual routine that is called by WIN32 when the thread is created.
+   We will call the start_func from here. When this returns the thread
+   is destroyed. */
+
+unsigned __stdcall silc_thread_win32_start(void *context)
+{
+  SilcWin32Thread thread = (SilcWin32Thread)context;
+
+  TlsSetValue(silc_thread_tls, context);
+  thread->start_func(thread->context);
+  silc_thread_exit(NULL);
+
+  return 0;
+}
+#endif
+
+SilcThread silc_thread_create(SilcThreadStart start_func, void *context,
+                             bool waitable)
+{
+#ifdef SILC_THREADS
+  SilcWin32Thread thread;
+  unsigned id;
+
+  SILC_LOG_DEBUG(("Creating new thread"));
+
+  thread = silc_calloc(1, sizeof(*thread));
+  thread->start_func = start_func;
+  thread->context = context;
+  thread->waitable = waitable;
+  thread->thread = (HANDLE)_beginthreadex(NULL, 0, silc_thread_win32_start,
+                                         (void *)thread, 0, &id);
+  if (!thread->thread) {
+    SILC_LOG_ERROR(("Could not create new thread"));
+    silc_free(thread);
+    return NULL;
+  }
+
+  return (SilcThread)thread;
+#else
+  /* Call thread callback immediately */
+  (*start_func)(context);
+  return NULL;
+#endif
+}
+
+void silc_thread_exit(void *exit_value)
+{
+#ifdef SILC_THREADS
+  SilcWin32Thread thread = TlsGetValue(silc_thread_tls);
+  
+  if (thread) {
+    /* If the thread is waitable the memory is freed only in silc_thread_wait
+       by another thread. If not waitable, free it now. */
+    if (!thread->waitable) {
+      CloseHandle(thread->thread);
+      silc_free(thread);
+    }
+
+    TlsSetValue(silc_thread_tls, NULL);
+  }
+
+  _endthreadex(0);
+#endif
+}
+
+SilcThread silc_thread_self(void)
+{
+#ifdef SILC_THREADS
+  SilcWin32Thread self = TlsGetValue(silc_thread_tls);
+
+  if (!self) {
+    /* This should only happen for the main thread! */
+    HANDLE handle = GetCurrentThread ();
+    HANDLE process = GetCurrentProcess ();
+    self = silc_calloc(1, sizeof(*self));
+    DuplicateHandle(process, handle, process, 
+                   &self->thread, 0, FALSE, 
+                   DUPLICATE_SAME_ACCESS);
+    TlsSetValue(silc_thread_tls, self);
+  }
+
+  return (SilcThread)self;
+#else
+  return NULL;
+#endif
+}
+
+bool silc_thread_wait(SilcThread thread, void **exit_value)
+{
+#ifdef SILC_THREADS
+  SilcWin32Thread self = (SilcWin32Thread)thread;
+
+  SILC_LOG_DEBUG(("Waiting for thread %p", self));
+
+  if (!self->waitable)
+    return FALSE;
+
+  /* The thread is waitable thus we will free all memory after the
+     WaitForSingleObject returns, the thread is destroyed after that. */
+  WaitForSingleObject(self->thread, INFINITE);
+  CloseHandle(self->thread);
+  silc_free(self);
+  if (exit_value)
+    *exit_value = NULL;
+
+  return TRUE;
+#else
+  return FALSE;
+#endif
+}
diff --git a/lib/silcutil/win32/silcwin32util.c b/lib/silcutil/win32/silcwin32util.c
new file mode 100644 (file)
index 0000000..c8d9c9b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+
+  silcwin32util.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "silcincludes.h"
+
+/* XXX GNU regex may work on Win32 too!! */
+char *silc_string_regexify(const char *string)
+{
+  return strdup(string);
+}
+
+char *silc_string_regex_combine(const char *string1, const char *string2)
+{
+  return strdup(string1);
+}
+
+int silc_string_regex_match(const char *regex, const char *string)
+{
+  return TRUE;
+}
+
+int silc_string_match(const char *string1, const char *string2)
+{
+  return TRUE;
+}
+
+#define FILETIME_1970 0x019db1ded53e8000
+const BYTE DWLEN = sizeof(DWORD) * 8;
+
+/* Return current time in struct timeval. Code ripped from some xntp
+   implementation on http://src.openresources.com. */
+
+int silc_gettimeofday(struct timeval *tv)
+{
+  FILETIME ft;
+  __int64 msec;
+  
+  GetSystemTimeAsFileTime(&ft);
+  msec = (__int64) ft.dwHighDateTime << DWLEN | ft.dwLowDateTime;
+  msec = (msec - FILETIME_1970) / 10;
+  tv->tv_sec  = (long) (msec / 1000000);
+  tv->tv_usec = (long) (msec % 1000000);
+
+  return 0;
+}
diff --git a/ltmain.sh b/ltmain.sh
new file mode 100644 (file)
index 0000000..2393e14
--- /dev/null
+++ b/ltmain.sh
@@ -0,0 +1,4946 @@
+# ltmain.sh - Provide generalized library-building support services.
+# NOTE: Changing this file will not affect anything until you rerun configure.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Check that we have a working $echo.
+if test "X$1" = X--no-reexec; then
+  # Discard the --no-reexec flag, and continue.
+  shift
+elif test "X$1" = X--fallback-echo; then
+  # Avoid inline document here, it may be left over
+  :
+elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then
+  # Yippee, $echo works!
+  :
+else
+  # Restart under the correct shell, and then maybe $echo will work.
+  exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+  # used as fallback echo
+  shift
+  cat <<EOF
+$*
+EOF
+  exit 0
+fi
+
+# The name of this program.
+progname=`$echo "$0" | sed 's%^.*/%%'`
+modename="$progname"
+
+# Constants.
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=1.4
+TIMESTAMP=" (1.920 2001/04/24 23:26:18)"
+
+default_mode=
+help="Try \`$progname --help' for more information."
+magic="%%%MAGIC variable%%%"
+mkdir="mkdir"
+mv="mv -f"
+rm="rm -f"
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e 1s/^X//'
+sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g'
+SP2NL='tr \040 \012'
+NL2SP='tr \015\012 \040\040'
+
+# NLS nuisances.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+# We save the old values to restore during execute mode.
+if test "${LC_ALL+set}" = set; then
+  save_LC_ALL="$LC_ALL"; LC_ALL=C; export LC_ALL
+fi
+if test "${LANG+set}" = set; then
+  save_LANG="$LANG"; LANG=C; export LANG
+fi
+
+if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+  echo "$modename: not configured to build any kind of library" 1>&2
+  echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+  exit 1
+fi
+
+# Global variables.
+mode=$default_mode
+nonopt=
+prev=
+prevopt=
+run=
+show="$echo"
+show_help=
+execute_dlfiles=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+
+# Parse our command line options once, thoroughly.
+while test $# -gt 0
+do
+  arg="$1"
+  shift
+
+  case $arg in
+  -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$prev"; then
+    case $prev in
+    execute_dlfiles)
+      execute_dlfiles="$execute_dlfiles $arg"
+      ;;
+    *)
+      eval "$prev=\$arg"
+      ;;
+    esac
+
+    prev=
+    prevopt=
+    continue
+  fi
+
+  # Have we seen a non-optional argument yet?
+  case $arg in
+  --help)
+    show_help=yes
+    ;;
+
+  --version)
+    echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP"
+    exit 0
+    ;;
+
+  --config)
+    sed -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $0
+    exit 0
+    ;;
+
+  --debug)
+    echo "$progname: enabling shell trace mode"
+    set -x
+    ;;
+
+  --dry-run | -n)
+    run=:
+    ;;
+
+  --features)
+    echo "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      echo "enable shared libraries"
+    else
+      echo "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      echo "enable static libraries"
+    else
+      echo "disable static libraries"
+    fi
+    exit 0
+    ;;
+
+  --finish) mode="finish" ;;
+
+  --mode) prevopt="--mode" prev=mode ;;
+  --mode=*) mode="$optarg" ;;
+
+  --quiet | --silent)
+    show=:
+    ;;
+
+  -dlopen)
+    prevopt="-dlopen"
+    prev=execute_dlfiles
+    ;;
+
+  -*)
+    $echo "$modename: unrecognized option \`$arg'" 1>&2
+    $echo "$help" 1>&2
+    exit 1
+    ;;
+
+  *)
+    nonopt="$arg"
+    break
+    ;;
+  esac
+done
+
+if test -n "$prevopt"; then
+  $echo "$modename: option \`$prevopt' requires an argument" 1>&2
+  $echo "$help" 1>&2
+  exit 1
+fi
+
+if test -z "$show_help"; then
+
+  # Infer the operation mode.
+  if test -z "$mode"; then
+    case $nonopt in
+    *cc | *++ | gcc* | *-gcc*)
+      mode=link
+      for arg
+      do
+       case $arg in
+       -c)
+          mode=compile
+          break
+          ;;
+       esac
+      done
+      ;;
+    *db | *dbx | *strace | *truss)
+      mode=execute
+      ;;
+    *install*|cp|mv)
+      mode=install
+      ;;
+    *rm)
+      mode=uninstall
+      ;;
+    *)
+      # If we have no mode, but dlfiles were specified, then do execute mode.
+      test -n "$execute_dlfiles" && mode=execute
+
+      # Just use the default operation mode.
+      if test -z "$mode"; then
+       if test -n "$nonopt"; then
+         $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2
+       else
+         $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2
+       fi
+      fi
+      ;;
+    esac
+  fi
+
+  # Only execute mode is allowed to have -dlopen flags.
+  if test -n "$execute_dlfiles" && test "$mode" != execute; then
+    $echo "$modename: unrecognized option \`-dlopen'" 1>&2
+    $echo "$help" 1>&2
+    exit 1
+  fi
+
+  # Change the help message to a mode-specific one.
+  generic_help="$help"
+  help="Try \`$modename --help --mode=$mode' for more information."
+
+  # These modes are in order of execution frequency so that they run quickly.
+  case $mode in
+  # libtool compile mode
+  compile)
+    modename="$modename: compile"
+    # Get the compilation command and the source file.
+    base_compile=
+    prev=
+    lastarg=
+    srcfile="$nonopt"
+    suppress_output=
+
+    user_target=no
+    for arg
+    do
+      case $prev in
+      "") ;;
+      xcompiler)
+       # Aesthetically quote the previous argument.
+       prev=
+       lastarg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+
+       case $arg in
+       # Double-quote args containing other shell metacharacters.
+       # Many Bourne shells cannot handle close brackets correctly
+       # in scan sets, so we specify it separately.
+       *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \   ]*|*]*|"")
+         arg="\"$arg\""
+         ;;
+       esac
+
+       # Add the previous argument to base_compile.
+       if test -z "$base_compile"; then
+         base_compile="$lastarg"
+       else
+         base_compile="$base_compile $lastarg"
+       fi
+       continue
+       ;;
+      esac
+
+      # Accept any command-line options.
+      case $arg in
+      -o)
+       if test "$user_target" != "no"; then
+         $echo "$modename: you cannot specify \`-o' more than once" 1>&2
+         exit 1
+       fi
+       user_target=next
+       ;;
+
+      -static)
+       build_old_libs=yes
+       continue
+       ;;
+
+      -prefer-pic)
+       pic_mode=yes
+       continue
+       ;;
+
+      -prefer-non-pic)
+       pic_mode=no
+       continue
+       ;;
+
+      -Xcompiler)
+       prev=xcompiler
+       continue
+       ;;
+
+      -Wc,*)
+       args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"`
+       lastarg=
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS=','
+       for arg in $args; do
+         IFS="$save_ifs"
+
+         # Double-quote args containing other shell metacharacters.
+         # Many Bourne shells cannot handle close brackets correctly
+         # in scan sets, so we specify it separately.
+         case $arg in
+           *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \       ]*|*]*|"")
+           arg="\"$arg\""
+           ;;
+         esac
+         lastarg="$lastarg $arg"
+       done
+       IFS="$save_ifs"
+       lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"`
+
+       # Add the arguments to base_compile.
+       if test -z "$base_compile"; then
+         base_compile="$lastarg"
+       else
+         base_compile="$base_compile $lastarg"
+       fi
+       continue
+       ;;
+      esac
+
+      case $user_target in
+      next)
+       # The next one is the -o target name
+       user_target=yes
+       continue
+       ;;
+      yes)
+       # We got the output file
+       user_target=set
+       libobj="$arg"
+       continue
+       ;;
+      esac
+
+      # Accept the current argument as the source file.
+      lastarg="$srcfile"
+      srcfile="$arg"
+
+      # Aesthetically quote the previous argument.
+
+      # Backslashify any backslashes, double quotes, and dollar signs.
+      # These are the only characters that are still specially
+      # interpreted inside of double-quoted scrings.
+      lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"`
+
+      # Double-quote args containing other shell metacharacters.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      case $lastarg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+       lastarg="\"$lastarg\""
+       ;;
+      esac
+
+      # Add the previous argument to base_compile.
+      if test -z "$base_compile"; then
+       base_compile="$lastarg"
+      else
+       base_compile="$base_compile $lastarg"
+      fi
+    done
+
+    case $user_target in
+    set)
+      ;;
+    no)
+      # Get the name of the library object.
+      libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'`
+      ;;
+    *)
+      $echo "$modename: you must specify a target with \`-o'" 1>&2
+      exit 1
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    xform='[cCFSfmso]'
+    case $libobj in
+    *.ada) xform=ada ;;
+    *.adb) xform=adb ;;
+    *.ads) xform=ads ;;
+    *.asm) xform=asm ;;
+    *.c++) xform=c++ ;;
+    *.cc) xform=cc ;;
+    *.cpp) xform=cpp ;;
+    *.cxx) xform=cxx ;;
+    *.f90) xform=f90 ;;
+    *.for) xform=for ;;
+    esac
+
+    libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"`
+
+    case $libobj in
+    *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;;
+    *)
+      $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2
+      exit 1
+      ;;
+    esac
+
+    if test -z "$base_compile"; then
+      $echo "$modename: you must specify a compilation command" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $libobj"
+    else
+      removelist="$libobj"
+    fi
+
+    $run $rm $removelist
+    trap "$run $rm $removelist; exit 1" 1 2 15
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2*)
+      pic_mode=default
+      ;;
+    esac
+    if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+      removelist="$removelist $output_obj $lockfile"
+      trap "$run $rm $removelist; exit 1" 1 2 15
+    else
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until $run ln "$0" "$lockfile" 2>/dev/null; do
+       $show "Waiting for $lockfile to be removed"
+       sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+       echo "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $run $rm $removelist
+       exit 1
+      fi
+      echo $srcfile > "$lockfile"
+    fi
+
+    if test -n "$fix_srcfile_path"; then
+      eval srcfile=\"$fix_srcfile_path\"
+    fi
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test "$pic_mode" != no; then
+       # All platforms use -DPIC, to notify preprocessed assembler code.
+       command="$base_compile $srcfile $pic_flag -DPIC"
+      else
+       # Don't build PIC code
+       command="$base_compile $srcfile"
+      fi
+      if test "$build_old_libs" = yes; then
+       lo_libobj="$libobj"
+       dir=`$echo "X$libobj" | $Xsed -e 's%/[^/]*$%%'`
+       if test "X$dir" = "X$libobj"; then
+         dir="$objdir"
+       else
+         dir="$dir/$objdir"
+       fi
+       libobj="$dir/"`$echo "X$libobj" | $Xsed -e 's%^.*/%%'`
+
+       if test -d "$dir"; then
+         $show "$rm $libobj"
+         $run $rm $libobj
+       else
+         $show "$mkdir $dir"
+         $run $mkdir $dir
+         status=$?
+         if test $status -ne 0 && test ! -d $dir; then
+           exit $status
+         fi
+       fi
+      fi
+      if test "$compiler_o_lo" = yes; then
+       output_obj="$libobj"
+       command="$command -o $output_obj"
+      elif test "$compiler_c_o" = yes; then
+       output_obj="$obj"
+       command="$command -o $output_obj"
+      fi
+
+      $run $rm "$output_obj"
+      $show "$command"
+      if $run eval "$command"; then :
+      else
+       test -n "$output_obj" && $run $rm $removelist
+       exit 1
+      fi
+
+      if test "$need_locks" = warn &&
+        test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then
+       echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $run $rm $removelist
+       exit 1
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test x"$output_obj" != x"$libobj"; then
+       $show "$mv $output_obj $libobj"
+       if $run $mv $output_obj $libobj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+
+      # If we have no pic_flag, then copy the object into place and finish.
+      if (test -z "$pic_flag" || test "$pic_mode" != default) &&
+        test "$build_old_libs" = yes; then
+       # Rename the .lo from within objdir to obj
+       if test -f $obj; then
+         $show $rm $obj
+         $run $rm $obj
+       fi
+
+       $show "$mv $libobj $obj"
+       if $run $mv $libobj $obj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+
+       xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'`
+       if test "X$xdir" = "X$obj"; then
+         xdir="."
+       else
+         xdir="$xdir"
+       fi
+       baseobj=`$echo "X$obj" | $Xsed -e "s%.*/%%"`
+       libobj=`$echo "X$baseobj" | $Xsed -e "$o2lo"`
+       # Now arrange that obj and lo_libobj become the same file
+       $show "(cd $xdir && $LN_S $baseobj $libobj)"
+       if $run eval '(cd $xdir && $LN_S $baseobj $libobj)'; then
+         exit 0
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+
+      # Allow error messages only from the first compilation.
+      suppress_output=' >/dev/null 2>&1'
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      if test "$pic_mode" != yes; then
+       # Don't build PIC code
+       command="$base_compile $srcfile"
+      else
+       # All platforms use -DPIC, to notify preprocessed assembler code.
+       command="$base_compile $srcfile $pic_flag -DPIC"
+      fi
+      if test "$compiler_c_o" = yes; then
+       command="$command -o $obj"
+       output_obj="$obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      command="$command$suppress_output"
+      $run $rm "$output_obj"
+      $show "$command"
+      if $run eval "$command"; then :
+      else
+       $run $rm $removelist
+       exit 1
+      fi
+
+      if test "$need_locks" = warn &&
+        test x"`cat $lockfile 2>/dev/null`" != x"$srcfile"; then
+       echo "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $run $rm $removelist
+       exit 1
+      fi
+
+      # Just move the object if needed
+      if test x"$output_obj" != x"$obj"; then
+       $show "$mv $output_obj $obj"
+       if $run $mv $output_obj $obj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+
+      # Create an invalid libtool object if no PIC, so that we do not
+      # accidentally link it into a program.
+      if test "$build_libtool_libs" != yes; then
+       $show "echo timestamp > $libobj"
+       $run eval "echo timestamp > \$libobj" || exit $?
+      else
+       # Move the .lo from within objdir
+       $show "$mv $libobj $lo_libobj"
+       if $run $mv $libobj $lo_libobj; then :
+       else
+         error=$?
+         $run $rm $removelist
+         exit $error
+       fi
+      fi
+    fi
+
+    # Unlock the critical section if it was locked
+    if test "$need_locks" != no; then
+      $run $rm "$lockfile"
+    fi
+
+    exit 0
+    ;;
+
+  # libtool link mode
+  link | relink)
+    modename="$modename: link"
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invokation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args="$nonopt"
+    compile_command="$nonopt"
+    finalize_command="$nonopt"
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+
+    avoid_version=no
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -all-static | -static)
+       if test "X$arg" = "X-all-static"; then
+         if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+           $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2
+         fi
+         if test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+       else
+         if test -z "$pic_flag" && test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+       fi
+       build_libtool_libs=no
+       build_old_libs=yes
+       prefer_static_libs=yes
+       break
+       ;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test $# -gt 0; do
+      arg="$1"
+      shift
+      case $arg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+       qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test
+       ;;
+      *) qarg=$arg ;;
+      esac
+      libtool_args="$libtool_args $qarg"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+       case $prev in
+       output)
+         compile_command="$compile_command @OUTPUT@"
+         finalize_command="$finalize_command @OUTPUT@"
+         ;;
+       esac
+
+       case $prev in
+       dlfiles|dlprefiles)
+         if test "$preload" = no; then
+           # Add the symbol object into the linking commands.
+           compile_command="$compile_command @SYMFILE@"
+           finalize_command="$finalize_command @SYMFILE@"
+           preload=yes
+         fi
+         case $arg in
+         *.la | *.lo) ;;  # We handle these cases below.
+         force)
+           if test "$dlself" = no; then
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         self)
+           if test "$prev" = dlprefiles; then
+             dlself=yes
+           elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+             dlself=yes
+           else
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         *)
+           if test "$prev" = dlfiles; then
+             dlfiles="$dlfiles $arg"
+           else
+             dlprefiles="$dlprefiles $arg"
+           fi
+           prev=
+           continue
+           ;;
+         esac
+         ;;
+       expsyms)
+         export_symbols="$arg"
+         if test ! -f "$arg"; then
+           $echo "$modename: symbol file \`$arg' does not exist"
+           exit 1
+         fi
+         prev=
+         continue
+         ;;
+       expsyms_regex)
+         export_symbols_regex="$arg"
+         prev=
+         continue
+         ;;
+       release)
+         release="-$arg"
+         prev=
+         continue
+         ;;
+       rpath | xrpath)
+         # We need an absolute path.
+         case $arg in
+         [\\/]* | [A-Za-z]:[\\/]*) ;;
+         *)
+           $echo "$modename: only absolute run-paths are allowed" 1>&2
+           exit 1
+           ;;
+         esac
+         if test "$prev" = rpath; then
+           case "$rpath " in
+           *" $arg "*) ;;
+           *) rpath="$rpath $arg" ;;
+           esac
+         else
+           case "$xrpath " in
+           *" $arg "*) ;;
+           *) xrpath="$xrpath $arg" ;;
+           esac
+         fi
+         prev=
+         continue
+         ;;
+       xcompiler)
+         compiler_flags="$compiler_flags $qarg"
+         prev=
+         compile_command="$compile_command $qarg"
+         finalize_command="$finalize_command $qarg"
+         continue
+         ;;
+       xlinker)
+         linker_flags="$linker_flags $qarg"
+         compiler_flags="$compiler_flags $wl$qarg"
+         prev=
+         compile_command="$compile_command $wl$qarg"
+         finalize_command="$finalize_command $wl$qarg"
+         continue
+         ;;
+       *)
+         eval "$prev=\"\$arg\""
+         prev=
+         continue
+         ;;
+       esac
+      fi # test -n $prev
+
+      prevarg="$arg"
+
+      case $arg in
+      -all-static)
+       if test -n "$link_static_flag"; then
+         compile_command="$compile_command $link_static_flag"
+         finalize_command="$finalize_command $link_static_flag"
+       fi
+       continue
+       ;;
+
+      -allow-undefined)
+       # FIXME: remove this flag sometime in the future.
+       $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2
+       continue
+       ;;
+
+      -avoid-version)
+       avoid_version=yes
+       continue
+       ;;
+
+      -dlopen)
+       prev=dlfiles
+       continue
+       ;;
+
+      -dlpreopen)
+       prev=dlprefiles
+       continue
+       ;;
+
+      -export-dynamic)
+       export_dynamic=yes
+       continue
+       ;;
+
+      -export-symbols | -export-symbols-regex)
+       if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+         $echo "$modename: more than one -exported-symbols argument is not allowed"
+         exit 1
+       fi
+       if test "X$arg" = "X-export-symbols"; then
+         prev=expsyms
+       else
+         prev=expsyms_regex
+       fi
+       continue
+       ;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+       case $with_gcc/$host in
+       no/*-*-irix*)
+         compile_command="$compile_command $arg"
+         finalize_command="$finalize_command $arg"
+         ;;
+       esac
+       continue
+       ;;
+
+      -L*)
+       dir=`$echo "X$arg" | $Xsed -e 's/^-L//'`
+       # We need an absolute path.
+       case $dir in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         absdir=`cd "$dir" && pwd`
+         if test -z "$absdir"; then
+           $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2
+           exit 1
+         fi
+         dir="$absdir"
+         ;;
+       esac
+       case "$deplibs " in
+       *" -L$dir "*) ;;
+       *)
+         deplibs="$deplibs -L$dir"
+         lib_search_path="$lib_search_path $dir"
+         ;;
+       esac
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+         case :$dllsearchpath: in
+         *":$dir:"*) ;;
+         *) dllsearchpath="$dllsearchpath:$dir";;
+         esac
+         ;;
+       esac
+       continue
+       ;;
+
+      -l*)
+       if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+         case $host in
+         *-*-cygwin* | *-*-pw32* | *-*-beos*)
+           # These systems don't actually have a C or math library (as such)
+           continue
+           ;;
+         *-*-mingw* | *-*-os2*)
+           # These systems don't actually have a C library (as such)
+           test "X$arg" = "X-lc" && continue
+           ;;
+         esac
+       fi
+       deplibs="$deplibs $arg"
+       continue
+       ;;
+
+      -module)
+       module=yes
+       continue
+       ;;
+
+      -no-fast-install)
+       fast_install=no
+       continue
+       ;;
+
+      -no-install)
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+         # The PATH hackery in wrapper scripts is required on Windows
+         # in order for the loader to find any dlls it needs.
+         $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2
+         $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2
+         fast_install=no
+         ;;
+       *) no_install=yes ;;
+       esac
+       continue
+       ;;
+
+      -no-undefined)
+       allow_undefined=no
+       continue
+       ;;
+
+      -o) prev=output ;;
+
+      -release)
+       prev=release
+       continue
+       ;;
+
+      -rpath)
+       prev=rpath
+       continue
+       ;;
+
+      -R)
+       prev=xrpath
+       continue
+       ;;
+
+      -R*)
+       dir=`$echo "X$arg" | $Xsed -e 's/^-R//'`
+       # We need an absolute path.
+       case $dir in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         $echo "$modename: only absolute run-paths are allowed" 1>&2
+         exit 1
+         ;;
+       esac
+       case "$xrpath " in
+       *" $dir "*) ;;
+       *) xrpath="$xrpath $dir" ;;
+       esac
+       continue
+       ;;
+
+      -static)
+       # The effects of -static are defined in a previous loop.
+       # We used to do the same as -all-static on platforms that
+       # didn't have a PIC flag, but the assumption that the effects
+       # would be equivalent was wrong.  It would break on at least
+       # Digital Unix and AIX.
+       continue
+       ;;
+
+      -thread-safe)
+       thread_safe=yes
+       continue
+       ;;
+
+      -version-info)
+       prev=vinfo
+       continue
+       ;;
+
+      -Wc,*)
+       args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'`
+       arg=
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS=','
+       for flag in $args; do
+         IFS="$save_ifs"
+         case $flag in
+           *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \       ]*|*]*|"")
+           flag="\"$flag\""
+           ;;
+         esac
+         arg="$arg $wl$flag"
+         compiler_flags="$compiler_flags $flag"
+       done
+       IFS="$save_ifs"
+       arg=`$echo "X$arg" | $Xsed -e "s/^ //"`
+       ;;
+
+      -Wl,*)
+       args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'`
+       arg=
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS=','
+       for flag in $args; do
+         IFS="$save_ifs"
+         case $flag in
+           *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \       ]*|*]*|"")
+           flag="\"$flag\""
+           ;;
+         esac
+         arg="$arg $wl$flag"
+         compiler_flags="$compiler_flags $wl$flag"
+         linker_flags="$linker_flags $flag"
+       done
+       IFS="$save_ifs"
+       arg=`$echo "X$arg" | $Xsed -e "s/^ //"`
+       ;;
+
+      -Xcompiler)
+       prev=xcompiler
+       continue
+       ;;
+
+      -Xlinker)
+       prev=xlinker
+       continue
+       ;;
+
+      # Some other compiler flag.
+      -* | +*)
+       # Unknown arguments in both finalize_command and compile_command need
+       # to be aesthetically quoted because they are evaled later.
+       arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+       case $arg in
+       *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \   ]*|*]*|"")
+         arg="\"$arg\""
+         ;;
+       esac
+       ;;
+
+      *.lo | *.$objext)
+       # A library or standard object.
+       if test "$prev" = dlfiles; then
+         # This file was specified with -dlopen.
+         if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+           dlfiles="$dlfiles $arg"
+           prev=
+           continue
+         else
+           # If libtool objects are unsupported, then we need to preload.
+           prev=dlprefiles
+         fi
+       fi
+
+       if test "$prev" = dlprefiles; then
+         # Preload the old-style object.
+         dlprefiles="$dlprefiles "`$echo "X$arg" | $Xsed -e "$lo2o"`
+         prev=
+       else
+         case $arg in
+         *.lo) libobjs="$libobjs $arg" ;;
+         *) objs="$objs $arg" ;;
+         esac
+       fi
+       ;;
+
+      *.$libext)
+       # An archive.
+       deplibs="$deplibs $arg"
+       old_deplibs="$old_deplibs $arg"
+       continue
+       ;;
+
+      *.la)
+       # A libtool-controlled library.
+
+       if test "$prev" = dlfiles; then
+         # This library was specified with -dlopen.
+         dlfiles="$dlfiles $arg"
+         prev=
+       elif test "$prev" = dlprefiles; then
+         # The library was specified with -dlpreopen.
+         dlprefiles="$dlprefiles $arg"
+         prev=
+       else
+         deplibs="$deplibs $arg"
+       fi
+       continue
+       ;;
+
+      # Some other compiler argument.
+      *)
+       # Unknown arguments in both finalize_command and compile_command need
+       # to be aesthetically quoted because they are evaled later.
+       arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+       case $arg in
+       *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \   ]*|*]*|"")
+         arg="\"$arg\""
+         ;;
+       esac
+       ;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+       compile_command="$compile_command $arg"
+       finalize_command="$finalize_command $arg"
+      fi
+    done # argument parsing loop
+
+    if test -n "$prev"; then
+      $echo "$modename: the \`$prevarg' option requires an argument" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      compile_command="$compile_command $arg"
+      finalize_command="$finalize_command $arg"
+    fi
+
+    # calculate the name of the file, without its directory
+    outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'`
+    libobjs_save="$libobjs"
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'`
+    if test "X$output_objdir" = "X$output"; then
+      output_objdir="$objdir"
+    else
+      output_objdir="$output_objdir/$objdir"
+    fi
+    # Create the object directory.
+    if test ! -d $output_objdir; then
+      $show "$mkdir $output_objdir"
+      $run $mkdir $output_objdir
+      status=$?
+      if test $status -ne 0 && test ! -d $output_objdir; then
+       exit $status
+      fi
+    fi
+
+    # Determine the type of output
+    case $output in
+    "")
+      $echo "$modename: you must specify an output file" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    specialdeplibs=
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      case "$libs " in
+      *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+      esac
+      libs="$libs $deplib"
+    done
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+    case $linkmode in
+    lib)
+       passes="conv link"
+       for file in $dlfiles $dlprefiles; do
+         case $file in
+         *.la) ;;
+         *)
+           $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2
+           exit 1
+           ;;
+         esac
+       done
+       ;;
+    prog)
+       compile_deplibs=
+       finalize_deplibs=
+       alldeplibs=no
+       newdlfiles=
+       newdlprefiles=
+       passes="conv scan dlopen dlpreopen link"
+       ;;
+    *)  passes="conv"
+       ;;
+    esac
+    for pass in $passes; do
+      if test "$linkmode" = prog; then
+       # Determine which files to process
+       case $pass in
+       dlopen)
+         libs="$dlfiles"
+         save_deplibs="$deplibs" # Collect dlpreopened libraries
+         deplibs=
+         ;;
+       dlpreopen) libs="$dlprefiles" ;;
+       link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+       esac
+      fi
+      for deplib in $libs; do
+       lib=
+       found=no
+       case $deplib in
+       -l*)
+         if test "$linkmode" = oldlib && test "$linkmode" = obj; then
+           $echo "$modename: warning: \`-l' is ignored for archives/objects: $deplib" 1>&2
+           continue
+         fi
+         if test "$pass" = conv; then
+           deplibs="$deplib $deplibs"
+           continue
+         fi
+         name=`$echo "X$deplib" | $Xsed -e 's/^-l//'`
+         for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do
+           # Search the libtool library
+           lib="$searchdir/lib${name}.la"
+           if test -f "$lib"; then
+             found=yes
+             break
+           fi
+         done
+         if test "$found" != yes; then
+           # deplib doesn't seem to be a libtool library
+           if test "$linkmode,$pass" = "prog,link"; then
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           else
+             deplibs="$deplib $deplibs"
+             test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+           fi
+           continue
+         fi
+         ;; # -l
+       -L*)
+         case $linkmode in
+         lib)
+           deplibs="$deplib $deplibs"
+           test "$pass" = conv && continue
+           newdependency_libs="$deplib $newdependency_libs"
+           newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`
+           ;;
+         prog)
+           if test "$pass" = conv; then
+             deplibs="$deplib $deplibs"
+             continue
+           fi
+           if test "$pass" = scan; then
+             deplibs="$deplib $deplibs"
+             newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`
+           else
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           fi
+           ;;
+         *)
+           $echo "$modename: warning: \`-L' is ignored for archives/objects: $deplib" 1>&2
+           ;;
+         esac # linkmode
+         continue
+         ;; # -L
+       -R*)
+         if test "$pass" = link; then
+           dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'`
+           # Make sure the xrpath contains only unique directories.
+           case "$xrpath " in
+           *" $dir "*) ;;
+           *) xrpath="$xrpath $dir" ;;
+           esac
+         fi
+         deplibs="$deplib $deplibs"
+         continue
+         ;;
+       *.la) lib="$deplib" ;;
+       *.$libext)
+         if test "$pass" = conv; then
+           deplibs="$deplib $deplibs"
+           continue
+         fi
+         case $linkmode in
+         lib)
+           if test "$deplibs_check_method" != pass_all; then
+             echo
+             echo "*** Warning: This library needs some functionality provided by $deplib."
+             echo "*** I have the capability to make that library automatically link in when"
+             echo "*** you link to this library.  But I can only do this if you have a"
+             echo "*** shared version of the library, which you do not appear to have."
+           else
+             echo
+             echo "*** Warning: Linking the shared library $output against the"
+             echo "*** static library $deplib is not portable!"
+             deplibs="$deplib $deplibs"
+           fi
+           continue
+           ;;
+         prog)
+           if test "$pass" != link; then
+             deplibs="$deplib $deplibs"
+           else
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           fi
+           continue
+           ;;
+         esac # linkmode
+         ;; # *.$libext
+       *.lo | *.$objext)
+         if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+           # If there is no dlopen support or we're linking statically,
+           # we need to preload.
+           newdlprefiles="$newdlprefiles $deplib"
+           compile_deplibs="$deplib $compile_deplibs"
+           finalize_deplibs="$deplib $finalize_deplibs"
+         else
+           newdlfiles="$newdlfiles $deplib"
+         fi
+         continue
+         ;;
+       %DEPLIBS%)
+         alldeplibs=yes
+         continue
+         ;;
+       esac # case $deplib
+       if test $found = yes || test -f "$lib"; then :
+       else
+         $echo "$modename: cannot find the library \`$lib'" 1>&2
+         exit 1
+       fi
+
+       # Check to see that this really is a libtool archive.
+       if (sed -e '2q' $lib | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+       else
+         $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+         exit 1
+       fi
+
+       ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'`
+       test "X$ladir" = "X$lib" && ladir="."
+
+       dlname=
+       dlopen=
+       dlpreopen=
+       libdir=
+       library_names=
+       old_library=
+       # If the library was installed with an old release of libtool,
+       # it will not redefine variable installed.
+       installed=yes
+
+       # Read the .la file
+       case $lib in
+       */* | *\\*) . $lib ;;
+       *) . ./$lib ;;
+       esac
+
+       if test "$linkmode,$pass" = "lib,link" ||
+          test "$linkmode,$pass" = "prog,scan" ||
+          { test "$linkmode" = oldlib && test "$linkmode" = obj; }; then
+          # Add dl[pre]opened files of deplib
+         test -n "$dlopen" && dlfiles="$dlfiles $dlopen"
+         test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen"
+       fi
+
+       if test "$pass" = conv; then
+         # Only check for convenience libraries
+         deplibs="$lib $deplibs"
+         if test -z "$libdir"; then
+           if test -z "$old_library"; then
+             $echo "$modename: cannot find name of link library for \`$lib'" 1>&2
+             exit 1
+           fi
+           # It is a libtool convenience library, so add in its objects.
+           convenience="$convenience $ladir/$objdir/$old_library"
+           old_convenience="$old_convenience $ladir/$objdir/$old_library"
+           tmp_libs=
+           for deplib in $dependency_libs; do
+             deplibs="$deplib $deplibs"
+             case "$tmp_libs " in
+             *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+             esac
+             tmp_libs="$tmp_libs $deplib"
+           done
+         elif test "$linkmode" != prog && test "$linkmode" != lib; then
+           $echo "$modename: \`$lib' is not a convenience library" 1>&2
+           exit 1
+         fi
+         continue
+       fi # $pass = conv
+
+       # Get the name of the library we link against.
+       linklib=
+       for l in $old_library $library_names; do
+         linklib="$l"
+       done
+       if test -z "$linklib"; then
+         $echo "$modename: cannot find name of link library for \`$lib'" 1>&2
+         exit 1
+       fi
+
+       # This library was specified with -dlopen.
+       if test "$pass" = dlopen; then
+         if test -z "$libdir"; then
+           $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2
+           exit 1
+         fi
+         if test -z "$dlname" || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+           # If there is no dlname, no dlopen support or we're linking
+           # statically, we need to preload.
+           dlprefiles="$dlprefiles $lib"
+         else
+           newdlfiles="$newdlfiles $lib"
+         fi
+         continue
+       fi # $pass = dlopen
+
+       # We need an absolute path.
+       case $ladir in
+       [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+       *)
+         abs_ladir=`cd "$ladir" && pwd`
+         if test -z "$abs_ladir"; then
+           $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2
+           $echo "$modename: passing it literally to the linker, although it might fail" 1>&2
+           abs_ladir="$ladir"
+         fi
+         ;;
+       esac
+       laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'`
+
+       # Find the relevant object directory and library name.
+       if test "X$installed" = Xyes; then
+         if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+           $echo "$modename: warning: library \`$lib' was moved." 1>&2
+           dir="$ladir"
+           absdir="$abs_ladir"
+           libdir="$abs_ladir"
+         else
+           dir="$libdir"
+           absdir="$libdir"
+         fi
+       else
+         dir="$ladir/$objdir"
+         absdir="$abs_ladir/$objdir"
+         # Remove this search path later
+         notinst_path="$notinst_path $abs_ladir"
+       fi # $installed = yes
+       name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'`
+
+       # This library was specified with -dlpreopen.
+       if test "$pass" = dlpreopen; then
+         if test -z "$libdir"; then
+           $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2
+           exit 1
+         fi
+         # Prefer using a static library (so that no silly _DYNAMIC symbols
+         # are required to link).
+         if test -n "$old_library"; then
+           newdlprefiles="$newdlprefiles $dir/$old_library"
+         # Otherwise, use the dlname, so that lt_dlopen finds it.
+         elif test -n "$dlname"; then
+           newdlprefiles="$newdlprefiles $dir/$dlname"
+         else
+           newdlprefiles="$newdlprefiles $dir/$linklib"
+         fi
+       fi # $pass = dlpreopen
+
+       if test -z "$libdir"; then
+         # Link the convenience library
+         if test "$linkmode" = lib; then
+           deplibs="$dir/$old_library $deplibs"
+         elif test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$dir/$old_library $compile_deplibs"
+           finalize_deplibs="$dir/$old_library $finalize_deplibs"
+         else
+           deplibs="$lib $deplibs"
+         fi
+         continue
+       fi
+
+       if test "$linkmode" = prog && test "$pass" != link; then
+         newlib_search_path="$newlib_search_path $ladir"
+         deplibs="$lib $deplibs"
+
+         linkalldeplibs=no
+         if test "$link_all_deplibs" != no || test -z "$library_names" ||
+            test "$build_libtool_libs" = no; then
+           linkalldeplibs=yes
+         fi
+
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           case $deplib in
+           -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test
+           esac
+           # Need to link against all dependency_libs?
+           if test $linkalldeplibs = yes; then
+             deplibs="$deplib $deplibs"
+           else
+             # Need to hardcode shared library paths
+             # or/and link against static libraries
+             newdependency_libs="$deplib $newdependency_libs"
+           fi
+           case "$tmp_libs " in
+           *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+           esac
+           tmp_libs="$tmp_libs $deplib"
+         done # for deplib
+         continue
+       fi # $linkmode = prog...
+
+       link_static=no # Whether the deplib will be linked statically
+       if test -n "$library_names" &&
+          { test "$prefer_static_libs" = no || test -z "$old_library"; }; then
+         # Link against this shared library
+
+         if test "$linkmode,$pass" = "prog,link" ||
+          { test "$linkmode" = lib && test "$hardcode_into_libs" = yes; }; then
+           # Hardcode the library path.
+           # Skip directories that are in the system default run-time
+           # search path.
+           case " $sys_lib_dlsearch_path " in
+           *" $absdir "*) ;;
+           *)
+             case "$compile_rpath " in
+             *" $absdir "*) ;;
+             *) compile_rpath="$compile_rpath $absdir"
+             esac
+             ;;
+           esac
+           case " $sys_lib_dlsearch_path " in
+           *" $libdir "*) ;;
+           *)
+             case "$finalize_rpath " in
+             *" $libdir "*) ;;
+             *) finalize_rpath="$finalize_rpath $libdir"
+             esac
+             ;;
+           esac
+           if test "$linkmode" = prog; then
+             # We need to hardcode the library path
+             if test -n "$shlibpath_var"; then
+               # Make sure the rpath contains only unique directories.
+               case "$temp_rpath " in
+               *" $dir "*) ;;
+               *" $absdir "*) ;;
+               *) temp_rpath="$temp_rpath $dir" ;;
+               esac
+             fi
+           fi
+         fi # $linkmode,$pass = prog,link...
+
+         if test "$alldeplibs" = yes &&
+            { test "$deplibs_check_method" = pass_all ||
+              { test "$build_libtool_libs" = yes &&
+                test -n "$library_names"; }; }; then
+           # We only need to search for static libraries
+           continue
+         fi
+
+         if test "$installed" = no; then
+           notinst_deplibs="$notinst_deplibs $lib"
+           need_relink=yes
+         fi
+
+         if test -n "$old_archive_from_expsyms_cmds"; then
+           # figure out the soname
+           set dummy $library_names
+           realname="$2"
+           shift; shift
+           libname=`eval \\$echo \"$libname_spec\"`
+           # use dlname if we got it. it's perfectly good, no?
+           if test -n "$dlname"; then
+             soname="$dlname"
+           elif test -n "$soname_spec"; then
+             # bleh windows
+             case $host in
+             *cygwin*)
+               major=`expr $current - $age`
+               versuffix="-$major"
+               ;;
+             esac
+             eval soname=\"$soname_spec\"
+           else
+             soname="$realname"
+           fi
+
+           # Make a new name for the extract_expsyms_cmds to use
+           soroot="$soname"
+           soname=`echo $soroot | sed -e 's/^.*\///'`
+           newlib="libimp-`echo $soname | sed 's/^lib//;s/\.dll$//'`.a"
+
+           # If the library has no export list, then create one now
+           if test -f "$output_objdir/$soname-def"; then :
+           else
+             $show "extracting exported symbol list from \`$soname'"
+             IFS="${IFS=       }"; save_ifs="$IFS"; IFS='~'
+             eval cmds=\"$extract_expsyms_cmds\"
+             for cmd in $cmds; do
+               IFS="$save_ifs"
+               $show "$cmd"
+               $run eval "$cmd" || exit $?
+             done
+             IFS="$save_ifs"
+           fi
+
+           # Create $newlib
+           if test -f "$output_objdir/$newlib"; then :; else
+             $show "generating import library for \`$soname'"
+             IFS="${IFS=       }"; save_ifs="$IFS"; IFS='~'
+             eval cmds=\"$old_archive_from_expsyms_cmds\"
+             for cmd in $cmds; do
+               IFS="$save_ifs"
+               $show "$cmd"
+               $run eval "$cmd" || exit $?
+             done
+             IFS="$save_ifs"
+           fi
+           # make sure the library variables are pointing to the new library
+           dir=$output_objdir
+           linklib=$newlib
+         fi # test -n $old_archive_from_expsyms_cmds
+
+         if test "$linkmode" = prog || test "$mode" != relink; then
+           add_shlibpath=
+           add_dir=
+           add=
+           lib_linked=yes
+           case $hardcode_action in
+           immediate | unsupported)
+             if test "$hardcode_direct" = no; then
+               add="$dir/$linklib"
+             elif test "$hardcode_minus_L" = no; then
+               case $host in
+               *-*-sunos*) add_shlibpath="$dir" ;;
+               esac
+               add_dir="-L$dir"
+               add="-l$name"
+             elif test "$hardcode_shlibpath_var" = no; then
+               add_shlibpath="$dir"
+               add="-l$name"
+             else
+               lib_linked=no
+             fi
+             ;;
+           relink)
+             if test "$hardcode_direct" = yes; then
+               add="$dir/$linklib"
+             elif test "$hardcode_minus_L" = yes; then
+               add_dir="-L$dir"
+               add="-l$name"
+             elif test "$hardcode_shlibpath_var" = yes; then
+               add_shlibpath="$dir"
+               add="-l$name"
+             else
+               lib_linked=no
+             fi
+             ;;
+           *) lib_linked=no ;;
+           esac
+
+           if test "$lib_linked" != yes; then
+             $echo "$modename: configuration error: unsupported hardcode properties"
+             exit 1
+           fi
+
+           if test -n "$add_shlibpath"; then
+             case :$compile_shlibpath: in
+             *":$add_shlibpath:"*) ;;
+             *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;;
+             esac
+           fi
+           if test "$linkmode" = prog; then
+             test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+             test -n "$add" && compile_deplibs="$add $compile_deplibs"
+           else
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+             if test "$hardcode_direct" != yes && \
+                test "$hardcode_minus_L" != yes && \
+                test "$hardcode_shlibpath_var" = yes; then
+               case :$finalize_shlibpath: in
+               *":$libdir:"*) ;;
+               *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+               esac
+             fi
+           fi
+         fi
+
+         if test "$linkmode" = prog || test "$mode" = relink; then
+           add_shlibpath=
+           add_dir=
+           add=
+           # Finalize command for both is simple: just hardcode it.
+           if test "$hardcode_direct" = yes; then
+             add="$libdir/$linklib"
+           elif test "$hardcode_minus_L" = yes; then
+             add_dir="-L$libdir"
+             add="-l$name"
+           elif test "$hardcode_shlibpath_var" = yes; then
+             case :$finalize_shlibpath: in
+             *":$libdir:"*) ;;
+             *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+             esac
+             add="-l$name"
+           else
+             # We cannot seem to hardcode it, guess we'll fake it.
+             add_dir="-L$libdir"
+             add="-l$name"
+           fi
+
+           if test "$linkmode" = prog; then
+             test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+             test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+           else
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+           fi
+         fi
+       elif test "$linkmode" = prog; then
+         if test "$alldeplibs" = yes &&
+            { test "$deplibs_check_method" = pass_all ||
+              { test "$build_libtool_libs" = yes &&
+                test -n "$library_names"; }; }; then
+           # We only need to search for static libraries
+           continue
+         fi
+
+         # Try to link the static library
+         # Here we assume that one of hardcode_direct or hardcode_minus_L
+         # is not unsupported.  This is valid on all known static and
+         # shared platforms.
+         if test "$hardcode_direct" != unsupported; then
+           test -n "$old_library" && linklib="$old_library"
+           compile_deplibs="$dir/$linklib $compile_deplibs"
+           finalize_deplibs="$dir/$linklib $finalize_deplibs"
+         else
+           compile_deplibs="-l$name -L$dir $compile_deplibs"
+           finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+         fi
+       elif test "$build_libtool_libs" = yes; then
+         # Not a shared library
+         if test "$deplibs_check_method" != pass_all; then
+           # We're trying link a shared library against a static one
+           # but the system doesn't support it.
+
+           # Just print a warning and add the library to dependency_libs so
+           # that the program can be linked against the static library.
+           echo
+           echo "*** Warning: This library needs some functionality provided by $lib."
+           echo "*** I have the capability to make that library automatically link in when"
+           echo "*** you link to this library.  But I can only do this if you have a"
+           echo "*** shared version of the library, which you do not appear to have."
+           if test "$module" = yes; then
+             echo "*** Therefore, libtool will create a static module, that should work "
+             echo "*** as long as the dlopening application is linked with the -dlopen flag."
+             if test -z "$global_symbol_pipe"; then
+               echo
+               echo "*** However, this would only work if libtool was able to extract symbol"
+               echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+               echo "*** not find such a program.  So, this module is probably useless."
+               echo "*** \`nm' from GNU binutils and a full rebuild may help."
+             fi
+             if test "$build_old_libs" = no; then
+               build_libtool_libs=module
+               build_old_libs=yes
+             else
+               build_libtool_libs=no
+             fi
+           fi
+         else
+           convenience="$convenience $dir/$old_library"
+           old_convenience="$old_convenience $dir/$old_library"
+           deplibs="$dir/$old_library $deplibs"
+           link_static=yes
+         fi
+       fi # link shared/static library?
+
+       if test "$linkmode" = lib; then
+         if test -n "$dependency_libs" &&
+            { test "$hardcode_into_libs" != yes || test $build_old_libs = yes ||
+              test $link_static = yes; }; then
+           # Extract -R from dependency_libs
+           temp_deplibs=
+           for libdir in $dependency_libs; do
+             case $libdir in
+             -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'`
+                  case " $xrpath " in
+                  *" $temp_xrpath "*) ;;
+                  *) xrpath="$xrpath $temp_xrpath";;
+                  esac;;
+             *) temp_deplibs="$temp_deplibs $libdir";;
+             esac
+           done
+           dependency_libs="$temp_deplibs"
+         fi
+
+         newlib_search_path="$newlib_search_path $absdir"
+         # Link against this library
+         test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+         # ... and its dependency_libs
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           newdependency_libs="$deplib $newdependency_libs"
+           case "$tmp_libs " in
+           *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+           esac
+           tmp_libs="$tmp_libs $deplib"
+         done
+
+         if test "$link_all_deplibs" != no; then
+           # Add the search paths of all dependency libraries
+           for deplib in $dependency_libs; do
+             case $deplib in
+             -L*) path="$deplib" ;;
+             *.la)
+               dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'`
+               test "X$dir" = "X$deplib" && dir="."
+               # We need an absolute path.
+               case $dir in
+               [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+               *)
+                 absdir=`cd "$dir" && pwd`
+                 if test -z "$absdir"; then
+                   $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2
+                   absdir="$dir"
+                 fi
+                 ;;
+               esac
+               if grep "^installed=no" $deplib > /dev/null; then
+                 path="-L$absdir/$objdir"
+               else
+                 eval libdir=`sed -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+                 if test -z "$libdir"; then
+                   $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
+                   exit 1
+                 fi
+                 if test "$absdir" != "$libdir"; then
+                   $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2
+                 fi
+                 path="-L$absdir"
+               fi
+               ;;
+             *) continue ;;
+             esac
+             case " $deplibs " in
+             *" $path "*) ;;
+             *) deplibs="$deplibs $path" ;;
+             esac
+           done
+         fi # link_all_deplibs != no
+       fi # linkmode = lib
+      done # for deplib in $libs
+      if test "$pass" = dlpreopen; then
+       # Link the dlpreopened libraries before other libraries
+       for deplib in $save_deplibs; do
+         deplibs="$deplib $deplibs"
+       done
+      fi
+      if test "$pass" != dlopen; then
+       test "$pass" != scan && dependency_libs="$newdependency_libs"
+       if test "$pass" != conv; then
+         # Make sure lib_search_path contains only unique directories.
+         lib_search_path=
+         for dir in $newlib_search_path; do
+           case "$lib_search_path " in
+           *" $dir "*) ;;
+           *) lib_search_path="$lib_search_path $dir" ;;
+           esac
+         done
+         newlib_search_path=
+       fi
+
+       if test "$linkmode,$pass" != "prog,link"; then
+         vars="deplibs"
+       else
+         vars="compile_deplibs finalize_deplibs"
+       fi
+       for var in $vars dependency_libs; do
+         # Add libraries to $var in reverse order
+         eval tmp_libs=\"\$$var\"
+         new_libs=
+         for deplib in $tmp_libs; do
+           case $deplib in
+           -L*) new_libs="$deplib $new_libs" ;;
+           *)
+             case " $specialdeplibs " in
+             *" $deplib "*) new_libs="$deplib $new_libs" ;;
+             *)
+               case " $new_libs " in
+               *" $deplib "*) ;;
+               *) new_libs="$deplib $new_libs" ;;
+               esac
+               ;;
+             esac
+             ;;
+           esac
+         done
+         tmp_libs=
+         for deplib in $new_libs; do
+           case $deplib in
+           -L*)
+             case " $tmp_libs " in
+             *" $deplib "*) ;;
+             *) tmp_libs="$tmp_libs $deplib" ;;
+             esac
+             ;;
+           *) tmp_libs="$tmp_libs $deplib" ;;
+           esac
+         done
+         eval $var=\"$tmp_libs\"
+       done # for var
+      fi
+      if test "$pass" = "conv" &&
+       { test "$linkmode" = "lib" || test "$linkmode" = "prog"; }; then
+       libs="$deplibs" # reset libs
+       deplibs=
+      fi
+    done # for pass
+    if test "$linkmode" = prog; then
+      dlfiles="$newdlfiles"
+      dlprefiles="$newdlprefiles"
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$rpath"; then
+       $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$xrpath"; then
+       $echo "$modename: warning: \`-R' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$vinfo"; then
+       $echo "$modename: warning: \`-version-info' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$release"; then
+       $echo "$modename: warning: \`-release' is ignored for archives" 1>&2
+      fi
+
+      if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+       $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2
+      fi
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      objs="$objs$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case $outputname in
+      lib*)
+       name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'`
+       eval libname=\"$libname_spec\"
+       ;;
+      *)
+       if test "$module" = no; then
+         $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+       if test "$need_lib_prefix" != no; then
+         # Add the "lib" prefix for modules if required
+         name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+         eval libname=\"$libname_spec\"
+       else
+         libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'`
+       fi
+       ;;
+      esac
+
+      if test -n "$objs"; then
+       if test "$deplibs_check_method" != pass_all; then
+         $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1
+         exit 1
+       else
+         echo
+         echo "*** Warning: Linking the shared library $output against the non-libtool"
+         echo "*** objects $objs is not portable!"
+         libobjs="$libobjs $objs"
+       fi
+      fi
+
+      if test "$dlself" != no; then
+       $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2
+      fi
+
+      set dummy $rpath
+      if test $# -gt 2; then
+       $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2
+      fi
+      install_libdir="$2"
+
+      oldlibs=
+      if test -z "$rpath"; then
+       if test "$build_libtool_libs" = yes; then
+         # Building a libtool convenience library.
+         libext=al
+         oldlibs="$output_objdir/$libname.$libext $oldlibs"
+         build_libtool_libs=convenience
+         build_old_libs=yes
+       fi
+
+       if test -n "$vinfo"; then
+         $echo "$modename: warning: \`-version-info' is ignored for convenience libraries" 1>&2
+       fi
+
+       if test -n "$release"; then
+         $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2
+       fi
+      else
+
+       # Parse the version information argument.
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS=':'
+       set dummy $vinfo 0 0 0
+       IFS="$save_ifs"
+
+       if test -n "$8"; then
+         $echo "$modename: too many parameters to \`-version-info'" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+
+       current="$2"
+       revision="$3"
+       age="$4"
+
+       # Check that each of the things are valid numbers.
+       case $current in
+       0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;;
+       *)
+         $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+         ;;
+       esac
+
+       case $revision in
+       0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;;
+       *)
+         $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+         ;;
+       esac
+
+       case $age in
+       0 | [1-9] | [1-9][0-9] | [1-9][0-9][0-9]) ;;
+       *)
+         $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+         ;;
+       esac
+
+       if test $age -gt $current; then
+         $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2
+         $echo "$modename: \`$vinfo' is not valid version information" 1>&2
+         exit 1
+       fi
+
+       # Calculate the version variables.
+       major=
+       versuffix=
+       verstring=
+       case $version_type in
+       none) ;;
+
+       darwin)
+         # Like Linux, but with the current version available in
+         # verstring for coding it into the library header
+         major=.`expr $current - $age`
+         versuffix="$major.$age.$revision"
+         # Darwin ld doesn't like 0 for these options...
+         minor_current=`expr $current + 1`
+         verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+         ;;
+
+       freebsd-aout)
+         major=".$current"
+         versuffix=".$current.$revision";
+         ;;
+
+       freebsd-elf)
+         major=".$current"
+         versuffix=".$current";
+         ;;
+
+       irix)
+         major=`expr $current - $age + 1`
+         verstring="sgi$major.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$revision
+         while test $loop != 0; do
+           iface=`expr $revision - $loop`
+           loop=`expr $loop - 1`
+           verstring="sgi$major.$iface:$verstring"
+         done
+
+         # Before this point, $major must not contain `.'.
+         major=.$major
+         versuffix="$major.$revision"
+         ;;
+
+       linux)
+         major=.`expr $current - $age`
+         versuffix="$major.$age.$revision"
+         ;;
+
+       osf)
+         major=`expr $current - $age`
+         versuffix=".$current.$age.$revision"
+         verstring="$current.$age.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$age
+         while test $loop != 0; do
+           iface=`expr $current - $loop`
+           loop=`expr $loop - 1`
+           verstring="$verstring:${iface}.0"
+         done
+
+         # Make executables depend on our current version.
+         verstring="$verstring:${current}.0"
+         ;;
+
+       sunos)
+         major=".$current"
+         versuffix=".$current.$revision"
+         ;;
+
+       windows)
+         # Use '-' rather than '.', since we only want one
+         # extension on DOS 8.3 filesystems.
+         major=`expr $current - $age`
+         versuffix="-$major"
+         ;;
+
+       *)
+         $echo "$modename: unknown library version type \`$version_type'" 1>&2
+         echo "Fatal configuration error.  See the $PACKAGE docs for more information." 1>&2
+         exit 1
+         ;;
+       esac
+
+       # Clear the version info if we defaulted, and they specified a release.
+       if test -z "$vinfo" && test -n "$release"; then
+         major=
+         verstring="0.0"
+         if test "$need_version" = no; then
+           versuffix=
+         else
+           versuffix=".0.0"
+         fi
+       fi
+
+       # Remove version info from name if versioning should be avoided
+       if test "$avoid_version" = yes && test "$need_version" = no; then
+         major=
+         versuffix=
+         verstring=""
+       fi
+
+       # Check to see if the archive will have undefined symbols.
+       if test "$allow_undefined" = yes; then
+         if test "$allow_undefined_flag" = unsupported; then
+           $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2
+           build_libtool_libs=no
+           build_old_libs=yes
+         fi
+       else
+         # Don't allow undefined symbols.
+         allow_undefined_flag="$no_undefined_flag"
+       fi
+      fi
+
+      if test "$mode" != relink; then
+       # Remove our outputs.
+       $show "${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*"
+       $run ${rm}r $output_objdir/$outputname $output_objdir/$libname.* $output_objdir/${libname}${release}.*
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+       oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+       # Transform .lo files to .o files.
+       oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      for path in $notinst_path; do
+       lib_search_path=`echo "$lib_search_path " | sed -e 's% $path % %g'`
+       deplibs=`echo "$deplibs " | sed -e 's% -L$path % %g'`
+       dependency_libs=`echo "$dependency_libs " | sed -e 's% -L$path % %g'`
+      done
+
+      if test -n "$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       temp_xrpath=
+       for libdir in $xrpath; do
+         temp_xrpath="$temp_xrpath -R$libdir"
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_rpath="$finalize_rpath $libdir" ;;
+         esac
+       done
+       if test $hardcode_into_libs != yes || test $build_old_libs = yes; then
+         dependency_libs="$temp_xrpath $dependency_libs"
+       fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles="$dlfiles"
+      dlfiles=
+      for lib in $old_dlfiles; do
+       case " $dlprefiles $dlfiles " in
+       *" $lib "*) ;;
+       *) dlfiles="$dlfiles $lib" ;;
+       esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles="$dlprefiles"
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+       case "$dlprefiles " in
+       *" $lib "*) ;;
+       *) dlprefiles="$dlprefiles $lib" ;;
+       esac
+      done
+
+      if test "$build_libtool_libs" = yes; then
+       if test -n "$rpath"; then
+         case $host in
+         *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*)
+           # these systems don't actually have a c library (as such)!
+           ;;
+         *-*-rhapsody* | *-*-darwin1.[012])
+           # Rhapsody C library is in the System framework
+           deplibs="$deplibs -framework System"
+           ;;
+         *-*-netbsd*)
+           # Don't link with libc until the a.out ld.so is fixed.
+           ;;
+         *)
+           # Add libc to deplibs on all other systems if necessary.
+           if test "$build_libtool_need_lc" = "yes"; then
+             deplibs="$deplibs -lc"
+           fi
+           ;;
+         esac
+       fi
+
+       # Transform deplibs into only deplibs that can be linked in shared.
+       name_save=$name
+       libname_save=$libname
+       release_save=$release
+       versuffix_save=$versuffix
+       major_save=$major
+       # I'm not sure if I'm treating the release correctly.  I think
+       # release should show up in the -l (ie -lgmp5) so we don't want to
+       # add it in twice.  Is that correct?
+       release=""
+       versuffix=""
+       major=""
+       newdeplibs=
+       droppeddeps=no
+       case $deplibs_check_method in
+       pass_all)
+         # Don't check for shared/static.  Everything works.
+         # This might be a little naive.  We might want to check
+         # whether the library exists or not.  But this is on
+         # osf3 & osf4 and I'm not really sure... Just
+         # implementing what was already the behaviour.
+         newdeplibs=$deplibs
+         ;;
+       test_compile)
+         # This code stresses the "libraries are programs" paradigm to its
+         # limits. Maybe even breaks it.  We compile a program, linking it
+         # against the deplibs as a proxy for the library.  Then we can check
+         # whether they linked in statically or dynamically with ldd.
+         $rm conftest.c
+         cat > conftest.c <<EOF
+         int main() { return 0; }
+EOF
+         $rm conftest
+         $CC -o conftest conftest.c $deplibs
+         if test $? -eq 0 ; then
+           ldd_output=`ldd conftest`
+           for i in $deplibs; do
+             name="`expr $i : '-l\(.*\)'`"
+             # If $name is empty we are operating on a -L argument.
+             if test -n "$name" && test "$name" != "0"; then
+               libname=`eval \\$echo \"$libname_spec\"`
+               deplib_matches=`eval \\$echo \"$library_names_spec\"`
+               set dummy $deplib_matches
+               deplib_match=$2
+               if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                 newdeplibs="$newdeplibs $i"
+               else
+                 droppeddeps=yes
+                 echo
+                 echo "*** Warning: This library needs some functionality provided by $i."
+                 echo "*** I have the capability to make that library automatically link in when"
+                 echo "*** you link to this library.  But I can only do this if you have a"
+                 echo "*** shared version of the library, which you do not appear to have."
+               fi
+             else
+               newdeplibs="$newdeplibs $i"
+             fi
+           done
+         else
+           # Error occured in the first compile.  Let's try to salvage the situation:
+           # Compile a seperate program for each library.
+           for i in $deplibs; do
+             name="`expr $i : '-l\(.*\)'`"
+            # If $name is empty we are operating on a -L argument.
+             if test -n "$name" && test "$name" != "0"; then
+               $rm conftest
+               $CC -o conftest conftest.c $i
+               # Did it work?
+               if test $? -eq 0 ; then
+                 ldd_output=`ldd conftest`
+                 libname=`eval \\$echo \"$libname_spec\"`
+                 deplib_matches=`eval \\$echo \"$library_names_spec\"`
+                 set dummy $deplib_matches
+                 deplib_match=$2
+                 if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                   newdeplibs="$newdeplibs $i"
+                 else
+                   droppeddeps=yes
+                   echo
+                   echo "*** Warning: This library needs some functionality provided by $i."
+                   echo "*** I have the capability to make that library automatically link in when"
+                   echo "*** you link to this library.  But I can only do this if you have a"
+                   echo "*** shared version of the library, which you do not appear to have."
+                 fi
+               else
+                 droppeddeps=yes
+                 echo
+                 echo "*** Warning!  Library $i is needed by this library but I was not able to"
+                 echo "***  make it link in!  You will probably need to install it or some"
+                 echo "*** library that it depends on before this library will be fully"
+                 echo "*** functional.  Installing it before continuing would be even better."
+               fi
+             else
+               newdeplibs="$newdeplibs $i"
+             fi
+           done
+         fi
+         ;;
+       file_magic*)
+         set dummy $deplibs_check_method
+         file_magic_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"`
+         for a_deplib in $deplibs; do
+           name="`expr $a_deplib : '-l\(.*\)'`"
+           # If $name is empty we are operating on a -L argument.
+           if test -n "$name" && test "$name" != "0"; then
+             libname=`eval \\$echo \"$libname_spec\"`
+             for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+                   potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+                   for potent_lib in $potential_libs; do
+                     # Follow soft links.
+                     if ls -lLd "$potent_lib" 2>/dev/null \
+                        | grep " -> " >/dev/null; then
+                       continue
+                     fi
+                     # The statement above tries to avoid entering an
+                     # endless loop below, in case of cyclic links.
+                     # We might still enter an endless loop, since a link
+                     # loop can be closed while we follow links,
+                     # but so what?
+                     potlib="$potent_lib"
+                     while test -h "$potlib" 2>/dev/null; do
+                       potliblink=`ls -ld $potlib | sed 's/.* -> //'`
+                       case $potliblink in
+                       [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+                       *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+                       esac
+                     done
+                     if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \
+                        | sed 10q \
+                        | egrep "$file_magic_regex" > /dev/null; then
+                       newdeplibs="$newdeplibs $a_deplib"
+                       a_deplib=""
+                       break 2
+                     fi
+                   done
+             done
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               echo
+               echo "*** Warning: This library needs some functionality provided by $a_deplib."
+               echo "*** I have the capability to make that library automatically link in when"
+               echo "*** you link to this library.  But I can only do this if you have a"
+               echo "*** shared version of the library, which you do not appear to have."
+             fi
+           else
+             # Add a -L argument.
+             newdeplibs="$newdeplibs $a_deplib"
+           fi
+         done # Gone through all deplibs.
+         ;;
+       match_pattern*)
+         set dummy $deplibs_check_method
+         match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"`
+         for a_deplib in $deplibs; do
+           name="`expr $a_deplib : '-l\(.*\)'`"
+           # If $name is empty we are operating on a -L argument.
+           if test -n "$name" && test "$name" != "0"; then
+             libname=`eval \\$echo \"$libname_spec\"`
+             for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+               potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+               for potent_lib in $potential_libs; do
+                 if eval echo \"$potent_lib\" 2>/dev/null \
+                     | sed 10q \
+                     | egrep "$match_pattern_regex" > /dev/null; then
+                   newdeplibs="$newdeplibs $a_deplib"
+                   a_deplib=""
+                   break 2
+                 fi
+               done
+             done
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               echo
+               echo "*** Warning: This library needs some functionality provided by $a_deplib."
+               echo "*** I have the capability to make that library automatically link in when"
+               echo "*** you link to this library.  But I can only do this if you have a"
+               echo "*** shared version of the library, which you do not appear to have."
+             fi
+           else
+             # Add a -L argument.
+             newdeplibs="$newdeplibs $a_deplib"
+           fi
+         done # Gone through all deplibs.
+         ;;
+       none | unknown | *)
+         newdeplibs=""
+         if $echo "X $deplibs" | $Xsed -e 's/ -lc$//' \
+              -e 's/ -[LR][^ ]*//g' -e 's/[    ]//g' |
+            grep . >/dev/null; then
+           echo
+           if test "X$deplibs_check_method" = "Xnone"; then
+             echo "*** Warning: inter-library dependencies are not supported in this platform."
+           else
+             echo "*** Warning: inter-library dependencies are not known to be supported."
+           fi
+           echo "*** All declared inter-library dependencies are being dropped."
+           droppeddeps=yes
+         fi
+         ;;
+       esac
+       versuffix=$versuffix_save
+       major=$major_save
+       release=$release_save
+       libname=$libname_save
+       name=$name_save
+
+       case $host in
+       *-*-rhapsody* | *-*-darwin1.[012])
+         # On Rhapsody replace the C library is the System framework
+         newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'`
+         ;;
+       esac
+
+       if test "$droppeddeps" = yes; then
+         if test "$module" = yes; then
+           echo
+           echo "*** Warning: libtool could not satisfy all declared inter-library"
+           echo "*** dependencies of module $libname.  Therefore, libtool will create"
+           echo "*** a static module, that should work as long as the dlopening"
+           echo "*** application is linked with the -dlopen flag."
+           if test -z "$global_symbol_pipe"; then
+             echo
+             echo "*** However, this would only work if libtool was able to extract symbol"
+             echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+             echo "*** not find such a program.  So, this module is probably useless."
+             echo "*** \`nm' from GNU binutils and a full rebuild may help."
+           fi
+           if test "$build_old_libs" = no; then
+             oldlibs="$output_objdir/$libname.$libext"
+             build_libtool_libs=module
+             build_old_libs=yes
+           else
+             build_libtool_libs=no
+           fi
+         else
+           echo "*** The inter-library dependencies that have been dropped here will be"
+           echo "*** automatically added whenever a program is linked with this library"
+           echo "*** or is declared to -dlopen it."
+
+           if test $allow_undefined = no; then
+             echo
+             echo "*** Since this library must not contain undefined symbols,"
+             echo "*** because either the platform does not support them or"
+             echo "*** it was explicitly requested with -no-undefined,"
+             echo "*** libtool will only create a static version of it."
+             if test "$build_old_libs" = no; then
+               oldlibs="$output_objdir/$libname.$libext"
+               build_libtool_libs=module
+               build_old_libs=yes
+             else
+               build_libtool_libs=no
+             fi
+           fi
+         fi
+       fi
+       # Done checking deplibs!
+       deplibs=$newdeplibs
+      fi
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+       if test "$hardcode_into_libs" = yes; then
+         # Hardcode the library paths
+         hardcode_libdirs=
+         dep_rpath=
+         rpath="$finalize_rpath"
+         test "$mode" != relink && rpath="$compile_rpath$rpath"
+         for libdir in $rpath; do
+           if test -n "$hardcode_libdir_flag_spec"; then
+             if test -n "$hardcode_libdir_separator"; then
+               if test -z "$hardcode_libdirs"; then
+                 hardcode_libdirs="$libdir"
+               else
+                 # Just accumulate the unique libdirs.
+                 case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+                 *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+                   ;;
+                 *)
+                   hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+                   ;;
+                 esac
+               fi
+             else
+               eval flag=\"$hardcode_libdir_flag_spec\"
+               dep_rpath="$dep_rpath $flag"
+             fi
+           elif test -n "$runpath_var"; then
+             case "$perm_rpath " in
+             *" $libdir "*) ;;
+             *) perm_rpath="$perm_rpath $libdir" ;;
+             esac
+           fi
+         done
+         # Substitute the hardcoded libdirs into the rpath.
+         if test -n "$hardcode_libdir_separator" &&
+            test -n "$hardcode_libdirs"; then
+           libdir="$hardcode_libdirs"
+           eval dep_rpath=\"$hardcode_libdir_flag_spec\"
+         fi
+         if test -n "$runpath_var" && test -n "$perm_rpath"; then
+           # We should set the runpath_var.
+           rpath=
+           for dir in $perm_rpath; do
+             rpath="$rpath$dir:"
+           done
+           eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+         fi
+         test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+       fi
+
+       shlibpath="$finalize_shlibpath"
+       test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+       if test -n "$shlibpath"; then
+         eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+       fi
+
+       # Get the real and link names of the library.
+       eval library_names=\"$library_names_spec\"
+       set dummy $library_names
+       realname="$2"
+       shift; shift
+
+       if test -n "$soname_spec"; then
+         eval soname=\"$soname_spec\"
+       else
+         soname="$realname"
+       fi
+       test -z "$dlname" && dlname=$soname
+
+       lib="$output_objdir/$realname"
+       for link
+       do
+         linknames="$linknames $link"
+       done
+
+       # Ensure that we have .o objects for linkers which dislike .lo
+       # (e.g. aix) in case we are running --disable-static
+       for obj in $libobjs; do
+         xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'`
+         if test "X$xdir" = "X$obj"; then
+           xdir="."
+         else
+           xdir="$xdir"
+         fi
+         baseobj=`$echo "X$obj" | $Xsed -e 's%^.*/%%'`
+         oldobj=`$echo "X$baseobj" | $Xsed -e "$lo2o"`
+         if test ! -f $xdir/$oldobj; then
+           $show "(cd $xdir && ${LN_S} $baseobj $oldobj)"
+           $run eval '(cd $xdir && ${LN_S} $baseobj $oldobj)' || exit $?
+         fi
+       done
+
+       # Use standard objects if they are pic
+       test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+
+       # Prepare the list of exported symbols
+       if test -z "$export_symbols"; then
+         if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+           $show "generating symbol list for \`$libname.la'"
+           export_symbols="$output_objdir/$libname.exp"
+           $run $rm $export_symbols
+           eval cmds=\"$export_symbols_cmds\"
+           IFS="${IFS=         }"; save_ifs="$IFS"; IFS='~'
+           for cmd in $cmds; do
+             IFS="$save_ifs"
+             $show "$cmd"
+             $run eval "$cmd" || exit $?
+           done
+           IFS="$save_ifs"
+           if test -n "$export_symbols_regex"; then
+             $show "egrep -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\""
+             $run eval 'egrep -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+             $show "$mv \"${export_symbols}T\" \"$export_symbols\""
+             $run eval '$mv "${export_symbols}T" "$export_symbols"'
+           fi
+         fi
+       fi
+
+       if test -n "$export_symbols" && test -n "$include_expsyms"; then
+         $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"'
+       fi
+
+       if test -n "$convenience"; then
+         if test -n "$whole_archive_flag_spec"; then
+           eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+         else
+           gentop="$output_objdir/${outputname}x"
+           $show "${rm}r $gentop"
+           $run ${rm}r "$gentop"
+           $show "mkdir $gentop"
+           $run mkdir "$gentop"
+           status=$?
+           if test $status -ne 0 && test ! -d "$gentop"; then
+             exit $status
+           fi
+           generated="$generated $gentop"
+
+           for xlib in $convenience; do
+             # Extract the objects.
+             case $xlib in
+             [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+             *) xabs=`pwd`"/$xlib" ;;
+             esac
+             xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+             xdir="$gentop/$xlib"
+
+             $show "${rm}r $xdir"
+             $run ${rm}r "$xdir"
+             $show "mkdir $xdir"
+             $run mkdir "$xdir"
+             status=$?
+             if test $status -ne 0 && test ! -d "$xdir"; then
+               exit $status
+             fi
+             $show "(cd $xdir && $AR x $xabs)"
+             $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+             libobjs="$libobjs "`find $xdir -name \*.o -print -o -name \*.lo -print | $NL2SP`
+           done
+         fi
+       fi
+
+       if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+         eval flag=\"$thread_safe_flag_spec\"
+         linker_flags="$linker_flags $flag"
+       fi
+
+       # Make a backup of the uninstalled library when relinking
+       if test "$mode" = relink; then
+         $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $?
+       fi
+
+       # Do each of the archive commands.
+       if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+         eval cmds=\"$archive_expsym_cmds\"
+       else
+         eval cmds=\"$archive_cmds\"
+       fi
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS='~'
+       for cmd in $cmds; do
+         IFS="$save_ifs"
+         $show "$cmd"
+         $run eval "$cmd" || exit $?
+       done
+       IFS="$save_ifs"
+
+       # Restore the uninstalled library and exit
+       if test "$mode" = relink; then
+         $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $?
+         exit 0
+       fi
+
+       # Create links to the real library.
+       for linkname in $linknames; do
+         if test "$realname" != "$linkname"; then
+           $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)"
+           $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $?
+         fi
+       done
+
+       # If -module or -export-dynamic was specified, set the dlname.
+       if test "$module" = yes || test "$export_dynamic" = yes; then
+         # On all known operating systems, these are identical.
+         dlname="$soname"
+       fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$deplibs"; then
+       $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2
+      fi
+
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$rpath"; then
+       $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$xrpath"; then
+       $echo "$modename: warning: \`-R' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$vinfo"; then
+       $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2
+      fi
+
+      if test -n "$release"; then
+       $echo "$modename: warning: \`-release' is ignored for objects" 1>&2
+      fi
+
+      case $output in
+      *.lo)
+       if test -n "$objs$old_deplibs"; then
+         $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2
+         exit 1
+       fi
+       libobj="$output"
+       obj=`$echo "X$output" | $Xsed -e "$lo2o"`
+       ;;
+      *)
+       libobj=
+       obj="$output"
+       ;;
+      esac
+
+      # Delete the old objects.
+      $run $rm $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # reload_cmds runs $LD directly, so let us get rid of
+      # -Wl from whole_archive_flag_spec
+      wl=
+
+      if test -n "$convenience"; then
+       if test -n "$whole_archive_flag_spec"; then
+         eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\"
+       else
+         gentop="$output_objdir/${obj}x"
+         $show "${rm}r $gentop"
+         $run ${rm}r "$gentop"
+         $show "mkdir $gentop"
+         $run mkdir "$gentop"
+         status=$?
+         if test $status -ne 0 && test ! -d "$gentop"; then
+           exit $status
+         fi
+         generated="$generated $gentop"
+
+         for xlib in $convenience; do
+           # Extract the objects.
+           case $xlib in
+           [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+           *) xabs=`pwd`"/$xlib" ;;
+           esac
+           xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+           xdir="$gentop/$xlib"
+
+           $show "${rm}r $xdir"
+           $run ${rm}r "$xdir"
+           $show "mkdir $xdir"
+           $run mkdir "$xdir"
+           status=$?
+           if test $status -ne 0 && test ! -d "$xdir"; then
+             exit $status
+           fi
+           $show "(cd $xdir && $AR x $xabs)"
+           $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+           reload_conv_objs="$reload_objs "`find $xdir -name \*.o -print -o -name \*.lo -print | $NL2SP`
+         done
+       fi
+      fi
+
+      # Create the old-style object.
+      reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+      output="$obj"
+      eval cmds=\"$reload_cmds\"
+      IFS="${IFS=      }"; save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+       IFS="$save_ifs"
+       $show "$cmd"
+       $run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+       if test -n "$gentop"; then
+         $show "${rm}r $gentop"
+         $run ${rm}r $gentop
+       fi
+
+       exit 0
+      fi
+
+      if test "$build_libtool_libs" != yes; then
+       if test -n "$gentop"; then
+         $show "${rm}r $gentop"
+         $run ${rm}r $gentop
+       fi
+
+       # Create an invalid libtool object if no PIC, so that we don't
+       # accidentally link it into a program.
+       $show "echo timestamp > $libobj"
+       $run eval "echo timestamp > $libobj" || exit $?
+       exit 0
+      fi
+
+      if test -n "$pic_flag" || test "$pic_mode" != default; then
+       # Only do commands if we really have different PIC objects.
+       reload_objs="$libobjs $reload_conv_objs"
+       output="$libobj"
+       eval cmds=\"$reload_cmds\"
+       IFS="${IFS=     }"; save_ifs="$IFS"; IFS='~'
+       for cmd in $cmds; do
+         IFS="$save_ifs"
+         $show "$cmd"
+         $run eval "$cmd" || exit $?
+       done
+       IFS="$save_ifs"
+      else
+       # Just create a symlink.
+       $show $rm $libobj
+       $run $rm $libobj
+       xdir=`$echo "X$libobj" | $Xsed -e 's%/[^/]*$%%'`
+       if test "X$xdir" = "X$libobj"; then
+         xdir="."
+       else
+         xdir="$xdir"
+       fi
+       baseobj=`$echo "X$libobj" | $Xsed -e 's%^.*/%%'`
+       oldobj=`$echo "X$baseobj" | $Xsed -e "$lo2o"`
+       $show "(cd $xdir && $LN_S $oldobj $baseobj)"
+       $run eval '(cd $xdir && $LN_S $oldobj $baseobj)' || exit $?
+      fi
+
+      if test -n "$gentop"; then
+       $show "${rm}r $gentop"
+       $run ${rm}r $gentop
+      fi
+
+      exit 0
+      ;;
+
+    prog)
+      case $host in
+       *cygwin*) output=`echo $output | sed -e 's,.exe$,,;s,$,.exe,'` ;;
+      esac
+      if test -n "$vinfo"; then
+       $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2
+      fi
+
+      if test -n "$release"; then
+       $echo "$modename: warning: \`-release' is ignored for programs" 1>&2
+      fi
+
+      if test "$preload" = yes; then
+       if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown &&
+          test "$dlopen_self_static" = unknown; then
+         $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support."
+       fi
+      fi
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+       # On Rhapsody replace the C library is the System framework
+       compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'`
+       finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'`
+       ;;
+      esac
+
+      compile_command="$compile_command $compile_deplibs"
+      finalize_command="$finalize_command $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       for libdir in $rpath $xrpath; do
+         # This is the magic to use -rpath.
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_rpath="$finalize_rpath $libdir" ;;
+         esac
+       done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           rpath="$rpath $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$perm_rpath " in
+         *" $libdir "*) ;;
+         *) perm_rpath="$perm_rpath $libdir" ;;
+         esac
+       fi
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*)
+         case :$dllsearchpath: in
+         *":$libdir:"*) ;;
+         *) dllsearchpath="$dllsearchpath:$libdir";;
+         esac
+         ;;
+       esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           rpath="$rpath $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$finalize_perm_rpath " in
+         *" $libdir "*) ;;
+         *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+         esac
+       fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+       # Transform all the library objects into standard objects.
+       compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+       finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+      fi
+
+      dlsyms=
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       if test -n "$NM" && test -n "$global_symbol_pipe"; then
+         dlsyms="${outputname}S.c"
+       else
+         $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2
+       fi
+      fi
+
+      if test -n "$dlsyms"; then
+       case $dlsyms in
+       "") ;;
+       *.c)
+         # Discover the nlist of each of the dlfiles.
+         nlist="$output_objdir/${outputname}.nm"
+
+         $show "$rm $nlist ${nlist}S ${nlist}T"
+         $run $rm "$nlist" "${nlist}S" "${nlist}T"
+
+         # Parse the name list into a source file.
+         $show "creating $output_objdir/$dlsyms"
+
+         test -z "$run" && $echo > "$output_objdir/$dlsyms" "\
+/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */
+/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* Prevent the only kind of declaration conflicts we can make. */
+#define lt_preloaded_symbols some_other_symbol
+
+/* External symbol declarations for the compiler. */\
+"
+
+         if test "$dlself" = yes; then
+           $show "generating symbol list for \`$output'"
+
+           test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist"
+
+           # Add our own program objects to the symbol list.
+           progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+           for arg in $progfiles; do
+             $show "extracting global C symbols from \`$arg'"
+             $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+           done
+
+           if test -n "$exclude_expsyms"; then
+             $run eval 'egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+             $run eval '$mv "$nlist"T "$nlist"'
+           fi
+
+           if test -n "$export_symbols_regex"; then
+             $run eval 'egrep -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+             $run eval '$mv "$nlist"T "$nlist"'
+           fi
+
+           # Prepare the list of exported symbols
+           if test -z "$export_symbols"; then
+             export_symbols="$output_objdir/$output.exp"
+             $run $rm $export_symbols
+             $run eval "sed -n -e '/^: @PROGRAM@$/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+           else
+             $run eval "sed -e 's/\([][.*^$]\)/\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$output.exp"'
+             $run eval 'grep -f "$output_objdir/$output.exp" < "$nlist" > "$nlist"T'
+             $run eval 'mv "$nlist"T "$nlist"'
+           fi
+         fi
+
+         for arg in $dlprefiles; do
+           $show "extracting global C symbols from \`$arg'"
+           name=`echo "$arg" | sed -e 's%^.*/%%'`
+           $run eval 'echo ": $name " >> "$nlist"'
+           $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'"
+         done
+
+         if test -z "$run"; then
+           # Make sure we have at least an empty file.
+           test -f "$nlist" || : > "$nlist"
+
+           if test -n "$exclude_expsyms"; then
+             egrep -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+             $mv "$nlist"T "$nlist"
+           fi
+
+           # Try sorting and uniquifying the output.
+           if grep -v "^: " < "$nlist" | sort +2 | uniq > "$nlist"S; then
+             :
+           else
+             grep -v "^: " < "$nlist" > "$nlist"S
+           fi
+
+           if test -f "$nlist"S; then
+             eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"'
+           else
+             echo '/* NONE */' >> "$output_objdir/$dlsyms"
+           fi
+
+           $echo >> "$output_objdir/$dlsyms" "\
+
+#undef lt_preloaded_symbols
+
+#if defined (__STDC__) && __STDC__
+# define lt_ptr_t void *
+#else
+# define lt_ptr_t char *
+# define const
+#endif
+
+/* The mapping between symbol names and symbols. */
+const struct {
+  const char *name;
+  lt_ptr_t address;
+}
+lt_preloaded_symbols[] =
+{\
+"
+
+           sed -n -e 's/^: \([^ ]*\) $/  {\"\1\", (lt_ptr_t) 0},/p' \
+               -e 's/^. \([^ ]*\) \([^ ]*\)$/  {"\2", (lt_ptr_t) \&\2},/p' \
+                 < "$nlist" >> "$output_objdir/$dlsyms"
+
+           $echo >> "$output_objdir/$dlsyms" "\
+  {0, (lt_ptr_t) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+         fi
+
+         pic_flag_for_symtable=
+         case $host in
+         # compiling the symbol table file with pic_flag works around
+         # a FreeBSD bug that causes programs to crash when -lm is
+         # linked before any other PIC object.  But we must not use
+         # pic_flag when linking with -static.  The problem exists in
+         # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+         *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+           case "$compile_command " in
+           *" -static "*) ;;
+           *) pic_flag_for_symtable=" $pic_flag -DPIC -DFREEBSD_WORKAROUND";;
+           esac;;
+         *-*-hpux*)
+           case "$compile_command " in
+           *" -static "*) ;;
+           *) pic_flag_for_symtable=" $pic_flag -DPIC";;
+           esac
+         esac
+
+         # Now compile the dynamic symbol file.
+         $show "(cd $output_objdir && $CC -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")"
+         $run eval '(cd $output_objdir && $CC -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $?
+
+         # Clean up the generated files.
+         $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T"
+         $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T"
+
+         # Transform the symbol file into the correct name.
+         compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+         finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"`
+         ;;
+       *)
+         $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2
+         exit 1
+         ;;
+       esac
+      else
+       # We keep going just in case the user didn't refer to
+       # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+       # really was required.
+
+       # Nullify the symbol file.
+       compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+       finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+      fi
+
+      if test $need_relink = no || test "$build_libtool_libs" != yes; then
+       # Replace the output file specification.
+       compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+       link_command="$compile_command$compile_rpath"
+
+       # We have no uninstalled library dependencies, so finalize right now.
+       $show "$link_command"
+       $run eval "$link_command"
+       status=$?
+
+       # Delete the generated files.
+       if test -n "$dlsyms"; then
+         $show "$rm $output_objdir/${outputname}S.${objext}"
+         $run $rm "$output_objdir/${outputname}S.${objext}"
+       fi
+
+       exit $status
+      fi
+
+      if test -n "$shlibpath_var"; then
+       # We should set the shlibpath_var
+       rpath=
+       for dir in $temp_rpath; do
+         case $dir in
+         [\\/]* | [A-Za-z]:[\\/]*)
+           # Absolute path.
+           rpath="$rpath$dir:"
+           ;;
+         *)
+           # Relative path: add a thisdir entry.
+           rpath="$rpath\$thisdir/$dir:"
+           ;;
+         esac
+       done
+       temp_rpath="$rpath"
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+       compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+       finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+       if test -n "$perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $perm_rpath; do
+           rpath="$rpath$dir:"
+         done
+         compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+       if test -n "$finalize_perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $finalize_perm_rpath; do
+           rpath="$rpath$dir:"
+         done
+         finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+      fi
+
+      if test "$no_install" = yes; then
+       # We don't need to create a wrapper script.
+       link_command="$compile_var$compile_command$compile_rpath"
+       # Replace the output file specification.
+       link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+       # Delete the old output file.
+       $run $rm $output
+       # Link the executable and exit
+       $show "$link_command"
+       $run eval "$link_command" || exit $?
+       exit 0
+      fi
+
+      if test "$hardcode_action" = relink; then
+       # Fast installation is not supported
+       link_command="$compile_var$compile_command$compile_rpath"
+       relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+       $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2
+       $echo "$modename: \`$output' will be relinked during installation" 1>&2
+      else
+       if test "$fast_install" != no; then
+         link_command="$finalize_var$compile_command$finalize_rpath"
+         if test "$fast_install" = yes; then
+           relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+         else
+           # fast_install is set to needless
+           relink_command=
+         fi
+       else
+         link_command="$compile_var$compile_command$compile_rpath"
+         relink_command="$finalize_var$finalize_command$finalize_rpath"
+       fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      $show "$link_command"
+      $run eval "$link_command" || exit $?
+
+      # Now create the wrapper script.
+      $show "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+       # Preserve any variables that may affect compiler behavior
+       for var in $variables_saved_for_relink; do
+         if eval test -z \"\${$var+set}\"; then
+           relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command"
+         elif eval var_value=\$$var; test -z "$var_value"; then
+           relink_command="$var=; export $var; $relink_command"
+         else
+           var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"`
+           relink_command="$var=\"$var_value\"; export $var; $relink_command"
+         fi
+       done
+       relink_command="cd `pwd`; $relink_command"
+       relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Quote $echo for shipping.
+      if test "X$echo" = "X$SHELL $0 --fallback-echo"; then
+       case $0 in
+       [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $0 --fallback-echo";;
+       *) qecho="$SHELL `pwd`/$0 --fallback-echo";;
+       esac
+       qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"`
+      else
+       qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if our run command is non-null.
+      if test -z "$run"; then
+       # win32 will think the script is a binary if it has
+       # a .exe suffix, so we strip it off here.
+       case $output in
+         *.exe) output=`echo $output|sed 's,.exe$,,'` ;;
+       esac
+       # test for cygwin because mv fails w/o .exe extensions
+       case $host in
+         *cygwin*) exeext=.exe ;;
+         *) exeext= ;;
+       esac
+       $rm $output
+       trap "$rm $output; exit 1" 1 2 15
+
+       $echo > $output "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='sed -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+if test \"\${CDPATH+set}\" = set; then CDPATH=:; export CDPATH; fi
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variable:
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$echo are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    echo=\"$qecho\"
+    file=\"\$0\"
+    # Make sure echo works.
+    if test \"X\$1\" = X--no-reexec; then
+      # Discard the --no-reexec flag, and continue.
+      shift
+    elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then
+      # Yippee, \$echo works!
+      :
+    else
+      # Restart under the correct shell, and then maybe \$echo will work.
+      exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+    fi
+  fi\
+"
+       $echo >> $output "\
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | sed -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | sed -n 's/.*-> //p'\`
+  done
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+       if test "$fast_install" = yes; then
+         echo >> $output "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" || \\
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | sed 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $mkdir \"\$progdir\"
+    else
+      $rm \"\$progdir/\$file\"
+    fi"
+
+         echo >> $output "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if (eval \$relink_command); then :
+      else
+       $rm \"\$progdir/\$file\"
+       exit 1
+      fi
+    fi
+
+    $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $rm \"\$progdir/\$program\";
+      $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $rm \"\$progdir/\$file\"
+  fi"
+       else
+         echo >> $output "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+       fi
+
+       echo >> $output "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+       # Export our shlibpath_var if we have one.
+       if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+         $echo >> $output "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+       fi
+
+       # fixup the dll searchpath if we need to.
+       if test -n "$dllsearchpath"; then
+         $echo >> $output "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+       fi
+
+       $echo >> $output "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+"
+       case $host in
+       # win32 systems need to use the prog path for dll
+       # lookup to work
+       *-*-cygwin* | *-*-pw32*)
+         $echo >> $output "\
+      exec \$progdir/\$program \${1+\"\$@\"}
+"
+         ;;
+
+       # Backslashes separate directories on plain windows
+       *-*-mingw | *-*-os2*)
+         $echo >> $output "\
+      exec \$progdir\\\\\$program \${1+\"\$@\"}
+"
+         ;;
+
+       *)
+         $echo >> $output "\
+      # Export the path to the program.
+      PATH=\"\$progdir:\$PATH\"
+      export PATH
+
+      exec \$program \${1+\"\$@\"}
+"
+         ;;
+       esac
+       $echo >> $output "\
+      \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\"
+      exit 1
+    fi
+  else
+    # The program doesn't exist.
+    \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2
+    \$echo \"This script is just a wrapper for \$program.\" 1>&2
+    echo \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+       chmod +x $output
+      fi
+      exit 0
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+       oldobjs="$libobjs_save"
+       addlibs="$convenience"
+       build_libtool_libs=no
+      else
+       if test "$build_libtool_libs" = module; then
+         oldobjs="$libobjs_save"
+         build_libtool_libs=no
+       else
+         oldobjs="$objs$old_deplibs "`$echo "X$libobjs_save" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`
+       fi
+       addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+       gentop="$output_objdir/${outputname}x"
+       $show "${rm}r $gentop"
+       $run ${rm}r "$gentop"
+       $show "mkdir $gentop"
+       $run mkdir "$gentop"
+       status=$?
+       if test $status -ne 0 && test ! -d "$gentop"; then
+         exit $status
+       fi
+       generated="$generated $gentop"
+
+       # Add in members from convenience archives.
+       for xlib in $addlibs; do
+         # Extract the objects.
+         case $xlib in
+         [\\/]* | [A-Za-z]:[\\/]*) xabs="$xlib" ;;
+         *) xabs=`pwd`"/$xlib" ;;
+         esac
+         xlib=`$echo "X$xlib" | $Xsed -e 's%^.*/%%'`
+         xdir="$gentop/$xlib"
+
+         $show "${rm}r $xdir"
+         $run ${rm}r "$xdir"
+         $show "mkdir $xdir"
+         $run mkdir "$xdir"
+         status=$?
+         if test $status -ne 0 && test ! -d "$xdir"; then
+           exit $status
+         fi
+         $show "(cd $xdir && $AR x $xabs)"
+         $run eval "(cd \$xdir && $AR x \$xabs)" || exit $?
+
+         oldobjs="$oldobjs "`find $xdir -name \*.${objext} -print -o -name \*.lo -print | $NL2SP`
+       done
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+       eval cmds=\"$old_archive_from_new_cmds\"
+      else
+       # Ensure that we have .o objects in place in case we decided
+       # not to build a shared library, and have fallen back to building
+       # static libs even though --disable-static was passed!
+       for oldobj in $oldobjs; do
+         if test ! -f $oldobj; then
+           xdir=`$echo "X$oldobj" | $Xsed -e 's%/[^/]*$%%'`
+           if test "X$xdir" = "X$oldobj"; then
+             xdir="."
+           else
+             xdir="$xdir"
+           fi
+           baseobj=`$echo "X$oldobj" | $Xsed -e 's%^.*/%%'`
+           obj=`$echo "X$baseobj" | $Xsed -e "$o2lo"`
+           $show "(cd $xdir && ${LN_S} $obj $baseobj)"
+           $run eval '(cd $xdir && ${LN_S} $obj $baseobj)' || exit $?
+         fi
+       done
+
+       eval cmds=\"$old_archive_cmds\"
+      fi
+      IFS="${IFS=      }"; save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+       IFS="$save_ifs"
+       $show "$cmd"
+       $run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+    done
+
+    if test -n "$generated"; then
+      $show "${rm}r$generated"
+      $run ${rm}r$generated
+    fi
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      $show "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+       if eval test -z \"\${$var+set}\"; then
+         relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command"
+       elif eval var_value=\$$var; test -z "$var_value"; then
+         relink_command="$var=; export $var; $relink_command"
+       else
+         var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"`
+         relink_command="$var=\"$var_value\"; export $var; $relink_command"
+       fi
+      done
+      # Quote the link command for shipping.
+      relink_command="cd `pwd`; $SHELL $0 --mode=relink $libtool_args"
+      relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+
+      # Only create the output if not a dry run.
+      if test -z "$run"; then
+       for installed in no yes; do
+         if test "$installed" = yes; then
+           if test -z "$install_libdir"; then
+             break
+           fi
+           output="$output_objdir/$outputname"i
+           # Replace all uninstalled libtool libraries with the installed ones
+           newdependency_libs=
+           for deplib in $dependency_libs; do
+             case $deplib in
+             *.la)
+               name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'`
+               eval libdir=`sed -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+               if test -z "$libdir"; then
+                 $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2
+                 exit 1
+               fi
+               newdependency_libs="$newdependency_libs $libdir/$name"
+               ;;
+             *) newdependency_libs="$newdependency_libs $deplib" ;;
+             esac
+           done
+           dependency_libs="$newdependency_libs"
+           newdlfiles=
+           for lib in $dlfiles; do
+             name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'`
+             eval libdir=`sed -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+             if test -z "$libdir"; then
+               $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+               exit 1
+             fi
+             newdlfiles="$newdlfiles $libdir/$name"
+           done
+           dlfiles="$newdlfiles"
+           newdlprefiles=
+           for lib in $dlprefiles; do
+             name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'`
+             eval libdir=`sed -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+             if test -z "$libdir"; then
+               $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+               exit 1
+             fi
+             newdlprefiles="$newdlprefiles $libdir/$name"
+           done
+           dlprefiles="$newdlprefiles"
+         fi
+         $rm $output
+         # place dlname in correct position for cygwin
+         tdlname=$dlname
+         case $host,$output,$installed,$module,$dlname in
+           *cygwin*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
+         esac
+         $echo > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+         if test "$installed" = no && test $need_relink = yes; then
+           $echo >> $output "\
+relink_command=\"$relink_command\""
+         fi
+       done
+      fi
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)"
+      $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $?
+      ;;
+    esac
+    exit 0
+    ;;
+
+  # libtool install mode
+  install)
+    modename="$modename: install"
+
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+       # Allow the use of GNU shtool's install command.
+       $echo "X$nonopt" | $Xsed | grep shtool > /dev/null; then
+      # Aesthetically quote it.
+      arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"`
+      case $arg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*)
+       arg="\"$arg\""
+       ;;
+      esac
+      install_prog="$arg "
+      arg="$1"
+      shift
+    else
+      install_prog=
+      arg="$nonopt"
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+    case $arg in
+    *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \      ]*|*]*)
+      arg="\"$arg\""
+      ;;
+    esac
+    install_prog="$install_prog$arg"
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    for arg
+    do
+      if test -n "$dest"; then
+       files="$files $dest"
+       dest="$arg"
+       continue
+      fi
+
+      case $arg in
+      -d) isdir=yes ;;
+      -f) prev="-f" ;;
+      -g) prev="-g" ;;
+      -m) prev="-m" ;;
+      -o) prev="-o" ;;
+      -s)
+       stripme=" -s"
+       continue
+       ;;
+      -*) ;;
+
+      *)
+       # If the previous option needed an argument, then skip it.
+       if test -n "$prev"; then
+         prev=
+       else
+         dest="$arg"
+         continue
+       fi
+       ;;
+      esac
+
+      # Aesthetically quote the argument.
+      arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`
+      case $arg in
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*)
+       arg="\"$arg\""
+       ;;
+      esac
+      install_prog="$install_prog $arg"
+    done
+
+    if test -z "$install_prog"; then
+      $echo "$modename: you must specify an install program" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    if test -n "$prev"; then
+      $echo "$modename: the \`$prev' option requires an argument" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+       $echo "$modename: no file or destination specified" 1>&2
+      else
+       $echo "$modename: you must specify a destination" 1>&2
+      fi
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    # Strip any trailing slash from the destination.
+    dest=`$echo "X$dest" | $Xsed -e 's%/$%%'`
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'`
+      test "X$destdir" = "X$dest" && destdir=.
+      destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'`
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files
+      if test $# -gt 2; then
+       $echo "$modename: \`$dest' is not a directory" 1>&2
+       $echo "$help" 1>&2
+       exit 1
+      fi
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+       case $file in
+       *.lo) ;;
+       *)
+         $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+         ;;
+       esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+       # Do the static libraries later.
+       staticlibs="$staticlibs $file"
+       ;;
+
+      *.la)
+       # Check to see that this really is a libtool archive.
+       if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+       else
+         $echo "$modename: \`$file' is not a valid libtool archive" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+
+       library_names=
+       old_library=
+       relink_command=
+       # If there is no directory component, then add one.
+       case $file in
+       */* | *\\*) . $file ;;
+       *) . ./$file ;;
+       esac
+
+       # Add the libdir to current_libdirs if it is the destination.
+       if test "X$destdir" = "X$libdir"; then
+         case "$current_libdirs " in
+         *" $libdir "*) ;;
+         *) current_libdirs="$current_libdirs $libdir" ;;
+         esac
+       else
+         # Note the libdir as a future libdir.
+         case "$future_libdirs " in
+         *" $libdir "*) ;;
+         *) future_libdirs="$future_libdirs $libdir" ;;
+         esac
+       fi
+
+       dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/
+       test "X$dir" = "X$file/" && dir=
+       dir="$dir$objdir"
+
+       if test -n "$relink_command"; then
+         $echo "$modename: warning: relinking \`$file'" 1>&2
+         $show "$relink_command"
+         if $run eval "$relink_command"; then :
+         else
+           $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
+           continue
+         fi
+       fi
+
+       # See the names of the shared library.
+       set dummy $library_names
+       if test -n "$2"; then
+         realname="$2"
+         shift
+         shift
+
+         srcname="$realname"
+         test -n "$relink_command" && srcname="$realname"T
+
+         # Install the shared library and build the symlinks.
+         $show "$install_prog $dir/$srcname $destdir/$realname"
+         $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $?
+         if test -n "$stripme" && test -n "$striplib"; then
+           $show "$striplib $destdir/$realname"
+           $run eval "$striplib $destdir/$realname" || exit $?
+         fi
+
+         if test $# -gt 0; then
+           # Delete the old symlinks, and create new ones.
+           for linkname
+           do
+             if test "$linkname" != "$realname"; then
+               $show "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+               $run eval "(cd $destdir && $rm $linkname && $LN_S $realname $linkname)"
+             fi
+           done
+         fi
+
+         # Do each command in the postinstall commands.
+         lib="$destdir/$realname"
+         eval cmds=\"$postinstall_cmds\"
+         IFS="${IFS=   }"; save_ifs="$IFS"; IFS='~'
+         for cmd in $cmds; do
+           IFS="$save_ifs"
+           $show "$cmd"
+           $run eval "$cmd" || exit $?
+         done
+         IFS="$save_ifs"
+       fi
+
+       # Install the pseudo-library for information purposes.
+       name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+       instname="$dir/$name"i
+       $show "$install_prog $instname $destdir/$name"
+       $run eval "$install_prog $instname $destdir/$name" || exit $?
+
+       # Maybe install the static library, too.
+       test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+       ;;
+
+      *.lo)
+       # Install (i.e. copy) a libtool object.
+
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+         destfile="$destdir/$destfile"
+       fi
+
+       # Deduce the name of the destination old-style object file.
+       case $destfile in
+       *.lo)
+         staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"`
+         ;;
+       *.$objext)
+         staticdest="$destfile"
+         destfile=
+         ;;
+       *)
+         $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+         ;;
+       esac
+
+       # Install the libtool object if requested.
+       if test -n "$destfile"; then
+         $show "$install_prog $file $destfile"
+         $run eval "$install_prog $file $destfile" || exit $?
+       fi
+
+       # Install the old object if enabled.
+       if test "$build_old_libs" = yes; then
+         # Deduce the name of the old-style object file.
+         staticobj=`$echo "X$file" | $Xsed -e "$lo2o"`
+
+         $show "$install_prog $staticobj $staticdest"
+         $run eval "$install_prog \$staticobj \$staticdest" || exit $?
+       fi
+       exit 0
+       ;;
+
+      *)
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+         destfile="$destdir/$destfile"
+       fi
+
+       # Do a test to see if this is really a libtool program.
+       if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         notinst_deplibs=
+         relink_command=
+
+         # If there is no directory component, then add one.
+         case $file in
+         */* | *\\*) . $file ;;
+         *) . ./$file ;;
+         esac
+
+         # Check the variables that should have been set.
+         if test -z "$notinst_deplibs"; then
+           $echo "$modename: invalid libtool wrapper script \`$file'" 1>&2
+           exit 1
+         fi
+
+         finalize=yes
+         for lib in $notinst_deplibs; do
+           # Check to see that each library is installed.
+           libdir=
+           if test -f "$lib"; then
+             # If there is no directory component, then add one.
+             case $lib in
+             */* | *\\*) . $lib ;;
+             *) . ./$lib ;;
+             esac
+           fi
+           libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test
+           if test -n "$libdir" && test ! -f "$libfile"; then
+             $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2
+             finalize=no
+           fi
+         done
+
+         relink_command=
+         # If there is no directory component, then add one.
+         case $file in
+         */* | *\\*) . $file ;;
+         *) . ./$file ;;
+         esac
+
+         outputname=
+         if test "$fast_install" = no && test -n "$relink_command"; then
+           if test "$finalize" = yes && test -z "$run"; then
+             tmpdir="/tmp"
+             test -n "$TMPDIR" && tmpdir="$TMPDIR"
+             tmpdir="$tmpdir/libtool-$$"
+             if $mkdir -p "$tmpdir" && chmod 700 "$tmpdir"; then :
+             else
+               $echo "$modename: error: cannot create temporary directory \`$tmpdir'" 1>&2
+               continue
+             fi
+             file=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+             outputname="$tmpdir/$file"
+             # Replace the output file specification.
+             relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+             $show "$relink_command"
+             if $run eval "$relink_command"; then :
+             else
+               $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2
+               ${rm}r "$tmpdir"
+               continue
+             fi
+             file="$outputname"
+           else
+             $echo "$modename: warning: cannot relink \`$file'" 1>&2
+           fi
+         else
+           # Install the binary that we compiled earlier.
+           file=`$echo "X$file" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+         fi
+       fi
+
+       # remove .exe since cygwin /usr/bin/install will append another
+       # one anyways
+       case $install_prog,$host in
+       /usr/bin/install*,*cygwin*)
+         case $file:$destfile in
+         *.exe:*.exe)
+           # this is ok
+           ;;
+         *.exe:*)
+           destfile=$destfile.exe
+           ;;
+         *:*.exe)
+           destfile=`echo $destfile | sed -e 's,.exe$,,'`
+           ;;
+         esac
+         ;;
+       esac
+       $show "$install_prog$stripme $file $destfile"
+       $run eval "$install_prog\$stripme \$file \$destfile" || exit $?
+       test -n "$outputname" && ${rm}r "$tmpdir"
+       ;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+
+      $show "$install_prog $file $oldlib"
+      $run eval "$install_prog \$file \$oldlib" || exit $?
+
+      if test -n "$stripme" && test -n "$striplib"; then
+       $show "$old_striplib $oldlib"
+       $run eval "$old_striplib $oldlib" || exit $?
+      fi
+
+      # Do each command in the postinstall commands.
+      eval cmds=\"$old_postinstall_cmds\"
+      IFS="${IFS=      }"; save_ifs="$IFS"; IFS='~'
+      for cmd in $cmds; do
+       IFS="$save_ifs"
+       $show "$cmd"
+       $run eval "$cmd" || exit $?
+      done
+      IFS="$save_ifs"
+    done
+
+    if test -n "$future_libdirs"; then
+      $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2
+    fi
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      test -n "$run" && current_libdirs=" -n$current_libdirs"
+      exec $SHELL $0 --finish$current_libdirs
+      exit 1
+    fi
+
+    exit 0
+    ;;
+
+  # libtool finish mode
+  finish)
+    modename="$modename: finish"
+    libdirs="$nonopt"
+    admincmds=
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for dir
+      do
+       libdirs="$libdirs $dir"
+      done
+
+      for libdir in $libdirs; do
+       if test -n "$finish_cmds"; then
+         # Do each command in the finish commands.
+         eval cmds=\"$finish_cmds\"
+         IFS="${IFS=   }"; save_ifs="$IFS"; IFS='~'
+         for cmd in $cmds; do
+           IFS="$save_ifs"
+           $show "$cmd"
+           $run eval "$cmd" || admincmds="$admincmds
+       $cmd"
+         done
+         IFS="$save_ifs"
+       fi
+       if test -n "$finish_eval"; then
+         # Do the single finish_eval.
+         eval cmds=\"$finish_eval\"
+         $run eval "$cmds" || admincmds="$admincmds
+       $cmds"
+       fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    test "$show" = ":" && exit 0
+
+    echo "----------------------------------------------------------------------"
+    echo "Libraries have been installed in:"
+    for libdir in $libdirs; do
+      echo "   $libdir"
+    done
+    echo
+    echo "If you ever happen to want to link against installed libraries"
+    echo "in a given directory, LIBDIR, you must either use libtool, and"
+    echo "specify the full pathname of the library, or use the \`-LLIBDIR'"
+    echo "flag during linking and do at least one of the following:"
+    if test -n "$shlibpath_var"; then
+      echo "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+      echo "     during execution"
+    fi
+    if test -n "$runpath_var"; then
+      echo "   - add LIBDIR to the \`$runpath_var' environment variable"
+      echo "     during linking"
+    fi
+    if test -n "$hardcode_libdir_flag_spec"; then
+      libdir=LIBDIR
+      eval flag=\"$hardcode_libdir_flag_spec\"
+
+      echo "   - use the \`$flag' linker flag"
+    fi
+    if test -n "$admincmds"; then
+      echo "   - have your system administrator run these commands:$admincmds"
+    fi
+    if test -f /etc/ld.so.conf; then
+      echo "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+    fi
+    echo
+    echo "See any operating system documentation about shared libraries for"
+    echo "more information, such as the ld(1) and ld.so(8) manual pages."
+    echo "----------------------------------------------------------------------"
+    exit 0
+    ;;
+
+  # libtool execute mode
+  execute)
+    modename="$modename: execute"
+
+    # The first argument is the command name.
+    cmd="$nonopt"
+    if test -z "$cmd"; then
+      $echo "$modename: you must specify a COMMAND" 1>&2
+      $echo "$help"
+      exit 1
+    fi
+
+    # Handle -dlopen flags immediately.
+    for file in $execute_dlfiles; do
+      if test ! -f "$file"; then
+       $echo "$modename: \`$file' is not a file" 1>&2
+       $echo "$help" 1>&2
+       exit 1
+      fi
+
+      dir=
+      case $file in
+      *.la)
+       # Check to see that this really is a libtool archive.
+       if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then :
+       else
+         $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2
+         $echo "$help" 1>&2
+         exit 1
+       fi
+
+       # Read the libtool library.
+       dlname=
+       library_names=
+
+       # If there is no directory component, then add one.
+       case $file in
+       */* | *\\*) . $file ;;
+       *) . ./$file ;;
+       esac
+
+       # Skip this library if it cannot be dlopened.
+       if test -z "$dlname"; then
+         # Warn if it was a shared library.
+         test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'"
+         continue
+       fi
+
+       dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+       test "X$dir" = "X$file" && dir=.
+
+       if test -f "$dir/$objdir/$dlname"; then
+         dir="$dir/$objdir"
+       else
+         $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2
+         exit 1
+       fi
+       ;;
+
+      *.lo)
+       # Just add the directory containing the .lo file.
+       dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+       test "X$dir" = "X$file" && dir=.
+       ;;
+
+      *)
+       $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2
+       continue
+       ;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+       eval "$shlibpath_var=\"\$dir\""
+      else
+       eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -*) ;;
+      *)
+       # Do a test to see if this is really a libtool program.
+       if (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         # If there is no directory component, then add one.
+         case $file in
+         */* | *\\*) . $file ;;
+         *) . ./$file ;;
+         esac
+
+         # Transform arg to wrapped name.
+         file="$progdir/$program"
+       fi
+       ;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"`
+      args="$args \"$file\""
+    done
+
+    if test -z "$run"; then
+      if test -n "$shlibpath_var"; then
+       # Export the shlibpath_var.
+       eval "export $shlibpath_var"
+      fi
+
+      # Restore saved enviroment variables
+      if test "${save_LC_ALL+set}" = set; then
+       LC_ALL="$save_LC_ALL"; export LC_ALL
+      fi
+      if test "${save_LANG+set}" = set; then
+       LANG="$save_LANG"; export LANG
+      fi
+
+      # Now actually exec the command.
+      eval "exec \$cmd$args"
+
+      $echo "$modename: cannot exec \$cmd$args"
+      exit 1
+    else
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+       eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\""
+       $echo "export $shlibpath_var"
+      fi
+      $echo "$cmd$args"
+      exit 0
+    fi
+    ;;
+
+  # libtool clean and uninstall mode
+  clean | uninstall)
+    modename="$modename: $mode"
+    rm="$nonopt"
+    files=
+    rmforce=
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    for arg
+    do
+      case $arg in
+      -f) rm="$rm $arg"; rmforce=yes ;;
+      -*) rm="$rm $arg" ;;
+      *) files="$files $arg" ;;
+      esac
+    done
+
+    if test -z "$rm"; then
+      $echo "$modename: you must specify an RM program" 1>&2
+      $echo "$help" 1>&2
+      exit 1
+    fi
+
+    rmdirs=
+
+    for file in $files; do
+      dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`
+      if test "X$dir" = "X$file"; then
+       dir=.
+       objdir="$objdir"
+      else
+       objdir="$dir/$objdir"
+      fi
+      name=`$echo "X$file" | $Xsed -e 's%^.*/%%'`
+      test $mode = uninstall && objdir="$dir"
+
+      # Remember objdir for removal later, being careful to avoid duplicates
+      if test $mode = clean; then
+       case " $rmdirs " in
+         *" $objdir "*) ;;
+         *) rmdirs="$rmdirs $objdir" ;;
+       esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if (test -L "$file") >/dev/null 2>&1 \
+        || (test -h "$file") >/dev/null 2>&1 \
+       || test -f "$file"; then
+        :
+      elif test -d "$file"; then
+        exit_status=1
+       continue
+      elif test "$rmforce" = yes; then
+        continue
+      fi
+
+      rmfiles="$file"
+
+      case $name in
+      *.la)
+       # Possibly a libtool archive, so verify it.
+       if (sed -e '2q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         . $dir/$name
+
+         # Delete the libtool libraries and symlinks.
+         for n in $library_names; do
+           rmfiles="$rmfiles $objdir/$n"
+         done
+         test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library"
+         test $mode = clean && rmfiles="$rmfiles $objdir/$name $objdir/${name}i"
+
+         if test $mode = uninstall; then
+           if test -n "$library_names"; then
+             # Do each command in the postuninstall commands.
+             eval cmds=\"$postuninstall_cmds\"
+             IFS="${IFS=       }"; save_ifs="$IFS"; IFS='~'
+             for cmd in $cmds; do
+               IFS="$save_ifs"
+               $show "$cmd"
+               $run eval "$cmd"
+               if test $? != 0 && test "$rmforce" != yes; then
+                 exit_status=1
+               fi
+             done
+             IFS="$save_ifs"
+           fi
+
+           if test -n "$old_library"; then
+             # Do each command in the old_postuninstall commands.
+             eval cmds=\"$old_postuninstall_cmds\"
+             IFS="${IFS=       }"; save_ifs="$IFS"; IFS='~'
+             for cmd in $cmds; do
+               IFS="$save_ifs"
+               $show "$cmd"
+               $run eval "$cmd"
+               if test $? != 0 && test "$rmforce" != yes; then
+                 exit_status=1
+               fi
+             done
+             IFS="$save_ifs"
+           fi
+           # FIXME: should reinstall the best remaining shared library.
+         fi
+       fi
+       ;;
+
+      *.lo)
+       if test "$build_old_libs" = yes; then
+         oldobj=`$echo "X$name" | $Xsed -e "$lo2o"`
+         rmfiles="$rmfiles $dir/$oldobj"
+       fi
+       ;;
+
+      *)
+       # Do a test to see if this is a libtool program.
+       if test $mode = clean &&
+          (sed -e '4q' $file | egrep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then
+         relink_command=
+         . $dir/$file
+
+         rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}"
+         if test "$fast_install" = yes && test -n "$relink_command"; then
+           rmfiles="$rmfiles $objdir/lt-$name"
+         fi
+       fi
+       ;;
+      esac
+      $show "$rm $rmfiles"
+      $run $rm $rmfiles || exit_status=1
+    done
+
+    # Try to remove the ${objdir}s in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+       $show "rmdir $dir"
+       $run rmdir $dir >/dev/null 2>&1
+      fi
+    done
+
+    exit $exit_status
+    ;;
+
+  "")
+    $echo "$modename: you must specify a MODE" 1>&2
+    $echo "$generic_help" 1>&2
+    exit 1
+    ;;
+  esac
+
+  $echo "$modename: invalid operation mode \`$mode'" 1>&2
+  $echo "$generic_help" 1>&2
+  exit 1
+fi # test -z "$show_help"
+
+# We need to display help for each of the modes.
+case $mode in
+"") $echo \
+"Usage: $modename [OPTION]... [MODE-ARG]...
+
+Provide generalized library-building support services.
+
+    --config          show all configuration variables
+    --debug           enable verbose shell tracing
+-n, --dry-run         display commands without modifying any files
+    --features        display basic configuration information and exit
+    --finish          same as \`--mode=finish'
+    --help            display this help message and exit
+    --mode=MODE       use operation mode MODE [default=inferred from MODE-ARGS]
+    --quiet           same as \`--silent'
+    --silent          don't print informational messages
+    --version         print version information
+
+MODE must be one of the following:
+
+      clean           remove files from the build directory
+      compile         compile a source file into a libtool object
+      execute         automatically set library path, then run a program
+      finish          complete the installation of libtool libraries
+      install         install libraries or executables
+      link            create a library or an executable
+      uninstall       remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE.  Try \`$modename --help --mode=MODE' for
+a more detailed description of MODE."
+  exit 0
+  ;;
+
+clean)
+  $echo \
+"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+  ;;
+
+compile)
+  $echo \
+"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -prefer-pic       try to building PIC objects only
+  -prefer-non-pic   try to building non-PIC objects only
+  -static           always build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+  ;;
+
+execute)
+  $echo \
+"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+  ;;
+
+finish)
+  $echo \
+"Usage: $modename [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+  ;;
+
+install)
+  $echo \
+"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+  ;;
+
+link)
+  $echo \
+"Usage: $modename [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                   try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                   try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -static           do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                   specify library version info [each variable defaults to 0]
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+  ;;
+
+uninstall)
+  $echo \
+"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+  ;;
+
+*)
+  $echo "$modename: invalid operation mode \`$mode'" 1>&2
+  $echo "$help" 1>&2
+  exit 1
+  ;;
+esac
+
+echo
+$echo "Try \`$modename --help' for more information about other modes."
+
+exit 0
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/missing b/missing
index 7789652e877fbc83c770377691249820eebea1f2..0a7fb5a2acec32d1ef949e5e7e7fb11460c5e021 100755 (executable)
--- a/missing
+++ b/missing
@@ -1,7 +1,7 @@
 #! /bin/sh
 # Common stub for a few missing GNU programs while installing.
-# Copyright (C) 1996, 1997 Free Software Foundation, Inc.
-# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996.
+# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 # 02111-1307, USA.
 
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
 if test $# -eq 0; then
   echo 1>&2 "Try \`$0 --help' for more information"
   exit 1
 fi
 
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+case "$1" in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
 case "$1" in
 
   -h|--h|--he|--hel|--help)
@@ -35,6 +61,7 @@ error status if there is no known handling for PROGRAM.
 Options:
   -h, --help      display this help and exit
   -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
 
 Supported PROGRAM values:
   aclocal      touch file \`aclocal.m4'
@@ -43,13 +70,15 @@ Supported PROGRAM values:
   automake     touch all \`Makefile.in' files
   bison        create \`y.tab.[ch]', if possible, from existing .[ch]
   flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
   lex          create \`lex.yy.c', if possible, from existing .c
   makeinfo     touch the output file
+  tar          try tar, gnutar, gtar, then tar without non-portable flags
   yacc         create \`y.tab.[ch]', if possible, from existing .[ch]"
     ;;
 
   -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
-    echo "missing - GNU libit 0.0"
+    echo "missing 0.3 - GNU automake"
     ;;
 
   -*)
@@ -61,7 +90,7 @@ Supported PROGRAM values:
   aclocal)
     echo 1>&2 "\
 WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`acinclude.m4' or \`configure.in'.  You might want
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
          to install the \`Automake' and \`Perl' packages.  Grab them from
          any GNU archive site."
     touch aclocal.m4
@@ -70,7 +99,7 @@ WARNING: \`$1' is missing on your system.  You should only need it if
   autoconf)
     echo 1>&2 "\
 WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`configure.in'.  You might want to install the
+         you modified \`${configure_ac}'.  You might want to install the
          \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
          archive site."
     touch configure
@@ -79,10 +108,10 @@ WARNING: \`$1' is missing on your system.  You should only need it if
   autoheader)
     echo 1>&2 "\
 WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`acconfig.h' or \`configure.in'.  You might want
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
          to install the \`Autoconf' and \`GNU m4' packages.  Grab them
          from any GNU archive site."
-    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in`
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
     test -z "$files" && files="config.h"
     touch_files=
     for f in $files; do
@@ -98,7 +127,7 @@ WARNING: \`$1' is missing on your system.  You should only need it if
   automake)
     echo 1>&2 "\
 WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'.
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
          You might want to install the \`Automake' and \`Perl' packages.
          Grab them from any GNU archive site."
     find . -type f -name Makefile.am -print |
@@ -159,7 +188,32 @@ WARNING: \`$1' is missing on your system.  You should only need it if
     fi
     ;;
 
+  help2man)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+        you modified a dependency of a manual page.  You may need the
+        \`Help2man' package in order for those modifications to take
+        effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+       file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+    fi
+    if [ -f "$file" ]; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo ".ab help2man is required to generate this page"
+       exit 1
+    fi
+    ;;
+
   makeinfo)
+    if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
+       # We have makeinfo, but it failed.
+       exit 1
+    fi
+
     echo 1>&2 "\
 WARNING: \`$1' is missing on your system.  You should only need it if
          you modified a \`.texi' or \`.texinfo' file, or any other file
@@ -175,6 +229,45 @@ WARNING: \`$1' is missing on your system.  You should only need it if
     touch $file
     ;;
 
+  tar)
+    shift
+    if test -n "$run"; then
+      echo 1>&2 "ERROR: \`tar' requires --run"
+      exit 1
+    fi
+
+    # We have already tried tar in the generic part.
+    # Look for gnutar/gtar before invocation to avoid ugly error
+    # messages.
+    if (gnutar --version > /dev/null 2>&1); then
+       gnutar ${1+"$@"} && exit 0
+    fi
+    if (gtar --version > /dev/null 2>&1); then
+       gtar ${1+"$@"} && exit 0
+    fi
+    firstarg="$1"
+    if shift; then
+       case "$firstarg" in
+       *o*)
+           firstarg=`echo "$firstarg" | sed s/o//`
+           tar "$firstarg" ${1+"$@"} && exit 0
+           ;;
+       esac
+       case "$firstarg" in
+       *h*)
+           firstarg=`echo "$firstarg" | sed s/h//`
+           tar "$firstarg" ${1+"$@"} && exit 0
+           ;;
+       esac
+    fi
+
+    echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+         You may want to install GNU tar or Free paxutils, or check the
+         command line arguments."
+    exit 1
+    ;;
+
   *)
     echo 1>&2 "\
 WARNING: \`$1' is needed, and you do not seem to have it handy on your
diff --git a/prepare b/prepare
index 7ac2ad60fdd53be41bcddc29f57a197b9d250689..e61e74fe26193554110320dc2e9194e1eaedb8ae 100755 (executable)
--- a/prepare
+++ b/prepare
@@ -1,15 +1,14 @@
-#!/bin/sh
+#! /bin/sh
 #
-#  prepare-clean
+#  prepare
 #
-#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#  Author: Pekka Riikonen <priikone@silcnet.org>
 #
-#  Copyright (C) 2000 Pekka Riikonen
+#  Copyright (C) 2000 - 2001 Pekka Riikonen
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
+#  the Free Software Foundation; version 2 of the License.
 #
 #  This program is distributed in the hope that it will be useful,
 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
 # temporary files (including these prepare* scripts) are removed.
 #
 
+#
+# Usage: ./prepare [<distribution> <base version> <package version>]
+#
+# If <package version> is omitted <base version> is used as package
+# version.  The package version appears in the package name and in those
+# distributions that used the SILC_DIST_VERSION_STRING define in the
+# code.  The base version is the SILC_VERSION_STRING define.
+#
+
+#
+# SILC Distribution versions. Set here or give the version on the command
+# line as argument.
+#
+SILC_VERSION=0.7.3                     # Base version
+
+#############################################################################
+
 echo "Preparing SILC source tree for configuration and compilation..."
+
+distribution=$1
+if test "$distribution" = ""; then
+  distribution="toolkit";
+fi
+
+version=$2
+if test "$version" = ""; then
+  version=$SILC_VERSION;
+fi
+
+dist_version=$3
+if test "$dist_version" = ""; then
+  dist_version=$version
+fi
+
+echo "Preparing $distribution distribution version $version (package $dist_version)"
+
+#
+# Go though the subdirs and create the Makefile.ams from the
+# Makefile.am.pre files.
+#
+subdirs=`grep "$distribution"_SUBDIRS= distributions |cut -d=  -f2`
+ed=`grep "$distribution"_EXTRA_DIST= distributions |cut -d=  -f2`
+sed -e "/SILC_DISTRIBUTION_SUBDIRS/s//$subdirs/" -e "/SILC_DISTRIBUTION_EXTRA/s,,$ed," Makefile.am.pre >Makefile.am
+path=`pwd`
+for i in $subdirs
+do
+  cd $i
+  sub=`grep "$distribution"_SUBDIRS_"$i"= $path/distributions |cut -d=  -f2`
+  if test "$sub" = ""; then
+    cd $path
+    continue;
+  fi
+  if [ -f Makefile.am.pre ]; then
+    sed -e "/SILC_DISTRIBUTION_SUBDIRS/s//$sub/" -e "/SILC_DISTRIBUTION_SUBDIRS/s//$sub/" Makefile.am.pre >Makefile.am 2>/dev/null
+  fi
+  cd $path
+done
+cd $path
+
+
+#
+# Replace version string, package name and distribution infos, and create 
+# the actual configure.in
+#
+echo "# Automatically generated by ./prepare from configure.in.pre. Do not edit!" >configure.in2
+sed -e "/SILC_VERSION/s//$dist_version/" -e "/SILC_PACKAGE/s//silc-"$distribution"/" configure.in.pre >>configure.in2
+
+#
+# Create also the acconfig.h for the distribution. Note that this sets
+# also dist labels for distributions that are not defined.
+#
+cp acconfig.h.pre acconfig.h
+dists=`grep DISTRIBUTIONS= distributions |cut -d=  -f2`
+touch am_cond
+for i in $dists
+do
+  dl=`grep "$i"_DISTLABEL= distributions |cut -d=  -f2`
+  echo "#undef $dl" >>acconfig.h
+  if test "$i" = "$distribution"; then
+    echo "AM_CONDITIONAL($dl, test xtrue = xtrue)" >>am_cond
+  else
+    echo "AM_CONDITIONAL($dl, test xtrue = xfalse)" >>am_cond
+  fi
+done
+
+sed '/SILC_DIST_DEFINE/ r am_cond' configure.in2 >configure.in3
+dl=`grep "$distribution"_DISTLABEL= distributions |cut -d=  -f2`
+sed -e "/SILC_DIST_DEFINE/s//$dl/" configure.in3 >configure.in
+rm -f configure.in2 configure.in3 am_cond
+
+
+#
+# Prepare the Makefile.defines
+#
+cp Makefile.defines.pre Makefile.defines.in
+cp Makefile.defines_int.pre Makefile.defines_int.in
+cp Makefile.defines.in irssi
+cp Makefile.defines_int.in irssi
+cp Makefile.defines.in lib/silcmath/mpi
+cp Makefile.defines_int.in lib/silcmath/mpi
+
 aclocal
 autoconf
-autoheader
+autoheader >/dev/null 2>/dev/null
 automake
+
+echo "Preparing mpi"
+cd lib/silcmath/mpi
+aclocal
+autoconf
+autoheader </dev/null 2>/dev/null
+automake >/dev/null 2>/dev/null
+cd ../../..
+
+file=includes/version_internal.h
+echo "/* Automatically generated by ./prepare */" >$file
+echo "#define SILC_VERSION_STRING \"$version\"" >>$file
+echo "#define SILC_DIST_VERSION_STRING \"$dist_version\"" >>$file
+echo "#define SILC_PROTOCOL_VERSION_STRING \"SILC-1.0-$version\"" >>$file
+echo "#define SILC_NAME \"SILC $distribution\"" >>$file
+
+# preparing irssi
+echo "Preparing irssi"
+cd irssi
+sh autogen.sh 2>/dev/null 1>/dev/null
+cd ..
+file=irssi/irssi-version.h.in
+version_date=`date +%Y%m%d`
+echo "/* automatically created by autogen.sh */" > $file
+echo "#define IRSSI_VERSION \"$dist_version (Irssi base: @VERSION@ - SILC base: SILC Toolkit $version)\"" >>$file
+echo "#define IRSSI_VERSION_DATE $version_date" >> $file
+echo "#define IRSSI_VERSION_TIME $version_date" >> $file
+
 echo "Done, now run ./configure and make."
index 3c8754b71c45345cbed423c840ef995e86fb32cc..a07ab1c6d852879b0cf704862729ef1ff6612582 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#! /bin/sh
 #
 #  prepare-clean
 #
 # and compilation by calling ./prepare.
 #
 
+if [ -z "$MAKE" ]; then
+       MAKE=`which gmake`
+       if [ -z "$MAKE" ]; then
+               MAKE=`which make`
+               if [ -z "$MAKE" ]; then
+                       echo "Error, no [g]make in your path."
+                       exit 0
+               fi
+       fi
+fi
+echo "Using $MAKE as a make program..."
+
 echo "Cleaning entire SILC source tree..."
 echo "All errors and warnings may be safely ignored."
-make distclean
+$MAKE clean -k
+$MAKE distclean -k
+rm -f ltcf*
+rm -f Makefile.am
+rm -f Makefile.defines
+rm -f Makefile.defines.in
+rm -f Makefile.defines_int
+rm -f Makefile.defines_int.in
+rm -f acconfig.h
+rm -f irssi/Makefile.defines
+rm -f irssi/Makefile.defines.in
+rm -f irssi/Makefile.defines_int
+rm -f irssi/Makefile.defines_int.in
+rm -f lib/Makefile.am
+rm -f configure.in
 rm -f includes/stamp-*
 rm -f includes/silcconfig.*
+rm -f includes/version_internal.h
+rm -f includes/silcdefs.h.in
 rm -f Makefile.in
-rm -f doc/draft-riikonen*.txt
+rm -f doc/draft-*.txt
+rm -f doc/Makefile.in
+rm -f includes/Makefile.in
 rm -f lib/Makefile.in
+rm -f lib/contrib/Makefile.in
+rm -f lib/silcclient/Makefile.in
 rm -f lib/silccore/Makefile.in
+rm -f lib/silcutil/Makefile.in
 rm -f lib/silccrypt/Makefile.in
 rm -f lib/silcmath/Makefile.in
 rm -f lib/silcsim/Makefile.in
+rm -f lib/silcsim/modules/Makefile.in
 rm -f lib/silcske/Makefile.in
+rm -rf lib/silcmath/gmp/.deps
 rm -f silcd/Makefile.in silcd/log* silcd/*.log
 rm -f silc/Makefile.in silc/log* silc/*.log
 rm -f aclocal.m4
+rm -f config.status
 rm -f configure
+cd irssi
+rm -f COPYING
+rm -f INSTALL
+rm -f Makefile.in
+rm -f */Makefile.in
+rm -f */*/Makefile.in
+rm -f */*/*/Makefile.in
+rm -f */*/*/*/Makefile.in
+rm -f default-theme.h
+rm -f libtool-shared
+rm -f stamp-*
+rm -f docs/startup*.txt
+cd docs/help
+cp Makefile.am.gen ..
+rm -f * 2>/dev/null
+mv ../Makefile.am.gen .
+cd ../..
+rm -f docs/help/in/Makefile.am
 echo "Done."
diff --git a/public_html/about.html b/public_html/about.html
deleted file mode 100644 (file)
index 2d6ee33..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<p><br>
-<a href="index.html"><img src="silc2.jpg" border=0></a>
-<table width="70%" border="0" cellspacing="0" cellpadding="1"
-align=center>
-<tr>
-<td>
-<p>
-<font size=4>
-<h1>About SILC</h1>
-<p>
-SILC (Secure Internet Live Conferencing) is a protocol which provides
-secure conferencing services in the Internet over insecure channel.
-SILC is IRC like softwarre although internally they are very different.
-Biggest similiarity between SILC and IRC is that they both provide
-conferencing services and that SILC has almost same commands as IRC.  Other
-than that they are nothing alike.  Biggest differences are that SILC is 
-secure what IRC is not in any way.  The network model is also entirely
-different compared to IRC.
-
-<p>
-<h1>Contact</h1>
-<p>
-Feedback and comments are welcome.  You can reach me in the following
-Address. 
-<p>
-[Note that generally bug reports should not be sent just yet as the 
-Developer's Version is full of them and the bug hunt has not even started 
-yet.]
-<p>
-Pekka Riikonen<br>
-priikone@poseidon.pspt.fi
-<p>
-
-</td>
-</tr>
-</table>
-</body>
-</html>
-
diff --git a/public_html/features.html b/public_html/features.html
deleted file mode 100644 (file)
index 426901f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<p><br>
-<a href="index.html"><img src="silc2.jpg" border=0></a>
-<table width="70%" border="0" cellspacing="0" cellpadding="1"
-align=center>
-<tr>
-<td>
-<font size=4>
-<p>
-<h1>Features</h1>
-<p>
-
-Features to be included into the final release of SILC.  [Note that the
-current Developer's Version does not include all of these features, read
-TODO file for more information.]
-<p>
-
-<li>Normal conferencing services such as private messages, channels,
-   channel messages, etc.  All traffic is secured and authenticated.
-<p>
-<li>No unique nicknames.  There can same nicknames in SILC without
-   collisions.  SILC has unique Client ID's, Server ID's and Channel ID's
-   to assure that there are no collisions.
-<p>
-<li>Secure key exchange and authentication protocol.  SILC Key Exchange
-   protocol provides key material used in the SILC sessions in secure
-   manner.  The protocol is immune for example to man-in-the-middle 
-   attacks.  The SILC Authentication protocol provides strong 
-   authentication.  Authentication may be based on passphrase or public
-   key (RSA) authentication.  For clients there is an option not to
-   use authentication when connecting to servers.
-<p>
-<li>All traffic is encrypted and authenticated using the best cryptographic
-   algorithms out there.  Command messages, private messages and channel
-   messages are all protected by encryption.  User can set private keys
-   for both private message and for channels so that even SILC servers do
-   not know the keys.  Cipher keys are, by default, 128 bits in length and
-   public keys, by default, 1024 bits in length.
-<p>
-<li>Supports data compression with GZIP to improve performance.
-<p>
-<li>SIM (SILC Module) support.  Support for loading of shared objects at 
-   run-time that provides new and extended features to both SILC client
-   and server.  These can provide extra ciphers and extra features to
-   the software.
-<p>
-<li>SILC client can be installed and used without root privileges.
-<p>
-<li>SILC client can be configured by system wide configuration files but
-   with user specific configuration files as well.
-<p>
-</td>
-</tr>
-</table>
-</body>
-</html>
diff --git a/public_html/history.html b/public_html/history.html
deleted file mode 100644 (file)
index ccfe31b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<p><br>
-<a href="index.html"><img src="silc2.jpg" border=0></a>
-<table width="70%" border="0" cellspacing="0" cellpadding="1"
-align=center>
-<tr>
-<td>
-<p>
-<font size=4>
-<h1>History</h1>
-<p>
-Even though SILC were just released to the public the idea and the protocol
-itself is quite old.  I got the idea about SILC in its current form in
-the year 1996 and first lines of codes were written in early 1997.  This
-release is now third rewrite of the SILC.  The very first version were
-written in 1997 and it included SILC client and very very preliminary
-SILC server.  The server actually weren't usable but the client looked
-pretty much the same as it does now.  At that time the SILC also included
-RSA implementation and 3DES implementation.  The random number generator
-that exists in this current release is actually based on the RNG written
-in 1997.  The RNG written in 1997, on the other hand, were based on
-the SSH's random number generator.  The RNG has been rewritten twice
-since the first version.
-<p>
-I stopped writing the SILC later in 1997 when I got busy at school and
-in work.  The pause lasted several months.  The development resumed in
-1998 when my friend (Juha Räsänen) and I implemented ElGamal algorithm.
-I rewrote some other parts as well.  However, for the same reasons as
-previously the development stopped again.  I resumed the development
-later in 1998 by doing rewrite of the SILC in C++.  This was obviously 
-a mistake but at that time it seemed like a good idea.  Again, in the 
-winter 1999 I got very busy writing my thesis and was forced to stop the 
-development again.  I also, started a new job in the spring.
-<p>
-Later, in 1999, I decided that this time I'm going to make it the right
-way.  C++ was obviously a bad choice so I decided to fall back to plain
-C language.  I also decided to do complete rewrite and started doing
-more thorough planning of what the SILC actually should include.  I also
-decided that this time it is going to kill me before I stop the 
-development.  I started writing SILC in the weekends and actually 
-everytime I had some spare time.  I also started a new job but I didn't
-let that get to my way.  The result of this development effort is the
-release now in public.
-<p>
-I've learned a lot by doing the SILC.  I guess, when I started it I wasn't
-that good of a C programmer.  That alone was a reason why SILC hasn't
-seen the day of light before now.  My programming style has also changed 
-dramatically during these years.  Actually, it has changed couple times 
-since this last rewrite as well.  However, the code style of current SILC 
-release is quite consistent (actually the coding style SILC has been 
-written now I've learned in my current job).
-<p>
-There is probably over 85% of new code in this third rewrite.  Rest has 
-just been copied from the old versions and only minor changes has been
-made (like changed function names and overall coding style).  I've 
-preserved the dates of the old files (dating back to 1997) that has 
-existed in some forms in the old versions.  There is a lot of new code but
-already I see a lot that needs rewriting.  The development continues.
-<p>
-</td>
-</tr>
-</table>
-</body>
-</html>
-
diff --git a/public_html/html/about.php b/public_html/html/about.php
new file mode 100644 (file)
index 0000000..ba6becc
--- /dev/null
@@ -0,0 +1,92 @@
+&nbsp;<br />
+<b><big>About SILC</big></b>
+<br />&nbsp;<br />
+SILC (Secure Internet Live Conferencing) is a protocol which provides
+secure conferencing services on the Internet over insecure channel. SILC 
+superficially resembles IRC, although they are very different internally.
+They both provide conferencing services and have almost the same set of
+commands. Other than that, they are nothing alike. The SILC is secure and 
+the network model is entirely different compared to IRC.
+<br />&nbsp;<br />
+SILC provides security services that any other conferencing protocol does
+not offer today. The most popular conferencing service, IRC, is entirely
+insecure. If you need secure place to talk to some person or to group of
+people over the Internet, IRC or any other conferencing service, for that 
+matter, cannot be used. Anyone can see the messages and their contents in 
+the IRC network. And the most worse case, some is able to change the 
+contents of the messages. Also, all the authentication data, such as, 
+passwords are sent plaintext in IRC.
+<br />&nbsp;<br />
+SILC is much more than just about `encrypting the traffic'. That is easy
+enough to do with IRC and SSL hybrids, but even then the entire network 
+cannot be secured, only part of it. SILC provides security services, such 
+as sending private messages entirely secure; no one can see the message 
+except you and the real receiver of the message. SILC also provides same 
+functionality for channels; no one except those clients joined to the 
+channel may see the messages destined to the channel. Communication 
+between client and server is also secured with session keys and all 
+commands, authentication data (such as passwords etc.) and other traffic 
+is entirely secured. The entire network, and all parts of it, is secured. 
+We are not aware of any other conferencing protocol providing same 
+features at the present time.
+<br />&nbsp;<br />
+SILC has secure key exchange protocol that is used to create the session
+keys for each connection. SILC also provides strong authentication based
+on either passwords or public key authentication. All authentication data
+is always encrypted in the SILC network. Each connection has their own
+session keys, all channels have channel specific keys, and all private
+messages can be secured with private message specific keys.
+<br />&nbsp;<br />
+
+<b>Distribution</b>
+<br />&nbsp;<br />
+The SILC is distributed currently in three different packages. The SILC 
+Client package, the SILC Server package and the SILC Toolkit package. Each 
+package has its intended audience.
+<br />&nbsp;<br />
+- SILC Client package is intended for end users who are looking for a good
+and full featured SILC client. The SILC Client package currently includes 
+Irssi-SILC client that supports all SILC features, themes and much more. 
+It is curses based but has a possibility of adding various other frontends 
+to it. The Irssi-SILC client's user interface is based on the Irssi client 
+(see <a href="http://irssi.org/" class="normal">Irssi project</a>).
+<br />&nbsp;<br />
+- SILC Server package is intended for system administrators who would like 
+to run their own SILC server or SILC router. The package includes the 
+actual server but not the client. If you are running a server and would 
+like to connect it to the silc.silcnet.org router you can contact us.
+<br />&nbsp;<br />
+- SILC Toolkit package is intended for developers and programmers who 
+would like to create their own SILC based applications or help in the 
+development of the SILC protocol. The actual development of the SILC is 
+done in the Toolkit and all the other packages are based on the Toolkit 
+releases. The Toolkit includes SILC Protocol Core library, SILC Crypto 
+library, SILC Key Exchange (SKE) library, SILC Math library, SILC Modules 
+(SIM) library, SILC Utility library, SILC Client library and few other
+libraries. It also includes the Irssi-SILC Client, another client as an 
+example how to program with the Toolkit and the SILC Server.
+<br />&nbsp;<br />
+
+<b>Licensing</b>
+<br />&nbsp;<br />
+SILC is an Open Source (or Free Software) project and it has been released
+under the <a href="?page=copying" class="normal">GNU General Public License</a>. The SILC is free to use and everyone
+is allowed to freely redistribute and change the SILC under the terms of the
+GNU GPL. While there is no guarantee for the product, SILC is made as secure
+as possible. The fact that the software and the protocol is open for public
+analysis is a good thing for end user.
+<br />&nbsp;<br />
+Specification of SILC protocol is available for anyone to look at. There
+exist four Internet Drafts that have been submitted to the <a
+href="http://www.ietf.org/" class="normal">IETF</a>. See <a
+href="?page=docs" class="normal">documentation page</a> for more information.
+<br />&nbsp;<br />
+
+<b>Contact</b>
+<br />&nbsp;<br />
+Feedback and comments are welcome. Bug reports should be sent to the 
+development mailing list.
+<br />&nbsp;<br />
+Development mailing list address: 
+<a href="mailto:silc-devel@lists.sourceforge.net" class="normal">
+silc-devel@lists.sourceforge.net</a>
diff --git a/public_html/html/contact.php b/public_html/html/contact.php
new file mode 100644 (file)
index 0000000..bb2903a
--- /dev/null
@@ -0,0 +1,32 @@
+&nbsp;<br />
+<b><big>Contact Us</big></b>
+<br />&nbsp;<br />
+You can contact us via email using the following email addresses.  If you 
+have questions or you need help with SILC we suggest reading the <a 
+href="?page=faq" class="normal">SILC FAQ</a> first.  Development related 
+questions can also be sent to the <a href="?page=lists" class="normal"> 
+silc-devel</a> mailing list.
+
+<br />&nbsp;<br />
+<b>Contact Us at info@silcnet.org</b>
+<br />&nbsp;<br />
+If you have generic questions or comments about SILC you can contact us at 
+<a href="mailto:info at silcnet.org" class="normal">info@silcnet.org</a>.
+
+<br />&nbsp;<br />
+<b>Server Linking Requests at server-links@silcnet.org</b>
+<br />&nbsp;<br />
+If you are SILC server administrator and would like to get your server 
+linked to the SILC Network, you can send your request for linking at 
+<a href="mailto:server-links at silcnet.org" class="normal"> 
+server-links@silcnet.org</a>.  Be sure to tell us about your server in the 
+email.  Also, it would be nice if you already have existing user base in 
+your server.
+
+<br />&nbsp;<br />
+<b>Contact Webmaster at webmaster@silcnet.org</b>
+<br />&nbsp;<br />
+You can reach our webmaster at <a href="mailto:webmaster at 
+silcnet.org" class="normal">webmaster@silcnet.org</a> address.  If you 
+have a comment about our web site, questions or suggestions to it you can 
+email them to the webmaster.
diff --git a/public_html/html/contribute.php b/public_html/html/contribute.php
new file mode 100644 (file)
index 0000000..d6992df
--- /dev/null
@@ -0,0 +1,78 @@
+&nbsp;<br />
+<b><big>Contributing</big></b>
+<br />&nbsp;<br />
+Developers are needed in SILC project. Everyone who has the time and
+ability is welcome to join the project.  We need C coders and technical
+writers (to write documentation). Feel free to start narrowing down the <a href="?page=todo" class="normal">TODO</a> list.
+<br />&nbsp;<br />
+Interested people are also welcome to give new ideas to the SILC protocol
+that is still in its draft phase. You should probably go and read the SILC
+protocol specification Internet Drafts to get the idea about what SILC
+actually is. The current software version might not give the whole picture
+of the SILC. The Internet Drafts are available in
+<a href="?page=docs" class="normal">documentation page.</a>
+<br />&nbsp;<br />
+Who wants to send code to the project should read the <a 
+href="docs/CodingStyle" class="normal">CodingStyle</a>
+documentation. New code must comply with the coding style conventions
+described in that document.
+<br />&nbsp;<br />
+There is anonymous CVS acccess for those who want to participate the
+development process. See the <a href="?page=cvs" class="normal">CVS page.</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Submitting Patches to Mailing List</b>
+<br />&nbsp;<br />
+Please follow these instructions when you are submitting a patch to the 
+silc-devel mailing list.
+
+<br />&nbsp;<br />
+ <table cellspacing="2" cellpadding="0" border="0">
+<tr><td valign="top"> - </td><td>
+Use the unified output format for the diff (diff -u)
+</td></tr>
+<tr><td valign="top"> - </td><td>
+Use diff, not cvs diff
+</td></tr>
+<tr><td valign="top"> - </td><td>
+Send the patch to the mailing list, and add those people that should know
+about it on CC:
+</td></tr>
+<tr><td valign="top"> - </td><td>
+Submit ready patches. If they are not ready then sending them to
+people who are involved in the development is preferred. The patch might 
+face several round trips so sending patches which are ready is preffered
+</td></tr>
+<tr><td valign="top"> - </td><td>
+Include the patch in the body of the email or attach it
+</td></tr>
+<tr><td valign="top"> - </td><td>
+Send separate patches for every bugfix or feature
+</td></tr>
+<tr><td valign="top"> - </td><td>
+If you think that your work is benefical to the development of the
+SILC and would like to be listed in the CREDITS file in SILC packages, you
+can submit patches for the CREDITS file too. Nobody will be added there
+without a patch, this way those who don't want to see their name there 
+won't get there
+</td></tr>
+<tr><td valign="top"> - </td><td>
+Do not submit bugs ;)
+</td></tr>
+</table>
+
+<br />&nbsp;<br />
+<b><big>Support</big></b>
+<br />&nbsp;<br />
+If you want to have a SILC Project's banner on your web site and support us
+this way, you can use the following banner or find some more at:  
+<a href="http://silcnet.org/salo/img/silc-banners.html" class="normal">
+http://silcnet.org/salo/img/silc-banners.html</a>.
+<br />&nbsp;<br />
+<table width="100%">
+ <tr>
+  <td width="100%" align="center">
+   <img src="/img/silc-banner.gif" alt="SILC Secure Internet Live Conferencing" width="468" height="60">
+  </td>
+ </tr>
+</table>
similarity index 91%
rename from public_html/copying.html
rename to public_html/html/copying.php
index 1fc18fedc855c2557b8dfe87672e231da33f6aaa..46c942f5564836ade0f696f3ea54746d426ced6a 100644 (file)
@@ -1,26 +1,15 @@
-<html>
-<body bgcolor="#ffffff">
-<p><br>
-<a href="index.html"><img src="silc2.jpg" border=0></a>
-<table width="70%" border="0" cellspacing="0" cellpadding="1" align=center>
-<tr>
-<td>
-
-<br><br>
-<h1>GNU GENERAL PUBLIC LICENSE</h1>
-<h3>Version 2, June 1991</h3>
-<PRE>
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
+&nbsp;<br />
+<b><big>GNU GENERAL PUBLIC LICENSE<br />
+Version 2, June 1991</big></b>
+<br />&nbsp;<br />
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.<br />
 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
-
-Everyone is permitted to copy and distribute verbatim copies
+<br />&nbsp;<br />
+Everyone is permitted to copy and distribute verbatim copies<br />
 of this license document, but changing it is not allowed.
-</PRE>
-
-
-<H4>Preamble</H4>
-
-<P>
+<br />&nbsp;<br />
+<b>Preamble</b>
+<br />&nbsp;<br />
   The licenses for most software are designed to take away your
 freedom to share and change it.  By contrast, the GNU General Public
 License is intended to guarantee your freedom to share and change free
@@ -30,68 +19,48 @@ Foundation's software and to any other program whose authors commit to
 using it.  (Some other Free Software Foundation software is covered by
 the GNU Library General Public License instead.)  You can apply it to
 your programs, too.
-
-</P>
-<P>
+<br />&nbsp;<br />
   When we speak of free software, we are referring to freedom, not
 price.  Our General Public Licenses are designed to make sure that you
 have the freedom to distribute copies of free software (and charge for
 this service if you wish), that you receive source code or can get it
 if you want it, that you can change the software or use pieces of it
 in new free programs; and that you know you can do these things.
-
-</P>
-<P>
+<br />&nbsp;<br />
   To protect your rights, we need to make restrictions that forbid
 anyone to deny you these rights or to ask you to surrender the rights.
 These restrictions translate to certain responsibilities for you if you
 distribute copies of the software, or if you modify it.
-
-</P>
-<P>
+<br />&nbsp;<br />
   For example, if you distribute copies of such a program, whether
 gratis or for a fee, you must give the recipients all the rights that
 you have.  You must make sure that they, too, receive or can get the
 source code.  And you must show them these terms so they know their
 rights.
-
-</P>
-<P>
+<br />&nbsp;<br />
   We protect your rights with two steps: (1) copyright the software, and
 (2) offer you this license which gives you legal permission to copy,
 distribute and/or modify the software.
-
-</P>
-<P>
+<br />&nbsp;<br />
   Also, for each author's protection and ours, we want to make certain
 that everyone understands that there is no warranty for this free
 software.  If the software is modified by someone else and passed on, we
 want its recipients to know that what they have is not the original, so
 that any problems introduced by others will not reflect on the original
 authors' reputations.
-
-</P>
-<P>
+<br />&nbsp;<br />
   Finally, any free program is threatened constantly by software
 patents.  We wish to avoid the danger that redistributors of a free
 program will individually obtain patent licenses, in effect making the
 program proprietary.  To prevent this, we have made it clear that any
 patent must be licensed for everyone's free use or not licensed at all.
-
-</P>
-<P>
+<br />&nbsp;<br />
   The precise terms and conditions for copying, distribution and
 modification follow.
-
-</P>
-
-
-<H4>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</H4>
-
-
-<P>
-
-<STRONG>0.</STRONG>
+<br />&nbsp;<br />
+<b>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</b>
+<br />&nbsp;<br />
+<b>0.</b>
  This License applies to any program or other work which contains
 a notice placed by the copyright holder saying it may be distributed
 under the terms of this General Public License.  The "Program", below,
@@ -101,18 +70,15 @@ that is to say, a work containing the Program or a portion of it,
 either verbatim or with modifications and/or translated into another
 language.  (Hereinafter, translation is included without limitation in
 the term "modification".)  Each licensee is addressed as "you".
-<P>
-
+<br />&nbsp;<br />
 Activities other than copying, distribution and modification are not
 covered by this License; they are outside its scope.  The act of
 running the Program is not restricted, and the output from the Program
 is covered only if its contents constitute a work based on the
 Program (independent of having been made by running the Program).
 Whether that is true depends on what the Program does.
-
-<P>
-
-<STRONG>1.</STRONG>
+<br />&nbsp;<br />
+<b>1.</b>
  You may copy and distribute verbatim copies of the Program's
 source code as you receive it, in any medium, provided that you
 conspicuously and appropriately publish on each copy an appropriate
@@ -120,34 +86,27 @@ copyright notice and disclaimer of warranty; keep intact all the
 notices that refer to this License and to the absence of any warranty;
 and give any other recipients of the Program a copy of this License
 along with the Program.
-<P>
-
+<br />&nbsp;<br />
 You may charge a fee for the physical act of transferring a copy, and
 you may at your option offer warranty protection in exchange for a fee.
-<P>
-
-<STRONG>2.</STRONG>
+<br />&nbsp;<br />
+<b>2.</b>
  You may modify your copy or copies of the Program or any portion
 of it, thus forming a work based on the Program, and copy and
 distribute such modifications or work under the terms of Section 1
 above, provided that you also meet all of these conditions:
-<P>
-
-<UL>
-
-<LI><STRONG>a)</STRONG>
+<br />&nbsp;<br />
+<b>a)</b>
      You must cause the modified files to carry prominent notices
      stating that you changed the files and the date of any change.
-
-<P>
-<LI><STRONG>b)</STRONG>
+<br />&nbsp;<br />
+<b>b)</b>
      You must cause any work that you distribute or publish, that in
      whole or in part contains or is derived from the Program or any
      part thereof, to be licensed as a whole at no charge to all third
      parties under the terms of this License.
-
-<P>
-<LI><STRONG>c)</STRONG>
+<br />&nbsp;<br />
+<b>c)</b>
      If the modified program normally reads commands interactively
      when run, you must cause it, when started running for such
      interactive use in the most ordinary way, to print or display an
@@ -158,8 +117,7 @@ above, provided that you also meet all of these conditions:
      License.  (Exception: if the Program itself is interactive but
      does not normally print such an announcement, your work based on
      the Program is not required to print an announcement.)
-</UL>
-
+<br />&nbsp;<br />
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -169,54 +127,42 @@ distribute the same sections as part of a whole which is a work based
 on the Program, the distribution of the whole must be on the terms of
 this License, whose permissions for other licensees extend to the
 entire whole, and thus to each and every part regardless of who wrote it.
-<P>
-
+<br />&nbsp;<br />
 Thus, it is not the intent of this section to claim rights or contest
 your rights to work written entirely by you; rather, the intent is to
 exercise the right to control the distribution of derivative or
 collective works based on the Program.
-<P>
-
+<br />&nbsp;<br />
 In addition, mere aggregation of another work not based on the Program
 with the Program (or with a work based on the Program) on a volume of
 a storage or distribution medium does not bring the other work under
 the scope of this License.
-
-<P>
-
-<STRONG>3.</STRONG>
+<br />&nbsp;<br />
+<b>3.</b>
  You may copy and distribute the Program (or a work based on it,
 under Section 2) in object code or executable form under the terms of
 Sections 1 and 2 above provided that you also do one of the following:
-
-
-<!-- we use this doubled UL to get the sub-sections indented, -->
-<!-- while making the bullets as unobvious as possible. -->
-<UL>
-
-<LI><STRONG>a)</STRONG>
+<br />&nbsp;<br />
+<b>a)</b>
      Accompany it with the complete corresponding machine-readable
      source code, which must be distributed under the terms of Sections
      1 and 2 above on a medium customarily used for software interchange; or,
-
-<P>
-<LI><STRONG>b)</STRONG>
+<br />&nbsp;<br />
+<b>b)</b>
      Accompany it with a written offer, valid for at least three
      years, to give any third party, for a charge no more than your
      cost of physically performing source distribution, a complete
      machine-readable copy of the corresponding source code, to be
      distributed under the terms of Sections 1 and 2 above on a medium
      customarily used for software interchange; or,
-
-<P>
-<LI><STRONG>c)</STRONG>
+<br />&nbsp;<br />
+<b>c)</b>
      Accompany it with the information you received as to the offer
      to distribute corresponding source code.  (This alternative is
      allowed only for noncommercial distribution and only if you
      received the program in object code or executable form with such
      an offer, in accord with Subsection b above.)
-</UL>
-
+<br />&nbsp;<br />
 The source code for a work means the preferred form of the work for
 making modifications to it.  For an executable work, complete source
 code means all the source code for all modules it contains, plus any
@@ -227,16 +173,14 @@ anything that is normally distributed (in either source or binary
 form) with the major components (compiler, kernel, and so on) of the
 operating system on which the executable runs, unless that component
 itself accompanies the executable.
-<P>
-
+<br />&nbsp;<br />
 If distribution of executable or object code is made by offering
 access to copy from a designated place, then offering equivalent
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-<P>
-
-<STRONG>4.</STRONG>
+<br />&nbsp;<br />
+<b>4.</b>
  You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -244,10 +188,8 @@ void, and will automatically terminate your rights under this License.
 However, parties who have received copies, or rights, from you under
 this License will not have their licenses terminated so long as such
 parties remain in full compliance.
-
-<P>
-
-<STRONG>5.</STRONG>
+<br />&nbsp;<br />
+<b>5.</b>
  You are not required to accept this License, since you have not
 signed it.  However, nothing else grants you permission to modify or
 distribute the Program or its derivative works.  These actions are
@@ -256,10 +198,8 @@ modifying or distributing the Program (or any work based on the
 Program), you indicate your acceptance of this License to do so, and
 all its terms and conditions for copying, distributing or modifying
 the Program or works based on it.
-
-<P>
-
-<STRONG>6.</STRONG>
+<br />&nbsp;<br />
+<b>6.</b>
  Each time you redistribute the Program (or any work based on the
 Program), the recipient automatically receives a license from the
 original licensor to copy, distribute or modify the Program subject to
@@ -267,10 +207,8 @@ these terms and conditions.  You may not impose any further
 restrictions on the recipients' exercise of the rights granted herein.
 You are not responsible for enforcing compliance by third parties to
 this License.
-
-<P>
-
-<STRONG>7.</STRONG>
+<br />&nbsp;<br />
+<b>7.</b>
  If, as a consequence of a court judgment or allegation of patent
 infringement or for any other reason (not limited to patent issues),
 conditions are imposed on you (whether by court order, agreement or
@@ -283,14 +221,12 @@ license would not permit royalty-free redistribution of the Program by
 all those who receive copies directly or indirectly through you, then
 the only way you could satisfy both it and this License would be to
 refrain entirely from distribution of the Program.
-<P>
-
+<br />&nbsp;<br />
 If any portion of this section is held invalid or unenforceable under
 any particular circumstance, the balance of the section is intended to
 apply and the section as a whole is intended to apply in other
 circumstances.
-<P>
-
+<br />&nbsp;<br />
 It is not the purpose of this section to induce you to infringe any
 patents or other property right claims or to contest validity of any
 such claims; this section has the sole purpose of protecting the
@@ -301,14 +237,11 @@ through that system in reliance on consistent application of that
 system; it is up to the author/donor to decide if he or she is willing
 to distribute software through any other system and a licensee cannot
 impose that choice.
-<P>
-
+<br />&nbsp;<br />
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-
-<P>
-
-<STRONG>8.</STRONG>
+<br />&nbsp;<br />
+<b>8.</b>
  If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -316,16 +249,13 @@ may add an explicit geographical distribution limitation excluding
 those countries, so that distribution is permitted only in or among
 countries not thus excluded.  In such case, this License incorporates
 the limitation as if written in the body of this License.
-
-<P>
-
-<STRONG>9.</STRONG>
+<br />&nbsp;<br />
+<b>9.</b>
  The Free Software Foundation may publish revised and/or new versions
 of the General Public License from time to time.  Such new versions will
 be similar in spirit to the present version, but may differ in detail to
 address new problems or concerns.
-<P>
-
+<br />&nbsp;<br />
 Each version is given a distinguishing version number.  If the Program
 specifies a version number of this License which applies to it and "any
 later version", you have the option of following the terms and conditions
@@ -333,11 +263,8 @@ either of that version or of any later version published by the Free
 Software Foundation.  If the Program does not specify a version number of
 this License, you may choose any version ever published by the Free Software
 Foundation.
-
-<P>
-
-
-<STRONG>10.</STRONG>
+<br />&nbsp;<br />
+<b>10.</b>
  If you wish to incorporate parts of the Program into other free
 programs whose distribution conditions are different, write to the author
 to ask for permission.  For software which is copyrighted by the Free
@@ -345,14 +272,10 @@ Software Foundation, write to the Free Software Foundation; we sometimes
 make exceptions for this.  Our decision will be guided by the two goals
 of preserving the free status of all derivatives of our free software and
 of promoting the sharing and reuse of software generally.
-
-
-
-<P><STRONG>NO WARRANTY</STRONG></P>
-
-<P>
-
-<STRONG>11.</STRONG>
+<br />&nbsp;<br />
+<b>NO WARRANTY</b>
+<br />&nbsp;<br />
+<b>11.</b>
  BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
@@ -362,10 +285,8 @@ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 REPAIR OR CORRECTION.
-
-<P>
-
-<STRONG>12.</STRONG>
+<br />&nbsp;<br />
+<b>12.</b>
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
@@ -375,12 +296,5 @@ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
-
-<P>
-
-
-<H3>END OF TERMS AND CONDITIONS</H3>
-
-</table>
-</body>
-</html>
+<br />&nbsp;<br />
+<b>END OF TERMS AND CONDITIONS</b>
diff --git a/public_html/html/counter.php b/public_html/html/counter.php
new file mode 100644 (file)
index 0000000..e85d400
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+function Hits() {
+
+// $datafile has to be writable by http server user uid
+// else hits count will not be increased, only printed out
+
+  $datafile = "COUNTER";
+
+  if (Is_Writable($datafile)) {
+    $fp = FOpen($datafile, "r+");
+    $writable = "true";
+  }
+  else
+    if (Is_Readable($datafile)) $fp = FOpen($datafile,"r");
+    else return;
+  
+  $hits = FGets($fp,255) + 1;
+  if(!$hits) $hits = 1;
+
+  if($writable) {
+    Rewind($fp);
+    FPuts($fp, $hits);
+  }
+
+  FClose($fp);
+  echo $hits." hits";
+}
+
+Hits();
+
+?>
diff --git a/public_html/html/cvs.php b/public_html/html/cvs.php
new file mode 100644 (file)
index 0000000..80a7eb6
--- /dev/null
@@ -0,0 +1,197 @@
+&nbsp;<br />
+<b><big>Anonymous CVS access</big></b>
+<br />&nbsp;<br />
+Anonymous CVS access is now available to SILC CVS repository. The
+repository includes everything related to SILC project; source codes,
+documentation and even these web pages. The CVS access is of course public
+but it is intended for developers. After you have checked out the SILC
+source tree you should read README.CVS file from the source tree or rest
+of this web page.
+<br />&nbsp;<br />
+Also note that this is the closest to real time development you can get
+thus you cannot expect that the source tree would work or even compile.
+While it is our intention that the trunk would always at least compile
+there might be situations when it will not.
+
+<br />&nbsp;<br />
+
+<b>Browsing the Source Tree</b>
+<br />&nbsp;<br />
+If you want to browse the source tree using web browser before checking
+out the tree with CVS use following link:
+<br />&nbsp;<br />
+<a href="http://cvs.silcnet.org/" class="normal">Web Access to CVS repository
+</a>
+<br />&nbsp;<br />
+Note that this is not real-time access to the CVS repository. It is
+updated once a day. If you want real-time access then checkout the CVS
+repository.
+
+<br />&nbsp;<br />
+
+<b>Howto Checkout The Source Tree</b>
+<br />&nbsp;<br />
+The repository can be checked out by using anonymous pserver with CVS.
+<br />&nbsp;<br />
+For those who are using sh/ksh/bash/zsh the check out is done as follows:
+<br />&nbsp;<br />
+<tt class="highlight">
+export CVSROOT=:pserver:<?php printf("%s@%s:%s", $CVS_User, $CVS_Site, $CVS_Root); ?>
+<br />&nbsp;<br />
+cvs login<br />
+cvs co silc<br />
+cvs logout<br />
+</tt>
+
+<br />&nbsp;<br />
+For those who are using csh/tcsh the check out is done as follows:
+<br />&nbsp;<br />
+<tt class="highlight">
+setenv CVSROOT :pserver:<?php printf("%s@%s:%s", $CVS_User, $CVS_Site, $CVS_Root); ?>
+<br />&nbsp;<br />
+cvs login<br />
+cvs co silc<br />
+cvs logout<br />
+</tt>
+<br />&nbsp;<br />
+If you don't want to set $CVSROOT environment variable you can set the
+path to the cvs as command line option:
+<br />&nbsp;<br />
+<tt class="highlight">
+cvs -d:pserver:<?php printf("%s@%s:%s", $CVS_User, $CVS_Site, $CVS_Root); ?> login
+<br />
+cvs -d:pserver:<?php printf("%s@%s:%s", $CVS_User, $CVS_Site, $CVS_Root); ?> co silc
+<br />
+cvs -d:pserver:<?php printf("%s@%s:%s", $CVS_User, $CVS_Site, $CVS_Root); ?> logout
+</tt>
+<br />&nbsp;<br />
+Whatever method you will decide to use, after you have done cvs login you will
+be prompted for password:
+<br />&nbsp;<br />
+<b>CVS password: </b>silc
+<br />&nbsp;<br />
+Type the password "silc" and press &lt;ENTER&gt;
+<br />&nbsp;<br />
+The actual SILC source tree is checked out using the cvs co silc command,
+described above. This command will fetch the source tree and save it into
+directory named silc. SILC CVS repository currently does not have any
+branches thus this will check out the trunk. The size of the trunk is
+currently about 13 MB but will grow in the future.
+
+<br />&nbsp;<br />
+
+<b>What SILC Source Tree Includes</b>
+<br />&nbsp;<br />
+SILC Source tree includes a lot more stuff that appears in public
+distribution. The source tree includes, for example, internal scripts,
+configuration files, SILC webpages etc. These never appear on a public
+distribution.
+<br />&nbsp;<br />
+Following directories currently exist in SILC source tree.
+<br />&nbsp;<br />
+<tt class="highlight">
+doc/
+<br />&nbsp;<br />
+&nbsp; Includes all the SILC documentation. Few parts of the documentation<br />
+&nbsp; are generated when distribution is generated. The automatically<br />
+&nbsp; generated files should never be commited to CVS.<br />
+<br />&nbsp;<br />
+includes/
+<br />&nbsp;<br />
+&nbsp; Includes SILC include files.
+<br />&nbsp;<br />
+lib/
+<br />&nbsp;<br />
+&nbsp; Includes SILC libraries. There are maybe libraries in the CVS which<br />
+&nbsp; are not inclduded in public distribution.<br />
+<br />&nbsp;<br />
+public_html/
+<br />&nbsp;<br />
+&nbsp; Includes the official SILC web pages and everything related to them.<br />
+&nbsp; This directory will never appear in public distribution.<br />
+<br />&nbsp;<br />
+silc/
+<br />&nbsp;<br />
+&nbsp; Includes SILC client. There can be some extra files that will<br />
+&nbsp; never appear in public distribution, such as configuration files.<br />
+<br />&nbsp;<br />
+silcd/
+<br />&nbsp;<br />
+&nbsp; Includes SILC server. There can be some extra files that will<br />
+&nbsp; never appear in public distribution, such as configuration files.<br />
+</tt>
+
+<br />&nbsp;<br />
+
+<b>Howto Compile SILC Source Tree</b>
+<br />&nbsp;<br />
+After checkout from CVS the SILC source tree needs to be prepared for
+configuration and compilation. To compile the source tree, type:
+<br />&nbsp;<br />
+<tt class="highlight">
+./prepare<br />
+./configure --enable-debug<br />
+make<br />&nbsp;<br />
+note: on non-GNU/Linux operating systems GNU make (gmake) is prefered
+</tt>
+<br />&nbsp;<br />
+
+The ./prepare script is included in the source tree and it will never
+appears in public distribution. The script prepares the source tree
+by creating configuration scripts and Makefiles. The prepare must be
+run every time you made any changes to configuration scripts (however,
+making changes to Makefile.am's does not require running ./prepare).
+<br />&nbsp;<br />
+As a developer you should read the ./configure script's help by typing
+./configure --help and study all of its different options. Also you
+should configure the script with --enable-debug option as it compiles
+SILC with -g (debugging) option and it enables the SILC_LOG_DEBUG*
+scripts. Warning is due here:  The debugging produced by both cilent
+and server is very huge, thus it is common to test the programs as
+follows:
+<br />&nbsp;<br />
+<tt class="highlight">
+./silc -d "*" -f configfile 2&gt;log<br />
+./silcd -d "*" -f configfile 2&gt;log
+</tt>
+
+<br />&nbsp;<br />
+The -d option enables the debug printing.  The argument for the -d option
+is a string that is used to match the output debug.  The example "*" will
+match for everything, and all debugs will be printed.  If you want to
+limit the debugs you want to printout you can give for example a string
+like "*server*,*rng*" to match all functions, and filenames that has
+"server" or "rng" string in them.  Others will not be printed out.  You 
+can freely define regural expressions as debug string.
+
+<br />&nbsp;<br />
+
+<b>How to clean SILC Source Tree</b>
+<br />&nbsp;<br />
+To entirely clear the source tree to the state after it was checked out
+from CVS, type:
+<br />&nbsp;<br />
+<tt class="highlight">
+./prepare-clean
+</tt>
+<br />&nbsp;<br />
+
+This calls `make distclean' plus removes automatically generated files
+by hand. It also removes *.log files. However, it will not remove any
+other files you might have created.
+
+<br />&nbsp;<br />
+
+<b>Makefiles and configuration files</b>
+<br />&nbsp;<br />
+Developers should never directly write a Makefile. All Makefiles are
+always automatically generated by ./prepare and later by ./configure
+scripts. Instead, developers have to write Makefile.am files. There
+are plenty of examples what they should look like. If you changed
+Makefile.am during development you do not need to run ./prepare, just
+run normal make.
+<br />&nbsp;<br />
+Configuration files are the files that ./prepare automatically generates
+and which will be included into public distribution. ./prepare creates
+for example the ./configure script that is not commited to the CVS.
+`configure.in' is the file that developers have to edit to change ./configure
diff --git a/public_html/html/docs.php b/public_html/html/docs.php
new file mode 100644 (file)
index 0000000..0344e44
--- /dev/null
@@ -0,0 +1,139 @@
+&nbsp;<br />
+<b><big>SILC Documentation</big></b>
+<br />&nbsp;<br />
+
+README file from packages: <a href="docs/README" class="normal">README</a>
+<br />&nbsp;<br />
+Software manual: <i>Coming later</i>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b>Installation Instructions</b>
+<br />&nbsp;<br />
+General installation instructions are available in all SILC distributions 
+in the INSTALL file.
+<br />&nbsp;<br />
+<a href="?page=install" class="normal">Installation instructions</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b><big>Technical Documentation</big></b>
+<br />&nbsp;<br />
+
+<b>SILC Toolkit Reference Manual</b>
+<br />&nbsp;<br />
+SILC Toolkit Reference Manual includes documentation for the SILC Toolkit 
+package.  It includes interface references to all interfaces found in 
+various SILC libraries.  The reference manual is automatically generated 
+from the source code.  Note that this version is preliminary and does not 
+include references to all interfaces.
+<br />&nbsp;<br />
+<a href="docs/toolkit/" class="normal">HTML version</a>, 
+<a href="docs/toolkit.html.tar.gz" class="normal">html.tar.gz</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b>Coding Conventions</b>
+<br />&nbsp;<br />
+If you would like to submit code to the SILC Project we would like you to 
+first check out these coding conventions.  They are here for the benefit 
+of all who read the code and is involved in the development of the SILC.
+<br />&nbsp;<br />
+<a href="docs/CodingStyle" class="normal">CodingStyle</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b><big>SILC Protocol Documentation</big></b>
+<br />&nbsp;<br />
+
+<b>SILC Protocol White Paper</b>
+<br />&nbsp;<br />
+SILC Protocol White Paper gives short but deep enough introduction to the 
+SILC Protocol. Note that this is for those who would like to know how the 
+protocol works. For more detailed description of the protocol we suggest 
+reading the protocol specifications.
+<br />&nbsp;<br />
+
+<a href="?page=whitepaper" class="normal">HTML version</a>,
+<a href="docs/silc_protocol.pdf.gz" class="normal">gzipped PDF</a>,
+<a href="docs/silc_protocol.ps.gz" class="normal">gzipped PostScript</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b>SILC Protocol Internet Drafts</b>
+<br />&nbsp;<br />
+SILC Protocol is documented and four Internet Drafts exist. These 
+Internet Drafts are also available from the
+<a href="http://www.ietf.org/" class="normal">IETF</a>.
+<br />&nbsp;<br />
+
+<b>Secure Internet Live Conferencing (SILC), Protocol Specification</b>
+<br />&nbsp;<br />
+Abstract
+<br />&nbsp;<br />
+   This memo describes a Secure Internet Live Conferencing (SILC)
+   protocol which provides secure conferencing services over insecure
+   network channel. SILC is IRC [IRC] like protocol, however, it is
+   not equivalent to IRC and does not support IRC. Strong cryptographic
+   methods are used to protect SILC packets inside the SILC network.
+   Three other Internet Drafts relates very closely to this memo;
+   SILC Packet Protocol [SILC2], SILC Key Exchange and Authentication
+   Protocols [SILC3] and SILC Commands [SILC4].
+<br />&nbsp;<br />
+<a href="docs/draft-riikonen-silc-spec-04.txt" class="normal">
+draft-riikonen-silc-spec-04.txt</a>
+<br />&nbsp;<br />&nbsp;<br />
+
+<b>SILC Packet Protocol</b>
+<br />&nbsp;<br />
+Abstract
+<br />&nbsp;<br />
+   This memo describes a Packet Protocol used in the Secure Internet Live
+   Conferencing (SILC) protocol, specified in the Secure Internet Live
+   Conferencing, Protocol Specification Internet Draft [SILC1].  This
+   protocol describes the packet types and packet payloads which defines
+   the contents of the packets. It provides secure binary packet protocol
+   that assures that the content of the packets is secured and authenticated.
+<br />&nbsp;<br />
+<a href="docs/draft-riikonen-silc-pp-04.txt" class="normal">
+draft-riikonen-silc-pp-04.txt</a>
+<br />&nbsp;<br />&nbsp;<br />
+
+<b>SILC Key Exchange and Authentication Protocols</b>
+<br />&nbsp;<br />
+Abstract
+<br />&nbsp;<br />
+   This memo describes two protocols used in the Secure Internet Live  
+   Conferencing (SILC) protocol, specified in the Secure Internet Live 
+   Conferencing, Protocol Specification internet-draft [SILC1].  The   
+   SILC Key Exchange (SKE) protocol provides secure key exchange between
+   two parties resulting into shared secret key material. The protocol
+   is based on Diffie-Hellman key exchange algorithm and its functionality
+   is derived from several key exchange protocols. SKE uses best parts
+   of the SSH2 Key Exchange protocol, Station-To-Station (STS) protocol 
+   and the OAKLEY Key Determination protocol [OAKLEY].
+<br />&nbsp;<br />
+   The SILC Connection Authentication protocol provides user level
+   authentication used when creating connections in SILC network. The 
+   protocol is transparent to the authentication data which means that it
+   can be used to authenticate the user with, for example, passphrase  
+   (pre-shared-secret) or public key (and certificate).
+<br />&nbsp;<br />
+<a href="docs/draft-riikonen-silc-ke-auth-04.txt" class="normal">
+draft-riikonen-silc-ke-auth-04.txt</a>
+<br />&nbsp;<br />&nbsp;<br />
+
+<b>SILC Commands</b>
+<br />&nbsp;<br />
+Abstract
+<br />&nbsp;<br />
+   This memo describes the commands used in the Secure Internet Live
+   Conferencing (SILC) protocol, specified in the Secure Internet Live
+   Conferencing, Protocol Specification Internet Draft [SILC1].  The
+   SILC Commands are very important part of the SILC protocol.  Usually
+   the commands are used by SILC clients to manage the SILC session, but
+   also SILC servers may use the commands.  This memo specifies detailed
+   command messages and command reply messages.
+<br />&nbsp;<br />
+<a href="docs/draft-riikonen-silc-commands-02.txt" class="normal">
+draft-riikonen-silc-commands-02.txt</a>
diff --git a/public_html/html/download.php b/public_html/html/download.php
new file mode 100644 (file)
index 0000000..4d0485d
--- /dev/null
@@ -0,0 +1,349 @@
+&nbsp;<br />
+<b><big>Download SILC</big></b>
+<br />&nbsp;<br />
+The SILC is distributed in three different packages; the SILC Client, the 
+SILC Server and the SILC Toolkit. The SILC Client is intended for end 
+users, the SILC Server for system administrators and the SILC Toolkit for 
+developers.
+<br />&nbsp;<br />
+Use a <a href="?page=mirrors" class="normal">mirror</a> near you for downloads.
+<br />&nbsp;<br /><br />
+
+
+<table width="600" cellspacing="2" cellpadding="2" border="0">
+ <tr>
+  <td colspan="7" class="text">
+
+<b>SILC Client</b>
+<br />&nbsp;<br />
+<b>The latest version of the SILC client is <?php echo $Latest_Client; ?></b>
+<br />&nbsp;<br />
+The SILC Client package is inteded for end users who need only the SILC 
+client. The package includes the new Irssi-SILC client.
+<br />&nbsp;<br />
+
+  </td>
+ </tr>
+ <tr>
+  <td class="title340" colspan="2" align="center"><b>Package</b></td>
+  <td class="title50" align="center"><b>Version</b></td>
+  <td class="title60" align="center"><b>Size</b></td>
+  <td class="title100" colspan="2" align="center"><b>URL</b></td>
+  <td class="title50" align="center"><b>SUM</b></td>
+ </tr>
+ <tr>
+  <td class="sources60">&nbsp;Sources</td>
+  <td class="sources280">&nbsp;tar.gz</td>
+  <td class="sources50" align="center"><?php echo $Latest_Client; ?></td>
+  <td class="sources60" align="right"><?php echo div(FileSize("download/client/sources/silc-client-".$Latest_Client.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources50" align="center"><a href="download/client/sources/silc-client-<?php echo $Latest_Client; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources50" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/sources/silc-client-<?php echo $Latest_Client; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources50" align="center"><a href="download/client/sources/silc-client-<?php echo $Latest_Client; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;tar.bz2</td>
+  <td class="sources" align="center"><?php echo $Latest_Client; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/client/sources/silc-client-".$Latest_Client.".tar.bz2"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/client/sources/silc-client-<?php echo $Latest_Client; ?>.tar.bz2" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/sources/silc-client-<?php echo $Latest_Client; ?>.tar.bz2" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/client/sources/silc-client-<?php echo $Latest_Client; ?>.tar.bz2.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;SRPM</td>
+  <td class="sources" align="center"><?php echo $Latest_RPM_Client_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/client/rpm/SRPMS/silc-client-".$Latest_RPM_Client.".src.rpm"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/client/rpm/SRPMS/silc-client-<?php echo $Latest_RPM_Client_src; ?>.src.rpm" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/rpm/SRPMS/silc-client-<?php echo $Latest_RPM_Client_src; ?>.src.rpm" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/client/rpm/SRPMS/silc-client-<?php echo $Latest_RPM_Client_src; ?>.src.rpm.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;deb-src (<a href="txt/debian.release.txt" class="normal">release notes</a>)</td>
+  <td class="sources" align="center"><?php echo $Latest_DEB_Client_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSizeDEB("client",$Latest_DEB_Client_Base,$Latest_DEB_Client_src),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/client/deb/<?php echo $Latest_DEB_Client_Base; ?>/" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/deb/<?php echo $Latest_DEB_Client_Base; ?>/" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/client/deb/<?php echo $Latest_DEB_Client_Base."/silc-client_".$Latest_DEB_Client_src; ?>_i386.changes" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;NetBSD pkgsrc</td>
+  <td class="sources" align="center"><?php echo $Latest_NetBSD_Client_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/client/netbsd/pkgsrc/silc-client-".$Latest_NetBSD_Client_src.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/client/netbsd/pkgsrc/silc-client-<?php echo $Latest_NetBSD_Client_src; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/netbsd/pkgsrc/silc-client-<?php echo $Latest_NetBSD_Client_src; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/client/netbsd/pkgsrc/silc-client-<?php echo $Latest_NetBSD_Client_src; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;OpenBSD ports</td>
+  <td class="sources" align="center"><?php echo $Latest_OpenBSD_Client_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/client/openbsd/ports/silc-client-".$Latest_OpenBSD_Client_src.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/client/openbsd/ports/silc-client-<?php echo $Latest_OpenBSD_Client_src; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/openbsd/ports/silc-client-<?php echo $Latest_OpenBSD_Client_src; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/client/openbsd/ports/silc-client-<?php echo $Latest_OpenBSD_Client_src; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;RPM GNU/Linux i386 (<a href="txt/rpm-release-notes.html" class="normal">release notes</a>)</td>
+  <td class="binaries" align="center"><?php echo $Latest_RPM_Client; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/client/rpm/i386/silc-client-".$Latest_RPM_Client.".i386.rpm"),1024); ?> kB&nbsp;</td>
+  <td class="binaries"  align="center"><a href="download/client/rpm/i386/silc-client-<?php echo $Latest_RPM_Client; ?>.i386.rpm" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/rpm/i386/silc-client-<?php echo $Latest_RPM_Client; ?>.i386.rpm" class="normal">FTP</a></td>
+  <td class="binaries"  align="center"><a href="download/client/rpm/i386/silc-client-<?php echo $Latest_RPM_Client; ?>.i386.rpm.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;Debian GNU/Linux i386 (<a href="txt/debian.release.txt" class="normal">release notes</a>)</td>
+  <td class="binaries" align="center"><?php echo $Latest_DEB_Client; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/client/deb/".$Latest_DEB_Client_Base."/silc-client_".$Latest_DEB_Client."_i386.deb"),1024); ?> kB&nbsp;</td>
+  <td class="binaries"  align="center"><a href="download/client/deb/<?php echo $Latest_DEB_Client_Base."/silc-client_".$Latest_DEB_Client ?>_i386.deb" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/deb/<?php echo $Latest_DEB_Client_Base."/silc-client_".$Latest_DEB_Client ?>_i386.deb" class="normal">FTP</a></td>
+  <td class="binaries" align="center"><a href="download/client/deb/<?php echo $Latest_DEB_Client_Base."/silc-client_".$Latest_DEB_Client; ?>_i386.changes" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;PKG Solaris 8 SPARC</td>
+  <td class="binaries" align="center"><?php echo $Latest_Solaris_Client; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/client/solaris/SPARC/SILCclie-".$Latest_Solaris_Client."-sol8-sparc-local.gz"),1024); ?> kB&nbsp;</td>
+  <td class="binaries"  align="center"><a href="download/client/solaris/SPARC/SILCclie-<?php echo $Latest_Solaris_Client; ?>-sol8-sparc-local.gz" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/solaris/SPARC/SILCclie-<?php echo $Latest_Solaris_Client; ?>-sol8-sparc-local.gz" class="normal">FTP</a></td>
+  <td class="binaries"  align="center"><a href="download/client/solaris/SPARC/SILCclie-<?php echo $Latest_Solaris_Client; ?>-sol8-sparc-local.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;EXE Windows/Cygwin</td>
+  <td class="binaries" align="center"><?php echo $Latest_Windows_Client; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/client/cygwin/silc-".$Latest_Windows_Client.".exe.zip"),1024); ?> kB&nbsp;</td>
+  <td class="binaries"  align="center"><a href="download/client/cygwin/silc-<?php echo $Latest_Windows_Client; ?>.exe.zip" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/client/cygwin/silc-<?php echo $Latest_Windows_Client; ?>.exe.zip" class="normal">FTP</a></td>
+  <td class="binaries"  align="center"><a href="download/client/cygwin/silc-<?php echo $Latest_Windows_Client; ?>.exe.zip.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td colspan="7" class="text">
+&nbsp;<br />
+The SILC Client package is also available in official package bases in <a href="http://www.freebsd.org/ports/" class="normal">FreeBSD</a> (<a href="http://www.freebsd.org/cgi/cvsweb.cgi/ports/net/silc-client/" class="normal">ports/net/silc-client/</a>) and <a href="http://netbsd.org/Documentation/software/packages.html" class="normal">NetBSD</a> (<a href="http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/chat/silc-client/" class="normal">pkgsrc/chat/silc-client/</a>).
+  </td>
+ </tr>
+</table>
+
+<br />&nbsp;<br />
+
+
+<table width="600" cellspacing="2" cellpadding="2" border="0">
+ <tr>
+  <td colspan="7" class="text">
+
+<b>SILC Server</b>
+<br />&nbsp;<br />
+<b>The latest version of the SILC server is <?php echo $Latest_Server; ?></b>
+<br />&nbsp;<br />
+The SILC Server package is intended for system administrators who wants to 
+setup their own SILC server or router. The package includes only the 
+server and not the client. People who is running SILC servers and are 
+interested to get the server linked to the new router on silc.silcnet.org 
+contact <a href="mailto:priikone at silcnet.org" class="normal">me</a> now.
+<br />&nbsp;<br />
+
+  </td>
+ </tr>
+ <tr>
+  <td class="title340" colspan="2" align="center"><b>Package</b></td>
+  <td class="title50" align="center"><b>Version</b></td>
+  <td class="title60" align="center"><b>Size</b></td>
+  <td class="title100" colspan="2" align="center"><b>URL</b></td>
+  <td class="title50" align="center"><b>SUM</b></td>
+ </tr>
+ <tr>
+  <td class="sources60">&nbsp;Sources</td>
+  <td class="sources280">&nbsp;tar.gz</td>
+  <td class="sources50" align="center"><?php echo $Latest_Server; ?></td>
+  <td class="sources60" align="right"><?php echo div(FileSize("download/server/sources/silc-server-".$Latest_Server.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources50" align="center"><a href="download/server/sources/silc-server-<?php echo $Latest_Server; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources50" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/sources/silc-server-<?php echo $Latest_Server; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources50" align="center"><a href="download/server/sources/silc-server-<?php echo $Latest_Server; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;tar.bz2</td>
+  <td class="sources" align="center"><?php echo $Latest_Server; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/server/sources/silc-server-".$Latest_Server.".tar.bz2"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/server/sources/silc-server-<?php echo $Latest_Server; ?>.tar.bz2" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/sources/silc-server-<?php echo $Latest_Server; ?>.tar.bz2" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/server/sources/silc-server-<?php echo $Latest_Server; ?>.tar.bz2.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;SRPM</td>
+  <td class="sources" align="center"><?php echo $Latest_RPM_Server_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/server/rpm/SRPMS/silc-server-".$Latest_RPM_Server_src.".src.rpm"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/server/rpm/SRPMS/silc-server-<?php echo $Latest_RPM_Server_src; ?>.src.rpm" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/rpm/SRPMS/silc-server-<?php echo $Latest_RPM_Server_src; ?>.src.rpm" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/server/rpm/SRPMS/silc-server-<?php echo $Latest_RPM_Server_src; ?>.src.rpm.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;deb-src (<a href="txt/debian.release.txt" class="normal">release notes</a>)</td>
+  <td class="sources" align="center"><?php echo $Latest_DEB_Server_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSizeDEB("server",$Latest_DEB_Server_Base,$Latest_DEB_Server_src),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/server/deb/<?php echo $Latest_DEB_Server_Base; ?>/" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/deb/<?php echo $Latest_DEB_Server_Base; ?>/" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/server/deb/<?php echo $Latest_DEB_Server_Base."/silc-server_".$Latest_DEB_Server; ?>_i386.changes" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;NetBSD pkgsrc</td>
+  <td class="sources" align="center"><?php echo $Latest_NetBSD_Server_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/server/netbsd/pkgsrc/silc-server-".$Latest_NetBSD_Server_src.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/server/netbsd/pkgsrc/silc-server-<?php echo $Latest_NetBSD_Server_src; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/netbsd/pkgsrc/silc-server-<?php echo $Latest_NetBSD_Server_src; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/server/netbsd/pkgsrc/silc-server-<?php echo $Latest_NetBSD_Server_src; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;OpenBSD ports</td>
+  <td class="sources" align="center"><?php echo $Latest_OpenBSD_Server_src; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/server/openbsd/ports/silc-server-".$Latest_OpenBSD_Server_src.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/server/openbsd/ports/silc-server-<?php echo $Latest_OpenBSD_Server_src; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/openbsd/ports/silc-server-<?php echo $Latest_OpenBSD_Server_src; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/server/openbsd/ports/silc-server-<?php echo $Latest_OpenBSD_Server_src; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;RPM GNU/Linux i386 (<a href="txt/rpm-release-notes.html" class="normal">release notes</a>)</td>
+  <td class="binaries" align="center"><?php echo $Latest_RPM_Server; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/server/rpm/i386/silc-server-".$Latest_RPM_Server.".i386.rpm"),1024); ?> kB&nbsp;</td>
+  <td class="binaries" align="center"><a href="download/server/rpm/i386/silc-server-<?php echo $Latest_RPM_Server; ?>.i386.rpm" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/rpm/i386/silc-server-<?php echo $Latest_RPM_Server; ?>.i386.rpm" class="normal">FTP</a></td>
+  <td class="binaries" align="center"><a href="download/server/rpm/i386/silc-server-<?php echo $Latest_RPM_Server; ?>.i386.rpm.md5" class="normal">MD5</a></td>
+</tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;Debian GNU/Linux i386 (<a href="txt/debian.release.txt" class="normal">release notes</a>)</td>
+  <td class="binaries" align="center"><?php echo $Latest_DEB_Server; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/server/deb/".$Latest_DEB_Server_Base."/silc-server_".$Latest_DEB_Server."_i386.deb"),1024); ?> kB&nbsp;</td>
+  <td class="binaries"  align="center"><a href="download/server/deb/<?php echo $Latest_DEB_Server_Base."/silc-server_".$Latest_DEB_Server ?>_i386.deb" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/server/deb/<?php echo $Latest_DEB_Server_Base."/silc-server_".$Latest_DEB_Server ?>_i386.deb" class="normal">FTP</a></td>
+  <td class="binaries" align="center"><a href="download/server/deb/<?php echo $Latest_DEB_Server_Base."/silc-server_".$Latest_DEB_Server; ?>_i386.changes" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td colspan="7" class="text">
+&nbsp;<br />
+The SILC Server package is also available in official package bases in <a href="http://www.freebsd.org/ports/" class="normal">FreeBSD</a> (<a href="http://www.freebsd.org/cgi/cvsweb.cgi/ports/net/silc-server/" class="normal">ports/net/silc-server/</a>) and <a href="http://netbsd.org/Documentation/software/packages.html" class="normal">NetBSD</a> (<a href="http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/chat/silc-server/" class="normal">pkgsrc/chat/silc-server/</a>).
+  </td>
+ </tr>
+</table>
+
+<br />&nbsp;<br />
+
+<table width="600" cellspacing="2" cellpadding="2" border="0">
+ <tr>
+  <td colspan="7" class="text">
+
+<b>SILC Toolkit</b>
+<br />&nbsp;<br />
+<b>The latest version of the SILC toolkit is <?php echo $Latest_Toolkit; ?></b>
+<br />&nbsp;<br />
+The SILC Toolkit package is intended for developers and programmers who 
+would like to create their own SILC applications or help in the 
+development of the SILC protocol. The Win32 binary package available 
+includes the entire Toolkit with sources and compiled DLLs.
+<br />&nbsp;<br />
+
+  </td>
+ </tr>
+ <tr>
+  <td class="title340" colspan="2" align="center"><b>Package</b></td>
+  <td class="title50" align="center"><b>Version</b></td>
+  <td class="title60" align="center"><b>Size</b></td>
+  <td class="title100" colspan="2" align="center"><b>URL</b></td>
+  <td class="title50" align="center"><b>SUM</b></td>
+ </tr>
+ <tr>
+  <td class="sources60">&nbsp;Sources</td>
+  <td class="sources280">&nbsp;tar.gz</td>
+  <td class="sources50" align="center"><?php echo $Latest_Toolkit; ?></td>
+  <td class="sources60" align="right"><?php echo div(FileSize("download/toolkit/sources/silc-toolkit-".$Latest_Toolkit.".tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources50" align="center"><a href="download/toolkit/sources/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources50" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/toolkit/sources/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.gz" class="normal">FTP</a></td>
+  <td class="sources50" align="center"><a href="download/toolkit/sources/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="sources">&nbsp;Sources</td>
+  <td class="sources">&nbsp;tar.bz2</td>
+  <td class="sources" align="center"><?php echo $Latest_Toolkit; ?></td>
+  <td class="sources" align="right"><?php echo div(FileSize("download/toolkit/sources/silc-toolkit-".$Latest_Toolkit.".tar.bz2"),1024); ?> kB&nbsp;</td>
+  <td class="sources" align="center"><a href="download/toolkit/sources/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.bz2" class="normal">HTTP</a></td>
+  <td class="sources" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/toolkit/sources/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.bz2" class="normal">FTP</a></td>
+  <td class="sources" align="center"><a href="download/toolkit/sources/silc-toolkit-<?php echo $Latest_Toolkit; ?>.tar.bz2.md5" class="normal">MD5</a></td>
+ </tr>
+ <tr>
+  <td class="binaries">&nbsp;Binaries</td>
+  <td class="binaries">&nbsp;Win32</td>
+  <td class="binaries" align="center"><?php echo $Latest_Toolkit_Win32; ?></td>
+  <td class="binaries" align="right"><?php echo div(FileSize("download/toolkit/win32/silc-toolkit-".$Latest_Toolkit_Win32.".zip"),1024); ?> kB&nbsp;</td>
+  <td class="binaries" align="center"><a href="download/toolkit/win32/silc-toolkit-<?php echo $Latest_Toolkit_Win32 ?>.zip" class="normal">HTTP</a></td>
+  <td class="binaries" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/toolkit/win32/silc-toolkit-<?php echo $Latest_Toolkit_Win32; ?>.zip" class="normal">FTP</a></td>
+  <td class="binaries" align="center"><a href="download/toolkit/win32/silc-toolkit-<?php echo $Latest_Toolkit_Win32 ?>.zip.md5" class="normal">MD5</a></td>
+ </tr>
+</table>
+
+<br />&nbsp;<br />
+
+<table width="600" cellspacing="2" cellpadding="2" border="0">
+ <tr>
+  <td colspan="7" class="text">
+
+<b>CVS Snapshots</b>
+<br />&nbsp;<br />
+Daily CVS snapshots are available. These are generated 22:00 GMT every
+night.  Read the <a href="?page=cvs" class="normal">CVS page</a> for more
+information.
+<br />&nbsp;<br />
+
+  </td>
+ </tr>
+ <tr>
+  <td class="title340" colspan="2" align="center"><b>Package</b></td>
+  <td class="title50" align="center"><b>Version</b></td>
+  <td class="title60" align="center"><b>Size</b></td>
+  <td class="title100" colspan="2" align="center"><b>URL</b></td>
+  <td class="title50" align="center"><b>SUM</b></td>
+ </tr>
+ <tr>
+  <td class="sources60">&nbsp;Sources</td>
+  <td class="sources280">&nbsp;tar.gz CVS snapshot</td>
+  <td class="sources50" align="center">CVS</td>
+  <td class="sources60" align="right"><?php echo div(FileSize("download/cvs/silc.tar.gz"),1024); ?> kB&nbsp;</td>
+  <td class="sources50" align="center"><a href="download/cvs/silc.tar.gz" class="normal">HTTP</a></td>
+  <td class="sources50" align="center"><a href="ftp://<?php echo $FTP_Site.$FTP_Root ?>/cvs/silc.tar.gz" class="normal">FTP</a></td>
+  <td class="sources50" align="center"><a href="download/cvs/silc.tar.gz.md5" class="normal">MD5</a></td>
+ </tr>
+</table>
+
+<table width="600" cellspacing="2" cellpadding="2" border="0">
+ <tr>
+  <td class="text">
+
+<br />&nbsp;<br />
+<b>Portability</b>
+<br />&nbsp;<br />
+The SILC has been reported to work on, at least:
+<br />&nbsp;<br />
+&nbsp;- <a href="http://www.linux.org/" class="normal">GNU/Linux</a><br />
+&nbsp;- <a href="http://www.freebsd.org/" class="normal">FreeBSD</a><br />
+&nbsp;- <a href="http://www.netbsd.org/" class="normal">NetBSD</a><br />
+&nbsp;- <a href="http://www.openbsd.org/" class="normal">OpenBSD</a><br />
+&nbsp;- <a href="http://www.hp.com/products1/unix/operating/" class="normal">HP-UX</a><br />
+&nbsp;- <a href="http://www.sun.com/software/solaris/" class="normal">Solaris</a><br />
+&nbsp;- <a href="http://www.sgi.com/developers/technology/irix.html" class="normal">IRIX</a><br />
+&nbsp;- <a href="http://www.ibm.com/servers/aix/" class="normal">AIX</a><br />
+&nbsp;- <a href="http://www.microsoft.com/windows/" class="normal">Windows</a><br />
+&nbsp;- <a href="http://sources.redhat.com/cygwin/" class="normal">Cygwin</a> &amp; <a href="http://www.mingw.org/" class="normal">MinGW</a><br />
+&nbsp;- <a href="http://www.apple.com/macosx/" class="normal">Mac OS X</a>
+
+  </td>
+ </tr>
+</table>
diff --git a/public_html/html/faq.php b/public_html/html/faq.php
new file mode 100644 (file)
index 0000000..8422d66
--- /dev/null
@@ -0,0 +1,811 @@
+&nbsp;<br />
+<b><big>Frequently Asked Questions</big></b>
+<br />&nbsp;<br />
+<a href="#f1_0" class="normal">1. General Questions</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_10" class="normal">
+1.1 What is SILC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_20" class="normal">
+1.2 When was SILC Project started?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_30" class="normal">
+1.3 Why SILC in the first place?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_40" class="normal">
+1.4 What license covers the SILC release?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_50" class="normal">
+1.5 Why SILC? Why not IRC3?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_55" class="normal">
+1.6 What platforms SILC supports?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_59" class="normal">
+1.7 How do you pronounce SILC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_60" class="normal">
+1.8 Where can I find more information?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f1_70" class="normal">
+1.9 I would like to help out, what can I do?</a>
+
+<br />&nbsp;<br />
+<a href="#f2_0" class="normal">2. Protocol Questions</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_10" class="normal">
+2.1 What is the status of SILC protocol in the IETF?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_20" class="normal">
+2.2 How much the SILC protocol is based on IRC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_30" class="normal">
+2.3 Why use SILC? Why not IRC with SSL?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_40" class="normal">
+2.4 Can I talk from SILC network to IRC network?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_45" class="normal">
+2.5 Does SILC support file transfer?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_46" class="normal">
+2.6 Does SILC support DCC or alike?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_47" class="normal">
+2.7 I am behind a firewall, can I use SILC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_50" class="normal">
+2.8 How secure SILC really is?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_60" class="normal">
+2.9 Does SILC  support instant messaging?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_70" class="normal">
+2.10 Why SILC does not have LINKS command like in IRC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_80" class="normal">
+2.11 Why SILC does not have STATS command like in IRC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_90" class="normal">
+2.12 Is anyone outside a channel able to see the channel messages?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_100" class="normal">
+2.13 Is it true that all messages are encrypted in SILC?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_110" class="normal">
+2.14 Can server or SILC operator gain operator mode on a channel?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f2_120" class="normal">
+2.15 I have suggestions to SILC Protocol, what can I do?</a>
+
+<br />&nbsp;<br />
+<a href="#f3_0" class="normal">3. Client Questions</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_10" class="normal">
+3.1 Where can I find SILC clients?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_20" class="normal">
+3.2 Can I use SILC with IRC client and vice versa?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_25" class="normal">
+3.3 The default theme sucks, where can I find a better one?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_30" class="normal">
+3.4 How do I send a private message?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_40" class="normal">
+3.5 How do I negotiate secret key with another user?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_50" class="normal">
+3.6 How do I negotiate secret keys behind a NAT?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_60" class="normal">
+3.7 How do I change channel modes?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_70" class="normal">
+3.8 What does the founder mode on channel mean, and how do I set it?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_80" class="normal">
+3.9 I am founder of invite only channel, how can I join the channel after I have left it?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_90" class="normal">
+3.10 How can I op or deop somebody on channel?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_100" class="normal">
+3.11 How do I set private key for channel, and what does that mean exactly?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_110" class="normal">
+3.12 How do I transfer a file?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_120" class="normal">
+3.13 How can I get other users public keys?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_130" class="normal">
+3.14 How can I see the fingerprint of my public key?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_140" class="normal">
+3.15 I gave WHOIS to a nick, and it returned multiple replies, why?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_150" class="normal">
+3.16 Is there a command to see all linked servers?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_160" class="normal">
+3.17 How do I list the users of a channel?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f3_170" class="normal">
+3.18 What is the difference between OPER and SILCOPER commands?</a>
+
+<br />&nbsp;<br />
+<a href="#f4_0" class="normal">4. Server Questions</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_10" class="normal">
+4.1 Where can I find SILC servers?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_20" class="normal">
+4.2 Can I run my own SILC server?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_30" class="normal">
+4.3 What is the difference between SILC server and SILC router?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_40" class="normal">
+4.4 Why server says permission denied to write to a log file?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_50" class="normal">
+4.5 When I connect to to my server, it says "server does not support one of your proposed cipher", what is wrong?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_60" class="normal">
+4.6 Why SILC server runs on privileged port 706?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_70" class="normal">
+4.7 I see [Unknown] in the log file, what does it mean?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f4_80" class="normal">
+4.8 How can I generate a new server key pair?</a>
+
+<br />&nbsp;<br />
+<a href="#f5_0" class="normal">5. Toolkit Questions</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f5_10" class="normal">
+5.1 What is SILC Toolkit?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f5_20" class="normal">
+5.2 Is the SILC Toolkit Reference Manual Available?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f5_30" class="normal">
+5.3 How do I compile the Toolkit on Unix?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f5_40" class="normal">
+5.4 How do I compile the Toolkit on Win32?</a><br />
+ &nbsp;&nbsp;&nbsp;&nbsp;<a href="#f5_50" class="normal">
+5.5 Does the Toolkit package include any sample code?</a><br />
+<br />&nbsp;<br />
+
+
+<a name="f1_0"></a>
+<b>1. General Questions</b><br />&nbsp;<br />
+
+<a name="f1_10"></a>
+<samp class="highlight">Q: What is SILC?</samp><br />
+A: SILC (Secure Internet Live Conferencing) is a protocol which provides
+secure conferencing services in the Internet over insecure channel. SILC
+is IRC like although internally they are very different. Biggest
+similarity between SILC and IRC is that they both provide conferencing
+services and that SILC has almost same commands as IRC. Other than that
+they are nothing alike.
+<br />&nbsp;<br />
+Biggest differences are that SILC is secure what IRC is not in any way.
+The network model is also entirely different compared to IRC.
+<br />&nbsp;<br />
+
+<a name="f1_20"></a>
+<samp class="highlight">Q: When was SILC Project started?</samp><br />
+A: The SILC development started in 1996 and early 1997. But, for various 
+reasons it suspended many times until it finally got some wind under its 
+wings in 1999. First public release was in summer 2000.
+<br />&nbsp;<br />
+
+<a name="f1_30"></a>
+<samp class="highlight">Q: Why SILC in the first place?</samp><br />
+A: Simply for fun, nothing more. And actually for need back in the days
+when it was started. When SILC was first developed there really did not 
+exist anything like this. SILC has been very interesting and educational 
+project.
+<br />&nbsp;<br />
+
+<a name="f1_40"></a>
+<samp class="highlight">Q: What license covers the SILC release?</samp><br />
+A: The SILC software developed here at silcnet.org, the SILC Client, the 
+SILC Server and the SILC Toolkit are covered by the GNU General Public 
+License.
+<br />&nbsp;<br />
+
+<a name="f1_50"></a>
+<samp class="highlight">Q: Why SILC? Why not IRC3?</samp><br />
+A: Question that is justified no doubt of that. SILC was not started to 
+become a replacement for IRC. SILC was something that didn't exist in 1996 
+or even today except that SILC is now released. However, I did check out 
+the IRC3 project in 1997 when I started coding and planning the SILC protocol.
+<br />&nbsp;<br />
+But, IRC3 is problematic. Why? Because it still doesn't exist. The
+project is almost at the same spot where it was in 1997 when I checked it 
+out. And it was old project back then as well. That's the problem of IRC3 
+project. The same almost happened to SILC as well as I wasn't making real 
+progress over the years. I talked to the original author of IRC, Jarkko 
+Oikarinen, in 1997 and he directed me to the IRC3 project, although he 
+said that IRC3 is a lot of talking and not that much of anything else. I 
+am not trying to put down the IRC3 project but its problem is that no one 
+in the project is able to make a decision what is the best way to go about
+making the IRC3 and I wasn't going to be part of that. The fact is that
+if I would've gone to IRC3 project, nor IRC3 or SILC would exist today. I
+think IRC3 could be something really great if they just would get their
+act together and start coding the thing.
+<br />&nbsp;<br />
+
+<a name="f1_55"></a>
+<samp class="highlight">Q: What platforms SILC supports?</samp><br />
+A: The SILC Client is available on various Unix systems and is reported to 
+work under cygwin on Windows. The SILC Server also works on various Unix 
+systems. However, the server has not been tested under cygwin as far as we 
+know. The SILC Toolkit is distributed for all platforms, Unix, Cygwin 
+and native Windows.
+<br />&nbsp;<br />
+
+<a name="f1_59"></a>
+<samp class="highlight">Q: How do you pronounce SILC?</samp><br />
+A: SILC is usually pronounced as `silk', but you are free to pronounce
+it the way you want.
+<br />&nbsp;<br />
+
+<a name="f1_60"></a>
+<samp class="highlight">Q: Where can I find more information?</samp><br />
+A: For more technical information we suggest reading the SILC Protocol 
+specifications. You might also want to take a look at the <a 
+href="?page=docs" class="normal">documentation </a> page on the web page.
+<br />&nbsp;<br />
+
+<a name="f1_70"></a>
+<samp class="highlight">Q: I would like to help out, what can I do?</samp><br />
+A: You might want to take a look at the <a 
+href="?page=contribute" class="normal">Contributing</a> page and the <a 
+href="?page=todo" class="normal">TODO</a> list. You might also want to join the
+SILC development mailing list.
+<br />&nbsp;<br />
+
+
+<a name="f2_0"></a><br />
+<b>2. Protocol Questions</b><br />&nbsp;<br />
+
+<a name="f2_10"></a>
+<samp class="highlight">Q: What is the status of SILC protocol in the IETF?</samp><br />
+A: The SILC protocol specifications has been submitted currently as 
+individual submissions. There does not currently exist a working group 
+for this sort of project. Our goal is to fully standardize the SILC and 
+thus submit it as RFC to the <a href="http://www.ietf.org/" class="normal">
+IETF</a> at a later time.  This can happen only after we have requested
+the IETF to accept SILC as RFC.  As of today, we have not yet even requested
+this from the IETF.  We want to let the protocol mature a bit more.
+<br />&nbsp;<br />
+
+<a name="f2_20"></a>
+<samp class="highlight">Q: How much SILC Protocol is based on IRC?</samp><br />
+A: SILC is not based on IRC. The client superficially resembles IRC 
+client but everything that happens under the hood is nothing alike IRC. 
+SILC could *never* support IRC because the entire network toppology is 
+different (hopefully more scalable and powerful). So no, SILC protocol 
+(client or server) is not based on IRC. Instead, We've taken good things 
+from IRC and left all the bad things behind and not even tried to burden 
+the SILC with the IRCs problems that will burden IRC and future IRC 
+projects till the end. SILC client resembles IRC client because it is 
+easier for new users to start using SILC when they already know all the 
+commands.
+<br />&nbsp;<br />
+
+<a name="f2_30"></a>
+<samp class="highlight">Q: Why use SILC? Why not IRC with SSL?</samp><br />
+A: Sure, that is possible, although, does that secure the entire IRC
+network? And does that increase or decrease the lags and splits in the 
+IRC network? Does that provide user based security where some specific 
+private message are secured? Does that provide security where some 
+specific channel messages are secured? And I know, you can answer yes to 
+some of these questions. But, security is not just about applying 
+encryption to traffic and SILC is not just about `encrypting the 
+traffic`. You cannot make insecure protocol suddenly secure just by 
+encrypting the traffic. SILC is not meant to be IRC replacement. IRC is 
+good for some things, SILC is good for same and some other things.
+<br />&nbsp;<br />
+
+<a name="f2_40"></a>
+<samp class="highlight">Q: Can I talk from SILC network to IRC network?</samp><br />
+A: Simple answer for this is No. The protocols are not compatible which 
+makes it impossible to directly talk from SILC network to IRC network or 
+vice versa. Developing a gateway between these two networks would 
+technically be possible but from security point of view strongly not 
+recommended. We have no plans for developing such a gateway.
+<br />&nbsp;<br />
+
+<a name="f2_45"></a>
+<samp class="highlight">Q: Does SILC support file transfer?</samp><br />
+A: Yes.  The SILC protocol support SFTP as mandatory file transfer 
+protocol.  It provides simple client to client file transfer, but also
+a possibility for file and directory manipulation.  Even though the SFTP
+is the file transfer protocol the support for file transferring has been 
+done so that practically any file transfer protocol may be used with SILC 
+protocol.
+<br />&nbsp;<br />
+
+<a name="f2_46"></a>
+<samp class="highlight">Q: Does SILC support DCC or alike?</samp><br />
+A: SILC does not support the DCC commonly used in IRC.  It does not need 
+it since it has builtin support for same features that DCC have.  You can 
+transfer files securely and encrypted directly with another client.  You 
+can also negotiate secret key material with another client directly to 
+use it in private message encryption.  The private messages are not, 
+however sent directly between clients.  The protocol, on the hand 
+does not prohibit sending messages directly between clients if the 
+implementation would support it.  The current SILC Client implementation 
+does not support it.  This means that private messages travel through the 
+SILC Network.  SILC protocol also has a capability to support DCC 
+and CTCP like protocols with SILC.  None of them, however have not been 
+defined to be used with SILC at the present time.
+<br />&nbsp;<br />
+
+<a name="f2_47"></a>
+<samp class="highlight">Q: I am behind a firewall, can I use SILC?</samp><br />
+A: Yes. If your network administrator can open the remote port 706 (TCP) you
+can use SILC without problems. You may also compile your SILC client with 
+SOCKS support which will proxy your SILC session through the firewall.
+<br />&nbsp;<br />
+
+<a name="f2_50"></a>
+<samp class="highlight">Q: How secure SILC really is?</samp><br />
+A: We have tried to make SILC as secure as possible. However, there is
+no security protocol or security software that has not been vulnerable to
+some sort of attacks. SILC is in no means different from this. So, it is
+suspected that there are security holes in the SILC. These holes just need
+to be found so that they can be fixed.  SILC's security features has been
+developed from attacker's point of view, and we've tried to find all the
+possible attacks and guard the protocol against them.
+<br />&nbsp;<br />
+But to give you some parameters of security SILC uses the most secure
+crytographic algorithms such as AES (Rijndael), Twofish, Blowfish, RC5,
+etc. SILC does not have DES or 3DES as DES is insecure and 3DES is just
+too slow. SILC also uses cryptographically strong random number generator
+when it needs random numbers. Public key cryptography uses RSA (PKCS #1)
+and Diffie-Hellman algorithms. Key lengths for ciphers are initially set
+to 256. For public key algorithms the starting key length is 1024 bits.
+<br />&nbsp;<br />
+But the best answer for this question is that SILC is as secure as its
+weakest link. SILC is open and the protocol is open and in public thus
+open for security analysis.
+<br />&nbsp;<br />
+To give a list of attacks that are ineffective against SILC:
+<br />&nbsp;<br />
+- Man-in-the-middle attacks are ineffective if proper public key
+infrastructure is used, and if all public keys are always verified.<br />
+- IP spoofing is ineffective (because of encryption and trusted keys).<br />
+- Attacks that change the contents of the data or add extra data to the
+packets are ineffective (because of encryption and integrity checks).<br />
+- Passive attacks (listenning network traffic) are ineffective (because
+of encryption). Everything is encrypted including authentication data
+such as passwords when they are needed.<br />
+- Any sort of cryptanalytic attacks are tried to make ineffective by
+using the best cryptographic algorithms out there, and by designing the
+protocol to guard against them.
+<br />&nbsp;<br />
+
+<a name="f2_60"></a>
+<samp class="highlight">Q: Does SILC support instant messaging?</samp><br />
+A: SILC is not an instant message (IM) system, like ICQ and the others. 
+SILC is more IRC like system, "real-time", connection-oriented chat and 
+that kind of stuff.  But I guess IRC is too sometimes called an Instant
+Messaging system.
+<br />&nbsp;<br />
+
+<a name="f2_70"></a>
+<samp class="highlight">Q: Why SILC does not have LINKS command like in 
+IRC?</samp><br />
+A: It was felt that this information as an own command in SILC is not 
+necessary.  Moreover, the topology of the network might be undisclosed 
+information even though the servers and routers in the network are still 
+open. We feel that the network topology information, if it is wanted to be 
+public, and the list of accessible servers can be made available in other 
+ways than providing command like LINKS, which shows the active server 
+links in IRC.
+<br />&nbsp;<br />
+
+<a name="f2_80"></a>
+<samp class="highlight">Q: Why SILC does not have STATS command like in  
+IRC?</samp><br />
+A: This too was considered as information that the protocol should not 
+address. We feel that server implementations will need to implement some 
+sort of adminstrative plugin, or module which provides various means of 
+accessing statistical and other information in the server. And, we do 
+consider this implementation issue, not protocol design issue.
+<br />&nbsp;<br />
+
+<a name="f2_90"></a>
+<samp class="highlight">Q: Is anyone outside a channel able to see the channel 
+messages?</samp><br />
+A: A short answer is simply No.  A longer answer involves assumptions 
+about security conditions.  Initially channel keys are generated by the 
+server, so if the server would get compromised it would be possible for 
+an adversary to see the messages.  However, users on the channel can 
+prevent this even if the server would be compromised.  It is possible to 
+set so called channel private key that only the users on the channel 
+know about.  The servers does not know about the key, and therefore cannot 
+see the messages even if they would be compromised.  So, longer answer 
+results into same as the short one; No.
+<br />&nbsp;<br />
+
+<a name="f2_100"></a>
+<samp class="highlight">Q: Is it true that all messages are encrypted in SILC?</samp><br />
+A: Most definitely yes.  The SILC protocol makes it impossible to send
+unencrypted messages or packets to the SILC network.  All messages are
+always encrypted, either using session keys, or other secret keys such as
+channel keys or private message keys.
+<br />&nbsp;<br />
+
+<a name="f2_110"></a>
+<samp class="highlight">Q: Can server or SILC operator gain operator mode on a channel?</samp><br />
+A: They cannot get operator status, founder status, join invite only channels,
+escape active bans, escape user limits or anything alike, without explicitly
+being allowed.  Only way to get channel operator status is that someone
+ops him.  Server and SILC operators in the network are normal users with
+the extra privileges of being able to adminstrate their server.  They cannot
+do anything more than a normal user.
+<br />&nbsp;<br />
+
+<a name="f2_120"></a>
+<samp class="highlight">Q: I have suggestions to SILC Protocol, what can I do?</samp><br />
+A: All suggestions and improvements are of course welcome. You should read 
+the protocol specifications first to check out whether your idea is 
+covered by them already. The best place to make your idea public is the 
+SILC development mailing list.  You might want to checkout the TODO list
+from the CVS as well.
+<br />&nbsp;<br />
+
+
+<a name="f3_0"></a><br />
+<b>3. Client Questions</b><br />&nbsp;<br />
+
+<a name="f3_10"></a>
+<samp class="highlight">Q: Where can I find SILC clients?</samp><br />
+A: The SILC client is available for free download from the silcnet.org web 
+page. Some people have also mentioned words Java and Perl when talking 
+about SILC clients. Nothing has appeared yet, though.
+<br />&nbsp;<br />
+
+<a name="f3_20"></a>
+<samp class="highlight">Q: Can I use SILC with IRC client and vice versa?</samp><br />
+A: Generally the answer would be no for both. However, there exist already 
+at least one IRC client that supports SILC, the <a href="http://irssi.org/"
+class="normal">Irssi client</a>. The current SILC client is actually based
+on the user interface of the Irssi client. So, yes it is possible to use
+SILC with some IRC clients and vice versa. But, this does not mean that you
+can talk from SILC network to IRC network, that is not possible.
+<br />&nbsp;<br />
+
+<a name="f3_25"></a>
+<samp class="highlight">Q: The default theme sucks, where can I find a better one?</samp><br />
+A: The Irssi SILC client's theme files are almost 100% compatible with
+the original Irssi IRC client's themes.  You can get those theme files
+from the <a href="http://irssi.org/" class="normal">Irssi project website</a>. 
+You can also try to make a better theme by yourself.
+<br />&nbsp;<br />
+
+<a name="f3_30"></a>
+<samp class="highlight">Q: How do I send a private message?</samp><br />
+A: Sending private message is done by using the MSG command.  For example,
+command: <samp class="highlight">/MSG john hello</samp>, will send a 
+`hello' message to a nickname `john'.  By default private messages are
+secured with session keys, and the message is re-encrypted by the servers
+when the message travels to the receiver.  If you would like to secure the
+private messages with a private key, you can negotiate a secret key with the
+receiver.  Always remember to give WHOIS command before sending a private
+message to assure that you are sending the message to correct person.
+<br />&nbsp;<br />
+
+<a name="f3_40"></a>
+<samp class="highlight">Q: How do I negotiate secret key with another user?</samp><br />
+A: It is important to negotiate secret keys if you cannot trust the servers
+and the network you are using.  By negotiating a key with the user you
+want to talk to assures that no one except you and your friend is able
+to encrypt and decrypt the messages.  The secret key negotiation is done with
+the KEY command.  Here is an example of how to negotiate keys for securing
+private messages.
+<br />&nbsp;<br />
+By giving command: <samp class="highlight">/KEY MSG john agreement 
+192.168.2.100</samp>, you will send a key negotiation request to a nickname
+`john'.  The 192.168.2.100 IP address would be your machine's IP address.
+You can also define an port to the KEY command after the IP address. If
+you do not do that the operating system will bind to a port of its choosing.
+John will receive a notification on the screen that you would like to 
+negotiate secret keys with him, and he will receive the IP address and port
+where you are listenning for the negotiation.  When he gives command: 
+<samp class="highlight">/KEY MSG You negotiate 192.168.2.100 31382</samp>,
+the key negotiation is started.  During the key negotiation you will be
+prompted on the screen to verify and accept John's public key if you do not
+have his public key already.  The John will be prompted to accept your
+public key as well.  After the key negotiation is over all private messages
+sent between you and John are secured with the negotiated secret key.
+Note that you must verify the public key you are prompted for, and this is
+very important since someone could be doing man-in-the-middle attack.
+<br />&nbsp;<br />
+
+<a name="f3_50"></a>
+<samp class="highlight">Q: How do I negotiate secret keys behind a NAT?</samp><br />
+A: If only you are behind a NAT, or firewall then key negotiation works,
+but if both you and your friend are behind a NAT then key negotiation will
+not work, since it is done peer to peer.  If you are behind a NAT then you
+obviously cannot receive key negotiations, and cannot bind to any IP address
+and port.  However, you can still use KEY command to negotiate the keys.
+<br />&nbsp;<br />
+By giving command: <samp class="highlight">/KEY MSG john agreement</samp>, 
+without any other arguments (such as IP address and port) you will send
+a negotiation request to John, but do not provide an address and port for
+the John to connect to.  When John receives the notification on the screen
+that you would like to perform key negotiation, he can give command:
+<samp class="highlight">/KEY MSG You agreement 172.16.100.78</samp>, which
+will send key negotiation request back to you.  You will receive the IP
+address and port where you need to connect in order to perform the negotiation.
+After receiving the notification you can give command: <samp class="highlight">
+/KEY MSG john negotiate 172.16.100.78 31181</samp>, which will start the
+key negotiation with John.  This way you can negotiate the keys if you are
+behind a NAT.
+<br />&nbsp;<br />
+
+<a name="f3_60"></a>
+<samp class="highlight">Q: How do I change channel modes?</samp><br />
+A: The command to manage channel modes is CMODE.  With this command you
+can change the channel status (to change it to secret channel for example),
+set user limit on the channel, passphrase for the channel, set the channel
+to use private keys on channel, and set the founder mode.
+<br />&nbsp;<br />
+
+<a name="f3_70"></a>
+<samp class="highlight">Q: What does the founder mode on channel mean, and how do I set it?</samp><br />
+A: Who ever creates the channel by being the first user to join the channel
+becomes automatically the founder of the channel.  Founder has some extra
+privileges on the channel.  For example, it is not possible to kick the
+founder off the channel, and there are some channel modes that only the
+founder of the channel can change.  If the creator of the channel wishes
+to preserve the channel founder mode even if he leave the channel he
+can set the founder mode for the channel.
+<br />&nbsp;<br />
+The mode is set by giving command: <samp class="highlight">/CMODE #channel
++f -pubkey</samp>.  This will set the founder mode and will use the public
+key of the founder as authenticator when the user is reclaiming the mode
+back.  If the founder leaves the channel he will be able to get the founder
+mode back by using JOIN or CUMODE commmands.  Giving command
+<samp class="highlight">/JOIN #channel -founder -pubkey</samp>,
+will get the founder mode back at the same time he joins the channel, or
+giving commmand <samp class="highlight">/CUMODE #channel +f -pubkey</samp>,
+will also give the founder mode back on the channel after he has joined
+the channel.
+<br />&nbsp;<br />
+If the channel is destroyed after the last client leaves the channel,
+the founder mode is also reset.  Who ever creates the channel after that
+will also get the channel founder mode automatically.  Note also that the
+founder mode is local.  You can reclaim the mode back only on the same
+server where you set the founder mode in the first place.
+<br />&nbsp;<br />
+
+<a name="f3_80"></a>
+<samp class="highlight">Q: I am founder of invite only channel, how can I join the channel after I have left it?</samp><br />
+A: Founder can override the invite only status by reclaiming the founder
+status on the channel using the JOIN command.  The channel must have the
+founder mode set in order for it to work.  Reclaiming founder status using
+JOIN command is important also if the channel has user limit set, and has
+active bans.  Founder can override these conditions as well.  However,
+founder cannot override the passphrase of the channel if it is set.  To
+get the founder mode during JOIN and to override the invite only condition,
+give command: <samp class="highlight">/JOIN #channel -founder -pubkey</samp>.
+This will join the channel and attempt to reclaim the founder status back
+to you.  Note that you need to be on the same server where you gave the
+founder mode for the channel for this to work.
+<br />&nbsp;<br />
+
+<a name="f3_90"></a>
+<samp class="highlight">Q: How can I op or deop somebody on channel?</samp><br />
+A: Giving operator status, or removing the operator status on a channel
+requires you to have at least operator status, or founder status on the
+channel.  You can give operator status to another user by using CUMODE
+command.  To give ops give the command: <samp class="highlight">/CUMODE
+#channel +o john</samp>, and to remove ops give command:
+<samp class="highlight">/CUMODE #channel -o john</samp>.  To indicate
+current channel you can also use `*' character in #channel's stead.
+<br />&nbsp;<br />
+
+<a name="f3_100"></a>
+<samp class="highlight">Q: How do I set private key for channel, and what does that mean exactly?</samp><br />
+A: Setting private key for channel requires first to set the private key mode
+for the channel.  You need to be the founder of the channel to be able to
+do this.  Give the command: <samp class="highlight">/CMODE #channel +k</samp>.
+After this mode is set the old channel key will not be used to encrypt and
+decrypt channel messages.  To set the key for the channel use the KEY command.
+Every user on the channel must do the same thing and set the same key.
+If some user on the channel does not set the key (or does not know the key)
+he won't be able to see any messages on the channel.  Give the command: 
+<samp class="highlight">/KEY CHANNEL #channel set verysecretkey</samp>.
+This command will set the `verysecretkey' passphrase as key to the #channel.
+How exactly other users will know this key is out of scope of the SILC
+protocol.  SILC does not provide yet a possibility of negotiating secret key
+with many users at the same time.  For this reason the secret key on the
+channel is usually a passphrase or a password that all users on the channel
+have to know.  Setting a private key for channel means that only the users
+on the channel who know the key is able to encrypt and decrypt messages.
+Servers do not know the key at all.  If you remove the private key mode
+from the channel, all users will start automatically using a new channel
+key to secure channel messages.
+<br />&nbsp;<br />
+
+<a name="f3_110"></a>
+<samp class="highlight">Q: How do I transfer a file?</samp><br />
+A: You can transfer files securely using the FILE command.  This command
+will automatically negotiate secret key with the remote user and the
+file transfer stream is secured using that key.  The file transfer
+stream is always sent peer to peer.  If you would like to send a file
+to another user you can give command: <samp class="highlight">/FILE
+SEND path/to/the/file john</samp>.  This command sends, or actually
+makes the `path/to/the/file' available for download for the user `john'.
+The John will decide whether he wants to actually download the file.
+When John gives the command: <samp class="highlight">/FILE RECEIVE</samp>,
+the key negotiation is started.  You and John will be prompted to verify
+and accept each other's public key if you do not have it cached already.
+After key negotiation is over the file transfer process starts.
+If you want to cancel the file transfer session, or if John wants to
+reject the file transfer request, giving the command: <samp class="highlight">
+/FILE CLOSE</samp> will close the session.
+<br />&nbsp;<br />
+
+<a name="f3_120"></a>
+<samp class="highlight">Q: How can I get other users public keys?</samp><br />
+A: You can get a user's public key using the GETKEY command.  This command
+will fetch the user's public key from the server where the user has connected
+to.  The server has verified that the user posesses the corresponding private
+key, however, you will be prompted to verify and accept the public key.
+All client public keys are saved in your local key directory in
+~/.silc/clientkeys/.  You can also receive clients public keys during
+key negotiation and file transfers.  The GETKEY command can be used to fetch
+a server's public key as well.  Those keys are saved in ~/.silc/serverkeys/
+directory.
+<br />&nbsp;<br />
+
+<a name="f3_130"></a>
+<samp class="highlight">Q: How can I see the fingerprint of my public key?</samp><br />
+A: You can check out your own fingerprint by giving just WHOIS command without
+any arguments.  Additionally you can also dump the contents of the key file
+using the silc program and giving -S option to it.  Your own public key is
+always saved in ~/.silc/public_key.pub file.  To dump your key run silc as:
+<samp class="highlight">silc -S .silc/public_key.pub</samp>.  The same way
+you can dump the contents of any public key inside ~/.silc/clientkeys/ and
+~/.silc/serverkeys/ directories.  The WHOIS command will also show other
+users public key fingerprints.
+<br />&nbsp;<br />
+
+<a name="f3_140"></a>
+<samp class="highlight">Q: I gave WHOIS to a nick, and it returned multiple replies, why?</samp><br />
+A: This will happen if there are several same nicknames in the network at
+the same time.  As you may already know nicknames are not unique in SILC
+network.  This means there can be multiple same nicknames.  This also means
+that you can always have the nickname you want.  If WHOIS returns multiple
+replies, you can distinguish the users by their realname, username,
+hostname and ultimately by the fingerprint of their public key, which the
+WHOIS will also show.  You will also notice an additional nickname inside a
+parenthesis.  It may show for example: <samp class="highlight">nickname:   John
+ (John@otaku)</samp>.  The real nickname is `John', but since there are
+many John's in the network you can access this one using `John@otaku'.
+So, if you were to send private message to this particular John you can do
+it by giving command: <samp class="highlight">/MSG John@otaku hello</samp>. 
+This will send `hello' message to the John@otaku.
+<br />&nbsp;<br />
+
+<a name="f3_150"></a>
+<samp class="highlight">Q: Is there a command to see all linked servers?</samp><br />
+A: No there is not.  For longer answer see also <a href="#f2_70" 
+class="normal">this FAQ</a>.
+<br />&nbsp;<br />
+
+<a name="f3_160"></a>
+<samp class="highlight">Q: How do I list the users of a channel?</samp><br />
+A: The command to list all users on a particular channel is USERS.  It is
+also aliased to WHO command in Irssi SILC Client.  To see the users of the
+current channel give the command: <samp class="highlight">/USERS *</samp>.
+You can replace the `*' with the channel name of your choosing.  If the
+channel is private or secret channel, and you have not joined the channel,
+you cannot list the users of that channel.
+<br />&nbsp;<br />
+
+<a name="f3_170"></a>
+<samp class="highlight">Q: What is the difference between OPER and SILCOPER commands?</samp><br />
+A: The OPER command is used to gain server operator privileges on normal
+SILC server, while SILCOPER is used to gain router operator (also known as
+SILC operator) privileges on router server.  You cannot use SILCOPER command
+on normal SILC server, it works only on router server.
+<br />&nbsp;<br />
+
+
+<a name="f4_0"></a><br />
+<b>4. Server Questions</b><br />&nbsp;<br />
+
+<a name="f4_10"></a>
+<samp class="highlight">Q: Where can I find SILC servers?</samp><br />
+A: The SILC server is available for free download from the silcnet.org 
+web page. We are not aware of any other SILC server implementations, so far.
+<br />&nbsp;<br />
+
+<a name="f4_20"></a>
+<samp class="highlight">Q: Can I run my own SILC server?</samp><br />
+A: Yes of course. Download the SILC server package, compile and install 
+it. Be sure to check out the installation instructions and the README 
+file. You also should decide whether you want to run SILC server or SILC 
+router.
+<br />&nbsp;<br />
+
+<a name="f4_30"></a>
+<samp class="highlight">Q: What is the difference between SILC 
+server and SILC router?</samp><br />
+A: The topology of the SILC network includes SILC routers and the SILC 
+servers (and SILC clients of course). Normal SILC server does not have 
+direct connections with other SILC servers. They connect directly to the 
+SILC router. SILC Routers may have several server connections and they 
+may connect to several SILC routers. The SILC routers are the servers in 
+the network that know everything about everything. The SILC servers know 
+only local information and query global information from the router when 
+necessary.
+<br />&nbsp;<br />
+If you are running SILC server you want to run it as router only if you 
+want to have server connections in it and are prepared to accept server 
+connections. You also need to get the router connected to some other 
+router to be able to join the SILC network. You may run the server as 
+normal SILC server if you do not want to accept other server connections 
+or cannot run it as router.
+<br />&nbsp;<br />
+
+<a name="f4_40"></a>
+<samp class="highlight">Q: Why server says permission denied to write to a 
+log file?</samp><br />
+A: The owner of the log files must be same user that the server is run 
+under, by default it is user `nobody'.  Just change the permissions and 
+try again.
+<br />&nbsp;<br />
+
+<a name="f4_50"></a>
+<samp class="highlight">Q: When I connect to my server it says "server does 
+not support one of your proposed ciphers", what is wrong?</samp><br />
+A: Most likely the ciphers and others has not been compiled as SIMs 
+(modules) and they are configured as modules in the silcd.conf. If they 
+are not compiled as modules remove the module paths from the ciphers and 
+hash functions from the silcd.conf, so that the server use the builtin 
+ciphers. Then try connecting to the server again. It is also possible 
+that the client IS proposing some ciphers that your server does not support.
+<br />&nbsp;<br />
+
+<a name="f4_60"></a>
+<samp class="highlight">Q: Why SILC server runs on privileged port 706?</samp><br />
+A: Ports 706/tcp and 706/udp have been assigned for the SILC protocol by
+<a href="http://www.iana.org" class="normal">IANA</a>. Server on the network
+listening above privileged ports (&gt;1023) SHOULD NOT be trusted as it could
+have been set up by untrusted party. The server normally drops root privileges
+after startup and then run as user previously defined in silcd.conf.
+<br />&nbsp;<br />
+
+<a name="f4_70"></a>
+<samp class="highlight">Q: I see [Unknown] in the log file, what does it mean?</samp><br />
+A: You can see in the log file for example: <samp class="highlight">
+[Info] Closing connection 192.168.78.139:3214 [Unknown]</samp>.  The [Unknown]
+means that the connection was not authenticated yet, and it is not known
+whether the connection was a client, server or router.  There will appear
+[Client], [Server] or [Router] if the connection is authenticated at that
+point.
+<br />&nbsp;<br />
+
+<a name="f4_80"></a>
+<samp class="highlight">Q: How can I generate a new server key pair?</samp><br />
+A: You can generate a new key pair using the silcd command with the -C 
+option.  When SILC Server is installed a key pair is generated 
+automatically for you.  However, it is suggested that you check the 
+information found in that key and generate a new key pair if the 
+information is incorrect.  You can check the information of your public 
+key by giving command: <samp class="highlight">silc -S file.pub</samp>.
+<br />&nbsp;<br />
+If you want to generate a new key pair then you can give for example 
+command: <samp class="highlight">
+silcd -C . --identifier="UN=silc-oper, HN=silc.silcnet.org, RN=SILC Router 
+Admin, E=silc-oper@silcnet.org, O=SILC Project, C=SK"</samp>. This will 
+create the key pair to current directory, with the specified identifier.  
+Please, give the --help option to the silcd to see usage help for the -C 
+and --identifier options.
+<br />&nbsp;<br />
+
+
+<a name="f5_0"></a><br />
+<b>5. Toolkit Questions</b><br />&nbsp;<br />
+
+<a name="f5_10"></a>
+<samp class="highlight">Q: What is SILC Toolkit?</samp><br />
+A: SILC Toolkit is a package intended for software developers who would 
+like to develope their own SILC based applications or help in the 
+development of the SILC. The Toolkit includes SILC Protocol Core library, 
+SILC Crypto library, SILC Key Exchange (SKE) library, SILC Math 
+library, SILC Modules (SIM) library, SILC Utility library, SILC Client 
+library and few other libraries. 
+<br />&nbsp;<br />
+
+<a name="f5_20"></a>
+<samp class="highlight">Q: Is the SILC Toolkit Reference Manual Available?</samp><br />
+A: Yes, partially completed reference manual is available in the Toolkit 
+releases as HTML package and they are available from the silcnet.org 
+website as well at the <a href="?page=docs" class="normal">documentation </a> page.
+<br />&nbsp;<br />
+
+<a name="f5_30"></a>
+<samp class="highlight">Q: How do I compile the Toolkit on Unix?</samp><br />
+A: You should read the INSTALL file from the package and follow its 
+instructions.  The compilation on Unix is as simple as compiling any other 
+SILC package.  Give, `./configure' command and then `make' command.
+<br />&nbsp;<br />
+
+<a name="f5_40"></a>
+<samp class="highlight">Q: How do I compile the Toolkit on Win32?</samp><br />
+A: We have prepared instructions to compile the Toolkit on Win32 in the 
+Toolkit package.  Please, read the README.WIN32 file from the package for 
+detailed instructions how to compile the Toolkit for Cygwin, MinGW and 
+native Win32 systems.  We have also prepared ready MSVC++ Workspace files 
+in the win32/ directory in the package that will compile automatically 
+the Toolkit.
+<br />&nbsp;<br />
+
+<a name="f5_50"></a>
+<samp class="highlight">Q: Does the Toolkit package include any sample code?</samp><br />
+A: Yes, naturally.  It includes sample codes for two different SILC Client 
+implementations, and SILC Server.  The silcer/ directory includes a simple
+GUI client based on GTK--, and Win32 samples are included in the win32/
+directory, for simple client.
+<br />&nbsp;<br />
diff --git a/public_html/html/features.php b/public_html/html/features.php
new file mode 100644 (file)
index 0000000..e09b683
--- /dev/null
@@ -0,0 +1,90 @@
+&nbsp;<br />
+<b><big>Features</big></b>
+<br />&nbsp;<br />
+- Normal conferencing services such as private messages, channels, 
+channel messages, etc. All traffic is secured and authenticated.
+
+<br />&nbsp;<br />
+- No unique nicknames. There can be same nicknames in SILC without 
+collisions. SILC has unique Client ID's, Server ID's and Channel ID's to 
+assure that there are no collisions. The maximum length of the nickname 
+is 128 characters. The maximum length of the channel name is 256 characters.
+
+<br />&nbsp;<br />
+- Channels can have channel operators and a channel founder which is the 
+client who created the channel. Channel founder privileges supersedes the 
+channel operator privileges. Also, channel founder privileges may be 
+regained even if the founder leaves the channel. The requirement for this 
+is that the client is connected to the same server it was originally 
+connected. The channel founder cannot be removed (kicked) from the 
+channel using force.
+
+<br />&nbsp;<br />
+- Channel messages are protected by channel key, generated by the server. 
+The key is re-generated once in an hour. It is possible to set a private 
+key for the channel so that even the servers does not know the key. 
+Actually, it is possible to set several private keys so that only 
+specific users on the channel may decrypt some specific messages. Adding 
+the private key significantly increases the security as nobody else but 
+the users on the channel know the key.
+
+<br />&nbsp;<br />
+- Private messages are protected using session keys, generated when 
+connecting to the server. This means that the private messages are 
+decrypted and re-encrypted enroute to the true receiver of the message. 
+However, it is possible to set a private key between two clients and 
+protect the private messages with that key. In this case no server 
+enroute can decrypt the message since they don't have the key. The SILC 
+protocol provides an automatic key negotiation between two clients using 
+the SKE protocol. This makes it very easy to negotiate a shared secret 
+key with another client in the network.
+
+<br />&nbsp;<br />
+- All the other traffic, like commands between client and the server are 
+protected using the session keys. Session keys are re-generated once in 
+an hour. The re-key may be done with or without the PFS (Perfect Forward 
+Secrecy).
+
+<br />&nbsp;<br />
+- Secure key exchange and authentication protocol. SILC Key Exchange 
+(SKE) protocol provides key material used in the SILC sessions in secure 
+manner. The protocol is immune for example to man-in-the-middle attacks 
+and is based on the Diffie-Hellman key exchange algorithm. The SILC 
+Authentication protocol provides strong authentication. Authentication 
+may be based on passphrase or public key (RSA) authentication. For 
+clients there is an option not to use authentication when connecting to 
+servers.
+
+<br />&nbsp;<br />
+- Supports secure file transferring between clients in the network.  SILC 
+use the SFTP as the main file transfer protocol.
+
+<br />&nbsp;<br />
+- All traffic is encrypted and authenticated using the best cryptographic 
+algorithms out there. Cipher keys are, by default, 256 bits in length and 
+public keys, by default, 1024 bits in length.
+
+<br />&nbsp;<br />
+- Supports the following ciphers: AES(Rijndael), Twofish, Blowfish, Mars, 
+Cast-256, RC5 and RC6. Supports the following hash functions: MD5 and 
+SHA1. Supports the following HMACs: hmac-sha1-96, hmac-md5-96, 
+hmac-sha1 and hmac-md5. Supports the PKCS #1 (RSA) for public key 
+cryptography. 
+
+<br />&nbsp;<br />
+- Supports data compression with GZIP to improve performance.
+
+<br />&nbsp;<br />
+- Supports SOCKS4 and SOCKS5 firewall traversal protocols.
+
+<br />&nbsp;<br />
+- SIM (SILC Module) support. Support for loading of shared objects at 
+run-time that provides new and extended features to both SILC client and 
+server. These can provide extra ciphers and extra features to the software.
+
+<br />&nbsp;<br />
+- SILC client can be installed and used without root privileges.
+
+<br />&nbsp;<br />
+- SILC client can be configured by system wide configuration files but 
+with user specific configuration files as well.
diff --git a/public_html/html/help.php b/public_html/html/help.php
new file mode 100644 (file)
index 0000000..a638794
--- /dev/null
@@ -0,0 +1,67 @@
+&nbsp;<br />
+<b><big>Need Help?</big></b>
+<br />&nbsp;<br />
+Are you confused with SILC? Don't quite know where to get some more 
+information? If you answer Yes, then this is the door you're looking for.  
+This page includes introduction to the help resources for SILC Network. 
+There are many places where you can get more information and help 
+regarding SILC in general, using SILC Network, developing SILC and much 
+more.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Getting Started with SILC</b>
+<br />&nbsp;<br />
+The very first thing you need to do to get online with SILC is to download 
+the SILC client.  Go to the <a href="?page=download" class="normal">download
+page</a>, and download the latest client.  There are several binary 
+packages available if you do not want to compile the client by yourself.   
+After you have your very own SILC client downloaded then locate the SILC 
+server nearest to you.  Please see our <a href="?page=servers" 
+class="normal">server list</a>, for all servers in the SILC Network.  You 
+can also directly connect to the <samp 
+class="highlight">silc.silcnet.org</samp> server if you like.
+<br />&nbsp;<br />
+You most likely have questions about SILC after you start using it.  The 
+best place to look for questions and answers is the <a 
+href="?page=faq" class="normal">SILC FAQ</a>. Please read it before you 
+start asking more questions.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Getting Help Online</b>
+<br />&nbsp;<br />
+The best and quickest way to get assistance is to get online on SILC 
+Network.  If you have questions, or just want to chat about SILC you can 
+join the <samp class="highlight">#silc</samp> channel. This friendly 
+channel includes people who are interested in SILC and ready to answer 
+questions assuming they know the answers.
+<br />&nbsp;<br />
+Give the command <samp class="highlight">/join #silc</samp> to join the 
+channel. Please introduce yourself before asking the question, and wait 
+patiently for your answer.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Getting Help On This Website</b>
+<br />&nbsp;<br />
+Reading documentation on SILC is also a good way to learn about SILC.  See 
+the documentation available in the <a href="?page=docs" 
+class="normal">documentation page</a> for more information.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Getting Help via Mailing Lists</b>
+<br />&nbsp;<br />
+There is currently one mailing list called silc-devel.  This is 
+general purpose mailing list that includes conversation on SILC 
+development, announcements of new releases and provides help for anyone 
+who ask questions.  You can subscribe to the mailist list on the <a 
+href="?page=lists" class="normal">lists page</a>, and read the archives of 
+the mailing list at <a 
+href="http://www.geocrawler.com/redir-sf.php3?list=silc-devel" 
+class="normal">silc-devel archives</a>.  You can send email to the mailing 
+list by sending it to <samp class="highlight">
+silc-devel@lists.sourceforge.net</samp> email address.
+
+<br />&nbsp;<br />
+Before you go ahead and start sending questions there is a nice document 
+about <a href="http://www.tuxedo.org/~esr/faqs/smart-questions.html" 
+class="normal">How To Ask Questions The Smart Way</a> by Eric S. Raymond, 
+a document you may want to read.
diff --git a/public_html/html/history.php b/public_html/html/history.php
new file mode 100644 (file)
index 0000000..3a454ae
--- /dev/null
@@ -0,0 +1,32 @@
+&nbsp;<br />
+<b><big>History</big></b>
+<br />&nbsp;<br />
+SILC was released in the summer 2000 to the public, but the idea and the 
+protocol itself is quite old. The SILC was designed by Pekka Riikonen in 
+the year 1996 and first lines of codes were written in the early 1997. The 
+SILC has been rewritten three times since its very first version in 1997.  
+The first version included SILC client, very preliminary SILC server, RSA 
+implementation and 3DES implementation. The server actually was not 
+usable but the client looked pretty much the same as the first client 
+released in the summer 2000. The first version had also random number 
+generator which were based on the SSH's random number generator. The 
+current RNG is based on the first RNG but has been rewritten twice since 
+the first version.
+<br />&nbsp;<br />
+The development of SILC was suspended in 1997 when Pekka got busy at 
+school and in work. The pause laster several months. The development 
+resumed in 1998 when Juha Räsänen and Pekka implemented the ElGamal 
+algorithm. However, for the same reasons as previously the development 
+stopped again, and was resumed again later in 1998 by doing rewrite of 
+ther SILC in C++. This was obviously a mistake but at that time it seemed 
+like a good idea. Again, in the winter 1999 the development suspended when 
+Pekka got busy writing his thesis and was forced to stop the development.
+<br />&nbsp;<br />
+Later, in 1999, it was decided that this time SILC will be rewritten from 
+scratch in the right way. C++ was obviously a bad choice so plain C 
+language was selected again. The protocol itself faced some rework by 
+redesigning some core parts of the protocol. The protocol was also fully 
+documented and the protocol specifications were submitted to the IETF.  
+The result of this development effort is the release now in public. Since 
+the release in the summer 2000 several other people have contributed to 
+the project as well. And, the development continues.
diff --git a/public_html/html/install.php b/public_html/html/install.php
new file mode 100644 (file)
index 0000000..0ef0102
--- /dev/null
@@ -0,0 +1,17 @@
+&nbsp;<br />
+<tt class="normal">
+<?php
+
+if (Is_Readable("docs/INSTALL")) {
+  $fp = FOpen("docs/INSTALL", "r");
+
+  while($line = FGets($fp, 255)) {
+    $newline = Ereg_Replace("^[ ]{2,4}","&nbsp;&nbsp;",$line);
+    $line = Ereg_Replace("^([\t]|[ ][\t])","&nbsp;&nbsp;&nbsp;&nbsp;",$newline);
+    printf("%s", nl2br($line));
+  }
+
+  FClose($fp);
+  }
+?>
+</tt>
diff --git a/public_html/html/links.php b/public_html/html/links.php
new file mode 100644 (file)
index 0000000..4129360
--- /dev/null
@@ -0,0 +1,42 @@
+&nbsp;<br />
+<b><big>Links</big></b>
+<br />&nbsp;<br />
+List of links related to Secure Internet Live Conferencing protocol, 
+security or just otherwise related. If you have a link that should be 
+listed here please send email to <a href="mailto:webmaster at silcnet.org" 
+class="normal"> webmaster@silcnet.org</a>.
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Other SILC Networks</b>
+<br />&nbsp;<br />
+- <a href="http://www.so36.net" class="normal">www.so36.net</a>, has a 
+small SILC network available for free use
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Other Chat Protocols</b>
+<br />&nbsp;<br />
+- <a href="http://www.irc.org" class="normal">www.irc.org</a>, a centre 
+for Internet Relay Chat protocol<br />
+- <a href="http://jabber.org" class="normal">jabber.org</a>, Open Source
+Instant Messaging protocol using XML<br />
+- <a href="http://www.gale.org" class="normal">www.gale.org</a>, Another 
+Open Source Instant Messaging protocol
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Organizations</b>
+<br />&nbsp;<br />
+- <a href="http://www.ietf.org" class="normal">www.ietf.org</a>, 
+Internet Engineering Task Force<br />
+- <a href="http://www.iana.org" class="normal">www.iana.org</a>, 
+Internet Assigned Numbers Authority<br />
+- <a href="http://www.iacr.org" class="normal">www.iacr.org</a>, 
+International Association for Cryptologic Research
+
+<br />&nbsp;<br />&nbsp;<br />
+<b>Security & Cryptography</b>
+<br />&nbsp;<br />
+- <a href="http://www.ssh.com/tech/crypto/" class="normal"> Cryptography 
+A-2-Z</a>, a comprehensive introduction to cryptography<br />
+- <a href="http://www.cacr.math.uwaterloo.ca/hac/" class="normal">
+The Hand Book of Applied Cryptography</a>, Free Edition of the book that 
+deals with cryptography, and is mathematically oriented
diff --git a/public_html/html/lists.php b/public_html/html/lists.php
new file mode 100644 (file)
index 0000000..498afd3
--- /dev/null
@@ -0,0 +1,54 @@
+&nbsp;<br />
+<b><big>Public SILC Mailing Lists</big></b>
+<br />&nbsp;<br />
+There is currently one mailing list available. The mailing list is the
+main SILC development mailing list. It is currently quite low volume so
+you can safely subscribe on to it. To send email to the list the email 
+must be destined to: <a href="mailto:silc-devel at 
+lists.sourceforge.net" class="normal">silc-devel@lists.sourceforge.net</a> address.
+<br />&nbsp;<br />
+To see prior postings to the list, browse the <a href="http://www.geocrawler.com/redir-sf.php3?list=silc-devel" class="normal">silc-devel archives</a>.
+<br />&nbsp;<br />
+
+<b>Subscribing to silc-devel</b>
+<br />&nbsp;<br />
+To subscribe to silc-devel mailing list send email to the following 
+address:
+<br />
+<a href="mailto:silc-devel-request@lists.sourceforge.net" class="normal">
+silc-devel-request@lists.sourceforge.net</a>, and add the following
+line to the body of the email.
+<br />&nbsp;<br />
+subscribe yourpassword
+<br />&nbsp;<br />
+You must set a password that you can use to modify your settings. It must 
+also be provided if you will unsubscribe from the list. The email address 
+you are using to send the email will be added to the mailing list. You 
+will receive a confirmation email to which you must reply in order to 
+complete the subscribing process.
+<br />&nbsp;<br />
+You can also use the following link to do the subscribing if you want:
+<br />
+<a href="http://lists.sourceforge.net/lists/listinfo/silc-devel" class="normal">
+subscribe to silc-devel</a>
+<br />&nbsp;<br />
+
+<b>Unsubscribing from silc-devel</b>
+<br />&nbsp;<br />
+To unsubcribe from the silc-devel mailing list send email to the following 
+address:
+<br />
+<a href="mailto:silc-devel-request@lists.sourceforge.net" class="normal">
+silc-devel-request@lists.sourceforge.net</a>, and add the following
+line to the body of the email.
+<br />&nbsp;<br />
+unsubscribe yourpassword [address]
+<br />&nbsp;<br />
+You must give the password you set in the subscribing process. If you are 
+unsubscribing from different address you must give the email address too.
+<br />&nbsp;<br />
+You can also use the following link to do the unsubscribing if you want:
+<br />
+<a href="http://lists.sourceforge.net/lists/listinfo/silc-devel" class="normal">
+unsubscribe from silc-devel</a>
+<br />&nbsp;<br />
diff --git a/public_html/html/mirrors.php b/public_html/html/mirrors.php
new file mode 100644 (file)
index 0000000..8c22719
--- /dev/null
@@ -0,0 +1,42 @@
+&nbsp;<br />
+<b><big>Mirrors</big></b>
+<br />&nbsp;<br />
+Mirrors synchronize once a day. Currently available mirrors:
+<br />&nbsp;<br />
+
+<a href="http://silcnet.org/" class="normal">Slovakia</a> (main site)
+<br />
+&nbsp; - <a href="http://www.silcnet.org/" class="normal">www site</a>, <a href="ftp://ftp.silcnet.org/pub/silc/" class="normal">ftp archive</a>, <a href="http://ftp.silcnet.org/" class="normal">ftp archive over http</a>, <a href="?page=cvs" class="normal">anonymous cvs</a>, and <a href="http://cvs.silcnet.org/" class="normal">cvs web</a><br />
+&nbsp; - 10Mbps connection, NEXTRA <br />
+&nbsp; - provided by <a href="mailto:salo at silcnet.org" class="normal">Lubomir Sedlacik (salo)</a>
+<br />&nbsp;<br />
+
+<a href="http://au.silcnet.org/" class="normal">Australia</a>
+<br />
+&nbsp; - <a href="http://www.au.silcnet.org" class="normal">www site</a>, <a href="ftp://ftp.au.silcnet.org/pub/silcnet/" class="normal">ftp archive</a> and <a href="http://www.planetmirror.com/pub/silcnet/" class="normal">ftp archive over http</a><br />
+&nbsp; - 155Mbps connection, SCCN <br />
+&nbsp; - provided by <a href="mailto:jason at planetmirror.com" class="normal">Jason Andrade</a>, <a href="http://planetmirror.com" class="normal">Planet Mirror</a>
+<br />&nbsp;<br />
+
+Australia 2
+<br />
+&nbsp; - <a href="ftp://ftp.wiretapped.net/pub/security/network-security/silc/" class="normal">ftp</a> and <a href="http://the.wiretapped.net/security/network-security/silc/" class="normal">ftp archive over http</a><br />
+&nbsp; - 10Mbps, Connect<br />
+&nbsp; - provided by <a href="http://www.wiretapped.net" class="normal">Wiretapped</a>
+<br />&nbsp;<br />
+
+Netherlands
+<br />
+&nbsp; - <a href="http://munitions.vipul.net/software/mirrors/silc/" class="normal">ftp archive over http</a><br />
+&nbsp; - 100Mbps, XS4ALL<br />
+&nbsp; - provided by <a href="mailto:mail at vipul.net" class="normal">Vipul Ved Prakash</a>, <a href="http://munitions.vipul.net" class="normal">Munitions</a>
+<br />&nbsp;<br />
+
+<a href="http://no.silcnet.org/" class="normal">Norway</a>
+<br />
+&nbsp; - <a href="http://www.no.silcnet.org" class="normal">www site</a> and <a href="ftp://ftp.no.silcnet.org/pub/silc/" class="normal">ftp archive</a><br />
+&nbsp; - 34Mbps connection <br />
+&nbsp; - provided by <a href="mailto:debolaz at debolaz.com" class="normal">Anders Nor Berle (Debolaz)</a>
+<br />&nbsp;<br />
+
+If you want to provide mirror of SILC webpage, FTP archive or CVS tree feel free to contact <a href="mailto: salo at silcnet.org" class="normal">me</a> for detailed instructions.
diff --git a/public_html/html/news.php b/public_html/html/news.php
new file mode 100644 (file)
index 0000000..e04b61e
--- /dev/null
@@ -0,0 +1,80 @@
+&nbsp;<br />
+<b><big>SILC Client <?php echo $Latest_Client; ?> Is Now Available!</big></b>
+<br /><small class="highlight"><?php echo $Date_Client ?></small>
+<br />&nbsp;<br />
+The new version <?php echo $Latest_Client; ?> of the SILC Client is available!
+Read the README and INSTALL files after downloading for instructions how 
+to compile and use the SILC. Report bugs to the 
+<a href="?page=lists" class="normal">SILC development mailing list</a>.
+<br />&nbsp;<br />
+Download: <a href="?page=download" class="normal">SILC Client <?php echo 
+$Latest_Client; ?> Version</a>
+<br />
+Changes: <a href="txt/changes-client.txt" class="normal">SILC Client <?php echo $Latest_Client; ?> 
+Changes</a>
+<br />
+Release notes: <a href="txt/release-client.txt" class="normal">SILC Client <?php echo $Latest_Client; ?> 
+Release Notes</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b><big>SILC Server <?php echo $Latest_Server; ?> Is Now Available!</big></b>
+<br /><small class="highlight"><?php echo $Date_Server ?></small>
+<br />&nbsp;<br />
+The new version <?php echo $Latest_Server; ?> of the SILC Server is available!
+Read the README and INSTALL files after downloading for instructions how 
+to compile and use the SILC. Report bugs to the 
+<a href="?page=lists" class="normal">SILC development mailing list</a>.
+<br />&nbsp;<br />
+People who is running SILC servers and are interested to get the server 
+linked to the new router on silc.silcnet.org contact 
+<a href="mailto:priikone at silcnet.org" class="normal">me</a> now.
+<br />&nbsp;<br />
+Download: <a href="?page=download" class="normal">SILC Server <?php echo 
+$Latest_Server; ?> Version</a>
+<br />
+Changes: <a href="txt/changes-server.txt" class="normal">SILC Server <?php echo $Latest_Server; ?> 
+Changes</a>
+<br />
+Release notes: <a href="txt/release-server.txt" class="normal">SILC 
+Server <?php echo $Latest_Server; ?> 
+Release Notes</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b><big>SILC Toolkit <?php echo $Latest_Toolkit; ?> Is Now Available!</big></b>
+<br /><small class="highlight"><?php echo $Date_Toolkit ?></small>
+<br />&nbsp;<br />
+The new version <?php echo $Latest_Toolkit; ?> of the SILC Toolkit is available!
+This package is intended for developers and programmers who would like to 
+create their own SILC applications or help in the development of the SILC 
+protocol.
+<br />&nbsp;<br />
+Download: <a href="?page=download" class="normal">SILC Toolkit <?php echo 
+$Latest_Toolkit; ?> Version</a>
+<br />
+Changes: <a href="txt/changes.txt" class="normal">SILC Toolkit <?php echo $Latest_Toolkit; ?> 
+Changes</a>
+<br />
+Release notes: <a href="txt/release-toolkit.txt" class="normal">SILC Toolkit <?php echo $Latest_Toolkit; ?> Release Notes</a>
+
+<br />&nbsp;<br />&nbsp;<br />
+
+<b><big>Try out SILC server at silc.silcnet.org</big></b>
+<br />&nbsp;<br />
+Feel free to connect to various SILC servers in the SILC Network.  The 
+network is still quite small but is growing all the time.  To connect 
+to the server type command <i>/server silc.silcnet.org</i>. You might also
+want to try out one of following servers:
+<br />&nbsp;<br />
+
+- <samp class="highlight">silc.silcnet.org (router)</samp> (Slovakia)<br />
+- <samp class="highlight">silc.ytti.fi</samp> (Finland)<br />
+- <samp class="highlight">silc.peelo.com</samp> (Finland)<br />
+- <samp class="highlight">silc.debolaz.com</samp> (Norway)<br />
+- <samp class="highlight">s.slash0.net</samp> (USA)<br />
+- <samp class="highlight">silc.silcnet.org on port 707</samp>
+
+<br />&nbsp;<br />
+There may be some action on channel #silc (unless everybody is sleeping) 
+so you might want to give command <i>/join #silc</i>.
diff --git a/public_html/html/servers.php b/public_html/html/servers.php
new file mode 100644 (file)
index 0000000..e30b2b0
--- /dev/null
@@ -0,0 +1,101 @@
+&nbsp;<br />
+<b><big>SILC Servers</big></b>
+<br />&nbsp;<br />
+Please refer to this SILC Servers list when you find the nearest server to 
+you.  You can also check the particular server's public key here.  When 
+you connect to a server for the very first time you will be prompted to 
+accept the server's public key.  You can then come here and verify the key 
+here.
+<br />&nbsp;<br />
+To connect to a server give the command <samp class="highlight">/server 
+servername</samp>.
+
+<br />&nbsp;<br />
+<table width="98%" cellspacing="2" cellpadding="2" border="0">
+ <tr>
+  <td class="title100" align="center"><b>Router</b></td>
+  <td class="title100" align="center"><b>Server</b></td>
+  <td class="title50" align="center"><b>Port</b></td>
+  <td class="title50" align="center"><b>Country</b></td>
+  <td class="title50" align="center"><b>Oper</b></td>
+  <td class="title50" align="center"><b>Public Key</b></td>
+  <td class="title50" align="center"><b>URL</b></td>
+ </tr>
+
+ <tr>
+  <td class="sources" align="center">&nbsp;silc.silcnet.org</td>
+  <td class="sources" align="center">&nbsp;</td>
+  <td class="sources" align="center">706</td>
+  <td class="sources" align="center">Slovakia</td>
+  <td class="sources" align="center">pekka</td>
+  <td class="sources" align="center"><a href="keys/serverkey_silc.silcnet.org_706.pub" class="normal">public key</a></td>
+  <td class="sources" align="center">N/A</td>
+ </tr>  
+
+ <tr>
+  <td class="sources" align="center">&nbsp;</td>
+  <td class="sources" align="center">&nbsp;silc.ytti.fi</td>
+  <td class="sources" align="center">706</td>
+  <td class="sources" align="center">Finland</td>
+  <td class="sources" align="center">ytti</td>
+  <td class="sources" align="center">N/A</td>
+  <td class="sources" align="center">N/A</td>
+ </tr>  
+
+ <tr>
+  <td class="sources" align="center">&nbsp;</td>
+  <td class="sources" align="center">&nbsp;silc.peelo.com</td>
+  <td class="sources" align="center">706</td>
+  <td class="sources" align="center">Finland</td>
+  <td class="sources" align="center">Toni</td>
+  <td class="sources" align="center">N/A</td>
+  <td class="sources" align="center">N/A</td>
+ </tr>  
+
+ <tr>
+  <td class="sources" align="center">&nbsp;</td>
+  <td class="sources" align="center">&nbsp;silc.debolaz.com</td>
+  <td class="sources" align="center">706</td>
+  <td class="sources" align="center">Norway</td>
+  <td class="sources" align="center">Debolaz</td>
+  <td class="sources" align="center">N/A</td>
+  <td class="sources" align="center">N/A</td>
+ </tr>  
+
+ <tr>
+  <td class="sources" align="center">&nbsp;</td>
+  <td class="sources" align="center">&nbsp;s.slash0.net</td>
+  <td class="sources" align="center">706</td>
+  <td class="sources" align="center">USA</td>
+  <td class="sources" align="center">next</td>
+  <td class="sources" align="center">N/A</td>
+  <td class="sources" align="center">N/A</td>
+ </tr>  
+
+ <tr>
+  <td class="sources" align="center">&nbsp;</td>
+  <td class="sources" align="center">&nbsp;silc.silcnet.org</td>
+  <td class="sources" align="center">707</td>
+  <td class="sources" align="center">Slovakia</td>
+  <td class="sources" align="center">pekka</td>
+  <td class="sources" align="center"><a href="keys/serverkey_silc.silcnet.org_707.pub" class="normal">public key</a></td>
+  <td class="sources" align="center">N/A</td>
+ </tr>  
+
+</table>
+
+<table width="98%" cellspacing="0" cellpadding="0" border="0">
+ <tr>
+  <td class="text">
+<br />&nbsp;<br />&nbsp;<br />
+<b>Linking to SILC Network</b>
+<br />&nbsp;<br />
+Would you like to link your server to the SILC Network? Please contact us 
+at <a href="mailto:server-links at silcnet.org" class="normal">
+server-links@silcnet.org</a>, and tell us about your server. It would be 
+nice if you already have existing user base in your server.
+
+  </td>
+ </tr> 
+</table>
+
diff --git a/public_html/html/todo.php b/public_html/html/todo.php
new file mode 100644 (file)
index 0000000..075aeae
--- /dev/null
@@ -0,0 +1,17 @@
+&nbsp;<br />
+<tt class="normal">
+<?php
+
+if (Is_Readable("txt/todo.txt")) {
+  $fp = FOpen("txt/todo.txt", "r");
+
+  while($line = FGets($fp, 255)) {
+    $newline = Ereg_Replace("^[ ]{2,4}","&nbsp;&nbsp;",$line);
+    $line = Ereg_Replace("^([\t]|[ ][\t])","&nbsp;&nbsp;&nbsp;&nbsp;",$newline);
+    printf("%s", nl2br($line));
+  }
+
+  FClose($fp);
+  }
+?>
+</tt>
diff --git a/public_html/html/whitepaper.php b/public_html/html/whitepaper.php
new file mode 100644 (file)
index 0000000..9b607a0
--- /dev/null
@@ -0,0 +1,947 @@
+<!-- This file is processed with html2ps to generate PostiScript. 
+     Do not edit the HTML syntax unless you know what you are doing. -->
+<html>
+<head>
+<title>SILC Protocol White Paper</title>
+<link rev=made href="mailto:priikone@silcnet.org">
+<meta name="Author" content="Pekka Riikonen - SILC Project">
+<meta name="Description"
+ content="SILC - Secure Internet Live Conferencing Protocol">
+<meta name="Created" content="Version 1.0.5 / 15 Jab 2002">
+</head>
+<body bgcolor="#ffffff">
+
+<font face="Helvetica">
+
+<font size="6"><b>SILC Protocol White Paper</b></font><br>
+<font size="2">Version 1.0.5 / 15 Jan 2002</font>
+
+<p>
+<h1>Introduction</h1>
+
+Chat protocols are very popular on the Internet.  They have actually
+been very popular since the very first chat protocols appeared on the net.
+The Internet Relay Chat (IRC) was one of the first chat protocols, and quickly
+gained the status of being the most popular chat on the net.  Today, IRC
+has several competitors from various other so called Instant Messaging (IM)
+protocols, such as ICQ.  However, all of these different chat protocols
+have something in common; they are all insecure.
+<p>
+
+The security is important feature in applications and protocols in 
+contemporary network environment.  The older chat protocols, however have
+failed to meet the growing security requirements on the Internet.
+It is not anymore enough to just provide services, like for example
+chat services. Now, they need to be secure services.
+<p>
+
+The Secure Internet Live Conferencing (SILC) protocol is a new generation
+chat protocol which provides full featured conferencing services, just
+like any other contemporary chat protocol provides.  In addition, it
+provides security by encrypting and authenticating the messages in
+the network.  The security has been the primary goal of the SILC protocol
+and the protocol has been designed from the day one security in mind.
+All packets and messages travelling in the SILC Network are always
+encrypted and authenticated.  The network topology is also different
+from for example IRC network.  The SILC network topology attempts to be
+more powerful and scalable than the IRC network.  The basic purpose
+of the SILC protocol is to provide secure conferencing services.
+<p>
+
+The SILC Protocol have been developed as Open Source project.  The
+protocol specifications are freely available and they have been submitted to
+the IETF.  The very first implementations of the protocol are also already
+available.
+
+<p><br>
+<h1>About This White Paper</h1>
+<p>
+The purpose of this white paper is to give short but deep enough introduction
+to the SILC Protocol.  The document describes the purpose of the protocol
+and how the protocol works in practice.  This document is intended for all
+audience.  This document should be easy to understand for non-technical
+person and still be detailed enough for technically oriented person.  See
+the section <a href="#terms">Terms and Abbreviations</a> for terms used
+in this document.
+<p>
+
+<p>
+(c) Copyright 2001 - 2002 Pekka Riikonen 
+(<a href="mailto:priikone at silcnet.org">priikone at silcnet.org</a>)
+<p>
+This document is free document; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.  This document is distributed in
+the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+
+<p><br>
+<h1>SILC Protocol</h1>
+<p>
+
+The Secure Internet Live Conferencing (SILC) protocol provides secure
+conferencing services over insecure network channel.  The SILC is IRC
+like protocol, however it does not support IRC.  Strong cryptographic
+methods are used to protect SILC packets inside the SILC network.  SILC
+provides all the common conferencing services like channels, channel
+messages, private messages, nicknames, various commands, and secure
+file transfer.  Difference to other chat protocol is in the design of
+the protocol.  The SILC protocol has been designed from the day one
+security in mind and it shows in the protocol design.
+<p>
+
+The packets and messages in the SILC network are always encrypted and
+authenticated.  It is not possible to send unencrypted messages in SILC
+at all.  This assures that end user cannot even accidently send unencrypted
+messages while thinking that it is encrypted.  This is one of the problems 
+of most of the other chat protocols that provide so called plugin encryption.
+They are not secure by default but try to provide security by applying
+external security protocol such as PGP or SSL over the insecure chat
+protocol.  In these cases the security is achieved usually by encrypting the
+data while key management, message authentication and other security issues
+may be left out, leaving the implementation vulnerable to various security
+problems.  The other problem is also that the external protocols tend to
+leave the network only partly secured; usually only two points in the
+network are secured with for example SSL.  While SSL does provide provable
+security it is not enough to provide security for a chat network as a whole.
+<p>
+
+SILC is secure in environment of mutual distrust between entities
+in the network.  It is possible to encrypt messages end to end, so that only
+the sender and the receiver is able to encrypt and decrypt messages.  It
+is also possible to send messages to group of users, so that only the
+specified group of users is able to encrypt and decrypt messages.  Many
+times the protocol use keys that are generated by the servers, so that
+if other external key exchange methods fail the network still remains
+encrypted.  However, it is always possible to negotiate and use locally
+generated keys to secure messages, so that the servers do not know the
+key.
+<p>
+
+Like so many other contemporary chat protocols, SILC too provides
+file transfer.  It is possible to transfer files securely between users
+in the SILC Network.  The actual file transfer stream is always sent
+outside the network peer to peer.  Before the file transfer is started
+a key exchange protocol is executed to negotiate file transfer session
+key.
+<p>
+
+The network topology is also different to various other chat protocols,
+like for example IRC.  IRC has tree style network, but SILC network can be
+described more as an hybrid ring-mesh network.  The routers in the network
+form a ring, but they can also have other direct routers (secondary routes)
+to other routers.  A router in the network is also called a cell, when it
+has multiple servers and clients connected to it.  The cell can also have
+backup routers in case the primary router becomes unresponsive.
+
+<p><br>
+<object data="silc_network.jpg" type="application/postscript">
+<img src="silc_network.png" alt="SILC Network" align="center" border"0">
+</object>
+<p><br>
+
+The diagram above illustrates a portion of the SILC network.  It shows
+two cells that both have several servers, and backup routers and several
+clients.  Clients can connect to server and routers if they want to.
+The following sections will describe the entities of the SILC Network
+in greater detail.
+<p>
+
+
+<p><br>
+<h2>Clients</h2>
+<p>
+
+A client is a piece of software connecting to SILC server.  The software
+is usually run by the end user, a real person that is.  The purpose of the
+clients is to provide the end user an interface to the SILC services.
+They are used to actually engage the conversations on the SILC Network,
+and they can be used to execute various SILC commands.
+<p>
+
+The clients are distinquished from other clients by unique Client ID.
+There cannot be multiple same Client IDs in the SILC Network at the same time.
+The end user, however does not use Client IDs.  The end users usually selects
+a preferred nickname they want to use, and identifies themself with that
+nickname to other users on the network.  The nicknames are not unique in
+the SILC Network.  There can be multiple same nicknames at the same time
+on the network.  The maximum length for the nickname is 128 characters.
+<p>
+
+Most of the other chat protocols have unique nicknames.  This is where SILC
+differs from most of the other chat protocols.  The purpose of this
+feature is to make IRC style nickname wars obsolete, as no one owns their
+nickname; there can always be somene else with the same nickname.
+<p>
+
+When client connects to the server the SILC Key Exchange (SKE) protocol and
+SILC Connection Authentication protocol are executed.  The result of the
+SKE protocol is the session key that the client and server use to secure
+their communication.  All commands, for example, that the client sends
+to the server are secured with the session key.  The session key expires
+periodically and the rekey process can be executed with or without the
+Perfect Forward Secrecy (PFS).  The connection authentication protocol is
+used to authenticate the client to the server.  The server may allow the
+client to connect without authentication, or it may require a passphrase or
+public key based (or certificates) authentication.
+
+
+<p><br>
+<h2>Servers</h2>
+<p>
+
+Servers forms the basis for the SILC Network, by providing a point to which
+clients may connect.  There are two kinds of servers in SILC; normal servers
+and router servers.  The next section describes the function of router
+server.
+<p>
+
+Normal servers connect to router server.  Normal servers cannot directly
+connect to other normal servers.  Messages that are destined outside the
+local server are always sent to the router for further routing.
+The clients usually connect to the normal server, however, clients may
+connect to router servers as well.  The SILC Network diagram above
+illustrates how normal servers connects to the router server.
+<p>
+
+The servers are distinquished by other servers in the network by unique
+Server ID.  There cannot be multiple same Server IDs in the SILC Network
+at the same time.  The servers keep track of local information.  It knows
+all locally connected clients and it knows all channels that its clients
+have joined.  However, it does not know any global information.  It
+usually does not keep track of global clients, however, it may cache
+that information if it was queried.  The reason for this is that the
+server does not need to keep global information up to date and thus
+makes the server faster (and in the end the entire network faster).
+They can always query the information from the router.
+<p>
+
+When server connects to its router the SILC Key Exchange (SKE) protocol
+and the SILC Connection Authentication protocol are executed, just like
+when client connects to server.  The SKE results in to the session key
+that is used to secure the communication between the server and the
+router.  The connection authentication protocol is used to authenticate
+the server to the router.  The authentication is always based in either 
+passphrase or public key (or certificates).
+
+
+<p><br>
+<h2>Routers</h2>
+<p>
+
+The router servers are servers that actually handles the message routing
+in the network.  They are, however also normal servers and they do accept
+client connections.  Each of the router in the network is called a cell.
+A cell can have only one active router and it may have several servers
+and several clients.  The cell, however may have backup routers that can
+take over the tasks of the primary router if it becomes unresponsive.
+The switch to the backup router should be transparent and only local
+connections to the primary router are lost.  Other connections in the
+cell are intact, and clients and servers merely experience some lag in
+the network connection during the switch to the backup router.
+<p>
+
+The normal server knows only local information.  Router server on the
+other hand knows local information and global information.  It considers
+the cell as local and outside cells as global.  It knows all the clients
+connected to the network, all created channels, and all routers and servers
+in the network.  The server may query the global information if it is needed.
+For example, when client sends WHOIS command, the server may query the
+information from the router.  If the router does not know all the details
+that the WHOIS command requires it can query the information from a router
+or a server which knows all the details.  It may then cache that information.
+<p>
+
+The primary purpose of the router server is to route the messages to
+local servers and local clients, and messages that are destined to outside
+the cell are routed to the primary route or some other secondary
+route if it is a faster route.  The routers in the network forms a ring.
+Each router has a primary route to other router in the network.  Finally
+the ring is closed by the last router using the first router in the
+network as its primary route.
+
+<p><br>
+<object data="silc_routers.jpg" type="application/postscript">
+<img src="silc_routers.png" alt="SILC Routers" align="center" border"0">
+</object>
+<p><br>
+
+The diagram above illustrates how the routers forms a ring in the network.
+A router may have several secondary routes which it may use when it
+routes the packets.
+<p>
+
+When routers connect to its primary router the SKE and the SILC Connection
+Authentication protocols are executed just like when normal server connects
+to its router.  The session key is used to secure the communication between
+the routers.  All the secondary routes also have their own session keys.
+
+
+<p><br>
+<h1>SILC Packet Protocol</h1>
+<p>
+
+The basis of SILC protocol relies in the SILC packets and they are with
+out a doubt the most important part of the protocol.  The SILC Packet 
+protocol is a binary packet protocol.  The protocol provides secure
+binary packets and assures that the contents of the packets are secured
+and authenticated.
+<p>
+
+Packets are used in the SILC protocol all the time to send for example
+channel messages, private messages, commands and other information.  All
+packets in SILC network are always encrypted and their integrity is
+assured by computed Message Authentication Codes (MAC).  The protocol
+defines several packet types and packet payloads.  Each packet type
+usually has a specific packet payload that actually defines the contents
+of the packet.  Hence, the actual data in the packet is the packet payload 
+defined in the protocol.
+
+<p><br>
+<object data="silc_packet.jpg" type="application/postscript">
+<img src="silc_packet.png" alt="Typical SILC Packet" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above illustrates the SILC packet is constructed from the
+SILC Packet Header that is included in all SILC packets, data area that
+includes the packet payloads, and MAC area which assures the integrity of the
+packet.  Entire SILC packet is always encrypted, except for the MAC area
+which is never encrypted.  The encryption process and the key used,
+however depends on the packet payload.  Some of the payloads are encrypted
+with the session key and some are encrypted with other keys, for example
+with channel message keys.  The SILC Packet Header is always encrypted with
+the session key.  The MAC is computed from the SILC Packet Header and the
+data area before encrypting the packet.
+
+
+<p><br>
+<h1>SILC Key Exchange Protocol</h1>
+<p>
+
+SILC Key Exchange Protocol (SKE) is used to exchange shared secret
+between connecting entities.  The result of this protocol is a key material
+used to secure the communication channel.  This protocol is executed when,
+for example client connects to server.  It is also executed when server
+connects to router.  And, there is no reason why it could not be executed
+between two clients too, if two clients would need to create secret key.
+The purpose of the SKE protocol is to create session keys to be used
+in current SILC session.  The SKE is based on the Diffie-Hellman key
+exchange algorithm, and is immune to for example man-in-the-middle attacks 
+by using digital signatures.
+<p>
+
+This is the first protocol that is executed when creating connection to,
+for example SILC server.  All the other protocols are always executed
+after this protocol.  This way all the other protocols are secured since
+the SKE creates the session key that is used to secure all subsequent
+packets.  The session keys created in the SKE are valid only for some
+period of time (usually an hour) or at most until the session ends.
+The rekey process can be executed with or without the Perfect Forward
+Secrecy (PFS).
+<p>
+
+The security properties that are used in the SILC session are also
+negotiated during the SKE.  The protocol has initiator and responder.
+The initator is the one who starts the SKE negotiation and responder is
+the one who receives the SKE negotiation.  When the protocol is started
+initiator sends a list of security properties that it supports.  The
+responder then selects the security properties it supports and sends
+its reply to the initiator.  The security properties includes ciphers,
+hash functions, public key algorithms, HMAC functions and other
+security properties.  The responder can always choose the properties
+it supports.
+<p>
+
+After the security properties are selected the protocol continues
+by performing the Diffie-Hellman key exchange algorithm.  At the same
+time the intiator and responder also sends their public keys or
+certificates to each other.  The initiator and responder also computes a
+signature that the other party will verify.  By default the protocol is
+executed in so called mutual authentication mode, where both of the
+parties computes a signature which are verified by each other
+independently.  This way both of the parties will have prove the
+posession of the private key to the public key they are providing
+in the protocol.  If any of the phases of the protocol are to fail the
+connection is closed immeadiately.
+<p>
+
+The public key or certificate that is received during the SKE protocol
+must be verified.  If it is not verified it would be possible to 
+execute a man-in-the-middle attack against the SKE protocol.  If
+certificates are used they can be verified by a third party Certification
+Authority (CA).  Verifying a public key requires either confirming
+a fingerprint of the public key over phone or email, or the server
+can for example publish the fingerprint (and the public key) on some 
+website.  In real life systems accepting the public key without
+verification, however is often desired.  In many security protocols,
+such as in SSH2, the public key is accepted without verification
+in the first time when the connection is created.  The public key is
+then cached on local hard disk.  When connecting next time to the
+server the public key on local cache is verified against the public
+key server sent.  In real life this works most of the time.  However,
+if client (or server) cannot trust this, it must find some other way
+to verify the received public key or certificate.
+
+
+<p><br>
+<h1>SILC Connection Authentication Protocol</h1>
+<p>
+
+Purpose of SILC Connection Authentication protocol is to authenticate the
+connecting party with server or router.  This protocol is executed when
+for example client connects to server.  It is also executed when server
+connects to router.  Its other purpose is to provide information for the
+server about which type of connection it is.  The type of the connection
+defines whether it is client, server or router.  If it is client then
+the server will create a new Client ID for the client.  If it is server
+then it will except the server to send its Server ID.  Server IDs are
+created by the servers and routers itself.
+<p>
+
+Since the SILC Connection Authentication protocol is always executed after
+the SKE protocol, session keys has been established already.  This means
+that all packets sent in the connection authentication protocol are encrypted 
+and authenticated.
+<p>
+
+The authentication may be based either in passphrase or public key
+encryption.  It is also possible to not require authentication at all.
+If the authentication is based to passphrase the passphrase is sent
+to the server.  As the packet sent by, for example client, is entirely
+encrypted it is safe to send the passphrase inside the packet.
+<p>
+
+If the authentication is based to public key then, for example the client, 
+signs data with its private key and sends it to the server.  The server
+then verifies this signature by using the client's public key.  The
+packet is also encrypted in the case of public key authentication.
+<p>
+
+If the authentication is to fail the connection to the server or router
+will be refused.  If it is successful the connection is granted.  After
+this the client is ready to communicate in the SILC Network.
+
+
+<p><br>
+<h1>Channels</h1>
+<p>
+
+A channel is a named group of one or more clients which will all receive
+messages addressed to that channel.  The channel is created when first
+client joins to it, and the channel ceases to exist when the last client
+leaves it.  When channel exists, any client can reference it using the 
+name of the channel.  Channel is a place where group of people can engage
+conversation.
+<p>
+
+Channel names are unique in the SILC Network.  There cannot be multiple
+same channels in the network at the same time.  However, channel has also
+a Channel ID which is actually used to reference the channel in the
+SILC Network.  The maximum length for the channel name is 256 characters.
+<p>
+
+Channels can have operators that can administrate the channel and operate
+all of its modes.  There are two types of operators on the channel:
+channel founder and channel operator.
+<p>
+
+The channel founder is the client which created the channel.  Channel
+founder is channel operator with some more privileges.  Channel founder
+can operate all of the channel's modes.  Furthermore, channel founder
+privileges cannot be removed by any other operator on channel and channel
+founder cannot be removed from the channel by force.  It is also possible
+for the channel founder to regain its privileges at later time, even if
+they have left the channel.
+<p>
+
+Channel operator is operator that can operate most of the channel's
+modes and administrate the channel.  However, it cannot operate all
+modes which are strictly reserved for channel founder.  Channel operator
+is, however able to adminstrate the channel, set some modes on the
+channel, remove a badly behaving client from the channel, and promote
+other clients to become channel operator.
+
+
+<p><br>
+<h2>Channel Message Delivery</h2>
+<p>
+
+All clients that have joined the channel can send messages to the channel.
+All channel messages are secured and authenticated by channel key.  The
+channel key is generated by the server when the channel is created,
+a client joins the channel, or a client leaves the channel.  The channel
+key is also regenerated periodically.  The reason for the regeneration
+of channel key everytime someone joins or leaves the channel is that
+it prevents new clients joining the channel, and old clients leaving the
+channel, to encrypt or decrypt old or new messages.  They can encrypt
+and decrypt channel messages only when they have joined on the channel.
+<p>
+
+Channel keys are cell specific in the SILC Network.  Each cell that
+have clients joined on a particular channel have also own key for the
+channel.  That key is not shared by other cells in the network.  Inside
+the cell the channel key is known by the router and all servers that
+have clients on the channel and all clients that have joined the channel.
+
+<p><br>
+<object data="silc_channel.jpg" type="application/postscript">
+<img src="silc_channel.png" alt="Channel Message Delivery" align="center" border"0">
+</object>
+<p><br>
+
+The diagram above illustrates typical delivery of channel messages inside
+a cell and between two cells.  Both of the cells have their own channel
+key.  Both cells knows all clients joined on the channel.  When message
+is sent to the channel by an client, it is encrypted with the current
+channel key in that cell.  The servers and the router in the local cell
+then routes the message to all local clients who have joined the channel.
+If the channel has clients that belong to other cell in the network the
+router will route the channel message to that cell.  When channel
+messages are sent between routers they are first decrypted with the
+current channel key, and then re-encrypted with the session key shared
+between the two routers.  The router who receives the channel message
+then decrypts it with the session and re-encrypts it with the
+current channel key in that cell.  It then distributes the channel message
+to all clients on the channel.  The clients who have joined the channel
+always knows the current channel key and can decrypt all channel messages
+they receive.  Note that normal servers in the SILC network never decrypt
+the channel messages even though the have the key.  There is no reason
+for servers to decrypt the message.  The router decrypts the message
+only when sending it between two routers.
+<p>
+
+This method of channel message delivery is the default way to send
+channel messages in the SILC Network.  However, this is not perfect
+solution on all circumstances.  If the clients joined on a particular
+channel cannot trust, or do not want to trust the servers and routers
+in the SILC Network they can consider the fact, that servers and routers
+knows the channel key is actually a breach of security.
+<p>
+
+If the clients on the other hand can trust their servers and routers
+in the SILC Network this is the recommended way of sending channel
+messages.  This method is the simplest method for end user since it
+does not require any special settings before engaging the conversation
+on the channel.  The client merely joins the channel, receives the
+channel key from the server and can start the conversation on the
+channel.
+<p>
+
+In addition of encrypting channel messages it also possible to digitally
+sign all sent channel messages.  The receiver could then verify the
+signature of each of the message using the sender's public key.
+
+
+<p><br>
+<h2>Channel Message Delivery With Channel Private Key</h2>
+<p>
+
+If the clients cannot trust the servers and routers in the SILC Network
+they should not use the default way of sending the channel messages.
+Instead, they should use channel private keys to encrypt and decrypt
+the channel messages.  Channel private keys are keys that are known
+only by the clients who have joined the channel.  Servers and
+routers do not know the key and cannot decrypt the messages.  When
+message is sent between two routers they are merely re-encrypted with
+the session key but not decrypted since the router do not have the
+key to do that.
+<p>
+
+The clients who have joined the channel must first agree on the channel
+private key they are going to use.  The key may generally be anything.
+It may be a passphrase or a random string, or the key may negotiated
+using some key exchange protocol which provides negotiating the
+key for multiple clients at the same time.
+<p>
+
+As the channel private key is actually entirely local setting in the
+client, it is possible to set several channel private keys for one
+channel.  It is possible to have multiple channel private keys that
+are not known by all channel members.  When encrypting messages with
+one channel private key only the clients who have that key can decrypt
+the message.  The other key could be shared for example by all clients
+on the channel and thus all clients can decrypt messages encrypted with
+that key.  In this way it is actually possible to have a private group
+conversation inside the channel while having global conversation at the
+same time.
+
+
+<p><br>
+<h1>Private Messages</h1>
+<p>
+Private messages are messages that are sent from one client to another 
+through the SILC Network.  They are private because they are not sent to
+anyone else except to the true receiver of the message.  Private messages
+can be used to engage private conversation with another client if channels
+are not desired.
+<p>
+
+As all messages in SILC the private message are also encrypted and
+authenticated.  There are several ways to secure private messages.  By
+default private messages are encrypted using the session keys established
+in the SKE protocol.  It is also possible to negotiate a private message
+key between the two clients and encrypt the messages with that key.  It
+is even possible to encrypt the messages with public key cryptosystem,
+if desired.  The next sections will describe all these private message
+delivery methods.
+
+<p>
+The SILC protocol provides these three methods of delivering private messages
+because none of the methods alone can satisfy the security requirements
+of all people.  The end user should decide the acceptable level of risk,
+the required level of security and other security and usability aspects when
+deciding what way of sending private message suites for them.
+<p>
+
+In addition of encrypting private messages it also possible to digitally
+sign all sent private messages.  The receiver could then verify the
+signature of each of the message using the sender's public key.
+
+
+<p><br>
+<h2>Private Message Delivery With Session Keys</h2>
+<p>
+Sending private messages are by default secured with session keys established
+in the SKE protocol.  This means that the private message is always encrypted
+with the session key of the next receiver of the message enroute to the 
+receiving client.  This also means that the message is decrypted and
+re-encrypted everytime it is sent further to the receiving client.
+
+<p><br>
+<object data="silc_priv1.jpg" type="application/postscript">
+<img src="silc_priv1.png" alt="Basic Private Message Delivery" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above shows the private messages sent by Client A to the
+Client B travels through the SILC Network and is always decrypted and
+re-encrypted with the session key of the next receiver.  The Client B then
+finally decrypts the private messages that is encrypted with the session
+key shared between the Client B and the Server Y.
+<p>
+
+This way of securing private messages is not perfect and cannot be used
+in all circumstances.  If the clients having the conversation cannot trust
+the servers and routers in the SILC Network they should not send private
+messages that are secured in this manner.  Messages secured in this manner
+can be decrypted by the servers and routers that the clients may consider
+to be untrusted.
+<p>
+
+If the clients on the other hand trust the servers and routers in their 
+SILC Network, or they do not care that servers can decrypt their messages,
+sending private messages in this way is very simple from client's point
+of view.  For servers and routers this of course means that they need
+to decrypt and re-encrypt each private message.  Since this way of securing
+private message cannot be used at all times the SILC protocol provides
+other ways of securing private messages.
+
+
+<p><br>
+<h2>Private Message Delivery With Private Message Key</h2>
+<p>
+Private messages can be secured with private message key as well.  This
+key is known only by the sender of the message and the receiver of the
+message.  This way no one else except the sender and the receiver can encrypt
+and decrypt the private messages.  The message is encrypted by the sender
+with the private message key and all the servers and routers pass the message
+through enroute to the receiver.  They cannot decrypt the message since
+they do not have the key.  When sending private messages in this way it
+does not matter whether the clients trust or do not trust the servers and
+routers in the SILC network.
+
+<p><br>
+<object data="silc_priv2.jpg" type="application/postscript">
+<img src="silc_priv2.png" alt="Private Messages with Private Message Key" align="center" border"0">
+</object>
+<p><br>
+
+As the diagram above shows the Client A encrypts the message with private
+message key and sends the message to the SILC Network.  All servers and
+routers merely pass the message through since they cannot decrypt it.
+The Client B then receives the message and decrypts it with the private
+message key.
+<p>
+
+Sending private messages in this manner is always secure since the key is
+shared only by the sender and the receiver.  The problem of this method
+is that the sender and the receiver must somehow agree about the key
+they are going to use.  The private message key can generally be anything.
+It can be a passphrase that only the sender and the receiver knows.  They
+could have been agreed to use some word or phrase as the key sometime
+earlier before they started the conversation.  Or the key maybe from some
+random string from a code book that only the sender and the receiver poses.
+Or it can be a key that is negotiated using some key exchange protocol.
+<p>
+
+The problem however is fundamental.  How to agree to use some key when
+you cannot reach the other person over secure channel?  The SILC protocol
+solves this problem by providing a possiblity to negotiate the key
+between two clients using the SKE protocol.  One or both of the clients
+can set up the SKE server running in their host and ask the other client
+to connect to it.  In this case the SKE is executed outside the SILC
+Network.  As a result of the SKE protocol the clients have now shared
+secret that they can use as private message key.  The key is known only
+by the two clients that executed the SKE protocol.  They can then use
+that key to secure all subsequent private messages.
+<p>
+
+Using this method of private messages delivery is recommended if the
+clients cannot trust the servers and routers in the SILC Network.  The 
+drawback is the extra phase of setting the private message key before
+starting the conversation.  However, using the SKE protocol is the
+recommended way to negotiate the private message key since it can be
+automatized and does not cause any extra tasks for end user.
+
+
+<p><br>
+<h2>Private Message Delivery With Public Key Encryption</h2>
+<p>
+If the clients cannot trust the servers and routers in the SILC Network
+they can use the private message key.  As described in the previous section
+it is easy to set up with the SKE protocol.  However, sometimes the two
+clients do not want to use any passphrases as private message key or
+negotiate the key with SKE, or perhaps they are unable to negotiate the
+key because of some other external problem.  The SILC protocol provides
+yet another way of securing the private messages.  This way does not
+require setting or negotiating private message key.  And, in this method
+also it does not matter whether the clients trust or do not trust the
+servers and routers in the SILC Network.  The method is public key
+encryption.  The clients can encrypt the private messages with the
+receiver's public key and send the message to the network.  The servers
+and routers cannot decrypt the messages since they do not have the
+receiver's private key.  The receiver on the other hand has the private
+key which it uses to decrypt the message.
+<p>
+
+Even this method of private message delivery is not perfect.  The drawbacks
+of this method is that the sender must first assure that the public key
+it is using in the encryption is actually the receiver's public key.
+This is a absolute requirement in this method.  If the sender cannot
+authenticate the receiver's public key this method of private message
+delivery should not be used.  In SILC protocol clients can fetch other
+clients public keys from servers, but the client still must verify the
+key.  Use of certificates can solve the problem.  The receiver's certificate
+could be authenticated by a third party Certification Authority (CA).
+
+<p>
+Usually verifying the public key is not a problem since the receiver
+can provide the public key on some website, or verify the fingerprint of
+the key over email, or phone call.  The clients can also fetch the
+public keys from SILC servers.  If both of the clients trust that the
+public keys are authentic using this method of private message delivery
+is very simple and recommended.
+
+
+<p><br>
+<h1>Secure File Transfers</h1>
+
+The file transfer support in chat protocols are a absolute requirement
+nowadays, and chat protocol without one is no chat protocol at all.  SILC
+also supports file transfer with the addition that the file transfer
+stream is secured.  When a user wants to transfer a file to another
+user, the SILC Key Exchange (SKE) protocol is first executed to negotiate
+a session key for the file transfer stream.  This key is then used to
+protect the peer to peer stream between users.
+<p>
+
+The file transfer protocol used in SILC protocol is the SSH File Transfer
+protocol (SFTP).  Even though the name of the protocol relates to SSH,
+the actual file transfer protocol has nothing to do with Secure Shell.
+The SFTP is totally independent file transfer protocol and its stream
+is secured using SILC.  The SFTP is very good protocol because in addition
+of providing simple file transfer support, it can also support complex
+file and directory manipulation.
+<p>
+
+The support for file transfer in SILC has been designed so that using
+practically any file transfer protocol is possible.  The mandatory protocol
+is SFTP but in the future adding support for other protocols is also
+possible.
+
+
+<p><br>
+<h1>Future of the Protocol</h1>
+
+The current protocol version is 1.0.  This does not mean that the protocol
+is perfect and does not need further development.  There is still features
+that are missing from the protocol, and it is clear that the protocol needs
+to mature a bit more.  There has been a talk about adding features like
+permanent channels, more wide channel founder privileges, and other similar
+features.  The network model of the protocol allows powerful routing
+capabilities, however the routing is not fully defined yet in the protocol
+and requires more in depth work.  The protocol is still in draft phase
+and is open for new features.  However, it is our intention that the
+protocol will be standardized in the future.
+
+
+<p><br>
+<h1>Conclusion</h1>
+
+The Secure Internet Live Conferencing (SILC) protocol is a new generation
+chat protocol that provides all the common conferencing services with
+strong support for security.  It has wide range of security properties
+that should meet the highest levels of security requirements, while not
+forgetting easy of use.  The network topology offers new architectural
+solution with better scalability over traditional chat protocols.
+
+
+<p><br>
+<h1>Further Information</h1>
+<p>
+More detailed information about the SILC protocol is available in the
+SILC protocol specification documents.  There exists currently four
+Internet Drafts that defines the protocol in great detail.  The Internet
+Drafts are available from the following sources but also from the
+<a href="http://www.ietf.org">IETF website</a>.
+<p>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-spec-04.txt">
+Secure Internet Live Conferencing (SILC), Protocol Specification</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-pp-04.txt">
+SILC Packet Protocol</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-ke-auth-04.txt">
+SILC Key Exchange and Authentication Protocols</a>
+<br>
+
+- <a href="http://silcnet.org/docs/draft-riikonen-silc-commands-02.txt">
+SILC Commands</a>
+<p>
+
+For comprehensive introduction to cryptography refer to the
+<a href="http://www.ssh.com/tech/crypto/">Cryptography A-2-Z document</a>.
+
+<p><br>
+<a name="terms"></a>
+<h1>Terms and Abbreviations</h1>
+<p>
+
+- Asymmetric cryptosystem
+<p>
+Asymmetric cryptosystem provides public encryption.  It has two keys,
+one public key and one private key (also called as secret key).  The public
+key is publicly available allowing anyone to encrypt messages with the
+public key.  Only the posessor of the private key can decrypt those messages.
+Difference to symmetric cryptosystem is that symmetric cryptosystem use only
+one key, and the key is usually used to both encryption and decryption.  The
+asymmetric cryptosystem is also called as public key encryption, public key
+cryptosystem or public key algorithm.  SILC supports RSA and DSS asymmetric
+cryptosystems.
+<p>
+
+- Authentication
+<p>
+The verification of the identity of a person, host or process in order
+to gain access to a service or prove identity.  In data communications
+it also means verifying the origin of a message.
+<p>
+
+- Certificate
+<p>
+Certificate is a digital document which can be used to verify the 
+identity of a person or host.  In SILC, certificates can be used to prove
+identity of clients, servers and routers.  Basically certificate is a public
+key with subject name.  SILC supports X.509, OpenPGP and SPKI certificates.
+Supported public keys are SILC style public key and SSH2 style public
+key.
+<p>
+
+- Certification Authority (CA)
+<p>
+A third party entity that can verify identity of a person or host.  CA
+is usually external company that provides certificates and their
+verification services.
+<p>
+
+- Diffie-Hellman key exchange
+<p>
+First public key algorithm ever invented.  It is used to generate a secret
+key between two or more parties.  It gets its security from the difficulty
+of calculating discrete logarithms.
+<p>
+
+- Encryption
+<p>
+A mechanism (usually mathematical) to transfer plaintext (or cleartext)
+to ciphertext to provide confidentiality.  A process to transfer
+the ciphertext back to plaintext is called decryption.
+<p>
+
+- Integrity
+<p>
+The verification of data to detect any modifications.  If data is
+modified enroute from the sender to the receiver, the modification will
+be detected.
+<p>
+
+- HMAC
+<p>
+Hash Message Authentication Code.  Also called as keyed hash function.
+It is a secret key authentication algorithm which proves that the message
+is not modified and that the HMAC was computed by the sender of the
+message.
+<p>
+
+- Key management
+<p>
+Key management is a set of processes and mechanisms which support key
+exchange and maintainance of current keying relationships between parties,
+including replacing older keys with new keys as necessary, by executing
+rekey.
+<p>
+
+- Man-in-the-middle attack
+<p>
+An attack against two connecting entities where the attacker executes
+key exchange protocol with both of the parties indepently without
+their knowledge.  Both of the connecting entities will end up having secret
+key with the attacker, and the attacker can encrypt and decrypt all the
+messages that goes between the two entities.
+<p>
+
+- Message Authentication Code (MAC)
+<p>
+MAC provides message integrity by computing the MAC using a secret
+key authentication algorithm (HMAC).
+<p>
+
+- Perfect Forward Secrecy (PFS)
+<p>
+A property of rekey (or key regeneration) which defines whether the
+new key is derived from the old key.  If Perfect Forward Secrecy is
+selected the new key is never dependent of the old key which means
+that if the old key would get compromised at later time it will not
+compromise the new key.  In SILC setting PFS in the SKE protocol means
+executing the SKE protocol again.  If PFS is not selected the new key
+is always derived from the old key.
+<p>
+
+- Rekey
+<p>
+A key regeneration process where the old key has expired or is not
+secure anymore to use.  In this case rekey is performed and new key
+is generated.
+<p>
+
+- Symmetric cryptosystem
+<p>
+Symmetric cryptosystem is one key cryptosystem where one key is used
+usually to both encryption and decryption process.  The symmetric
+cryptosystems are usually significantly faster than asymmetric cryptosystems.
+DES, AES, Twofish and Blowfish are examples of symmetric cryptosystems.
+SILC supports all the common symmetric cryptosystems including AES.
+SILC does not support DES as it is insecure and 3DES as it is too slow.
+
+
+</font>
+
+</body>
+</html>
diff --git a/public_html/img/silc.gif b/public_html/img/silc.gif
new file mode 100755 (executable)
index 0000000..8e741ba
Binary files /dev/null and b/public_html/img/silc.gif differ
diff --git a/public_html/index.html b/public_html/index.html
deleted file mode 100644 (file)
index 4ce90b3..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-<html>
-<header>
-<style TYPE="text/css"><!-- A:link {text-decoration: none}A:visited{text-decoration:none}A:active{text-decoration:none}--></style>
-<title>SILC - Secure Internet Live Conferencing</title>
-</header>
-
-<body bgcolor="#FFFFFF">
-<center>
-<p><br>
-<img src="silc.jpg" border=0 ALT="SILC Logo">
-<h1>SILC - Secure Internet Live Conferencing</h1>
-<h3>Welcome to the Secure Internet Live Conferencing project homepage</h3>
-<table>
-<tr>
-<td>
-<ul>
-  <li><a href="about.html">About the SILC</a>
-  <li><a href="history.html">History</a>
-  <li><a href="faq.html">The SILC FAQ</a>
-  <li><a href="doc.html">SILC Documentation</a>
-  <li><a href="features.html">SILC Features</a>
-</ul>
-</td>
-<td>
-<ul>
-  <li><a href="download.html">Download SILC</a>
-  <li><a href="todo.html">TODO</a>
-  <li><a href="contribute.html">Contributing</a>
-  <li>Anonymous CVS access [coming]
-  <li><a href="copying.html">The General Public License (GPL)</a>
-</ul>
-</td>
-</tr>
-</table>
-<p><br>
-
-<tr><td align=center>
-<table width="80%" cellspacing=0 cellpadding=0 border=0 bgcolor="#FFFFFF">
-<tr><td bgcolor="#EEEEFF">&nbsp;<tr><td>&nbsp;</td></tr>
-<tr><td>
-<div style="margin-left: 20px">
-<center><h1>SILC XXXX2000 Development Version Available</h1></center>
-<center>
-<font size=4>
-No, it's not available just yet.
-</center>
-<p><br>
-
-</div>
-</td></tr>
-<tr><td bgcolor="#EEEEFF">&nbsp;<tr><td>&nbsp;</td></tr>
-<tr><td>
-<div style="margin-left: 20px">
-<center><h1>Developers Wanted For SILC Project</h1></center>
-<center>
-<font size=4>
-XXX
-</center>
-<p><br>
-</div>
-</td></tr>
-</table>
-
-<p>
-<hr width="80%">
-<font size=2>
-<center>
-Webpage by Pekka Riikonen <a href="mailto:priikone@poseidon.pspt.fi">
-priikone@poseidon.pspt.fi</a><br>
-Logos automagically generated with GIMP<br>
-[ <!--#exec cgi="/cgi-bin/textcounter/counter.cgi"--> ] hits since June 12 2000<br>
-Last updated: Mon Jun 12 10:44:06 EEST 2000
-</center>
-</font>
-</body>
-</html>
diff --git a/public_html/index.php b/public_html/index.php
new file mode 100644 (file)
index 0000000..c32506e
--- /dev/null
@@ -0,0 +1,206 @@
+<?php
+// 
+// Copyright (c) 2001, Lubomir Sedlacik <salo@silcnet.org>
+// and other members of the SILC Project (http://silcnet.org)
+// All rights reserved.
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 
+// 1) Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+// 2) Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.
+// 3) Neither the name of the SILC Project nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+// 
+
+// Read neccessary stuff if accessible
+  if (@Is_Readable("config.php")) include("config.php");
+  if (@Is_Readable("mirror.php")) include("mirror.php");
+
+?>
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <meta http-equiv="Content-Language" content="en" />
+ <meta name="description" content="SILC Secure Internet Live Conferencing" />
+ <meta name="keywords" content="SILC silcnet secure chat protocol cipher encrypt encryption SKE private channel conferencing" />
+ <meta content="INDEX, FOLLOW" name="ROBOTS" />
+ <title> SILC Secure Internet Live Conferencing - 
+<?php
+
+  // sites
+  if ($Country_Code != "")
+    $WWW_Site = $Country_Code.".silcnet.org";
+  else
+    $WWW_Site = "silcnet.org";
+
+  if ($Country_Code != "" && StrToLower($FTP_Archive) == "yes")
+    $FTP_Site = "ftp.".$Country_Code.".silcnet.org";
+  else
+    $FTP_Site = "ftp.silcnet.org";
+
+  if ($Country_Code != "" && StrToLower($CVS_Archive) == "yes"
+                          && $CVS_User && $CVS_Root)
+    $CVS_Site = "cvs.".$Country_Code.".silcnet.org";
+  else {
+    $CVS_Site = "cvs.silcnet.org";
+    $CVS_User = "cvs";
+    $CVS_Root = "/cvs/silc";
+  }
+
+  // find out release dates from release archive files
+  $Date_Toolkit = date("l dS of F Y H:i:s",
+                  filemtime("download/toolkit/sources/silc-toolkit-".$Latest_Toolkit.".tar.gz"));
+  $Date_Client  = date("l dS of F Y H:i:s",
+                  filemtime("download/client/sources/silc-client-".$Latest_Client.".tar.gz"));
+  $Date_Server  = date("l dS of F Y H:i:s",
+                  filemtime("download/server/sources/silc-server-".$Latest_Server.".tar.gz"));
+
+  // remove possibly dangerous characters, only alphanumerical characters are passed
+  function Filter($input) {
+    return EReg_Replace("([^a-zA-Z0-9])*", "", $input);
+  }
+
+  // div();
+  function div($a,$b) {
+    return (int) ($a/$b);
+  }
+
+  $pass = 0;
+  if (@Is_Readable("html/".Filter($page).".php")) {
+    echo $page;
+    $pass = 1;
+  }
+  else
+    echo "news";
+?>
+ </title>
+ <link href="silc.css" rel="stylesheet" type="text/css" />
+</head>
+<body>
+
+<table width="100%" cellpadding="0" cellspacing ="0" border="0">
+<tr><td align="center">
+
+<table width="700" cellpadding="1" cellspacing="0" border="0">
+ <tr>
+  <td class="outline">
+    <table width="100%" cellpadding="0" cellspacing="0" border="0">
+     <tr>
+      <td class="links">
+        <img src="img/silc.gif" width="700" height="100" alt=" " />
+      </td>
+     </tr>
+     <tr>
+      <td class="links" align="center">
+        <table cellspacing="3" cellpadding="10" border="0">
+        <tr><td valign="top" class="links">
+        <b>General</b><br />
+        <small class="normal">o</small> <a href="?page=news" class="normal">SILC News</a><br />
+        <small class="normal">o</small> <a href="?page=about" class="normal">About the SILC</a><br />
+        <small class="normal">o</small> <a href="?page=history" class="normal">History of SILC</a><br />
+        <small class="normal">o</small> <a href="?page=contact" class="normal">Contact Us</a><br />
+        <small class="normal">o</small> <a href="?page=lists" class="normal">SILC Mailing Lists</a><br />
+        </td><td valign="top" class="links">
+        <b>Documentation</b><br />
+        <small class="normal">o</small> <a href="?page=docs" class="normal">SILC Documentation</a><br />
+        <small class="normal">o</small> <a href="?page=whitepaper" class="normal">SILC White Paper</a><br />
+        <small class="normal">o</small> <a href="?page=faq" class="normal">SILC FAQ</a><br />
+        <small class="normal">o</small> <a href="?page=features" class="normal">SILC Features</a><br />
+        <small class="normal">o</small> <a href="?page=todo" class="normal">TODO List</a><br />
+        </td><td valign="top" class="links">
+        <b>Software</b><br />
+        <small class="normal">o</small> <a href="?page=download" class="normal">Download SILC</a><br />
+        <small class="normal">o</small> <a href="?page=mirrors" class="normal">Mirrors Worldwide</a><br />
+        <small class="normal">o</small> <a href="?page=cvs" class="normal">Anonymous CVS</a><br />
+        <small class="normal">o</small> <a href="txt/changes.txt" class="normal">ChangeLog</a><br />
+        <small class="normal">o</small> <a href="?page=copying" class="normal">Licensing</a><br />
+        </td><td valign="top" class="links">
+        <b>Community</b><br />
+        <small class="normal">o</small> <a href="?page=servers" class="normal">Server List</a><br />
+        <small class="normal">o</small> <a href="?page=contribute" class="normal">Contributing</a><br />
+        <small class="normal">o</small> <a href="?page=help" class="normal">Help</a><br />
+        <small class="normal">o</small> <a href="?page=links" class="normal">Links</a><br />
+        <small class="normal">o</small> <a href="txt/credits.txt" class="normal">Credits</a><br />
+        </td></tr></table>
+      </td>
+     </tr>
+     <tr><td class="line"></td></tr>
+     <tr>
+      <td class="<?php if($pass == 1 && $page == "whitepaper") $color="whitepaper"; else $color="text"; echo $color; ?>">
+        <table width="100%" cellpadding="10" cellspacing="0" border="0">
+        <tr><td class="<?php echo $color; ?>">
+<?php
+  // read document, if it is not valid then read opening page
+  if ($pass == 1)
+    include("html/".Filter($page).".php");
+  else
+    include("html/news.php");
+?>
+          </td>
+         </tr>
+<?php
+
+  if ($OS_Type) {
+    switch(StrToLower($OS_Type)) {
+      case "bsd":   $img = "daemon.gif";
+                    $alt = "( daemon powered - IMAGE )";
+                    break;
+      case "linux": $img = "penguin.gif";
+                    $alt = "( penguin powered - IMAGE )";
+                    break;
+      case "sun":   $img = "sun.png";
+                    $alt = "( powered by Sun - IMAGE )";
+                    break;
+    }
+    echo "<tr>";
+    echo "<td align=\"right\" valign=\"bottom\">";
+    echo "&nbsp;<br />";
+    echo "<img src=\"img/".$img."\" alt=\"".$alt."\" />";
+    echo "</td>";
+    echo "</tr>";
+  }
+?>
+        </table>
+      </td>
+     </tr>
+    </table>
+  </td>
+ </tr>
+</table>
+<small class="highlight">
+webpage by
+<a href="mailto:salo at silcnet.org" class="small">salo at silcnet.org</a> | 
+<?php
+  // insert counter
+  include("html/counter.php");
+?> | W3C 
+<a href="http://validator.w3.org/check/referer" class="small">XHTML</a> and 
+<a href="http://jigsaw.w3.org/css-validator/check/referer" class="small">CSS</a>
+</small>
+<br /><br />
+
+</td></tr></table>
+
+</body>
+</html>
diff --git a/public_html/silc.css b/public_html/silc.css
new file mode 100644 (file)
index 0000000..a8d8ecf
--- /dev/null
@@ -0,0 +1,115 @@
+body {
+  background:      #aaaaaa;
+  color:           #000000;
+  font-family:     Helvetica, Arial, Sans-serif;
+  font-size:       10pt;
+  margin-top:      15px;
+}
+
+a.normal {
+  text-decoration: none;
+  background:      none;
+  color:           #2f486f;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+a.small {
+  text-decoration: none;
+  background:      none;
+  color:           #2f486f;
+  font-size:       8pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+td.black {
+  background:      #000000;
+  color:           #ffffff;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+td.grey {
+  background:      #e2e2e2;
+  color:           #000000;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+td.white {
+  background:      #ffffff;
+  color:           #000000;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+td.blackline {
+  background:      #000000;
+  color:           #ffffff;
+  height:          1px;
+}
+
+td.greytext {
+  background:      #e2e2e2;
+  color:           #000000;
+  text-align:      justify;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+td.whitetext {
+  background:      #ffffff;
+  color:           #000000;
+  text-align:      justify;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+small.blue {
+  background:      none;
+  color:           #2f486f;
+  font-size:       8pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+small.black {
+  background:      none;
+  color:           #000000;
+  font-size:       8pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+big {
+  background:      none;
+  color:           #000000;
+  font-size:       14pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+i {
+  background:      none;
+  color:           #2f486f;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
+
+tt.blue {
+  background:      none;
+  color:           #2f486f;
+  font-size:       10pt;
+  font-family:     Courier, monospace;
+}
+
+tt.black {
+  background:      none;
+  color:           #000000;
+  font-size:       10pt;
+  font-family:     Courier, monospace;
+}
+
+samp.blue {
+  background:      none;
+  color:           #2f486f;
+  font-size:       10pt;
+  font-family:     Helvetica, Arial, Sans-serif;
+}
diff --git a/public_html/silc.jpg b/public_html/silc.jpg
deleted file mode 100644 (file)
index 4af8ca6..0000000
Binary files a/public_html/silc.jpg and /dev/null differ
diff --git a/public_html/silc2.jpg b/public_html/silc2.jpg
deleted file mode 100644 (file)
index 8d62b72..0000000
Binary files a/public_html/silc2.jpg and /dev/null differ
similarity index 100%
rename from doc/fix.pl
rename to scripts/fix.pl
diff --git a/scripts/html2ps b/scripts/html2ps
new file mode 100755 (executable)
index 0000000..d11ca0c
--- /dev/null
@@ -0,0 +1,4606 @@
+: # Use perl
+eval 'exec perl -S $0 "$@"'
+  if $running_under_some_shell;
+
+# This is html2ps version 1.0 beta3, an HTML-to-PostScript converter.
+#   Copyright (C) 1995-2000 Jan Karrman.
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Author: Jan Karrman, Dept. of Scientific Computing, Uppsala University,
+#         Sweden, e-mail: jan@tdb.uu.se.
+
+
+# Set the name of the global configuration file. See the installation notes
+# and manual page for more details on configuration files.
+
+$globrc='/usr/local/lib/html2ps/html2psrc';
+$ug='/usr/local/lib/html2ps/html2ps.html';
+
+$conf=<<'EOR';
+@html2ps {
+  package {
+    PerlMagick: 0;
+    ImageMagick: 0;
+    pbmplus: 0;
+    netpbm: 0;
+    djpeg: 0;
+    Ghostscript: 0;
+    TeX: 0;
+    dvips: 0;
+    libwww-perl: 0;
+    jfriedl: 0;
+    geturl: "";
+    check: "";
+    path: "";
+  }
+  paper {
+    type: A4;
+    height: "";
+    width: "";
+  }
+  option {
+    twoup: 0;
+    base: "";
+    check: 0;
+    toc: "";
+    debug: 0;
+    DSC: 0;
+    encoding: "ISO-8859-1";
+    rcfile: "";
+    frame: 0;
+    grayscale: 0;
+    help: 0;
+    hyphenate: 0;
+    scaleimage: 1;
+    cookie: "";
+    language: "";
+    landscape: 0;
+    scalemath: 1;
+    number: 0;
+    startno: "";
+    output: "";
+    original: 0;
+    rootdir: "";
+    xref: 0;
+    scaledoc: 1;
+    style: "";
+    titlepage: 0;
+    text: 0;
+    underline: 0;
+    colour: 0;
+    version: 0;
+    web: "";
+    duplex: "";
+  }
+  margin {
+    middle: 2cm;
+  }
+  xref {
+    text: "[p $N]";
+    passes: 1;
+  }
+  quote {
+    en {
+      open: "&ldquo;";
+      close: "&rdquo;";
+      open2: "`";
+      close2: "'";
+    }
+    sv {
+      open: "&rdquo;";
+      close: "&rdquo;";
+      open2: "'";
+      close2: "'";
+    }
+    da {
+      open: "&raquo;";
+      close: "&laquo;";
+    }
+    no {
+      open: "&laquo;";
+      close: "&raquo;";
+    }
+    fr {
+      open: "&laquo;&nbsp;";
+      close: "&nbsp;&raquo;";
+    }
+    de {
+      open: "&bdquo;";
+      close: "&ldquo;";
+      open2: "&sbquo;";
+      close2: "`";
+    }
+    fi: sv;
+    es: en;
+    it: no;
+    nn: no;
+    nb: no;
+  }
+  toc {
+    heading: "<H1>Table of Contents</H1>";
+    level: 6;
+    indent: 1em;
+  }
+  titlepage {
+    content: "<DIV align=center>
+      <H1><BIG>$T</BIG></H1>
+      <H2>$[author]</H2></DIV>";
+    margin-top: 4cm;
+  }
+  font {
+    times {
+      names: "Times-Roman
+              Times-Italic
+              Times-Bold
+              Times-BoldItalic";
+    }
+    new-century-schoolbook {
+      names: "NewCenturySchlbk-Roman
+              NewCenturySchlbk-Italic
+              NewCenturySchlbk-Bold
+              NewCenturySchlbk-BoldItalic";
+    }
+    helvetica {
+      names: "Helvetica
+              Helvetica-Oblique
+              Helvetica-Bold
+              Helvetica-BoldOblique";
+    }
+    helvetica-narrow {
+      names: "Helvetica-Narrow
+              Helvetica-Narrow-Oblique
+              Helvetica-Narrow-Bold
+              Helvetica-Narrow-BoldOblique";
+    }
+    palatino {
+      names: "Palatino-Roman
+              Palatino-Italic
+              Palatino-Bold
+              Palatino-BoldItalic";
+    }
+    avantgarde {
+      names: "AvantGarde-Book
+              AvantGarde-BookOblique
+              AvantGarde-Demi
+              AvantGarde-DemiOblique";
+    }
+    bookman {
+      names: "Bookman-Light
+              Bookman-LightItalic
+              Bookman-Demi
+              Bookman-DemiItalic";
+    }
+    courier {
+      names: "Courier
+              Courier-Oblique
+              Courier-Bold
+              Courier-BoldOblique";
+    }
+  }
+  hyphenation {
+    min: 8;
+    start: 4;
+    end: 3;
+    en {
+      file: "";
+      extfile: "";
+    }
+  }
+  header {
+    left: "";
+    center: "";
+    right: "";
+    odd-left: "";
+    odd-center: "";
+    odd-right: "";
+    even-left: "";
+    even-center: "";
+    even-right: "";
+    font-family: Helvetica;
+    font-size: 8pt;
+    font-style: normal;
+    font-weight: normal;
+    color: black;
+    alternate: 1;
+  }
+  footer {
+    left: "";
+    center: "";
+    right: "";
+    odd-left: "";
+    odd-center: "";
+    odd-right: "";
+    even-left: "";
+    even-center: "";
+    even-right: "";
+    font-family: Helvetica;
+    font-size: 8pt;
+    font-style: normal;
+    font-weight: normal;
+    color: black;
+    alternate: 1;
+  }
+  frame {
+    width: 0.6pt;
+    margin: 0.5cm;
+    color: black;
+  }
+  justify {
+    word: 15pt;
+    letter: 0pt;
+  }
+  draft {
+    text: DRAFT;
+    print: "";
+    dir: 0;
+    font-family: Helvetica;
+    font-style: normal;
+    font-weight: bold;
+    color: F0F0F0;
+  }
+  colour {
+    black: 000000;
+    green: 008000;
+    silver: C0C0C0;
+    lime: 00FF00;
+    gray: 808080;
+    olive: 808000;
+    white: FFFFFF;
+    yellow: FFFF00;
+    maroon: 800000;
+    navy: 000080;
+    red: FF0000;
+    blue: 0000FF;
+    purple: 800080;
+    teal: 008080;
+    fuchsia: FF00FF;
+    aqua: 00FFFF;
+  }
+  html2psrc: "$HOME/.html2psrc";
+  imgalt: "[IMAGE]";
+  datefmt: "%e %b %Y  %R";
+  locale: "";
+  doc-sep: "<!--NewPage-->";
+  ball-radius: 0.25em;
+  numbstyle: 0;
+  showurl: 0;
+  seq-number: 0;
+  extrapage: 1;
+  break-table: 0;
+  forms: 1;
+  textarea-data: 0;
+  page-break: 1;
+  expand-acronyms: 0;
+  spoof: "";
+  ssi: 1;
+  prefilled: 0;
+}
+
+BODY {
+  font-family: Times;
+  font-size: 11pt;
+  text-align: left;
+  background: white;
+}
+
+H1, H2, H3, H4, H5, H6 {
+  font-weight: bold;
+  margin-top: 0.8em;
+  margin-bottom: 0.5em;
+}
+H1 { font-size: 19pt }
+H2 { font-size: 17pt }
+H3 { font-size: 15pt }
+H4 { font-size: 13pt }
+H5 { font-size: 12pt }
+H6 { font-size: 11pt }
+
+P, OL, UL, DL, BLOCKQUOTE, PRE {
+  margin-top: 1em;
+  margin-bottom: 1em;
+}
+
+P {
+  line-height: 1.2em;
+  text-indent: 0;
+}
+
+OL, UL, DD { margin-left: 2em }
+
+TT, KBD, PRE { font-family: Courier }
+
+PRE { font-size: 9pt }
+
+BLOCKQUOTE {
+  margin-left: 1em;
+  margin-right: 1em;
+}
+
+ADDRESS {
+  margin-top: 0.5em;
+  margin-bottom: 0.5em;
+}
+
+TABLE {
+  margin-top: 1.3em;
+  margin-bottom: 1em;
+}
+
+DIV.noprint { display: none }
+
+DEL { text-decoration: line-through }
+
+A:link, HR { color: black }
+
+@page {
+  margin-left: 2.5cm;
+  margin-right: 2.5cm;
+  margin-top: 3cm;
+  margin-bottom: 3cm;
+}
+
+EOR
+
+eval "require POSIX";
+$posix = !$@;
+
+%extend=('quote',1, 'font',1, 'colour',1, 'hyphenation',1);
+%fal=("serif","times", "sans_serif","helvetica", "monospace","courier");
+@fo=("p","pre","h1","h2","h3","h4","h5","h6","i","b","tt","kbd","cite","samp",
+ "address","blockquote","ol","ul","dl","dt","dd","table","header","footer");
+%quote=('','en');
+%valid=('font',1, 'font_files',1, 'margin,left',1, 'margin,right',1,
+ 'margin,top',1, 'margin,bottom',1);
+%cm=('cm',1, 'mm',0.1, 'in',2.54, 'pt',2.54/72, 'pc',2.54/6);
+%pt=('cm',72/2.54, 'mm',72/25.4, 'in',72, 'pt',1, 'pc',12);
+%space=('thinsp',6, '#8201',6, 'ensp',2, '#8194',2, 'emsp',1, '#8195',1);
+$space=join('|',keys %space);
+%vars=("T","Ti", "N","Pn", "U","UR", "H","h", "A","Au");
+%height=("letter",27.9, "legal",35.6, "arche",121.9, "archd",91.4, "archc",61,
+ "archb",45.7, "archa",30.5, "flsa",33, "flse",33, "halfletter",21.6,
+ "11x17",43.2, "ledger",27.9);
+%width=("letter",21.6, "legal",21.6, "arche",91.4, "archd",61, "archc",45.7,
+ "archb",30.5, "archa",22.9, "flsa",21.6, "flse",21.6, "halfletter",14,
+ "11x17",27.9, "ledger",43.2);
+
+$version="html2ps version 1.0 beta3";
+$opts="2|b:|c|C:|d|D|e:|f:|F|g|h|H|i:|k:|l:|L|m:|n|N:|o:|O|r:|R|s:|S:|t|T|u|"
+ ."U|v|W:|x:";
+%optalias=( 'twoup','2', 'base','b', 'check','c', 'toc','C', 'debug','d',
+ 'DSC','D', 'encoding','e', 'rcfile','f', 'frame','F', 'grayscale','g',
+ 'help','h', 'hyphenate','H', 'scaleimage','i', 'cookie','k', 'language','l',
+ 'landscape','L', 'scalemath','m', 'number','n', 'startno','N', 'output','o',
+ 'original','O', 'rootdir','r', 'xref','R', 'scaledoc','s', 'style','S',
+ 'titlepage','t', 'text','T', 'underline','u', 'colour','U', 'version','v',
+ 'web','W', 'duplex','x');
+%type=( 'paper,height',2, 'paper,width',2, 'option,i',3, 'option,m',3,
+ 'option,N',4, 'option,s',3, 'option,x',4, 'xref,passes',4, 'draft,print',5);
+
+$usage=<<EOU;
+Usage:
+ html2ps [-2cdDFghHLnORtTuUv] [-b URL] [-C string] [-e encoding]
+  [-f file[:file[:...]]] [-i num] [-k file] [-l lang] [-m num] [-N num]
+  [-o file] [-r path] [-s num] [-S string] [-W string] [-x num]
+  [file|URL [file|URL [...]]]
+
+EOU
+
+$usage.="The html2ps users guide is available as $ug\n\n" if(-r $ug);
+
+$args="@ARGV";
+&Getopts($opts) || die $usage;
+
+if($opt_d) {
+  open(DBG,">html2ps.dbg") || die "Error opening debug file: html2ps.dbg\n";
+  print STDERR "***** Writing debug info to html2ps.dbg\n";
+  print DBG "***** $version\n***** Command: $0 $args\n***** Perl: $]\n";
+  print DBG "***** HTML2PSPATH=$ENV{'HTML2PSPATH'}\n";
+}
+undef $/;
+$user=0;
+$V='(-?\d+\.?\d*|-?\d*\.?\d+)';
+&getconf($conf);
+&pagedef;
+undef %AT_page;
+for(@fo,"draft") {
+  eval "\$deffnt{'$_'}=defined \$$_\{'font-family'\}?"
+      ."\$$_\{'font-family'\}:\$body{'font-family'}";
+}
+$user=1;
+if(open(RC,$globrc)) {
+  $conf=<RC>;
+  print DBG "***** Global file $globrc:\n$conf" if($opt_d);
+  &getconf($conf);
+  close RC;
+} else {
+  &dbg("Warning: cannot open the global resource file: $globrc\n") if($globrc);
+}
+$html2psrc=~s/^(~|\$HOME)/$ENV{"HOME"}/;
+$prc=$html2psrc;
+if($opt_f) {
+  ($prc=$opt_f)=~s/^:/$html2psrc:/;
+  $prc=~s/:$/:$html2psrc/;
+  $prc=~s/::/:$html2psrc:/;
+}
+$cwd=$posix?POSIX::getcwd():"";
+opendir(DIR,$cwd);
+@local=readdir DIR;
+closedir DIR;
+$globrc=~/html2psrc/;
+$gdir=$`;
+$hpath=$ENV{"HTML2PSPATH"}?$ENV{"HTML2PSPATH"}:".:";
+$hpath=~s/^:/$gdir:/;
+$hpath=~s/:$/:$gdir/;
+$hpath=~s/::/:$gdir:/;
+$cur=$hpath=~s/(^|:)\.($|:)/$1$cwd$2/;
+@hpath=split(/:/,$hpath);
+for(@hpath) {
+  if(opendir(DIR,$_)) {
+    @files=readdir DIR;
+    $files{$_}=" @files ";
+    closedir DIR;
+  }
+}
+@rc=split(/:/,$prc);
+for $rc (@rc) {
+  $found=0;
+  S:for $dir (@hpath) {
+    if(-r $rc && !grep(/^$rc$/,@local) || $files{$dir}=~/ $rc /) {
+      chdir $dir if($files{$dir}=~/ $rc / && $cwd);
+      if(open(RC,$rc)) {
+        $conf=<RC>;
+        print DBG "***** Personal file $rc:\n$conf" if($opt_d);
+        &getconf($conf);
+        close RC;
+        $found=1;
+      }
+      last S;
+    }
+  }
+  &dbg("Error opening resource file: $rc\n") if($opt_f && !$found);
+}
+chdir $cwd if($cwd);
+$user=2;
+&getconf($opt_S) if($opt_S);
+print DBG "*****\n" if($opt_d);
+&pagedef;
+($pagew,$pageh)=split /\s+/, $AT_page{'size'} if(defined $AT_page{'size'});
+
+require Image::Magick if($package{'PerlMagick'});
+$geturl=$package{'geturl'};
+$ulanch="f";
+$f=72/2.54;
+$giftopm="giftopnm" if($package{'netpbm'});
+$giftopm="giftoppm" if($package{'pbmplus'});
+
+for(keys %option){eval "\$opt_$_='$option{$_}' if(!defined \$opt_$_)"};
+die $usage if $opt_h;
+die "$version\n" if $opt_v;
+&dbg("$version\n") if ($opt_v||$opt_d);
+die "Ghostscript is required to generate DSC PostScript\n"
+ if($opt_D && !$package{'Ghostscript'});
+die "Ghostscript is required to generate cross references\n"
+ if($opt_R && !$package{'Ghostscript'});
+$tmpname=$posix?POSIX::tmpnam():"h2p_$$";
+($scr=$tmpname)=~/\w+$/;
+$tempdir=$`;
+
+if($opt_u) {$ulanch="t"};
+if(defined $opt_x && $opt_x!~/^[0-2]$/) {
+  die "Illegal duplex value: $opt_x\n";
+}
+$V='(-?\d+\.?\d*|-?\d*\.?\d+)';
+for $o ($opt_s,$opt_i,$opt_m,$opt_N) {
+  die "Non numeric: $o\n" if(defined($o) && $o!~/^$V$/);
+}
+
+$twoup=$opt_2?"t":"f";
+$xp=$extrapage?"t":"f";
+
+die "Invalid option: -W $opt_W\n" if($opt_W!~/^[abflprsL\d]*$/);
+$tocdoc=$opt_C=~/[ft]/;
+if($tocdoc && !defined $opt_W) {$opt_W=4};
+$mult=$#ARGV>0 || $opt_W;
+$maxlev=$opt_W=~/(\d+)/?$1:4;
+$link=$opt_W=~/l/;
+$local=$opt_W=~/s/;
+$rel=$opt_W=~/r/;
+$below=$opt_W=~/b/;
+$layer=$opt_W=~/L/;
+$prompt=$opt_W=~/p/;
+
+if($opt_C && $opt_C!~/^(b?[ft]|[ft]b?|b?h|hb?)$/)
+  {die "Invalid option: -C $opt_C\n"};
+$tc=$opt_C?"t":"f";
+$rev=$opt_C=~/t/;
+$first=$opt_C=~/b/ || $opt_R;
+$th=$tocdoc?"f":"t";
+$oeh=0;
+$oef=0;
+
+@now=localtime;
+@gmnow=gmtime;
+POSIX::setlocale(&POSIX::LC_TIME,$locale) if($posix);
+$R='(\s*>|[^a-zA-Z0-9>][^>]*>)';
+$S='([\w.:/%-]+)|"([^"]*)"|\'([^\']*)\'';
+$X='[\da-fA-F]';
+$IM='(gif|jpeg|jpg|png|xbm|xpm|ps|eps)';
+
+for('odd-left','odd-center','odd-right','even-left','even-center','even-right')
+ {
+  $oeh=1 if defined $header{$_};
+  $oef=1 if defined $footer{$_};
+}
+%metarc=();
+for $a ('left','center','right') {
+  if(defined $header{"odd-$a"} || defined $header{"even-$a"}) {
+    $oeh=1;
+  }
+  if(defined $footer{"odd-$a"} || defined $footer{"even-$a"}) {
+    $oef=1;
+  }
+  for('','odd-','even-') {
+    $apa=$header{$_.$a};
+    $numb=1 if($apa=~/(^|[^\$])\$N/);
+    &spec($header{$_.$a});
+    &spec($footer{$_.$a});
+    $header{$_.$a}="($apa)";
+    $apa=$footer{$_.$a};
+    $numb=1 if($apa=~/(^|[^\$])\$N/);
+    $footer{$_.$a}="($apa)";
+    &varsub($header{$_.$a},$footer{$_.$a});
+  }
+}
+if($oeh) {
+  $yz="/YY [[{$header{'odd-left'}}{$header{'even-left'}}]"
+     ."[{$header{'odd-right'}}{$header{'even-right'}}]"
+     ."[{$header{'odd-center'}}{$header{'even-center'}}]] D\n";
+} else {
+  $ind=$header{'alternate'};
+  $yz="/YY [[{$header{'left'}}$ind][{$header{'right'}}".(1-$ind)
+     ."][{$header{'center'}}2]] D\n";
+}
+if($oef) {
+  $yz.="/ZZ [[{$footer{'odd-left'}}{$footer{'even-left'}}]"
+      ."[{$footer{'odd-right'}}{$footer{'even-right'}}]"
+      ."[{$footer{'odd-center'}}{$footer{'even-center'}}]] D";
+} else {
+  $ind=$footer{'alternate'};
+  $yz.="/ZZ [[{$footer{'left'}}$ind][{$footer{'right'}}".(1-$ind)
+      ."][{$footer{'center'}}2]] D";
+}
+$number=$opt_n || !$numb && ($opt_C || $opt_N || $opt_R)?"t":"f";
+$tih=$titlepage{'content'};
+$toch=$toc{'heading'};
+for ($imgalt,$xref{'text'},$tih,$toch,$inh,$draft{'text'}) {
+  &spec($_);
+}
+
+for ($paper{'height'},$paper{'width'},$margin{'middle'},$frame{'margin'},
+     $mlr,$mrl,$mtl,$mbl,$mll,$mrr,$mtr,$mbr,$pagew,$pageh) {
+  &getval($_,1);
+}
+$opt_s*=0.65 if($opt_2 && $opt_L);
+$opt_N=1 if(!defined $opt_N);
+$opt_N=int($opt_N-1);
+$mm=int($margin{'middle'}*$f);
+$is=0.8*$opt_i;
+$msc=1/$opt_s;
+$mag=1200*$opt_m*$opt_s;
+$xref=$opt_R?"t":"f";
+$xref{'text'}=~s/\$N/) WB pN WB (/g;
+
+$d=int($f*$frame{'margin'});
+for (0..10) {
+  $temp=2**(-$_/2);
+  $width{"a$_"}=int($temp*2**(-1/4)*1000+.5)/10;
+  $height{"a$_"}=int($temp*2**(1/4)*1000+.5)/10;
+  $width{"b$_"}=int($temp*1000+.5)/10;
+  $height{"b$_"}=int($temp*2**(1/2)*1000+.5)/10;
+}
+if(!$pagew || !$pageh) {
+  if($width{"\L$paper{'type'}"}) {
+    $paper{'width'}=$width{"\L$paper{'type'}"} if(!defined $paper{'width'});
+    $paper{'height'}=$height{"\L$paper{'type'}"} if(!defined $paper{'height'});
+    ($pagew,$pageh)=($paper{'width'},$paper{'height'});
+  } elsif($paper{'type'}) {
+    &dbg("Unknown paper type: $paper{'type'}\n");
+  }
+}
+if($opt_L) {
+  $wl=$pageh-$mll-$mrl;
+  $wr=$pageh-$mlr-$mrr;
+  $hl=int(($pagew-$mtl-$mbl)*$f+.5);
+  $hr=int(($pagew-$mtr-$mbr)*$f+.5);
+  $xl=int($mtl*$f+.5);
+  $xr=int($mtr*$f+.5);
+  $yl=int($mll*$f+.5);
+  $yr=int($mlr*$f+.5);
+  $rot=" 90 rotate";
+} else {
+  $wl=$pagew-$mll-$mrl;
+  $wr=$pagew-$mlr-$mrr;
+  $hl=int(($pageh-$mtl-$mbl)*$f+.5);
+  $hr=int(($pageh-$mtr-$mbr)*$f+.5);
+  $xl=int($mll*$f+.5);
+  $xr=int($mlr*$f+.5);
+  $yl=int(($pageh-$mtl)*$f+.5);
+  $yr=int(($pageh-$mtr)*$f+.5);
+  $rot="";
+}
+
+if($opt_2) {
+  $wl=($wl-$margin{'middle'})/2;
+  $wr=($wr-$margin{'middle'})/2;
+}
+$wl=int($wl*$f+.5);
+$wr=int($wr*$f+.5);
+$pag=int($pageh*$f+.5);
+$fe=$opt_F?"t":"f";
+$cf=$opt_U?"t":"f";
+$tp=$opt_t?"t":"f";
+$rm=$numbstyle?"t":"f";
+$pa=$showurl?"t":"f";
+$nh=$seq_number?"t":"f";
+$bt=$break_table?"t":"f";
+$ea=$expand_acronyms?"t":"f";
+$fi=$prefilled?"t":"f";
+$latin1=$opt_e=~/ISO-8859-1/i;
+$lt=$del{'text-decoration'}=~/^line-through$/i?"SE":"WB";
+if(!$opt_x && defined $opt_x) {
+  $dupl="[{false statusdict/setduplexmode get exec} stopped cleartomark";
+}
+if($opt_x) {
+  $dupl="[{true statusdict/setduplexmode get exec} stopped cleartomark";
+}
+if($opt_x==2) {
+  $dupl.="\n[{true statusdict/settumble get exec} stopped cleartomark";
+}
+
+%head=("html",1, "head",1, "title",1, "base",1, "meta",1, "link",1, "style",1,
+ "script",1, "isindex",1);
+%algn=("left",1, "center",2, "right",3, "justify",4, "char",5);
+%f=("void",1, "above",2, "below",3, "hsides",4, "lhs",5, "rhs",6, "vsides",7,
+    "box",8, "border",9);
+%r=("none",1, "groups",2, "rows",3, "cols",4, "all",5);
+%v=("top",1, "middle",2, "bottom",3, "baseline",4);
+%it=("radio",0, "checkbox",1, "text",2, "password",2, "image",3);
+%ssy=(200,"\\", 201, "(", 202, ")");
+%lity=("I",0, "i",1, "A",2, "a",3, "1",4, "disc",5, "square",6, "circle",7);
+$ltr=join('|',keys %lity);
+%tex=('`a',"\340", '\^a',"\342", '`e',"\350", '`e',"\350", 'c\{c\}',"\347",
+      "'e","\351", '\^e',"\352", '"e',"\353", '\^i',"\356", '"i',"\357",
+      '\^o',"\364", '`u',"\371", '\^u',"\373", '"u',"\374", '"y',"\377",
+      'aa',"\345", '"a',"\344", '"o',"\366", 'ae',"\346", 'oe',"\225");
+@hind=(0,0,0,0,0,0);
+$ltrs='A-Za-z\222-\226\300-\377';
+%ent=(
+"lsquo|#8216",96,
+"rsquo|#8217",39,
+"circ|#710",141,
+"tilde|#732",142,
+"permil|#8240",143,
+"dagger|#8224",144,
+"Dagger|#8225",145,
+"Yuml|#376",146,
+"scaron|#353",147,
+"Scaron|#352",148,
+"oelig|#339",149,
+"OElig|#338",150,
+"lsaquo|#8249",151,
+"rsaquo|#8250",152,
+"sbquo|#8218",153,
+"bdquo|#8222",154,
+"ldquo|#8220",155,
+"rdquo|#8221",156,
+"ndash|#8211",157,
+"mdash|#8212",158,
+"trade|#8482",159,
+"nbsp",160,
+"iexcl",161,
+"cent",162,
+"pound",163,
+"curren",164,
+"yen",165,
+"brvbar",166,
+"sect",167,
+"uml",168,
+"copy",169,
+"ordf",170,
+"laquo",171,
+"not",172,
+"reg",174,
+"macr",175,
+"deg",176,
+"plusmn",177,
+"sup2",178,
+"sup3",179,
+"acute",180,
+"micro",181,
+"para",182,
+"middot",183,
+"cedil",184,
+"sup1",185,
+"ordm",186,
+"raquo",187,
+"frac14",188,
+"frac12",189,
+"frac34",190,
+"iquest",191,
+"Agrave",192,
+"Aacute",193,
+"Acirc",194,
+"Atilde",195,
+"Auml",196,
+"Aring",197,
+"AElig",198,
+"Ccedil",199,
+"Egrave",200,
+"Eacute",201,
+"Ecirc",202,
+"Euml",203,
+"Igrave",204,
+"Iacute",205,
+"Icirc",206,
+"Iuml",207,
+"ETH",208,
+"Ntilde",209,
+"Ograve",210,
+"Oacute",211,
+"Ocirc",212,
+"Otilde",213,
+"Ouml",214,
+"times",215,
+"Oslash",216,
+"Ugrave",217,
+"Uacute",218,
+"Ucirc",219,
+"Uuml",220,
+"Yacute",221,
+"THORN",222,
+"szlig",223,
+"agrave",224,
+"aacute",225,
+"acirc",226,
+"atilde",227,
+"auml",228,
+"aring",229,
+"aelig",230,
+"ccedil",231,
+"egrave",232,
+"eacute",233,
+"ecirc",234,
+"euml",235,
+"igrave",236,
+"iacute",237,
+"icirc",238,
+"iuml",239,
+"eth",240,
+"ntilde",241,
+"ograve",242,
+"oacute",243,
+"ocirc",244,
+"otilde",245,
+"ouml",246,
+"divide",247,
+"oslash",248,
+"ugrave",249,
+"uacute",250,
+"ucirc",251,
+"uuml",252,
+"yacute",253,
+"thorn",254,
+"yuml",255);
+%symb=(
+"alpha|#945",141,
+"beta|#946",142,
+"gamma|#947",147,
+"delta|#948",144,
+"epsilon|#949",145,
+"zeta|#950",172,
+"eta|#951",150,
+"theta|#952",161,
+"thetasym|#977",112,
+"iota|#953",151,
+"kappa|#954",153,
+"lambda|#955",154,
+"mu|#956",155,
+"nu|#957",156,
+"xi|#958",170,
+"pi|#960",160,
+"piv|#982",166,
+"omicron|#959",157,
+"rho|#961",162,
+"sigma|#963",163,
+"sigmaf|#962",126,
+"tau|#964",164,
+"upsilon|#965",165,
+"upsih|#978",241,
+"phi|#966",146,
+"phiv",152,
+"chi|#967",143,
+"psi|#968",171,
+"omega|#969",167,
+"Alpha|#913",101,
+"Beta|#914",102,
+"Gamma|#915",107,
+"Delta|#916",104,
+"Epsilon|#917",105,
+"Zeta|#918",132,
+"Eta|#919",110,
+"Theta|#920",121,
+"Iota|#921",111,
+"Kappa|#922",113,
+"Lambda|#923",114,
+"Mu|#924",115,
+"Nu|#925",116,
+"Xi|#926",130,
+"Omicron|#927",117,
+"Pi|#928",120,
+"Rho|#929",122,
+"Sigma|#931",123,
+"Tau|#932",124,
+"Upsilon|#933",125,
+"Phi|#934",106,
+"Chi|#935",103,
+"Psi|#936",131,
+"Omega|#937",127,
+"fnof|#402",246,
+"perp|#8869",136,
+"plusmn|#177",261,
+"cdot|#183",327,
+"or|#8744",332,
+"and|#8743",331,
+"le|#8804",243,
+"ge|#8805",263,
+"equiv|#8801",272,
+"cong|#8773",100,
+"asymp|#8776",273,
+"ne|#8800",271,
+"sub|#8834",314,
+"sube|#8838",315,
+"sup|#8835",311,
+"supe|#8839",312,
+"isin|#8712",316,
+"larr|#8592",254,
+"rarr|#8594",256,
+"uarr|#8593",255,
+"darr|#8595",257,
+"harr|#8596",253,
+"lArr|#8656",334,
+"rArr|#8658",336,
+"uArr|#8657",335,
+"dArr|#8659",337,
+"hArr|#8660",333,
+"forall|#8704","042",
+"exist|#8707","044",
+"infin|#8734",245,
+"nabla|#8711",321,
+"part|#8706",266,
+"hellip|#8230",274,
+"int|#8747",362,
+"sum|#8721",345,
+"prod|#8719",325,
+"real|#8476",302,
+"image|#8465",301,
+"bull|#8226",267,
+"prime|#8242",242,
+"Prime|#8243",262,
+"oline|#8254",140,
+"frasl|#8260",244,
+"weierp|#8472",303,
+"alefsym|#8501",300,
+"crarr|#8629",277,
+"empty|#8709",306,
+"notin|#8713",317,
+"ni|#8715","047",
+"minus|#8722","055",
+"lowast|#8727","052",
+"radic|#8730",326,
+"prop|#8733",265,
+"ang|#8736",320,
+"cap|#8745",307,
+"cup|#8746",310,
+"sim|#8764",176,
+"nsub|#8836",313,
+"oplus|#8853",305,
+"otimes|#8855",304,
+"sdot|#8901",327,
+"lceil|#8968",351,
+"rceil|#8969",371,
+"lfloor|#8970",353,
+"rfloor|#8971",373,
+"lang|#9001",341,
+"rang|#9002",361,
+"spades|#9824",252,
+"clubs|#9827",247,
+"hearts|#9829",251,
+"diams|#9830",250,
+"loz|#9674",340);
+
+$pc=')WB NL NP(';
+$nimg=-1;
+$nm=-1;
+@font=();
+@size=();
+@styl=();
+@alig=();
+@colr=();
+@topm=();
+@botm=();
+@lftm=();
+@rgtm=();
+@z1=();
+@z2=();
+@z3=();
+&Subst($doc_sep);
+($toct=$toch)=~s|<[\w/!?][^>]*>||g;
+$dh="/h0 [()($toct)] D\n";
+&Subst($toch);
+$toch=~s/  H\(/ -1 H(/g;
+$toch="($toch)";
+&varsub($toch);
+&Subst($tih);
+$tih=~s/  H\(/ -1 H(/g;
+$tih="($tih)";
+&varsub($tih);
+$mn=0;
+$nfont=0;
+$mi=0;
+for (@fo) {&setel($_)};
+%arr=%draft;
+&fs("draft");
+
+if(!$latin1 && !defined $fontid{"times"}) {
+  $fontid{"times"}=$nfont++;
+  @docfonts=(@docfonts,split(/\s+/,$font_names{"times"}));
+}
+$wind=0;
+$wf="t";
+if(!$latin1) {
+  $wind=$fontid{"times"};
+  $wf="f";
+}
+
+for $k (keys %font_files){
+  @ff=split(/\s+/,$font_files{$k});
+  @fn=split(/\s+/,$font_names{$k});
+  for (0..3) {
+    if($ff[$_]) {
+      $ff{$fn[$_]}=$ff[$_];
+    } elsif(!$ff{$fn[$_]}) {
+      $ff{$fn[$_]}=$ff[0];
+    }
+    $fr{$fn[$_]}=$k;
+  }
+}
+$pta=defined $p{"text-align"}?$p{"text-align"}:$body{"text-align"};
+$pal=0;
+$pal=1 if($pta=~/^c/i);
+$pal=2 if($pta=~/^r/i);
+$pal=3 if($pta=~/^j/i);
+$bgcol=&col2rgb($body{"background"});
+if(!$bgcol) {$bgcol="[16#FF 16#FF 16#FF]"};
+if(!$p{"color"}) {$p{"color"}="black"};
+$tcol=&col2rgb($p{"color"});
+$lcol=&col2rgb($a__link{"color"});
+if($lcol) {
+  $Lc="/Lc t D\n/Dl $lcol D\n";
+  $Lc.=$tcol ne $lcol?"/LX t D":"/LX f D";
+} else {
+  $Lc="/Lc f D\n/LX f D";
+}
+$pcol=&col2rgb($pre{"color"});
+if(!$pcol) {$pcol="[0 0 0]"};
+$deftbg=&col2rgb($table{"background"});
+$hc=&col2rgb($hr{"color"});
+if(!$hc) {$hc="[0 0 0]"};
+$fcol=&col2rgb($frame{"color"});
+if(!$fcol) {$fcol="[0 0 0]"};
+for ($p{"font-size"},$pre{"font-size"},$header{"font-size"},$frame{'width'},
+ $footer{"font-size"},$justify{'letter'},$justify{'word'},
+ $titlepage{'margin-top'}) {
+  &getval($_,2);
+}
+for ($p{"line-height"},$p{"text-indent"},$p{"margin-top"},$toc{'indent'},
+     $ball_radius) {
+  &getval($_,0);
+}
+$fl="/FL [/".join("\n/",@docfonts)."] D";
+for $k (keys %ff) {
+  $f=$ff{$k};
+  if(defined $fontid{$fr{$k}} && !defined($cont{$f})) {
+    open(FONT,$f) || &dbg("Error opening fontfile $f\n");
+    ($cont{$f}=<FONT>)=~s/(^|\r?\n|\r)%.*//g;
+    close FONT;
+  }
+}
+$fontdef="";
+for (keys %cont) {
+  $fontdef.=$cont{$_};
+}
+&ent($yz);
+&ent($xref{'text'});
+&ent($draft{'text'});
+
+$lnum=0;
+for (keys %quote_open) {
+  $lid{$_}=$lnum++;
+  if(!defined $quote_close{$_}) {$quote_close{$_}=$quote_open{$_}};
+  if(!defined $quote_open2{$_}) {$quote_open2{$_}=$quote_open{$_}};
+  if(!defined $quote_close2{$_}) {$quote_close2{$_}=$quote_close{$_}};
+  &ent($quote_open{$_});
+  &ent($quote_close{$_});
+  &ent($quote_open2{$_});
+  &ent($quote_close2{$_});
+  push(@qo,$quote_open{$_});
+  push(@qc,$quote_close{$_});
+  push(@qo2,$quote_open2{$_});
+  push(@qc2,$quote_close2{$_});
+}
+$qo=join(')(',@qo);
+$qc=join(')(',@qc);
+$qo2=join(')(',@qo2);
+$qc2=join(')(',@qc2);
+$hyphenation_file{''}=$hyphenation_file{'en'};
+$br=$collapse_br?"f":"t";
+$gd=0;
+$ddr=defined $draft{'print'};
+if($ddr) {
+  if($draft{'print'}==0) {
+    $draft="f";
+  } else {
+    $gd=1;
+    $draft="t";
+  }
+}
+if(-e '/dev/null' || !-e 'nul') {
+  $pathsep=':';
+  $gs='gs';
+} else {
+  $pathsep=';';
+  $gs='gswin32c';
+}
+$gb=$gs_bug?"t":"f";
+for (keys %quote) {$lid{$_}=$lid{$quote{$_}}};
+$ENV{'PATH'}.="$pathsep$package{'path'}" if($package{'path'});
+$delim="%-- End of variable part --";
+$cd="/Cd {aload length 2 idiv dup dict begin {D} repeat currentdict end} D";
+
+$mysymb=<<EOF;
+/MySymbol 10 dict dup begin
+ /FontType 3 D /FontMatrix [.001 0 0 .001 0 0 ] D /FontBBox [25 -10 600 600] D
+ /Encoding 256 array D 0 1 255{Encoding exch /.notdef put}for
+ Encoding (e) 0 get /euro put
+ /Metrics 2 dict D Metrics begin
+  /.notdef 0 D
+  /euro 651 D
+ end
+ /BBox 2 dict D BBox begin
+  /.notdef [0 0 0 0] D
+  /euro [25 -10 600 600] D
+ end
+ /CharacterDefs 2 dict D CharacterDefs begin
+  /.notdef {} D
+  /euro{newpath 114 600 moveto 631 600 lineto 464 200 lineto 573 200 lineto
+   573 0 lineto -94 0 lineto 31 300 lineto -10 300 lineto closepath clip
+   50 setlinewidth newpath 656 300 moveto 381 300 275 0 360 arc stroke
+   -19 350 moveto 600 0 rlineto -19 250 moveto 600 0 rlineto stroke}d
+ end
+ /BuildChar{0 begin
+  /char E D /fontdict E D /charname fontdict /Encoding get char get D
+  fontdict begin
+   Metrics charname get 0 BBox charname get aload pop setcachedevice
+   CharacterDefs charname get exec
+  end
+ end}D
+ /BuildChar load 0 3 dict put /UniqueID 1 D
+end
+definefont pop
+EOF
+
+$P0=<<EOC;
+%%Creator: $version
+%%EndComments
+save
+2000 dict begin
+/d {bind def} bind def
+/D {def} d
+/t true D
+/f false D
+$fl
+/WF $wf D
+/WI $wind D
+/F $opt_s D
+/IW $wr F div D
+/IL $hr F div D
+/PS $pag D
+/EF [@font] D
+/EZ [@size] D
+/Ey [@styl] D
+/EG [@alig] D
+/Tm [@topm] D
+/Bm [@botm] D
+/Lm [@lftm] D
+/Rm [@rgtm] D
+/EU [@colr] D
+/NO $number D
+$yz
+/Ts EZ 0 get D
+/TU $twoup D
+/Xp $xp D
+/AU $ulanch D
+/SN $opt_N D
+/Cf $cf D
+/Tp $tp D
+/Fe $fe D
+/TI $toc{'indent'} Ts mul D
+/Fm $d D
+/xL $xl D
+/xR $xr D
+/yL $yl D
+/yR $yr D
+/Wl $wl F div D
+/Wr $wr F div D
+/hL $hl F div D
+/hR $hr F div D
+/FE {newpath Fm neg Fm M CP BB IW Fm add Fm L IW Fm add IL Fm add neg L CP BB
+ Fm neg IL Fm add neg L closepath} D
+/LA {PM 0 eq{/IW Wl D /IL hL D}{/IW Wr D /IL hR D}ie /W IW D /LL W D /LS W D
+ /LE IL D TU PM 0 eq and{IW $mm F div add SA{Sf div}if 0 translate}
+ {PM 0 eq{xL yL}{xR yR}ie translate$rot F SA{Sf mul}if dup scale
+ CS CF FS Cf{CA CL get VC}if /Bb f D}ie 0 0 M
+ TF not Tc or {Cf{gsave SA{1 Sf div dup scale}if Cb VC FE fill grestore}if}if}D
+/Pi $p{"text-indent"} Ts mul D
+/SG [$is $opt_i $msc] D
+/Ab $justify{'word'} D
+/J $justify{'letter'} D
+/Tc $tc D
+/NH $toc{'level'} D
+/Nf $nh D
+/Pa $pa D
+/LH $p{"line-height"} D
+/XR $xref D
+/Xr {/pN E D ( $xref{'text'} )WB} D
+/Db $bgcol D
+/Dt $tcol D
+/eA $ea D
+/Fi $fi D
+/bT $bt D
+$Lc
+/Br $ball_radius D
+/IA ($imgalt) D
+/DS {/PF f D($doc_sep)pop RC ZF} D
+/Gb $gb D
+/Mb $br D
+/Hc $hc D
+/Bl 3 D
+/MI -$mi D
+/DX ($draft{'text'}) D
+/Di $draft{'dir'} D
+/Tt $titlepage{'margin-top'} D
+/Th {$tih} D
+/tH {$toch} D
+/FD $fontid{"\L$font"} D
+/Dy $styl D
+/cD $col D
+/FW $frame{'width'} D
+/FU $fcol D
+/ET {/RM $rm D /A0 $pal D /PN SN D /OU t D /Ou t D /W IW D /LL W D D1
+ Ms not TP and{Ip}if /TF f D} D
+$dupl
+$delim
+$mysymb
+EOC
+
+$reenc=<<EOD;
+WF{FL{reencodeISO D}forall}{4 1 FL length 1 sub{FL E get reencodeISO D}for}ie
+/Symbol dup dup findfont dup length dict begin
+ {1 index /FID ne{D}{pop pop}ie}forall /Encoding [Encoding aload pop]
+ dup 128 /therefore put D currentdict end definefont D
+EOD
+$defs=<<EOD;
+/reencodeISO {
+ dup dup findfont dup length dict begin{1 index /FID ne{D}{pop pop}ie}forall
+ /Encoding ISOLatin1Encoding D currentdict end definefont} D
+/ISOLatin1Encoding [
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright
+/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash
+/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon
+/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N
+/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright
+/asciicircum/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m
+/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
+/.notdef/space/exclamdown/cent/sterling/currency/yen/brokenbar
+/section/dieresis/copyright/ordfeminine/guillemotleft/logicalnot
+/hyphen/registered/macron/degree/plusminus/twosuperior/threesuperior
+/acute/mu/paragraph/periodcentered/cedilla/onesuperior/ordmasculine
+/guillemotright/onequarter/onehalf/threequarters/questiondown
+/Agrave/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla
+/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute/Icircumflex
+/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
+/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute
+/Thorn/germandbls/agrave/aacute/acircumflex/atilde/adieresis
+/aring/ae/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave
+/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute/ocircumflex
+/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex/udieresis
+/yacute/thorn/ydieresis
+] D
+[128/backslash 129/parenleft 130/parenright 141/circumflex 142/tilde
+143/perthousand 144/dagger 145/daggerdbl 146/Ydieresis 147/scaron 148/Scaron
+149/oe 150/OE 151/guilsinglleft 152/guilsinglright 153/quotesinglbase
+154/quotedblbase 155/quotedblleft 156/quotedblright 157/endash 158/emdash
+159/trademark]
+aload length 2 idiv 1 1 3 -1 roll{pop ISOLatin1Encoding 3 1 roll put}for
+/colorimage where{pop}{
+ /colorimage {
+  pop pop /Pr E D {/Cv Pr D /Gr Cv length 3 idiv string D 0 1 Gr length 1 sub
+   {Gr E dup /i E 3 mul D Cv i get 0.299 mul Cv i 1 add get 0.587 mul add
+    Cv i 2 add get 0.114 mul add cvi put}for Gr} image} D
+}ie
+/pdfmark where{pop}{userdict /pdfmark /cleartomark load put}ie
+EOD
+
+$P1=<<EOT;
+$cd
+/EX {EC cvx exec} D
+/DU {} d
+/BB {pop pop}d
+/ie {ifelse} d
+/E {exch} d
+/M {moveto} d
+/R {rmoveto} d
+/L {lineto} d
+/RL {rlineto} d
+/CP {currentpoint} d
+/SW {stringwidth} d
+/GI {getinterval} d
+/PI {putinterval} d
+/Sg {setgray} d
+/LW {setlinewidth} d
+/S {dup () ne OU and{0 Co R AT 3 eq LB and HF not and A1 0 ne A2 0 ne or and
+ {A2 0 32 A1 0 6 -1 roll awidthshow}{show}ie 0 Co neg R}{pop}ie
+ OU PH 3 eq or{/Ms t D}if} D
+/U {OU{gsave CP currentfont /FontInfo get /UnderlinePosition get
+ 0 E currentfont /FontMatrix get dtransform E pop add newpath M dup SW pop
+ CJ 0 RL stroke grestore}if} D
+/B {OU Br 0 gt and{CP Ts neg Ts .33 mul R gsave 0 Sg
+ CP newpath Ts Br mul 0 360 arc closepath UI 2 mod 0 eq{stroke}{fill}ie
+ grestore M CP E Ts Br 1 add mul sub E BB /Ms t D}if}D
+/NP {Ms TP not or PA and OU and{TP{OR}if f1{mF k2 /mF E D /YC 0 D}if
+ TP TU not PM 0 eq or and{showpage}if DU Ip TE not{LA}if 0.6 LW
+ /CI 0 D /TP t D /Hs f D /hl 6 D /Hv 6 D /HI hi D /Ms f D}if Bs XO BO M} D
+/Np {LE sub CP E pop gt PL 0 eq and{NP}if}D
+/Ip {/PN PN 1 add D /Pn RM{1}{4}ie PN Ns D /PM PN SN sub 2 mod D} D
+/GP {E dup 3 -1 roll get PN 1 add 2 mod get dup type /integertype eq
+ {get 0 get}{E pop}ie}d
+/Fc {dup 2 GP exec SW pop /S1 E D dup 1 GP exec SW pop /S2 E D 0 GP exec SW
+ pop /S3 E D S1 0 gt{S2 2 mul S1 add S3 2 mul S1 add 2 copy lt{E}if pop}{0}ie
+ S2 S3 add 2 copy lt{E}if pop IW .9 mul div dup 1 gt{1 E div}{pop 1}ie}D
+/OR {Df{Sd}if tp not{gsave SA{1 Sf div dup scale}if Fe{Cf{FU VC}if FW LW
+ 1 setlinejoin FE stroke}if /YO {60 F div dup 40 gt{pop 40}if}D /cs CS D
+ /cf CF D /CF 0 D /pf PF D /PF f D /Fn FN D /At AT D /AT 0 D /FN EF Hf 1 add
+ get D Fz Fs FS ZZ Fc Fz mul Fs FS EU Hf 1 add get dup type /arraytype eq
+ Cf and{VC}{pop 0 Sg}ie IW IL neg YO sub M ZZ 1 GP exec dup SW pop neg 0 R Sh
+ 0 IL neg YO sub M ZZ 0 GP exec Sh ZZ 2 GP exec dup SW pop IW E sub 2 div
+ IL neg YO sub M Sh Fz Fs FS NO{/AW IW Pn SW pop sub D AW 2 div IL neg YO sub
+ S1 0 gt S2 AW .45 mul gt or S3 AW .45 mul gt or{Fz 2 mul sub}if M Pn Sh}if
+ EU Hf get dup type /arraytype eq Cf and{VC}{pop 0 Sg}ie YY Fc /FN EF Hf get D
+ Hz mul HS FS IW YO M YY 1 GP exec dup SW pop neg 0 R Sh 0 YO M YY 0 GP exec Sh
+ YY 2 GP exec dup SW pop IW E sub 2 div YO M Sh /FN Fn D /AT At D t Pb XO SZ
+ SL get neg R /PF pf D grestore /CF 0 D cs cf FS}if}D
+/Sh {dup () ne{CP Hz 4 div sub BB show CP CS add BB}{pop}ie}D
+/Pb {/OU E D /Ou OU D /PB t D 0 0 M Ba{/Sa save D /BP t D /Fl t D RC /PL 0 D
+ /PH 0 D /W IW D /LE IL .7 mul D /EO 0 D SI ZF /YA 0 D /BO 0 D /C1 () D
+ BA 0 Ts neg R Bb{Xl Yl Xh Yh}if Bb CP Sa restore M
+ {/Yh E D /Xh E D /Yl E D /Xl E D}if /Fl t D}if
+ BL /OU t D /HM f D /Ou t D /PB f D} D
+/Bs {/BP Ba not D}D
+$defs$fontdef$reenc
+/SF {/CS E D SZ SL CS put FO SL FN put /YI CS LH neg mul D dup ST cvs ( ) join
+ CS ST cvs join C1 E join ( NF ) join /C1 E D CS NF /Wf WF FN 0 gt or D
+ /BW Wf{( ) SW pop}{0}ie D}D
+/NF {/cS E D /cF E D cF 0 ge{FL cF get}{cF -1 eq{/Symbol}{/MySymbol}ie}ie
+ findfont cS scalefont setfont} D
+/FS {CF or /CF E D FR SL CF put CF CF 0 ge{FN 4 mul add}if E SF} D
+/PC {SH /BP f D fin not GL not and{NL}if /HM t D /LL LS D} D
+/BS {/TX E D Wf{/fin f D /CW 0 D /LK 0 D /SC 0 D
+ /RT TX D {RT ( ) search{/NW E D pop /RT E D /WH NW SW pop D CW WH add LL gt
+ {TX SC LK SC sub 1 sub NN GI GL{SH cF cS OC
+ 2 copy cS ne E cF ne or{NF}{pop pop}ie}{PC /CW WH BW add D}ie
+ /SC LK D}
+ {GL{JC}if
+ /CW CW WH add BW add D /HM t D}ie /GL f D /Ph f D
+ /LK LK NW length 1 add add D}{pop exit}ie}loop
+ /fin t D TX SC LK SC sub GI SH RT () ne{GL not{CC}if}if
+ /LC TX length D /WH RT SW pop D CW WH add Hy{HC SW pop add}if LL gt
+ {RT GL{SH cF cS OC 2 copy cS ne E cF ne or{NF}{pop pop}ie
+ Hy{/Ph t D}if /LL LS D}{NL /LL LS D SH}ie}
+ {RT PC Hy{CC}if /Ph Ph Hy or D}ie RT () ne{/GL t D /HM t D}if}
+ {TX SW pop LL le{TX SH}{/NW () D 0 2 TX length 1 sub
+ {/CW E D TX 0 CW GI dup SW pop LL gt{pop NW SH /HM t D NL/LL W XO sub MR sub D
+ /CW CW 2 sub NN D /TX TX CW TX length CW sub GI D TX BS exit}
+ {/NW E D}ie}for}ie}ie /HM t D}D
+/CC {C0 length 0 gt{JC}if /C0 [C1 L1 YA YB Mf NS NB TB AF Bw] D
+ /C1 () D /L0 L1 D /YA 0 D /YB 0 D /Mf 0 D /NS 0 D /NB 0 D}D
+/JC {C0 aload length 0 gt{pop pop pop NB add /NB E D NS add /NS E D
+ dup Mf gt{/Mf E D}{pop}ie dup YB gt{/YB E D}{pop}ie
+ dup YA gt{/YA E D}{pop}ie pop C1 join /C1 E D /C0 [] D}if}D
+/OC {C0 length 0 gt{C1 L1 L0 sub YA YB Mf NS NB TB AF Bw GL C0 aload pop
+ /Bw E D /AF E D /TB E D /NB E D /NS E D /Mf E D /YB E D /YA E D /C0 [] D
+ /L1 E D /C1 E D Ph{HC SH}if NL /GL E D /Bw E D /AF E D /TB E D /NB E D /NS E D
+ /Mf E D /YB E D /YA E D /L1 E D /LL W L1 sub XO sub MR sub WH sub D /CW 0 D
+ C1 E join /C1 E D}if}D
+/BT {/LB t D dup length string copy RS dup dup () ne E ( ) ne and
+ {/CI 0 D /LS LL D /LL W L1 sub XO sub MR sub D BS}
+ {dup ( ) eq{/GL f D}if dup () eq L1 0 eq or{pop}{SH /BP f D /Ph f D}ie}ie
+ /LB f D} D
+/BL {CP E pop XO E M} D
+/NL {JC /GL f D /SK W XO sub MR sub L1 sub TB{Bw add}if D
+ /YA LF{Mf HM Fl not and PF or{LH mul}if}{0 /LF t D}ie YA 2 copy lt{E}if pop D
+ C1 () ne{/FB YB Mf SA{Sf mul}if 4 div 2 copy lt{E}if pop D}if Fl{/Ya YA D}if
+ CP E pop YA sub YB sub LE neg lt Fl not and PB not and{NP}if NT TL BL
+ OU PF not and PB or{/RE L1 TB{Bw sub}if
+ W XO sub MR sub div YA YB add LE BO add div 2 copy lt{E}if pop D
+ RE 1 gt{BL 1 RE div dup scale}if}if
+ AT 2 le{SK AT mul 2 div YA neg R}if
+ AT 3 eq{0 YA neg R TB{/NB NB 1 sub D /NS NS 1 sub D}if /NB NB 1 sub NN D
+ /A3 NS 6 mul NB add D NS NB add 0 eq
+  {/A1 0 D /A2 0 D}
+  {NS 0 eq{/A1 SK NB div dup J gt{pop 0}if D /A2 0 D}{J A3 mul SK lt
+   {/A1 J D /A2 SK J NB mul sub NS div dup Ab gt{/A1 0 D pop 0}if D}
+   {/A1 SK A3 div D /A2 A1 6 mul D}ie}ie}ie /A1 A1 NN D /A2 A2 NN D}if
+ AT 4 eq{0 YA neg R PH 2 le{PD 0 lt{/PD L1 D}if PD M1 gt{/M1 PD D}if
+ L1 PD sub M2 gt{/M2 L1 PD sub D}if}{DV ID 1 sub get 0 ge{Lo 0 R}if}ie}if
+ F0 cF ne Cs cS ne or{F0 Cs NF}if
+ /ms Ms D /Ms f D CP FB sub
+ C1 cvx exec XO EO sub L1 add TB{BW sub}if dup LM gt{/LM E D}{pop}ie
+ PH 0 eq PH 4 eq or Ms and{HF not{/PO t D /AH t D}if
+ BB CP YA add E AT 3 eq LB and{A1 sub}if TB{BW sub}if E BB}
+ {pop pop}ie Ms HM PH 3 eq and or{/BP f D /Fl f D}if
+ /Lo 0 D /L1 0 D /F0 cF D /Cs cS D BP not{0 YB NN neg R}if
+ OU f1 and mF not and{k2 /f1 f D}if
+ OU PF not and PB or{RE 1 gt{RE dup scale}if}if /Ms ms Ms or D
+ /C1 AF{(Cp )}{()}ie D /YA 0 D /YB 0 D BL
+ AT 4 eq LB not and PH 3 ge and
+ {ID DV length lt{DV ID get dup 0 ge{DO E sub /Lo E D /L1 Lo D}{pop}ie
+ /ID ID 1 add D}if}if /T t D CD{/LN LN 1 add D PD}if
+ /PD -1 D /NS 0 D /NB 0 D /TB f D /Ph f D /Mf 0 D /HM f D} D
+/RS {/TM E D /CN 0 D TM{10 eq{TM CN ( ) PI}if /CN CN 1 add D}forall
+ /CN 0 D /BK HM EN and{0}{1}ie D TM
+ {dup 32 ne{TM CN 3 2 roll put /CN CN 1 add D /BK 0 D}
+ {pop BK 0 eq{TM CN 32 put /CN CN 1 add D}if /BK 1 D}ie}forall
+ TM 0 CN GI dup dup () ne E ( ) ne and
+ {dup CN 1 sub get 32 eq{/EN f D}{/EN t D}ie}if} D
+/join {2 copy length E length add string dup 4 2 roll 2 index 0 3 index
+ PI E length E PI}d
+/WR {(\\n) search{dup () ne BP not or
+ {Li 4 le CP E pop YI Li mul add LE add 0 lt and PL 0 eq and{NP}if
+ SH NL pop /Li Li 1 sub D WR}{pop pop WR}ie}{SH}ie /CI 0 D /BP f D} D
+/SH {dup dup () ne E ( ) ne and PF or CS Mf gt and{/Mf CS D}if
+ T not Wf and{( ) E join /T t D}if dup BP{/MF CS D}if
+ AT 3 eq{2 copy length dup 0 gt{/NB E NB add D
+ {( ) search{/NS NS 1 add D pop pop}{pop exit}ie}loop}{pop pop}ie}if
+ CD PD 0 lt and{dup DC search{SW pop /PD E L1 add D pop pop}{pop}ie}if
+ 0 Np dup SW pop L1 add /L1 E D dup () ne
+ {C1 (\\() join E join (\\)) join AU AF and UF or Wf and{( U ) join}if
+ sF{( s ) join}if ( S ) join
+ /C1 E D dup length 1 sub get 32 eq /TB E D /Bw BW D}{pop pop}ie} D
+/BG {AI LG BC add add 0 eq} D
+/ON {OU{Ty AR AI NN get dup 1 add Ln Ns Ty 2 mod 0 eq{(.  )}{(\\)  )}ie join
+ dup SW pop neg 0 R CP E 0 lt{0 E M}{pop}ie CP BB show /Ms t D}if} D
+/Ln {AR AI 3 -1 roll put}D
+/SP {dup CI lt BP not and{dup CI sub 0 E R /CI E D}{pop}ie} D
+/BN {PF{WR /HM f D}{BT NL}ie} D
+/NN {dup 0 lt{pop 0}if} D
+/h {(h) HI ST cvs join cvx exec dup 1 get E Nf{0 get E join}{pop}ie} D
+/H {/fn FN D /Hi E 1 add D 1 sub /HL E D /H2 HL 2 add D /GS EZ H2 get D
+ E Tm H2 get GS mul BE dup 0 gt{1 sub}{pop EG H2 get dup 0 lt{pop AT}if}ie NA
+ WW Np /SL SL 1 add D /FN EF H2 get D GS Ey H2 get FS
+ EU H2 get Sc Hs not HL Hl lt and Hs HL hl lt and or Hi 0 eq or
+ {/HI Hi D /Hs t D /hl HL D /Hv HL D}if HL Hl lt{/hi Hi D}if
+ Nf HI 0 gt and{(h) Hi ST cvs join cvx exec 0 get WB}if
+ /HF t D /AH f D /PO f D} D
+/EH {Bm H2 get GS mul BE OA /SL SL 1 sub NN D /CF 0 D /FN fn D
+ SZ SL get FR SL get FS /HF f D /GS Ts D ()Ec} D
+/P {E PF{WR}{PO{EP}{BN}ie Ts 4 mul Np AE not{Tm 0 get Ts mul neg SP}if
+ dup 0 ge AH and{Pi Pd}if}ie 1 sub dup 0 lt{pop AV AL get}if /AT E D /PO t D} D
+/EP {PF{WR}{BN Ts 4 mul Np}ie AE not{Bm 0 get Ts mul neg SP}if
+ /AT AV AL get D /PO f D} D
+/BE {E PO{EP}{BN}ie Ts 4 mul Np neg SP} D
+/HR {/Aw W EO sub D /RW E dup 0 gt{Aw mul}{neg}ie dup Aw gt{pop Aw}if D /RZ E D
+ E BN Ts neg SP 1 sub 2 div Aw RW sub mul EO add CP E pop M PF{0 Ps neg R}if
+ 0 Np OU{gsave RZ LW Cf{Hc VC}{0 Sg}ie CP BB RW 0 RL CP BB stroke grestore}if
+ /CI 0 D /BP f D PF not{Ts neg SP}if /Ms t D} D
+/AD {I NL EG 14 get dup 0 lt{pop AT}if NA /AE t D Tm 14 get Ts mul neg SP
+ Cf{EU 14 get dup -1 eq{pop CA CL get}if Sc}if} D
+/DA {BN ()ES OA /AE f D ()Ec Bm 14 get Ts mul neg SP} D
+/PR {/MW E D /Li E D Tm 1 get Ps mul BE 0 NA /FN Fp D /PF t D SI /SL SL 1 add D
+ /CF 0 D Ps CS mul Ts div MW WC mul CS mul Ts div dup LL gt PL 0 eq and
+ {LL div div}{pop}ie Ey 1 get FS CP E pop LE add YI neg div cvi dup Li lt
+ AH and{4 lt YI Li mul 5 mul LE add 0 gt or PL 0 eq and{NP}if}{pop}ie
+ EU 1 get Sc /GS Ps D}D
+/RP {WR NL () /PF f D SI /FN 0 D ES Bm 1 get Ps mul neg SP OA /GS Ts D} D
+/SI {/XO Lm 15 get BC NN mul Lm 16 get AI UI sub NN mul add
+ Lm 17 get UI NN mul add Lm 20 get LG NN mul add Ts mul
+ PF{Lm 1 get Ps mul add}if EO add D
+ /MR Rm 15 get BC NN mul Rm 16 get AI UI sub NN mul add
+ Rm 17 get UI NN mul add Rm 20 get LG NN mul add Ts mul
+ PF{Rm 1 get Ps mul add}if D /LL W XO sub MR sub D} D
+/DT {/cC E D BN /LG LG 1 sub D SI /LG LG 1 add D WW 2 div Np BL} D
+/DD {WB Cc 0 eq cC 0 eq and L1 0 eq or Lm 20 get Ts mul L1 sub TB{BW add}if
+ Ts 2 div lt or NL /LF E D SI BL /cC 0 D} D
+/DL {Dc LG Cc put /Cc E D BG{Tm 18 get Ts mul BE}{BN}ie /LG LG 1 add D BL} D
+/LD {BN LG 0 gt{/LG LG 1 sub D}if /Cc Dc LG get D SI
+ BG{()Bm 18 get Ts mul BE}if BL} D
+/UL {BG{Tm 17 get Ts mul BE}{BN}ie NR AI NN 0 put /UI UI 1 add D
+ /AI AI 1 add D SI BL} D
+/LU {BN /UI UI 1 sub D /AI AI 1 sub D SI BG{()Bm 17 get Ts mul BE}if BL} D
+/OL {E BG{Tm 16 get Ts mul BE}{BN}ie TR AI NN Ty put /Ty E D NR AI NN 1 put
+ /AI AI 1 add D SI BL 1 Ln} D
+/LO {BN /AI AI 1 sub D /Ty TR AI get D SI BG{()Bm 16 get Ts mul BE}if BL} D
+/LI {E BN -1 SP /BP f D /CI 0 D 0 Np NR AI 1 sub NN get 1 eq
+ {dup dup 0 gt E 4 le and{/Ty E D}{pop}ie
+ /L1 L1 Ty AR AI NN get Ns SW pop XO sub dup 0 lt{pop 0}if add D ( ON )}
+ {pop ( B )}ie C1 E join /C1 E D CS Mf gt{/Mf CS D}if BL} D
+/BQ {Tm 15 get Ts mul BE /BC BC 1 add D SI BL} D
+/QB {Bm 15 get Ts mul BE /BC BC 1 sub D SI BL} D
+/Al {E EP 1 sub dup 0 lt{pop AV AL get}if NA} D
+/Ea {EP OA} D
+/WB {PF{WR}{BT}ie} D
+/F1 {WB /FN 0 D CS 0 FS} D
+/F2 {WB /FN WI D CS 0 FS} D
+/HY {/Hy t D WB /Hy f D} D
+/YH {WB} D
+/A {/LT E D LT 1 eq{/RN E D}if /Lh E D WB /C1 C1 ( Cp ) join D
+ Lc AF not and{Cl Sc}if /AF t D} D
+/EA {Lc AF and{Ec}{WB}ie TL Pa AF and Lh 0 ne and
+ {( \\() Lh join (\\)) join /AF f D WB}if /AF f D} D
+/TL {C1 ( Tl ) apa /C1 E D} d
+/apa {AF OU and Lh 0 ne LT 1 eq or and{LT 1 eq{RN ( /) E ST cvs join}
+ {(\\() Lh join (\\)) join}ie E join join}{pop}ie} d
+/Cp {/Xc CP /Yc E D D} D
+/SS {Cf{dup 0 ge{EU E get dup -1 eq{pop CA CL get}if}{pop CA CL get}ie Sc}
+ {pop}ie SZ SL get /SL SL 1 add D} D
+/I {WB 8 SS 1 FS} D
+/EM {WB 8 SS /CF CF 1 xor D 0 FS} D
+/BD {WB 9 SS 2 FS} D
+/TT {WB 10 SS /FN Fp D 0 FS} D
+/KB {WB 11 SS /FN Fp D 2 FS} D
+/CT {WB 12 SS 1 FS} D
+/SM {WB 13 SS /FN Fp D 0 FS} D
+/Q {/QL QL 1 add D QO QL 2 mod get La get join WB} D
+/EQ {QC QL 2 mod get La get join WB /QL QL 1 sub D} D
+/RO {WB -1 SS /CF 0 D 0 FS} D
+/SY {WB -1 SS -1 FS} D
+/MY {WB -1 SS -2 FS} D
+/ES {WB /SL SL 1 sub NN D /CF 0 D /FN FO SL get D SZ SL get FR SL get FS ()Ec}D
+/FZ {3 sub 1.2 E exp GS mul E WB TL /C1 C1 ( Cp ) join D /SL SL 1 add D 0 FS} D
+/Ef {WB TL ()ES /C1 C1 ( Cp ) join D} D
+/BZ {dup /Bf E D FZ}D
+/Sc {dup -1 ne Cf and{/CL CL 1 add D dup 0 eq{pop [0 0 0]}if
+ dup CA E CL E put VS ( VC ) join C1 E join /C1 E D}{pop}ie} D
+/Ec {WB Cf{/CL CL 1 sub NN D CA CL get VS ( VC ) join C1 E join /C1 E D}if} D
+/VS {dup type /arraytype eq{([) E {ST cvs join ( ) join}forall (]) join}if} D
+/VC {{255 div}forall setrgbcolor} D
+/Sl {dup type /integertype ne{Ds}if /La E D WB}d
+/UN {WB /UF t D} D
+/NU {WB /UF f D} D
+/SE {WB /sF t D} D
+/XE {WB /sF f D} D
+/sM {/C1 C1 ( k1 ) join D}d
+/eM {/C1 C1 ( k2 ) join D}d
+/k1 {/YC CP E pop Ts add D /mF t D /f1 t D}d
+/k2 {gsave 3 LW -9 CP E pop Ts 0.2 mul sub M -9 YC L stroke grestore /mF f D}d
+/Ac {/AC E D WB}d
+/Ca {eA{( \\()join AC join(\\) )join}if WB}d
+/s {OU{gsave 0 CS .25 mul R dup SW pop CJ 0 RL stroke grestore}if}D
+/CJ {AT 3 eq LB and{E dup dup length 1 sub A1 mul E
+ {( ) search{pop pop E A2 add E}{pop exit}ie}loop 3 -1 roll add
+ W CP pop sub 2 copy gt{E}if pop}if}D
+/So {/Co E D} D
+/SO {C1 Yo ST cvs join ( So ) join /C1 E D (j) SW pop 2 div Pd} D
+/Se {E WB CS E div Pd}D
+/Pd {dup type /stringtype eq{SW pop}if dup /L1 E L1 add D
+ ST cvs ( 0 R ) join C1 E join /C1 E D} D
+/Sp {0.35 CO} D
+/Sb {-0.2 CO} D
+/CO {OV Io Yo put /Yo E CS mul Yo add D /Io Io 1 add D -1.5 Io mul 3 add FZ SO
+ CS Yo add dup YA gt{/YA E D}{pop}ie
+ Yo neg dup YB gt{/YB E D}{pop}ie} D
+/Es {ES /Io Io 1 sub NN D /Yo OV Io get D SO} D
+/SB {/N2 0 D 0 1 NI{/N E D{IX N2 get 0 lt{/N2 N2 1 add D}{exit}ie}loop
+ /K WS N get FC N get mul D /NY AY N2 get D /BV NY array D
+ 0 1 NY 1 sub{/TM K string D currentfile TM readhexstring pop pop BV E TM put}
+ for BM N BV put /N2 N2 1 add D}for} D
+/IC [{/MA E D /MB 0 D}{2 div /MA E D /MB MA D}{/MB E CS sub D /MA CS D}
+ {pop /MA YS AB mul D /MB 1 AB sub YS mul D}{pop /MA 0 D /MB 0 D}] D
+/IP {BV N get /N N 1 add D} D
+/II {/K E D IX K get 0 lt{/EC E D}if /TY E D
+ TY 4 eq{/Y E D /X E D}if TY 3 eq{/AB E D}if
+ /XW AX K get D /YW AY K get D /IS SG IT K get get D /XS XW IS mul D
+ /YS YW IS mul D YS IC TY get exec /MA MA Fl not{3 add}if D} D
+/IM {II /ty TY D /xs XS D /ys YS D /ya YA D /yb YB D /ma MA D /mb MB D /k K D
+ /ec EC D /BP f D /CI 0 D WB TL L1 xs add dup XO add MR add W gt
+ {pop /ma ma Fl{3 add}if D NL /YA ma D /YB mb D /YS ys D /L1 xs D}
+ {/L1 E D ma YA gt{/YA ma D}if mb YB gt{/YB mb D}if}ie /TB f D
+ OU{CP E pop YS sub LE neg lt Fl not and PB not and{NP /YA ma D /YB mb D}if
+ /BP f D ty ST cvs ( ) join IX k get 0 lt{(\\() join ec join (\\) ) join}if
+ k ST cvs join ty 3 eq{AB ST cvs ( ) join E join}if
+ ty 4 eq{X ST cvs ( ) join Y ST cvs join ( ) join E join}if C1 E join
+ ( DI ) join FP 2 eq FP 1 eq AF and or{( FM ) join}if
+ ( Il Cp ) apa /C1 E D /EN f D}if /HM t D /T f D} D
+/DI {II /Xc CP /Yc E D D /YN YW neg D /HM t D /CI 0 D /K2 IX K get D gsave
+ TY 4 eq{OX X IS mul add OY FY add YS sub Y IS mul sub}
+ {/FY YS D CP MB sub 2 copy /OY E D /OX E D}ie
+ translate K2 0 ge{/DP AZ K2 get D /BV BM K2 get D XS YS scale /N 0 D XW YW DP
+ [XW 0 0 YN 0 YW] {IP} FC K2 get 1 eq{image}{f 3 colorimage}ie}
+ {EX}ie grestore XS 0 R /Ms t D} D
+/FM {gsave 0 Sg CP MB sub translate XS neg 0 M 0 YS RL XS 0 RL 0 YS neg RL
+ XS neg 0 RL stroke grestore} D
+/NA {/AT E D /AL AL 1 add D AV AL AT put} D
+/OA {AL 0 gt{/AL AL 1 sub D /AT AV AL get D}if} D
+/D1 {/BR {CP E pop E BN Mb{CP E pop eq{0 YI R}if}{pop}ie} D
+ /Sn {OU{C1 E ST cvs join ( Ld ) join /C1 E D}{pop}ie} D} D
+/D1 {/BR {BN} D /Sn {OU {C1 E ST cvs join ( Ld ) join /C1 E D} {pop} ie} D} D
+/TC {/TF t D /ML 0 D HN{SW pop dup ML gt{/ML E D}{pop}ie}forall NP /RM RM not D
+ RC /OU Tc D Ep /PN 0 D Ms not TP and{Ip}if /W IW ML sub Ts sub D
+ /A0 0 D TH{/BR {( ) join BT} D /Sn {pop} D /Au () D}if} D
+/TN {0 eq{E EA PF HF or not XR and{HN E get Xr}{pop}ie}
+ {OU{Tn 0 ge{() BN}if /Tn E D}{pop}ie WB}ie} D
+/NT {OU LB not and Tn 0 ge and{PL 0 eq{Ms not{CS CF FS}if CP dup
+ /y E YA sub D W 9 sub CS -1.8 mul XO L1 add 2 add{y M (.) show}for
+ HN Tn get dup SW pop IW E sub y M show CP BB M}if /Tn -1 D}if} D
+/Ld {/DN E D HN DN Pn put [/View [/XYZ -4 Fl{PS}{CP YA add US E pop}ie null]
+ /Dest DN ST cvs cvn /DEST pdfmark} D
+/C {ND 1 eq{1 sub}if TI mul /XO E D NL Nf not{pop()}if 0 3 -1 roll 1 A} D
+/OP {BP not{NP}if PN 2 mod 0 eq{NP}if}D
+/Ep {Xp PN 2 mod 0 eq and OU and{/Pn (-) D showpage /PM 1 D LA}if}D
+/Dg [73 86 88 76 67 68 77] D
+/Rd [0 [1 1 0][2 1 0][3 1 0][2 1 1][1 1 1][2 2 1][3 3 1][4 4 1][2 1 2]] D
+/Ns {/m E D /c E 32 mul D /j m 1000 idiv D /p j 12 add string D
+ c 96 le m 0 gt and{c 32 le {/i 0 D /d 77 D /l 100 D /m m j 1000 mul sub D
+  j -1 1 {pop p i d c add put /i i 1 add D}for
+  4 -2 0 {/j E D /n m l idiv D /m m n l mul sub D /d Dg j get D
+   n 0 gt {/x Rd n get D x 0 get -1 1 {pop p i d c add put /i i 1 add D}for
+   p i x 1 get sub Dg x 2 get j add get c add put}if /l l 10 idiv D
+  }for p 0 i GI}
+  {/i ST length 1 sub D m {1 sub dup 0 ge{dup 26 mod c add 1 add
+   ST i 3 -1 roll put 26 idiv dup 0 eq{pop exit}if}if /i i 1 sub D}loop
+   ST i ST length i sub GI}ie}
+ {m p cvs}ie} D
+/US {matrix currentmatrix matrix defaultmatrix matrix invertmatrix
+ matrix concatmatrix transform} D
+/GB {Gb{US}if}D
+/Tl {/Rn E D Xc CP pop ne{
+ [/Rect [Xc 1 sub Yc cS 0.25 mul sub GB CP E 1 add E cS 0.85 mul add GB]
+  /Subtype /Link /Border [0 0 Cf Lc and LX and AU or{0}{1}ie] Rn type
+  /nametype eq {/Dest Rn}{/Action [/Subtype /URI /URI Rn] Cd}ie
+  /ANN pdfmark}if} D
+/Il {/Rn E D [/Rect [Xc Yc GB Xc XS add Yc YS add GB] /Subtype /Link
+ /Border [0 0 0] Rn type /nametype eq{/Dest Rn}
+ {/Action [/Subtype /URI /URI Rn] Cd}ie /ANN pdfmark} D
+/XP {[{/Z Bz 2 div D Z 0 R Z Z RL Z neg Z RL Z neg Z neg RL Z Z neg RL
+ Fi cH 1 eq and{fill}if} {Bz 0 RL 0 Bz RL Bz neg 0 RL 0 Bz neg RL
+ Fi cH 1 eq and{fill}if} {0 -5 R Bz 0 RL 0 21 RL Bz neg 0 RL 0 -21 RL}]} D
+/MS {/Sm E D WB}D
+/O {BN()Sm BX} D
+/BX {/Bt E D Bt 2 lt{/Ch E D CS 0.8 mul}{11 mul}ie W XO sub MR sub
+ 2 copy gt{E}if pop /HZ E D Bt 2 eq{Fi not{pop()}if ( )E join /Ft E D TT
+ /PF t D /MW 1 D /Li 1 D /Fw Ft SW pop D Fw HZ gt{/HZ Fw 8 add D}if
+ HZ ST cvs( )join}{WB Ch ST cvs( )join}ie L1 HZ add XO add MR add W gt{NL}if
+ Bt 2 eq{Ft ES Fw neg HM{CS sub}if Pd}if Bt ST cvs join( Bx )join
+ Bt 2 eq HM and{CS Pd}if C1 E join /C1 E D /L1 L1 HZ add D /T f D
+ ( ) Pd /PF f D Bt 2 lt{YA CS .8 mul lt{/YA CS .8 mul D}if}
+ {YB 5 lt{/YB 5 D}if YA 21 lt{/YA 21 D}if}ie /CI 0 D} D
+/Bx {dup 2 eq{E /Bz E D}{E /cH E D /Bz CS .8 mul D}ie
+ OU {gsave 0 Sg XP E get exec stroke grestore}{pop}ie Bz 0 R /Ms t D}D
+/SD {FD 4 mul Dy add DZ NF newpath 0 0 M DX t charpath pathbbox
+ 3 -1 roll sub /DY E D E dup /X1 E D sub WM mul WX DY mul add WM DG mul E div
+ /DF E D /DR WX DF mul DY mul WM div 2 div D} d
+/Sd {gsave 0 IL Di mul neg translate IL IW atan Di 0 eq{neg}if rotate
+ FD 4 mul Dy add DZ NF DR X1 sub DY 2 div neg M cD VC DX show grestore} d
+/Pt {/tp t D Tp{NP /Pn (TP) D 0 Tt neg R Th BN NP Ep ET RC ZF}if /tp f D} D
+/RC {/AI 0 D /LG 0 D /BC 0 D /UI 0 D /PF f D /Cc 0 D /cC 0 D /Dc 10 array D
+ /NR [0 1 9{pop 0}for] D /La Ds D /AR 10 array D /TR 10 array D /AV 30 array D
+ SI /AL -1 D /AT A0 D AT NA /OV 9 array D /Yo 0 D /Co 0 D /Io 0 D /Hy f D
+ /Ph f D /CL -1 D Ct Sc}D
+/ZF {/FR [0 1 30{pop 0}for] D /SZ [0 1 30{pop 0}for] D /FO [0 1 30{pop 0}for] D
+ /SL 0 D /CF 0 D /FN 0 D 0 Ts SF}D
+/QO [[($qo)][($qo2)]] D
+/QC [[($qc)][($qc2)]] D
+/Hf EF length 2 sub D
+/Hz EZ Hf get D
+/HS Ey Hf get D
+/Fz EZ Hf 1 add get D
+/Fs Ey Hf 1 add get D
+/LE IL D
+/Ps EZ 1 get D
+/Fp EF 1 get D
+/XO 0 D
+/YI 0 D
+/CI 0 D
+/FP 0 D
+/WW Ts 7 mul D
+/Mf 0 D
+/YA 0 D
+/YB 0 D
+/Cs Ts D
+/GS Ts D
+/F0 0 D
+/NS 0 D
+/NB 0 D
+/N 0 D
+/C0 [] D
+/C1 () D
+/Lo 0 D
+/L1 0 D
+/LM 0 D
+/PH 0 D
+/EC 0 D
+/Lh 0 D
+/LT 0 D
+/CH 1 string D
+/ST 16 string D
+/CA 9 array D
+/HC (\\255) D
+/HM f D
+/PF f D
+/EN f D
+/TB f D
+/UF f D
+/sF f D
+/AE f D
+/AF f D
+/BP t D
+/CD f D
+/PA t D
+/GL f D
+/T t D
+/HF f D
+/AH f D
+/SA f D
+/PB f D
+/f1 f D
+/mF f D
+/OX 0 D
+/OY 0 D
+/FY 0 D
+/EO 0 D
+/FB 0 D
+/PL 0 D
+/Bw 0 D
+/PD -1 D
+/TP f D
+/tp f D
+/TH $th D
+/Ty 4 D
+/Tn -1 D
+/Fl t D
+/LB t D
+/PM 1 D
+/Ms f D
+/Ba f D
+/Bb f D
+/Hl 3 D
+/hl 6 D
+/Hv 6 D
+/Hs f D
+/HI 0 D
+/hi 0 D
+/PO t D
+/TE f D
+/LF t D
+/BO 0 D
+/Sm 1 D
+/Bf 3 D
+/A1 0 D
+/A2 0 D
+/Ds $lid{'en'} D
+/QL -1 D
+/Cb Db D
+/Ct Dt D
+/Cl Dl D
+EOT
+
+$tbl=<<EOT;
+/TS {
+ tables E get /table E D
+ table aload pop /rdesc E D /cdesc E D /tdesc E D
+ tdesc aload pop /capalg E D /caption E D /rules E D /frame E D /nfoot E D
+  /nhead E D /ncol E D /nrow E D /border E D /twid E D /units E D /talign E D
+  /flow E D /clear E D /tclass E D pop pop
+ /w W D /eps 0.1 D /OU f D /PL 1 D
+ /FN EF 21 get D EZ 21 get Ey 21 get FS
+ 0 1 1{
+  /pass E D
+  0 1 nrow{
+   /irow E D
+   /cells rdesc irow get 6 get D
+   0 1 ncol{
+    /icol E D
+    /cell cells icol get D
+    cell 0 ne{
+     cell aload pop /ang E D /CB E D pop pop pop
+     /DV E D /bot E D /top E D /right E D /left E D /nowrap E D /valign E D
+     /dp E D /align E D /rspan E D /cspan E D /cclass E D /ctype E D /cmax E D
+     /cmin E D /proc E D
+     rspan 0 eq{/rspan nrow irow sub 1 add D}if
+     cspan 0 eq{/cspan ncol icol sub 1 add D}if
+     pass 0 eq cspan 1 eq and pass 1 eq cspan 1 gt and or{
+      /W 1e5 D /LL W D /PH 1 D
+      ctype 1 eq{() BD}if
+      RC align NA
+      AT 4 eq{/CD t D /DC dp D /LN 0 D /M1 0 D /M2 0 D}{/CD f D}ie
+      0 0 M /LM 0 D proc exec BN
+      AT 4 eq{
+       LN array astore cell 15 3 -1 roll put
+       cdesc icol get dup dup 5 get M1 lt{5 M1 put}{5 get /M1 E D}ie
+       dup 6 get M2 lt{6 M2 put}{6 get /M2 E D}ie
+       /LM M1 M2 add D
+      }if
+      /CD f D
+      ang 0 ne{/LM CP E pop neg D}if
+      /thiswid LM left add right add eps add D
+      /oldmin 0 D /oldmax 0 D
+      0 1 cspan 1 sub{
+       icol add cdesc E get dup 2 get /oldmax E oldmax add D
+       1 get /oldmin E oldmin add D
+      }for
+      thiswid oldmax ge{
+       0 1 cspan 1 sub{
+        icol add cdesc E get dup 2 E 2 get oldmax 0 eq
+         {pop thiswid cspan div}{thiswid mul oldmax div}ie
+        put
+       }for
+      }if
+      nowrap 1 eq{
+       thiswid oldmin ge{
+        0 1 cspan 1 sub{
+         icol add cdesc E get dup 1 E 1 get oldmin 0 eq
+          {pop thiswid cspan div}{thiswid mul oldmin div}ie
+         put
+        }for
+       }if
+      }{
+       /W 0 D /LL W D /PH 2 D
+       ctype 1 eq{() ES () BD}if
+       0 0 M /LM 0 D RC proc exec BN
+       /thiswid LM left add right add eps add D
+       thiswid oldmin ge{
+        0 1 cspan 1 sub{
+         icol add cdesc E get dup 1 E 1 get oldmin 0 eq
+          {pop thiswid cspan div}{thiswid mul oldmin div}ie
+         put
+        }for
+       }if
+      }ie
+      ctype 1 eq{() ES}if
+     }if
+    }if
+   }for
+  }for
+ }for
+ /tmin 0 D /tmax 0 D
+ 0 1 ncol{
+  cdesc E get dup 1 get E 2 get 2 copy gt{pop dup}if
+  tmax add /tmax E D tmin add /tmin E D
+ }for
+ twid 0 lt{twid neg IW gt{IW neg}{twid}ie /twid E D}if
+ tdesc 0 twid neg tmin 2 copy lt{E}if pop put
+ tdesc 1 twid neg tmax 2 copy lt{E}if pop put
+ /W w D /LL W D /OU t D /PH 0 D /PL 0 D
+} D
+/PT {
+ /PL PL 1 add D
+ tables E get /table E D Tm 21 get Ts mul BE
+ PL 2 ge{save}if
+ /SL SL 1 add D /FN EF 21 get D EZ 21 get Ey 21 get FS
+ table aload pop /rdesc E D /cdesc E D /tdesc E D
+ tdesc aload pop /capalg E D /caption E D /rules E D /frame E D /nfoot E D
+  /nhead E D /ncol E D /nrow E D /border E D /twid E D /units E D /talign E D
+  /flow E D /clear E D /tclass E D /tmax E D /tmin E D
+ /w W D /xo XO D /mr MR D /ll LL D /lg LG D /ai AI D /bc BC D /nr NR D /ar AR D
+ /tr TR D /ui UI D /ph PH D /a0 A0 D /pf PF D /at AT D /av AV D /al AL D
+ /Le LE D /la La D
+ talign 0 lt{/talign AL 0 gt{AV AL get}{A0 2 le{A0}{0}ie}ie D}if
+ ph 1 eq ph 2 eq or{
+  NL ph 1 eq{tmax}{tmin}ie dup XO add LM gt{/LM E XO add D}{pop}ie LM E
+ }{
+  /PH 3 D /LE 1e5 D RC %ZF
+  border 0 gt{/border 1 D}if
+  /twidth 0 D /avail W xo sub D
+  twid 0 eq{0 1 ncol{cdesc E get dup 2 get E 3 get dup 0 gt{div neg dup twid lt
+   {/twid E D}{pop}ie}{pop pop}ie}for}if
+  /twid twid dup 0 lt{neg avail 2 copy gt{E}if pop}{avail mul}ie D
+  /OK t D 0 1 ncol{cdesc E get dup 1 get E 3 get twid mul gt{/OK f D}if}for
+  0 1 ncol{
+   cdesc E get dup 1 get /colmin E D dup 3 get /cwid E twid mul D dup
+   tmax avail le{2 get}if
+   tmin avail le tmax avail gt and{
+    dup 2 get E 1 get dup 3 1 roll sub avail tmin sub mul tmax tmin sub div add
+   }if
+   tmin avail gt{1 get}if
+   0 E colmin cwid lt OK and{pop cwid}if dup /twidth E twidth add D put
+  }for
+  /OU f D CP
+  tmin twid le{
+   0 1 ncol{cdesc E get dup 0 get twidth div twid mul 0 E put}for
+   /twidth twid D
+  }if
+  CP printcap CP E pop sub /caphig E D pop
+  0 1 1{
+   /pass E D
+   0 1 nrow{
+    /irow E D
+    /cells rdesc irow get 6 get D
+    0 1 ncol{
+     /icol E D
+     /cell cells icol get D
+     cell 0 ne{
+      cell aload pop /ang E D /CB E D pop pop pop
+      /DV E D /bot E D /top E D /right E D /left E D /nowrap E D /valign E D
+      /dp E D /align E D /rspan E D /cspan E D /cclass E D /ctype E D /cmax E D
+      /cmin E D /proc E D
+      rspan 0 eq{/rspan nrow irow sub 1 add D}if
+      cspan 0 eq{/cspan ncol icol sub 1 add D}if
+      /W 0 D
+      0 1 cspan 1 sub{icol add cdesc E get 0 get /W E W add D}for
+      pass 0 eq rspan 1 eq and pass 1 eq rspan 1 gt and or{
+       ctype 1 eq{() BD}if
+       /W W left sub right sub D /XO 0 D /EO 0 D SI
+       /A0 align D RC align NA
+       AT 4 eq{
+        /DC dp D /DO 0 D /ID 1 D
+        0 1 DV length 1 sub{DV E get dup DO gt{/DO E D}{pop}ie}for
+        /Lo DO DV 0 get sub D /L1 Lo D
+       }if
+       0 0 M /BP t D /Fl t D /MF 0 D /FB 0 D
+       proc exec T not{/CI 0 D}if BN 0 FB neg R MF 0 eq{/MF CS D}if
+       CP /thishig E neg bot add top add CI add D pop
+       ang 0 ne{/thishig LM bot add top add D}if
+       cell 16 MF put cell 17 Ya put cell 18 thishig put
+       valign 4 eq{
+        /below thishig Ya sub D
+        rdesc irow get dup dup 4 get Ya lt
+         {4 Ya put}{4 get /Ya E D}ie
+        dup 5 get below lt{5 below put}{5 get /below E D}ie
+        /thishig Ya below add D
+       }if
+       ctype 1 eq{()ES}if
+       /oldhig 0 D
+       0 1 rspan 1 sub{
+        irow add rdesc E get 0 get /oldhig E oldhig add D
+       }for
+       thishig oldhig ge{
+        0 1 rspan 1 sub{
+         irow add rdesc E get dup 0 E 0 get oldhig 0 eq
+          {pop thishig rspan div}{thishig mul oldhig div}ie
+         put
+        }for
+       }if
+      }if
+     }if
+    }for
+   }for
+  }for M RC %ZF
+  /thight 0 D /racc 0 D /maxh 0 D /brk 0 D /rbeg nhead nfoot add D
+  rbeg 1 nrow{
+   rdesc E get dup 0 get dup /thight E thight add D
+   brk 0 eq{/racc E D}{/racc E racc add D}ie
+   racc maxh gt{/maxh racc D}if 2 get /brk E D
+  }for
+  ph 3 ge{thight caphig add E}if
+  ph 0 eq ph 4 eq or{
+   /PH 4 D /LE Le D /OU Ou D /yoff 0 D /headsz 0 D
+   0 1 nhead 1 sub{rdesc E get 0 get headsz add /headsz E D}for
+   /footsz 0 D
+   0 1 nfoot 1 sub{rdesc E nhead add get 0 get footsz add /footsz E D}for
+   /ahig LE BO add MI add D /maxh maxh headsz add footsz add D
+   /thight thight headsz add footsz add D
+   tmin avail gt maxh ahig gt or
+    {/Sf avail tmin div dup ahig maxh div gt{pop ahig maxh div}if D /SA t D}
+    {/Sf 1 D}ie
+   tclass 1 eq thight LE 15 sub gt and
+    {/SA t D LE 15 sub thight div dup Sf lt{/Sf E D}{pop}ie}if
+   SA{Sf Sf scale /ll ll Sf div D /xo xo Sf div D /LE LE Sf div D
+    /mr mr Sf div D /BO BO Sf div D /ahig ahig Sf div D}if
+   nhead nfoot add getwid
+   LE CP E pop add capalg 0 eq{caphig sub}if
+   bT{f}{dup thight lt thight ahig lt and}ie
+   E headsz sub footsz sub rwid lt or{NP}if
+   capalg 0 eq{printcap -8 SP}if
+   CP /ycur E D pop
+   printhead
+   rbeg 1 nrow{/row E D row
+    getwid
+    ycur yoff add rwid sub footsz sub LE add 0 lt
+    {nfoot 0 gt{printfoot}if Tf NP /rbeg irow1 D
+     Ba{MI /MI MI SA{Sf div}if D MI SP /MI E D}if
+     CP /ycur E D pop /yoff 0 D printhead}if
+    irow1 printrow
+   }for
+   printfoot /row row 1 add D Tf
+   0 ycur yoff add M
+   capalg 1 eq{/EO 0 D SI -3 SP printcap}if
+   Sf 1 lt{1 Sf div dup scale /ll ll Sf mul D /xo xo Sf mul D /LE LE Sf mul D
+    /mr mr Sf mul D /BO BO Sf mul D /SA f D}if
+   /EO 0 D
+  }if
+ }ie
+ /W w D /XO xo D /MR mr D /LL ll D /LG lg D /AI ai D /BC bc D /NR nr D /AR ar D
+ /TR tr D /UI ui D /PH ph D /A0 a0 D /PF pf D /AT at D /AV av D /AL al D
+ /La la D
+ /SL SL 1 sub NN D /CF 0 D /FN 0 D SZ SL get FR SL get FS Wf not{()F2}if
+ PL 2 ge{Ms E restore Ms or /Ms E D PH 1 eq PH 2 eq or
+  {/LM E D}if PH 3 ge{/CI 0 D NL 0 E neg R}if
+ }if
+ /PL PL 1 sub D /CI 0 D /BP f D /PO f D () Bm 21 get Ts mul BE BL %CF CS SF
+} D
+/printcap{
+ capalg 0 ge{
+  SA{/W w Sf div D}
+   {talign 1 eq{/XO xo ll twidth sub 2 div add D}if
+    talign 2 eq{/XO xo ll twidth sub add D}if
+    /W XO twidth add D
+   }ie /XO xo D /LL W XO sub MR sub D
+  /PA f D /Fl capalg 0 eq D
+  1 NA BL caption exec BN OA /PA t D
+ }if
+} D
+/getwid{
+ /irow1 E D
+ /irow2 irow1 D
+ /rwid 0 D
+ {rdesc irow2 get dup 0 get rwid add /rwid E D 2 get 0 eq
+  {exit}{/irow2 irow2 1 add D}ie
+ }loop
+} D
+/printrow{
+ /xoff ll twidth PL 2 ge{Sf div}if sub talign mul 2 div D
+ /xleft xoff xo add D
+ /irow E D
+ /cells rdesc irow get 6 get D
+ 0 1 ncol{
+  /icol E D
+  /cell cells icol get D
+  cell 0 ne{
+   cell aload pop /ang E D /CB E D /cvsize E D /above E D /fontsz E D
+   /DV E D /bot E D /top E D /right E D /left E D /nowrap E D /valign E D
+   /dp E D /align E D /rspan E D /cspan E D /cclass E D /ctype E D /cmax E D
+   /cmin E D /proc E D
+   rspan 0 eq{/rspan nrow irow sub 1 add D}if
+   cspan 0 eq{/cspan ncol icol sub 1 add D}if
+   /width 0 D
+   0 1 cspan 1 sub{icol add cdesc E get 0 get /width E width add D}for
+   /rhight rdesc irow get 0 get D
+   /hight rhight D
+   1 1 rspan 1 sub{irow add rdesc E get 0 get /hight E hight add D}for
+   /W xo xoff add width add right sub D
+   ang 0 ne{/W xo xoff add hight add right sub D}if
+   /EO xo xoff add left add D SI
+   Cf{
+    gsave CB VC xo xoff add ycur yoff add M
+    0 hight neg RL width 0 RL 0 hight RL width neg 0 RL fill
+    grestore
+   }if
+   ctype 1 eq{() BD}if
+   /A0 align D RC
+   AT 4 eq{
+    /DC dp D /ID 1 D /DO cdesc icol get 5 get D /Lo DO DV 0 get sub D /L1 Lo D
+   }if
+   ang 0 ne{
+    gsave ang 90 eq
+     {xoff ycur add hight cvsize sub 2 div sub ycur hight sub xoff sub}
+     {xoff ycur sub width add hight cvsize sub 2 div add ycur xoff add}ie
+    translate ang rotate
+   }if
+   valign 3 le{0 ycur yoff add top sub
+    hight cvsize sub valign 1 sub mul 2 div sub M}
+   {0 ycur yoff add top sub above add rdesc irow get 4 get sub M}ie
+   /PA f D /BP t D /Fl t D
+   BL proc exec BN
+   ang 0 ne{grestore}if
+   /PA t D
+   ctype 1 eq{() ES}if
+  }if
+  /xoff xoff cdesc icol get 0 get add D
+ }for
+ /yoff yoff rhight sub D
+} D
+/printhead {0 1 nhead 1 sub{printrow}for} D
+/printfoot {nhead 1 nhead nfoot add 1 sub{printrow}for} D
+/Tf {
+ OU{rules 2 ge{/yoff 0 D
+   gsave 0 Sg
+   [0 1 nhead 1 sub{}for rbeg 1 row 1 sub{}for nhead 1 nhead nfoot add 1 sub{}for]{
+    /irow E D
+    /xoff ll twidth PL 2 ge{Sf div}if sub talign mul 2 div D
+    /cells rdesc irow get 6 get D
+    0 1 ncol{
+     /icol E D
+     /cell cells icol get D
+     cell 0 ne{
+      /rspan cell 6 get D
+      /cspan cell 5 get D
+      rspan 0 eq{/rspan nrow irow sub 1 add D}if
+      cspan 0 eq{/cspan ncol icol sub 1 add D}if
+      /width 0 D
+      0 1 cspan 1 sub{icol add cdesc E get 0 get /width E width add D}for
+      /rhight rdesc irow get 0 get D
+      /hight rhight D
+      1 1 rspan 1 sub{irow add rdesc E get 0 get /hight E hight add D}for
+      xo xoff add width add ycur yoff add M
+      0 hight neg icol cspan add 1 sub ncol lt
+       {cdesc icol 1 add get 4 get dup rules 3 le{1 eq}{pop t}ie
+        {1 eq{0.8}{0.3}ie
+        LW RL CP stroke M}{pop R}ie}{R}ie
+      irow nhead nfoot add 1 sub ne nfoot 0 eq or
+       {irow rspan add 1 sub nrow lt
+       {rdesc irow rspan add get 3 get}{nfoot 0 eq{0}{1}ie}ie
+       dup rules 2 mod 0 eq{1 eq}{pop t}ie
+       {1 eq irow rspan add nhead eq or irow rspan add row eq nfoot 0 gt and or
+        {0.8}{0.3}ie LW width neg 0 RL CP stroke M}{pop}ie}if
+     }if
+     /xoff xoff cdesc icol get 0 get add D
+    }for
+    /yoff yoff rhight sub D
+   }forall
+   grestore
+   /Ms t D
+  }if
+  frame 1 gt{
+   gsave
+   1 LW 0 Sg
+   xleft ycur M CP BB
+   0 yoff frame 5 eq frame 7 ge or{RL}{R}ie
+   twidth 0 frame 3 eq frame 4 eq or frame 8 ge or{RL}{R}ie CP BB
+   0 yoff neg frame 6 ge{RL}{R}ie
+   twidth neg 0 frame 2 eq frame 4 eq or frame 8 ge or{RL}{R}ie
+   closepath stroke
+   grestore
+   /Ms t D
+  }if
+ }if
+} D
+EOT
+
+&openps if($opt_o);
+$ntab=-1;
+$tables="/tables [";
+@docs=$#ARGV<0?("-"):@ARGV;
+if($tocdoc) {$#docs=0};
+for (@docs) {$levl{$_}=1};
+$nref=0;
+$nhd=0;
+$nlnk=1;
+$ndoc=0;
+$nrem=0;
+$toc=$first?"Pt\n":"";
+$toc.="/BO 0 D TC /Ba f D Bs /AU f D /UR () D RC ZF\n";
+$toc.="()F2" if(!$latin1);
+$toc.="tH WB\n" if(!$tocdoc);
+$fl1="";
+$fl2="";
+$np="NP RC ZF";
+$P3="";
+while($html=shift @docs) {
+  $ndoc++;
+  $ba2="";
+  $P2="(";
+  $banner="";
+  undef @links;
+  $level=$levl{$html};
+  if(&h2p) {
+    if($ndoc==1) {
+      $toc=~s/\$T/$ti/g;
+      $toc=~s/\$A/$au/g;
+      $toc=~s/[\200-\377]+/)F1($&)F2(/g if(!$latin1);
+    }
+    if($layer) {
+      @docs=(@docs,@links);
+    } else {
+      @docs=(@links,@docs);
+    }
+    $rem=$#docs+1;
+    if($rem && $opt_W) {
+      &dbg("At least $rem document".($rem>1?"s":"")." remaining\n");
+    }
+    if($banner) {
+      $_="/Ba t D /BA {($banner)BN} D\nBs f Pb CP /BO E D pop\n";
+      &Subst($_);
+      s/  H\(/ -1 H(/g;
+    } else {
+      $_="/Ba f D /BO 0 D Bs";
+    }
+    if($tocdoc && $first && $ndoc==1) {
+      $TC=" TC\n";
+      $et=" NP Ep ET /Tc f D";
+    } else {
+      $TC="";
+      $et="";
+    }
+    $_.="\n/UR ($html) D\n/Ti ($ti) D\n/Au ($au) D\n/Df $draft D\n/ME [";
+    for $i (sort {$metarc{$a} <=> $metarc{$b}} keys %metarc){
+      $_.="($meta{$i})";
+    }
+    $_.="] D\n$TC";
+    if($ndoc==1) {$top=$_};
+    if(!$tocdoc) {
+      $toc.="ND 1 gt{Ts 3 mul Np $refs{$html}()0 C()BD($ti)ES()$refs{$html}"
+           ." 1 TN()EA()BN}if\n";
+    }
+    $hv=0;
+    while($P2=~s/(\d) (\d)  H\(([^\s<)]*)/$1 $2 $nhd H($3)WB $nref Sn(/) {
+      $nhd++;
+      if($hv+1<$2) {
+        for($hv+1..$2-1) {
+          push(@z1,-$nref);
+          push(@z2,$_);
+        }
+      }
+      $hv=$2;
+      $hind[$hv-1]++;
+      for $i ($hv..5) {$hind[$i]=0};
+      $hind=join('.',@hind[0..$hv-1]);
+      $hst=$3;
+      $'=~/\)EH/;
+      ($htxt=$hst.$`)=~s/\)EA\(//g;
+      if(!$tocdoc) {
+        $toc.="$hv NH le{$nref($hind\\240\\240)$hv C($htxt)$nref 1 TN()EA()BN}if\n";
+      }
+      push(@z1,$nref);
+      push(@z2,$hv);
+      $nref++;
+      $htxt=~s/(\s+|\)BR\()/ /g;
+      $htxt=~s/(^\s+|\)[^(]*\(|\s+$)//g;
+      $htxt="" if(!$latin1);
+      $dh.="/h$nhd [($hind\\240\\240)($htxt)] D\n";
+    }
+    if($tocdoc) {
+      if($ndoc==1 && !$first) {
+        $toc="TC RC ZF $_ $P2 WB () BN\n";
+        $P2="";
+        $P3="";
+        $np="";
+        $_="";
+      }
+    }
+    $P3.="$fl1\n/Cb $bg D /Ct $tcol D /Cl $lcol D /CL -1 D Ct Sc\n";
+    if($ndoc==1 && !$first) {$P3.="Pt\n"};
+    $P3.="$fl2\n$_\n$np\n$P2";
+    if($tocdoc && $ndoc==1 && !$first) {
+      $np="/Cb $bg D NP RC ZF";
+    } else {
+      $fl1="WB NL$et";
+      $fl2="DS";
+      $np="0 BO R";
+    }
+  }
+}
+
+$P3.=($P3!~/\)\s*$/?"()":"")."WB NL";
+if(!$tocdoc && $first && $nhd){$P3="$toc/OU t D /Cb $bg D NP Ep ET $P3"};
+if(!$first && ($tocdoc || !$tocdoc && $nhd)){$P3.=" $toc"};
+
+if($ntab>=0) {
+  $_="$tables] D";
+  &ack($_);
+  y/\t\f/ /;
+  s/[\200-\377]/sprintf("\\%3.3o",ord($&))/eg;
+  s/\)XX/)9 9 PR/g;
+  s/  H\(/ -1 H(/g;
+  $tables=$_;
+}
+$_="%!PS\n%%Title: $title\n$P0$P1";
+if($nimg>=0) {
+  $_.="/AX [".join(' ',@XS)."] D\n/AY [".join(' ',@YS)."] D\n"
+     ."/IX [".join(' ',@IX)."] D\n/IT [".join(' ',@IT)."] D\n";
+  if($nm>=0) {
+    $_.="/AZ [".join(' ',@DP)."] D\n/WS [".join(' ',@WS)."] D\n"
+       ."/FC [".join(' ',@FC)."] D\n/NI $nm D\n/BM ".($nm+1)." array D\nSB\n";
+    for $i (0..$nm) {$_.="$BM[$i]\n\n"}
+  }
+  $_.="\n$pv%Endpv\n" if($nps);
+}
+@kw=split(/[, ]+/,$kw);
+@Kw=();
+for $i (@kw){push(@Kw,$i) if(!grep(/^$i$/,@Kw))};
+$kw=join(', ',@Kw);
+for $i (0..$#z2) {
+  $n=0;
+  $j=$i;
+  while($j++<=$#z2 && $z2[$j]>$z2[$i]) {$n++ if($z2[$j]==$z2[$i]+1)};
+  push(@z3,$n);
+}
+$tdef=$ntab>=0?"$tbl$tables\n0 1 $ntab\{TS}for RC ZF\n":"";
+$hd="/Hr [@z1]D\n/HV [@z2]D\n/Cn [@z3]D";
+&cut($hd);
+if($gd) {
+  $sd="/Df t D /DG IW IW mul IL IL mul add sqrt D IW IL IW IL lt{E}if"
+  ." /WM E D /WX E D /DZ 180 D gsave SD /DZ DZ DF mul D SD grestore\n";
+} else {
+  $sd="/Df f D\n";
+}
+$_.=<<EOD;
+[/Creator ($version) /Author ($Au) /Keywords ($kw) /Subject ($su)
+ /Title ($title) /DOCINFO pdfmark
+/ND $ndoc D
+/HN [1 1 $nref\{pop (??)}for] D
+$dh$hd
+Hr length 0 gt{[/PageMode /UseOutlines /DOCVIEW pdfmark}if
+/Hn 1 D
+0 1 Hr length 1 sub{
+ /Bn E D [Cn Bn get dup 0 gt{/Count E HV Bn get Bl ge{neg}if}{pop}ie
+ /Dest Hr Bn get dup abs ST cvs cvn E 0 ge{(h)Hn ST cvs join cvx exec
+ dup 1 get E Nf{0 get E join}{pop}ie /Hn Hn 1 add D}{()}ie
+ /Title E dup length 255 gt{0 255 getinterval}if /OUT pdfmark}for
+ZF /FN Fp D Ps 0 FS /WC Wf{( )}{<A1A1>}ie SW pop D
+ET RC ZF
+$sd$rfs$tdef$top$P3
+/TE t D NP TU PM 0 eq and{/Pn () D showpage}if end restore
+EOD
+
+if(($first || $opt_R) && $xref{'passes'}) {
+  &dbg("Inserting cross references\n") if($opt_d);
+  for $i (1..$xref{'passes'}) {&ref};
+}
+&fin;
+
+sub h2p {
+  if($html eq '-') {
+    $_=<>;
+  } elsif($html=~m|://|) {
+    if(($prompt || $nrem>50) && $level>1) {
+      &prompt("Retrieve document $html (y/n/q)? ",$ans);
+      if($ans=~/q/i) {undef @docs};
+      return 0 unless($ans=~/y/i);
+    }
+    &geturl($html,$_) || return;
+    $nrem++;
+    if($contyp!~m|text/html|i) {$_=" <plaintext>\n$_"};
+    unless(($ba2)=$html=~m|(.*://.*/)|) {$ba2=$html."/"};
+  } else {
+    if(open(FILE,$html)) {
+      &dbg("Reading $html\n") if($opt_W || $opt_d);
+      $_=<FILE>;
+      if(!/<HTML/i && $html!~/html?$/i && ($html!~/\.ps$/i || $ndoc>1)) {
+        $_=" <plaintext>\n$_";
+      }
+      close FILE;
+      $var{DOCUMENT_NAME}=$html;
+      if($posix) {
+        $var{LAST_MODIFIED}=POSIX::strftime("%c",localtime((stat $html)[9]));
+        $var{DATE_LOCAL}=POSIX::strftime("%c",@now);
+        $var{DATE_GMT}=POSIX::strftime("%c",@gmnow);
+      }  
+      $mod=(stat $html)[9];
+    } else {
+      &dbg("*** Error opening $html\n");
+      return 0;
+    }
+  }
+
+  if(/^%!/ && /$delim/) {
+    $psin=1;
+    &openps if($opt_o);
+    $_=$P0.$';
+    for $s ("b","c","cw","g","t") {
+      &dbg("Option -$s ignored\n") if(eval "\$opt_$s");
+    }
+    &fin;
+  }
+
+  &hb($_,$head);
+  $head=~/<title$R\s*([\w\W]*)<\/title/i;
+  ($ti=$2)=~s/\s+/ /g;
+  $ti=$doctit{$html} if(!$ti);
+  $ti=~s/\s*$//g;
+  &spec($ti);
+  &ent($ti);
+  $ti="<Untitled>" if(!$ti);
+  $title=$ti if(!$title);
+  $draft="f" if(!$ddr);
+  %meta=();
+  $au="";
+  while($head=~/<meta\s[^>]*(name|http-equiv)\s*=\s*["']?\s*(\w+)$R/gi) {
+    $k=lc $2;
+    ($v)=$&=~/content\s*=\s*["']\s*([^"']+)/i;
+    $v=~s/\s+/ /g;
+    $v=~s/\s*$//g;
+    &spec($v);
+    &ent($v);
+    $meta{$k}=$v;
+    if($k=~/author/) {$au=$au?"$au, $v":$v};
+    if($k=~/keywords/) {$kw=$kw?"$kw, $v":$v};
+    if($k=~/subject/ && !$su) {$su=$v};
+    if(!$ddr && $k=~/status/ && $v=~/draft/i) {$draft="t";$gd=1};
+  }
+  $Au.=($Au?" + ":"").$au if($au);
+  $b2=$opt_b;
+  unless($b2) {
+    ($b2)=$head=~/<base\s+href\s*=\s*"([^"]*)"$R/i;
+    unless($b2) {($b2)=$head=~/<base\s+href\s*=\s*([\w\.-]+)$R/i}
+    unless($b2) {$b2=$ba2}
+  }
+  $b2=~s|[^/]*$||;
+  ($b1)=$b2=~m|(.*://[^/]*)/|;
+  unless($b1) {$b1=$opt_r};
+  unless($b2) {$b2=$html=~m|(.*/)[^/]*$|?$1:""};
+  if(!defined $B2) {$B2=$b2};
+  $levl{$b2.$html}=$levl{$html};
+
+  while($link && $head=~/<link\s+[^>]*rel\s*=\s*["']?next$R/gi) {
+    if(($lnk)=$&=~/href\s*=\s*["']?\s*([^"' >]*)/gi) {
+      if($lnk=~m|.+//[^/]+$|) {$lnk=$&."/"}
+      if($lnk=~m|://|) {
+        $rlnk=0;
+      } else {
+        $rlnk=1;
+        if($lnk=~m|^/|) {$lnk=$b1.$lnk} else {$lnk=$b2.$lnk}
+      }
+      while($lnk!~m|^\.\./| && $lnk=~m|[^/]*/\.\./|) {$lnk=$`.$'};
+      $lnk=~s|/\./|/|g;
+      if(&follow && !$levl{$lnk}) {
+        $levl{$lnk}=$level+1;
+        push(@links,$lnk);
+      }
+    }
+  }
+  ($battr)=/<BODY$R/i;
+  ($lang)=$battr=~/\slang\s*=\s*['"]?([a-zA-Z-]+)/i;
+  ($lang)=$head=~/<html[^>]+lang\s*=\s*['"]?([a-zA-Z-]+)/i if(!$lang);
+  $lang=$opt_l if($opt_l);
+  $lang='en' if(!$lang);
+  $lang=lc $lang;
+  if($battr=~/\stext\s*=\s*['"]?\s*#?(\w+)/i) {$tcol=&col2rgb($1)};
+  if(!$tcol) {$tcol="Dt"};
+  if($battr=~/\slink\s*=\s*['"]?\s*#?(\w+)/i) {$lcol=&col2rgb($1)};
+  if(!$lcol) {$lcol="Dl"};
+  &inihyph if($opt_H);
+  ($bg)=$battr=~/BGCOLOR\s*=\s*["']?\s*#?(\w+)/i;
+  $bg=&col2rgb($bg);
+  if($bg) {
+    ($red,$grn,$blu)=@cvec;
+  } else {
+    ($red,$grn,$blu)=$bgcol=~/#(\w+).*#(\w+).*#(\w+)/;
+    $bg="Db";
+  }
+
+  $temp="";
+  while(/<object$R/i) {
+    $temp.=$`;
+    $tag=$&;
+    $end=$';
+    $type=$tag=~/type\s*=\s*($S)/i?$+:"";
+    $uaddr=$tag=~/data\s*=\s*($S)/i?$+:"";
+    if($type=~/^text\/(html|plain)$/i
+     || !$type && $uaddr=~m"(\.html?|://.+/|://[^/]+)$"i) {
+      $tag=~/data\s*=\s*/i;
+      if(&open($uaddr,$idoc)) {
+        if($type=~/plain/i) {
+          $idoc="<XMP>$idoc</XMP>";
+        } else {
+          &hb($idoc,$dum);
+        }
+        $_=$idoc;
+        $_.=$' if($end=~/<\/object>/i);
+      } else {
+        &dbg("\n*** Error opening $uaddr\n");
+        $_=$end;
+      }
+    } else {
+      $temp.=$tag;
+      $_=$end;
+    }
+  }
+  $_=$temp.$_;
+
+  if($opt_c && defined $package{'check'}) {
+    $file=$html;
+    if($html=~m|://|) {
+      open(SCRATCH,">$scr");
+      print SCRATCH;
+      close SCRATCH;
+      $file="$scr";
+    }
+    &dbg(`$package{'check'} $file`);
+  }
+
+  if(!$latin1) {
+    if($opt_e=~/EUC-/i) {
+      s/([\216\217\241-\376].)+/\000$&\000/g;
+      &spec($_);
+      s/\000(.+?)\000/)F1($1)F2(/g;
+    } elsif($opt_e=~/SHIFT-JIS/i) {
+      s/[\201-\237\340-\374][@-~\200-\374]/$&\000/g;
+      s/[\241-\337]+(?!\000)/$&\000/g;
+      s/[ -~\t\n\r\240]+(?!\000)/\002$&\001/g;
+      &spec($_);
+      s/\000//g;
+    } else {
+      while(/\e\$B([^\e]*)/) {
+        $beg=$`;
+        $end=$';
+        $mat=$1;
+        $mat=~s/\s//g;
+        $_="$beg\001$mat$end";
+      }
+      s/\e\([BJ]/\002/g;
+      &spec($_);
+    }
+    s/\001/)F1(/g;
+    s/\002/)F2(/g;
+    $_=")F2($_";
+    y/\000-\010\013\016-\037\177//d;
+  } else {
+    &spec($_);
+    y/\000-\010\013\016-\037\177-\237//d;
+  }
+  s/(\r\n|\r)/\n/g;
+  $refs{$html}=$nref++ if(!defined $refs{$html});
+  $_="\004$lang\004)WB $refs{$html} Sn($_";
+
+#  Yes, I know Perl has case-insensitive pattern matching. But on my system
+#  it takes about 10 times longer to run!
+
+  $pt="";
+  if(/<[pP][lL][aA][iI][nN][tT][eE][xX][tT]$R/) {$_=$`;$pt=$'};
+  while($_){
+    if(/(<[lL][iI][sS][tT][iI][nN][gG]$R)/) {$_=$`; $tag=$1; $rest=$';
+      if(/<[xX][mM][pP]$R/){$_=$`; &Subst($_); $P2.="$_)XX("; $_=$'.$tag.$rest;
+        if(m|</[xX][mM][pP]$R|) {$P2.="$`)RP("; $_=$'}
+        else {$P2.=$'; $_=""}}
+      else {&Subst($_); $P2.="$_)XX("; $_=$rest;
+        if(m|</[lL][iI][sS][tT][iI][nN][gG]$R|) {$P2.="$`)RP("; $_=$'}
+        else {$P2.=$'; $_=""}}}
+    elsif(/<[xX][mM][pP]$R/) {$_=$`; &Subst($_); $P2.="$_)XX("; $_=$';
+      if(m|</[xX][mM][pP]$R|) {$P2.="$`)RP("; $_=$'}
+      else {$P2.=$'; $_=""}}
+    else {&Subst($_);$P2.=$_; $_=""}
+  }
+  $pt=~s/\f/$pc/g;
+  if($pt) {$P2.=")XX($pt"};
+  $P2.=")";
+  if($plain) {$P2.="RP ()"};
+  while($P2=~/XX\(/) {
+    $beg=$`;
+    $'=~/\)(RP|$)/;
+    $mat=$`;
+    $end=$&.$';
+    $mat=~s/(.*\n){30}.*/$&)WR(/g;
+    ($temp=$mat)=~s/\)[^(]+\(//g;
+    @prel=split(' *\n',$temp);
+    $maxl=0;
+    for $line (@prel) {
+      $line=~s/\\.../x/g;
+      while($line=~/\t+/) {
+        $sp=' ' x (length($&)*8-length($`)%8);
+        $line=~s/\t+/$sp/;
+        $mat=~s/\t+/$sp/;
+      }
+      $ll=length($line);
+      $maxl=$ll if($ll > $maxl);
+    }
+    $P2="$beg ".($#prel+1)." $maxl PR($mat$end";
+  }
+  $P1=~s/[\200-\377]/sprintf("\\%3.3o",ord($&))/eg;
+  $P2=~s/[\200-\377]/sprintf("\\%3.3o",ord($&))/eg;
+  $P2=~y/\t\f/ /;
+  1;
+}
+sub Subst{
+  local($_)=@_;
+  if($page_break) {
+    s/<!--NewPage-->/$pc/g;
+    s/<(\?|hr\s+class\s*=\s*["']?)\s*page-break$R/$pc/gi;
+  }
+  s/<!--OddPage-->/)WB NL OP(/g;
+  if($ssi && $html!~m|://|) {
+    while(/<!--#(include|config|echo)\s+(\w+)\s*="([^"]+)"\s*-->/) {
+      $inc="";
+      $file=$3;
+      if($1 eq "include" && (substr($file,0,1) ne "/" || $opt_r)) {
+        if(substr($file,0,1) ne "/") {
+          $file=$B2.$file;
+        } elsif($2 eq "virtual") {
+          $file=$opt_r.$file;
+        }
+        if(open INC,$file) {
+          $inc=<INC>;
+          &spec($inc);
+          close INC;
+        }
+      } elsif ($1 eq "config" && $2 eq "timefmt") {
+        if($posix) {
+          $var{LAST_MODIFIED}=POSIX::strftime($3,localtime((stat $html)[9]));
+          $var{DATE_LOCAL}=POSIX::strftime($3,@now);
+          $var{DATE_GMT}=POSIX::strftime($3,@gmnow);
+        }  
+      } elsif ($1 eq "echo") {
+        $inc=$var{$3};
+      }
+      $_=$`.$inc.$';
+    }
+  }
+  s/(&shy;?|&#173;?|<!--hy-->)/)HY(/g;
+  while(/<!--/) {
+    $_=$`;
+    &getcom;
+    $_.=$rest;
+  }
+  $temp="";
+  while(/<([^"'>]*=\s*["'])/) {
+    $temp.=$`."<";
+    $_=$1.$';
+    while(/^[^"'>]*=\s*(["'])/) {
+      $temp.=$&;
+      $_=$';
+      if(/$1/) {
+        ($tg=$`)=~y/>/\003/;
+        $temp.=$tg.$&;
+        $_=$';
+      }
+    }
+  }
+  $_=$temp.$_;
+  $a='[aA][lL][iI][gG][nN]';
+  $Y='[sS][tT][yY][lL][eE]';
+  $A="($a\\s*=\"?|$Y\\s*=\\s*\"?[tT][eE][xX][tT]-\\s*$a:)";
+  $I='[lL][eE][fF][tT]';
+  $C='[cC][eE][nN][tT][eE][rR]';
+  $D='[rR][iI][gG][hH][tT]';
+  $J='[jJ][uU][sS][tT][iI][fF][yY]';
+  $s='[sS][eE][lL][eE][cC][tT]';
+  $F='[fF][oO][nN][tT]';
+  $U='[cC][oO][lL][oO][rR]';
+  $O='[cC][oO][mM][pP][aA][cC][tT]';
+  s/<\w+[^>]*\s+[iI][dD]\s*=\s*($S)[^>]*>/$&<a name="$+">/g;
+  s|<[dD][eE][lL]$R[\w\W]*?</[dD][eE][lL]>||g if($del{'display'}=~/^none$/);
+  $ndiv=1;
+  s|</?[dD][iI][vV]\d$R||g;
+  s|<(/?)([dD][iI][vV])([>\s])|"<$1$2".($1?--$ndiv:$ndiv++).$3|eg;
+  while(/<[dD][iI][vV](\d+)$R/) {
+    $dbeg=$`;
+    $dnum=$1;
+    $dattr=$2;
+    $dend=$';
+    $div="";
+    $ediv="";
+    if($2=~/class\s*=\s*["']?noprint$R/i) {
+      $_=$dbeg;
+      $_.=$' if($dend=~/<\/[dD][iI][vV]$dnum>/);
+    } else {
+      if($dattr=~/$A\s*($I|$C|$D|$J)/) {
+        $div.=")".$algn{"\L$2"}." Al(";
+        $ediv.=")Ea(";
+      }
+      if($dattr=~/lang\s*=\s*["']?([a-zA-Z-]+)/i) {
+        $lang=lc $1;
+        $div.="\004$lang\004";
+        &inihyph if($opt_H);
+        $dbeg=~/(\004[^\004]*\004)[^\004]*$/;
+        $ediv.=$1;
+      }
+      $dend=~s|</[dD][iI][vV]$dnum>|$ediv)BR(|;
+      $_="$dbeg$div)BR($dend";
+    }
+  }
+  s|<$C$R|)2 Al(|g;
+  s|</$C$R|)Ea(|g;
+  s|<(\w+)/([^/]+)/|<$1>$2</$1>|g;
+  s/(<\w+[^>]*>)\n|\n(<\/\w+>)/$+/g;
+  s|(<[lL][iI])$R\s*<[pP]>|)0 P($1$2|g;
+  s/<[hH]([1-6])\s+$A\s*($I|$C|$D|$J)$R/)$algn{"\L$3"} $1  H(/g;
+  s|<[hH]([1-6])$R|)0 $1  H(|g;
+  s|</[hH][1-6]>|)EH(|g;
+  s|<[bB][rR]$R|)BR(|g;
+  s/<[pP]\s+[^>]*$A\s*($I|$C|$D|$J)$R/)$algn{"\L$2"} P(/g;
+  s|<[pP]$R|)0 P(|g;
+  s|</[pP]>|)EP(|g;
+  s|<[aA][dD][dD][rR][eE][sS][sS]$R|)AD(|g;
+  s|</[aA][dD][dD][rR][eE][sS][sS]>|)DA(|g;
+  s|<[pP][rR][eE]$R\n?|)XX(|g;
+  s|\n? *</[pP][rR][eE]>|)RP(|g;
+  s|<[dD][tT]\s[^>]*$O$R|)1 DT(|g;
+  s|<[dD][tT]$R|)0 DT(|g;
+  s|<[dD][dD]$R|)DD(|g;
+  s|<[dD][lL]\s[^>]*$O$R|)1 DL(|g;
+  s|<[dD][lL]$R|)0 DL(|g;
+  s|</[dD][lL]>|)LD(|g;
+  s|<[uU][lL]$R|)UL(|g;
+  s|</[uU][lL]>|)LU(|g;
+  s|<[mM][eE][nN][uU]$R|)UL(|g;
+  s|</[mM][eE][nN][uU]>|)LU(|g;
+  s|<[dD][iI][rR]$R|)UL(|g;
+  s|</[dD][iI][rR]>|)LU(|g;
+  s|<[oO][lL]\s[^>]*[sS][tT][aA][rR][tT]\s*=\s*['"]?(-?\d+)$R|$&)WB $1 Ln(|g;
+  s|<[oO][lL]\s[^>]*[tT][yY][pP][eE]\s*=\s*['"]?([1iIaA])$R|)$lity{$1} OL(|g;
+  s|<[oO][lL]$R|)4 OL(|g;
+  s|</[oO][lL]>|)LO(|g;
+  s|<[lL][iI]\s[^>]*[vV][aA][lL][uU][eE]\s*=\s*['"]?(-?\d+)$R|$&)WB $1 Ln(|g;
+  s|<[lL][iI]\s[^>]*[tT][yY][pP][eE]\s*=\s*['"]?($ltr)$R|)$lity{$1} LI(|g;
+  s|<[lL][iI]$R|)-1 LI(|g;
+  s|</[lL][iI]$R||g;
+  s"<([bB][qQ]|[bB][lL][oO][cC][kK][qQ][uU][oO][tT][eE])$R")BQ("g;
+  s"</([bB][qQ]|[bB][lL][oO][cC][kK][qQ][uU][oO][tT][eE])>")QB("g;
+  s|<[sS][tT][rR][oO][nN][gG]$R|)BD(|g;
+  s|</[sS][tT][rR][oO][nN][gG]>|)ES(|g;
+  s|<[sS][aA][mM][pP]$R|)SM(|g;
+  s|</[sS][aA][mM][pP]>|)ES(|g;
+  s|<[qQ]$R(\s*)|$2)Q(|g;
+  s|(\s*)</[qQ]>|)EQ($1|g;
+  s|<[cC][iI][tT][eE]$R|)CT(|g;
+  s|</[cC][iI][tT][eE]>|)ES(|g;
+  s|<[vV][aA][rR]$R|)I(|g;
+  s|</[vV][aA][rR]>|)ES(|g;
+  s|<[bB]$R|)BD(|g;
+  s|</[bB]>|)ES(|g;
+  s|<[iI]$R|)I(|g;
+  s|</[iI]>|)ES(|g;
+  s|<[tT][tT]$R|)TT(|g;
+  s|</[tT][tT]>|)ES(|g;
+  s|<[uU]$R|)UN(|g;
+  s|</[uU]>|)NU(|g;
+  s|<[sS]([tT][rR][iI][kK][eE])?$R|)SE(|g;
+  s|</[sS]([tT][rR][iI][kK][eE])?>|)XE(|g;
+  s|<[dD][fF][nN]$R|)I(|g;
+  s|</[dD][fF][nN]>|)ES(|g;
+  s|<[eE][mM]$R|)EM(|g;
+  s|</[eE][mM]>|)ES(|g;
+  s|<[cC][oO][dD][eE]$R|)SM(|g;
+  s|</[cC][oO][dD][eE]>|)ES(|g;
+  s|<[kK][bB][dD]$R|)KB(|g;
+  s|</[kK][bB][dD]>|)ES(|g;
+  s|<[bB][iI][gG]$R|)4 FZ(|g;
+  s|</[bB][iI][gG]>|)ES(|g;
+  s|<[sS][mM][aA][lL][lL]$R|)2 FZ(|g;
+  s|</[sS][mM][aA][lL][lL]>|)ES(|g;
+  s|<[iI][nN][sS]$R|)sM WB(|g;
+  s|</[iI][nN][sS]>|)WB eM(|g;
+  s|<[dD][eE][lL]$R|)sM $lt(|g;
+  s|</[dD][eE][lL]>|)XE eM(|g;
+  s|<[aA][cC][rR][oO][nN][yY][mM][^>]+[tT][iI][tT][lL][eE]\s*=\s*($S)[^>]*>|)($+)Ac(|g;
+  s|</[aA][cC][rR][oO][nN][yY][mM]>|)Ca(|g;
+  s|<[fF][oO][rR][mM][\w\W]*?</[fF][oO][rR][mM]>||g if(!$forms);
+  s|</?[fF][oO][rR][mM]$R|)Ts BE(|g;
+  s/<$s[^>]*[mM][uU][lL][tT][iI][pP][lL][eE]$R/)1 MS(<table>/g;
+  s/<$s$R/)0 MS(<table>/g;
+  s|</$s>|</table>|g;
+  s/<[oO][pP][tT][iI][oO][nN]$R/<tr><td>)O(/g;
+  while(/<[iI][nN][pP][uU][tT]$R/) {
+    $beg=$`;
+    $iattr=$1;
+    $rest=$';
+    $it=2;
+    if($iattr=~/type\s*=\s*["']?(\w+)/i) {$it=$it{"\L$1"}};
+    if($it<2) {
+      $it=($iattr=~/\schecked\W/i?1:0) ." $it";
+    } elsif($it==2) {
+      $siz=$iattr=~/size\s*=\s*["']?(\d+)/i?$1:12;
+      $ival=$iattr=~/value\s*=\s*($S)/i?$+:"";
+      $it="($ival)$siz $it";
+    }
+    if(defined($it)) {
+      $cmd=$it==3?"<img $iattr":")$it BX(";
+    } else {
+      $cmd="";
+    }
+    $_=$beg.$cmd.$rest;
+  }
+  while(/<[tT][eE][xX][tT][aA][rR][eE][aA]$R/) {
+    $beg=$`;
+    $txatr=$1;
+    $rest=$';
+    if($rest=~m|</[tT][eE][xX][tT][aA][rR][eE][aA]>|) {
+      $rest=$';
+      $data=$prefilled||$textarea_data?$`:"";
+      $rows=4;
+      $cols=20;
+      if($txatr=~/rows\s*=\s*["']?(\d+)/i) {$rows=$1};
+      if($txatr=~/cols\s*=\s*["']?(\d+)/i) {$cols=$1};
+      $nl=$data=~y/\n/\n/;
+      for ($nl..$rows) {$data.="\n"};
+      $data=~/(.*\n){$rows}/;
+      $tfont=$prefilled?"TT":"0 FZ";
+      ($data=$&)=~s/.*\n/<tr height=24><td valign=top>)$tfont($&)ES(/g;
+      $wi=10*$cols;
+      $frame=$prefilled?"frame=box":"border";
+      $_="$beg<table $frame width=$wi cellpadding=2>$data</table>$rest";
+    } else {
+      $_=$beg.$rest;
+    }
+  }
+  $nfnt=1;
+  s|<(/?)($F)([>\s])|"<$1$2".($1?--$nfnt:$nfnt++).$3|eg;
+  while(/<$F(\d+)([^>]*)$U\s*=\s*["']?\s*#?(\w+)$R/) {
+    $rgb=&col2rgb($3);
+    $_=$`.($rgb?")WB $rgb Sc(":"")."<font$2$4";
+    $temp=$';
+    $temp=~s|</$F$1>|</font>)Ec(| if($rgb);
+    $_.=$temp;
+  }
+  $base{"+"}="Bf add ";
+  $base{"-"}="Bf add ";
+  s/<$F\d*\s[^>]*[sS][iI][zZ][eE]\s*=\s*["']?([+-]?)(\d+\.?\d*)$R/)$1$2 $base{$1}FZ(/g;
+  s|<$F\d*$R|)3 FZ(|g;
+  s|</$F\d*>|)Ef(|g;
+  s|<[bB][aA][sS][eE]$F\s[^>]*[sS][iI][zZ][eE]\s*=\s*["']?(\d+)$R|)$1 BZ(|g;
+  while(/(<[aA]\s+[^>]*)[nN][aA][mM][eE]\s*=\s*(["']?)([^"'\s>]*)$R([^\s<)]*)/) {
+    $lnk="$html#$3";
+    $refs{$lnk}=$nref++ unless(defined $refs{$lnk});
+    $_="$`$1$4$5)WB $refs{$lnk} Sn($'";
+  }
+  while(/<[aA]\s+[^>]*[hH][rR][eE][fF]\s*=\s*["']?\s*([^"'\s>]*)$R/) {
+    $beg=$`;
+    $tag=$&;
+    $rest=$';
+    $lnk=$1;
+    $revtoc=$tag=~/rev\s*=['"]?\s*toc/i;
+    $html=~m|[^/]*$|;
+    $lnk=~s/^\Q$&#/#/;
+    $loc=$lnk=~/^#/;
+    if($loc) {
+      $html=~m|[^/]*$|;
+      $lnk=$&.$lnk;
+    }
+    if($lnk=~m|.+//[^/]+$|) {$lnk=$&."/"}
+    if($lnk=~m|://|) {
+      $rlnk=0;
+    } else {
+      $rlnk=1;
+      if($lnk=~m|^/|) {$lnk=$b1.$lnk} elsif($lnk!~m|^\w+:|) {$lnk=$b2.$lnk};
+    }
+    while($lnk!~m|^\.\./| && $lnk=~m|[^/]*/\.\./|) {$lnk=$`.$'};
+    $lnk=~s"(^|/)\./"$1"g;
+    ($doc)=$lnk=~/([^#]*)/;
+    ($doctit{$doc})=$tag=~/title\s*=['"]([^'"]*)['"]/i;
+    $T=0;
+    $anch=2;
+    if($loc || grep(/^\Q$doc\E$/,(@docs,@links))
+     || $opt_W && !$link && $level<=$maxlev && &follow){
+      $refs{$lnk}=$nref++ unless(defined $refs{$lnk});
+      $anch="$refs{$lnk} 1";
+      $ltype=$rev && $revtoc && $ndoc==1 || $opt_C=~/f/ && $ndoc==1?1:0;
+      $rest=~s|</a>|)$refs{$lnk} $ltype TN TL()Ec /AF f D(|i;
+      if(&follow && !$levl{$doc}) {
+        &dbg("Link: $doc\n") if($opt_d);
+        $levl{$doc}=$level+1;
+        push(@links,$doc);
+      }
+    } elsif(defined $refs{$lnk}) {
+      $anch="$refs{$lnk} 1";
+    }
+    $addr=$dum{$lnk}?"R$dum{$lnk}":0;
+    if(!$dum{$lnk} && $lnk=~m|://|) {
+      $dum{$lnk}=$nlnk++;
+      $rfs.="/R$dum{$lnk} ($lnk) D\n";
+      $addr="R$dum{$lnk}";
+    }
+    $_=$beg.")$addr $anch A(".$rest;
+  }
+  s|</[aA]>|)EA(|g;
+  if((!$mult || $doc_sep eq $pc && $mult)
+   && m|<[bB][aA][nN][nN][eE][rR]$R([\w\W]*)</[bB][aA][nN][nN][eE][rR]>|) {
+    $banner=$2;
+    $_=$`.$';
+  }
+  while(/<[tT][aA][bB][lL][eE]([^>]*)>/) {
+    local($beg)=$`;
+    local($rest)=$';
+    $tattr=$1;
+    $tag=$&;
+    $rest=~/(<\/[tT][aA][bB][lL][eE]>|$)/;
+    $table=$`;
+    $rest=$';
+    while($table=~/<[tT][aA][bB][lL][eE]([^>]*)>/) {
+      $tattr=$1;
+      $table=$';
+      $beg.=$tag.$`;
+      $tag=$&;
+    }
+    ($tla)=$tattr=~/lang\s*=\s*["']?([a-zA-Z-]+)/i;
+    if(!$tla) {($tla)=$beg=~/\004_?([^\004]*)\004[^\004]*$/};
+    $ntab++;
+    $table=~s/^\s*//;
+    undef %cells;
+    undef %rd;
+    undef @cali;
+    undef @cval;
+    undef @cgrp;
+    undef @cwid;
+    undef @codc;
+    undef @r0;
+    undef @c0;
+    ($capat,$cap)=$table=~m|<caption$R([\w\W]*)</caption>|i;
+    $capat=~/ALIGN\s*=\s*["']?\s*(\w+)/i;
+    $capa=0;
+    $capa=1 if("\L$1" eq "bottom");
+    $capa=-1 if($cap!~/\S/);
+    $bord=0;
+    if($tattr=~/border/i) {
+      ($bord)=$'=~/^\s*=\s*["']?(\d+)/;
+      if(!$bord && $bord ne "0") {$bord=1};
+    }
+    ($talgn)=$tattr=~/ALIGN\s*=\s*["']?\s*(\w+)/i;
+    $tal=-1;
+    $tal=0 if($talgn=~/^left$/i);
+    $tal=1 if($talgn=~/^center$/i);
+    $tal=2 if($talgn=~/^right$/i);
+    ($fra)=$tattr=~/FRAME\s*=\s*["']?\s*(\w+)/i;
+    if($fra && $f{"\L$fra"}) {$fra=$f{"\L$fra"}} else {$fra=$bord?9:1};
+    ($rul)=$tattr=~/RULES\s*=\s*["']?\s*(\w+)/i;
+    if($rul && $r{"\L$rul"}) {$rul=$r{"\L$rul"}} else {$rul=$bord?5:1};
+    unless(($twid)=$tattr=~/WIDTH\s*=\s*["']?(\d+\.?\d*%?)/i) {$twid=0};
+    $twid=$twid=~/%$/?$`/100:-$twid;
+    ($cpad)=$tattr=~/CELLPADDING\s*=\s*["']?\s*$V/i;
+    if($tattr=~/CELLSPACING\s*=\s*["']?\s*$V/i && $1!=0) {$cpad+=$1/2};
+    ($tbg)=$tattr=~/BGCOLOR\s*=\s*["']?\s*#?(\w+)/i;
+    $tbg=&col2rgb($tbg);
+    ($tcl)=$tattr=~/CLASS\s*=\s*["']?\s*(\w+)/i;
+    $tcl="\L$tcl" eq "telelista"? 1: 0;
+    $ic=0;
+    $span=1;
+    undef $gal;
+    undef $gva;
+    undef $odc;
+    undef $gwi;
+    $cgs=0;
+    while($table=~/<[cC][oO][lL]([^>]*>)/g && $span>0) {
+      $cola=$1;
+      $ab=$`;
+      $aft=$';
+      $cola=~/(^|\s)ALIGN\s*=\s*["']?\s*(\w+)/i;
+      $alg=$algn{"\L$2"};
+      ($val)=$cola=~/VALIGN\s*=\s*["']?\s*(\w+)/i;
+      $val=$v{"\L$val"};
+      ($odc)=$cola=~/CHAR\s*=\s*["']?(.)/i;
+      unless(($wid)=$cola=~/WIDTH\s*=\s*["'](\d+%?)/i) {$wid=0};
+      if($wid=~/%$/) {$wid=$`/100};
+      ($span)=$cola=~/SPAN\s*=\s*["']?\s*(\d+)/i;
+      if(!$span && $span ne "0") {$span=1};
+      $us=1;
+      if($cola=~/^GROUP/i) {
+        $gal=$alg;
+        $gva=$val;
+        $gdc=$odc;
+        $gwi=$wid;
+        $cgs=1;
+        if($aft=~/<COL\s/i) {
+          $us=$`=~m|</?COLGROUP|i;
+        }
+      } else {
+        while($ab=~/<COLGROUP/i) {$ab=$'};
+        if($ab=~m|</COLGROUP|i) {
+          undef $gal;
+          undef $gva;
+          undef $odc;
+          undef $gwi;
+        }
+        if(!$alg) {$alg=$gal};
+        if(!$val) {$val=$gva};
+        if(!$odc) {$odc=$gdc};
+        if(!$wid) {$wid=$gwi};
+        if($span eq "0") {
+          $ic++;
+          push(@cali,$alg);
+          push(@cval,$val);
+          push(@cwid,$wid);
+          push(@codc,$odc);
+          push(@cgrp,1);
+        }
+      }
+      if($us) {
+        for (1..$span) {
+          $ic++;
+          push(@cali,$alg);
+          push(@cval,$val);
+          push(@cwid,$wid);
+          push(@codc,$odc);
+          push(@cgrp,$cgs);
+          $cgs=0;
+        }
+      }
+    }
+    $table=~/<t[hd][\s>]/i;
+    if($`!~/<tr/i) {$table="<TR>$table"};
+    $nrow=-1;
+    $ncol=-1;
+    $nhead=0;
+    $nfoot=0;
+    $nb=0;
+    unless($table=~/<tbody$R/i || $table=~s|</tfoot>|$&<tbody>|i) {
+      $table=~s|</thead>|$&<tbody>|i;
+    }
+    while($table=~/<[tT][rR]\s*([^>]*)>/g) {
+      $nrow++;
+      $ab=$`;
+      $row=$';
+      $rattr=$1;
+      for $j (@c0) {
+        $cells{"$nrow,$j,0"}=1;
+      }
+      $rgrp=0;
+      if($ab=~/<tbody$R/i) {
+        $ib=0;
+        while($ab=~/<[tT][bB][oO][dD][yY]$R/g) {
+          $ib++;
+          $battr=$1;
+          if($ib>$nb) {
+            $rgrp=1;
+            $nb=$ib;
+          }
+        }
+      } else {
+        if($ab=~/<tfoot$R/i) {
+          $nfoot++;
+          $battr=$1;
+        } elsif($ab=~/<thead$R/i) {
+          $nhead++;
+          $battr=$1;
+        }
+      }
+      $battr=~/(^|\s)ALIGN\s*=\s*["']?\s*(\w+)/i;
+      $balgn=$algn{"\L$2"};
+      $battr=~/VALIGN\s*=\s*["']?\s*(\w+)/i;
+      $bva=$v{"\L$1"};
+      ($bdc)=$battr=~/CHAR\s*=\s*["']?(.)/i;
+      $rd{"$nrow,0"}=0;
+      $rd{"$nrow,1"}=0;
+      $rd{"$nrow,2"}=0 unless($rd{"$nrow,2"});
+      $rd{"$nrow,3"}=$rgrp;
+      $rattr=~/(^|\s)ALIGN\s*=\s*["']?\s*(\w+)/i;
+      $ralgn=$algn{"\L$2"};
+      $rattr=~/VALIGN\s*=\s*["']?\s*(\w+)/i;
+      $rva=$v{"\L$1"};
+      ($rla[$nrow])=$rattr=~/lang\s*=\s*["']?([a-zA-Z-]+)/i;
+      ($rbg)=$rattr=~/BGCOLOR\s*=\s*["']?\s*#?(\w+)/i;
+      $rbg[$nrow]=&col2rgb($rbg);
+      ($rdc)=$rattr=~/CHAR\s*=\s*["']?(.)/i;
+      if($row=~/<tr/i) {$row=$`};
+      $rh[$nrow]=$rattr=~/HEIGHT\s*=\s*["']?(\d+)/i?$1:0;
+      $icol=0;
+      $colsp=1;
+      while($row=~/<[tT]([hH]|[dD])(\s*[^>]*)>/g && $colsp>0) {
+        $cattr=$2;
+        $cell=$';
+        $ctype=$1=~/h/i?1:0;
+        if($cell=~/<t[hd]/i) {$cell=$`};
+        $cell=~s/\s+$//;
+        $cell=~s/\)HY\($/\255/;
+        $cell=~s/[\200-\377]([^\\]|\\20.)/$&)WB(/g if(!$latin1);
+        ($rowsp)=$cattr=~/ROWSPAN\s*=\s*["']?(\d+)/i;
+        $rsp=$rowsp;
+        if(!$rsp) {
+          if($rsp eq "0") {
+            push(@c0,$icol);
+          } else {
+            $rowsp=1;
+          }
+          $rsp=1;
+        }
+        ($colsp)=$cattr=~/COLSPAN\s*=\s*["']?(\d+)/i;
+        $csp=$colsp;
+        if(!$csp) {
+          if($csp eq "0") {
+            push(@r0,$nrow);
+            $csp=$ncol-$icol<0? 1: $ncol-$icol+1;
+          } else {
+            $colsp=1;
+            $csp=1;
+          }
+        }
+        ($cdc)=$cattr=~/CHAR\s*=\s*["']?(.)/i;
+        while($cells{"$nrow,$icol,0"}==1) {$icol++};
+#
+        for $i ($nrow..$nrow+$rsp-2) {$rd{"$i,2"}=1};
+        for $j ($icol..$icol+$csp-1) {
+          for $i ($nrow..$nrow+$rsp-1) {
+            $cells{"$i,$j,0"}=1;
+          }
+        }
+        if($colsp) {
+          for $j ($ncol+1..$icol+$csp) {
+            for $i (@r0) {
+              $cells{"$i,$j,0"}=1;
+            }
+          }
+        }
+        if($ic<$icol+$csp) {
+          for ($ic..$icol+$csp-1) {
+            push(@cali,$cali[$ic-1]);
+            push(@cval,$cval[$ic-1]);
+            push(@cgrp,0);
+          }
+          $ic=$icol+$csp;
+        }
+        $cal=$ctype;
+        $cal=$balgn-1 if($balgn);
+        $cal=$ralgn-1 if($ralgn);
+        $cal=$cali[$icol]-1 if($cali[$icol]);
+        if($cattr=~/(^|\s)ALIGN\s*=\s*["']?\s*(\w+)/i) {
+          $cal=$algn{"\L$2"}-1 if($algn{"\L$2"});
+        }
+        $cvl=2;
+        $cvl=$cval[$icol] if($cval[$icol]);
+        $cvl=$bva if($bva);
+        $cvl=$rva if($rva);
+        if($cattr=~/VALIGN\s*=\s*["']?\s*(\w+)/i) {
+          $cvl=$v{"\L$1"} if($v{"\L$1"});
+        }
+        ($cbg)=$cattr=~/BGCOLOR\s*=\s*["']?\s*#?(\w+)/i;
+        $cbg=&col2rgb($cbg);
+        for($rbg[$nrow],$tbg,$deftbg,$bg) {$cbg=$_ if(!$cbg)};
+        $now=0;
+        $ro=$cattr=~/class\s*=\s*["']?rot(-?90)/i?$1:0;
+        if($cattr=~/NOWRAP/i || $cal==4 || $ro) {$now=1};
+        $dc=".";
+        $dc=$bdc if($bdc || $bdc eq "0");
+        $dc=$rdc if($rdc || $rdc eq "0");
+        $dc=$codc[$icol] if($codc[$icol] || $codc[$icol] eq "0");
+        $dc=$cdc if($cdc || $cdc eq "0");
+        ($wid)=$cattr=~/WIDTH\s*=\s*["'](\d+%)/i;
+        if($wid=~/%$/) {$wid=$`/100};
+        if($wid>$cwid[$icol]) {$cwid[$icol]=$wid};
+        if($cpad || $cpad eq "0") {
+          $clm=$cpad;
+          $crm=$cpad;
+          $ctm=$cpad;
+          $cbm=$cpad;
+        } else {
+          $clm=$rul<5?8:4;
+          $crm=$rul<5?8:4;
+          $ctm=2;
+          $cbm=6;
+        }
+        if($tcl==1) {
+          $clm=$icol>0?12:2;
+          $crm=12;
+          $ctm=0;
+          $cbm=0;
+        }
+        if($rul==1 && $fra==1 && $icol==0 && $tal==0) {$clm=0};
+        ($lang)=$cattr=~/lang\s*=\s*["']?([a-zA-Z-]+)/i;
+        for($rla[$nrow],$tla) {$lang=$_ if(!$lang)};
+        $lang=lc $lang;
+        &inihyph if($opt_H);
+        $cbg=~/#(\w+).*#(\w+).*#(\w+)/;
+        &img($cell,$1,$2,$3);
+        @cll=("{(\004$lang\004)WB($cell)}",0,0,$ctype,0,$colsp,$rowsp,$cal,
+         "($dc)",$cvl,$now,$clm,$crm,$ctm,$cbm,0,0,0,0,$cbg,$ro);
+        for $i (0..$#cll) {$cells{"$nrow,$icol,$i"}=$cll[$i]};
+        $icol+=$csp;
+        if($icol-1>$ncol) {$ncol=$icol-1};
+      }
+    }
+    for $j (0..$ncol) {
+      for $i (0..$nrow) {
+        if($cells{"$i,$j,0"} && $cells{"$i,$j,0"}!=1) {
+          if($cells{"$i,$j,6"}>$nrow-$i+1) {$cells{"$i,$j,6"}=$nrow-$i+1};
+          if($cells{"$i,$j,6"}>1) {$rd{"$i,2"}=1};
+        }
+      }
+    }
+    $rd{"$nrow,2"}=0;
+    $rw="[";
+    for $i (0..$nrow) {
+      $rw.="[$rh[$i] ".$rd{"$i,1"}." ".$rd{"$i,2"}." ".$rd{"$i,3"}." 0 0 [";
+      for $j (0..$ncol) {
+        $cbg="";
+        for($rbg[$i],$tbg,$deftbg,$bg) {$cbg=$_ if(!$cbg)};
+        $temp="[{()}0 0 0 0 1 1 0(.)0 0 $clm $crm $ctm $cbm 0 0 0 0 $cbg $ro]";
+        if($cells{"$i,$j,0"}==1) {
+          $temp="0";
+        } elsif($cells{"$i,$j,0"}) {
+          $temp="[";
+          for $k (0..$#cll) {
+            $temp.=$cells{"$i,$j,$k"}." ";
+          }
+          $temp.="]";
+        }
+        $rw.="$temp\n";
+      }
+      $rw.="]]\n";
+    }
+    if($nrow==$nhead+$nfoot-1) {
+      $nhead=0;
+      $nfoot=0;
+    }
+    $tdesc="[0 0 $tcl 0 0 $tal 0 $twid $bord $nrow $ncol"
+           ." $nhead $nfoot $fra $rul {($cap)} $capa]\n";
+    $cdesc="[";
+    for (0..$ncol) {
+      unless($wid=$cwid[$_]) {$wid=0};
+      $cdesc.="[0 0 0 $wid $cgrp[$_] 0 0]";
+    }
+    $cdesc.="]\n";
+    $tables.="[$tdesc $cdesc $rw]]\n";
+    $_=$beg.")$ntab PT(".$rest;
+  }
+  &img($_,$red,$grn,$blu);
+  &ack($_);
+  $_[0]=$_;
+}
+sub getcom{
+  $com=$&;
+  $'=~/--\s*(--|>)/;
+  $com.=$`.$&;
+  $rest=$';
+  while($1 eq "--") {
+    $'=~/(>)/ if($'!~/--\s*(--|>)/);
+    $com.=$`.$&;
+    $rest=$';
+  }
+}
+sub getl {
+  ($l)=@_;
+  if(!$lid{$l}) {
+    while($l=~s/-[^-]+$// && !$lid{$l}) {};
+  }
+  $lid{$l};
+}
+sub ack {
+  local($_)=@_;
+  chdir $tempdir;
+  while (/<[mM][aA][tT][hH]/) {
+    $beg=$`;
+    $rest=$&.$';
+    $rest=~m|</[mM][aA][tT][hH]>|;
+    $end=$';
+    $math=$`;
+    if(&math2sym($math)) {
+      $_=$beg.$sym.$end;
+    } elsif($package{'TeX'} && $package{'dvips'}) {
+      $math=~s|\\200|\\|g;
+      $math=~s|\\201|\(|g;
+      $math=~s|\\202|\)|g;
+      &math2tex($math);
+      open(SCRATCH,">$scr.tex");
+      print SCRATCH $tex;
+      close SCRATCH;
+      `tex $scr.tex`;
+      `dvips -E -o $scr.ps $scr.dvi`;
+      open(LOG,"$scr.log");
+      $log=<LOG>;
+      close LOG;
+      ($h,$d)=$log=~/[\w\W]*$prog: +([\d.]+)pt: +([\d.]+)/ ? ($1,$2) : (1,0);
+      $above=$h+$d>0?sprintf("%.4f",$h/($h+$d)):0;
+      open(PS,"$scr.ps");
+      $pic=<PS>;
+      if($pic=~/^%!/ && $pic=~/%%BoundingBox: +$V +$V +$V +$V/) {
+        $xs=$3-$1;
+        $ys=$4-$2;
+        $llx=$1;
+        $lly=$2;
+        $ps="";
+        for $i (split(/\n/,$pic)) {
+          $ps.=$i."\n" if($i && $i!~/^%/);
+        }
+        if($ps=~/\nTeXDict begin/) {
+          if(!$ph) {
+            $ph="/DH {1 F div dup scale /showpage {} D\n$`$&} D\n%EndDH\n";
+            $pv=$ph.$pv;
+          }
+          $ps="save -$llx -$lly translate\nDH$' restore";
+        }
+      }
+      $nimg++;
+      $nps--;
+      push(@XS,$xs);
+      push(@YS,$ys);
+      push(@IX,$nps);
+      push(@IT,2);
+      $pv.="/P$nimg {$ps} D\n";
+      $eps{"P$nimg"}=$ps;
+      $_=$beg.")$above 3 (P$nimg) $nimg IM(".$end;
+    } else {
+      $math=~s/<math$R//i;
+      $_=$beg.$math.$end;
+    }
+  }
+  chdir $cwd;
+  s|<[sS][uU][bB]$R|)Sb(|g;
+  s|<[sS][uU][pP]$R|)Sp(|g;
+  s"</[sS][uU]([bB]|[pP])>")Es("g;
+  s|<[A-Za-z/!?]\w*$R||g;
+  &ent($_);
+  y/\003/>/;
+  s/\004([^\004]*)\004/")".&getl($1)." Sl($&"/eg;
+  if($opt_H) {
+    &dbg("Inserting potential hyphenation points\n") if($opt_d && $ndoc>0);
+    $temp="";
+    while(/\004([^\004]*)\004/) {
+      $temp.=$`;
+      $lang=$1;
+      $end=$';
+      if($end=~/\004([^\004]*)\004/) {
+        $htext=$`;
+        $end=$&.$';
+      } else {
+        $htext=$end;
+        $end="";
+      }
+      $apa="";
+      while($htext=~/(..?)\(([^)]*)/) {
+        $slut=$';
+        if($1 eq "XX") {
+          $apa.=$`.$&;
+          if($'=~/RP\(/) {
+            $apa.=$`;
+            $htext=$&.$';
+          } else {
+            $apa.=$slut;
+            $htext="";
+          }
+        } elsif($1 eq ") ") {
+          $apa.=$`.$&;
+          $htext=$';
+        } else {
+          $apa.="$`$1(";
+          $htext=$';
+          ($txt=$2)=~s/[$ltrs]{$hyphenation{'min'},}/&hyph($&)/eg;
+          $apa.=$txt;
+        }
+      }
+      $_=$apa.$slut.$end;
+    }
+    $_=$temp.$_;
+  }
+  s/\004([^\004]*)\004//g;
+  $_[0]=$_;
+}
+sub ent {
+  local($_)=@_;
+  s|&#x($X+);?|"&#".hex($1).";"|egi;
+  for $char (keys %ent) {s/&($char)(;|$|(?=\W))/chr($ent{$char})/eg};
+  for $char (keys %symb) {s/&($char)(;|$|(?=\W))/)SY(\\$symb{$char})ES(/g};
+  s/&(euro|#8364)(;|$|(?=\W))/)MY(e)ES(/g;
+  s|&lt;?|<|g;
+  s|&gt;?|>|g;
+  s|&quot;?|"|g;
+  s/&($space);?/)$space{$1} Se(/g;
+  s|&#(\d+);?|$1==38?"\005":$1<256?chr($1):$&|eg;
+  s/(\005|&amp;?)/\&/g;
+  $_[0]=$_;
+}
+sub spec {
+  $_[0]=~s/(\\|&#92(;|$|(?=\W)))/\\200/g;
+  $_[0]=~s/(\(|&#40(;|$|(?=\W)))/\\201/g;
+  $_[0]=~s/(\)|&#41(;|$|(?=\W)))/\\202/g;
+  $_[0]=~s/&(there4|#8756|#[xX]2234)(;|$|(?=\W))/)SY(\\200)ES(/g;
+}
+sub math2tex {
+  local($_)=@_;
+  local($beg,$rest);
+  %a=("line","overline",
+  "cub","overbrace",
+  "hat","widehat",
+  "tilde","widetilde",
+  "larr", "overleftarrow",
+  "rarr", "overrightarrow");
+  %b=("line","underline",
+  "cub","underbrace",
+  "hat","widehat",
+  "tilde","widetilde");
+  %s=("medium","\\big",
+  "large","\\Big",
+  "huge","\\bigg");
+  ($mattr)=/<math$R/i;
+  $st=$mattr=~/class\s*=\s*["']?chem/i?'\rm ':'';
+  $di=$mattr=~/class\s*=\s*["']?displayed/i?'\displaystyle ':'';
+  s/<math$R//gi;
+  s/\\/\\backslash/g;
+  s/__/_\\>_/gi;
+  s/\^\^/^\\>^/gi;
+  s/_([^_]+)_/_{$1}/g;
+  s/\^([^^]+)\^/^{$1}/g;
+  s/&thinsp;?/\\,/g;
+  s/&sp;?/\\>/g;
+  s/&emsp;?/\\;/g;
+  s/&nbsp;?/\\>/g;
+  s/&epsi;?/\\varepsilon /g;
+  s/&upsi;?/\\upsilon /g;
+  s/&piv;?/\\varpi /g;
+  s/&sigmav;?/\\varsigma /g;
+  s/&thetav;?/\\vartheta /g;
+  s/&phiv;?/\\varphi /g;
+  s/&Upsi;?/\\Upsilon /g;
+  s/&omicron;?/o/g;
+  s/&plusmn;?/\\pm /g;
+  s/&or;?/\\vee /g;
+  s/&and;?/\\wedge /g;
+  s/&ap;?/\\approx /g;
+  s/&sube;?/\\subseteq /g;
+  s/&sub;?/\\subset /g;
+  s/&supe;?/\\supseteq /g;
+  s/&sup;?/\\supset /g;
+  s/&isin;?/\\in /g;
+  s/&larr;?/\\leftarrow /g;
+  s/&rarr;?([_^])/\\mathop\\rightarrow\\limits$1 /g;
+  s/&rarr;?/\\rightarrow /g;
+  s/&uarr;?/\\uparrow /g;
+  s/&darr;?/\\downarrow /g;
+  s/&harr;?/\\leftrightarrow /g;
+  s/&lArr;?/\\Leftarrow /g;
+  s/&rArr;?/\\Rightarrow /g;
+  s/&uArr;?/\\Uparrow /g;
+  s/&dArr;?/\\Downarrow /g;
+  s/&exist;?/\\exists /g;
+  s/&inf;?/\\infty /g;
+  s/&?int;?/\\int\\limits /g;
+  s/&?sum;?/\\sum\\limits /g;
+  s/&?prod;?/\\prod\\limits /g;
+  s/&pd;?/\\partial /g;
+  s/&lcub;?/\\{/g;
+  s/&rcub;?/\\}/g;
+  s/<t>/\\hbox{/gi;
+  s/<b>/\\bf /gi;
+  s/<bt>/{\\bf\\hbox{/gi;
+  s/<sub$R/_{/gi;
+  s/<sup$R/\^{/gi;
+  s/<box\s*size=["']?(\w+)["']?>/{\\def\\lft{$s{$1}}\\def\\rgt{$s{$1}}/gi;
+  s/<box$R/{/gi;
+  s/<text\s*>/\\hbox{/gi;
+  s/([\(\[\|])\s*<left>/\\lft$1/gi;
+  s/<right>/\\rgt /gi;
+  s/<(atop|choose|over)>/\\\L$1 /gi;
+  s/<of>/}\\of{/gi;
+  s/<bar>/\\overline{/gi;
+  s/<vec>/\\overrightarrow{/gi;
+  s/<hat>/\\widehat{/gi;
+  s/<tilde>/\\widetilde{/gi;
+  s/<(sqrt|root|vec|dot|ddot|hat|tilde)>/\\\L$1\{/gi;
+  while(/<above\s+sym\s*=\s*["']?equals["']?\s*>/i) {
+    $beg=$`."\\overline{\\overline{";
+    $rest=$';
+    $rest=~s/<\/above>/}}/i;
+    $_=$beg.$rest;
+  }
+  s/<above\s*>/\\overline{/gi;
+  s/<above\s+sym\s*=\s*["']?(\w+)["']?\s*>/\\$a{$1}\{/gi;
+  s/<below\s*>/\\underline{/gi;
+  s/<below\s+sym\s*=\s*["']?(\w+)["']?\s*>/\\$b{$1}\{/gi;
+  s/<\/(math|row|item|b)>//gi;
+  s/<\/(box|t|sup|sub|sqrt|root|vec|bar|dot|ddot|hat|tilde|above|below|text|array)>/}/gi;
+  s/<\/bt>/}}/gi;
+  s/&lt;?/< /gi;
+  s/&gt;?/>/gi;
+  s/&(\w+);?/\\$1 /gi;
+  s/<array$R/\\matrix{/gi;
+  s/<row>\s*<item$R//i;
+  s/<row>\s*<item$R/\\cr /gi;
+  s/<item>/&/gi;
+  s/<[^ ]$R//gi;
+  s/\n*$//;
+  $tex="\\batchmode\\magnification=$mag\\hsize=40cm\\nopagenumbers\n"
+ ."\\def\\lft{\\left}\\def\\rgt{\\right}\n\\setbox0=\\hbox{\$$st$di".$_."\$}\n"
+ ."\\immediate\\write0{$prog: \\the\\ht0: \\the\\dp0}\\box0\n\\end\n";
+}
+sub Getopts {
+  local($optlist)=@_;
+  local(@args,$_,$opt,$opts,$rest,$olist,$plist,$found,@popts);
+  local($errs)=0;
+  local($[)=0;
+  @args=split( /\|/, $optlist );
+  for $opt (@args) {
+    if(substr($opt,-1,1) ne ':') {$olist.=$opt}
+    else {$plist.=$opt}
+  }
+  @popts=split(/:/,$plist);
+  while(@ARGV && ($_=$ARGV[0]) =~ /^-(.*)/) {
+    $opt=$1;
+    if($opt=~/^-/ && $optalias{"\L$'"}) {$opt=$optalias{"\L$'"}};
+    if($opt =~ /^[$olist]+$/) {
+      while ($char=chop $opt) {eval "\$opt_$char=1"}
+      shift(@ARGV);
+    }
+    else {
+      $found=0;
+      for $opts (@popts) {
+        $rest=substr($opt,length($opts));
+        if(index($opt,$opts)==0) {
+          $found=1;
+          shift(@ARGV);
+          if(length($rest)==0) {
+            ++$errs unless @ARGV;
+            $rest=shift(@ARGV);
+          }
+          eval "\$opt_$opts=\$rest";
+        }
+      }
+      if(!$found) {
+        &dbg("Unknown option: $opt\n");
+        ++$errs;
+        shift(@ARGV);
+      }
+    }
+  }
+  $errs==0;
+}
+sub openps {
+  open(STDOUT,">$opt_o") || die "*** Error opening $opt_o for output\n";
+}
+sub getalt {
+  if($imgcmd eq "img") {
+    $alt="";
+    $match=0;
+    if($img=~/alt\s*=\s*"([^"]*)"/i) {$alt=$1; $match=1};
+    if(!$match && $img=~/alt\s*=\s*([\w\.-]+)/i) {$alt=$1; $match=1};
+    if(!$match) {$alt=")WB IA WB("};
+    $text="$alt )WB(";
+    return;
+  }
+  if($imgcmd eq "hr") {
+    $text=$img=~/align\s*=\s*["']?(left|center|right)/i?")$algn{lc $1} ":")2 ";
+    $text.=$img=~/size\s*=\s*["']?$V/i?$1:.6;
+    $wd=1;
+    if($img=~/width\s*=\s*["']?$V(%?)/i) {$wd=$2?$1/100:-$1};
+    $text.=" $wd HR(";
+    return;
+  }
+  if($imgcmd eq "fig") {
+    $text=")BN(";
+  }
+}
+sub xbmtops {
+  $fc=1;
+  $dp=1;
+  ($xs,$ys)=$pic=~/^#define.* (\d+)[\w\W]*^#define.* (\d+)/;
+  $nd=2*int(($xs+7)/8)*$ys;
+  ($pic)=$pic=~/[^#].* char.*[\w\W]*{([\w\W]*)}/;
+  $pic=~s/[ ,\n\r]*0x[ ,]*//g;
+  $pic=~y/01246789bdef/f7bd91e62480/;
+  $bm=unpack("H*", pack("h*",$pic));
+}
+sub pmtops {
+  pmtoraw($pm) if($pm=~/^P([1-3])/);
+  $pm=~/^P([4-6])/;
+  $maptype=$1;
+  return if(!$maptype);
+  $pm=$';
+  $bm="";
+  $nint=3;
+  $dp=8;
+  if($maptype==4) {
+    $nint=2;
+    $dp=1;
+  }
+  undef @num;
+  $found=0;
+  while($pm && $found<$nint) {
+    if($pm=~/^\s*(\d+)/) {$num[$found]=$1};
+    if($num[$found]) {
+      $found++;
+      $pm=$';
+    } elsif($pm=~/^\s*#.*\n/) {
+      $pm=$';
+    } else {
+      return;
+    }
+  }
+  ($b)=$pm=~/\s([\w\W]*)/;
+  ($xs,$ys,$bits)=@num;
+  return if($bits>255);
+  $fc=1;
+  if($maptype==6) {
+    $fc=3;
+    $nd=6*$xs*$ys;
+    $bm=unpack("H*",$b);
+  } else {
+    $bm=unpack("H*",$b);
+    if($maptype==4) {
+      $nd=2*int(($xs+7)/8)*$ys;
+      $bm=~y/0123456789abcdef/fedcba9876543210/;
+    } else {
+      $nd=2*$xs*$ys;
+    }
+  }
+}
+sub trans {
+  $next = 13;
+  $temp = ord substr($pic,10,1);
+  if($temp & 0x80) {$next += 3*2**(($temp & 0x07) + 1)} else {return};
+  $byte = ord substr($pic,$next,1);
+  while($byte != 0x3b && $next <= length $pic) {
+    if($byte == 0x21) {
+      if(ord substr($pic,$next+1,1) == 0xf9) {
+        if(ord substr($pic,$next+3,1) & 0x01) {
+          &dbg("Transparent\n") if($opt_d);
+          $src{$URL}=$pic;
+          $idx = 3*(ord substr($pic,$next+6,1))+13;
+          substr($pic,$idx,3) = pack("H*",$red.$grn.$blu) if($idx<length $pic);
+        }
+        return;
+        $next += 2;
+        &skip;
+      } else {
+        $next += 2;
+        &skip;
+      }
+    } elsif($byte == 0x2c) {
+      $next += 10;
+      $temp = ord substr($pic,$next-1,1);
+      if($temp & 0x80) {$next += 3*2**(($temp & 0x07) + 1)};
+      $next++;
+      &skip;
+    } else {return}
+  }
+}
+sub skip {
+  $byte = ord substr($pic,$next,1);
+  while($byte != 0) {
+    $next += $byte + 1;
+    $byte = ord substr($pic,$next,1);
+  }
+  $next++;
+  $byte = ord substr($pic,$next,1);
+}
+sub run {
+  &dbg("@_\n") if($opt_d);
+  $pm=`@_`;
+}
+sub geturl {
+  local($url)=@_;
+  &dbg("Retrieving $url");
+  if($package{'libwww-perl'} || $package{'jfriedl'}) {
+    warn "\n";
+    &gu();
+    if($code==401) {
+      &prompt("\nDocument requires username and password\n\nUsername: ",$user);
+      &prompt("Password: ",$pass);
+      &gu($user,$pass);
+    }
+    $_[1]=$cont;
+  } elsif(defined $geturl) {
+    &dbg("...");
+    $_[1]=`$geturl '$url'`;
+    if($?) {
+      &dbg("\n*** Error opening $url\n");
+      return 0;
+    }
+    &dbg("done\n");
+    if($_[1]=~/\r?\n\r?\n/) {
+      $_[1]=$';
+      $dhead=$`;
+      ($code)=$dhead=~/HTTP\/\S+ +(\d+)/i;
+      ($contyp)=$dhead=~/Content-type:\s+(.*)/i;
+    } else {
+      $code=500;
+    }
+  }
+  $_[0]=$url;
+  $code<300;
+}
+sub gu {
+  if($package{'libwww-perl'}) {
+    require LWP::UserAgent;
+    $ua=new LWP::UserAgent;
+    $ua->env_proxy();
+    if ($opt_k) {
+      require HTTP::Cookies;
+      $cookie_jar = HTTP::Cookies::Netscape->new(File => $opt_k, AutoSave => 1);
+      $ua->cookie_jar($cookie_jar);
+    }
+    $req = HTTP::Request->new(GET => $url);
+    $req->authorization_basic(@_) if(@_);
+    $ua->agent($spoof) if($spoof);
+    my $res = $ua->request($req);
+    $code=$res->code;
+    $contyp=$res->header('content-type');
+    $cont=$res->content;
+  } else {
+    require "www.pl";
+    @opts=@_?("authorization=$_[0]:$_[1]"):();
+    push(@opts,"quiet") if(!$opt_d);
+    $www::useragent=$spoof if($spoof);
+    ($status,$memo,%info)=&www::open_http_url(*FILE,$url,@opts);
+    $code=$info{'CODE'};
+    ($contyp)=$info{'HEADER'}=~/Content-type:\s+(.*)/i;
+    $cont=<FILE>;
+  }
+}
+sub pictops {
+  if($opt_g) {
+    $fc=1;
+    $pg1="pgm";
+    $pg2="|ppmtopgm";
+  } else {
+    $fc=3;
+    $pg1="ppm";
+    $pg2="";
+  }
+  ($type)=$URL=~/([^\?]+)\??/;
+  ($type)=$type=~/\.(\w+)$/;
+  $bm="";
+  $ps="";
+  $pm="";
+  if($opt_U && $src{$URL} && !$cmd{$URL.$red.$grn.$blu}) {
+    $pic=$src{$URL};
+  } elsif($URL=~m|://|) {
+    &geturl($URL,$pic) || return;
+  } else {
+    $flag=0;
+    if($opt_O) {
+      $orig=$URL;
+      unless($orig=~s/\.\w*$/.ps/) {$orig.=".ps"};
+      if(open(ORIG,"$orig")) {
+        $pic=<ORIG>;
+        close ORIG;
+        if($pic=~/^%!/ && $pic=~/%%BoundingBox:/) {
+          $flag=1;
+          &dbg("Using $orig as original for $URL\n") if($opt_d);
+        }
+      }
+    }
+    if(!$flag) {
+      if(open(PIC,"$URL")) {
+        binmode PIC;
+        $pic=<PIC>;
+        close PIC;
+      } else {
+        &dbg("*** Error opening $URL\n");
+        return;
+      }
+    }
+  }
+  $pic=~s/^[\n\r]*//;
+  &trans if($pic=~/^GIF/);
+  if($pic=~/^P[1-6]/) {
+    $pm=$pic;
+  } else {
+    open(SCRATCH,">$scr");
+    binmode(SCRATCH);
+    print SCRATCH "$pic";
+    close SCRATCH;
+    if($pic=~/^%!/ && $pic=~/%%BoundingBox: +$V +$V +$V +$V/) {
+      $xs=$3-$1;
+      $ys=$4-$2;
+      $ps="save\n0 0 M\nIS IS scale\n/showpage {}D\n".(0-$1)." ".(0-$2)." translate\n";
+      for $i (split(/\n/,$pic)) {
+        $ps.=$i."\n" if($i && $i!~/^%/);
+      }
+      $ps.="restore";
+    } elsif($type=~/.xbm$/i || $pic=~/^#define/) {
+      &xbmtops;
+    } elsif($package{'ImageMagick'}) {
+      if($package{'PerlMagick'}) {
+        $imobj=Image::Magick->new;
+        $mess=$imobj->Read($scr);
+        if($mess) {
+          &dbg("$mess\n");
+        } else {
+          $mess=$imobj->Write("$scr\.$pg1");
+          &dbg("$mess\n") if($mess);
+        }
+        undef $imobj;
+      } else {
+#        &run("convert $scr $pg1:-");
+        &run("convert $scr $scr\.$pg1");
+      }
+      open(PNM,"$scr\.$pg1");
+      binmode PNM;
+      $pm=<PNM>;
+      close PNM;
+      if(!$pm && $pic=~/^\377\330/ && $package{'djpeg'}) {
+        &run("djpeg $scr$pg2");
+      }
+    } elsif($pic=~/^\377\330/ && $package{'djpeg'}) {
+      &run("djpeg $scr$pg2");
+    } elsif($package{'pbmplus'} || $package{'netpbm'}) {
+      if($pic=~/^GIF/) {
+        &run("$giftopm $scr");
+      } else {
+        &run("anytopnm $scr");
+      }
+      if($opt_g && $pm=~/^P6/) {
+        open(SCRATCH,">$scr");
+        binmode(SCRATCH); 
+        print SCRATCH $pm;
+        close SCRATCH;
+        &run("ppmtopgm $scr");
+      }
+    }
+  }
+  &pmtops if(!$bm);
+  return if(!$bm);
+  $bm=substr($bm,0,$nd);
+  $pad=$nd-length($bm);
+  if($pad) {$bm.="f" x $pad};
+  $bm=~s/(.{60})/$1\n/g;
+}
+sub math2sym {
+  local($_)=@_;
+  s/<math$R//gi;
+  for $char (keys %symb) {s/&($char)(;|$|(?=\W))/\\$symb{$char}/g};
+  $stat=!/([&<][a-zA-Z]|[_^{])/;
+  s/[a-zA-Z\s]*[a-zA-Z][a-zA-Z\s]*/)ES()I($&)ES()SY(/g;
+  s/(\\200|\\201|\\202)/)RO($&)ES(/g;
+  $sym=")SY($_)ES(";
+  $stat;
+}
+sub varsub {
+  for (@_) {
+    s/\\\\/\000/g;
+    s/\([^)]+\)/()$&join /g;
+    s/(^|[^\\])\$(T|N|U|H|A)/$1)join $vars{$2} join(/g;
+    s/(^|[^\\])\$D\{"(.*?)"\}/"$1".POSIX::strftime($+,@now)/eg if($posix);
+    s/(^|[^\\])\$D/"$1".POSIX::strftime($datefmt,@now)/eg if($posix);
+#    while(/(?=[^\\])\${([^}]+)}/) {
+    while(/(?=[^\\])\$\[([^]]+)\]/) {
+      if(!defined $metarc{lc $1}) {
+        $metarc{lc $1}=$mn++;
+      }
+      $_="$`)join ME $metarc{lc $1} get join($'";
+    }
+    s/\\\$/\$/g;
+    s/\000/\\\\/g;
+    s/\(\)join//g;
+    s/\(\) ?(\([^)]*\)|\w+) ?join/ $1/g;
+  }
+}
+sub follow {
+  return 0 if(!$opt_W);
+  $H=$lnk=~/\.html?(#|$)/i || $lnk=~m|.+//.+/[^/\.]*$|;
+  $T=$rev && ($revtoc && $ndoc==1 || $ndoc>1 && $H);
+  $L=$b1=~m|://| && $lnk=~m"^$b1(/|$)" || $b1!~m|://| && $lnk!~m|://|;
+  $B=$B2 && $lnk=~/^$B2/ || !$B2 && $lnk!~m"(^\.\.|://)";
+  return $rlnk && ($H || $T) if($rel);
+  return $L && ($H || $T) if($local);
+  return $B && ($H || $T) if($below);
+  return $H || $T if($rev);
+  $H;
+}
+sub DSC {
+  &dbg("Generating DSC PostScript\n") if($opt_d);
+  %op=("moveto",2, "rmoveto",2, "lineto",2, "rlineto",2, "translate",2,
+       "scale",2, "show",1, "awidthshow",6, "stroke",0, "save",0, "restore",0,
+       "gsave",0, "grestore",0, "showpage",0, "newpath",0, "setlinewidth",1,
+       "setlinejoin",1, "setgray",1, "closepath",0, "fill",0, "arc",5,
+       "setrgbcolor",3, "rotate",1, "image",5, "colorimage",7);
+  %sho=("moveto","M", "rmoveto","RM", "lineto","L", "rlineto","RL", "show","S",
+        "showpage","N", "awidthshow","A");
+  $i=0;
+  $po="/OU true D\n";
+  for (keys %op) {
+    $cmd=$sho{$_}?$sho{$_}:$_;
+    push(@val,$cmd);
+    $in{$_}=$i++;
+    $j=$op{$_}+1;
+    $extra="";
+    if(/showpage/) {$extra="Bb{Xl Yl Xh Yh}if Pn "};
+    if(/image/) {$extra="K ";$j++};
+    $t=$op{$_}?"$op{$_} copy $extra$in{$_} $j array astore":"[$extra$in{$_}]";
+    $po.="/$_ {OU {$t ==} if $_}d\n";
+  }
+  $po.="/pdfmark {$i] ==} D\n";
+  $in{"pdfmark"}=$i++;
+  push(@val,"pdfmark");
+  $po.="/NF {OU{2 copy E $i 3 array astore ==}if ONF}d\n"
+      ."/EX {[IS EC] ==} D\n/Cd {} D\n/DU {TU PM 1 eq and TP and{Pn ==}if}d\n"
+      ."/BB {US Bb{dup Yl lt{dup /Yl E D}if dup Yh gt{/Yh E D}{pop}ie\n"
+      ." dup Xl lt{dup /Xl E D}if dup Xh gt{/Xh E D}{pop}ie}\n"
+      ." {/Yl E D /Yh Yl D /Xl E D /Xh Xl D /Bb t D}ie}D\n";
+  $in{"Nf"}=$i++;
+  s|/(NF.*)|/O$1|;
+  s|/BB .*|$po|;
+  push(@val,"Nf");
+  if($psin) {
+    ($ti)=/%%Title: (.*)/;
+    if(m|/BM (\d+)|) {
+      $nbit=$1;
+      for $vec ("IT","WS") {
+        /\/$vec \[(.*)\]/;
+        @$vec=split(' ',$1);
+      }
+      /\nSB/;
+      for (0..$nbit-1) {
+        $'=~/\n\n/;
+        push(@BM,$`);
+      }
+    }
+    ($epsf)=/(\n\/P\d+_?\d* [\w\W]*)%Endpv/;
+    while($epsf=~/\n\/(P\d+_?\d*) \{/g) {
+      $pid=$1;
+      $rest=$';
+      $temp=$'=~/\/P\d+_?\d* \{/?$`:$rest;
+      ($eps{$pid})=$temp=~/([\w\W]*)} D/;
+    }
+    if(/\/DH {/) {
+      $'=~/%EndDH/;
+      $ph="/DH {$`";
+    }
+  }
+  $j=-1;
+  for $i (0..$#IT) {
+    $j++ if($IT[$i] == 0);
+    push(@ix,$j);
+  }
+  $dfn="/F $opt_s D\n$ph";
+  for (keys %sho) {
+    $dfn.="/$sho{$_} {$_} d\n";
+  }
+
+  open(SCR,">$scr.ps");
+  print SCR "$_ quit\n";
+  close SCR;
+  $cd{')]'}="Cd ";
+  $io="($in{'image'}|$in{'colorimage'})";
+  $_="";
+  $temp="";
+  $pn="";
+  $start=1;
+  $EPS="%%EndPageSetup";
+  $pp=0;
+  $n=0;
+  
+  for $line (split(/\r?\n/,`$gs -q -dNODISPLAY $scr.ps -c quit`)) {
+    if(!$pp) {
+      $mv="";
+      $cx="";
+      $cy="";
+    }
+    if($start && $line!~/ $in{"pdfmark"}\]$/) {
+      $pdf=$temp;
+      $temp="";
+      $start=0;
+    }
+    $pp=$line=~/^\[(\S+) (\S+) ($in{'moveto'}|$in{'rmoveto'}|$in{'Nf'})\]$/;
+    S:{
+      if($pp && $3==$in{"Nf"}) {
+        $fn="$1 $2 $val[$3]\n";
+        last S;
+      }
+      if($pp && $3==$in{"moveto"}) {
+        $cx=$1;
+        $cy=$2;
+        $mv=sprintf("%.1f %.1f %s\n",$1,$2,$val[$3]);
+        last S;
+      }
+      if($pp && $3==$in{"rmoveto"}) {
+        if($mv) {
+          $cx+=$1;
+          $cy+=$2;
+          $mv=sprintf("%.1f %.1f %s\n",$cx,$cy,$val[$in{"moveto"}]);
+        } else {
+          $temp.=sprintf("%.1f %.1f %s\n",$1,$2,$val[$3]) if($1||$2);
+        }
+        last S;
+      }
+      if($line=~s/^\[(.*)\((\S*)\) ($in{"showpage"})\]$/pgsave restore $val[$3]/) {
+        $pbb="";
+        if($1) {
+          ($llx,$lly,$urx,$ury)=split(/ /,$1);
+          $llx=int($llx);
+          $lly=int($lly);
+          $urx=int($urx+1);
+          $ury=int($ury+1);
+          $pbb="%%PageBoundingBox: $llx $lly $urx $ury\n";
+          if(!defined($Llx) || $llx<$Llx) {$Llx=$llx};
+          if(!defined($Lly) || $lly<$Lly) {$Lly=$lly};
+          if(!defined($Urx) || $urx>$Urx) {$Urx=$urx};
+          if(!defined($Ury) || $ury>$Ury) {$Ury=$ury};
+        }
+        $pn.="," if($pn && $2);
+        $n++;
+        @df=();
+        for(0..$#docfonts) {
+          push(@df,$docfonts[$_]) if($uf{$_}==$n);
+        }
+        push(@df,"Symbol") if($uf{"-1"}==$n);
+        $fu="";
+        $tmp=@df?"%%PageResources: font":"";
+        &splitline(@df);
+        $_.="%%Page: $pn$2 $n\n$fu$pbb%%BeginPageSetup\n/pgsave save D\n$temp$line\n";
+        $EPS="%%EndPageSetup";
+        $temp="";
+        $line="";
+        $fn="";
+        $mv="";
+        $pn="";
+        last S;
+      }
+      if($line=~/^\[([^(]*)(\(.*\)) ($in{"show"}|$in{"awidthshow"})\]$/) {
+        if(length $2>2) {
+          $line="$fn$1$2 $val[$3]";
+          if($fn=~/(\S+) Nf/ && $uf{$1}!=$n+1) {
+            $uf{$1}=$n+1;
+            $fnt=$1<0?"Symbol":$docfonts[$1];
+            $line="%%IncludeResource: font $fnt\n$line";
+          }
+          $fn="";
+          $pp=0;
+        } else {
+          $pp=1;
+        }
+        last S;
+      }
+      if($line=~/^\[(\S+) (\S+) ($in{"scale"})\]$/) {
+        $line=$1!=1||$2!=1?"$1 $2 $val[$3]\n$EPS":"$EPS";
+        $EPS="";
+        last S;
+      }
+      if($line=~/^\[(.*) (\d+) $io\]$/) {
+        $li=$BM[$2]=~y/\n/\n/+2;
+        $line="\/picstr $WS[$ix[$2]] string D\n$1\n"
+             ."%%BeginData: $li Hex Lines\n$val[$3]\n$BM[$2]\n%%EndData";
+        last S;
+      }
+      if($line=~/^\((.*)\)$/) {
+        $pn=$1;
+        $line="";
+        last S;
+      }
+      last S if($line=~s/^\[([^\/].* )?(\d+)\]$/$1$val[$2]/);
+      last S if($line=~s/(\)\])? (\/\w+) $in{"pdfmark"}\]$/$1 $cd{$1}$2 pdfmark/);
+      last S if($line=~s/^\[(\S+) \((P\d+.*)\)\]$/\/IS $1 D\n$eps{$2}/);
+      &dbg("$line\n");
+    }
+    if(!$pp && $mv.$line) {
+      $mv=~s/\.0//g;
+      $temp.="$mv$line\n";
+    }
+  }
+  @nf=();
+  @sf=();
+  $fontdef="";
+  for(0..$#docfonts) {
+    $ff=$ff{$docfonts[$_]};
+    if($ff && $uf{$_}>0) {
+      push(@sf,$docfonts[$_]);
+      $fontdef.="%%BeginResource: font $docfonts[$_]\n$cont{$ff}\n%%EndResource\n";
+      $cont{$ff}="";
+    }
+    push(@nf,$docfonts[$_]) if(!$ff && $uf{$_}>0);
+  }
+  push(@nf,"Symbol") if($uf{"-1"}>0);
+  $ti="@ARGV" unless($psin);
+  $or=$opt_L?"Landscape":"Portrait";
+  $setup="%%BeginSetup\n";
+  $setup.="$dupl\n" if($dupl);
+  $setup.="$fontdef" if($fontdef);
+  $fu="";
+  $tmp=@nf?"%%DocumentNeededResources: font":"";
+  &splitline(@nf);
+  $tmp=@sf?"%%DocumentSuppliedResources: font":"";
+  &splitline(@sf);
+  s/\\(200|201|202)/\\$ssy{$1}/g;
+  $dd="Clean7Bit";
+  $dd="Clean8Bit" if(($fontdef.$_)=~/[\200-\377]/);
+  $dd="Binary" if(($fontdef.$_)=~/[\000-\010\013-\014\016-\036]/);
+  $time=localtime;
+  print <<EOT;
+%!PS-Adobe-3.0
+%%Title: $title
+%%Creator: $version
+%%CreationDate: $time
+$fu%%DocumentData: $dd
+%%Orientation: $or
+%%BoundingBox: $Llx $Lly $Urx $Ury
+%%Pages: $n
+%%EndComments
+%%BeginProlog
+/d {bind def} bind def
+/D {def} d
+/ie {ifelse} d
+/E {exch} d
+/t true D
+/f false D
+$fl
+$cd
+$defs
+$mysymb/Nf {dup 0 ge{FL E get}{-1 eq{/Symbol}{/MySymbol}ie}ie findfont
+ E scalefont setfont} D
+/IP {currentfile picstr readhexstring pop} D
+/WF $wf D
+$dfn%%EndProlog
+$setup$reenc$pdf%%EndSetup
+$_%%EOF
+EOT
+}
+sub splitline {
+  for (@_) {
+    if(length($tmp.$_)>78) {
+      $fu.="$tmp\n";
+      $tmp="%%+ font";
+    }
+    $tmp.=" $_";
+  }
+  $fu.="$tmp\n" if($tmp);
+}
+sub ref {
+  @pnum=();
+  /.*\s*$/;
+  open(SCR,">$scr.ps");
+  print SCR "$`HN{==}forall $& quit\n";
+  close SCR;
+  $pnum=`$gs -q -dNODISPLAY $scr.ps -c quit`;
+  while($pnum=~/\([^)]*\)/g) {push(@pnum,$&)};
+  $pnum="@pnum";
+  &cut($pnum);
+  s|/HN [^D]*D|/HN [$pnum] D|;
+}
+sub cut {
+  $_[0]=~s/(.{70}[^ \n]*) ([^ ])/$1\n$2/g;
+}
+sub fin {
+  if($opt_D) {
+    &DSC;
+  } else {
+    print;
+  }
+  if($opt_d) {
+    print DBG "\n";
+    close DBG;
+  }
+  unlink "$scr","$scr.ps","$scr.ppm","$scr.tex","$scr.dvi","$scr.log" if($scr);
+  exit;
+}
+sub col2rgb {
+  $rgb=$colour{"\L$_[0]"}?($colour{"\L$_[0]"}):$_[0];
+  @cvec=$rgb=~/($X$X)($X$X)($X$X)/?($1,$2,$3):();
+  @cvec?"[16#$1 16#$2 16#$3]":"";
+}
+sub inihyph {
+  if($hyphenation_file{$lang}) {
+    $hyfile=$hyphenation_file{$lang};
+  } else {
+    &dbg("No hyphenation file for language '$lang'\n");
+    $lng=$lang;
+    while($lng=~s/-?[^-]+$// && !$hyphenation_file{$lng}) {};
+    $hyfile=$hyphenation_file{$lng};
+    &dbg(" ..using $hyfile\n");
+  }
+  if($init{$hyfile}) {
+    $rep{$lang}=$refl{$hyfile};
+    return;
+  }
+  if(open(HYPH,$hyfile)) {
+    &dbg("Reading hyphenation patterns from $hyfile\n") if($opt_d);
+    <HYPH>=~/\\patterns{.*/;
+    close HYPH;
+    $def=$`;
+    ($patterns=$')=~s/\^\^($X$X)/chr hex $1/eg;
+    $upp{$lang}='';
+    $low{$lang}='';
+    while ($def=~/\\lccode(`\\?\^\^|")($X$X)=(`\\?\^\^|")($X$X)/g) {
+      if($2 ne $4) {
+        $uc=$2;
+        $lc=$4;
+        if($`=~/\n$/ || $`!~/%.*$/) {
+          $upp{$lang}.=chr hex $uc;
+          $low{$lang}.=chr hex $lc;
+        }
+      }
+    }
+    while ($def=~/\\let\\(\w+)=(\^\^|")($X$X)/g) {
+      $key=$1;
+      $value=chr hex $3;
+      $tex{$key}=$value if($`=~/\n$/ || $`!~/%.*$/);
+    }
+    for $key (keys %tex) {
+      $patterns=~s/\\$key */$tex{$key}/g;
+    }
+    if($lang=~/^de/) {
+      %de=('"a',228, '"o',246, '"u',252, '\3', 223);
+      $patterns=~s/\\c\{[^}]*\}//g;
+      $patterns=~s/\\n\{([^}]*)\}/$1/g;
+      $patterns=~s/("a|"o|"u|\\3)/chr $de{$1}/eg;
+      $upp{"de"}=~s/\337//;
+      $low{"de"}=~s/\377//;
+    }
+    if($lang=~/^is/ && !$upp{"is"}) {
+      %is=("'a","\341", "'e","\351", "'i","\355", "'o","\363", "'u","\372",
+           "'y","\375", '"x',"\346", '"o',"\366", "'d","\360", "`t","\376");
+      $isch=join("|",keys %is);
+      $patterns=~s/($isch)/$is{$1}/g;
+      $upp{"is"}="\301\311\315\323\332\335\306\326\336\320";
+      $low{"is"}="\341\351\355\363\372\375\346\366\376\360";
+    }
+    if($lang=~/^fi/ && !$upp{"fi"}) {
+      $upp{"fi"}="\304\326";
+      $low{"fi"}="\344\366";
+    }
+    if($lang=~/^fr/ && !$upp{"fr"}) {
+      $upp{"fr"}="\300\302\307\311\310\312\313\316\317\324\326\333\226";
+      $low{"fr"}="\340\342\347\351\350\352\353\356\357\364\366\373\225";
+    }
+    if($lang=~/^es/ && !$upp{"es"}) {
+      $upp{"es"}="\301\311\315\323\321\332\334";
+      $low{"es"}="\341\351\355\363\361\372\374";
+    }
+    $patterns=~s/\{([\w\W]*?)\}/[$1]/g;
+    $patterns=~/}/;
+    $end=$`;
+    if($def.$'=~/\\hyphenation\[.*/) {
+      $'=~/]/;
+      $hyext=$`;
+    }
+    ($patterns=$end)=~s/%.*//g;
+    for $key (split('\s+',$patterns)) {
+      $value=$key;
+      $key=~s/\d//g;
+      $value=~s/^([$ltrs.])/0$1/;
+      $value=~s/[$ltrs](\d)/$1/g;
+      $value=~s/[$ltrs.]/0/g;
+      $patt{"$key,$lang"}=$value if($value=~/^\d+$/);
+    }
+  } else {
+    &dbg("Cannot open hyphenation file: $hyfile\n");
+  }
+  $hext=$hyphenation_extfile{$lang};
+  for(split('\s*:\s*',$hext)) {
+    if(open(HEXT,$_)) {
+      &dbg("Reading hyphenation extensions from $_\n") if($opt_d);
+      $hyext.=<HEXT>;
+      close HEXT;
+    } else {
+      &dbg("Cannot open hyphenation extension file: $_\n");
+    }
+  }
+  if($hyext) {
+    for $key (keys %tex) {
+      $hyext=~s/$key */$tex{$key}/g;
+    }
+    for $key (split('\s+',$hyext)) {
+      $key=~s/\s+//g;
+      $value="00$key\0";
+      $key=~s/-//g;
+      $value=~s/[$ltrs]/0/g;
+      $value=~s/0-/1/g;
+      $hext{"$key,$lang"}=$value;
+    }
+  }
+  $refl{$hyfile}=$lang;
+  $rep{$lang}=$lang;
+  $init{$hyfile}=1;
+}
+sub hyph {
+  $word="\L$_[0]";
+  eval "\$word=~y/$upp{$rep{$lang}}/$low{$rep{$lang}}/" if($upp{$rep{$lang}});
+  $len=length($word);
+  $h=$hext{"$word,$rep{$lang}"};
+  if($h) {
+    @br=split(//,$h);
+  } else {
+    @br=(0) x ($len+3);
+    for $i (0..$len) {
+      for $j (0..$len-$i) {
+        $str=substr(".$word.",$j,$i+2);
+        $pstr=$patt{"$str,$rep{$lang}"};
+        if($pstr) {
+          @patt=split(//,$pstr);
+          for $k (0..$#patt) {
+            $br[$k+$j]=$patt[$k] if($br[$k+$j]<$patt[$k]);
+          }
+        }
+      }
+    }
+  }
+  $hword="";
+  for $i (0..$len-1) {
+    $hword.=substr($_[0],$i,1);
+    if(($h || $i>$hyphenation{'start'}-2 && $i<$len-$hyphenation{'end'})
+      && $br[$i+2]%2==1) {$hword.=")HY("};
+  }
+  $hword.=")YH(" if(length $word < length $hword);
+  $hword;
+}
+sub setel {
+  $el=$_[0];
+  eval "\%arr=\%$el";
+  &fs($el);
+  push(@font,$fontid{"\L$font"});
+  push(@styl,$styl);
+  push(@size,$arr{'font-size'});
+  push(@alig,$algn{$arr{'text-align'}}-1);
+  push(@topm,$arr{'margin-top'});
+  push(@botm,$arr{'margin-bottom'});
+  push(@lftm,$arr{'margin-left'});
+  push(@rgtm,$arr{'margin-right'});
+  push(@colr,$col eq "[16#00 16#00 16#00]"?0:$col);
+  $temp=$arr{'margin-top'}*$arr{'font-size'};
+  $mi=$temp if($temp>$mi);
+  $temp=$arr{'margin-bottom'}*$arr{'font-size'};
+  $mi=$temp if($temp>$mi);
+}
+sub fs {
+  $arr{'font-family'}='times' if($el ne 'p' && !$latin1 && !defined $arr{$_});
+  for ("font-family","font-size") {
+    $arr{$_}=$body{$_} if(!defined $arr{$_});
+  }
+  ($font=$arr{'font-family'})=~s/\W/-/g;
+  if(!$font_names{"\L$font"}) {$font=$fal{$font}};
+  if(!$font_names{"\L$font"}) {
+    &dbg("Unknown font: $arr{'font-family'}, using $deffnt{$_[0]}\n");
+    $font=$deffnt{$_[0]};
+  }
+  if(!defined $fontid{"\L$font"}) {
+    $fontid{"\L$font"}=$nfont++;
+    @names=split(/\s+/,$font_names{"\L$font"});
+    for($#names+1..3) {push(@names,$names[0])};
+    @docfonts=(@docfonts,@names);
+  }
+  &getval($arr{"font-size"},2);
+  for ('left','right','top','bottom') {
+    $arr{"margin-$_"}=0 if(!defined $arr{"margin-$_"});
+  }
+  for ($arr{"text-indent"},$arr{"margin-top"},$arr{"margin-bottom"},
+       $arr{"margin-left"},$arr{"margin-right"}) {
+    &getval($_,0);
+  }
+  $styl=$arr{'font-style'}=~/^(i|o)/+2*($arr{'font-weight'}=~/^b/);
+  $col=$arr{'color'}?&col2rgb($arr{'color'}):-1;
+}
+sub img {
+  local($_,$red,$grn,$blu)=@_;
+  local($beg,$end);
+  ($red,$grn,$blu)=("FF","FF","FF") if(!$opt_U);
+  while (/<(img|fig|hr|overlay|object)\s/i) {
+    $imgcmd="\L$1";
+    $beg=$`;
+    $'=~/>/;
+    $img=" $`";
+    $end=$';
+    $img=~s/\n/ /g;
+    if($imgcmd ne "object" || $img=~/data\s*=\s*['"]?([\w\/\.:~%-]+\.$IM)/i
+       || $img=~/type\s*=\s*['"]?(image\/|application\/postscript)/i){
+    if($opt_T) {
+      &getalt;
+    } else {
+      $al=0;
+      $off="";
+      ($align)=$img=~/align\s*=\s*['"]?(\w*)/i;
+      if($align=~/^middle$/i) {$al=1};
+      if($align=~/^top$/i) {$al=2};
+      if($imgcmd eq "overlay") {
+        $al=4;
+        $xoff=0;
+        $yoff=0;
+        if($img=~/\s*x\s*=\s*['"]?(\d+)/i) {$xoff=$1};
+        if($img=~/\s*y\s*=\s*['"]?(\d+)/i) {$yoff=$1};
+        $off="$xoff $yoff ";
+      }
+      $url="";
+      if($img=~/\s(src|data)\s*=\s*($S)/i) {($url)=$+=~/([^ \n]*)/};
+      &dbg("Image: $url\n") if($opt_d && $url);
+      $URL=$url;
+      unless($url=~m|://|) {
+        $url=~s/^file://;
+        if($url=~m|^/|) {$URL=$b1.$url} else {$URL=$b2.$url}
+      }
+      while($URL!~m|^\.\./| && $URL=~m|[^/]*/\.\./|) {$URL=$`.$'};
+      $URL=~s|/\./|/|g;
+      $text=$src{$URL}?$cmd{$URL.$red.$grn.$blu}:$cmd{$URL};
+      if(!$text || $opt_U && $src{$URL} && !$cmd{$URL.$red.$grn.$blu}) {
+        if(!$url || $failed{$url}) {
+          &getalt;
+        } else {
+          &pictops;
+          if($bm || $ps) {
+            &dbg("Size: $xs*$ys\n") if($opt_d);
+            $nimg++;
+            push(@XS,$xs);
+            push(@YS,$ys);
+            if($bm) {
+              $nm++;
+              push(@DP,$dp);
+              push(@BM,$bm);
+              push(@WS,int(($xs-1)*$dp/8)+1);
+              push(@FC,$fc);
+              push(@IX,$nm);
+              push(@IT,0);
+            }
+            if($ps) {
+              $nps--;
+              push(@IX,$nps);
+              push(@IT,1);
+              $nli=30000;
+              $n=1;
+              $npr=$ps=~s|(.*\n){$nli}|sprintf("$&} D\n/P$nimg\_%d {",$n++)|eg;
+              if($npr) {
+                $proc=" (";
+                for $i (0..$npr) {
+                  $proc.="P$nimg\_$i ";
+                }
+                $proc.=")";
+                $pv.="/P$nimg\_0 {$ps} D\n";
+                $eps{"P$nimg\_0"}=$ps;
+              } else {
+                $proc=" (P$nimg)";
+                $pv.="/P$nimg {$ps} D\n";
+                $eps{"P$nimg"}=$ps;
+              }
+            }
+            $text="$proc $nimg IM(";
+            $cmd{$URL}=$text if(!$cmd{$URL});
+            $cmd{$URL.$red.$grn.$blu}=$text if($src{$URL});
+            $proc="";
+            $end=$' if($imgcmd eq "object" && $end=~m|</object>|i);
+          } else {
+            &getalt;
+            $failed{"$url"}=1;
+          }
+        }
+      } elsif($imgcmd eq "object" && $end=~m|</object>|i) {
+        $end=$';
+      }
+    }
+    if($cmd{$URL}) {
+      $text=")".$off.$al.$text;
+      if($imgcmd eq "fig") {
+        $end=~m|</fig>|i;
+        $fig=$`;
+        $end=$';
+        $over="";
+        while($fig=~/(<overlay$R)/ig) {$over.=$1};
+        ($dum,$cap)=$fig=~m|<caption$R([\w\W]*)</caption>|i;
+        ($dum,$cred)=$fig=~m|<credit$R([\w\W]*)</credit>|i;
+        $text=")BN($text$over)BN($cap)BN($cred)BN(";
+      }
+    }
+    }
+    $_=$beg.$text.$end;
+  }
+  s|<[hH][rR]$R|)2 1 1 HR(|g;
+  $_[0]=$_;
+}
+sub getval{
+  local($val,$unit)=$_[0]=~/$V\s*(\w*)/g;
+  $val*=$cm{$unit} if($_[1]==1 && defined $cm{$unit});
+  $val*=$pt{$unit} if($_[1]==2 && defined $pt{$unit});
+  $_[0]=$val;
+}
+sub getconf {
+  local($_)=@_;
+  while(/\@import\s+(([\w.\/-]+)|"([^"]*)"|'([^']*)')\s*;/) {
+    if(open(SS,$+) && !$read{$+}) {
+      $conf=<SS>;
+      $_=$`.$conf.$';
+      print DBG "***** $+:\n$conf" if($opt_d);
+      close SS;
+      $read{$+}=1;
+    } else {
+      &dbg($read{$+}?"Infinite \@import loop: $+\n":"Error opening: $+\n");
+      $_=$`.$';
+    }
+  }
+  @block=();
+  while(&getblk($_)){};
+}
+sub getblk {
+  local($_)=@_;
+  local ($beg,$match,$end,$blk,$key,$val,$id,$temp);
+  while(/^\s*\/\*/) {
+    /\*\/|$/;
+    $_=$';
+  }
+  return 0 if !/\S/;
+  /[\w,:.@\s-]+\{/;
+  $_=$';
+  ($id=$&)=~s/^\s*|\s*\{//g;
+  $id=lc $id;
+  push(@block,"\L$id");
+  if($#block==1) {
+    $valid{$id}=1 if(!$user);
+    if($id eq "color") {$id="colour"};
+    if(!$valid{$id}) {
+      &dbg("Error in configuration file: unknown block name '$id'\n");
+    }
+  }
+  $blk="";
+  W:while(/\s*(\/\*|[\w][\w-]*\s*:|[\w,:.\s-]+\{|\})\s*/) {
+    $blk.=$1 if($1 ne "/*");
+    $beg=$`;
+    $match=$1;
+    $end=$';
+    S:{
+      if($match=~/\{$/) {
+        $temp=$match.$end;
+        $blk.=&getblk($temp);
+        $_=$temp;
+        last S;
+      }
+      if($match=~/:$/) {
+        ($key=$`)=~s/\s*$//;
+#        $end=~/([\w.\$-]+|"[^"]*"|'[^']*')\s*;?/;
+        $end=~/("[^"]*"|'[^']*'|.*?(?= *(\/\*|;|}|$)))/m;
+        $blk.=$`.$&;
+        $_=$';
+        ($val=$1)=~s/^["']|["']$//g;
+        $val=~s/'|\\/\\$&/g;
+        $typ=1;
+        $typ=2 if($val=~/^$V(cm|mm|in|pt|pc|em)$/);
+        $typ=3 if($val=~/^$V$/);
+        $typ=4 if($val=~/^-?\d+$/);
+        $typ=5 if($val eq "0" || $val eq "1");
+        if($block[0] eq '@html2ps') {
+          if($#block==0) {
+            if(!$user) {
+              $valid{$key}=1;
+              $type{$key}=$typ if(!defined $type{$key});
+            }
+            if($valid{$key}) {
+              if($typ>=$type{$key}) {
+                $key=~s/-/_/g;
+                eval "\$$key='$val'" if($user || $val ne '');
+#                print DBG "\$$key='$val'\n" if($opt_d && $user);
+              } elsif($user) {
+                &dbg("Error in configuration file: bad value for $key: $val\n");
+              }
+            } else {
+              &dbg("Error in configuration file: unknown key '$key'\n");
+            }
+          }
+          if($#block==1) {
+            if($id eq "option" && $optalias{$key}) {$key=$optalias{$key}};
+            if(!$user) {
+              $valid{"$id,$key"}=1;
+              $type{"$id,$key"}=$typ if(!defined $type{"$id,$key"});
+            }
+            if($valid{"$id,$key"} || $extend{$id}) {
+              if($typ>=$type{"$id,$key"} || $id eq "colour") {
+                eval "\$$id\{'$key'}='$val'" if($user || $val ne '');
+#                print DBG "\$$id\{'$key'}='$val'\n" if($opt_d && $user);
+              } elsif($user) {
+                &dbg("Error in configuration file: bad value for $key: $val\n");
+              }
+            } else {
+              &dbg("Error in block '$id' in configuration file:"
+                  ." unknown key '$key'\n");
+            }
+          }
+          if($#block>1) {
+            $temp="$block[$#block-1]_$key";
+            $valid{$temp}=1 if(!$user);
+            $parblk=$block[$#block-1];
+            if($valid{$temp}) {
+              eval "\$$parblk\_$key\{'$id'}='$val'";
+#              print DBG "\$$parblk\_$key\{'$id'}='$val'\n" if($opt_d && $user);
+            } elsif($valid{$parblk}) {
+              &dbg("Error in block '$parblk' in configuration file:"
+                  ." unknown key '$key'\n");
+            }
+          }
+        } else {
+          for $i (split(',\s*',$id)) {
+            $i=~s/@/AT_/;
+            $i=~s/\./_/;
+            $i=~s/ *:/__/;
+            eval "\$$i\{'\L$key'}='\L$val'";
+#            print DBG "\$$i\{'\L$key'}='\L$val'\n" if($opt_d && $user);
+          }
+        }
+        last S;
+      }
+      if($match eq "/*") {
+        /\*\/|$/;
+        $_=$';
+        last S;
+      }
+    last W;
+    }
+  }
+  pop(@block);
+  $_[0]=$end;
+  $blk;
+}
+sub prompt {
+  local($/)="\n";
+  &dbg($_[0]);
+  chop($_[1]=<STDIN>);
+}
+sub dbg {
+  print STDERR $_[0];
+  print DBG $_[0];
+}
+sub hb {
+  local($_)=@_;
+  local($head,$body,$beg,$end,$match,$tag);
+#If neither </HEAD> nor <BODY> can be found, find the separation point (messy).
+  if(!/<(body|\/head)/i || $`=~/<plaintext|<xmp|<listing|<!--/i) {
+    $head="";
+    $int="";
+    S1: while(/<(\/?\w+|!--|!|\?)/) {
+      S2:{
+        $beg=$`;
+        $end=$';
+        $match=$&;
+        $tag=$1;
+        if($tag eq "!--") {
+          $int.=$`;
+          &getcom;
+          $int.=$com;
+          $_=$rest;
+          last S2;
+        }
+        if($tag=~/[!?]/) {
+          $end=~/>/;
+          $int.="$beg$match$`>";
+          $_=$';
+          last S2;
+        }
+        $tag=~s|/||;
+        last S1 if(!$head{"\L$tag"});
+        $end=~/$R/;
+        $head.=$int.$beg.$match.$&;
+        $int="";
+        $_=$';
+      }
+    }
+    $body=$int.$_;
+  } else {
+    $head=$`;
+    $body=$&.$';
+  }
+  $_[0]=$body;
+  $_[1]=$head;
+}
+sub open {
+  if($_[0]=~m|://|) {
+    &geturl($_[0],$_[1]);
+  } elsif(open(FILE,$_[0])) {
+    $_[1]=<FILE>;
+    close FILE;
+  } else {
+    0;
+  }
+}
+sub pagedef {
+  for $margin ('left','right','top','bottom') {
+    ($m)=$margin=~/(.)/;
+    if(defined $margin{$margin}) {
+      &dbg("'margin { margin-$margin:... }' is obsolete, use '\@page'\n");
+      $AT_page{"margin-$margin"}=$margin{$margin} if(!defined $AT_page{"margin-$margin"});
+    }
+    for $page ('left','right') {
+      ($p)=$page=~/(.)/;
+        eval "\$m$m$p=\$AT_page\{'margin'} if(defined \$AT_page\{'margin'})";
+        eval "\$m$m$p=\$AT_page\{'margin-$margin'} if(defined \$AT_page\{'margin-$margin'})";
+        eval "\$m$m$p=\$AT_page__$page\{'margin'} if(defined \$AT_page__$page\{'margin'})";
+        eval "\$m$m$p=\$AT_page__$page\{'margin-$margin'} if(defined \$AT_page__$page\{'margin-$margin'})";
+    }
+  }
+}
+sub pmtoraw {
+  @pars=();
+  ($temp)=@_;
+  for $i (0..3) {
+    1 while ($temp=~s/^\s*#.*//);
+    next if($pars[0] eq 'P1' && $i == 3);
+    $temp=~s/\s*(\S+)\s*//;
+    $pars[$i]=$1;
+  }
+  $temp=~s/#.*//g;
+  $pars[0]=~s/\d/$&+3/e;
+  $_[0]="$pars[0]\n$pars[1] $pars[2]\n";
+  if($pars[0] eq 'P4') {
+    $temp=~s/\s//g;
+    $_[0].=pack("B*",$temp);
+  } else {
+    $_[0].="255\n";
+    while ($temp=~/\d+/g) {
+      $_[0].=pack("C",int(255*$&/$pars[3]+.5));
+    }
+  }
+}
diff --git a/scripts/html2psrc b/scripts/html2psrc
new file mode 100755 (executable)
index 0000000..8b619f4
--- /dev/null
@@ -0,0 +1,64 @@
+/* This is a sample configuration file for html2ps. You may try using this
+   to convert the documentation file that comes with html2ps (html2ps.html).
+   Issue the command:
+
+     html2ps -f sample -o html2ps.ps html2ps.html
+
+   This should create a PostScript file html2ps.ps formatted based on the
+   information below.
+*/
+
+@html2ps {
+  header {               /* Running page header */
+    odd-left: $T;          /* Document title */
+    odd-right: $H;         /* Current main heading */
+    even-left: $H;         /* Ditto */
+    even-right: "$[author]"; /* Document author (from <META NAME="Author"...>) */
+  }
+  footer {               /* Running page footer */
+    center: "- $N -";      /* Page number */
+  }
+  option {
+    toc: bh;              /* Generate a table of contents, based on headings */
+    titlepage: 1;         /* Generate a title page */
+ /*   original: 1; */
+  }
+  titlepage {           /* The title page content: document title, author and
+                           creation date */
+    content: "<DIV align=center><H1><BIG>$T</BIG></H1>
+              <H2>$[author]</H2>$[created]";
+  }
+  package {
+    djpeg: 1;
+    Ghostscript: 1;
+  }
+  toc {
+    heading: "TABLE OF CONTENTS";
+  }
+  showurl: 1;             /* Show URL:s for external links */
+  seq-number: 1;          /* Automatic numbering of headings */
+}
+
+/* Standard style sheet definitions */
+P { text-align: justify }
+H1, H2, H3, H4, H5, H6 { font-family: Helvetica; font-weight: bold }
+/* H1 { font-size: 19pt; text-align: center } */
+H1 { font-size: 19pt; }
+H3, H5 { font-style: oblique }
+H2, H3 { font-size: 16pt }
+H4, H5 { font-size: 13pt }
+H6 { font-size: 11pt }
+ADDRESS { text-align: right }
+@page:left {
+  margin-left: 2cm;
+  margin-right: 3cm;
+  type: a4;
+}
+@page:right {
+  margin-left: 3cm;
+  margin-right: 2cm;
+}
+BODY {
+  font-family: Helvetica;
+  text-align: justify
+}
similarity index 75%
rename from doc/makerfc
rename to scripts/makerfc
index a8b3a46e2b6466c35803f7303fada2b804d95b1b..2948e2c7d634d6b28f5420943ebe0b05235c9182 100755 (executable)
@@ -6,4 +6,4 @@
 #
 # Usage: makerfc <input_file> <output_file>
 #
-nroff -ms $1 | ./fix.pl > $2
+nroff -ms $1 | perl ../scripts/fix.pl > $2
diff --git a/scripts/silcdoc/gen.sh b/scripts/silcdoc/gen.sh
new file mode 100755 (executable)
index 0000000..c09c9af
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+cat << EOF > tmp.php
+<?php \$page="$4"; \$dest="$1"; \$type="$3"; require "$2"; ?>
+EOF
+php -f tmp.php >$5.tmp
+mv $5.tmp $5
+rm -f tmp.php
diff --git a/scripts/silcdoc/gen_detail.php b/scripts/silcdoc/gen_detail.php
new file mode 100644 (file)
index 0000000..7b3d129
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/*
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  This is the detailed page generator. This generates the actual data
+  that is shown plus index at the right side. 
+
+*/
+?>
+
+<div align="center">
+<table cellpadding=2 cellspacing=0 border=0 width="99%" align=center>
+<tr>
+<td valign=top>
+<font face="Helvetica,Arial,Sans-serif" size="+1">
+
+<?php
+/* Get the actual data for the page */
+require "$page";
+?>
+
+</font>
+</td>
+
+<td bgcolor="#dddddd">
+<table bgcolor="#dddddd" cellpadding=4 cellspacing=0 border=0 
+width="99%" align=center>
+<tr><td>
+<font face="Helvetica,Arial,Sans-serif" size="1">
+
+<?php
+/* Get the index for this page */
+$len = strcspn($page, "__");
+$fname = substr($page, 0, $len);
+require "$fname"."__index.tmpl";
+?>
+
+</font>
+</td></tr>
+</table>
+</td>
+
+</tr>
+</table>
+</div>
similarity index 51%
rename from lib/silccrypt/e2_internal.h
rename to scripts/silcdoc/gen_html_detail.php
index 446b0a1bf21008142cde87aa587ee7efe9af770c..999fcb16a6d0f30dea96a8aaba250eae3fdbfaa0 100644 (file)
@@ -1,34 +1,39 @@
+<?php
 /*
 
-  e2_internal.h
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
-
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 2001 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
-  
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
-*/
-
-#ifndef E2_INTERNAL_H
-#define E2_INTERNAL_H
+  This is the detailed page generator. This generates the actual data
+  that is shown plus index at the right side. 
 
-typedef struct {
-  u4byte l_key[72];
-} E2Context;
-
-/* Prototypes */
-u4byte *e2_set_key(E2Context *ctx,
-                  const u4byte in_key[], const u4byte key_len);
-void e2_encrypt(E2Context *ctx, const u4byte in_blk[4], u4byte out_blk[4]);
-void e2_decrypt(E2Context *ctx, const u4byte in_blk[4], u4byte out_blk[4]);
-
-#endif
+*/
+?>
+
+<div align="center">
+<table cellpadding=2 cellspacing=0 border=0 width="99%" align=center>
+<tr>
+<td valign=top>
+<font face="Helvetica,Arial,Sans-serif" size="+1">
+
+<?php
+/* Get the actual data for the page */
+require "$page";
+?>
+
+</font>
+</td>
+</tr>
+</table>
+</div>
diff --git a/scripts/silcdoc/gen_index.php b/scripts/silcdoc/gen_index.php
new file mode 100644 (file)
index 0000000..4ee117c
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+/*
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2002 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  type values:
+
+       0 = add left index, details and right index
+       1 = add left index, details, no right index
+       2 = no left index, add details, no right index
+*/
+?>
+
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-a" />
+ <meta http-equiv="Content-Language" content="en" />
+ <meta name="description" content="SILC Secure Internet Live Conferencing" />
+ <meta name="keywords" content="SILC, secure, chat, protocol, cipher, encrypt, SKE" />
+ <meta content="INDEX, FOLLOW" name="ROBOTS" />
+ <style type="text/css">
+  <!--
+  body { color: #000000; background: #f0f0f0; font-family: Helvetica, Arial, Sans-serif; }
+  a:link { text-decoration: none; color: #2f488f; }
+  a:visited { text-decoration: none;color: #2f488f; }
+  a:active { text-decoration: none; color: #2f488f; }
+  -->
+ </style>
+</head>
+
+<body topmargin="0" leftmargin="0" marginheight="0" marginwidth="0">
+
+<table border="0" cellspacing="0" cellpadding="6" width="100%">
+ <tr valign="top" bgcolor="#dddddd">
+  <td><small>Copyright &copy; 2001 - 2002 SILC Project<br />
+    <a href="http://silcnet.org">SILC Project Website</a></small></td>
+  <td align="right"><small>
+   <a href="index.html">SILC Toolkit Reference Manual</a><br />
+   </small></td>
+ </tr>
+</table>
+<table border="0" cellspacing="0" cellpadding="0" width="100%">
+<tr bgcolor="#444444"><td><img src="space.gif" width="1" height="1"border="0" alt="" ></td></tr>
+</table>
+
+<table cellpadding="0" cellspacing="0" border="0">
+ <tr valign="top">
+
+  <td width="200" bgcolor="#f0f0f0">
+   <img src="space.gif" width="1" height="1" border="0" alt="">
+   <table width="100%" cellpadding="2" cellspacing="2" border="0">
+    <tr valign="top"><td>
+<br />
+<small>
+<?php
+if ($type != 2)
+  require "$dest/index.tmpl";
+?>
+</small>
+<br /><br /><br /><br />
+    </td></tr>
+   </table>
+  </td>
+
+  <td bgcolor="#cccccc" background="dot.gif">
+   <img src="space.gif" width="1" height="1" border="0" alt=""></td>
+
+  <td width="720" bgcolor="#ffffff">
+   <img src="space.gif" width="1" height="1" border="0" alt="">
+   <table cellpadding="2" cellspacing="6" width="100%">
+    <tr><td valign="top">
+<br />
+<?php
+require "$page";
+?>
+<br /><br /><br /><br />
+    </td></tr>
+   </table>
+  </td>
+
+  <td bgcolor="#cccccc" background="dot.gif">
+   <img src="space.gif" width="1" height="1" border="0" alt=""></td>
+
+  <td width="180" bgcolor="#f0f0f0">
+    <img src="space.gif" width="1" height="1" border="0" alt="">
+    <table width="100%" cellpadding="4" cellspacing="0">
+    <tr valign="top"><td>
+<br />
+<font face="Helvetica,Arial,Sans-serif" size="1">
+<?php
+if ($type == 0) {
+  /* Get the index for this page */
+  $len = strcspn($page, "__");
+  $fname = substr($page, 0, $len);
+  require "$fname"."__index.tmpl";
+}
+?>
+</font>
+
+<br /><br /><br /><br />
+    </td></tr>
+    </table>
+  </td>
+</tr>
+</table>
+
+<table border="0" cellspacing="0" cellpadding="0" width="100%">
+<tr bgcolor="#444444"><td><img src="space.gif" width="1" height="1"border="0" alt="" ></td></tr>
+</table>
+<table border="0" cellspacing="0" cellpadding="6" width="100%">
+ <tr valign="top" bgcolor="#dddddd">
+  <td><small>Copyright &copy; 2001 - 2002 SILC Project<br />
+    <a href="http://silcnet.org">SILC Project Website</a></small></td>
+  <td align="right"><small>
+   <a href="index.html">SILC Toolkit Reference Manual</a><br />
+   </small></td>
+ </tr>
+</table>
+
+</body>
+</html>
diff --git a/scripts/silcdoc/gen_toc.php b/scripts/silcdoc/gen_toc.php
new file mode 100644 (file)
index 0000000..48e60e3
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+/*
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2001 Pekka Riikonen
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  This generates the TOC for a specific section.
+
+*/
+?>
+
+<div align="center">
+<table cellpadding=2 cellspacing=0 border=0 width="99%" align=center>
+<tr>
+<td>
+<font face="Helvetica,Arial,Sans-serif" size="+1">
+
+<?php
+/* Get the actual data for the page */
+require "$page";
+?>
+
+</font>
+</td>
+</tr>
+</table>
+</div>
diff --git a/scripts/silcdoc/index.php b/scripts/silcdoc/index.php
new file mode 100644 (file)
index 0000000..49a3862
--- /dev/null
@@ -0,0 +1,57 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-a" />
+ <meta http-equiv="Content-Language" content="en" />
+ <meta name="description" content="SILC Secure Internet Live Conferencing" />
+ <meta name="keywords" content="SILC, secure, chat, protocol, cipher, encrypt, SKE" />
+ <meta content="INDEX, FOLLOW" name="ROBOTS" />
+ <style type="text/css">
+  <!--
+  body { color: #000000; background: #bbbbbb; font-family: Helvetica, Arial, Sans-serif; }
+  a:link { text-decoration: none; color: #2f486f; }
+  a:visited { text-decoration: none;color: #2f486f; }
+  a:active { text-decoration: none; color: #2f486f; }
+  -->
+ </style>
+</head>
+
+<body bgcolor="#aaaaaa" text="#000000" link="#2f486f" alink="#2f486f" vlink="#2f486f">
+
+<br />
+<div align="center">
+<table width="90%" bgcolor="#000000" cellpadding="1" cellspacing="0" border="0">
+ <tr>
+  <td>
+    <table width="100%" bgcolor="#ffffff" cellpadding="0" cellspacing="0" border="0">
+     <tr>
+      <td>
+        <table width="100%" bgcolor="#e2e2e2" cellpadding="10" cellspacing="0" border="0">
+        <tr>
+       <td width="20%" valign="top"><font face="Helvetica,Arial,Sans-serif">
+       <table bgcolor="#e2e2e2" cellpadding="1" cellspacing="0" border="0" align="left">
+       <tr><td valign="top"><font size="2"face="Helvetica,Arial,Sans-serif">
+<?php
+require "$dest/index.tmpl";
+?>
+       </td></tr>
+       </table>
+       </td>
+       <td valign="top"><font face="Helvetica,Arial,Sans-serif">
+<?php
+require "$page";
+?>
+           </font>
+          </td>
+         </tr>
+        </table>
+      </td>
+     </tr>
+    </table>
+  </td>
+ </tr>
+</table>
+</font>
+</div>
+
+</body>
+</html>
diff --git a/scripts/silcdoc/silcdoc b/scripts/silcdoc/silcdoc
new file mode 100755 (executable)
index 0000000..41c5664
--- /dev/null
@@ -0,0 +1,151 @@
+#!/bin/sh
+#
+# Author: Pekka Riikonen <priikone@silcnet.org>
+#
+# Copyright (C) GNU GPL 2001 Pekka Riikonen
+#
+# SILC Toolkit Reference Manual documentation script.  This will automatically
+# generate documentation from the source tree.  This will require the 
+# robodoc compiled in util/robodoc and php utility installed in your system.
+#
+# This will tarverse the given directory and all subdirectories for the
+# SILC style header files.  All header files starting with prefix `silc' 
+# will be checked.  For example, silcpkcs.h.
+#
+# Usage: ./sildoc <type> <source directory> <destination directory> <robodoc>
+#
+# The <source directory> is the directory where this starts checking for
+# the headers and will traverse all subdirectories.  The <destination
+# directory> is the directory to where the documentation is generated.
+#
+
+# Arguments checking
+if [ $# -lt "4" ]; then
+  echo "Usage: ./silcdoc <type> <source directory> <destination directory> <robodoc>"
+  echo "Supported types: HTML"
+#  echo "Supported types: HTML, ASCII, LATEX or RTF"
+  exit 1
+fi
+
+TYPE=$1
+SRC=$2
+DST=$3
+ROBO=$4
+
+# Get all headers in the source directory
+headers=`find $SRC -name "silc*.h"`
+
+#
+# ASCII documentation
+#
+#if [ "$TYPE" = "ASCII" ]; then
+#
+#fi
+
+#
+# HTML documentation
+#
+if [ "$TYPE" = "HTML" ]; then
+  rm -rf /tmp/silcdoc.html
+  rm -rf /tmp/silcdoc_html.html
+  mkdir /tmp/silcdoc.html
+  mkdir /tmp/silcdoc_html.html
+  cp $headers /tmp/silcdoc.html
+
+  # Generate index template from the DIRECTORY files. The template for
+  # the generated index template is INDEX.tmpl.
+  files=`find $SRC -name "DIRECTORY"`
+  for i in $files
+  do
+    # Get library name
+    name=`grep "@LIBRARY=" $i |cut -d=  -f2`
+    fname=`grep "@FILENAME=" $i |cut -d=  -f2`
+    links=`grep "@LINK=" $i |cut -d=  -f2 |cut -d:  -f1`
+
+    # Generate links to template file that can be included into various
+    # places on the webpage.
+    echo "<a href="$fname"><img src="box.gif" border="0" alt="">$name</a><br />" >>$DST/index.tmpl
+    for k in $links
+    do
+      n=`grep $k $i |cut -d=  -f2 |cut -d:  -f2`
+      echo "<li><a href="$k">$n</a>" >>$DST/$fname.links
+      echo "&nbsp;&nbsp;&nbsp; <a href="$k"><img src="box2.gif" border="0" alt="">$n</a><br />" >>$DST/index.tmpl
+    done
+  done
+  # Now get the template for the link template, and generate the final index
+  # template file
+  temp=`find $SRC -name "INDEX.tmpl"`
+  sed -e "/@BODY@/ r $DST/index.tmpl" -e s/@BODY@//g $temp >$DST/index.tmpl.tmp
+  mv $DST/index.tmpl.tmp $DST/index.tmpl
+
+  # Copy all HTML files to destination
+  htmlfiles=`find $SRC -name "*.html"`
+  for i in $htmlfiles
+  do
+    cp $i /tmp/silcdoc_html.html
+  done
+  path=`pwd`
+  cd /tmp/silcdoc_html.html
+  htmlfiles=`find . -name "*.html" | cut -d/  -f2`
+  cd $path
+  for i in $htmlfiles
+  do
+    # Generate the details and the layout
+    f="/tmp/silcdoc_html.html/$i"
+    sh gen.sh $DST gen_index.php 1 $f $f
+    cp /tmp/silcdoc_html.html/$i $DST
+echo $f
+  done
+
+  # Generate the actual detailed documentation
+  path=`pwd`
+  cd /tmp/silcdoc.html
+  headers=`find . -name "silc*.h" |cut -d/  -f2 |cut -d.  -f1`
+  cd $path
+  for i in $headers
+  do
+    $ROBO /tmp/silcdoc.html/$i.h $DST/$i.html $TYPE
+
+    # Generate the TOC file
+    sh gen.sh $DST gen_index.php 1 $DST/$i.html $DST/$i.html
+
+    # Generate the details and the layout
+    files=`find $DST -name ""$i"__*.html"`
+    for k in $files
+    do
+      sh gen.sh $DST gen_index.php 0 $k $k
+    done
+
+    rm -f $DST/$i__index.tmpl
+  done
+
+  # Generate the index and TOC files from the DIRECTORY files
+  files=`find $SRC -name "DIRECTORY"`
+  for i in $files
+  do
+    # Get library name
+    name=`grep "@LIBRARY=" $i |cut -d=  -f2`
+    fname=`grep "@FILENAME=" $i |cut -d=  -f2`
+
+    # Generate links for this library
+    sed -e "/@LINKS@/ r $DST/$fname.links" -e s/@LINKS@//g $i >$DST/$fname
+
+    # Generate the TOC file for the library
+    sh gen.sh $DST gen_index.php 1 $DST/$fname $DST/$fname
+
+    # Generate the link for the top index.html for this library
+    echo "<li><a href="$fname">$name</a>" >>$DST/index.html.tmp
+    rm -f $DST/$fname.links
+  done
+
+  # Generate the top index.html file
+  index=`find $SRC -name "LIBINDEX"`
+  version=`grep SILC_VERSION_STRING $SRC/../includes/version_internal.h |cut -d\"  -f2`
+  curdate=`date`
+  sed -e "/@VERSION@/s//$version/" -e "/@DATE@/s//$curdate/" -e "/@BODY@/ r $DST/index.html.tmp" -e s/@BODY@//g $index >$DST/index.html
+  sh gen.sh $DST gen_index.php 2 $DST/index.html $DST/index.html
+
+  rm -rf $DST/index.html.tmp
+  rm -rf /tmp/silcdoc.html
+  rm -rf /tmp/silcdoc_html.html
+fi
diff --git a/util/robodoc/AUTHORS b/util/robodoc/AUTHORS
new file mode 100644 (file)
index 0000000..60db402
--- /dev/null
@@ -0,0 +1,8 @@
+Frans Slothouber <fslothouber@acm.org>
+Jacco van Weert <weertj@xs4all.nl>
+Petteri Kettunen <petterik@iki.fi>   
+Bernd Koesling <KOESSI@CHESSY.aworld.de> 
+Anthon Pang  <apang@mindlink.net>
+Thomas Aglassinger <agi@sbox.tu-graz.ac.at>
+Stefan Kost <kost@imn.htwk-leipzig.de>
+
diff --git a/util/robodoc/COPYING b/util/robodoc/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/util/robodoc/ChangeLog b/util/robodoc/ChangeLog
new file mode 100644 (file)
index 0000000..dbc8165
--- /dev/null
@@ -0,0 +1,65 @@
+$Id$
+
+This file lists the changes to the archive, not the changes to the 
+ROBODoc source code. These are listed in the main header of 
+Source/robodoc.c.
+
+
+Sep 2000 - Frans Slothouber (V3.2.3)
+ o Added a descrip.mms file for compilation under VMS
+ o make install installs additional documentation.
+
+July 2000 - Frans Slothouber (V3.2.2)
+ o Documentation explains how to use ROBODoc when your sources are
+   in several subdirectories.
+ o Documentation explains master index file for LaTeX, how to view
+   the generated documentation, and the new options, NOSOURCE, SINGLEDOC
+   and TITLE.
+ o example makefile includes commands to view the
+   generated documentation.
+ o Updated the man page
+
+June 2000 - Frans Slothouber (v3.2.1)
+  o Improved documentation, better example makefile.
+
+May 2000 - Frans Slothouber (v3.2)
+  o Added autoconf and automake support.
+
+December 1999 - Frans Slothouber (v3.1e)
+  o Moved the C example in Examples to Examples/C
+  o Added an C++ example in Examples/CPP
+  o Added empty headers for C++ in Headers/ 
+  o More documentation.
+
+December 1999 - Frans Slothouber (v3.1d)
+  o Added list of possible item names to the robodoc man page.
+  o Added list of possible header types to the robodoc man page.
+  o Updated manual with information on the generation of the
+    master index file and new header types.
+
+December 1999 - Frans Slothouber (v3.1c)
+  o Added testheader.c for debug purposes.
+  o Split the source code into serveral files.
+  o Fixed numerous typos in the documentation.
+  o Using m4 to create the html documentation (for table of contents ect).
+  o Added cross links between the documentation and examples.
+
+November 1999 - Frans Slothouber (v3.1b)
+  o Added a man page
+  o Cleaned-up html documentation.
+
+August 1999 - Frans Slothouber:
+  o Added GPL licence
+  o Added INSTALL, README, and TODO
+  o Converted the documentation to HTML
+  o Spell-checked all documentation
+
+
+
+
+
+
+
+
+
+
diff --git a/util/robodoc/Docs/example.c b/util/robodoc/Docs/example.c
new file mode 100644 (file)
index 0000000..32b1727
--- /dev/null
@@ -0,0 +1,29 @@
+/****f* Robodoc/RB_Panic [2.0d]
+ * NAME
+ *   RB_Panic -- Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ *   RB_Panic (cause, add_info)
+ *   RB_Panic (char *, char *)
+ * FUNCTION
+ *   Prints an error message.
+ *   Frees all resources used by robodoc.
+ *   Terminates program.
+ * INPUTS
+ *   cause    - pointer to a string which describes the
+ *              cause of the error.
+ *   add_info - pointer to a string with additional information.
+ * SEE ALSO
+ *   RB_Close_The_Shop ()
+ * SOURCE
+ */
+
+  void RB_Panic (char *cause, char *add_info)
+  {
+    printf ("Robodoc: Error, %s\n",cause) ;
+    printf ("         %s\n", add_info) ;
+    printf ("Robodoc: Panic Fatal error, closing down...\n") ;
+    RB_Close_The_Shop () ; /* Free All Resources */
+    exit(100) ;
+  }
+
+/*******/
diff --git a/util/robodoc/Docs/example_makefile b/util/robodoc/Docs/example_makefile
new file mode 100644 (file)
index 0000000..30059ff
--- /dev/null
@@ -0,0 +1,121 @@
+SHELL = /bin/sh
+.SUFFIXES:
+
+ROBODOC=robodoc
+ROBOOPTS=C SORT 
+
+# Your source files.
+#
+SOURCES=analyser.c generator.c items.c util.c \
+  folds.c headers.c links.c robodoc.c \
+  analyser.h generator.h items.h util.h \
+  folds.h headers.h links.h robodoc.h
+
+# The name of your Project
+#
+PROJECT=ROBODoc
+
+# The various documentation files, derived from the source files.
+# HTML
+#
+HTMLDOCS=$(SOURCES:=.html)
+HTMLXREFS=$(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE=$(PROJECT)_html.xrefs
+# LATEX
+#
+LATEXDOCS=$(SOURCES:=.tex)
+LATEXXREFS=$(LATEXDOCS:.tex=.tex.xref)
+LATEXXREFSFILE=$(PROJECT)_tex.xrefs
+# ASCII
+#
+ASCIIDOCS=$(SOURCES:=.txt)
+# RTF
+#
+RTFDOCS=$(SOURCES:=.rtf)
+RTFXREFS=$(RTFDOCS:.rtf=.rtf.xref)
+RTFXREFSFILE=$(PROJECT)_rtf.xrefs
+
+# Some common targets
+xrefall: xrefhtml xreftex xrefrtf
+docall: html tex ascii rtf
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+xtex: $(LATEXXREFSFILE) 
+xrtf: $(RTFXREFSFILE)
+
+# Create the documentation files for the various formats.
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+tex: $(LATEXDOCS) $(PROJECT)_mi.tex
+rtf: $(RTFDOCS)
+ascii: $(ASCIIDOCS)
+
+# master index file, currently works only for html and latex documentation.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $< $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+$(PROJECT)_mi.tex: $(LATEXXREFSFILE)
+       $(ROBODOC) $< $@ INDEX LATEX TITLE "$(PROJECT) API Reference"
+
+# create xrefs file (file with the names of all .xref files).
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) > $@
+$(LATEXXREFSFILE) : $(LATEXXREFS)
+       /bin/ls  $(LATEXXREFS) > $@
+$(RTFXREFSFILE) : $(RTFXREFS)
+       /bin/ls  $(RTFXREFS) > $@
+
+# Rule to create an .xref file from a source file for the various formats.
+%.html.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.tex.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.rtf.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $< $@ HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+
+# Rule to create latex documentation from a source file.
+# We do not include source items, and generate laxtex documents
+# than can be included in a master document.
+%.tex : %
+       $(ROBODOC) $< $@ LATEX $(ROBOOPTS) NOSOURCE SINGLEDOC XREF $(LATEXXREFSFILE)
+
+# Rule to create ascii documentation from a source file.
+%.txt : %
+       $(ROBODOC) $< $@ ASCII 
+
+# Rule to create rtf documentation from a source file.
+%.rtf : %
+       $(ROBODOC) $< $@ RTF $(ROBOOPTS) XREF $(RTFXREFSFILE)
+
+# Use netscape to view the master index file for our project.
+htmlview: html
+       netscape $(PROJECT)_mi.html
+
+# Use the latex programs to generate a .dvi from the master index file
+# for our prokect. View this .dvi file with xdvi
+texview:  tex
+       latex $(PROJECT)_mi
+       makeindex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       xdvi  $(PROJECT)_mi.dvi
+
+# Clean-up the mess we made
+#
+clean:
+       rm -f $(HTMLXREFS) 
+       rm -f $(HTMLDOCS) 
+       rm -f $(LATEXXREFS)
+       rm -f $(LATEXDOCS) 
+       rm -f $(PROJECT)_mi.* *.aux
+       rm -f $(RTFXREFS)
+       rm -f $(RTFDOCS)
+       rm -f $(ASCIIDOCS)
+       rm -f $(HTMLXREFSFILE) 
+       rm -f $(LATEXXREFSFILE) 
+       rm -f $(RTFXREFSFILE)
+
diff --git a/util/robodoc/Docs/general.m4 b/util/robodoc/Docs/general.m4
new file mode 100644 (file)
index 0000000..db7cba7
--- /dev/null
@@ -0,0 +1,14 @@
+m4_changecom(`/--*--', `--*--/')m4_dnl
+m4_define(`www_docstart', `<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML>')m4_dnl
+m4_define(`www_header', `<HEAD><LINK rel=stylesheet href="main.css"><TITLE>$1</TITLE></HEAD>')m4_dnl
+m4_define(`www_link', `<A HREF="$1">$2</A>')m4_dnl
+m4_define(`www_bodystart', `<BODY>')m4_dnl
+m4_define(`www_bodyend', `</BODY>')m4_dnl
+m4_define(`www_docend', `</HTML>')m4_dnl
+m4_define(`www_title', `<H1>$1</H1>')m4_dnl
+m4_define(`www_sectionCounter',0)m4_dnl
+m4_define(`www_subSectionCounter',0)m4_dnl
+m4_define(`www_incrCounter',`m4_define(`$1',m4_incr($1))')m4_dnl
+m4_define(`www_section', `www_incrCounter(`www_sectionCounter') <H2><font color="red">www_sectionCounter  </font><A NAME="$1">$2</A></H2> m4_define(`www_subSectionCounter', 0)')m4_dnl
+m4_define(`www_subSection', `www_incrCounter(`www_subSectionCounter')<H3><font color="red">www_sectionCounter.www_subSectionCounter  </font><A NAME="$1">$2</A></H3>')m4_dnl
+
diff --git a/util/robodoc/Docs/main.css b/util/robodoc/Docs/main.css
new file mode 100644 (file)
index 0000000..65d0043
--- /dev/null
@@ -0,0 +1,106 @@
+BODY { 
+  margin-top: 1em;
+  margin-bottom: 1em;
+  margin-left: 2.5em;
+  margin-right: 2.5em;
+  font-family: sans-serif;
+}
+
+#A:link { 
+#  color: #00FF00; 
+#}
+#A:visited {
+#  color: #003333;  
+#}
+#A:active {
+#  color: #FF00FF;
+#}
+
+p, form {
+  font-family:sans-serif;
+  font-size:12pt; 
+}
+
+#p { text-align: justify; }
+
+B, STRONG, I, EM, CITE, VAR, TT, CODE, KBD, SAMP, IMG
+{ 
+ display: inline;
+}
+
+UL {
+  margin-top: 1em;
+  margin-bottom: 1em;
+  list-style-type: disc;
+  display: block;
+}
+
+LI {
+  margin-bottom: 0.2em;
+}
+
+
+B, STRONG { 
+  font-variant: small-caps;
+  font-weight: bold;
+}
+
+I, CITE, EM, VAR, ADDRESS, BLOCKQUOTE { font-style: italic }
+
+PRE, TT, KBD, SAMP { font-family: monospace }
+
+
+TABLE {
+  color: #000000;
+  background-color: #AAAAAA;
+}
+
+TT {
+  white-space: pre;
+}
+
+CODE {
+  font-family: monospace;
+  font-style:none;
+  white-space: pre;
+}
+
+PRE { 
+  white-space: pre;
+  margin-top: 0.5em; 
+}
+
+ADDRESS { 
+  font-family: monospace;
+  font-size: 12pt; 
+  text-align: left;
+  margin-bottom: 0.5em;
+}
+
+
+
+H2, H3 { 
+  margin-top: 0.5em; 
+}
+
+H1, H2, h3 { font-weight: bold }
+
+H1 { 
+  font-family: sans-serif;
+  font-size:24pt; 
+  text-align:right;
+  margin-right:36px;
+  margin-top: 0.5em; 
+  margin-bottom: 0.5em;
+}
+
+H2 { 
+  font-family:sans-serif;
+  font-size:18pt; 
+}
+
+H3 { 
+  font-family:sans-serif;
+  font-size:14pt;  
+}
+
diff --git a/util/robodoc/Docs/makefile.am b/util/robodoc/Docs/makefile.am
new file mode 100644 (file)
index 0000000..7bed062
--- /dev/null
@@ -0,0 +1,30 @@
+## Process this file with automake to produce Makefile.in
+
+man_MANS = robodoc.1
+MAINTAINERCLEANFILES = robodoc.html stoc.html stoc.m4
+
+docdir = $(prefix)/doc/$(PACKAGE)-$(VERSION)
+doc_DATA = main.css robodoc.html
+
+#
+# End of automake
+#
+
+#
+# Create the documentation in HTML format
+#
+
+M4=/usr/bin/m4
+
+myclean:
+       rm -f *~ stoc.html stoc.m4 robodoc.html
+       rm -f makefile.in makefile
+
+robodoc.html: robodoc.m4 stoc.html
+       $(M4) -P -DVERSION=$(VERSION) $< > $@  
+stoc.html: stoc.m4 tocgen.m4
+       $(M4) -P $< > $@
+stoc.m4: robodoc.m4
+       echo  "m4_include(tocgen.m4)m4_dnl" > $@
+       egrep "(www).*(ection)" $< >> $@
+
diff --git a/util/robodoc/Docs/makefile.in b/util/robodoc/Docs/makefile.in
new file mode 100644 (file)
index 0000000..6c71fb1
--- /dev/null
@@ -0,0 +1,260 @@
+# makefile.in generated automatically by automake 1.4 from makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+CC = @CC@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+man_MANS = robodoc.1
+MAINTAINERCLEANFILES = robodoc.html stoc.html stoc.m4
+
+docdir = $(prefix)/doc/$(PACKAGE)-$(VERSION)
+doc_DATA = main.css robodoc.html
+
+#
+# End of automake
+#
+
+#
+# Create the documentation in HTML format
+#
+
+M4 = /usr/bin/m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../Source/config.h
+CONFIG_CLEAN_FILES = 
+man1dir = $(mandir)/man1
+MANS = $(man_MANS)
+
+NROFF = nroff
+DATA =  $(doc_DATA)
+
+DIST_COMMON =  makefile.am makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+all: all-redirect
+.SUFFIXES:
+$(srcdir)/makefile.in: makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+       cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps Docs/makefile
+
+makefile: $(srcdir)/makefile.in  $(top_builddir)/config.status
+       cd $(top_builddir) \
+         && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+install-man1:
+       $(mkinstalldirs) $(DESTDIR)$(man1dir)
+       @list='$(man1_MANS)'; \
+       l2='$(man_MANS)'; for i in $$l2; do \
+         case "$$i" in \
+           *.1*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+         else file=$$i; fi; \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \
+         $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \
+       done
+
+uninstall-man1:
+       @list='$(man1_MANS)'; \
+       l2='$(man_MANS)'; for i in $$l2; do \
+         case "$$i" in \
+           *.1*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \
+         rm -f $(DESTDIR)$(man1dir)/$$inst; \
+       done
+install-man: $(MANS)
+       @$(NORMAL_INSTALL)
+       $(MAKE) $(AM_MAKEFLAGS) install-man1
+uninstall-man:
+       @$(NORMAL_UNINSTALL)
+       $(MAKE) $(AM_MAKEFLAGS) uninstall-man1
+
+install-docDATA: $(doc_DATA)
+       @$(NORMAL_INSTALL)
+       $(mkinstalldirs) $(DESTDIR)$(docdir)
+       @list='$(doc_DATA)'; for p in $$list; do \
+         if test -f $(srcdir)/$$p; then \
+           echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(docdir)/$$p"; \
+           $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(docdir)/$$p; \
+         else if test -f $$p; then \
+           echo " $(INSTALL_DATA) $$p $(DESTDIR)$(docdir)/$$p"; \
+           $(INSTALL_DATA) $$p $(DESTDIR)$(docdir)/$$p; \
+         fi; fi; \
+       done
+
+uninstall-docDATA:
+       @$(NORMAL_UNINSTALL)
+       list='$(doc_DATA)'; for p in $$list; do \
+         rm -f $(DESTDIR)$(docdir)/$$p; \
+       done
+tags: TAGS
+TAGS:
+
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = Docs
+
+distdir: $(DISTFILES)
+       @for file in $(DISTFILES); do \
+         d=$(srcdir); \
+         if test -d $$d/$$file; then \
+           cp -pr $$d/$$file $(distdir)/$$file; \
+         else \
+           test -f $(distdir)/$$file \
+           || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+           || cp -p $$d/$$file $(distdir)/$$file || :; \
+         fi; \
+       done
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am:
+install-exec: install-exec-am
+
+install-data-am: install-man install-docDATA
+install-data: install-data-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-man uninstall-docDATA
+uninstall: uninstall-am
+all-am: makefile $(MANS) $(DATA)
+all-redirect: all-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+       $(mkinstalldirs)  $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(docdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -rm -f Makefile $(CONFIG_CLEAN_FILES)
+       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+mostlyclean-am:  mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am:  clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am:  distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am:  maintainer-clean-generic distclean-am
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: install-man1 uninstall-man1 install-man uninstall-man \
+uninstall-docDATA install-docDATA tags distdir info-am info dvi-am dvi \
+check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+myclean:
+       rm -f *~ stoc.html stoc.m4 robodoc.html
+       rm -f makefile.in makefile
+
+robodoc.html: robodoc.m4 stoc.html
+       $(M4) -P -DVERSION=$(VERSION) $< > $@  
+stoc.html: stoc.m4 tocgen.m4
+       $(M4) -P $< > $@
+stoc.m4: robodoc.m4
+       echo  "m4_include(tocgen.m4)m4_dnl" > $@
+       egrep "(www).*(ection)" $< >> $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/util/robodoc/Docs/robodoc.1 b/util/robodoc/Docs/robodoc.1
new file mode 100644 (file)
index 0000000..d8814ec
--- /dev/null
@@ -0,0 +1,246 @@
+.de EX          \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+
+.TH ROBODoc 1 "V3.2.1 June 1999"
+
+.SH NAME
+
+ROBODoc - Extract documentation from source code.
+
+.SH SYNOPSIS
+.B robodoc
+.I source-file
+.I documentation-file
+.B [options]
+
+.SH DESCRIPTION
+
+ROBODoc extracts specially formated documentation from the source code.
+It allows you to maintain a program and its documentation in a single
+file.
+
+.SH OPTIONS
+
+.IP ASCII
+Generate documentation in ASCII format.
+
+.IP -c
+Show warranty and copyright statement.
+
+.IP C
+Use ANSI C grammar in source items (experimental, HTML only).
+
+.IP FOLD
+Enable folding if HTML output is selected (experimental).
+
+.IP "GENXREF xreffile"
+Generate a xreffile.
+.IP GUIDE
+Generate documentation in Amiga Guide format.
+
+.IP HTML
+Generate documentation in HTML format.
+
+.IP INDEX
+Create a master index file. In this case call robodoc as
+robodoc <xrefsfile> <master index file> INDEX 
+
+.IP INTERNAL
+Also include headers that are marked internal.
+
+.IP INTERNALONLY
+Only extract the headers that are marked internal (that start with ****i*).
+
+.IP NOSOURCE
+Do not include the source items in the documentation.
+
+.IP LATEX
+Generate documentation in LaTeX format.
+
+.IP RTF
+Generate documentation in RTF format.
+
+.IP SINGLEDOC
+Do not create a document header and footer when creating 
+documentation in LaTeX format.  This allows you to include
+the generated documents into big document or 
+master index file.
+
+.IP SORT
+Sort the headers alphabetically.
+
+.IP "TABSIZE <n>"         
+Convert each tab into n spaces.
+
+.IP TITLE 
+Sets the title that is used for the master index file.
+
+.IP TOC
+Generate a table of contents. Is only useful when you select ASCII as
+output mode. With all other output modes the Table of contents is
+generated anyway.
+
+.IP -v
+Verbose mode, robodoc tells what it is doing.
+
+.IP "XREF <xrefsfile>"
+Use the all xref files listed in the file xrefsfile to make
+cross links between documents.
+
+The following abbreviations are also allowed: -s SORT, -t TOC, -x
+XREF, -g GENXREF, -i INTERNAL, -io INTERNALONLY, -ts TABSIZE.
+
+.SH "ITEM NAMES SUPPORTED"
+
+.IP NAME 
+Item's name followed by --, then a short description.
+.IP COPYRIGHT 
+Who own the copyright.
+
+.IP "SYNOPSIS, USAGE"
+How to use it. 
+
+.IP "FUNCTION, DESCRIPTION, PURPOSE"
+What does it do.
+
+.IP AUTHOR 
+Who wrote it.
+
+.IP "CREATION DATE"
+When did the work start.
+
+.IP "MODIFICATION HISTORY, HISTORY"
+Who has done which changes and when.
+
+.IP "INPUTS, ARGUMENTS, OPTIONS, PARAMETERS, SWITCHES"
+What can we feed into it. 
+
+.IP "OUTPUT, SIDE EFFECTS"
+What output is made.
+
+.IP "RESULT, RETURN VALUE"
+What do we get returned.
+
+.IP "EXAMPLE" 
+A clear example of the items use. 
+
+.IP "NOTES"
+Any annotations. 
+
+.IP "DIAGNOSTICS" 
+Diagnostical output. 
+
+.IP "WARNINGS, ERRORS" 
+Warning & error-messages.
+
+.IP BUGS
+Known bugs. 
+
+.IP "TODO, IDEAS" 
+What to implement next & ideas. 
+
+.IP PORTABILITY
+Where does it come from, where will it work.
+
+.IP "SEE ALSO"
+References to other functions, man pages, other documentation.
+
+.IP "METHODS, NEW METHODS"
+OOP methods.
+
+.IP "ATTRIBUTES, NEW ATTRIBUTES"
+OOP attributes, could also be used for structures.
+
+.IP TAGS
+Tagitem description.
+
+.IP COMMANDS
+Command description.
+
+.IP "DERIVED FROM"
+OOP super class.
+
+.IP "DERIVED BY"
+OOP sub class.
+
+.IP "USES, CHILDREN"
+What modules are used by this one.
+
+.IP "USED BY, PARENTS"
+Which modules use this one.
+
+.IP SOURCE
+Source code inclusion.
+
+.SH "HEADER TYPES SUPPORTED"
+
+.IP h
+Header that describes the project.
+
+
+.IP f
+Header for a function.
+
+.IP s
+Header for a structure.
+
+.IP c
+Header for a class.
+
+.IP m
+Header for a method.
+
+.IP v
+Header for a variable
+
+.IP d
+Header for a constant (from define).
+
+.IP i
+Internal header.
+
+.IP *
+Generic header, for everything else.
+
+.SH EXAMPLES
+
+A simple example, you have one tcl source file and want to extract
+the documentation.
+
+.EX
+robodoc wopr.tcl wopr.tcl.html HTML SORT
+.EE
+
+Assume you have a program divided in two source files, gluify.c and
+gluify.h.  To generate the documentation for this program, complete
+with cross links, you would use:
+
+.EX
+echo "gluify.c.xref" > gluify.xrefs
+echo "gluify.h.xref" >> gluify.xrefs
+robodoc gluify.c gluify.c.html HTML GENXREF gluify.c.xref
+robodoc gluify.h gluify.h.html HTML GENXREF gluify.h.xref
+robodoc gluify.c gluify.c.html HTML XREF gluify.xrefs
+robodoc gluify.h gluify.h.html HTML XREF gluify.xrefs
+.EE
+
+.SH SEE ALSO
+
+The documentation in HTML format that comes with ROBODoc.  Latest
+version can be found on http://www.xs4all.nl/~rfsber/Robo/ or on
+http://freshmeat.net/
+
+
diff --git a/util/robodoc/Docs/robodoc.html b/util/robodoc/Docs/robodoc.html
new file mode 100644 (file)
index 0000000..16a17dd
--- /dev/null
@@ -0,0 +1,1320 @@
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML>
+<HEAD><LINK rel=stylesheet href="main.css"><TITLE>ROBODoc Manual</TITLE></HEAD>
+<BODY>
+<H1>ROBODoc 3.2.3 Manual</H1>
+
+<P><STRONG>Updated July 2000</STRONG></P>
+
+<P>ROBODoc is a documentation tool for C, C++, Java, Assembler, Basic,
+Fortran, LaTeX, Postscript, Tcl/Tk, LISP, Forth, Perl, Shell
+Scripts, Occam, COBOL, HTML and many other languages. Additional
+languages can be supported by a few modifications to the source
+code.</P>
+
+<P>Copyright (C) 1994-2000 Frans Slothouber and Jacco van Weert.</P>
+
+<P>This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.</P>
+
+<P>This program is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.</P>
+
+<P>You should have received a copy of the GNU General Public
+License along with this program; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA</P>
+
+
+
+ <H2><font color="red">1  </font><A NAME="creds">Credits</A></H2> 
+
+<UL>
+
+  <LI>Original program and idea: Jacco van Weert</LI> 
+
+  <LI>Versions 2.0 and up: Frans Slothouber, Petteri Kettunen,
+      Bernd Koesling, Anthon Pang, Thomas Aglassinger,
+      and Stefan Kost, Guillaume Etorre, Simo Muinonen,
+      Petter Reinholdtsen.
+  </LI>
+
+  <LI>Maintainer: Frans Slothouber (fslothouber@acm.org),
+      The Netherlands.</LI>
+
+</UL>
+
+
+ <H2><font color="red">2  </font><A NAME="toc">Table of Contents</A></H2> 
+
+<STRONG><FONT COLOR="red">01</FONT>......... <A HREF="#creds">Credits</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">02</FONT>......... <A HREF="#toc">Table of Contents</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">03</FONT>......... <A HREF="#INTRO">Introduction</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">04</FONT>......... <A HREF="#HSR">Hardware and software requirements</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">05</FONT>......... <A HREF="#LMT">Goals and Limitations</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">06</FONT>......... <A HREF="#HFCWR">How to Format Your Code for use with ROBODoc</A></STRONG><BR> 
+<STRONG><font color="red">06.01</font>.......... <A HREF="#hname">Header Names</A></STRONG><BR>
+<STRONG><font color="red">06.02</font>.......... <A HREF="#htypes">Header Types</A></STRONG><BR>
+<STRONG><font color="red">06.03</font>.......... <A HREF="#bmar">Begin Marker</A></STRONG><BR>
+<STRONG><font color="red">06.04</font>.......... <A HREF="#rmarker">Remark Marker</A></STRONG><BR>
+<STRONG><font color="red">06.05</font>.......... <A HREF="#emar">End Marker</A></STRONG><BR>
+<STRONG><font color="red">06.06</font>.......... <A HREF="#hitem">Header Items</A></STRONG><BR>
+<STRONG><font color="red">06.07</font>.......... <A HREF="#inlimits">Item Name Limitations</A></STRONG><BR>
+<STRONG><font color="red">06.08</font>.......... <A HREF="#SI">Source Item</A></STRONG><BR>
+<STRONG><FONT COLOR="red">07</FONT>......... <A HREF="#CLD">Creating Cross Links</A></STRONG><BR> 
+<STRONG><font color="red">07.01</font>.......... <A HREF="#limits">Limitations</A></STRONG><BR>
+<STRONG><FONT COLOR="red">08</FONT>......... <A HREF="#MAIND">Master Index File</A></STRONG><BR> 
+<STRONG><font color="red">08.01</font>.......... <A HREF="#MIEXM">examples</A></STRONG><BR>
+<STRONG><FONT COLOR="red">09</FONT>......... <A HREF="#makefile">Automation with <TT>make</TT></A></STRONG><BR> 
+<STRONG><FONT COLOR="red">10</FONT>......... <A HREF="#MDSO">What to do if You have Sources in Multiple Directories</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">11</FONT>......... <A HREF="#RDF">The ROBODoc Defaults File</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">12</FONT>......... <A HREF="#UOB">ROBODoc Command Line Options</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">13</FONT>......... <A HREF="#ADV">Adding New Languages</A></STRONG><BR> 
+<STRONG><FONT COLOR="red">14</FONT>......... <A HREF="#SAB">Suggestions and Bugs</A></STRONG><BR> 
+
+
+
+
+ <H2><font color="red">3  </font><A NAME="INTRO">Introduction</A></H2> 
+
+<P>ROBODoc is based on the AutoDocs program written some time ago
+by Commodore.  The idea is to include for every function a
+standard header containing all sorts of information about that
+procedure/function.  An AutoDocs program extracts these headers
+from the source file and puts them in an autodocs file.  This
+allows you to include the program documentation in the source
+code and makes it unnecessary to maintain two documents.</P>
+
+
+<P>ROBODoc is such a program, however ROBODoc has several
+additions.  For one it can generate the documentation in
+different formats, ASCII, HTML, RTF, LaTeX, and AmigaGuide.
+Another feature is that it automatically creates links within the
+document, and to other documents.  It is also possible to include
+parts of the source in you documentation, complete with links.
+For instance it is possible to include the complete source code
+of a function, and have the function names in the source point to
+their documentation. Besides documenting functions, you can also
+document classes, methods, structures, variables, and
+constants.</P>
+
+
+<P>If you never have used AutoDoc or ROBODoc before you might
+take a look at the example in the <TT>Source/</TT>.  Run the
+command:</P>
+
+<PRE>
+  make xhtml
+  make example
+</PRE>
+
+
+<P>This creates the ROBODoc
+<A HREF="../Source/ROBODoc_mi.html">documentation</A> for the
+ROBODoc program itself and then starts netscape to view the
+documentation. Also have a look at the source files, they
+illustrates the use of headers.</P>
+
+<P>ROBODoc can generate documentation in five different
+formats:</P>
+
+<UL>
+  <LI>HTML format complete with hyperlinks and mark-ups.</LI>
+
+  <LI>LaTeX, based on D. Knuth excellent typesetting system.</LI>
+
+  <LI>Plain ASCII text file, this file is very close to what the
+      original AutoDocs program would generate.</LI>
+
+  <LI>RTF, Rich Text Format, mostly used on Windows machines
+      before WWW revolution.</LI>
+
+  <LI>AmigaGuide format, it is the Amiga computer's equivalent
+      HTML. The AmigaGuide program is necessary to view the
+      resulting autodocs-file. (This was the preferred format when the
+      program was written in 1994.)</LI>
+
+</UL>
+
+
+ <H2><font color="red">4  </font><A NAME="HSR">Hardware and software requirements</A></H2> 
+
+<P>ROBODoc was developed in 1994 on a standard Amiga 1200, a
+system with a 20MHz 68020 processor and 2 Mbyte of RAM. It should
+therefore be no problem to run it on any of the currently
+available systems :) The complete source code consists of a
+series of file that can be found in the <TT>Source</TT>
+directory.  It is written according (hopefully) to the ANSI C
+standard and uses no special functions, so it should run on every
+system with an ANSI C-compiler.</P>
+
+
+ <H2><font color="red">5  </font><A NAME="LMT">Goals and Limitations</A></H2> 
+
+<P>ROBODoc is intended for small to medium sized projects 
+that have a relatively flat structure and especially projects 
+that use a mix of different programming languages.</P>
+
+<P>
+ROBODoc was designed to be easy to use and to work with a lot of
+different programming languages.  It has no knowledge of the
+syntax of a programming languages.  It just some knowledge about
+how remarks start and end in some programming languages. This
+means that you sometimes have to do a little more work compared
+to other tools that have detailed knowledge of the syntax of a
+particular language.  They can use that knowledge to figure out
+some of the information automatically.  This usually also means
+that they work only with one or two languages.
+</P>
+
+<P>ROBODoc operates on one file at a time.  It has no mechanism to
+process whole sets of source files. Makefiles should be used for
+this.  How to do this is explained in this document with various
+example makefiles. Have a look at them.
+</P>
+
+<P>ROBODoc can work with projects where the source code is located
+in different subdirectories. However the generated documentation is
+expected to go into one single directory.</P>
+
+
+ <H2><font color="red">6  </font><A NAME="HFCWR">How to Format Your Code for use with ROBODoc</A></H2> 
+
+<P>ROBODoc allows you to mix the program documentation with the
+source code.  It does require though that this documentation has
+a particular layout so ROBODoc can recognize it. The following
+header was taken from the original ROBODoc program (many versions
+back).</P>
+
+<TABLE>
+<TR>
+<TD>
+<PRE>
+           <FONT COLOR="red">------------------------------- Header Name</FONT>
+          <FONT COLOR="red">/                          \</FONT>
+  /****f* financial.library/StealMoney  <FONT COLOR="red">&lt;---- Begin Marker</FONT>
+  *    <FONT COLOR="red">^------- Header Type</FONT>
+  *
+  *  <FONT COLOR="red">&lt;---- Remark Marker</FONT>
+  *  NAME
+  *    StealMoney -- Steal money from the Federal Reserve Bank. (V77)
+  *  SYNOPSIS  <FONT COLOR="red">&lt;---- Item Name</FONT>
+  *    error = StealMoney( userName,amount,destAccount,falseTrail )
+  *    D0,Z                D0       D1.W    A0         [A1]
+  *
+  *    BYTE StealMoney
+  *         ( STRPTR,UWORD,struct AccountSpec *,struct falseTrail *);
+  *  FUNCTION
+  *    Transfer money from the Federal Reserve Bank into the
+  *    specified interest-earning checking account.  No records of
+  *    the transaction will be retained.
+  *  INPUTS
+  *    userName    - name to make the transaction under.  Popular
+  *                  favorites include "Ronald Reagan" and
+  *                  "Mohamar Quadaffi".
+  *    amount      - Number of dollars to transfer (in thousands).
+  *    destAccount - A filled-in AccountSpec structure detailing the
+  *                  destination account (see financial/accounts.h).
+  *                  If NULL, a second Great Depression will be
+  *                  triggered.
+  *    falseTrail  - If the DA_FALSETRAIL bit is set in the
+  *                  destAccount, a falseTrail structure must be
+  *                  provided.
+  *  RESULT
+  *    error - zero for success, else an error code is returned
+  *           (see financial/errors.h).  The Z condition code
+  *           is guaranteed.
+  *  EXAMPLE
+  *    Federal regulations prohibit a demonstration of this function.
+  *  NOTES
+  *    Do not run on Tuesdays!
+  *  BUGS
+  *    Before V88, this function would occasionally print the
+  *    address and home phone number of the caller on local police
+  *    976 terminals.  We are confident that this problem has been
+  *    resolved.
+  *  SEE ALSO
+  *    CreateAccountSpec(),security.device/SCMD_DESTROY_EVIDENCE,
+  *    financial/misc.h
+  *
+  ******  <FONT COLOR="red">&lt;---- End Marker</FONT>
+  *
+  * You can use this space for remarks that should not be included
+  * in the documentation.
+  *
+  */
+</PRE>
+</TD>
+</TR>
+</TABLE>
+
+<P>You would place this headers in front of functions, classes,
+methods, structure definitions, or any of the major components in
+your program.  The header itself contains a number of items that
+provide structured information about the component. </P>
+
+<P>There are a number of special markers in a header (indicated
+in red above).  There are two special markers that mark the begin
+and end of a header.  Each line in a header should start with a
+remark marker.  The starts of each item is marked by an Item Name
+(in all capitals).</P>
+
+
+<H3><font color="red">6.1  </font><A NAME="hname">Header Names</A></H3>
+
+<P>ROBODoc makes some assumptions about the structure a project.
+It assumes that a project consists of a number of modules, and
+that each module consists of a number of components.  These
+components can be anything that you care to document; functions,
+variables, structures, classes, methods etc.</P>
+
+<P> Projects, modules, and components all have names.  The names
+allow ROBODoc to structure the documentation and create
+cross-links. Names are defined in the header name.  It is either
+of the form <TT> &lt;project name&gt;/&lt;module name&gt;</TT>
+for a module header, or of the form <TT>&lt;module
+name&gt;/&lt;component name&gt;</TT> for all other headers.</P>
+
+<H3><font color="red">6.2  </font><A NAME="htypes">Header Types</A></H3>
+
+<P>You can provide ROBODoc with some additional information
+by specifying the header type.  The header type tells ROBODoc
+what kind of component you are documenting. This information
+allows ROBODoc to create more useful index tables.</P>
+
+<P>The type is identified by a single character, as listed in the
+following table</P>
+
+<TABLE>
+<TR><TD>h</TD><TD>Header for a module in a project.</TD></TR>
+<TR><TD>f</TD><TD>Header for a function.</TD></TR>
+<TR><TD>s</TD><TD>Header for a structure.</TD></TR>
+<TR><TD>c</TD><TD>Header for a class.</TD></TR>
+<TR><TD>m</TD><TD>Header for a method.</TD></TR>
+<TR><TD>v</TD><TD>Header for a variable.</TD></TR>
+<TR><TD>d</TD><TD>Header for a constant 
+(from <STRONG>d</STRONG>efine).</TD></TR>
+<TR><TD>*</TD><TD>Generic header for every thing else.</TD></TR>
+<TR><TD>i</TD><TD>Internal header.</TD></TR>
+</TABLE>
+
+<P>Internal headers are special. They can be used to hide certain
+headers. They are only extracted if requested. You could use to
+document internal functions that you do now want clients to
+see.</P>
+
+
+<H3><font color="red">6.3  </font><A NAME="bmar">Begin Marker</A></H3>
+
+<P>The beginning of a header is marked with a special marker.
+The above header is intended for a program in C.  In other
+programming languages the marker looks slightly different, since
+each language has its own convention for starting remarks.
+ROBODoc recognizes the following begin markers:</P>
+
+<TABLE >
+<TR><TD><TT>"/****"</TT>
+    <TD>C, C++</TD>
+</TR>
+<TR><TD><TT>"//****"</TT></TD>
+    <TD>C++</TD>
+</TR>
+<TR><TD><TT>";****"</TT></TD>
+    <TD>Assembler</TD>
+</TR>
+<TR><TD><TT>"****"</TT></TD>
+    <TD>Assembler</TD>
+</TR>
+<TR><TD><TT>"{****"</TT></TD>
+    <TD>Pascal</TD>
+</TR>
+<TR><TD><TT>"REM ****"</TT></TD>
+    <TD>Basic (Rem, rem, or even rEM also works)</TD>
+</TR>
+<TR><TD><TT>"C     ****"</TT></TD>
+    <TD>Fortran (c     **** also works)</TD>
+</TR>
+<TR><TD><TT>"%****"</TT></TD>
+    <TD>LaTeX, TeX, Postscript.</TD>
+</TR>
+<TR><TD><TT>"#****"</TT></TD>
+    <TD>Tcl/Tk, Perl, makefiles, gnuplot etc.</TD>
+</TR>
+<TR><TD><TT>"(****"</TT></TD>
+    <TD>Pascal, Modula-2, LISP</TD>
+</TR>
+<TR><TD><TT>"--****"</TT></TD>
+    <TD>Occam</TD>
+</TR>
+<TR><TD><TT>"&lt;!--****"</TT></TD>
+    <TD>HTML</TD>
+</TR>
+<TR><TD><TT>"&lt;!---****"</TT></TD>
+    <TD>HTML</TD>
+</TR>
+<TR><TD><TT>"|****"</TT></TD>
+    <TD>GNU Assembler</TD>
+</TR>
+<TR><TD><TT>"!!****"</TT></TD>
+    <TD>Fortran 90</TD>
+</TR>
+</TABLE>
+
+<P>After these initial four asterisks, there is the character to
+identify the kind of header, then another asterisks, and then
+header name. After this you can specify a version number
+surrounded by "[]". The version number is stored but not used for
+anything at the moment. All characters after that are
+ignored.</P>
+
+<P>This might sound terribly complicated, it is not. Here are
+some examples:</P>
+
+<P>A header for a module called analyser in a project called ChessMaster
+for C, is has version number 1.0</P>
+<PRE>
+  /****h* ChessMaster/analyser [1.0] *
+</PRE>
+
+<P>In Assembler, a function header, for the function init() in the 
+  module finance.library:</P>
+<PRE>
+  ****f* finance.library/init *
+</PRE>
+
+<P>In C++, a class header for class Puppet, for the module puppetmaster,
+version v2.12</P> 
+<PRE>
+  /****c* puppetMaster/Puppet [v2.12] ******
+</PRE>
+
+<P>For the same class a method called Puppet::Talk</P>
+<PRE>
+  /****m* puppetMaster/Puppet::Talk [v2.12] ******
+</PRE>
+
+<P>A project header, in Fortran</P>
+<PRE>
+  C     ****h* ChessMaster/analyser              C
+</PRE>
+
+<P>In Basic</P>
+<PRE>
+  REM ****h* ChessMaster/analyser
+</PRE>
+
+
+
+<H3><font color="red">6.4  </font><A NAME="rmarker">Remark Marker</A></H3>
+
+<P>Each line in the body of a header should start with a remark
+marker.  This marker is stripped from the line and the remaining
+part is used to generated the documentation.  The following
+markers are recognized by ROBODoc.</P>
+
+<TABLE >
+<TR><TD><TT>"*"</TT></TD>
+    <TD>C, C++, Pascal, Modula-2</TD>
+</TR> 
+<TR><TD><TT>"//"</TT></TD>
+    <TD>C++</TD>
+</TR> 
+<TR><TD><TT>" *"</TT></TD>
+    <TD>C, C++, M68K assembler, Pascal, Modula-2, HTML</TD>
+</TR> 
+<TR><TD><TT>";*"</TT></TD>
+    <TD>M68K assembler</TD>
+</TR> 
+<TR><TD><TT>";"</TT></TD>
+    <TD>M68K assembler</TD>
+</TR> 
+<TR><TD><TT>"C    "</TT></TD>
+    <TD>Fortran</TD>
+</TR> 
+<TR><TD><TT>"REM "</TT></TD>
+    <TD>BASIC</TD>
+</TR> 
+<TR><TD><TT>"%"</TT></TD>
+    <TD>LaTeX, TeX, Postscript</TD>
+</TR> 
+<TR><TD><TT>"#"</TT></TD>
+    <TD>Tcl/Tk, shell scripts, makefiles</TD>
+</TR> 
+<TR><TD><TT>"      *"</TT></TD>
+    <TD>COBOL</TD>
+</TR> 
+<TR><TD><TT>"--"</TT></TD>
+    <TD>Occam</TD>
+</TR> 
+<TR><TD><TT>"|"</TT></TD>
+    <TD>GNU Assembler</TD>
+</TR>
+<TR><TD><TT>"!!"</TT></TD>
+    <TD>Fortan 90</TD>
+</TR>
+</TABLE>
+
+
+
+
+<H3><font color="red">6.5  </font><A NAME="emar">End Marker</A></H3>
+
+<P>A header ends with an end marker.  An end marker is a remark
+marker followed by three asterisks.  ROBODoc recognizes following
+strings as end markers:</P>
+
+<TABLE >
+<TR><TD><TT>"/***"</TT></TD>
+    <TD> C, C++ </TD></TR>
+<TR><TD><TT>"//***"</TT></TD>
+    <TD> C++ </TD></TR>
+<TR><TD><TT>" ****"</TT></TD>
+    <TD> C, C++, Pascal, Modula-2 </TD></TR>
+<TR><TD><TT>"{***"</TT></TD>
+    <TD> Pascal </TD></TR>
+<TR><TD><TT>"(***"</TT></TD>
+    <TD> Pascal, Modula-2, B52 LISP</TD></TR>
+<TR><TD><TT>";***"</TT></TD>
+    <TD> M68K assembler </TD></TR>
+<TR><TD><TT>"****"</TT></TD>
+    <TD> M68K assembler </TD></TR>
+<TR><TD><TT>"C     ***"</TT></TD>
+    <TD> Fortran </TD></TR>
+<TR><TD><TT>"REM ***"</TT></TD>
+    <TD> BASIC </TD></TR>
+<TR><TD><TT>"%***"</TT></TD>
+    <TD> LaTeX, TeX, Postscript </TD></TR>
+<TR><TD><TT>"#***"</TT></TD>
+    <TD> Tcl/Tk, Perl, Makefiles, Shell scripts </TD></TR>
+<TR><TD><TT>"      ****"</TT></TD>
+    <TD> COBOL </TD></TR>
+<TR><TD><TT>"--***"</TT></TD>
+    <TD> Occam </TD></TR>
+<TR><TD><TT>"&lt;!--***"</TT></TD>
+    <TD> HTML </TD></TR>
+<TR><TD><TT>"&lt;!---***"</TT></TD>
+    <TD> HTML </TD></TR>
+<TR><TD><TT>"|***"</TT></TD>
+    <TD>GNU Assembler</TD></TR>
+<TR><TD><TT>"!!***"</TT></TD>
+    <TD>Fortan 90</TD></TR>
+</TABLE>
+
+
+
+
+<H3><font color="red">6.6  </font><A NAME="hitem">Header Items</A></H3>
+
+<P>When ROBODoc has found a header it will try to identify the
+items in this header.  It does this by looking for the item name. The following
+item names are currently supported:</P>
+
+<TABLE >
+<TR><TD> NAME </TD>
+    <TD> Item name plus a short description. </TD> 
+<TR><TD> COPYRIGHT </TD>
+    <TD> Who own the copyright : "(c) &lt;year&gt;-&lt;year&gt; by 
+         &lt;company/person&gt;" </TD>
+<TR><TD> SYNOPSIS, USAGE </TD>
+    <TD> How to use it. </TD>
+<TR><TD> FUNCTION, DESCRIPTION,  PURPOSE </TD>
+    <TD> What does it do. </TD>
+<TR><TD> AUTHOR </TD>
+    <TD>Who wrote it. </TD> 
+<TR><TD> CREATION DATE </TD>
+    <TD> When did the work start. </TD> 
+<TR><TD> MODIFICATION HISTORY,  HISTORY </TD>
+    <TD> Who has done which changes and when. </TD>
+<TR><TD> INPUTS, ARGUMENTS, OPTIONS, PARAMETERS, SWITCHES </TD>
+    <TD> What can we feed into it.  </TD>
+<TR><TD> OUTPUT, SIDE EFFECTS </TD>
+    <TD> What output is made. </TD>
+<TR><TD> RESULT, RETURN VALUE </TD>
+    <TD> What do we get returned. </TD>
+<TR><TD> EXAMPLE  </TD>
+    <TD> A clear example of the items use. </TD> 
+<TR><TD> NOTES </TD>
+    <TD> Any annotations </TD> 
+<TR><TD> DIAGNOSTICS  </TD>
+    <TD>Diagnostical output  </TD>
+<TR><TD> WARNINGS, ERRORS  </TD>
+    <TD> Warning & error-messages. </TD>
+<TR><TD> BUGS </TD>
+    <TD> Known bugs. </TD> 
+<TR><TD> TODO, IDEAS  </TD>
+    <TD> What to implement next & ideas. </TD> 
+<TR><TD> PORTABILITY </TD>
+    <TD> Where does it come from, where will it work. </TD>
+<TR><TD> SEE ALSO </TD>
+    <TD> References to other functions, man pages, other documentation. </TD>
+<TR><TD> METHODS, NEW METHODS </TD>
+    <TD> OOP methods. </TD>
+<TR><TD> ATTRIBUTES, NEW ATTRIBUTES </TD>
+    <TD> OOP attributes  </TD>
+<TR><TD> TAGS </TD>
+    <TD> Tag-item description. </TD>
+<TR><TD> COMMANDS </TD>
+    <TD> Command description. </TD> 
+<TR><TD> DERIVED FROM </TD>            
+    <TD> OOP super class. </TD>
+<TR><TD> DERIVED BY </TD>
+    <TD> OOP sub class. </TD>
+<TR><TD> USES, CHILDREN        </TD>
+    <TD> What modules are used by this one. </TD> 
+<TR><TD> USED BY, PARENTS </TD>
+    <TD> Which modules do use this one. </TD> 
+<TR><TD> SOURCE </TD>
+    <TD> Source code inclusion. </TD> 
+</TABLE>
+
+<P>ROBODoc does this so that it can format each item with a
+different style (colour, font, etc.) if the user want it.  These
+can be specified in the robodoc.defaults file, see the next
+section more information.</P>
+
+
+<H3><font color="red">6.7  </font><A NAME="inlimits">Item Name Limitations</A></H3>
+
+<P>If you happen to have a function which name is in all uppercase,
+this sometimes conflicts with where ROBODoc thinks an item name
+starts and where the item body starts.
+Bernhard Roessmann suggest the following workaround:
+Example header producing this error:</P>
+<PRE>
+/***** basic.c/RETURN
+* NAME
+*  RETURN : Return from subroutine
+* SYNOPSIS
+*  RETURN
+* FUNCTION
+*  Return from subroutine
+******/
+</PRE>
+<P>Here the item name  "FUNCTION" will be interpreted as ordinary text, 
+not as an item name.  Workaround: Add an empty line:</P>
+<PRE>
+/***** basic.c/RETURN
+* NAME
+*  RETURN : Return from subroutine
+* SYNOPSIS
+*  RETURN
+*
+* FUNCTION
+*  Return from subroutine
+******/
+</PRE>
+
+
+
+<H3><font color="red">6.8  </font><A NAME="SI">Source Item</A></H3>
+
+<P>The source item allows you to include part of the source in
+the documentation as is demonstrated by the following
+example.</P>
+
+<TABLE><TR><TD><PRE>
+/****f* Robodoc/RB_Panic [2.0d]
+ * NAME
+ *   RB_Panic -- Shout panic, free resources, and shut down.
+ * SYNOPSIS
+ *   RB_Panic (cause, add_info)
+ *   RB_Panic (char *, char *)
+ * FUNCTION
+ *   Prints an error message.
+ *   Frees all resources used by robodoc.
+ *   Terminates program.
+ * INPUTS
+ *   cause    - pointer to a string which describes the
+ *              cause of the error.
+ *   add_info - pointer to a string with additional information.
+ * SEE ALSO
+ *   RB_Close_The_Shop ()
+ * SOURCE
+ */
+
+  void RB_Panic (char *cause, char *add_info)
+  {
+    printf ("Robodoc: Error, %s\n",cause) ;
+    printf ("         %s\n", add_info) ;
+    printf ("Robodoc: Panic Fatal error, closing down...\n") ;
+    RB_Close_The_Shop () ; /* Free All Resources */
+    exit(100) ;
+  }
+
+/*******/
+
+</PRE></TD></TABLE>
+
+<P>This would create the following documentation</P>
+
+<TABLE><TR><TD>
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>RB_Panic</B> -- Shout panic, free resources, and shut down.
+</EM></PRE><FONT SIZE="+1">SYNOPSIS</FONT>
+<PRE>   <B>RB_Panic</B> (cause, add_info)
+   <B>RB_Panic</B> (char *, char *)
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>   Prints an error message.
+   Frees all resources used by robodoc.
+   Terminates program.
+</PRE><FONT SIZE="+1">INPUTS</FONT>
+<PRE>   cause    - pointer to a string which describes the
+              cause of the error.
+   add_info - pointer to a string with additional information.
+</PRE><FONT SIZE="+1">SEE ALSO</FONT>
+<PRE>   RB_Close_The_Shop ()
+</PRE><FONT SIZE="+1">SOURCE</FONT>
+<PRE>      void <B>RB_Panic</B> (char *cause, char *add_info)
+      {
+        printf ("Robodoc: Error, %s\n",cause) ;
+        printf ("         %s\n", add_info) ;
+        printf ("Robodoc: Panic Fatal error, closing down...\n") ;
+        RB_Close_The_Shop () ; <FONT COLOR = "#FF0000">/* Free All Resources */</FONT>
+        exit(100) ;
+      }    
+</PRE></TD></TR></TABLE>
+
+
+
+
+ <H2><font color="red">7  </font><A NAME="CLD">Creating Cross Links</A></H2> 
+
+<P>Creating hyper links within a document and across documents
+is the most interesting feature of ROBODoc.  A document with such
+links is much more easier to read.  If your source code consists
+of just one file, creating links is easy.  Just tell ROBODoc that
+you want to have the output in HTML or AmigaGuide format, and it
+will automatically generate the links.  That is, at the beginning
+of the document it will create a table of contents that consists
+of links to all your function headers.</P>
+
+<P>ROBODoc will also search the complete text of you
+documentation for reference to function names, and it will create
+a link to the documentation of that function.</P>
+
+<P>In most cases, however, your source code does not consists of
+a single file.  It is also possible, however, to create links to
+other files.  This does require the use of some additional files,
+called xref files. These files can be generated with ROBODoc.
+These files contain information about in which file and where in
+the file references can be found.</P>
+
+<P>Lets assume your project is split up in five different source
+files, and you want to generate links between these five files.
+What you have to do to accomplish this is to create a xref file
+for each of those five files.</P>
+
+<P>With the GENXREF option ROBODoc will generate such a xref file
+from the a source-file.  When you use this option, only the xref
+file is created not the autodocs-file, however you still have to
+specify the name of the autodocs file because this name is needed
+for the creation of the xref file.</P>
+
+<P>When all xref files are created you are ready to create the
+documentation.  To do so you use ROBODOC with the XREF option. It
+needs a parameter which is the name of the file in which the
+names of all xref files are defined.  Notice: this is a file with
+file names, it has to be created it by hand.</P>
+
+<P>An example will make things more clear. In the ROBODoc
+archive, under <TT>examples/C</TT> there are two source files
+<A HREF="../Examples/C/prog1.c">prog1.c</A> and
+<A HREF="../Examples/C/prog2.c">prog2.c</A>.  We can create
+documentation with hyper links from these two files as follows:
+</P>
+
+<P>First create the xref files:</P>
+
+<TABLE>
+<TR>
+<TD>
+<PRE>
+  robodoc prog1.c prog1.c.html GENXREF prog1.c.xref HTML INTERNAL
+  robodoc prog2.c prog2.c.html GENXREF prog2.c.xref HTML INTERNAL
+</PRE>
+</TD>
+</TR>
+</TABLE>
+
+<P>Now there are two xref files: prog1.c.xref and prog2.c.xref.
+Note that ROBODoc did not create any HTML files, just the xref
+files. The name prog1.c.html is needed to create the correct xref
+files.  For prog1.c internal headers were also included. </P>
+
+<P>Now create a file with the xref file names.  This file will
+hold only two lines. You can give it any name, say
+<TT>xref_files</TT>.</P>
+<TABLE>
+<TR>
+<TD>
+<PRE>
+  echo prog1.c.xref &gt;  xref_files 
+  echo prog2.c.xref &gt;&gt; xref_files
+</PRE>
+</TD>
+</TR>
+</TABLE>
+<P>Now generate the final documentation:</P>
+<TABLE>
+<TR>
+<TD>
+<PRE>
+  robodoc prog1.c prog1.c.html XREF xref_files HTML INTERNAL
+  robodoc prog2.c prog2.c.html XREF xref_files HTML INTERNAL
+</PRE>
+</TD>
+</TR>
+</TABLE>
+
+<P>Have a look the the documentation generated:</P>
+<OL>
+  <LI><A HREF="../Examples/C/prog1.c.html">prog1.c.html</A></LI>
+  <LI><A HREF="../Examples/C/prog2.c.html">prog2.c.html</A></LI>
+</OL>
+
+
+
+<H3><font color="red">7.1  </font><A NAME="limits">Limitations</A></H3>
+
+<P> ROBODoc knows very little about the grammar of programming
+languages.  Links are created by looking up words in a table.
+This means that it sometimes creates links where there should be
+none.  For instance if you have a function called usage(); every
+time you use the word usage in any of your documentation a link
+will show up. It also means that sometimes is does not create
+links where you would like it to create a link. Say you include
+the source code of a method using the source item. Your method
+uses other methods of the class. You would like to have links
+pointing to the documentation of these methods each time you use
+one. They will not appear though. Since to ROBODoc stores the
+whole name of a method, ie, <TT>someClass::MethodName</TT>. In
+the method source you will use just <TT>MethodName()</TT>. </P>
+
+
+
+ <H2><font color="red">8  </font><A NAME="MAIND">Master Index File</A></H2> 
+
+<P>If your project consists of many source files you might want
+to create a master index file.</P> 
+
+<P>For HTML output this file contains links to the documentation
+generated from each of your source files as well as a list of all
+"objects" that you documented. All "objects" are listed according
+to header type, using the following order: Projects, Classes,
+Methods, Stuctures, Functions, Variables, Constants, Generic,
+Internal.</P>
+
+<P>For LaTeX output this file is one big document that contains
+the documentation generated from all your source files. It also
+includes a table of contents and an index section.  This index
+lists the page number of the page a function's documentation.
+</P>
+
+<P>This index file is generated based on the information found in
+your xrefs file. That is the file with the names of all your xref
+files. So before you can create the master index file you have to
+create all your xref files.</P>
+
+<P>To generate a master index file use:</P>
+<PRE>
+   robodoc &lt;xrefs file&gt; &lt;master index file&gt; INDEX HTML TITLE "Master Index"
+</PRE>
+<P>or</P>
+<PRE>
+   robodoc &lt;xrefs file&gt; &lt;master index file&gt; INDEX LATEX TITLE "ROBODoc API Documentation"
+</PRE>
+<P>The master index file can currently only be generated in HTML or LaTeX.</P>
+
+<P>If you use if for LaTeX documentation you need to use the option
+<TT>SINGLEDOC</TT> when you generate the documentation from your various
+source files.  This ensures that no document preambles are generated.
+The master index file contains command that includes all your documentation
+files and make it into one single document.</P>
+
+
+<H3><font color="red">8.1  </font><A NAME="MIEXM">examples</A></H3>
+
+<P>Here are some examples of master index files</P>
+<UL>
+
+  <LI><A HREF="../Examples/CPP/masterindex.html">Master index for a C++ project</A> to be found in 
+      <TT>Examples/CPP/</TT></LI>
+
+  <LI><A HREF="../Source/ROBODoc_mi.html">Master index for ROBODoc</A> to be found in 
+      <TT>Source/</TT>. 
+  </LI>
+
+</UL>
+
+
+
+ <H2><font color="red">9  </font><A NAME="makefile">Automation with <TT>make</TT></A></H2> 
+
+<P>The whole process of creating documentation with ROBODoc is of
+course best automated with <TT>make</TT>.
+Have a look at the following makefile.</P> 
+
+<TABLE><TR><TD><PRE>
+SHELL = /bin/sh
+.SUFFIXES:
+
+ROBODOC=robodoc
+ROBOOPTS=C SORT 
+
+# Your source files.
+#
+SOURCES=analyser.c generator.c items.c util.c \
+  folds.c headers.c links.c robodoc.c \
+  analyser.h generator.h items.h util.h \
+  folds.h headers.h links.h robodoc.h
+
+# The name of your Project
+#
+PROJECT=robodoc
+
+# The various documentation files, derived from the source files.
+# HTML
+#
+HTMLDOCS=$(SOURCES:=.html)
+HTMLXREFS=$(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE=$(PROJECT)_html.xrefs
+# LATEX
+#
+LATEXDOCS=$(SOURCES:=.tex)
+LATEXXREFS=$(LATEXDOCS:.tex=.tex.xref)
+LATEXXREFSFILE=$(PROJECT)_tex.xrefs
+# ASCII
+#
+ASCIIDOCS=$(SOURCES:=.txt)
+# RTF
+#
+RTFDOCS=$(SOURCES:=.rtf)
+RTFXREFS=$(RTFDOCS:.rtf=.rtf.xref)
+RTFXREFSFILE=$(PROJECT)_rtf.xrefs
+
+# Some common targets
+xrefall: xrefhtml xreftex xrefrtf
+docall: html tex ascii rtf
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+xtex: $(LATEXXREFSFILE) 
+xrtf: $(RTFXREFSFILE)
+
+# Create the documentation files for the various formats.
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+tex: $(LATEXDOCS) $(PROJECT)_mi.tex
+rtf: $(RTFDOCS)
+ascii: $(ASCIIDOCS)
+
+# master index file, currently works only for html and latex documentation.
+# Note that you can define the title of the document.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $&lt; $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+$(PROJECT)_mi.tex: $(LATEXXREFSFILE)
+       $(ROBODOC) $&lt; $@ INDEX LATEX TITLE "$(PROJECT) API Reference"
+
+# create xrefs file (file with the names of all .xref files).
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) &gt; $@
+$(LATEXXREFSFILE) : $(LATEXXREFS)
+       /bin/ls  $(LATEXXREFS) &gt; $@
+$(RTFXREFSFILE) : $(RTFXREFS)
+       /bin/ls  $(RTFXREFS) &gt; $@
+
+# Rule to create an .xref file from a source file for the various formats.
+%.html.xref : %
+       $(ROBODOC) $&lt; $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.tex.xref : %
+       $(ROBODOC) $&lt; $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.rtf.xref : %
+       $(ROBODOC) $&lt; $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $&lt; $@ HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+
+# Rule to create latex documentation from a source file.
+# We do not include source items, and generate laxtex documents
+# than can be included in a master document.
+%.tex : %
+       $(ROBODOC) $&lt; $@ LATEX $(ROBOOPTS) NOSOURCE SINGLEDOC XREF $(LATEXXREFSFILE)
+
+# Rule to create ascii documentation from a source file.
+%.txt : %
+       $(ROBODOC) $&lt; $@ ASCII 
+
+# Rule to create rtf documentation from a source file.
+%.rtf : %
+       $(ROBODOC) $&lt; $@ RTF $(ROBOOPTS) XREF $(RTFXREFSFILE)
+
+# Use netscape to view the master index file for our project.
+htmlview: html
+       netscape $(PROJECT)_mi.html
+
+# Use the latex programs to generate a .dvi from the master index file
+# for our prokect. View this .dvi file with xdvi
+texview:  tex
+       latex $(PROJECT)_mi
+       makeindex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       xdvi  $(PROJECT)_mi.dvi
+
+# Clean-up the mess we made
+#
+clean:
+       rm -f $(HTMLXREFS) 
+       rm -f $(HTMLDOCS) 
+       rm -f $(LATEXXREFS)
+       rm -f $(LATEXDOCS) 
+       rm -f $(PROJECT)_mi.* *.aux
+       rm -f $(RTFXREFS)
+       rm -f $(RTFDOCS)
+       rm -f $(ASCIIDOCS)
+       rm -f $(HTMLXREFSFILE) 
+       rm -f $(LATEXXREFSFILE) 
+       rm -f $(RTFXREFSFILE)
+</PRE></TD></TR></TABLE>
+
+<P>It includes all the necessary commands to generate and view the documentation for you project. You create documentation in any of the four formats.
+For instance to create documentation in html format use:</P>
+<TABLE><TR><TD><PRE>
+  make xhtml
+  make html
+</PRE></TD></TR></TABLE>
+<P>To make documentation in LaTeX format use:</P>
+<TABLE><TR><TD><PRE>
+  make xtex
+  make tex
+</PRE></TD></TR></TABLE>
+<P>To view your documentation use:</P>
+<TABLE><TR><TD><PRE>
+  make xhtml
+  make texview
+</PRE></TD></TR></TABLE>
+<P>or</P>
+<TABLE><TR><TD><PRE>
+  make xtex
+  make texview
+</PRE></TD></TR></TABLE>
+
+
+<P>To clean up all the documentation files use:</P>
+<PRE>
+  make clean
+</PRE>
+
+<P>To use this make file in project set the variable
+<TT>SOURCE</TT> to the names of your source files and set the
+variable <TT>PROJECT</TT> to the name of your project.</P>
+
+<P>You can find a copy of the above makefile
+<TT>Docs/example_makefile</TT>.  This should get you started in
+no time.</P>
+
+ <H2><font color="red">10  </font><A NAME="MDSO">What to do if You have Sources in Multiple Directories</A></H2> 
+
+<P>It is possible to have your sources in multiple
+subdirectories. However the generated documentation is expected
+to be in one single directory. If not the cross references will
+be wrong, at least in the HTML documentation.</P>
+
+<P>Say you have the following directory structure:</P>
+<TABLE><TR><TD><PRE>
+  Project/
+     Dir1/
+        program1.c 
+     Dir2/
+        program2.c 
+</PRE></TD></TR></TABLE>
+
+<P>You can create the documentation for that as follows (assuming
+you are in Project):
+</P>
+<TABLE><TR><TD><PRE>
+  robodoc Dir1/prog1.c prog1.c.html HTML GENXREF Dir1/prog1.xref 
+  robodoc Dir2/prog2.c prog2.c.html HTML GENXREF Dir2/prog2.xref 
+  echo "Dir1/prog1.xref" &gt; xreffiles 
+  echo "Dir2/prog2.xref" &gt;&gt; xreffiles 
+  robodoc Dir1/prog1.c prog1.c.html HTML XREF xreffiles 
+  robodoc Dir2/prog2.c prog2.c.html HTML XREF xreffiles 
+  robodoc xreffiles master_index.html INDEX HTML 
+</PRE></TD></TR></TABLE>
+<P>
+This generates the following files:
+</P>
+<TABLE><TR><TD><PRE>
+   prog1.c.html
+   prog2.c.html
+   master_index.html
+</PRE></TD></TR></TABLE>
+
+
+<P>With some version of make (for instance the gnu version) you
+can strip the directory part of a filename with $(notdir NAME)
+How this can be used is shown in the following example
+makefile.  Here we have the sources for robodoc, the <TT>.c</TT> files are
+in the directory <TT>Sources/</TT> and <TT>.h</TT> files are in the
+directory <TT>Headers/</TT>.</P>
+
+<TABLE><TR><TD><PRE>
+SHELL = /bin/sh
+.SUFFIXES:
+
+ROBODOC=./robodoc
+ROBOOPTS=C SORT 
+
+# Your source files.
+#
+SOURCES=Sources/analyser.c Sources/generator.c Sources/items.c Sources/util.c \
+  Sources/folds.c Sources/headers.c Sources/links.c Sources/robodoc.c \
+  Headers/analyser.h Headers/generator.h Headers/items.h Headers/util.h \
+  Headers/folds.h Headers/headers.h Headers/links.h Headers/robodoc.h
+
+# The name of your Project
+#
+PROJECT=ROBODoc
+
+# The various documentation files, derived from the source files.
+#
+HTMLDOCS=$(SOURCES:=.html)
+HTMLXREFS=$(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE=$(PROJECT)_html.xrefs
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+
+# Create the documentation 
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+
+# Create master index file.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $&lt; $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+# Create the file with the names of all xref files.
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) &gt; $@
+
+# Rule to create an .xref file from a source file 
+%.html.xref : %
+       $(ROBODOC) $&lt; $(notdir $(@:.xref=)) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $&lt; $(notdir ${@}) HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+</PRE></TD></TR></TABLE>
+
+
+ <H2><font color="red">11  </font><A NAME="RDF">The ROBODoc Defaults File</A></H2> 
+
+<P>The robodoc.default file can be used to change the appearance
+of the documentation. For each item type you can define how the
+corresponding text should be rendered.  Each line in the default
+file consists of two parts, the item type and the item
+attributes. For instance</P>
+
+<PRE>
+AUTHOR                    LARGE ITALICS BOLD UNDERLINE
+</PRE>
+
+<P>Specifies that the AUTHOR item has the attributes LARGE,
+ITALICS, BOLD, and UNDERLINE.  The effect of each attribute is
+listed in the following table.</P>
+
+<TABLE>
+<TR><TD>Item Attributes</TD> 
+    <TD>Effect in HTML</TD>
+</TR>
+<TR><TD>LARGE</TD>
+    <TD>&lt;FONT SIZE=5&gt;,&lt;/FONT&gt;</TD>
+</TR>
+<TR><TD>SMALL</TD>
+    <TD>&lt;FONT SIZE=-1&gt;,&lt;/FONT&gt;</TD>
+</TR>
+<TR><TD>ITALICS</TD>
+    <TD>&lt;I&gt;,&lt;/I&gt;</TD>
+</TR>
+<TR><TD>BOLD</TD>
+    <TD>&lt;B&gt;,&lt;/B&gt;</TD>
+</TR>
+<TR><TD>UNDERLINE</TD>
+    <TD>&lt;U&gt;,&lt;/U&gt;</TD>
+</TR>
+<TR><TD>HIGHLIGHT</TD>
+    <TD>&lt;EM&gt;,&lt;/EM&gt;</TD>
+</TR>
+</TABLE>
+
+
+ <H2><font color="red">12  </font><A NAME="UOB">ROBODoc Command Line Options</A></H2> 
+
+<P>When using ROBODoc you should provide at least two
+parameters</P>
+
+<PRE>
+  robodoc &lt;source file&gt; &lt;documentation file&gt; [options]
+</PRE>
+
+<P>Here sourcefile is the file with the program source from which
+the documentation is to be extracted. The documentation file is
+the file that will contain the extracted documentation.  </P>
+
+<P>In case you are creating a master index file you have to
+specify three parameters</P> 
+<PRE>
+  robodoc &lt;xrefs file&gt; &lt;master index file&gt; INDEX [options]
+</PRE>
+
+
+<P>In addition to this you can specify one or more of the
+following options:</P>
+
+<TABLE >
+  <TR><TD><TT>ASCII</TT></TD>
+      <TD>Generate documentation in ASCII format (default)</TD>
+  </TR>
+  <TR><TD><TT>GUIDE</TT></TD>
+      <TD>Generate documentation in AmigaGuide format.</TD>
+  </TR>
+  <TR><TD><TT>HTML</TT></TD>
+      <TD>Generate documentation in HTML format.</TD>
+  </TR>
+  <TR><TD><TT>LATEX</TT></TD>
+      <TD>Generate documentation in LaTeX format. (Experimental)</TD>
+  </TR>
+  <TR><TD><TT>RTF</TT></TD>
+      <TD>Generate documentation in RTF format.</TD>
+  </TR>
+  <TR><TD><TT>C</TT></TD>
+      <TD>Use ANSI C grammar in source items (test, HTML only)</TD>
+  </TR>
+  <TR><TD><TT>FOLD</TT></TD>
+      <TD>Enable folding. (Experimental)</TD>
+  </TR>
+  <TR><TD><TT>GENXREF &lt;xref file&gt;</TT></TD>
+      <TD>Generate a xref file, which can be used to create
+         <A HREF="#CLD">cross links</A> between documents.</TD>
+  </TR>
+  <TR><TD><TT>XREF &lt;xrefs file&gt;</TT></TD>
+      <TD>Use a set of xref files to create references (links) to other
+      documents or within the document. <TT>&lt;xrefs file&gt;</TT>
+      is a file with xref file names.</TD>
+  </TR>
+  <TR><TD><TT>INDEX</TT></TD>
+     <TD>Create a <A HREF="#MAIND">master index file</A>.</TD>
+  </TR>
+  <TR><TD><TT>INTERNAL</TT></TD>
+     <TD>Also include headers that are marked internal.</TD>
+  </TR>
+  <TR><TD><TT>INTERNALONLY</TT></TD>
+      <TD>Only extract the headers marked internal.</TD>
+  </TR>
+  <TR><TD><TT>NOSOURCE</TT></TD>
+      <TD>Do not include the source items in the documentation.</TD>
+  </TR>
+  <TR><TD><TT>SORT</TT></TD>
+      <TD>Sort the headers alphabetically.</TD>
+  </TR>
+  <TR><TD><TT>SINGLEDOC</TT></TD>
+      <TD>Do not create a document header and footer when creating 
+          documentation in LaTeX format.  This allows you to include
+          the generated documents into big document or 
+          <A HREF="#MAIND">master index file</A>.</TD>
+  </TR>
+  <TR><TD><TT>TITLE &lt;title&gt;</TT></TD>
+      <TD>Sets the title that is used for the 
+         <A HREF="#MAIND">master index file</A>.</TD>
+  </TR>
+  <TR><TD><TT>TOC</TT></TD>
+      <TD>Generate a table of contents.  It is only useful when you select
+      ASCII as output mode.  With all other output modes the
+      table of contents is generated anyway.</TD>
+  </TR>
+  <TR><TD><TT>TABSIZE &lt;n&gt;</TT></TD>
+      <TD>Convert each tab into <TT>n</TT> spaces.</TD>
+  </TR>
+  <TR><TD><TT>-v</TT></TD>
+      <TD>Verbose option, ROBODoc will tell you what it is doing.</TD>
+  </TR>
+</TABLE>
+
+<P>If you wonder why all the odd ALL CAPS flags are used instead
+of for instance "-x"; this was how it was done on the Amiga.</P>
+
+<P>The following abbreviations are also allowed:</P>
+<TABLE >
+<TR><TD><TT>-s </TT></TD><TD><TT>SORT</TT></TD></TR>
+<TR><TD><TT>-t </TT></TD><TD><TT>TOC</TT></TD></TR>
+<TR><TD><TT>-x </TT></TD><TD><TT>XREF</TT></TD></TR>
+<TR><TD><TT>-g </TT></TD><TD><TT>GENXREF</TT></TD></TR>
+<TR><TD><TT>-i </TT></TD><TD><TT>INTERNAL</TT></TD></TR>
+<TR><TD><TT>-io</TT></TD><TD><TT>INTERNALONLY</TT></TD></TR>
+<TR><TD><TT>-ts</TT></TD><TD><TT>TABSIZE</TT></TD></TR>
+</TABLE>
+
+
+ <H2><font color="red">13  </font><A NAME="ADV">Adding New Languages</A></H2> 
+
+<P>To add a new programming language to ROBODoc you have to edit
+<TT>headers.c</TT>.  Here you find three variables:
+<TT>header_markers</TT>, <TT>remark_markers</TT>, and
+<TT>end_markers</TT>.  There are all arrays, and you have to add
+an new entry to each of these three arrays.</P>
+
+<P>Say your programming language uses the following type of remarks:</P>
+<PRE>
+   $%% This is a remark with some text       
+       and some more and some more  %%$
+</PRE>
+
+<P>That is is starts with three spaces and then <TT>$%%</TT>, and
+has to end with <TT>%%$</TT>. Then you would add to <TT>header_markers</TT>
+</P>
+<PRE>
+   "   $%%****",
+</PRE>
+<P>To <TT>remark_markers</TT> you would add</P>
+<PRE>
+   "   *",
+</PRE>
+<P>And to <TT>end_markers</TT> you would add</P>
+<PRE>
+   "   $%%***",
+</PRE>
+<P>You can then use the following kind of headers in your program:</P>
+<PRE>
+   $%%****f* Test/afunction ***** 
+   * NAME  
+   *   afunction
+   * FUNCTION
+   *   A test.
+   * SOURCE
+   *%%$
+     afunction(test,test) [
+       print hello world ;
+     ]
+   $%%***%%$
+</PRE>
+
+
+
+
+
+ <H2><font color="red">14  </font><A NAME="SAB">Suggestions and Bugs</A></H2> 
+
+<P>If you find any bugs, catch them, put them in a jar, and send
+them to:</P> 
+<ADDRESS>fslothouber@acm.org</ADDRESS>  
+<P>Suggestions are also welcome at this address.  Flames can be
+directed to the sun.</P>
+
+</BODY>
+</HTML>
+
diff --git a/util/robodoc/Docs/robodoc.m4 b/util/robodoc/Docs/robodoc.m4
new file mode 100644 (file)
index 0000000..b60b52a
--- /dev/null
@@ -0,0 +1,1269 @@
+m4_include(`general.m4')m4_dnl
+www_docstart()
+www_header(`ROBODoc Manual')
+www_bodystart
+www_title(`ROBODoc VERSION Manual')
+
+<P><STRONG>Updated July 2000</STRONG></P>
+
+<P>ROBODoc is a documentation tool for C, C++, Java, Assembler, Basic,
+Fortran, LaTeX, Postscript, Tcl/Tk, LISP, Forth, Perl, Shell
+Scripts, Occam, COBOL, HTML and many other languages. Additional
+languages can be supported by a few modifications to the source
+code.</P>
+
+<P>Copyright (C) 1994-2000 Frans Slothouber and Jacco van Weert.</P>
+
+<P>This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of
+the License, or (at your option) any later version.</P>
+
+<P>This program is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.</P>
+
+<P>You should have received a copy of the GNU General Public
+License along with this program; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA</P>
+
+
+
+www_section(`creds', `Credits')
+
+<UL>
+
+  <LI>Original program and idea: Jacco van Weert</LI> 
+
+  <LI>Versions 2.0 and up: Frans Slothouber, Petteri Kettunen,
+      Bernd Koesling, Anthon Pang, Thomas Aglassinger,
+      and Stefan Kost, Guillaume Etorre, Simo Muinonen,
+      Petter Reinholdtsen.
+  </LI>
+
+  <LI>Maintainer: Frans Slothouber (fslothouber@acm.org),
+      The Netherlands.</LI>
+
+</UL>
+
+
+www_section(`toc', `Table of Contents')
+
+m4_include(stoc.html)
+
+
+
+www_section(`INTRO', `Introduction')
+
+<P>ROBODoc is based on the AutoDocs program written some time ago
+by Commodore.  The idea is to include for every function a
+standard header containing all sorts of information about that
+procedure/function.  An AutoDocs program extracts these headers
+from the source file and puts them in an autodocs file.  This
+allows you to include the program documentation in the source
+code and makes it unnecessary to maintain two documents.</P>
+
+
+<P>ROBODoc is such a program, however ROBODoc has several
+additions.  For one it can generate the documentation in
+different formats, ASCII, HTML, RTF, LaTeX, and AmigaGuide.
+Another feature is that it automatically creates links within the
+document, and to other documents.  It is also possible to include
+parts of the source in you documentation, complete with links.
+For instance it is possible to include the complete source code
+of a function, and have the function names in the source point to
+their documentation. Besides documenting functions, you can also
+document classes, methods, structures, variables, and
+constants.</P>
+
+
+<P>If you never have used AutoDoc or ROBODoc before you might
+take a look at the example in the <TT>Source/</TT>.  Run the
+command:</P>
+
+<PRE>
+  make xhtml
+  make example
+</PRE>
+
+
+<P>This creates the ROBODoc
+www_link(`../Source/ROBODoc_mi.html', `documentation') for the
+ROBODoc program itself and then starts netscape to view the
+documentation. Also have a look at the source files, they
+illustrates the use of headers.</P>
+
+<P>ROBODoc can generate documentation in five different
+formats:</P>
+
+<UL>
+  <LI>HTML format complete with hyperlinks and mark-ups.</LI>
+
+  <LI>LaTeX, based on D. Knuth excellent typesetting system.</LI>
+
+  <LI>Plain ASCII text file, this file is very close to what the
+      original AutoDocs program would generate.</LI>
+
+  <LI>RTF, Rich Text Format, mostly used on Windows machines
+      before WWW revolution.</LI>
+
+  <LI>AmigaGuide format, it is the Amiga computer's equivalent
+      HTML. The AmigaGuide program is necessary to view the
+      resulting autodocs-file. (This was the preferred format when the
+      program was written in 1994.)</LI>
+
+</UL>
+
+
+www_section(`HSR', `Hardware and software requirements')
+
+<P>ROBODoc was developed in 1994 on a standard Amiga 1200, a
+system with a 20MHz 68020 processor and 2 Mbyte of RAM. It should
+therefore be no problem to run it on any of the currently
+available systems :) The complete source code consists of a
+series of file that can be found in the <TT>Source</TT>
+directory.  It is written according (hopefully) to the ANSI C
+standard and uses no special functions, so it should run on every
+system with an ANSI C-compiler.</P>
+
+
+www_section(`LMT', `Goals and Limitations')
+
+<P>ROBODoc is intended for small to medium sized projects 
+that have a relatively flat structure and especially projects 
+that use a mix of different programming languages.</P>
+
+<P>
+ROBODoc was designed to be easy to use and to work with a lot of
+different programming languages.  It has no knowledge of the
+syntax of a programming languages.  It just some knowledge about
+how remarks start and end in some programming languages. This
+means that you sometimes have to do a little more work compared
+to other tools that have detailed knowledge of the syntax of a
+particular language.  They can use that knowledge to figure out
+some of the information automatically.  This usually also means
+that they work only with one or two languages.
+</P>
+
+<P>ROBODoc operates on one file at a time.  It has no mechanism to
+process whole sets of source files. Makefiles should be used for
+this.  How to do this is explained in this document with various
+example makefiles. Have a look at them.
+</P>
+
+<P>ROBODoc can work with projects where the source code is located
+in different subdirectories. However the generated documentation is
+expected to go into one single directory.</P>
+
+
+www_section(`HFCWR', `How to Format Your Code for use with ROBODoc')
+
+<P>ROBODoc allows you to mix the program documentation with the
+source code.  It does require though that this documentation has
+a particular layout so ROBODoc can recognize it. The following
+header was taken from the original ROBODoc program (many versions
+back).</P>
+
+<TABLE>
+<TR>
+<TD>
+<PRE>
+           <FONT COLOR="red">------------------------------- Header Name</FONT>
+          <FONT COLOR="red">/                          \</FONT>
+  /****f* financial.library/StealMoney  <FONT COLOR="red">&lt;---- Begin Marker</FONT>
+  *    <FONT COLOR="red">^------- Header Type</FONT>
+  *
+  *  <FONT COLOR="red">&lt;---- Remark Marker</FONT>
+  *  NAME
+  *    StealMoney -- Steal money from the Federal Reserve Bank. (V77)
+  *  SYNOPSIS  <FONT COLOR="red">&lt;---- Item Name</FONT>
+  *    error = StealMoney( userName,amount,destAccount,falseTrail )
+  *    D0,Z                D0       D1.W    A0         [A1]
+  *
+  *    BYTE StealMoney
+  *         ( STRPTR,UWORD,struct AccountSpec *,struct falseTrail *);
+  *  FUNCTION
+  *    Transfer money from the Federal Reserve Bank into the
+  *    specified interest-earning checking account.  No records of
+  *    the transaction will be retained.
+  *  INPUTS
+  *    userName    - name to make the transaction under.  Popular
+  *                  favorites include "Ronald Reagan" and
+  *                  "Mohamar Quadaffi".
+  *    amount      - Number of dollars to transfer (in thousands).
+  *    destAccount - A filled-in AccountSpec structure detailing the
+  *                  destination account (see financial/accounts.h).
+  *                  If NULL, a second Great Depression will be
+  *                  triggered.
+  *    falseTrail  - If the DA_FALSETRAIL bit is set in the
+  *                  destAccount, a falseTrail structure must be
+  *                  provided.
+  *  RESULT
+  *    error - zero for success, else an error code is returned
+  *           (see financial/errors.h).  The Z condition code
+  *           is guaranteed.
+  *  EXAMPLE
+  *    Federal regulations prohibit a demonstration of this function.
+  *  NOTES
+  *    Do not run on Tuesdays!
+  *  BUGS
+  *    Before V88, this function would occasionally print the
+  *    address and home phone number of the caller on local police
+  *    976 terminals.  We are confident that this problem has been
+  *    resolved.
+  *  SEE ALSO
+  *    CreateAccountSpec(),security.device/SCMD_DESTROY_EVIDENCE,
+  *    financial/misc.h
+  *
+  ******  <FONT COLOR="red">&lt;---- End Marker</FONT>
+  *
+  * You can use this space for remarks that should not be included
+  * in the documentation.
+  *
+  */
+</PRE>
+</TD>
+</TR>
+</TABLE>
+
+<P>You would place this headers in front of functions, classes,
+methods, structure definitions, or any of the major components in
+your program.  The header itself contains a number of items that
+provide structured information about the component. </P>
+
+<P>There are a number of special markers in a header (indicated
+in red above).  There are two special markers that mark the begin
+and end of a header.  Each line in a header should start with a
+remark marker.  The starts of each item is marked by an Item Name
+(in all capitals).</P>
+
+
+www_subSection(`hname', `Header Names')
+
+<P>ROBODoc makes some assumptions about the structure a project.
+It assumes that a project consists of a number of modules, and
+that each module consists of a number of components.  These
+components can be anything that you care to document; functions,
+variables, structures, classes, methods etc.</P>
+
+<P> Projects, modules, and components all have names.  The names
+allow ROBODoc to structure the documentation and create
+cross-links. Names are defined in the header name.  It is either
+of the form <TT> &lt;project name&gt;/&lt;module name&gt;</TT>
+for a module header, or of the form <TT>&lt;module
+name&gt;/&lt;component name&gt;</TT> for all other headers.</P>
+
+www_subSection(`htypes', `Header Types')
+
+<P>You can provide ROBODoc with some additional information
+by specifying the header type.  The header type tells ROBODoc
+what kind of component you are documenting. This information
+allows ROBODoc to create more useful index tables.</P>
+
+<P>The type is identified by a single character, as listed in the
+following table</P>
+
+<TABLE>
+<TR><TD>h</TD><TD>Header for a module in a project.</TD></TR>
+<TR><TD>f</TD><TD>Header for a function.</TD></TR>
+<TR><TD>s</TD><TD>Header for a structure.</TD></TR>
+<TR><TD>c</TD><TD>Header for a class.</TD></TR>
+<TR><TD>m</TD><TD>Header for a method.</TD></TR>
+<TR><TD>v</TD><TD>Header for a variable.</TD></TR>
+<TR><TD>d</TD><TD>Header for a constant 
+(from <STRONG>d</STRONG>efine).</TD></TR>
+<TR><TD>*</TD><TD>Generic header for every thing else.</TD></TR>
+<TR><TD>i</TD><TD>Internal header.</TD></TR>
+</TABLE>
+
+<P>Internal headers are special. They can be used to hide certain
+headers. They are only extracted if requested. You could use to
+document internal functions that you do now want clients to
+see.</P>
+
+
+www_subSection(`bmar', `Begin Marker')
+
+<P>The beginning of a header is marked with a special marker.
+The above header is intended for a program in C.  In other
+programming languages the marker looks slightly different, since
+each language has its own convention for starting remarks.
+ROBODoc recognizes the following begin markers:</P>
+
+<TABLE >
+<TR><TD><TT>"/****"</TT>
+    <TD>C, C++</TD>
+</TR>
+<TR><TD><TT>"//****"</TT></TD>
+    <TD>C++</TD>
+</TR>
+<TR><TD><TT>";****"</TT></TD>
+    <TD>Assembler</TD>
+</TR>
+<TR><TD><TT>"****"</TT></TD>
+    <TD>Assembler</TD>
+</TR>
+<TR><TD><TT>"{****"</TT></TD>
+    <TD>Pascal</TD>
+</TR>
+<TR><TD><TT>"REM ****"</TT></TD>
+    <TD>Basic (Rem, rem, or even rEM also works)</TD>
+</TR>
+<TR><TD><TT>"C     ****"</TT></TD>
+    <TD>Fortran (c     **** also works)</TD>
+</TR>
+<TR><TD><TT>"%****"</TT></TD>
+    <TD>LaTeX, TeX, Postscript.</TD>
+</TR>
+<TR><TD><TT>"#****"</TT></TD>
+    <TD>Tcl/Tk, Perl, makefiles, gnuplot etc.</TD>
+</TR>
+<TR><TD><TT>"(****"</TT></TD>
+    <TD>Pascal, Modula-2, LISP</TD>
+</TR>
+<TR><TD><TT>"--****"</TT></TD>
+    <TD>Occam</TD>
+</TR>
+<TR><TD><TT>"&lt;!--****"</TT></TD>
+    <TD>HTML</TD>
+</TR>
+<TR><TD><TT>"&lt;!---****"</TT></TD>
+    <TD>HTML</TD>
+</TR>
+<TR><TD><TT>"|****"</TT></TD>
+    <TD>GNU Assembler</TD>
+</TR>
+<TR><TD><TT>"!!****"</TT></TD>
+    <TD>Fortran 90</TD>
+</TR>
+</TABLE>
+
+<P>After these initial four asterisks, there is the character to
+identify the kind of header, then another asterisks, and then
+header name. After this you can specify a version number
+surrounded by "[]". The version number is stored but not used for
+anything at the moment. All characters after that are
+ignored.</P>
+
+<P>This might sound terribly complicated, it is not. Here are
+some examples:</P>
+
+<P>A header for a module called analyser in a project called ChessMaster
+for C, is has version number 1.0</P>
+<PRE>
+  /****h* ChessMaster/analyser [1.0] *
+</PRE>
+
+<P>In Assembler, a function header, for the function init() in the 
+  module finance.library:</P>
+<PRE>
+  ****f* finance.library/init *
+</PRE>
+
+<P>In C++, a class header for class Puppet, for the module puppetmaster,
+version v2.12</P> 
+<PRE>
+  /****c* puppetMaster/Puppet [v2.12] ******
+</PRE>
+
+<P>For the same class a method called Puppet::Talk</P>
+<PRE>
+  /****m* puppetMaster/Puppet::Talk [v2.12] ******
+</PRE>
+
+<P>A project header, in Fortran</P>
+<PRE>
+  C     ****h* ChessMaster/analyser              C
+</PRE>
+
+<P>In Basic</P>
+<PRE>
+  REM ****h* ChessMaster/analyser
+</PRE>
+
+
+
+www_subSection(`rmarker', `Remark Marker')
+
+<P>Each line in the body of a header should start with a remark
+marker.  This marker is stripped from the line and the remaining
+part is used to generated the documentation.  The following
+markers are recognized by ROBODoc.</P>
+
+<TABLE >
+<TR><TD><TT>"*"</TT></TD>
+    <TD>C, C++, Pascal, Modula-2</TD>
+</TR> 
+<TR><TD><TT>"//"</TT></TD>
+    <TD>C++</TD>
+</TR> 
+<TR><TD><TT>" *"</TT></TD>
+    <TD>C, C++, M68K assembler, Pascal, Modula-2, HTML</TD>
+</TR> 
+<TR><TD><TT>";*"</TT></TD>
+    <TD>M68K assembler</TD>
+</TR> 
+<TR><TD><TT>";"</TT></TD>
+    <TD>M68K assembler</TD>
+</TR> 
+<TR><TD><TT>"C    "</TT></TD>
+    <TD>Fortran</TD>
+</TR> 
+<TR><TD><TT>"REM "</TT></TD>
+    <TD>BASIC</TD>
+</TR> 
+<TR><TD><TT>"%"</TT></TD>
+    <TD>LaTeX, TeX, Postscript</TD>
+</TR> 
+<TR><TD><TT>"#"</TT></TD>
+    <TD>Tcl/Tk, shell scripts, makefiles</TD>
+</TR> 
+<TR><TD><TT>"      *"</TT></TD>
+    <TD>COBOL</TD>
+</TR> 
+<TR><TD><TT>"--"</TT></TD>
+    <TD>Occam</TD>
+</TR> 
+<TR><TD><TT>"|"</TT></TD>
+    <TD>GNU Assembler</TD>
+</TR>
+<TR><TD><TT>"!!"</TT></TD>
+    <TD>Fortan 90</TD>
+</TR>
+</TABLE>
+
+
+
+
+www_subSection(`emar', `End Marker')
+
+<P>A header ends with an end marker.  An end marker is a remark
+marker followed by three asterisks.  ROBODoc recognizes following
+strings as end markers:</P>
+
+<TABLE >
+<TR><TD><TT>"/***"</TT></TD>
+    <TD> C, C++ </TD></TR>
+<TR><TD><TT>"//***"</TT></TD>
+    <TD> C++ </TD></TR>
+<TR><TD><TT>" ****"</TT></TD>
+    <TD> C, C++, Pascal, Modula-2 </TD></TR>
+<TR><TD><TT>"{***"</TT></TD>
+    <TD> Pascal </TD></TR>
+<TR><TD><TT>"(***"</TT></TD>
+    <TD> Pascal, Modula-2, B52 LISP</TD></TR>
+<TR><TD><TT>";***"</TT></TD>
+    <TD> M68K assembler </TD></TR>
+<TR><TD><TT>"****"</TT></TD>
+    <TD> M68K assembler </TD></TR>
+<TR><TD><TT>"C     ***"</TT></TD>
+    <TD> Fortran </TD></TR>
+<TR><TD><TT>"REM ***"</TT></TD>
+    <TD> BASIC </TD></TR>
+<TR><TD><TT>"%***"</TT></TD>
+    <TD> LaTeX, TeX, Postscript </TD></TR>
+<TR><TD><TT>"#***"</TT></TD>
+    <TD> Tcl/Tk, Perl, Makefiles, Shell scripts </TD></TR>
+<TR><TD><TT>"      ****"</TT></TD>
+    <TD> COBOL </TD></TR>
+<TR><TD><TT>"--***"</TT></TD>
+    <TD> Occam </TD></TR>
+<TR><TD><TT>"&lt;!--***"</TT></TD>
+    <TD> HTML </TD></TR>
+<TR><TD><TT>"&lt;!---***"</TT></TD>
+    <TD> HTML </TD></TR>
+<TR><TD><TT>"|***"</TT></TD>
+    <TD>GNU Assembler</TD></TR>
+<TR><TD><TT>"!!***"</TT></TD>
+    <TD>Fortan 90</TD></TR>
+</TABLE>
+
+
+
+
+www_subSection(`hitem', `Header Items')
+
+<P>When ROBODoc has found a header it will try to identify the
+items in this header.  It does this by looking for the item name. The following
+item names are currently supported:</P>
+
+<TABLE >
+<TR><TD> NAME </TD>
+    <TD> Item name plus a short description. </TD> 
+<TR><TD> COPYRIGHT </TD>
+    <TD> Who own the copyright : "(c) &lt;year&gt;-&lt;year&gt; by 
+         &lt;company/person&gt;" </TD>
+<TR><TD> SYNOPSIS, USAGE </TD>
+    <TD> How to use it. </TD>
+<TR><TD> FUNCTION, DESCRIPTION,  PURPOSE </TD>
+    <TD> What does it do. </TD>
+<TR><TD> AUTHOR </TD>
+    <TD>Who wrote it. </TD> 
+<TR><TD> CREATION DATE </TD>
+    <TD> When did the work start. </TD> 
+<TR><TD> MODIFICATION HISTORY,  HISTORY </TD>
+    <TD> Who has done which changes and when. </TD>
+<TR><TD> INPUTS, ARGUMENTS, OPTIONS, PARAMETERS, SWITCHES </TD>
+    <TD> What can we feed into it.  </TD>
+<TR><TD> OUTPUT, SIDE EFFECTS </TD>
+    <TD> What output is made. </TD>
+<TR><TD> RESULT, RETURN VALUE </TD>
+    <TD> What do we get returned. </TD>
+<TR><TD> EXAMPLE  </TD>
+    <TD> A clear example of the items use. </TD> 
+<TR><TD> NOTES </TD>
+    <TD> Any annotations </TD> 
+<TR><TD> DIAGNOSTICS  </TD>
+    <TD>Diagnostical output  </TD>
+<TR><TD> WARNINGS, ERRORS  </TD>
+    <TD> Warning & error-messages. </TD>
+<TR><TD> BUGS </TD>
+    <TD> Known bugs. </TD> 
+<TR><TD> TODO, IDEAS  </TD>
+    <TD> What to implement next & ideas. </TD> 
+<TR><TD> PORTABILITY </TD>
+    <TD> Where does it come from, where will it work. </TD>
+<TR><TD> SEE ALSO </TD>
+    <TD> References to other functions, man pages, other documentation. </TD>
+<TR><TD> METHODS, NEW METHODS </TD>
+    <TD> OOP methods. </TD>
+<TR><TD> ATTRIBUTES, NEW ATTRIBUTES </TD>
+    <TD> OOP attributes  </TD>
+<TR><TD> TAGS </TD>
+    <TD> Tag-item description. </TD>
+<TR><TD> COMMANDS </TD>
+    <TD> Command description. </TD> 
+<TR><TD> DERIVED FROM </TD>            
+    <TD> OOP super class. </TD>
+<TR><TD> DERIVED BY </TD>
+    <TD> OOP sub class. </TD>
+<TR><TD> USES, CHILDREN        </TD>
+    <TD> What modules are used by this one. </TD> 
+<TR><TD> USED BY, PARENTS </TD>
+    <TD> Which modules do use this one. </TD> 
+<TR><TD> SOURCE </TD>
+    <TD> Source code inclusion. </TD> 
+</TABLE>
+
+<P>ROBODoc does this so that it can format each item with a
+different style (colour, font, etc.) if the user want it.  These
+can be specified in the robodoc.defaults file, see the next
+section more information.</P>
+
+
+www_subSection(`inlimits', `Item Name Limitations')
+
+<P>If you happen to have a function which name is in all uppercase,
+this sometimes conflicts with where ROBODoc thinks an item name
+starts and where the item body starts.
+Bernhard Roessmann suggest the following workaround:
+Example header producing this error:</P>
+<PRE>
+/***** basic.c/RETURN
+* NAME
+*  RETURN : Return from subroutine
+* SYNOPSIS
+*  RETURN
+* FUNCTION
+*  Return from subroutine
+******/
+</PRE>
+<P>Here the item name  "FUNCTION" will be interpreted as ordinary text, 
+not as an item name.  Workaround: Add an empty line:</P>
+<PRE>
+/***** basic.c/RETURN
+* NAME
+*  RETURN : Return from subroutine
+* SYNOPSIS
+*  RETURN
+*
+* FUNCTION
+*  Return from subroutine
+******/
+</PRE>
+
+
+
+www_subSection(`SI', `Source Item')
+
+<P>The source item allows you to include part of the source in
+the documentation as is demonstrated by the following
+example.</P>
+
+<TABLE><TR><TD><PRE>
+m4_include(`example.c')
+</PRE></TD></TABLE>
+
+<P>This would create the following documentation</P>
+
+<TABLE><TR><TD>
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>RB_Panic</B> -- Shout panic, free resources, and shut down.
+</EM></PRE><FONT SIZE="+1">SYNOPSIS</FONT>
+<PRE>   <B>RB_Panic</B> (cause, add_info)
+   <B>RB_Panic</B> (char *, char *)
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>   Prints an error message.
+   Frees all resources used by robodoc.
+   Terminates program.
+</PRE><FONT SIZE="+1">INPUTS</FONT>
+<PRE>   cause    - pointer to a string which describes the
+              cause of the error.
+   add_info - pointer to a string with additional information.
+</PRE><FONT SIZE="+1">SEE ALSO</FONT>
+<PRE>   RB_Close_The_Shop ()
+</PRE><FONT SIZE="+1">SOURCE</FONT>
+<PRE>      void <B>RB_Panic</B> (char *cause, char *add_info)
+      {
+        printf ("Robodoc: Error, %s\n",cause) ;
+        printf ("         %s\n", add_info) ;
+        printf ("Robodoc: Panic Fatal error, closing down...\n") ;
+        RB_Close_The_Shop () ; <FONT COLOR = "#FF0000">/* Free All Resources */</FONT>
+        exit(100) ;
+      }    
+</PRE></TD></TR></TABLE>
+
+
+
+
+www_section(`CLD', `Creating Cross Links')
+
+<P>Creating hyper links within a document and across documents
+is the most interesting feature of ROBODoc.  A document with such
+links is much more easier to read.  If your source code consists
+of just one file, creating links is easy.  Just tell ROBODoc that
+you want to have the output in HTML or AmigaGuide format, and it
+will automatically generate the links.  That is, at the beginning
+of the document it will create a table of contents that consists
+of links to all your function headers.</P>
+
+<P>ROBODoc will also search the complete text of you
+documentation for reference to function names, and it will create
+a link to the documentation of that function.</P>
+
+<P>In most cases, however, your source code does not consists of
+a single file.  It is also possible, however, to create links to
+other files.  This does require the use of some additional files,
+called xref files. These files can be generated with ROBODoc.
+These files contain information about in which file and where in
+the file references can be found.</P>
+
+<P>Lets assume your project is split up in five different source
+files, and you want to generate links between these five files.
+What you have to do to accomplish this is to create a xref file
+for each of those five files.</P>
+
+<P>With the GENXREF option ROBODoc will generate such a xref file
+from the a source-file.  When you use this option, only the xref
+file is created not the autodocs-file, however you still have to
+specify the name of the autodocs file because this name is needed
+for the creation of the xref file.</P>
+
+<P>When all xref files are created you are ready to create the
+documentation.  To do so you use ROBODOC with the XREF option. It
+needs a parameter which is the name of the file in which the
+names of all xref files are defined.  Notice: this is a file with
+file names, it has to be created it by hand.</P>
+
+<P>An example will make things more clear. In the ROBODoc
+archive, under <TT>examples/C</TT> there are two source files
+www_link(`../Examples/C/prog1.c', `prog1.c') and
+www_link(`../Examples/C/prog2.c', `prog2.c').  We can create
+documentation with hyper links from these two files as follows:
+</P>
+
+<P>First create the xref files:</P>
+
+<TABLE>
+<TR>
+<TD>
+<PRE>
+  robodoc prog1.c prog1.c.html GENXREF prog1.c.xref HTML INTERNAL
+  robodoc prog2.c prog2.c.html GENXREF prog2.c.xref HTML INTERNAL
+</PRE>
+</TD>
+</TR>
+</TABLE>
+
+<P>Now there are two xref files: prog1.c.xref and prog2.c.xref.
+Note that ROBODoc did not create any HTML files, just the xref
+files. The name prog1.c.html is needed to create the correct xref
+files.  For prog1.c internal headers were also included. </P>
+
+<P>Now create a file with the xref file names.  This file will
+hold only two lines. You can give it any name, say
+<TT>xref_files</TT>.</P>
+<TABLE>
+<TR>
+<TD>
+<PRE>
+  echo prog1.c.xref &gt;  xref_files 
+  echo prog2.c.xref &gt;&gt; xref_files
+</PRE>
+</TD>
+</TR>
+</TABLE>
+<P>Now generate the final documentation:</P>
+<TABLE>
+<TR>
+<TD>
+<PRE>
+  robodoc prog1.c prog1.c.html XREF xref_files HTML INTERNAL
+  robodoc prog2.c prog2.c.html XREF xref_files HTML INTERNAL
+</PRE>
+</TD>
+</TR>
+</TABLE>
+
+<P>Have a look the the documentation generated:</P>
+<OL>
+  <LI>www_link(`../Examples/C/prog1.c.html',  `prog1.c.html')</LI>
+  <LI>www_link(`../Examples/C/prog2.c.html',  `prog2.c.html')</LI>
+</OL>
+
+
+
+www_subSection(`limits', `Limitations')
+
+<P> ROBODoc knows very little about the grammar of programming
+languages.  Links are created by looking up words in a table.
+This means that it sometimes creates links where there should be
+none.  For instance if you have a function called usage(); every
+time you use the word usage in any of your documentation a link
+will show up. It also means that sometimes is does not create
+links where you would like it to create a link. Say you include
+the source code of a method using the source item. Your method
+uses other methods of the class. You would like to have links
+pointing to the documentation of these methods each time you use
+one. They will not appear though. Since to ROBODoc stores the
+whole name of a method, ie, <TT>someClass::MethodName</TT>. In
+the method source you will use just <TT>MethodName()</TT>. </P>
+
+
+
+www_section(`MAIND', `Master Index File')
+
+<P>If your project consists of many source files you might want
+to create a master index file.</P> 
+
+<P>For HTML output this file contains links to the documentation
+generated from each of your source files as well as a list of all
+"objects" that you documented. All "objects" are listed according
+to header type, using the following order: Projects, Classes,
+Methods, Stuctures, Functions, Variables, Constants, Generic,
+Internal.</P>
+
+<P>For LaTeX output this file is one big document that contains
+the documentation generated from all your source files. It also
+includes a table of contents and an index section.  This index
+lists the page number of the page a function's documentation.
+</P>
+
+<P>This index file is generated based on the information found in
+your xrefs file. That is the file with the names of all your xref
+files. So before you can create the master index file you have to
+create all your xref files.</P>
+
+<P>To generate a master index file use:</P>
+<PRE>
+   robodoc &lt;xrefs file&gt; &lt;master index file&gt; INDEX HTML TITLE "Master Index"
+</PRE>
+<P>or</P>
+<PRE>
+   robodoc &lt;xrefs file&gt; &lt;master index file&gt; INDEX LATEX TITLE "ROBODoc API Documentation"
+</PRE>
+<P>The master index file can currently only be generated in HTML or LaTeX.</P>
+
+<P>If you use if for LaTeX documentation you need to use the option
+<TT>SINGLEDOC</TT> when you generate the documentation from your various
+source files.  This ensures that no document preambles are generated.
+The master index file contains command that includes all your documentation
+files and make it into one single document.</P>
+
+
+www_subSection(`MIEXM', `examples')
+
+<P>Here are some examples of master index files</P>
+<UL>
+
+  <LI>www_link(`../Examples/CPP/masterindex.html', 
+      `Master index for a C++ project') to be found in 
+      <TT>Examples/CPP/</TT></LI>
+
+  <LI>www_link(`../Source/ROBODoc_mi.html', 
+      `Master index for ROBODoc') to be found in 
+      <TT>Source/</TT>. 
+  </LI>
+
+</UL>
+
+
+
+www_section(`makefile', `Automation with <TT>make</TT>')
+
+<P>The whole process of creating documentation with ROBODoc is of
+course best automated with <TT>make</TT>.
+Have a look at the following makefile.</P> 
+
+<TABLE><TR><TD><PRE>
+SHELL = /bin/sh
+.SUFFIXES:
+
+ROBODOC=robodoc
+ROBOOPTS=C SORT 
+
+# Your source files.
+#
+SOURCES=analyser.c generator.c items.c util.c \
+  folds.c headers.c links.c robodoc.c \
+  analyser.h generator.h items.h util.h \
+  folds.h headers.h links.h robodoc.h
+
+# The name of your Project
+#
+PROJECT=robodoc
+
+# The various documentation files, derived from the source files.
+# HTML
+#
+HTMLDOCS=$(SOURCES:=.html)
+HTMLXREFS=$(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE=$(PROJECT)_html.xrefs
+# LATEX
+#
+LATEXDOCS=$(SOURCES:=.tex)
+LATEXXREFS=$(LATEXDOCS:.tex=.tex.xref)
+LATEXXREFSFILE=$(PROJECT)_tex.xrefs
+# ASCII
+#
+ASCIIDOCS=$(SOURCES:=.txt)
+# RTF
+#
+RTFDOCS=$(SOURCES:=.rtf)
+RTFXREFS=$(RTFDOCS:.rtf=.rtf.xref)
+RTFXREFSFILE=$(PROJECT)_rtf.xrefs
+
+# Some common targets
+xrefall: xrefhtml xreftex xrefrtf
+docall: html tex ascii rtf
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+xtex: $(LATEXXREFSFILE) 
+xrtf: $(RTFXREFSFILE)
+
+# Create the documentation files for the various formats.
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+tex: $(LATEXDOCS) $(PROJECT)_mi.tex
+rtf: $(RTFDOCS)
+ascii: $(ASCIIDOCS)
+
+# master index file, currently works only for html and latex documentation.
+# Note that you can define the title of the document.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $&lt; $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+$(PROJECT)_mi.tex: $(LATEXXREFSFILE)
+       $(ROBODOC) $&lt; $@ INDEX LATEX TITLE "$(PROJECT) API Reference"
+
+# create xrefs file (file with the names of all .xref files).
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) &gt; $@
+$(LATEXXREFSFILE) : $(LATEXXREFS)
+       /bin/ls  $(LATEXXREFS) &gt; $@
+$(RTFXREFSFILE) : $(RTFXREFS)
+       /bin/ls  $(RTFXREFS) &gt; $@
+
+# Rule to create an .xref file from a source file for the various formats.
+%.html.xref : %
+       $(ROBODOC) $&lt; $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.tex.xref : %
+       $(ROBODOC) $&lt; $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.rtf.xref : %
+       $(ROBODOC) $&lt; $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $&lt; $@ HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+
+# Rule to create latex documentation from a source file.
+# We do not include source items, and generate laxtex documents
+# than can be included in a master document.
+%.tex : %
+       $(ROBODOC) $&lt; $@ LATEX $(ROBOOPTS) NOSOURCE SINGLEDOC XREF $(LATEXXREFSFILE)
+
+# Rule to create ascii documentation from a source file.
+%.txt : %
+       $(ROBODOC) $&lt; $@ ASCII 
+
+# Rule to create rtf documentation from a source file.
+%.rtf : %
+       $(ROBODOC) $&lt; $@ RTF $(ROBOOPTS) XREF $(RTFXREFSFILE)
+
+# Use netscape to view the master index file for our project.
+htmlview: html
+       netscape $(PROJECT)_mi.html
+
+# Use the latex programs to generate a .dvi from the master index file
+# for our prokect. View this .dvi file with xdvi
+texview:  tex
+       latex $(PROJECT)_mi
+       makeindex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       xdvi  $(PROJECT)_mi.dvi
+
+# Clean-up the mess we made
+#
+clean:
+       rm -f $(HTMLXREFS) 
+       rm -f $(HTMLDOCS) 
+       rm -f $(LATEXXREFS)
+       rm -f $(LATEXDOCS) 
+       rm -f $(PROJECT)_mi.* *.aux
+       rm -f $(RTFXREFS)
+       rm -f $(RTFDOCS)
+       rm -f $(ASCIIDOCS)
+       rm -f $(HTMLXREFSFILE) 
+       rm -f $(LATEXXREFSFILE) 
+       rm -f $(RTFXREFSFILE)
+</PRE></TD></TR></TABLE>
+
+<P>It includes all the necessary commands to generate and view the documentation for you project. You create documentation in any of the four formats.
+For instance to create documentation in html format use:</P>
+<TABLE><TR><TD><PRE>
+  make xhtml
+  make html
+</PRE></TD></TR></TABLE>
+<P>To make documentation in LaTeX format use:</P>
+<TABLE><TR><TD><PRE>
+  make xtex
+  make tex
+</PRE></TD></TR></TABLE>
+<P>To view your documentation use:</P>
+<TABLE><TR><TD><PRE>
+  make xhtml
+  make texview
+</PRE></TD></TR></TABLE>
+<P>or</P>
+<TABLE><TR><TD><PRE>
+  make xtex
+  make texview
+</PRE></TD></TR></TABLE>
+
+
+<P>To clean up all the documentation files use:</P>
+<PRE>
+  make clean
+</PRE>
+
+<P>To use this make file in project set the variable
+<TT>SOURCE</TT> to the names of your source files and set the
+variable <TT>PROJECT</TT> to the name of your project.</P>
+
+<P>You can find a copy of the above makefile
+<TT>Docs/example_makefile</TT>.  This should get you started in
+no time.</P>
+
+www_section(`MDSO', `What to do if You have Sources in Multiple Directories')
+
+<P>It is possible to have your sources in multiple
+subdirectories. However the generated documentation is expected
+to be in one single directory. If not the cross references will
+be wrong, at least in the HTML documentation.</P>
+
+<P>Say you have the following directory structure:</P>
+<TABLE><TR><TD><PRE>
+  Project/
+     Dir1/
+        program1.c 
+     Dir2/
+        program2.c 
+</PRE></TD></TR></TABLE>
+
+<P>You can create the documentation for that as follows (assuming
+you are in Project):
+</P>
+<TABLE><TR><TD><PRE>
+  robodoc Dir1/prog1.c prog1.c.html HTML GENXREF Dir1/prog1.xref 
+  robodoc Dir2/prog2.c prog2.c.html HTML GENXREF Dir2/prog2.xref 
+  echo "Dir1/prog1.xref" &gt; xreffiles 
+  echo "Dir2/prog2.xref" &gt;&gt; xreffiles 
+  robodoc Dir1/prog1.c prog1.c.html HTML XREF xreffiles 
+  robodoc Dir2/prog2.c prog2.c.html HTML XREF xreffiles 
+  robodoc xreffiles master_index.html INDEX HTML 
+</PRE></TD></TR></TABLE>
+<P>
+This generates the following files:
+</P>
+<TABLE><TR><TD><PRE>
+   prog1.c.html
+   prog2.c.html
+   master_index.html
+</PRE></TD></TR></TABLE>
+
+
+<P>With some version of make (for instance the gnu version) you
+can strip the directory part of a filename with $(notdir NAME)
+How this can be used is shown in the following example
+makefile.  Here we have the sources for robodoc, the <TT>.c</TT> files are
+in the directory <TT>Sources/</TT> and <TT>.h</TT> files are in the
+directory <TT>Headers/</TT>.</P>
+
+<TABLE><TR><TD><PRE>
+SHELL = /bin/sh
+.SUFFIXES:
+
+ROBODOC=./robodoc
+ROBOOPTS=C SORT 
+
+# Your source files.
+#
+SOURCES=Sources/analyser.c Sources/generator.c Sources/items.c Sources/util.c \
+  Sources/folds.c Sources/headers.c Sources/links.c Sources/robodoc.c \
+  Headers/analyser.h Headers/generator.h Headers/items.h Headers/util.h \
+  Headers/folds.h Headers/headers.h Headers/links.h Headers/robodoc.h
+
+# The name of your Project
+#
+PROJECT=ROBODoc
+
+# The various documentation files, derived from the source files.
+#
+HTMLDOCS=$(SOURCES:=.html)
+HTMLXREFS=$(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE=$(PROJECT)_html.xrefs
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+
+# Create the documentation 
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+
+# Create master index file.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $&lt; $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+# Create the file with the names of all xref files.
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) &gt; $@
+
+# Rule to create an .xref file from a source file 
+%.html.xref : %
+       $(ROBODOC) $&lt; $(notdir $(@:.xref=)) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $&lt; $(notdir ${@}) HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+</PRE></TD></TR></TABLE>
+
+
+www_section(`RDF', `The ROBODoc Defaults File')
+
+<P>The robodoc.default file can be used to change the appearance
+of the documentation. For each item type you can define how the
+corresponding text should be rendered.  Each line in the default
+file consists of two parts, the item type and the item
+attributes. For instance</P>
+
+<PRE>
+AUTHOR                    LARGE ITALICS BOLD UNDERLINE
+</PRE>
+
+<P>Specifies that the AUTHOR item has the attributes LARGE,
+ITALICS, BOLD, and UNDERLINE.  The effect of each attribute is
+listed in the following table.</P>
+
+<TABLE>
+<TR><TD>Item Attributes</TD> 
+    <TD>Effect in HTML</TD>
+</TR>
+<TR><TD>LARGE</TD>
+    <TD>&lt;FONT SIZE=5&gt;,&lt;/FONT&gt;</TD>
+</TR>
+<TR><TD>SMALL</TD>
+    <TD>&lt;FONT SIZE=-1&gt;,&lt;/FONT&gt;</TD>
+</TR>
+<TR><TD>ITALICS</TD>
+    <TD>&lt;I&gt;,&lt;/I&gt;</TD>
+</TR>
+<TR><TD>BOLD</TD>
+    <TD>&lt;B&gt;,&lt;/B&gt;</TD>
+</TR>
+<TR><TD>UNDERLINE</TD>
+    <TD>&lt;U&gt;,&lt;/U&gt;</TD>
+</TR>
+<TR><TD>HIGHLIGHT</TD>
+    <TD>&lt;EM&gt;,&lt;/EM&gt;</TD>
+</TR>
+</TABLE>
+
+
+www_section(`UOB', `ROBODoc Command Line Options')
+
+<P>When using ROBODoc you should provide at least two
+parameters</P>
+
+<PRE>
+  robodoc &lt;source file&gt; &lt;documentation file&gt; [options]
+</PRE>
+
+<P>Here sourcefile is the file with the program source from which
+the documentation is to be extracted. The documentation file is
+the file that will contain the extracted documentation.  </P>
+
+<P>In case you are creating a master index file you have to
+specify three parameters</P> 
+<PRE>
+  robodoc &lt;xrefs file&gt; &lt;master index file&gt; INDEX [options]
+</PRE>
+
+
+<P>In addition to this you can specify one or more of the
+following options:</P>
+
+<TABLE >
+  <TR><TD><TT>ASCII</TT></TD>
+      <TD>Generate documentation in ASCII format (default)</TD>
+  </TR>
+  <TR><TD><TT>GUIDE</TT></TD>
+      <TD>Generate documentation in AmigaGuide format.</TD>
+  </TR>
+  <TR><TD><TT>HTML</TT></TD>
+      <TD>Generate documentation in HTML format.</TD>
+  </TR>
+  <TR><TD><TT>LATEX</TT></TD>
+      <TD>Generate documentation in LaTeX format. (Experimental)</TD>
+  </TR>
+  <TR><TD><TT>RTF</TT></TD>
+      <TD>Generate documentation in RTF format.</TD>
+  </TR>
+  <TR><TD><TT>C</TT></TD>
+      <TD>Use ANSI C grammar in source items (test, HTML only)</TD>
+  </TR>
+  <TR><TD><TT>FOLD</TT></TD>
+      <TD>Enable folding. (Experimental)</TD>
+  </TR>
+  <TR><TD><TT>GENXREF &lt;xref file&gt;</TT></TD>
+      <TD>Generate a xref file, which can be used to create
+         www_link(`#CLD', `cross links') between documents.</TD>
+  </TR>
+  <TR><TD><TT>XREF &lt;xrefs file&gt;</TT></TD>
+      <TD>Use a set of xref files to create references (links) to other
+      documents or within the document. <TT>&lt;xrefs file&gt;</TT>
+      is a file with xref file names.</TD>
+  </TR>
+  <TR><TD><TT>INDEX</TT></TD>
+     <TD>Create a www_link(`#MAIND', `master index file').</TD>
+  </TR>
+  <TR><TD><TT>INTERNAL</TT></TD>
+     <TD>Also include headers that are marked internal.</TD>
+  </TR>
+  <TR><TD><TT>INTERNALONLY</TT></TD>
+      <TD>Only extract the headers marked internal.</TD>
+  </TR>
+  <TR><TD><TT>NOSOURCE</TT></TD>
+      <TD>Do not include the source items in the documentation.</TD>
+  </TR>
+  <TR><TD><TT>SORT</TT></TD>
+      <TD>Sort the headers alphabetically.</TD>
+  </TR>
+  <TR><TD><TT>SINGLEDOC</TT></TD>
+      <TD>Do not create a document header and footer when creating 
+          documentation in LaTeX format.  This allows you to include
+          the generated documents into big document or 
+          www_link(`#MAIND', `master index file').</TD>
+  </TR>
+  <TR><TD><TT>TITLE &lt;title&gt;</TT></TD>
+      <TD>Sets the title that is used for the 
+         www_link(`#MAIND', `master index file').</TD>
+  </TR>
+  <TR><TD><TT>TOC</TT></TD>
+      <TD>Generate a table of contents.  It is only useful when you select
+      ASCII as output mode.  With all other output modes the
+      table of contents is generated anyway.</TD>
+  </TR>
+  <TR><TD><TT>TABSIZE &lt;n&gt;</TT></TD>
+      <TD>Convert each tab into <TT>n</TT> spaces.</TD>
+  </TR>
+  <TR><TD><TT>-v</TT></TD>
+      <TD>Verbose option, ROBODoc will tell you what it is doing.</TD>
+  </TR>
+</TABLE>
+
+<P>If you wonder why all the odd ALL CAPS flags are used instead
+of for instance "-x"; this was how it was done on the Amiga.</P>
+
+<P>The following abbreviations are also allowed:</P>
+<TABLE >
+<TR><TD><TT>-s </TT></TD><TD><TT>SORT</TT></TD></TR>
+<TR><TD><TT>-t </TT></TD><TD><TT>TOC</TT></TD></TR>
+<TR><TD><TT>-x </TT></TD><TD><TT>XREF</TT></TD></TR>
+<TR><TD><TT>-g </TT></TD><TD><TT>GENXREF</TT></TD></TR>
+<TR><TD><TT>-i </TT></TD><TD><TT>INTERNAL</TT></TD></TR>
+<TR><TD><TT>-io</TT></TD><TD><TT>INTERNALONLY</TT></TD></TR>
+<TR><TD><TT>-ts</TT></TD><TD><TT>TABSIZE</TT></TD></TR>
+</TABLE>
+
+
+www_section(`ADV', `Adding New Languages')
+
+<P>To add a new programming language to ROBODoc you have to edit
+<TT>headers.c</TT>.  Here you find three variables:
+<TT>header_markers</TT>, <TT>remark_markers</TT>, and
+<TT>end_markers</TT>.  There are all arrays, and you have to add
+an new entry to each of these three arrays.</P>
+
+<P>Say your programming language uses the following type of remarks:</P>
+<PRE>
+   $%% This is a remark with some text       
+       and some more and some more  %%$
+</PRE>
+
+<P>That is is starts with three spaces and then <TT>$%%</TT>, and
+has to end with <TT>%%$</TT>. Then you would add to <TT>header_markers</TT>
+</P>
+<PRE>
+   "   $%%****",
+</PRE>
+<P>To <TT>remark_markers</TT> you would add</P>
+<PRE>
+   "   *",
+</PRE>
+<P>And to <TT>end_markers</TT> you would add</P>
+<PRE>
+   "   $%%***",
+</PRE>
+<P>You can then use the following kind of headers in your program:</P>
+<PRE>
+   $%%****f* Test/afunction ***** 
+   * NAME  
+   *   afunction
+   * FUNCTION
+   *   A test.
+   * SOURCE
+   *%%$
+     afunction(test,test) [
+       print hello world ;
+     ]
+   $%%***%%$
+</PRE>
+
+
+
+
+
+www_section(`SAB', `Suggestions and Bugs')
+
+<P>If you find any bugs, catch them, put them in a jar, and send
+them to:</P> 
+<ADDRESS>fslothouber@acm.org</ADDRESS>  
+<P>Suggestions are also welcome at this address.  Flames can be
+directed to the sun.</P>
+
+www_bodyend
+www_docend
+
diff --git a/util/robodoc/Docs/tocgen.m4 b/util/robodoc/Docs/tocgen.m4
new file mode 100644 (file)
index 0000000..94d2f51
--- /dev/null
@@ -0,0 +1,6 @@
+m4_changecom(`/-*', `*-/')m4_dnl
+m4_define(`www_sectionCounter',0)m4_dnl
+m4_define(`www_subSectionCounter',0)m4_dnl
+m4_define(`www_incrCounter',`m4_define(`$1',m4_incr($1))')m4_dnl
+m4_define(`www_section', `www_incrCounter(`www_sectionCounter')<STRONG><FONT COLOR="red">m4_format(`%02d', www_sectionCounter)</FONT>......... <A HREF="#$1">$2</A></STRONG><BR> m4_define(`www_subSectionCounter', 0)')m4_dnl
+m4_define(`www_subSection', `www_incrCounter(`www_subSectionCounter')<STRONG><font color="red">m4_format(`%02d.%02d', www_sectionCounter, www_subSectionCounter)</font>.......... <A HREF="#$1">$2</A></STRONG><BR>')m4_dnl
diff --git a/util/robodoc/Examples/C/makefile b/util/robodoc/Examples/C/makefile
new file mode 100644 (file)
index 0000000..12dd865
--- /dev/null
@@ -0,0 +1,34 @@
+#
+# $Id$
+#
+#
+
+myclean:
+       rm -f *~
+       rm -f *.xref
+       rm -f *.html
+       rm -f xref_files
+
+#
+# Creates the example used in the documentation.
+# Assumed robodoc has been installed.
+#
+# It also shows how to make some plain makefile rules to
+# generate documentation.
+#
+
+xref_files : prog1.c.xref prog2.c.xref 
+       echo "prog1.c.xref" >  xref_files
+       echo "prog2.c.xref" >> xref_files
+
+prog1.c.xref : prog1.c 
+       robodoc prog1.c prog1.c.html GENXREF prog1.c.xref HTML INTERNAL
+
+prog2.c.xref : prog2.c
+       robodoc prog2.c prog2.c.html GENXREF prog2.c.xref HTML INTERNAL
+
+prog1.c.html : prog1.c xref_files 
+       robodoc prog1.c prog1.c.html XREF xref_files HTML INTERNAL
+
+prog2.c.html : prog2.c xref_files 
+       robodoc prog2.c prog2.c.html XREF xref_files HTML INTERNAL
diff --git a/util/robodoc/Examples/C/prog1.c b/util/robodoc/Examples/C/prog1.c
new file mode 100644 (file)
index 0000000..ba29eb2
--- /dev/null
@@ -0,0 +1,55 @@
+/****h* TEST/Prog1 ***
+*  NAME
+*    Prog1 -- Test program 1. (v1.0)
+*  COPYRIGHT
+*    Maverick Software Development (C) 1995
+*  FUNCTION
+*    Totally nothing and useless.
+*  AUTHOR
+*    Jacco van Weert
+*  CREATION DATE
+*    15-Feb-95
+*  MODIFICATION HISTORY
+*    15-Feb-95 - v1.0 - First version
+*  NOTES
+*    Nothing special
+***********
+*/
+
+/****i* Prog1/Proc_Internal1 ***
+*  NAME
+*    Proc_Internal1 -- Internal procedure test program. (v1.0)
+*  SYNOPSIS
+*    Proc_Internal1
+*  FUNCTION
+*    Just for fun.
+*  BUGS
+*    The procedure does not exist :)
+*  PURPOSE
+*    The purpose of the procedure, whatever it is.
+*  SEE ALSO
+*    Proc_Normal1, Proc_Internal2
+*    Proc_Normal2, prog2.c
+*********
+*/
+
+/****** Prog1/Proc_Normal1 **
+*
+* NAME
+*   Proc_Normal1 -- Normal procedure. (v1.0)
+* SYNOPSIS
+*   Proc_Normal1
+* FUNCTION
+*    Useless.
+* SEE ALSO
+*  Proc_Internal1, 
+*  Proc_Normal2,
+*  prog2.c, 
+*  Prog2
+***********
+*/
+
+
+
+
+
diff --git a/util/robodoc/Examples/C/prog1.c.html b/util/robodoc/Examples/C/prog1.c.html
new file mode 100644 (file)
index 0000000..b7cabbb
--- /dev/null
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML><HEAD>
+<TITLE>prog1.c.html</TITLE>
+<!-- Source: prog1.c -->
+<!-- Generated with ROBODoc Version 3.2.2 (Jan 28 2001) -->
+<!-- ROBODoc (c) 1994-2000 by Frans Slothouber and Jacco van Weert. -->
+</HEAD><BODY BGCOLOR="#FFFFFF">
+<A NAME="prog1.c">Generated from prog1.c</A> with ROBODoc v3.2.2 on Mon Apr 02 20:03:02 2001
+<BR>
+<H3 ALIGN="center">TABLE OF CONTENTS</H3>
+<OL>
+<LI><A HREF="#Prog1">TEST/Prog1</A>
+<LI><A HREF="#Proc_Internal1">Prog1/Proc_Internal1</A>
+<LI><A HREF="#Proc_Normal1">Prog1/Proc_Normal1</A>
+</OL>
+<HR>
+
+<H2><A NAME="Prog1">TEST/Prog1</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>    <B>Prog1</B> -- Test program 1. (v1.0)
+</EM></PRE><FONT SIZE="+1">COPYRIGHT</FONT>
+<PRE>    Maverick Software Development (C) 1995
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>    Totally nothing and useless.
+</PRE><FONT SIZE="+1">AUTHOR</FONT>
+<PRE><B>    Jacco van Weert
+</B></PRE><FONT SIZE="+1">CREATION DATE</FONT>
+<PRE><B>    15-Feb-95
+</B></PRE><FONT SIZE="+1">MODIFICATION HISTORY</FONT>
+<PRE>    15-Feb-95 - v1.0 - First version
+</PRE><FONT SIZE="+1">NOTES</FONT>
+<PRE><EM>    Nothing special
+</EM></PRE>
+<HR>
+
+<H2><A NAME="Proc_Internal1">Prog1/Proc_Internal1</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>    <B>Proc_Internal1</B> -- Internal procedure test program. (v1.0)
+</EM></PRE><FONT SIZE="+1">SYNOPSIS</FONT>
+<PRE>    <B>Proc_Internal1</B>
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>    Just for fun.
+</PRE><FONT SIZE="+1">BUGS</FONT>
+<PRE><EM>    The procedure does not exist :)
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>    The purpose of the procedure, whatever it is.
+</PRE><FONT SIZE="+1">SEE ALSO</FONT>
+<PRE>    <A HREF="#Proc_Normal1">Proc_Normal1</A>, <A HREF="prog2.c.html#Proc_Internal2">Proc_Internal2</A>
+    <A HREF="prog2.c.html#Proc_Normal2">Proc_Normal2</A>, <A HREF="prog2.c.html#prog2.c">prog2.c</A>
+</PRE>
+<HR>
+
+<H2><A NAME="Proc_Normal1">Prog1/Proc_Normal1</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>Proc_Normal1</B> -- Normal procedure. (v1.0)
+</EM></PRE><FONT SIZE="+1">SYNOPSIS</FONT>
+<PRE>   <B>Proc_Normal1</B>
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>    Useless.
+</PRE><FONT SIZE="+1">SEE ALSO</FONT>
+<PRE>  <A HREF="#Proc_Internal1">Proc_Internal1</A>, 
+  <A HREF="prog2.c.html#Proc_Normal2">Proc_Normal2</A>,
+  <A HREF="prog2.c.html#prog2.c">prog2.c</A>, 
+  <A HREF="prog2.c.html#Prog2">Prog2</A>
+</PRE>
+</BODY></HTML>
diff --git a/util/robodoc/Examples/C/prog2.c b/util/robodoc/Examples/C/prog2.c
new file mode 100644 (file)
index 0000000..b712436
--- /dev/null
@@ -0,0 +1,50 @@
+/****h* TEST/Prog2 *****
+* NAME
+*   Prog2 -- Test program 2. (v1.0)
+* COPYRIGHT
+*   Maverick Software Development (C) 1995
+* FUNCTION
+*  Totally nothing and useless.
+* AUTHOR
+*  Jacco van Weert
+* MODIFICATION HISTORY
+*   15-Feb-95 - v1.0 - First version
+* NOTES
+*   Nothing special
+******
+*/
+
+
+/****i* Prog2/Proc_Internal2 **
+*  NAME
+*    Proc_Internal2 -- Internal procedure test program. (v1.0)
+*  SYNOPSIS
+*    Proc_Internal2
+*  FUNCTION
+*    Just for fun.
+*  INPUTS
+*    No inputs
+*  BUGS
+*    The procedure does not exist :)
+* SEE ALSO
+*  Proc_Normal1, Proc_Internal1, Proc_Normal2, prog1.c
+************
+*/
+
+/****** Prog2/Proc_Normal2 ***
+*  NAME
+*    Proc_Normal2 -- Normal procedure. (v1.0)
+*  SYNOPSIS
+*    Proc_Normal2
+*  FUNCTION
+*    Useless.
+*  SEE ALSO
+*    Proc_Internal1, Proc_Normal1, prog1.c
+******
+*/
+
+
+
+
+
+
diff --git a/util/robodoc/Examples/C/prog2.c.html b/util/robodoc/Examples/C/prog2.c.html
new file mode 100644 (file)
index 0000000..8410c01
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML><HEAD>
+<TITLE>prog2.c.html</TITLE>
+<!-- Source: prog2.c -->
+<!-- Generated with ROBODoc Version 3.2.2 (Jan 28 2001) -->
+<!-- ROBODoc (c) 1994-2000 by Frans Slothouber and Jacco van Weert. -->
+</HEAD><BODY BGCOLOR="#FFFFFF">
+<A NAME="prog2.c">Generated from prog2.c</A> with ROBODoc v3.2.2 on Mon Apr 02 20:03:02 2001
+<BR>
+<H3 ALIGN="center">TABLE OF CONTENTS</H3>
+<OL>
+<LI><A HREF="#Prog2">TEST/Prog2</A>
+<LI><A HREF="#Proc_Internal2">Prog2/Proc_Internal2</A>
+<LI><A HREF="#Proc_Normal2">Prog2/Proc_Normal2</A>
+</OL>
+<HR>
+
+<H2><A NAME="Prog2">TEST/Prog2</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>Prog2</B> -- Test program 2. (v1.0)
+</EM></PRE><FONT SIZE="+1">COPYRIGHT</FONT>
+<PRE>   Maverick Software Development (C) 1995
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>  Totally nothing and useless.
+</PRE><FONT SIZE="+1">AUTHOR</FONT>
+<PRE><B>  Jacco van Weert
+</B></PRE><FONT SIZE="+1">MODIFICATION HISTORY</FONT>
+<PRE>   15-Feb-95 - v1.0 - First version
+</PRE><FONT SIZE="+1">NOTES</FONT>
+<PRE><EM>   Nothing special
+</EM></PRE>
+<HR>
+
+<H2><A NAME="Proc_Internal2">Prog2/Proc_Internal2</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>    <B>Proc_Internal2</B> -- Internal procedure test program. (v1.0)
+</EM></PRE><FONT SIZE="+1">SYNOPSIS</FONT>
+<PRE>    <B>Proc_Internal2</B>
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>    Just for fun.
+</PRE><FONT SIZE="+1">INPUTS</FONT>
+<PRE>    No inputs
+</PRE><FONT SIZE="+1">BUGS</FONT>
+<PRE><EM>    The procedure does not exist :)
+</EM></PRE><FONT SIZE="+1">SEE ALSO</FONT>
+<PRE>  <A HREF="prog1.c.html#Proc_Normal1">Proc_Normal1</A>, <A HREF="prog1.c.html#Proc_Internal1">Proc_Internal1</A>, <A HREF="#Proc_Normal2">Proc_Normal2</A>, <A HREF="prog1.c.html#prog1.c">prog1.c</A>
+</PRE>
+<HR>
+
+<H2><A NAME="Proc_Normal2">Prog2/Proc_Normal2</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>    <B>Proc_Normal2</B> -- Normal procedure. (v1.0)
+</EM></PRE><FONT SIZE="+1">SYNOPSIS</FONT>
+<PRE>    <B>Proc_Normal2</B>
+</PRE><FONT SIZE="+1">FUNCTION</FONT>
+<PRE>    Useless.
+</PRE><FONT SIZE="+1">SEE ALSO</FONT>
+<PRE>    <A HREF="prog1.c.html#Proc_Internal1">Proc_Internal1</A>, <A HREF="prog1.c.html#Proc_Normal1">Proc_Normal1</A>, <A HREF="prog1.c.html#prog1.c">prog1.c</A>
+</PRE>
+</BODY></HTML>
diff --git a/util/robodoc/Examples/CPP/makefile b/util/robodoc/Examples/CPP/makefile
new file mode 100644 (file)
index 0000000..7fc75ae
--- /dev/null
@@ -0,0 +1,44 @@
+# $Id$
+
+ROBODOC=robodoc
+DOCS=muppets.cpp.html muppets.h.html
+XREF=$(DOCS:.html=.xref)
+
+all: masterindex.html
+
+myclean:
+       rm -f *~
+       rm -f *.xref
+       rm -f *.xrefs
+       rm -f *.html
+       rm -f xref_files
+#
+# This makefile shows how with a few rules you can generate
+# the documentation from all your sources.
+#
+
+#
+# create xrefs file (file with the names of all .xref files).
+#
+muppets.xrefs : $(XREF)
+       /bin/ls *.xref > $@
+
+#
+# Rule to create an .xref file.
+#
+%.xref : % muppets.xrefs
+       $(ROBODOC) $< $(@:.xref=.html) INTERNAL -g $@ -v
+
+#
+# Rule to create an .html file.
+#
+%.html : %
+       $(ROBODOC) $< $@ HTML INTERNAL -x muppets.xrefs -v
+
+#
+#
+#
+masterindex.html : muppets.xrefs $(DOCS)
+       $(ROBODOC) $< $@ INDEX HTML
+
+
diff --git a/util/robodoc/Examples/CPP/masterindex.html b/util/robodoc/Examples/CPP/masterindex.html
new file mode 100644 (file)
index 0000000..396d0aa
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML><HEAD>
+<TITLE>Master Index File</TITLE>
+<!-- Source: muppets.xrefs -->
+<!-- Generated with ROBODoc Version 3.2.2 (Jan 28 2001) -->
+<!-- ROBODoc (c) 1994-2000 by Frans Slothouber and Jacco van Weert. -->
+</HEAD><BODY BGCOLOR="#FFFFFF">
+<A NAME="muppets.xrefs">Generated from muppets.xrefs</A> with ROBODoc v3.2.2 on Mon Apr 02 20:03:03 2001
+<BR>
+<H1>Master Index File</H1>
+<H2>Source Files</H2>
+<TABLE>
+<TR>
+<TD><A HREF="muppets.cpp.html#muppets.cpp"><TT>muppets.cpp</TT></A></TD>
+<TD><A HREF="muppets.h.html#muppets.h"><TT>muppets.h</TT></A></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+</TR>
+</TABLE>
+<H2>Classes</H2>
+<TABLE>
+<TR>
+<TD><A HREF="muppets.h.html#puppet"><TT>puppet</TT></A></TD>
+<TD><A HREF="muppets.h.html#stage"><TT>stage</TT></A></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+</TR>
+</TABLE>
+<H2>Methods</H2>
+<TABLE>
+<TR>
+<TD><A HREF="muppets.cpp.html#puppet::act"><TT>puppet::act</TT></A></TD>
+<TD><A HREF="muppets.cpp.html#puppet::talk"><TT>puppet::talk</TT></A></TD>
+<TD><A HREF="muppets.cpp.html#puppet::walk"><TT>puppet::walk</TT></A></TD>
+<TD><A HREF="muppets.cpp.html#stage::lights"><TT>stage::lights</TT></A></TD>
+</TR>
+<TR>
+<TD><A HREF="muppets.cpp.html#stage::open_curtains"><TT>stage::open_curtains</TT></A></TD>
+<TD></TD>
+<TD></TD>
+<TD></TD>
+</TR>
+</TABLE>
+</BODY></HTML>
diff --git a/util/robodoc/Examples/CPP/muppets.cpp b/util/robodoc/Examples/CPP/muppets.cpp
new file mode 100644 (file)
index 0000000..d5a96e1
--- /dev/null
@@ -0,0 +1,51 @@
+
+/****m* Mupputs/puppet::walk
+ * NAME
+ *   puppet::walk
+ * PURPOSE
+ *   Let puppet walk.
+ ******
+ */
+
+/****m* Mupputs/puppet::talk
+ * NAME
+ *   puppet::talk
+ * PURPOSE
+ *   Let puppet talk.
+ ******
+ */
+
+/****m* Mupputs/puppet::act
+ * NAME
+ *   puppet::act
+ * PURPOSE
+ *   Let puppet walk and talk.
+ ******
+ */
+
+
+/****m* Mupputs/stage::open_curtains
+ * NAME
+ *   stage::open_curtains -- 
+ * PURPOSE
+ *   Open the curtains.
+ * SOURCE
+ */
+
+void stage::open_curtains()
+{
+  lights(ON);
+}
+
+/*********/
+
+
+
+/****m* Mupputs/stage::lights
+ * NAME
+ *   stage::lights -- switch lights on or off.
+ * PURPOSE
+ *   Switch lights on or off.
+ ******
+ */
+
diff --git a/util/robodoc/Examples/CPP/muppets.cpp.html b/util/robodoc/Examples/CPP/muppets.cpp.html
new file mode 100644 (file)
index 0000000..db88b30
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML><HEAD>
+<TITLE>muppets.cpp.html</TITLE>
+<!-- Source: muppets.cpp -->
+<!-- Generated with ROBODoc Version 3.2.2 (Jan 28 2001) -->
+<!-- ROBODoc (c) 1994-2000 by Frans Slothouber and Jacco van Weert. -->
+</HEAD><BODY BGCOLOR="#FFFFFF">
+<A NAME="muppets.cpp">Generated from muppets.cpp</A> with ROBODoc v3.2.2 on Mon Apr 02 20:03:03 2001
+<BR>
+<H3 ALIGN="center">TABLE OF CONTENTS</H3>
+<OL>
+<LI><A HREF="#puppet::walk">Mupputs/puppet::walk</A>
+<LI><A HREF="#puppet::talk">Mupputs/puppet::talk</A>
+<LI><A HREF="#puppet::act">Mupputs/puppet::act</A>
+<LI><A HREF="#stage::open_curtains">Mupputs/stage::open_curtains</A>
+<LI><A HREF="#stage::lights">Mupputs/stage::lights</A>
+</OL>
+<HR>
+
+<H2><A NAME="puppet::walk">Mupputs/puppet::walk</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>puppet::walk</B>
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>   Let <A HREF="muppets.h.html#puppet">puppet</A> walk.
+</PRE>
+<HR>
+
+<H2><A NAME="puppet::talk">Mupputs/puppet::talk</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>puppet::talk</B>
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>   Let <A HREF="muppets.h.html#puppet">puppet</A> talk.
+</PRE>
+<HR>
+
+<H2><A NAME="puppet::act">Mupputs/puppet::act</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>puppet::act</B>
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>   Let <A HREF="muppets.h.html#puppet">puppet</A> walk and talk.
+</PRE>
+<HR>
+
+<H2><A NAME="stage::open_curtains">Mupputs/stage::open_curtains</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>stage::open_curtains</B> -- 
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>   Open the curtains.
+</PRE><FONT SIZE="+1">SOURCE</FONT>
+<PRE>    void <B>stage::open_curtains</B>()
+    {
+      lights(ON);
+    }    
+</PRE>
+<HR>
+
+<H2><A NAME="stage::lights">Mupputs/stage::lights</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>stage::lights</B> -- switch lights on or off.
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>   Switch lights on or off.
+</PRE>
+</BODY></HTML>
diff --git a/util/robodoc/Examples/CPP/muppets.h b/util/robodoc/Examples/CPP/muppets.h
new file mode 100644 (file)
index 0000000..873cbc9
--- /dev/null
@@ -0,0 +1,25 @@
+
+/****c* Mupputs/puppet
+ * NAME
+ *   pupput --
+ * PURPOSE
+ *   Little monsters that can walk and talk on a stage.
+ * METHODS
+ *   puppet::walk -- make a puppet walk
+ *   puppet::talk -- make a puppet talk
+ *   puppet::act  -- make a puppet act
+ *******
+ */
+
+
+/****c* Mupputs/stage
+ * NAME
+ *   stage -- the floor on which the puppets act. 
+ * METHODS
+ *   stage::open_curtains
+ *   stage::lights
+ ******
+ */
+
+
+
diff --git a/util/robodoc/Examples/CPP/muppets.h.html b/util/robodoc/Examples/CPP/muppets.h.html
new file mode 100644 (file)
index 0000000..c91e78e
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML><HEAD>
+<TITLE>muppets.h.html</TITLE>
+<!-- Source: muppets.h -->
+<!-- Generated with ROBODoc Version 3.2.2 (Jan 28 2001) -->
+<!-- ROBODoc (c) 1994-2000 by Frans Slothouber and Jacco van Weert. -->
+</HEAD><BODY BGCOLOR="#FFFFFF">
+<A NAME="muppets.h">Generated from muppets.h</A> with ROBODoc v3.2.2 on Mon Apr 02 20:03:03 2001
+<BR>
+<H3 ALIGN="center">TABLE OF CONTENTS</H3>
+<OL>
+<LI><A HREF="#puppet">Mupputs/puppet</A>
+<LI><A HREF="#stage">Mupputs/stage</A>
+</OL>
+<HR>
+
+<H2><A NAME="puppet">Mupputs/puppet</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   pupput --
+</EM></PRE><FONT SIZE="+1">PURPOSE</FONT>
+<PRE>   Little monsters that can walk and talk on a <A HREF="#stage">stage</A>.
+</PRE><FONT SIZE="+1">METHODS</FONT>
+<PRE>   <A HREF="muppets.cpp.html#puppet::walk">puppet::walk</A> -- make a <B>puppet</B> walk
+   <A HREF="muppets.cpp.html#puppet::talk">puppet::talk</A> -- make a <B>puppet</B> talk
+   <A HREF="muppets.cpp.html#puppet::act">puppet::act</A>  -- make a <B>puppet</B> act
+</PRE>
+<HR>
+
+<H2><A NAME="stage">Mupputs/stage</A></H2>
+
+<FONT SIZE="+1">NAME</FONT>
+<PRE><EM>   <B>stage</B> -- the floor on which the puppets act. 
+</EM></PRE><FONT SIZE="+1">METHODS</FONT>
+<PRE>   <A HREF="muppets.cpp.html#stage::open_curtains">stage::open_curtains</A>
+   <A HREF="muppets.cpp.html#stage::lights">stage::lights</A>
+</PRE>
+</BODY></HTML>
diff --git a/util/robodoc/Headers/assembler.sample b/util/robodoc/Headers/assembler.sample
new file mode 100644 (file)
index 0000000..dbf066b
--- /dev/null
@@ -0,0 +1,63 @@
+****h* main_module/module [1.0] *
+*
+* NAME
+*
+* COPYRIGHT
+*
+* FUNCTION
+*
+* AUTHOR
+*
+* CREATION DATE
+*
+* MODIFICATION HISTORY
+*
+* NOTES
+*
+*******
+
+
+****f* module/procname [1.0] *
+*
+* NAME
+*
+* SYNOPSIS
+*
+* FUNCTION
+*
+* INPUTS
+*
+* RESULT
+*
+* EXAMPLE
+*
+* NOTES
+*
+* BUGS
+*
+* SEE ALSO
+*
+**********
+
+
+****i* module/i_procname [1.0] *
+*
+* NAME
+*
+* SYNOPSIS
+*
+* FUNCTION
+*
+* INPUTS
+*
+* RESULT
+*
+* EXAMPLE
+*
+* NOTES
+*
+* BUGS
+*
+* SEE ALSO
+*
+**********
diff --git a/util/robodoc/Headers/basic.sample b/util/robodoc/Headers/basic.sample
new file mode 100644 (file)
index 0000000..22fca0c
--- /dev/null
@@ -0,0 +1,63 @@
+REM  ****h* main_module/module [1.0] *
+REM
+REM  NAME
+REM
+REM  COPYRIGHT
+REM
+REM  FUNCTION
+REM
+REM  AUTHOR
+REM
+REM  CREATION DATE
+REM
+REM  MODIFICATION HISTORY
+REM
+REM  NOTES
+REM
+REM  *****
+
+
+REM  ***** module/procname [1.0] *
+REM
+REM  NAME
+REM
+REM  SYNOPSIS
+REM
+REM  FUNCTION
+REM
+REM  INPUTS
+REM
+REM  RESULT
+REM
+REM  EXAMPLE
+REM
+REM  NOTES
+REM
+REM  BUGS
+REM
+REM  SEE ALSO
+REM
+REM  ********
+
+
+REM  ****i* module/i_procname [1.0] *
+REM
+REM  NAME
+REM
+REM  SYNOPSIS
+REM
+REM  FUNCTION
+REM
+REM  INPUTS
+REM
+REM  RESULT
+REM
+REM  EXAMPLE
+REM
+REM  NOTES
+REM
+REM  BUGS
+REM
+REM  SEE ALSO
+REM
+REM  ********
diff --git a/util/robodoc/Headers/c.sample b/util/robodoc/Headers/c.sample
new file mode 100644 (file)
index 0000000..beca449
--- /dev/null
@@ -0,0 +1,46 @@
+/****h* projectname/module_name [1.0]
+*  NAME
+*  COPYRIGHT
+*  FUNCTION
+*  AUTHOR
+*  CREATION DATE
+*  MODIFICATION HISTORY
+*  NOTES
+*******
+*/
+
+
+/****f* module_name/funtion_name [1.0] *
+*  NAME
+*  SYNOPSIS
+*  FUNCTION
+*  INPUTS
+*  RESULT
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+**********
+*/
+
+
+/****s* module_name/stucture_name [1.0] *
+*  NAME
+*  PURPOSE
+*  ATTRIBUTES
+*  NOTES
+*  BUGS
+*  SEE ALSO
+**********
+*/
+
+
+/****v* module_name/variable_name [1.0] *
+*  NAME
+*  PURPOSE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+**********
+*/
+
diff --git a/util/robodoc/Headers/cpp.sample b/util/robodoc/Headers/cpp.sample
new file mode 100644 (file)
index 0000000..056d33f
--- /dev/null
@@ -0,0 +1,68 @@
+/****h* projectname/module_name [1.0]
+*  NAME
+*  COPYRIGHT
+*  FUNCTION
+*  AUTHOR
+*  CREATION DATE
+*  MODIFICATION HISTORY
+*  NOTES
+*******
+*/
+
+
+/****f* module_name/funtion_name [1.0] *
+*  NAME
+*  SYNOPSIS
+*  FUNCTION
+*  INPUTS
+*  RESULT
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+**********
+*/
+
+/****f* module_name/funtion_name [1.0] *
+*  NAME
+*  SYNOPSIS
+*  FUNCTION
+*  INPUTS
+*  RESULT
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+*  SOURCE
+*/
+
+example()
+
+/**********/
+
+
+/****c* module_name/class_name [1.0] *
+*  NAME
+*  PURPOSE
+*  METHODS
+*  DERIVED FROM
+*  DERIVED BY
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+**********
+*/
+
+
+/****m* module_name/method_name [1.0] *
+*  NAME
+*  SYNOPSIS
+*  PURPOSE
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+**********
+*/
+
diff --git a/util/robodoc/Headers/fortan.sample b/util/robodoc/Headers/fortan.sample
new file mode 100644 (file)
index 0000000..b646a73
--- /dev/null
@@ -0,0 +1,63 @@
+C    ****h* main_module/module [1.0] *
+C
+C    NAME
+C
+C    COPYRIGHT
+C
+C    FUNCTION
+C
+C    AUTHOR
+C
+C    CREATION DATE
+C
+C    MODIFICATION HISTORY
+C
+C    NOTES
+C
+C    *****
+
+
+C    ***** module/procname [1.0] *
+C
+C    NAME
+C
+C    SYNOPSIS
+C
+C    FUNCTION
+C
+C    INPUTS
+C
+C    RESULT
+C
+C    EXAMPLE
+C
+C    NOTES
+C
+C    BUGS
+C
+C    SEE ALSO
+C
+C    ********
+
+
+C    ****i* module/i_procname [1.0] *
+C
+C    NAME
+C
+C    SYNOPSIS
+C
+C    FUNCTION
+C
+C    INPUTS
+C
+C    RESULT
+C
+C    EXAMPLE
+C
+C    NOTES
+C
+C    BUGS
+C
+C    SEE ALSO
+C
+C    ********
diff --git a/util/robodoc/Headers/html.sample b/util/robodoc/Headers/html.sample
new file mode 100644 (file)
index 0000000..7f3e557
--- /dev/null
@@ -0,0 +1,38 @@
+<!---****h* projectname/module_name [1.0]
+*  NAME
+*  COPYRIGHT
+*  FUNCTION
+*  AUTHOR
+*  CREATION DATE
+*  MODIFICATION HISTORY
+*  NOTES
+******* --->
+
+
+<!---****f* module_name/funtion_name [1.0] *
+*  NAME
+*  SYNOPSIS
+*  FUNCTION
+*  INPUTS
+*  RESULT
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SEE ALSO
+********** --->
+
+<!---****f* module_name/funtion_name [1.0] *
+*  NAME
+*  SYNOPSIS
+*  FUNCTION
+*  INPUTS
+*  RESULT
+*  EXAMPLE
+*  NOTES
+*  BUGS
+*  SOURCE
+* --->
+
+<H1> Example Source </H1>
+
+<!---********---->
diff --git a/util/robodoc/Headers/tcl.sample b/util/robodoc/Headers/tcl.sample
new file mode 100644 (file)
index 0000000..be76e76
--- /dev/null
@@ -0,0 +1,39 @@
+#****h* main_module/module [1.0] *
+#  NAME
+#  COPYRIGHT
+#  FUNCTION
+#  AUTHOR
+#  CREATION DATE
+#  MODIFICATION HISTORY
+#  NOTES
+#******
+#
+
+#****f* module/procname [1.0] *
+#  NAME
+#  SYNOPSIS
+#  FUNCTION
+#  INPUTS
+#  RESULT
+#  EXAMPLE
+#  NOTES
+#  BUGS
+#  SEE ALSO
+#********
+#
+
+#****f* module/procname [1.0] *
+#  NAME
+#  SYNOPSIS
+#  FUNCTION
+#  INPUTS
+#  RESULT
+#  EXAMPLE
+#  NOTES
+#  BUGS
+#  SEE ALSO
+#  SOURCE
+
+example()
+
+#********
diff --git a/util/robodoc/INSTALL b/util/robodoc/INSTALL
new file mode 100644 (file)
index 0000000..3a22cdd
--- /dev/null
@@ -0,0 +1,49 @@
+$Id$
+
+
+There are two possibilities, if you have a system that supports
+auto configuration (most Unix systems), then use:
+
+  ./configure
+  make
+  make docall
+
+become root and do
+
+  make install
+
+
+If your system does not support auto-configuration, then
+have a look at  Source/makefile.plain  
+you can build robodoc with
+
+
+  make -f makefile.plain 
+
+
+Additional documentation is provided in Docs/, in the form of
+robodoc.html.  For a good example of how to use ROBODoc see the
+ROBODoc source code.  To see what kind of documentation can
+generated with ROBODoc, change to Source/ and do a
+
+  make xhtml
+  make example
+
+or
+
+  make -f makefile.plain example
+
+It assumes you have netscape installed.
+
+If you want to see the LaTeX documentation use
+   make xtex
+   make texview
+   gv ROBODoc_mi.ps
+
+
+Also have a look at the example makefile in the Docs/ directory.
+
+
+Have fun,
+Frans.
+
diff --git a/util/robodoc/NEWS b/util/robodoc/NEWS
new file mode 100644 (file)
index 0000000..0348612
--- /dev/null
@@ -0,0 +1,17 @@
+V3.2.3  o Bug fix. The crosslink generator did not recognize links that
+          contained a '/' and ended with an '.' or ','.
+        o make install  installs additional documentation.
+
+V3.2.2  o Added a Master Index File for LaTeX output.  
+        o New NOSOURCE option to exclude source items from the documentation.
+        o New SINGLEDOC option to all documentation to be included into one
+          single document.
+        o New TITLE option to set the title for a master index file.
+          example make files includes commands to view the
+          generated documentation.
+        o Made the search for remark markers case insensitive.  So you
+          can use REM and Rem or even rEM.
+
+V3.2.1  Some small bugfixes, support for mailto:... links, and updates
+in the manual.
+
diff --git a/util/robodoc/README b/util/robodoc/README
new file mode 100644 (file)
index 0000000..ed54c74
--- /dev/null
@@ -0,0 +1,63 @@
+_______________________________________________________________________________
+
+
+       THIS IS MODIFIED VERSION OF ROBODOC FOR SILC DISTRIBUTION
+
+                   Pekka Riikonen <priikone@silcnet.org>
+
+_______________________________________________________________________________
+
+
+$Id$
+
+ROBODoc  Version 3.2.3 May 2001.
+
+ROBODoc is program documentation tool. The idea is to include for
+every function or procedure a standard header containing all
+sorts of information about the procedure or function.  ROBODoc
+extracts these headers from the source file and puts them in a
+separate autodocs-file.  ROBODoc thus allows you to include the
+program documentation in the source code and avoid having to
+maintain two separate documents.  Or as Petteri puts it: "robodoc
+is very useful - especially for programmers who don't like
+writing documents with Word or some other strange tool."
+
+ROBODoc can format the headers in a number of different formats:
+HTML, ASCII, AmigaGuide, LaTeX, or RTF. In HTML mode it can
+generate cross links between headers. You can even include parts
+of your source code.
+
+ROBODoc works with many programming languages: For instance C,
+Pascal, Shell Scripts, Assembler, COBOL, Occam, Postscript,
+Forth, Tcl/Tk, C++, Java -- basically any program in which you
+can use remarks/comments.
+
+  o For information on how to build and install see INSTALL
+  o For information on how to use ROBODoc see Docs/robodoc.html. 
+  o Blank headers for various languages can be found in Headers/
+  o For an example on how ROBODoc can be used
+    (1) The ROBODoc source code in Source/
+    (2) The C++ example in Examples/CPP/
+  o For licence information see COPYING
+  o For a change log see  Source/robodoc.c
+Many people contributed to ROBODoc, to name a few:
+
+o Petteri Kettunen <petterik@iki.fi>   
+  Bug fixes, FOLD, C features.
+o Bernd Koesling <KOESSI@CHESSY.aworld.de> 
+  Bug fixes, functional improvements, code cleanup.
+o Anthon Pang  <apang@mindlink.net>
+  RTF support, Bug fixes.
+o Thomas Aglassinger <agi@sbox.tu-graz.ac.at>
+  Fixes and cleanup of HTML-output
+o Stefan Kost kost@imn.htwk-leipzig.de
+  Idea of the master index file and different header types.
+
+
+Questions, found a bug or a typo; send an email to <fslothouber@acm.org>
+
+(c) 1994-2000  Frans Slothouber and Jacco van Weert 
+
+
+
diff --git a/util/robodoc/Source/analyser.c b/util/robodoc/Source/analyser.c
new file mode 100644 (file)
index 0000000..50af048
--- /dev/null
@@ -0,0 +1,670 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "robodoc.h"
+#include "headers.h"
+#include "items.h"
+#include "util.h"
+#include "folds.h"
+#include "links.h"
+#include "analyser.h"
+
+
+/****** ROBODoc/RB_Analyse_Document [3.0i]
+ * NAME
+ *   RB_Analyse_Document -- scan document for headers and store them
+ * SYNOPSIS
+ *   RB_Analyse_Document (document)
+ *   RB_Analyse_Document (FILE *)
+ * FUNCTION
+ *   Searches the document for headers. Stores information about
+ *   any headers that are found in a linked list. Information
+ *   that is stored includes, the name of the header, its version
+ *   number, and its contents.
+ * INPUTS
+ *   document - a pointer to a file with the document to be
+ *              analysed
+ *   the gobal buffer line_buffer.
+ * RESULT
+ *   1)   A linked list pointed to by the global variable
+ *        first_header that contains information about each
+ *        header.
+ * NOTES
+ *   Using fseek and ftell because gcc doesn't know fgetpos and fsetpos,
+ *   on the sun unix system that I use.
+ * SEE ALSO
+ *   RB_Find_Marker
+ * SOURCE
+ */
+
+void
+RB_Analyse_Document (FILE * document)
+{
+  int header_type;
+  int real_size;
+  char *name;
+
+  for (;
+       (header_type = RB_Find_Marker (document)) != NO_HEADER;
+       )
+    {
+      struct RB_header *new_header;
+      
+      if (!
+         (
+          ((header_type == INTERNAL_HEADER) &&
+           !(course_of_action & (DO_INCLUDE_INTERNAL | DO_INTERNAL_ONLY)))
+          ||
+          ((header_type != INTERNAL_HEADER) &&
+           (course_of_action & DO_INTERNAL_ONLY))
+          ||
+          (header_type == BLANK_HEADER)
+          )
+         )
+       {
+         long cur_file_pos;
+         
+         new_header = RB_Alloc_Header ();
+         RB_Insert_In_List (&first_header, new_header);
+         new_header->type = header_type;
+         if ((new_header->name = RB_Find_Header_Name ()) != NULL)
+           {
+             RB_Say ("found header [line %5d]: \"%s\"\n",
+                     line_number, new_header->name);
+             if ((new_header->function_name
+                  = RB_Function_Name (new_header->name)) == NULL)
+               {
+                 RB_Panic ("Can't determine the \"function\" name.\n");
+               }
+             cur_file_pos = (long) ftell (document);
+             if ((real_size = RB_Find_End_Marker (document,
+                                                  &new_header->size))
+                 != 0)
+               {
+                 char *contents;
+                 
+                 fseek (document, cur_file_pos, 0);
+                 if ((contents = malloc ((new_header->size +
+                                          2) * sizeof (char)))
+                     != NULL)
+                   {
+                     fread (contents, new_header->size, sizeof (char), document);
+                     
+                     contents[real_size] = '\0';
+                     new_header->contents = contents;
+                     new_header->size = real_size;
+                   }
+                 else
+                   RB_Panic ("out of memory! [Alloc Header Contents]\n");
+               }
+             else
+               {
+                 RB_Panic ("found header with no end marker \"%s\"\n",
+                           new_header->name);
+               }
+           }
+         else
+           {
+             RB_Panic ("found header marker but no name [line %d]\n",
+                       line_number);
+           }
+       }
+      else
+       {
+         if (header_type != BLANK_HEADER)
+           {
+             if ((name = RB_Find_Header_Name ()) != NULL)
+               {
+                 new_header = RB_Alloc_Header ();
+                 if ((real_size =
+                      RB_Find_End_Marker (document, &new_header->size))
+                     == 0)
+                   {
+                     RB_Free_Header (new_header);
+                     RB_Panic ("found header with no end marker \"%s\"\n", name);
+                   }
+                 else
+                   {
+                     RB_Free_Header (new_header);
+                   }
+               }
+             else
+               {
+                 RB_Panic ("found header marker but no name [line %d]\n",
+                           line_number);
+               }
+           }
+       }
+    }
+}
+
+/****** END RB_Analyse_Document *******/
+
+
+
+
+/****f* ROBODoc/RB_Function_Name [2.0x]
+ * NAME
+ *   RB_Function_Name -- get pointer to the function name.
+ * SYNOPSIS
+ *   char *RB_NamePart(char *header_name)
+ * FUNCTION
+ *   A header name is consists of two parts. The module name and
+ *   the function name. This returns a pointer to the function name.
+ *   The name "function name" is a bit obsolete. It is really the name
+ *   of any of objects that can be documented; classes, methods,
+ *   variables, functions, projects, etc.
+ * SOURCE
+ */
+
+char *
+RB_Function_Name (char *header_name)
+{
+  char *cur_char;
+  char c;
+  char *name;
+
+  name = NULL;
+  if ((cur_char = header_name) != NULL)
+    {
+      for (; (c = *cur_char) != '\0'; ++cur_char)
+       {
+         if ('/' == *cur_char)
+           {
+             ++cur_char;
+             if (*cur_char)
+               name = cur_char;
+           }
+       }
+    }
+  if (name) {
+     char *temp;
+     temp = malloc((strlen(name) + 1) * sizeof(char));
+     strcpy(temp, name);
+     return temp; 
+  } else {
+     return (name);
+  }
+}
+
+/*** RB_Name_Part ***/
+
+
+
+/****** ROBODoc/RB_Find_Marker [3.0h]
+ * NAME
+ *   RB_Find_Marker -- Search for header marker in document.
+ * SYNOPSIS
+ *   header_type = RB_Find_Marker (document)
+ *             int RB_Find_Marker (FILE *)
+ * FUNCTION
+ *   Read document file line by line, and search each line for the
+ *   any of the headers defined in the array  header_markers
+ * INPUTS
+ *   document - pointer to the file to be searched.
+ *   the gobal buffer line_buffer.
+ * RESULT
+ *   header type
+ *   can be:
+ *    (1) NO_HEADER - no header found, end of file reached
+ *    (2) MAIN_HEADER
+ *    (3) GENERIC_HEADER
+ *    (4) INTERNAL_HEADER
+ * BUGS
+ *   Bad use of feof(), fgets().
+ * SEE ALSO
+ *   RB_Find_End_Marker
+ * SOURCE
+ */
+
+int
+RB_Find_Marker (FILE * document)
+{
+  int found;
+  int marker, marker_type;
+  char *cur_char, *cur_mchar;
+
+  marker_type = NO_HEADER;
+  cur_char = NULL;
+  found = FALSE;
+  while (!feof (document) && !found)
+    {
+      *line_buffer = '\0';
+      fgets (line_buffer, MAX_LINE_LEN, document);
+      if (!feof (document))
+       {
+         line_number++;
+         for (marker = 0;
+              ((cur_mchar = header_markers[marker]) != NULL) && !found;
+              marker++)
+           {
+             for (found = TRUE, cur_char = line_buffer;
+                  *cur_mchar && *cur_char && found;
+                  cur_mchar++, cur_char++)
+               {
+                 if (tolower(*cur_mchar) != tolower(*cur_char))
+                   found = FALSE;
+               }
+           }
+         if (found)
+           {
+             switch (*cur_char)
+               {
+               case 'h':
+                 marker_type = MAIN_HEADER;
+                 break;
+               case '*':
+                 marker_type = GENERIC_HEADER;
+                 break;
+               case 'i':
+                 marker_type = INTERNAL_HEADER;
+                 break;
+               case 'f':
+                 marker_type = FUNCTION_HEADER;
+                 break;
+               case 's':
+                 marker_type = STRUCT_HEADER;
+                 break;
+               case 'c':
+                 marker_type = CLASS_HEADER;
+                 break;
+               case 'm':
+                 marker_type = METHOD_HEADER;
+                 break;
+               case 'd':
+                 marker_type = CONSTANT_HEADER;
+                 break;
+               case 'v':
+                 marker_type = VARIABLE_HEADER;
+                 break;
+               default:
+                 RB_Say ("%s: WARNING, [line %d] undefined headertype,"
+                         " using GENERIC\n", whoami, line_number);
+                 marker_type = GENERIC_HEADER;
+               }
+           }
+       }
+    }
+  if (!found || feof (document))
+    {
+      marker_type = NO_HEADER;
+    }
+  else if (marker_type == GENERIC_HEADER)
+    {
+      skip_while (*cur_char == '*');
+      if (*cur_char == '/')
+       {
+         marker_type = BLANK_HEADER;
+       }
+    }
+  return marker_type;
+}
+
+/******** END RB_Find_Marker ******/
+
+
+/****** ROBODoc/RB_Find_End_Marker [3.0h]
+ * NAME
+ *   RB_Find_End_Marker -- Search for end marker in document.
+ * SYNOPSIS
+ *   result = RB_Find_End_Marker (document)
+ *        int RB_Find_End_Marker (FILE *)
+ * FUNCTION
+ *   Searches line by line till any of the markers in the
+ *   array: end_markers is found.
+ * INPUTS
+ *   document   - pointer to the file to be searched.
+ *   int *total_size - external size
+ *   the gobal buffer line_buffer.
+ * RESULT
+ *                real_size if end marker was found
+ *   0          - no end marker was found
+ * SEE ALSO
+ *   RB_Find_Marker
+ * SOURCE
+ */
+
+int
+RB_Find_End_Marker (FILE * document, int *total_size)
+{
+  int real_size = 0;
+  int found = FALSE;
+  int marker;
+  int line_len = 0;
+  char *cur_char, *cur_mchar;
+
+  while (!feof (document) && !found)
+    {
+      cur_char = line_buffer;
+      *cur_char = '\0';
+      fgets (cur_char, MAX_LINE_LEN, document);
+      ++line_number;           /* global linecounter *koessi */
+
+      line_len = strlen (cur_char);
+      real_size += line_len;
+
+      if (!feof (document))
+       {
+         for (marker = 0;
+              ((cur_mchar = end_markers[marker]) != NULL) && !found;
+              marker++)
+           {
+             for (found = TRUE, cur_char = line_buffer;
+                  *cur_mchar && *cur_char && found;
+                  cur_mchar++, cur_char++)
+               {
+                 if (tolower(*cur_mchar) != tolower(*cur_char))
+                   found = FALSE;
+               }
+           }
+       }
+    }
+  if (total_size)
+    *total_size = real_size;
+  if (found)
+    return real_size - line_len;
+  else
+    return 0;
+}
+
+/*****  RB_Find_End_Marker   *****/
+
+
+/****** ROBODoc/RB_Find_Header_Name   [3.0b]
+ * NAME
+ *   RB_Find_Header_Name -- search for header name
+ * SYNOPSIS
+ *   result = RB_Find_Header_Name ()
+ *      char *RB_Find_Header_Name ()
+ * FUNCTION
+ *   Searches the line buffer for the header name.
+ *   It assumes that the header name follows after the
+ *   header marker, seperated by one or more spaces, and terminated
+ *   by one or more spaces or a '\n'.
+ *   It allocates an array of chars and copies the name to this array.
+ * INPUTS
+ *   the gobal buffer line_buffer.
+ * RESULT
+ *   pointer to the allocated array of chars that contains the name,
+ *   terminated with a '\0'.
+ *   NULL if no header name was found.
+ * MODIFICATION HISTORY
+ *   8. August 1995      --  optimized by koessi
+ * SEE ALSO
+ *   RB_Find_Function_Name(), RB_WordLen(), RB_StrDup()
+ * SOURCE
+ */
+
+char *
+RB_Find_Header_Name (void)
+{
+  char *cur_char;
+
+  cur_char = line_buffer;
+  skip_while (*cur_char != '*');
+  skip_while (!isspace (*cur_char));
+  skip_while (isspace (*cur_char));
+  if (*cur_char)
+    {
+      char *end_char, old_char;
+
+      end_char = cur_char + RB_WordLen (cur_char);
+      old_char = *end_char;
+      *end_char = '\0';
+      cur_char = RB_StrDup (cur_char);
+      *end_char = old_char;
+      return (cur_char);
+    }
+  return (NULL);
+}
+
+/*****  RB_Find_Header_Name  *****/
+
+
+/****** ROBODoc/RB_Find_Item [3.0b]
+ * NAME
+ *   RB_Find_Item -- find item in header contents.
+ * SYNOPSIS
+ *   item_type = RB_Find_Item (next_line,item_line)
+ *
+ *           int RB_Find_Item (char **,  char **)
+ * FUNCTION
+ *   Searches the header contents line by line, looking
+ *   for an item Indicator.
+ * INPUTS
+ *   next_line  - pointer to a pointer that points to line
+ *                at which the search will start.
+ * SIDE-EFFECTS
+ *   next_line  - pointer to a pointer that points to begin of the line
+ *                after the line the item was found on.
+ *   item_line  - pointer to a pointer that points to the line the item
+ *                was found on.
+ * RESULT
+ *   item_type  - one of possible items indicators.
+ * SOURCE
+ */
+
+int
+RB_Find_Item (char **next_line, char **item_line)
+{
+  char *cur_char = *next_line;
+  int item_type;
+
+  for (item_type = NO_ITEM;
+       *cur_char && (item_type == NO_ITEM);
+    )
+    {
+      *item_line = cur_char;
+      cur_char = RB_Skip_Remark_Marker (cur_char);
+
+      skip_while (isspace (*cur_char) && *cur_char != '\n');
+      if (isupper (*cur_char))
+       {
+         char *item_begin = cur_char;
+         char *item_end;
+
+         skip_while (isupper (*cur_char));
+         item_end = cur_char;
+         if (isspace (*cur_char) && *cur_char)
+           {
+             skip_while (isspace (*cur_char) && *cur_char != '\n');
+
+             /* Item consists of two words ? */
+             if (isupper (*cur_char) && *cur_char)
+               {
+                 skip_while (isupper (*cur_char));
+                 item_end = cur_char;
+                 skip_while (isspace (*cur_char) && *cur_char != '\n');
+               }
+             if (*cur_char == '\n')
+               {
+                 char old_char = *item_end;
+
+                 *item_end = '\0';
+                 item_type = RB_Get_Item_Type (item_begin);
+                 *item_end = old_char;
+                 cur_char++;
+               }
+           }
+       }
+      if (item_type == NO_ITEM)
+       {
+         find_eol;
+         if (*cur_char)
+           cur_char++;
+       }
+    }
+
+  /* advance item_line to end of comment block when we have no more items */
+  if (item_type == NO_ITEM)
+    {
+      *item_line = cur_char;
+    }
+  *next_line = cur_char;
+  return item_type;
+}
+
+/*****  RB_Find_Item    *****/
+
+
+/****** ROBODoc/RRB_Number_Duplicate_Headers 
+ * NAME
+ *    RB_Number_Duplicate_Headers -- number duplicate headers
+ * SYNOPSIS
+ *    RB_Number_Duplicate_Headers (void)
+ * FUNCTION
+ *    Extends the function name with an additional number if there 
+ *    are several components with the same name.
+ *    Otherwise there will be labels with the same name in HTML
+ *    which confuses the browser.
+ * SOURCE
+ */
+
+void 
+RB_Number_Duplicate_Headers (void)
+{
+  struct RB_header *cur_header;
+  struct RB_header *dup_header;
+  for (cur_header = first_header; 
+       cur_header;
+       cur_header = cur_header->next_header)
+  {  
+    char number[20];
+    int  nr = 0;
+    for (dup_header = cur_header->next_header; 
+         dup_header;
+         dup_header = dup_header->next_header)
+    {
+       if (strcmp(cur_header->function_name,
+                  dup_header->function_name) == 0) {
+          char *new_name;
+          nr++;
+          sprintf(number, "(%d)", nr);
+          new_name = malloc ((strlen(number) + 1 + 
+              strlen(dup_header->function_name) + 1 ) * sizeof(char));
+          if (new_name == NULL) 
+             RB_Panic ("out of memory! [Number Duplicates]\n");
+          sprintf(new_name, "%s%s", dup_header->function_name,
+                  number);
+          free(dup_header->function_name);
+          dup_header->function_name = new_name;
+       }
+    }
+  }
+}
+
+/******/
+
+
+/****** ROBODoc/RB_Make_Index_Tables [3.0b]
+ * NAME
+ *    RB_Make_Index_Tables
+ * SYNOPSIS
+ *    void RB_Make_Index_Tables (void)
+ * FUNCTION
+ *    Creates sorted index tables of headers and links to speed up
+ *    matching links later on.
+ * INPUTS
+ *    none
+ * SIDE EFFECTS
+ *    Modifies header_index & link_index
+ * RESULT
+ *    none
+ * SOURCE
+ */
+
+void
+RB_Make_Index_Tables ()
+{
+  int nr_of_headers, header;
+  int nr_of_links, link;
+  struct RB_link *cur_link;
+  struct RB_header *cur_header;
+
+  for (cur_header = first_header, nr_of_headers = 0;
+       cur_header;
+       cur_header = cur_header->next_header)
+    nr_of_headers++;
+
+  for (cur_link = first_link, nr_of_links = 0;
+       cur_link;
+       cur_link = cur_link->next_link)
+    nr_of_links++;
+
+  if (nr_of_headers)
+    {
+      int sort1, sort2;
+
+      RB_Say ("Allocating Header Index Table\n");
+      header_index = malloc (nr_of_headers * sizeof (struct RB_header **));
+
+      header_index_size = nr_of_headers;
+      if (!header_index)
+       RB_Panic ("out of memory! [Make Index Tables]\n");
+
+      /* Fill Index Table */
+      for (cur_header = first_header, header = 0;
+          cur_header;
+          cur_header = cur_header->next_header, header++)
+       header_index[header] = cur_header;
+
+      /* Sort Index Table */
+      RB_Say ("Sorting Header Index Table\n");
+      for (sort1 = 0; sort1 < nr_of_headers; sort1++)
+       {
+         struct RB_header *temp;
+
+         for (sort2 = sort1; sort2 < nr_of_headers; sort2++)
+           {
+             if (strcmp (header_index[sort1]->function_name,
+                         header_index[sort2]->function_name) > 0)
+               {
+                 temp = header_index[sort1];
+                 header_index[sort1] = header_index[sort2];
+                 header_index[sort2] = temp;
+               }
+           }
+       }
+    }
+  if (nr_of_links)
+    {
+      int sort1, sort2;
+
+      RB_Say ("Allocating Link Index Table\n");
+      link_index = malloc (nr_of_links * sizeof (struct RB_link **));
+
+      link_index_size = nr_of_links;
+      if (!link_index)
+       RB_Panic ("out of memory! [Make Index Tables]\n");
+
+      /* Fill Index Table */
+      for (cur_link = first_link, link = 0;
+          cur_link;
+          cur_link = cur_link->next_link, link++)
+       {
+         link_index[link] = cur_link;
+       }
+
+      /* Sort Index Table */
+      RB_Say ("Sorting Link Index Table\n");
+      for (sort1 = 0; sort1 < nr_of_links; sort1++)
+       {
+         struct RB_link *temp;
+
+         for (sort2 = sort1; sort2 < nr_of_links; sort2++)
+           {
+             if (strcmp (link_index[sort1]->label_name,
+                         link_index[sort2]->label_name) > 0)
+               {
+                 temp = link_index[sort1];
+                 link_index[sort1] = link_index[sort2];
+                 link_index[sort2] = temp;
+               }
+           }
+       }
+    }
+}
+
+/****** RB_Make_Index_Tables  *****/
diff --git a/util/robodoc/Source/analyser.h b/util/robodoc/Source/analyser.h
new file mode 100644 (file)
index 0000000..ce1be2a
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef ROBODOC_ANALYSER_H
+#define ROBODOC_ANALYSER_H
+
+void RB_Analyse_Document (FILE *);
+int RB_Find_Marker (FILE *);
+char *RB_Find_Header_Name (void);
+int RB_Find_End_Marker (FILE *, int *);
+int RB_Find_Item (char **, char **);
+void RB_Number_Duplicate_Headers(void);
+void RB_Make_Index_Tables (void);
+char *RB_Function_Name (char *header_name);
+
+#endif /* ROBODOC_ANALYSER_H */
diff --git a/util/robodoc/Source/config.h b/util/robodoc/Source/config.h
new file mode 100644 (file)
index 0000000..6304664
--- /dev/null
@@ -0,0 +1,30 @@
+/* Source/config.h.  Generated automatically by configure.  */
+/* Source/config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the strftime function.  */
+#define HAVE_STRFTIME 1
+
+/* Define if you have the vprintf function.  */
+#define HAVE_VPRINTF 1
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+/* #undef size_t */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you have the strstr function.  */
+#define HAVE_STRSTR 1
+
+/* Name of package */
+#define PACKAGE "robodoc"
+
+/* Version number of package */
+#define VERSION "3.2.3"
+
diff --git a/util/robodoc/Source/config.h.in b/util/robodoc/Source/config.h.in
new file mode 100644 (file)
index 0000000..4465948
--- /dev/null
@@ -0,0 +1,29 @@
+/* Source/config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if you have the strftime function.  */
+#undef HAVE_STRFTIME
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you have the strstr function.  */
+#undef HAVE_STRSTR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Version number of package */
+#undef VERSION
+
diff --git a/util/robodoc/Source/folds.c b/util/robodoc/Source/folds.c
new file mode 100644 (file)
index 0000000..4138b70
--- /dev/null
@@ -0,0 +1,141 @@
+#include <stddef.h>
+#include <string.h>
+#include "robodoc.h"
+#include "folds.h"
+
+
+/****v* ROBODoc/fold_start_markers 
+ * NAME
+ *   fold_start_markers
+ * FUNCTION
+ *   Strings for fold start.
+ * SOURCE
+ */
+fold_mark_t     fold_start_markers[] =
+{
+  {"/*{{{", "*/"},     /* C, C++ */
+  {"--{{{", "\n"},     /* Occam, line ends with newline */
+  {"#{{{", "\n"},      /* Various scripts, line ends with newline */
+  {NULL, NULL}
+};
+
+/****/
+
+
+/****v* ROBODoc/fold_end_markers 
+ * NAME
+ *   fold_start_end
+ * FUNCTION
+ *   Strings for fold end.
+ * SOURCE
+ */
+fold_mark_t fold_end_markers[] =
+{
+  {"/*}}}", "*/"},
+  {"--}}}", "\n"},
+  {"#}}}", "\n"},
+  {NULL, NULL}
+};
+
+/****/
+
+int extra_flags = 0;
+
+/****v* ROBODoc/fold 
+* NAME
+*   fold
+* FUNCTION
+*   Fold counter - true global. 
+* SOURCE
+*/
+
+int fold = 0;
+
+/****/
+
+
+
+/****f* ROBODoc/RB_Check_Fold_End [3.0h]
+* NAME
+*  RB_Check_Fold_End
+* AUTHOR
+*  PetteriK
+* FUNCTION
+*  See if a fold end is found.
+* RETURN VALUE
+*   1 if end mark is found
+* SOURCE
+*/
+
+char
+RB_Check_Fold_End (char *cur_char)
+{
+  fold_mark_t *t = fold_end_markers;
+  char found = 0;
+
+  while (found == 0 && t->start != NULL)
+    {
+      if (strncmp (t->start, cur_char, strlen (t->start)) == 0)
+       {
+         found = 1;
+         break;
+       }
+      t++;                     /* try the next fold mark string */
+    }
+  return found;
+}
+
+/*******/
+
+/****f* ROBODoc/RB_Check_Fold_Start 
+ * NAME
+ *   RB_Check_Fold_Start
+ * AUTHOR
+ *   PetteriK
+ * FUNCTION
+ *   Check if a fold start is found.
+ * RETURN VALUE
+ *   Pointer to the item body, fold mark and name skipped.
+ * SIDE EFFECTS
+ *   *found = 1 if fold mark is found. Fold name is copied to *foldname.
+ *******
+ */
+
+char *
+RB_Check_Fold_Start (char *cur_char, char *foldname, char *found)
+{
+  int n = 0;
+  fold_mark_t *t = fold_start_markers;
+
+  *found = 0;
+  while (*found == 0 && t->start != NULL)
+    {
+      if (strncmp (t->start, cur_char, strlen (t->start)) == 0)
+       {
+         *found = 1;
+         break;
+       }
+      t++;                     /* try the next fold mark string */
+    }
+  if (*found == 0)
+    {
+      return cur_char;         /* not found, get out of here */
+    }
+  cur_char += strlen (t->start);       /* skip fold mark */
+  /* get the fold name */
+  while (strncmp (t->end, cur_char, strlen (t->end)) != 0)
+    {
+      foldname[n++] = *cur_char++;
+    }
+  /* if fold mark does not end with newline, skip chars... */
+  if (t->end[0] != '\n')
+    {
+      cur_char += strlen (t->end);
+    }
+  foldname[n] = '\0';
+  while (*cur_char != '\n')
+    {
+      cur_char++;              /* not so sure about this */
+    }
+  return cur_char;
+}
diff --git a/util/robodoc/Source/folds.h b/util/robodoc/Source/folds.h
new file mode 100644 (file)
index 0000000..93d8df3
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef ROBODOC_FOLDS_H
+#define ROBODOC_FOLDS_H
+
+
+
+/****s* ROBODoc/fold_mark_t
+ * NAME
+ *   fold_mark_t
+ * FUNCTION
+ *   Handy structure for fold start/end markers.
+ * SOURCE
+ */
+
+typedef struct _fold_mark_t
+  {
+    char *start;
+    char *end;
+  }
+fold_mark_t;
+
+/*******/
+
+/****d* ROBODoc/extra_flags 
+* NAME
+*   extra_flags
+* AUTHOR
+*   PetteriK
+* FUNCTION
+*   Bitflags for extra controls. 
+* SOURCE
+*/
+
+#define FOLD     (1<<0)
+#define C_MODE   (1<<1)
+
+
+/****/
+
+extern fold_mark_t fold_start_markers[];
+extern fold_mark_t fold_end_markers[];
+extern int extra_flags;
+extern int fold;
+
+char RB_Check_Fold_End (char *cur_char);
+char *RB_Check_Fold_Start (char *cur_char, char *foldname, char *found);
+
+#endif /* ROBODOC_FOLDS_H */
diff --git a/util/robodoc/Source/generator.c b/util/robodoc/Source/generator.c
new file mode 100644 (file)
index 0000000..f93b00b
--- /dev/null
@@ -0,0 +1,1688 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "robodoc.h"
+#include "headers.h"
+#include "items.h"
+#include "folds.h"
+#include "util.h"
+#include "links.h"
+#include "generator.h"
+#include "analyser.h"
+#include <errno.h>
+
+/****f* ROBODoc/RB_Generate_Documentation [3.0h]
+ * NAME
+ *   RB_Generate_Documentation
+ * SYNOPSIS
+ *   RB_Generate_Documentation (dest_doc, name, name)
+ *
+ *   RB_Generate_Documentation (FILE *, char *, char *)
+ * FUNCTION
+ *   Generates the autodoc documentation from the list of
+ *   function headers that has been created by
+ *   RB_Analyse_Document.
+ * INPUTS
+ *   dest_doc   - Pointer to the file to which the output will be written.
+ *   src_name   - The name of the source file.
+ *   dest_name  - The name of this file.
+ * BUGS
+ *   There might be plenty.
+ * SEE ALSO
+ *   RB_Generate_Doc_Start,
+ *   RB_Generate_Doc_End,
+ *   RB_Generate_Header_Start,
+ *   RB_Generate_Header_End,
+ *   RB_Generate_Header_Name,
+ *   RB_Generate_Item_Name,
+ *   RB_Generate_Item_Doc,
+ *   RB_Generate_Item_Body .
+ * SOURCE
+ */
+
+void
+RB_Generate_Documentation (
+                           FILE * dest_doc, char *src_name, char *dest_name)
+{
+  struct RB_header *cur_header;
+  char fname[256];
+  FILE *orig_doc = dest_doc;
+
+  RB_Make_Index_Tables ();
+
+  RB_Generate_Doc_Start (dest_doc, src_name, dest_name, 1);
+
+  for (cur_header = first_header;
+       cur_header;
+       cur_header = cur_header->next_header)
+    {
+      int item_type;
+      char *next_line, *item_line = NULL;
+
+      RB_Say ("generating documentation for \"%s\"\n", cur_header->name);
+
+      if (output_mode == HTML)
+        {
+          sprintf(fname, "%s__%s.html", doc_base, cur_header->function_name);
+          dest_doc = fopen(fname, "w");
+          if (!dest_doc)
+            {
+             fprintf(stderr, "%s\n", strerror(errno));
+             exit(1);
+           }
+        }
+
+      RB_Generate_Header_Start (dest_doc, cur_header);
+
+      next_line = cur_header->contents;
+      item_type = RB_Find_Item (&next_line, &item_line);
+
+      if (item_type != NO_ITEM)
+       {
+         int old_item_type;
+         char *old_next_line;
+
+         do
+           {
+             if (course_of_action & DO_TELL)
+               printf ("[%s] ", item_names[item_type]);
+
+             if (!((item_type == SOURCE_ITEM) &&  
+                 (course_of_action & DO_NOSOURCE)))
+               RB_Generate_Item_Name (dest_doc, item_type);
+             
+             old_next_line = next_line;
+             old_item_type = item_type;
+             
+             item_type = RB_Find_Item (&next_line, &item_line);
+
+             if (!((old_item_type == SOURCE_ITEM) &&  
+                 (course_of_action & DO_NOSOURCE)))
+               RB_Generate_Item_Doc (dest_doc, dest_name,
+                                     old_next_line, item_line,
+                                     cur_header->function_name, 
+                                     old_item_type);
+           }
+         while (item_type != NO_ITEM);
+         if (course_of_action & DO_TELL)
+           putchar ('\n');
+       }
+      else
+       printf ("%s: WARNING, header \"%s\" has no items\n",
+               whoami, cur_header->name);
+
+      RB_Generate_Header_End (dest_doc, cur_header);
+
+      if (output_mode == HTML)
+        fclose(dest_doc);
+    }
+
+  dest_doc = orig_doc;
+  RB_Generate_Doc_End (dest_doc, dest_name);
+}
+
+/***** RB_Generate_Documentation ***/
+
+
+
+
+
+/****f* ROBODoc/RB_Generate_Doc_Start [3.0j]
+ * NAME
+ *   RB_Generate_Doc_Start -- Generate document header.
+ * SYNOPSIS
+ *   RB_Generate_Doc_Start (dest_doc, src_name, name, toc)
+ *
+ *   RB_Generate_Doc_Start (FILE *, char *, char *, char)
+ * FUNCTION
+ *   Generates for depending on the output_mode the text that
+ *   will be at the start of a document.
+ *   Including the table of contents.
+ * INPUTS
+ *   dest_doc - pointer to the file to which the output will
+ *              be written.
+ *   src_name - the name of the source file.
+ *   name     - the name of this file.
+ *   output_mode - global variable that indicates the output
+ *                 mode.
+ *   toc      - generate table of contens
+ * SEE ALSO
+ *   RB_Generate_Doc_End
+ * SOURCE
+ */
+
+void
+RB_Generate_Doc_Start (
+                     FILE * dest_doc, char *src_name, char *name, char toc)
+{
+  struct RB_header *cur_header;
+  int cur_len, max_len, header_nr;
+
+  switch (output_mode)
+    {
+    case AMIGAGUIDE:
+      if (strstr (name + 1, ".guide") == NULL)
+       fprintf (dest_doc, "@database %s.guide\n", name);
+      else
+       fprintf (dest_doc, "@database %s\n", name);
+      fprintf (dest_doc, "@rem Source: %s\n", src_name);
+      fprintf (dest_doc, "@rem " COMMENT_ROBODOC);
+      fprintf (dest_doc, "@rem " COMMENT_COPYRIGHT);
+      fprintf (dest_doc, "@node Main %s\n", name);
+      fprintf (dest_doc, "@{jcenter}\n");
+      fprintf (dest_doc,
+              "@{fg highlight}@{b}TABLE OF CONTENTS@{ub}@{fg text}\n\n");
+
+      max_len = 0;
+      for (cur_header = first_header;
+          cur_header;
+          cur_header = cur_header->next_header)
+       {
+         if (cur_header->name)
+           {
+             cur_len = strlen (cur_header->name);
+             if (cur_len > max_len)
+               max_len = cur_len;
+           }
+       }
+
+      for (cur_header = first_header;
+          cur_header;
+          cur_header = cur_header->next_header)
+       {
+         if (cur_header->name && cur_header->function_name)
+           {
+             fprintf (dest_doc, "@{\"%s", cur_header->name);
+
+             for (cur_len = strlen (cur_header->name);
+                  cur_len < max_len;
+                  ++cur_len)
+               fputc (' ', dest_doc);
+             fprintf (dest_doc, "\" Link \"%s\"}\n", cur_header->function_name);
+           }
+       }
+
+      fprintf (dest_doc, "@{jleft}\n");
+      fprintf (dest_doc, "@endnode\n");
+      break;
+
+    case HTML:
+      /* Append document type and title */
+      fprintf (dest_doc,
+              "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n");
+      fprintf (dest_doc, "<HTML><HEAD>\n<TITLE>%s</TITLE>\n", name);
+
+      /* append SGML-comment with document- and copyright-info. This code
+       * ensures that every line has an own comment to avoid problems with 
+       * buggy browsers */
+      fprintf (dest_doc, "<!-- Source: %s -->\n", src_name);
+      {
+       static const char copyright_text[]
+       = COMMENT_ROBODOC COMMENT_COPYRIGHT;
+       size_t i = 0;
+       char previous_char = '\n';
+       char current_char = copyright_text[i];
+
+       while (current_char)
+         {
+           if (previous_char == '\n')
+             {
+               fprintf (dest_doc, "<!-- ");
+             }
+           if (current_char == '\n')
+             {
+               fprintf (dest_doc, " -->");
+             }
+           else if ((current_char == '-') && (previous_char == '-'))
+             {
+               /* avoid "--" inside SGML-comment, and use "-_" instead; this
+                * looks a bit strange, but one should still be able to figure 
+                * out what is meant when reading the output */
+               current_char = '_';
+             }
+           fputc (current_char, dest_doc);
+           i += 1;
+           previous_char = current_char;
+           current_char = copyright_text[i];
+         }
+      }
+
+      /* append heading and start list of links to functions */
+      fprintf (dest_doc, "</HEAD><BODY BGCOLOR=\"#FFFFFF\">\n");
+#if 0
+      fprintf (dest_doc, "<A NAME=\"%s\">Generated from %s</A> with ROBODoc v"
+              VERSION
+              " on ",
+              src_name, src_name);
+      RB_TimeStamp (dest_doc);
+#endif
+      fprintf (dest_doc, "<BR>\n");
+      if (toc)
+       {
+         char iname[256];
+         FILE *index;
+         int start = 0;
+
+         /* do toc if not in fold */
+#if 0
+         fprintf (dest_doc,
+                  "<H3 ALIGN=\"center\">TABLE OF CONTENTS</H3>\n");
+         fprintf (dest_doc, "<OL>\n");
+#endif
+
+         /* Generate quick index file, for fast referencing */
+         sprintf(iname, "%s__index.tmpl", doc_base);
+          index = fopen(iname, "w");
+          if (!index)
+           {
+             fprintf(stderr, "%s\n", strerror(errno));
+             exit(1);
+           }
+
+         for (cur_header = first_header;
+              cur_header;
+              cur_header = cur_header->next_header)
+           {
+             char fname[256];
+
+             sprintf(fname, "%s__%s.html", RB_FilePart(doc_base), 
+                     cur_header->function_name);
+
+             if (cur_header->name && cur_header->function_name)
+               {
+                 if (start == 0) 
+                   {
+                     int item_type;
+                     char *next_line, *item_line = NULL;
+                   
+                     RB_Generate_Header_Start (dest_doc, cur_header);
+
+                     next_line = cur_header->contents;
+                     item_type = RB_Find_Item (&next_line, &item_line);
+                     
+                     if (item_type != NO_ITEM)
+                       {
+                         int old_item_type;
+                         char *old_next_line;
+                         
+                         do
+                           {
+                             if (course_of_action & DO_TELL)
+                               printf ("[%s] ", item_names[item_type]);
+                             
+                             if (!((item_type == SOURCE_ITEM) &&
+                                   (course_of_action & DO_NOSOURCE)))
+                               RB_Generate_Item_Name (dest_doc, item_type);
+                             
+                             old_next_line = next_line;
+                             old_item_type = item_type;
+                             
+                             item_type = RB_Find_Item (&next_line, 
+                                                       &item_line);
+                             
+                             if (!((old_item_type == SOURCE_ITEM) &&
+                                   (course_of_action & DO_NOSOURCE)))
+                               RB_Generate_Item_Doc(dest_doc, name,
+                                                    old_next_line, item_line,
+                                                    cur_header->function_name,
+                                                    old_item_type);
+                           }
+                         while (item_type != NO_ITEM);
+                         if (course_of_action & DO_TELL)
+                           putchar ('\n');
+                       }
+
+                     if (index)
+                       {
+                         fprintf (index, "<A HREF=\"%s\"><IMG SRC=\"index_pic.gif\" BORDER=\"0\" ALT=\"\">%s</A><BR>\n",
+                                  name, cur_header->function_name);
+                       }
+
+                     start = 1;
+                   }
+                 else
+                   {
+                     fprintf (dest_doc, "<LI><A HREF=\"%s\">%s</A>\n",
+                              fname, cur_header->function_name);
+                     if (index)
+                       fprintf (index, "<A HREF=\"%s\"><IMG SRC=\"index_pic.gif\" BORDER=\"0\" ALT=\"\">%s</A><BR>\n",
+                                fname, cur_header->function_name);
+                   }
+               }
+           }
+
+#if 0
+         fprintf (dest_doc, "</OL>\n");
+#endif
+
+         if (index)
+           fclose(index);
+       }
+      break;
+
+    case LATEX:
+      fprintf (dest_doc, "%% Document: %s\n", name);
+      fprintf (dest_doc, "%% Source: %s\n", src_name);
+      fprintf (dest_doc, "%% " COMMENT_ROBODOC);
+      fprintf (dest_doc, "%% " COMMENT_COPYRIGHT);
+      if (course_of_action & DO_SINGLEDOC) {
+       fprintf (dest_doc, "\\section{%s}\n", src_name);
+      } else {
+       fprintf (dest_doc, "\\documentclass{article}\n");
+        fprintf (dest_doc, "\\usepackage{makeidx}\n");
+       fprintf (dest_doc, "\\oddsidemargin  0.15 in\n");
+       fprintf (dest_doc, "\\evensidemargin 0.35 in\n");
+       fprintf (dest_doc, "\\marginparwidth 1 in   \n");
+       fprintf (dest_doc, "\\oddsidemargin 0.25 in \n");
+       fprintf (dest_doc, "\\evensidemargin 0.25 in\n");
+       fprintf (dest_doc, "\\marginparwidth 0.75 in\n");
+       fprintf (dest_doc, "\\textwidth 5.875 in\n");
+       
+       fprintf (dest_doc, "\\setlength{\\parindent}{0in}\n");
+       fprintf (dest_doc, "\\setlength{\\parskip}{.08in}\n\n");
+       
+       /* changed default header to use boldface (vs slant) */
+       fprintf (dest_doc, "\\pagestyle{headings}\n");
+
+       if (document_title) {
+         fprintf (dest_doc, "\\title{%s}\n", 
+                  document_title);
+       } else {
+         fprintf (dest_doc, "\\title{API Reference}\n");
+       }
+       fprintf (dest_doc, "\\author{%s}\n", COMMENT_ROBODOC);
+       fprintf (dest_doc, "\\makeindex\n");
+       fprintf (dest_doc, "\\begin{document}\n");
+       fprintf (dest_doc, "\\maketitle\n");
+       /* autogenerate table of contents! */
+       fprintf (dest_doc, "\\printindex\n");
+       fprintf (dest_doc, "\\tableofcontents\n");
+       fprintf (dest_doc, "\\newpage\n");
+       /* trick to disable the autogenerated \newpage */
+       fprintf (dest_doc, "\n");
+      }
+      break;
+
+    case RTF:
+      {
+       char *cook_link;
+
+       /* RTF header */
+       fprintf (dest_doc, "{\\rtf1\\ansi \\deff0"
+                "{\\fonttbl;"
+                "\\f0\\fswiss MS Sans Serif;"
+                "\\f1\\fmodern Courier New;"
+                "\\f2\\ftech Symbol;"
+                "}"
+                "{\\colortbl;"
+                "\\red255\\green255\\blue255;"
+                "\\red0\\green0\\blue0;"
+                "\\red0\\green0\\blue255;"
+                "}");
+
+       /* RTF document info */
+       fprintf (dest_doc, "{\\info"
+                "{\\title %s}"
+                "{\\comment\n"
+                " Source: %s\n"
+                " " COMMENT_ROBODOC
+                " " COMMENT_COPYRIGHT
+                "}"
+                "}", name, src_name);
+
+       /* RTF document format */
+       fprintf (dest_doc, "{\\margl1440\\margr1440}\n");
+
+       /* RTF document section */
+       fprintf (dest_doc, "\\f0\\cb1\\cf3\\fs28\\b1\\qc"
+                "{\\super #{\\footnote{\\super #}%s_TOC}}"
+                "{\\super ${\\footnote{\\super $}Contents}}"
+                "{TABLE OF CONTENTS}\\ql\\b0\\fs20\\cf2\\par\n", src_name);
+       for (cur_header = first_header;
+            cur_header;
+            cur_header = cur_header->next_header)
+         {
+           if (cur_header->name && cur_header->function_name)
+             {
+               cook_link = RB_CookStr (cur_header->function_name);
+               fprintf (dest_doc, "{\\uldb %s}{\\v %s}\\line\n",
+                        cur_header->name, cook_link);
+               free (cook_link);
+             }
+         }
+       fprintf (dest_doc, "\\par\n");
+      }
+      break;
+    case ASCII:
+      if (course_of_action & DO_TOC)
+       {
+         fprintf (dest_doc, "TABLE OF CONTENTS\n");
+         for (cur_header = first_header, header_nr = 1;
+              cur_header;
+              cur_header = cur_header->next_header, header_nr++)
+           {
+             if (cur_header->name && cur_header->function_name)
+               {
+                 fprintf (dest_doc, "%4.4d %s\n",
+                          header_nr, cur_header->name);
+               }
+           }
+         fputc ('\f', dest_doc);
+       }
+    default:
+      break;
+    }
+}
+
+/***************/
+
+
+/****f* ROBODoc/RB_Generate_Doc_End [3.0h]
+ * NAME
+ *   RB_Generate_Doc_End -- generate document trailer.
+ * SYNOPSIS
+ *   RB_Generate_Doc_End (dest_doc, name)
+ *
+ *   RB_Generate_Doc_End (FILE *, char *)
+ * FUNCTION
+ *   Generates for depending on the output_mode the text that
+ *   will be at the end of a document.
+ * INPUTS
+ *   dest_doc - pointer to the file to which the output will
+ *              be written.
+ *   name     - the name of this file.
+ *   output_mode - global variable that indicates the output
+ *                 mode.
+ * NOTES
+ *   Doesn't do anything with its arguments, but that might
+ *   change in the future.
+ * BUGS
+ * SOURCE
+ */
+
+void
+RB_Generate_Doc_End (FILE * dest_doc, char *name)
+{
+  switch (output_mode)
+    {
+    case AMIGAGUIDE:
+      fputc ('\n', dest_doc);
+      break;
+    case HTML:
+      fprintf (dest_doc, "</BODY></HTML>\n");
+      break;
+    case LATEX:
+      if (!(course_of_action & DO_SINGLEDOC)) { 
+       fprintf (dest_doc, "\\end{document}\n");
+      }
+      break;
+    case RTF:
+      fputc ('}', dest_doc);
+      break;
+    case ASCII:
+      break;
+    }
+}
+
+/************/
+
+
+/****f* ROBODoc/RB_Generate_Header_Start [3.0h]
+ * NAME
+ *   RB_Generate_Header_Start -- generate header start text.
+ * SYNOPSIS
+ *  void RB_Generate_Header_Start (dest_doc, cur_header)
+ *
+ *  void RB_Generate_Header_Start (FILE *, struct RB_header *)
+ * FUNCTION
+ *   Generates depending on the output_mode the text that
+ *   will be at the end of each header.
+ * INPUTS
+ *   dest_doc - pointer to the file to which the output will
+ *              be written.
+ *   cur_header - pointer to a RB_header structure.
+ * SEE ALSO
+ *   RB_Generate_Header_End
+ * SOURCE
+ */
+
+void
+RB_Generate_Header_Start (FILE * dest_doc, struct RB_header *cur_header)
+{
+  char *cook_link;
+
+  switch (output_mode)
+    {                          /* switch by *koessi */
+    case AMIGAGUIDE:
+      if (cur_header->name && cur_header->function_name)
+       {
+         fprintf (dest_doc, "@Node \"%s\" \"%s\"\n",
+                  cur_header->function_name,
+                  cur_header->name);
+         fprintf (dest_doc, "%s", att_start_command[MAKE_SHINE][output_mode]);
+         fprintf (dest_doc, "%s", cur_header->name);
+         fprintf (dest_doc, "%s", att_stop_command[MAKE_SHINE][output_mode]);
+         fprintf (dest_doc, "\n\n");
+       }
+      break;
+    case HTML:
+      if (cur_header->name && cur_header->function_name)
+       {
+#if 0
+         fprintf (dest_doc, "<HR>\n");
+#endif
+         if (cur_header->type == FUNCTION_HEADER)
+           fprintf (dest_doc, 
+                    "\n<FONT SIZE=\"+2\" COLOR=\"#000055\"><B>"
+                    "Function <A NAME=\"%s\">%s</A>"
+                    "</FONT></B><BR><BR>\n\n",
+                    cur_header->function_name,
+                    cur_header->function_name);
+         else if (cur_header->type == STRUCT_HEADER)
+           fprintf (dest_doc, 
+                    "\n<FONT SIZE=\"+2\" COLOR=\"#000055\"><B>"
+                    "Structure <A NAME=\"%s\">%s</A>"
+                    "</FONT></B><BR><BR>\n\n",
+                    cur_header->function_name,
+                    cur_header->function_name);
+         else if (cur_header->type == VARIABLE_HEADER)
+           fprintf (dest_doc, 
+                    "\n<FONT SIZE=\"+2\" COLOR=\"#000055\"><B>"
+                    "Variable <A NAME=\"%s\">%s</A>"
+                    "</FONT></B><BR><BR>\n\n",
+                    cur_header->function_name,
+                    cur_header->function_name);
+         else
+           fprintf (dest_doc, 
+                    "\n<FONT SIZE=\"+2\" COLOR=\"#000055\"><B>"
+                    "<A NAME=\"%s\">%s</A>"
+                    "</FONT></B><BR><BR>\n\n",
+                    cur_header->function_name,
+                    cur_header->function_name);
+       }
+      break;
+    case LATEX:
+      cook_link = RB_CookStr (cur_header->name);
+      if (!(course_of_action & DO_SINGLEDOC)) {
+       fprintf (dest_doc, "\\newpage\n");
+      }
+      fprintf (dest_doc, "\\subsection{%s}\n", cook_link);
+      free (cook_link);
+      if (cur_header->function_name) {
+       cook_link = RB_CookStr (cur_header->function_name);
+       fprintf (dest_doc, "\\index{unsorted!%s}\\index{%s!%s}\n", cook_link, 
+                RB_header_type_names[cur_header->type], cook_link);
+       free (cook_link);
+      }
+      break;
+    case RTF:
+      if (cur_header->name && cur_header->function_name)
+       {
+         cook_link = RB_CookStr (cur_header->function_name);
+         fprintf (dest_doc, "\\page"
+                  "{\\super #{\\footnote{\\super #}%s}}"
+                  "{\\super ${\\footnote{\\super $}%s}}"
+                  "\\cf3 %s\\cf2\\line\n",
+                  cur_header->function_name,
+                  cur_header->name,
+                  cur_header->name);
+         free (cook_link);
+       }
+      break;
+    case ASCII:
+      {
+       fprintf (dest_doc, "%s", att_start_command[MAKE_SHINE][output_mode]);
+       fprintf (dest_doc, "%s", cur_header->name);
+       fprintf (dest_doc, "%s", att_stop_command[MAKE_SHINE][output_mode]);
+       fprintf (dest_doc, "\n\n");
+      }
+      break;
+    }
+}
+
+/******/
+
+
+/****f* ROBODoc/RB_Generate_Header_End [3.0h]
+ * NAME
+ *   RB_Generate_Header_End
+ * SYNOPSIS
+ *   void RB_Generate_Header_End (dest_doc, cur_header)
+ *
+ *   void RB_Generate_Header_End (FILE *, struct RB_header *)
+ * FUNCTION
+ *   Generates for depending on the output_mode the text that
+ *   will be at the end of a header.
+ * INPUTS
+ *   dest_doc - pointer to the file to which the output will
+ *              be written.
+ *   cur_header - pointer to a RB_header structure.
+ * SEE ALSO
+ *   RB_Generate_Header_Start
+ * SOURCE
+ */
+
+void
+RB_Generate_Header_End (FILE * dest_doc, struct RB_header *cur_header)
+{
+  switch (output_mode)
+    {                          /* switch by *koessi */
+    case AMIGAGUIDE:
+      if (cur_header->name && cur_header->function_name)
+       fprintf (dest_doc, "@endnode\n");
+      break;
+    case HTML:
+    case LATEX:
+      fputc ('\n', dest_doc);
+      break;
+    case RTF:
+      fprintf (dest_doc, "\\par\n");
+      break;
+    case ASCII:
+      fputc ('\f', dest_doc);
+    default:
+      break;
+    }
+}
+
+/*****/
+
+
+/****f* ROBODoc/RB_Generate_Header_Name [3.0c]
+ * NAME
+ *   RB_Generate_Header_Name
+ * SYNOPSIS
+ *   RB_Generate_Header_Name (dest_doc, name)
+ *
+ *   RB_Generate_Header_Name (FILE *, char *)
+ * INPUTS
+ *  dest_doc - pointer to the file to which the output will
+ *             be written.
+ *  name - pointer to the header name.
+ * SOURCE
+ */
+
+void
+RB_Generate_Header_Name (FILE * dest_doc, char *name)
+{
+  char format_str[] = "%s";
+
+  fprintf (dest_doc, format_str, att_start_command[MAKE_SHINE][output_mode]);
+  fprintf (dest_doc, format_str, name);
+  fprintf (dest_doc, format_str, att_stop_command[MAKE_SHINE][output_mode]);
+  fprintf (dest_doc, "\n\n");
+}
+
+/*** RB_Generate_Header_Name ***/
+
+
+/****** ROBODoc/RB_Generate_Item_Name [2.01]
+ * NAME
+ *   RB_Generate_Item_Name -- fast&easy
+ * SYNOPSIS
+ *   void RB_Generate_Item_Name( FILE * dest_doc, int item_type )
+ * FUNCTION
+ *   write the items name to the doc
+ * INPUTS
+ *   FILE * dest_doc         -- document in progress
+ *   int item_type           -- this leads to the name and makes colors
+ * AUTHOR
+ *   Koessi
+ * NOTES
+ *   uses globals: output_mode, item_names[]
+ * SOURCE
+ */
+
+void
+RB_Generate_Item_Name (FILE * dest_doc, int item_type)
+{
+  char format_str[] = "%s";
+
+  if (item_attributes[item_type] & ITEM_NAME_LARGE_FONT)
+    {
+      fprintf (dest_doc, format_str,
+              att_start_command[MAKE_LARGE][output_mode]);
+      fprintf (dest_doc, format_str,
+              att_start_command[MAKE_BOLD][output_mode]);
+      if (output_mode == HTML)
+       fprintf (dest_doc, "\n<FONT COLOR=\"#000055\">");
+      fprintf (dest_doc, format_str, item_names[item_type]);
+      if (output_mode == HTML)
+       fprintf (dest_doc, "\n</FONT>");
+      fprintf (dest_doc, format_str,
+              att_stop_command[MAKE_BOLD][output_mode]);
+      fprintf (dest_doc, format_str,
+              att_stop_command[MAKE_LARGE][output_mode]);
+    }
+  else
+    fprintf (dest_doc, format_str, item_names[item_type]);
+
+  fputc ('\n', dest_doc);
+}
+
+/*****/
+
+
+
+/****f* ROBODoc/RB_Generate_Item_Doc [3.0j]
+ * NAME
+ *   RB_Generate_Item_Doc
+ * SYNOPSIS
+ *   void RB_Generate_Item_Doc(FILE * dest_doc, char *dest_name,
+ *                             char *begin_of_item,
+ *                             char *end_of_item,
+ *                             char *function_name,
+ *                             int item_type)
+ * FUNCTION
+ *   Generates the body text of an item, applying predefined attributes
+ *   to the text.
+ * NOTES
+ *   Body text is always non-proportional for several reasons:
+ *   1) text is rarely written with prop spacing and text wrapping
+ *      in mind -- e.g., see SYNOPSIS above
+ *   2) source code looks better
+ *   3) it simplifies LaTeX handling
+ * SOURCE
+ */
+
+void
+RB_Generate_Item_Doc (FILE * dest_doc, char *dest_name,
+                     char *begin_of_item,
+                     char *end_of_item,
+                     char *function_name,
+                     int item_type)
+{
+  char format_str[] = "%s";
+
+  if (begin_of_item == end_of_item)
+    {
+      switch (output_mode)
+       {
+       case HTML:
+         fprintf (dest_doc, "<BR>\n");
+         break;
+       case LATEX:
+         fprintf (dest_doc, "\\\\\n");
+         break;
+       case RTF:
+         fprintf (dest_doc, "\n");
+         break;
+       default:
+         break;
+       }
+      return;
+    }
+  /* For text body in HTML, change to non-prop _before_ changing font
+   * style. * To conform to DTD, this avoids <B><PRE> and instead uses
+   * <PRE><B> */
+  if (output_mode == HTML)
+    {
+      fprintf (dest_doc, "<PRE>");
+    }
+  /* change font style */
+  if (item_attributes[item_type] & TEXT_BODY_LARGE_FONT)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_LARGE][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_ITALICS)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_ITALICS][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_NON_PROP)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_NON_PROP][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_SMALL_FONT)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_SMALL][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_BOLD)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_BOLD][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_UNDERLINE)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_UNDERLINE][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_SHINE)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_SHINE][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_DEFAULT)
+    fprintf (dest_doc, format_str,
+            att_start_command[MAKE_DEFAULT][output_mode]);
+
+  /* 
+   * For some modes, the text body is always non-prop
+   */
+  switch (output_mode)
+    {
+    case LATEX:
+      fprintf (dest_doc, "\\begin{verbatim}\n");
+      break;
+    case RTF:
+      fprintf (dest_doc, "{\\f1{}");
+      break;
+    default:
+      break;
+    }
+
+  RB_Generate_Item_Body (dest_doc, dest_name, begin_of_item, end_of_item,
+                        function_name, item_type, 0);
+
+  switch (output_mode)
+    {
+    case LATEX:
+      /* split the text so LaTeX doesn't get confused ;) */
+      fprintf (dest_doc, "\\" "end{verbatim}\n");
+      break;
+    case RTF:
+      fputc ('}', dest_doc);
+    default:
+      break;
+    }
+
+  /* restore font style */
+  if (item_attributes[item_type] & TEXT_BODY_SHINE)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_SHINE][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_UNDERLINE)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_UNDERLINE][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_BOLD)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_BOLD][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_SMALL_FONT)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_SMALL][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_NON_PROP)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_NON_PROP][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_ITALICS)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_ITALICS][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_LARGE_FONT)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_LARGE][output_mode]);
+  if (item_attributes[item_type] & TEXT_BODY_DEFAULT)
+    fprintf (dest_doc, format_str,
+            att_stop_command[MAKE_DEFAULT][output_mode]);
+
+  if (output_mode != HTML)
+    {
+      fputc ('\n', dest_doc);
+    }
+  /* for HTML, switch back to prop-font after restoring font style */
+  if (output_mode == HTML)
+    {
+      fprintf (dest_doc, "</PRE>");
+    }
+}
+
+/******/
+
+
+
+/****f* ROBODoc/RB_Generate_Item_Body [3.0h]
+ * NAME
+ *  RB_Generate_Item_Body
+ * SYNOPSIS
+ *  char * RB_Generate_Item_Body(FILE * dest_doc, char *dest_name,
+ *                             char *begin_of_item, char *end_of_item,
+ *                             char *function_name,
+ *                             int   item_type, int tabs_to_skip)
+ *
+ * FUNCTION
+ *   Generates body of an item in output-specific form
+ * INPUTS
+ *   dest_doc      - pointer to the file to which
+ *                   the output will be written.
+ *   dest_name     - the name of this file.
+ *   begin_of_item -
+ *   end_of_item   -
+ *   function_name -
+ *   item_type     -
+ *   tabs_to_skip  - how many tabs to skip in this fold.
+ * BUGS
+ *   o Unbalanced fold marks lead to crash.
+ * NOTES
+ *   o Almost completely rewritten by koessi
+ *   o Almost completely Re-Rewritten by Slothouber :)
+ *   o Folding mode by PetteriK.
+ *   o Linking fixed inside folds / PetteriK 08.04.2000 
+ * SOURCE
+ */
+
+char *
+RB_Generate_Item_Body (FILE * dest_doc, char *dest_name,
+                      char *begin_of_item, char *end_of_item,
+                      char *function_name,
+                      int item_type, int tabs_to_skip)
+{
+  char *cur_char, old_char, c;
+  int html_incr;
+  char fname[128], foldname[128];
+  static int in_fold = 0;      /* PetteriK 08.04.2000 */
+
+  cur_char = begin_of_item;
+
+  if (item_type == SOURCE_ITEM)
+    {
+      /* skip end_comment_marker */
+      for (; *cur_char && *cur_char != '\n'; cur_char++);
+
+      /* skip blank lines leading up to source code */
+      while (*cur_char == '\n')
+       cur_char++;
+
+      /* trim blanks following source code */
+      do
+       {
+         end_of_item--;
+       }
+      while (end_of_item > cur_char && isspace (*end_of_item));
+      end_of_item++;           /* advance 1 for placement of the NUL */
+    }
+  old_char = *end_of_item;
+  *end_of_item = '\0';
+
+  for (; *cur_char; cur_char++)
+    {
+      int tb = tab_size;
+      int do_search = TRUE;
+      int was_link = FALSE;
+      int tabs = 0;
+
+      if (item_type != SOURCE_ITEM)
+       {
+          /* Skip empty lines */
+          while (*cur_char == '\n') {
+                cur_char++;
+          }
+         cur_char = RB_Skip_Remark_Marker (cur_char);
+       }
+      else
+       {
+         /* indent source */
+         switch (output_mode)
+           {
+           case RTF:
+             fprintf (dest_doc, "\\tab ");
+             break;
+
+           case AMIGAGUIDE:
+           case HTML:
+           case LATEX:
+           default:
+             fprintf (dest_doc, "    ");
+           }
+       }
+
+      while (((c = *cur_char) != '\0') && (c != '\n'))
+       {
+         char *label_name, *file_name;
+         char found = 0;
+         int tmp;
+
+         if (!do_search)
+           {
+             if (!isalnum (c) && (c != '_'))
+               {
+                 do_search = TRUE;
+               }
+           }
+         else
+           {
+             if (isalpha (c) || (c == '_'))
+               {
+                 if (((was_link = RB_Find_Link (cur_char, &label_name,
+                                                &file_name)) == FALSE))
+                   {
+                     do_search = FALSE;
+                   }
+               }
+             else
+               was_link = FALSE;
+           }
+
+         if (!was_link)
+           {
+             switch (output_mode)
+               {
+               case AMIGAGUIDE:
+                 switch (c)
+                   {
+                   case '\n':
+                     --cur_char;
+                     break;
+                   case '\t':
+                     for (tb %= tab_size; tb < tab_size; ++tb)
+                       fputc (' ', dest_doc);
+                     break;
+                   case '@':
+                     fprintf (dest_doc, "\\@");
+                     tb++;
+                     break;
+                   case '\\':
+                     fprintf (dest_doc, "\\\\");
+                     tb++;
+                     break;
+                   default:
+                     fputc (c, dest_doc);
+                     tb++;
+                   }
+                 break;
+
+               case HTML:
+                 /* PetteriK 26.07.1999 */
+                 if (extra_flags & FOLD)
+                   {
+                     cur_char = RB_Check_Fold_Start (cur_char,
+                                                     foldname, &found);
+                   }
+                 if ((extra_flags & FOLD) && found)
+                   {
+                     FILE *fp;
+
+                     RB_Say ("fold name %s\n", foldname);
+                     RB_Say ("fold begin %d\n", ++fold);
+                     RB_Say ("tabs %d\n", tabs);
+                     sprintf (fname, "%s_fold_%d.html", doc_base, fold);
+                     RB_Say ("opening file %s\n", fname);
+                     fp = fopen (fname, "w");
+                     RB_Generate_Doc_Start (fp, foldname, foldname, 0);
+                     fprintf (fp, "<PRE>\n");
+                     fprintf (dest_doc, "<A HREF=\"%s\">... %s</A>",
+                              fname, foldname);
+                     in_fold++;        /* PetteriK 08.04.2000 */
+                     cur_char = RB_Generate_Item_Body (fp, dest_name,
+                                                     cur_char, end_of_item,
+                                                       function_name,
+                                                       item_type, tabs);
+                     in_fold--;        /* PetteriK 08.04.2000 */
+                     /* skip chars until newline */
+                     while (*cur_char != '\n')
+                       {
+                         cur_char++;
+                       }
+                     cur_char--;
+                     fprintf (fp, "\n</PRE>\n");
+                     RB_Generate_Doc_End (fp, foldname);
+                     fclose (fp);
+                   }
+                 else if ((extra_flags & FOLD) && RB_Check_Fold_End (cur_char))
+                   {
+                     RB_Say ("fold end found\n");
+                     return cur_char;
+                   }
+                 else if ((html_incr = RB_HTML_Extra (dest_doc,
+                                                      item_type, cur_char)))
+                   {
+                     cur_char += html_incr;
+                   }
+                 else
+                   {
+                     switch (c)
+                       {
+                       case '\n':
+                         --cur_char;
+                         break;
+                       case '\t':
+                         if (extra_flags & FOLD)
+                           {
+                             if (tabs >= tabs_to_skip)
+                               {
+                                 for (tb %= tab_size; tb < tab_size; ++tb)
+                                   {
+                                     fputc (' ', dest_doc);
+                                   }
+                               }
+                             tabs++;
+                           }
+                         else
+                           {
+                             for (tb %= tab_size; tb < tab_size; ++tb)
+                               {
+                                 fputc (' ', dest_doc);
+                               }
+                           }
+                         break;
+                       case '<':
+                         fprintf (dest_doc, "&lt;");
+                         tb++;
+                         break;
+                       case '>':
+                         fprintf (dest_doc, "&gt;");
+                         tb++;
+                         break;
+                       case '&':
+                         fprintf (dest_doc, "&amp;");
+                         tb++;
+                         break;
+                       default:
+                         fputc (c, dest_doc);
+                         tb++;
+                       }
+                   }
+                 break;        /* end case HTML */
+
+               case LATEX:
+                 switch (c)
+                   {
+                   case '\n':
+                     --cur_char;
+                     break;
+                   case '\t':
+                     for (tb %= tab_size; tb < tab_size; ++tb)
+                       fputc (' ', dest_doc);
+                     break;
+#if 0
+                     /* not used in LaTeX's verbatim environment */
+                   case '$':
+                   case '&':
+                   case '%':
+                   case '#':
+                   case '_':
+                   case '{':
+                   case '}':
+                     fputc ('\\', dest_doc);
+                     fputc (c, dest_doc);
+                     tb++;
+                     break;
+                   case '\\':
+                     fprintf (dest_doc, "$\\backslash$");
+                     tb++;
+                     break;
+                   case '~':
+                     fprintf (dest_doc, "$\\tilde$");
+                     tb++;
+                     break;
+                   case '^':
+                     fprintf (dest_doc, "$\\,\\!^{\\sim}$");
+                     tb++;
+                     break;
+#endif
+                   default:
+                     fputc (c, dest_doc);
+                     tb++;
+                   }
+                 break;
+
+               case RTF:
+                 switch (c)
+                   {
+                   case '\n':
+                     --cur_char;
+                     break;
+                   case '\t':
+                     for (tb %= tab_size; tb < tab_size; ++tb)
+                       fputc (' ', dest_doc);
+                     break;
+                   case '\\':
+                   case '{':
+                   case '}':
+                     fputc ('\\', dest_doc);
+                     fputc (c, dest_doc);
+                     tb++;
+                     break;
+                   default:
+                     fputc (c, dest_doc);
+                     tb++;
+                   }
+                 break;
+
+               default:
+                 fputc (c, dest_doc);
+                 tb++;
+               }
+             cur_char++;
+           }
+         else
+           {
+             switch (output_mode)
+               {
+               case AMIGAGUIDE:
+                 if (file_name && strcmp (file_name, dest_name))
+                   fprintf (dest_doc, "@{\"%s\" Link \"%s/%s\"}",
+                            label_name, file_name, label_name);
+                 else
+                   {
+                     if (strcmp (label_name, function_name))
+                       fprintf (dest_doc, "@{\"%s\" Link \"%s\"}",
+                                label_name, label_name);
+                     else
+                       {
+                         fprintf (dest_doc, "%s",
+                                att_start_command[MAKE_BOLD][output_mode]);
+                         fprintf (dest_doc, "%s", label_name);
+                         fprintf (dest_doc, "%s",
+                                  att_stop_command[MAKE_BOLD][output_mode]);
+                       }
+                   }
+                 break;
+
+               case HTML:
+                 /* Include the file name in the link if we are in fold
+                  * PetteriK 08.04.2000 
+                  */
+                 if (in_fold)
+                   {
+                     /* We are in fold, always use the file name in the link, 
+                      * in file_name == NULL (i.e. the label is in the current file 
+                      * that we are processing), refer to value in dest_name. 
+                      * This also makes sure that we link correctly if function_name
+                      * is the same as label_name.
+                      */
+                     fprintf (dest_doc, "<A HREF=\"%s#%s\">%s</A>",
+                              (file_name ? file_name : dest_name),
+                              label_name, label_name);
+                   }
+                 else if (file_name && strcmp (file_name, dest_name))
+                   {
+                     fprintf (dest_doc, "<A HREF=\"%s#%s\">%s</A>",
+                              file_name, label_name, label_name);
+                   }
+                 else
+                   {
+                     if (strcmp (label_name, function_name))
+                       {
+#if 0
+                         fprintf (dest_doc, "<A HREF=\"#%s\">%s</A>",
+                                  label_name, label_name);
+#endif
+                         fprintf (dest_doc, "<A HREF=\"%s__%s.html\">%s</A>",
+                                  RB_FilePart(doc_base), label_name, 
+                                              label_name);
+                       }
+                     else
+                       {
+                         fprintf (dest_doc, "%s",
+                                att_start_command[MAKE_BOLD][output_mode]);
+                         fprintf (dest_doc, "%s", label_name);
+                         fprintf (dest_doc, "%s",
+                                  att_stop_command[MAKE_BOLD][output_mode]);
+                       }
+                   }
+                 break;
+
+               case RTF:
+                 if (strcmp (label_name, function_name))
+                   {
+                     char *cook_link;
+
+                     cook_link = RB_CookStr (label_name);
+                     fprintf (dest_doc, "{\\uldb %s}{\\v %s}",
+                              label_name, cook_link);
+                     free (cook_link);
+                   }
+                 else
+                   {
+                     fprintf (dest_doc, "%s",
+                              att_start_command[MAKE_BOLD][output_mode]);
+                     fprintf (dest_doc, "%s", label_name);
+                     fprintf (dest_doc, "%s",
+                              att_stop_command[MAKE_BOLD][output_mode]);
+                   }
+                 break;
+               default:
+                 fprintf (dest_doc, "%s", label_name);
+               }
+             tmp = strlen (label_name);
+             cur_char += tmp;
+             tb += tmp;
+           }                   /* end if */
+       }
+
+      if (*cur_char)
+       {
+         if (output_mode == RTF)
+           fprintf (dest_doc, "\\line");
+         fputc ('\n', dest_doc);
+         tabs = 0;
+       }
+    }
+  *end_of_item = old_char;
+  return (char *) 0;
+}
+
+
+/***************/
+
+
+/****f* ROBODoc/RB_HTML_Extra
+* NAME
+*   RB_HTML_Extra
+* AUTHOR
+*   PetteriK
+* HISTORY
+*   05/15/2000 Added mailto: support (Guillaume Etorre)
+* FUNCTION
+*   Check and process embedded hyperlinks.
+* RETURN VAL* FUNCTION
+*   Check and process embedded hyperlinks.
+* RETURN VALUE
+*   Number of chars processed from *cur_char
+* TODO
+*   Flag for C and other grammars.
+* BUGS
+*   As the documentation generated for this functions shows, if
+*   the C source code contains a string with " / * " in it, this
+*   function fails :)
+* SOURCE
+*/
+
+int
+RB_HTML_Extra (FILE * dest_doc, int item_type, char *cur_char)
+{
+  int res = 0;
+  char link[1024];
+
+  if (strncmp ("http://", cur_char, strlen ("http://")) == 0)
+    {
+      sscanf (cur_char, "%s", link);
+      RB_Say ("found link %s\n", link);
+      res = (strlen (link) - 1);
+      fprintf (dest_doc, "<A HREF=\"%s\">%s</A>", link, link);
+    }
+  else if (strncmp ("href:", cur_char, strlen ("href:")) == 0)
+    {
+      /* handy in relative hyperlink paths, e.g. href:../../modulex/ */
+      sscanf ((cur_char + strlen ("href:")), "%s", link);
+      RB_Say ("found link %s\n", link);
+      res = (strlen (link) + strlen ("href:") - 1);
+      fprintf (dest_doc, "<A HREF=\"%s\">%s</A>", link, link);
+    }
+  else if (strncmp ("mailto:", cur_char, strlen ("mailto:")) == 0)
+    {
+      sscanf ((cur_char + strlen ("mailto:")), "%s", link);
+      RB_Say ("found mail to %s\n", link);
+      res = (strlen (link) + strlen ("mailto:") - 1);
+      fprintf (dest_doc, "<A HREF=\"mailto:%s\">%s</A>", link, link);
+    }
+  else if ((extra_flags & C_MODE) && (item_type == SOURCE_ITEM) &&
+          (strncmp ("/*", cur_char, 2) == 0))
+    {
+      /* start of C comment */
+      fprintf (dest_doc, "<FONT COLOR = \"#FF0000\">/*");
+      res = 1;
+    }
+  else if ((extra_flags & C_MODE) && (item_type == SOURCE_ITEM) &&
+          (strncmp ("*/", cur_char, 2) == 0))
+    {
+      /* end of C comment */
+      fprintf (dest_doc, "*/</FONT>");
+      res = 1;
+    }
+  return res;
+}
+
+/**********/
+
+
+/****f* ROBODoc/RB_Generate_Index
+ * NAME
+ *   RB_Generate_Index -- generate index file based on xref files.
+ * SYNOPSIS
+ *   void RB_Generate_Index(FILE *dest, char *name) 
+ * FUNCTION
+ *   Create a master index file. It contains pointers to the
+ *   documentation generated for each source file, as well as all
+ *   "objects" found in the source files.
+ ********
+ */
+
+void
+RB_Generate_Index (FILE * dest, char *source)
+{
+  RB_Slow_Sort_Links ();
+
+  switch (output_mode)
+    {
+    case HTML:
+      {
+       if (document_title) {
+         RB_Generate_Doc_Start (dest, source, document_title, 0);
+         fprintf (dest, "<H1>%s</H1>\n", document_title);
+       } else {
+         RB_Generate_Doc_Start (dest, source, "Master Index File", 0);
+         fprintf (dest, "<H1>Master Index File</H1>\n");
+       }
+       if (RB_Number_Of_Links (MAIN_HEADER, NULL))
+         RB_Generate_Index_Table (dest, MAIN_HEADER, "Project Modules");
+       RB_Generate_Index_Table (dest, NO_HEADER, "Source Files");
+       if (RB_Number_Of_Links (CLASS_HEADER, NULL))
+         RB_Generate_Index_Table (dest, CLASS_HEADER, "Classes");
+       if (RB_Number_Of_Links (METHOD_HEADER, NULL))
+         RB_Generate_Index_Table (dest, METHOD_HEADER, "Methods");
+       if (RB_Number_Of_Links (STRUCT_HEADER, NULL))
+         RB_Generate_Index_Table (dest, STRUCT_HEADER, "Structures");
+       if (RB_Number_Of_Links (FUNCTION_HEADER, NULL))
+         RB_Generate_Index_Table (dest, FUNCTION_HEADER, "Functions");
+       if (RB_Number_Of_Links (VARIABLE_HEADER, NULL))
+         RB_Generate_Index_Table (dest, VARIABLE_HEADER, "Variables");
+       if (RB_Number_Of_Links (CONSTANT_HEADER, NULL))
+         RB_Generate_Index_Table (dest, CONSTANT_HEADER, "Constants");
+       if (RB_Number_Of_Links (GENERIC_HEADER, NULL))
+         RB_Generate_Index_Table (dest, GENERIC_HEADER, "Generic");
+       if (RB_Number_Of_Links (INTERNAL_HEADER, NULL))
+         RB_Generate_Index_Table (dest, INTERNAL_HEADER, "Internal");
+       RB_Generate_Doc_End (dest, source);
+      } break;
+    case LATEX:
+      {
+       RB_Generate_Doc_Start (dest, source, "Master File", 0);
+       RB_Generate_LaTeX_Includes (dest);
+       RB_Generate_Doc_End (dest, source);
+      }
+    }
+}
+
+
+/****f* ROBODoc/Generate_LaTeX_Includes
+ * NAME
+ *   Generate_LaTeX_Includes -- generate include commands
+ * SYNOPSIS
+ *   void RB_Generate_LaTeX_Includes (FILE *dest)
+ * FUNCTION
+ *   Generates a series of \include commands to include the
+ *   documentation generated for each source file into one
+ *   big file.
+ ****
+ */
+
+void
+RB_Generate_LaTeX_Includes (FILE *dest)
+{
+  struct RB_link *cur_link;
+  for (cur_link = first_link;
+       cur_link;
+       cur_link = cur_link->next_link) {
+    {
+      if (cur_link->type == NO_HEADER)
+       fprintf (dest, "\\include{%s}\n", cur_link->label_name);
+    }
+  }
+}
+
+/****f* ROBODoc/RB_Generate_Index_Table
+ * NAME
+ *   RB_Generate_Index --
+ * SYNOPSIS
+ *   void RB_Generate_Index_Table(FILE *, int type, char *title)
+ *        RB_Generate_Index_Table(dest, type, title)
+ * FUNCTION
+ *   Creates a table with index items of a particular type.
+ *   If the type is NO_HEADER, then the table is a table of
+ *   source files. In this case no link is added if the
+ *   source file did not contain any documentation.  
+ * INPUTS
+ *   dest  -- output file
+ *   type  -- kind of header index. 
+ *   title -- title for the table
+ * SOURCE
+ */
+
+void
+RB_Generate_Index_Table (FILE * dest, int type, char *title)
+{
+  struct RB_link *cur_link;
+  int number_of_columns;
+  int cur_column;
+
+  number_of_columns = 60 / RB_Max_Name_Length (type, NULL);
+
+  fprintf (dest, "<H2>%s</H2>\n", title);
+  fprintf (dest, "<TABLE>\n");
+  cur_column = 0;
+  for (cur_link = first_link;
+       cur_link;
+       cur_link = cur_link->next_link)
+    {
+      if (cur_link->type == type)
+       {
+         if (cur_column == 0)
+           {
+             fprintf (dest, "<TR>\n");
+           }
+         if (type == NO_HEADER)
+           {
+             if (RB_Number_Of_Links (NO_HEADER, cur_link->file_name) > 1)
+               {
+                 fprintf (dest,
+                          "<TD><A HREF=\"%s#%s\"><TT>%s</TT></A></TD>\n",
+                          cur_link->file_name, cur_link->label_name,
+                          cur_link->label_name);
+               }
+             else
+               {
+                 fprintf (dest, "<TD>%s</TD>\n", cur_link->label_name);
+               }
+           }
+         else
+           {
+             fprintf (dest, "<TD><A HREF=\"%s#%s\"><TT>%s</TT></A></TD>\n",
+                      cur_link->file_name, cur_link->label_name,
+                      cur_link->label_name);
+           };
+         cur_column++;
+         if (cur_column > number_of_columns)
+           {
+             fprintf (dest, "</TR>\n");
+             cur_column = 0;
+           }
+       }
+    }
+  for (; cur_column <= number_of_columns;)
+    {
+      if (cur_column == 0)
+       {
+         fprintf (dest, "<TR>\n");
+       }
+      fprintf (dest, "<TD></TD>\n");
+      cur_column++;
+    }
+  fprintf (dest, "</TR>\n");
+  fprintf (dest, "</TABLE>\n");
+}
+
+/******* END RB_Generate_Index_Table  *****/
+
+
+/****f* ROBODoc/RB_Number_Of_Links
+ * NAME
+ *   RB_Number_Of_Links -- Count the number of links.
+ * FUNCTION
+ *   Counts the number of links that are of a particular type
+ *   and that can be found in a particular file.
+ * INPUTS
+ *   type      -- the header type of the header the link is pointing to.
+ *                If NO_HEADER, all header types are counted.
+ *   file_name -- name of the file the link comes from, can be NULL, in
+ *                which case only the type is checked.
+ * RESULT
+ *   number of links.
+ ******
+ */
+
+int
+RB_Number_Of_Links (int type, char *file_name)
+{
+  struct RB_link *cur_link;
+  int n = 0;
+
+  for (cur_link = first_link;
+       cur_link;
+       cur_link = cur_link->next_link)
+    {
+      if (cur_link->type == type || (type == NO_HEADER))
+       {
+         if (file_name)
+           {
+             if (strcmp (file_name, cur_link->file_name) == 0)
+               {
+                 n++;
+               }
+           }
+         else
+           {
+             n++;
+           }
+       }
+    }
+
+  return n;
+}
+
+
+/****f* ROBODoc/RB_Max_Name_Length
+ * NAME
+ *   RB_Max_Name_Length -- find longest label name.
+ * FUNCTION
+ *   Find the length of the longest label name in a sub list
+ *   of the list with links.  This is used to determine how
+ *   many columns can be displayed in a table.
+ *   The sublist is specified by the type of header the link
+ *   should point to, as well as by the name of the documentation 
+ *   file.
+ * EXAMPLE
+ *     RB_Max_Name_Length(CLASS_HEADER, "muppets.c.html")
+ *   longest label name in the list of links to class headers 
+ *   in muppets.c.html.
+ *     RB_Max_Name_Length(CLASS_HEADER, NULL)
+ *   longest label name in the list of links to class headers.
+ * INPUTS
+ *   type      -- type of header
+ *   file_name -- file the header come from, can be NULL.
+ *                In which links from all files are used.
+ * SOURCE
+ */
+
+int
+RB_Max_Name_Length (int type, char *file_name)
+{
+  struct RB_link *cur_link;
+  int n = 1;
+
+  for (cur_link = first_link;
+       cur_link;
+       cur_link = cur_link->next_link)
+    {
+      if (cur_link->type == type)
+       {
+         if (file_name)
+           {
+             if (strcmp (file_name, cur_link->file_name) == 0)
+               {
+                 if (strlen (cur_link->label_name) > n)
+                   {
+                     n = strlen (cur_link->label_name);
+                   }
+               }
+           }
+         else
+           {
+             if (strlen (cur_link->label_name) > n)
+               {
+                 n = strlen (cur_link->label_name);
+               }
+           }
+       }
+    }
+  return n;
+}
+
+/*********/
diff --git a/util/robodoc/Source/generator.h b/util/robodoc/Source/generator.h
new file mode 100644 (file)
index 0000000..2460fb4
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef ROBODOC_GENERATOR_H
+#define ROBODOC_GENERATOR_H
+
+void RB_Generate_Documentation (FILE *, char *, char *);
+void RB_Generate_Item_Doc (FILE *, char *, char *, char *, char *, int);
+char *RB_Generate_Item_Body (FILE *, char *, char *, char *, char *, int, int);
+void RB_Generate_Header_Name (FILE *, char *);
+void RB_Generate_Item_Name (FILE *, int);
+void RB_Generate_Doc_Start (FILE *, char *, char *, char);
+void RB_Generate_Doc_End (FILE *, char *);
+void RB_Generate_Header_Start (FILE *, struct RB_header *);
+void RB_Generate_Header_End (FILE *, struct RB_header *);
+int RB_HTML_Extra (FILE * dest_doc, int item_type, char *cur_char);
+void RB_Generate_Index (FILE * dest, char *name);
+void RB_Generate_LaTeX_Includes (FILE *dest);
+void RB_Generate_Index_Table (FILE * dest, int type, char *source);
+int RB_Max_Name_Length (int type, char *file_name);
+int RB_Number_Of_Links (int type, char *file_name);
+
+#endif /* ROBODOC_GENERATOR_H */
diff --git a/util/robodoc/Source/headers.c b/util/robodoc/Source/headers.c
new file mode 100644 (file)
index 0000000..bb2bf98
--- /dev/null
@@ -0,0 +1,160 @@
+#include <stdio.h>
+#include <stddef.h>
+#include "robodoc.h"
+#include "headers.h"
+
+
+/****v* ROBODoc/header_markers [3.0h]
+ * NAME
+ *   header_markers -- strings that mark the begin of a header.
+ * FUNCTION
+ *   These specify what robodoc recognizes as the beginning
+ *   of a header.
+ * SOURCE
+ */
+
+char *header_markers[] =
+{
+  "/****",                     /* C, C++ */
+  "//****",                    /* C++ */
+  "(****",                     /* Pascal, Modula-2, B52 */
+  "{****",                     /* Pascal */
+  ";****",                     /* M68K assembler */
+  "****",                      /* M68K assembler */
+  "C     ****",                        /* Fortran */
+  "REM ****",                  /* BASIC */
+  "%****",                     /* LaTeX, TeX, Postscript */
+  "#****",                     /* Tcl/Tk */
+  "      ****",                        /* COBOL */
+  "--****",                    /* Occam */
+  "<!--****",                  /* HTML Code */
+  "<!---****",                 /* HTML Code,  the three-dashed comment 
+                                * tells the [server] pre-processor not 
+                                * to send that comment with the HTML */
+  "|****",                     /* GNU Assembler */
+  "!!****",                    /* FORTAN 90 */
+  NULL};
+
+/****/
+
+
+/****v* ROBODoc/remark_markers [3.0h]
+ * NAME
+ *   remark_markers
+ * FUNCTION
+ *   These specify what robodoc recognizes as a comment marker.
+ * SOURCE
+ */
+
+char *remark_markers[] =
+{
+  " *",                                /* C, C++, Pascal, Modula-2 */
+  "//",                                /* C++ */
+  "*",                         /* C, C++, M68K assembler, Pascal, *
+                                * Modula-2 */
+  ";*",                                /* M68K assembler */
+  ";",                         /* M68K assembler */
+  "C    ",                     /* Fortran */
+  "REM ",                      /* BASIC */
+  "%",                         /* LaTeX, TeX, Postscript */
+  "#",                         /* Tcl/Tk */
+  "      *",                   /* COBOL */
+  "--",                                /* Occam */
+  "|",                         /* GNU Assembler */
+  "!!",                                /* FORTAN 90 */
+  NULL};
+
+/****/
+
+/****v* ROBODoc/end_markers [3.0h]
+ * NAME
+ *   end_markers -- strings that mark the end of a header.
+ * FUNCTION
+ *   These specify what robodoc recognizes as the end of a 
+ *   documentation header. In most cases this will be
+ *   "***" or " ***". If the header contains a SOURCE item
+ *   then the end of the source has to be marked, which
+ *   is when the other strings in this array are used.
+ * SOURCE
+ */
+
+char *end_markers[] =
+{
+  "/***",                      /* C, C++ */
+  "//***",                     /* C++ */
+  " ***",                      /* C, C++, Pascal, Modula-2 */
+  "{***",                      /* Pascal */
+  "(***",                      /* Pascal, Modula-2, B52 */
+  ";***",                      /* M68K assembler */
+  "***",                       /* M68K assembler */
+  "C     ***",                 /* Fortran */
+  "REM ***",                   /* BASIC */
+  "%***",                      /* LaTeX, TeX, Postscript */
+  "#***",                      /* Tcl/Tk */
+  "      ***",                 /* COBOL */
+  "--***",                     /* Occam */
+  "<!--***",                   /* HTML */
+  "<!---***",                  /* HTML */
+  "|***",                      /* GNU Assembler */
+  "!!***",                     /* FORTAN 90 */
+  NULL};
+
+/****/
+
+/****v* ROBODoc/RB_header_typenames
+ * NAME
+ *   RB_header_typename
+ * FUNCTION
+ *   Handy table to translate a header type number (see RB_header_types)
+ *   to an ascii string.
+ *****
+ */
+
+char *RB_header_type_names[] =
+{
+  "none",
+  "main",
+  "generic",
+  "internal",
+  "function",
+  "struct",
+  "class",
+  "method",
+  "constant",
+  "variable",
+  "blank"
+};
+
+/****v* ROBODoc/first_header
+ * NAME
+ *   first_header -- pointer to the first header in the list of headers.
+ * SOURCE
+ */
+
+struct RB_header *first_header = NULL;
+
+/*****/
+
+/****v* ROBODoc/last_header
+ * NAME
+ *   last_header -- pointer to the last header in the list of headers.
+ * SOURCE
+ */
+
+struct RB_header *last_header = NULL;
+
+/******/
+
+/****v* ROBODoc/first_link
+ * NAME
+ *   first_link -- pointer to the first link in the list of links.
+ * SOURCE
+ */
+
+struct RB_link *first_link = NULL;
+
+/*****/
+
+
+int header_index_size = 0;
+struct RB_header **header_index = NULL;
diff --git a/util/robodoc/Source/headers.h b/util/robodoc/Source/headers.h
new file mode 100644 (file)
index 0000000..8361d8b
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef ROBODOC_HEADERS_H
+#define ROBODOC_HEADERS_H
+
+/****d* ROBODoc/RB_header_types
+ * NAME 
+ *   RB_header_types -- symbolic constants for the header types.
+ * SOURCE
+ */
+
+enum
+  {
+    NO_HEADER = 0,
+    MAIN_HEADER,
+    GENERIC_HEADER,
+    INTERNAL_HEADER,
+    FUNCTION_HEADER,
+    STRUCT_HEADER,
+    CLASS_HEADER,
+    METHOD_HEADER,
+    CONSTANT_HEADER,
+    VARIABLE_HEADER,
+    BLANK_HEADER
+  };
+
+/********/
+
+
+/****s* ROBODoc/RB_header [2.0]
+ *  NAME
+ *    RB_header -- header data structure
+ *  MODIFICATION HISTORY
+ *    8. August 1995: Koessi
+ *                    changed int version to char *version
+ *  ATTRIBUTES
+ *    next_header 
+ *    prev_header 
+ *    name          -- 
+ *    version       -- unused
+ *    type          -- header type see RB_header_types
+ *    size          --
+ *    function_name --
+ *    contents      --
+ *  SOURCE
+ */
+
+struct RB_header
+  {
+    struct RB_header *next_header;
+    struct RB_header *prev_header;
+    char *name;
+    char *version;
+    int type;
+    int size;
+    char *function_name;
+    char *contents;
+  };
+
+/*********/
+
+extern char *header_markers[];
+extern char *remark_markers[];
+extern char *end_markers[];
+extern char *RB_header_type_names[];
+extern struct RB_header *first_header;
+extern struct RB_header *last_header;
+extern struct RB_link *first_link;
+extern int header_index_size;
+extern struct RB_header **header_index;
+
+#endif /* ROBODOC_HEADERS_H */
+
+
diff --git a/util/robodoc/Source/items.c b/util/robodoc/Source/items.c
new file mode 100644 (file)
index 0000000..97457dd
--- /dev/null
@@ -0,0 +1,280 @@
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include "robodoc.h"
+#include "items.h"
+
+/****v* ROBODoc/item_names [3.0g]
+ * NAME
+ *   item_names
+ * SYNOPSIS
+ *   char *item_names[]
+ * FUNCTION
+ *   Defines the names of items that ROBODoc recognized as
+ *   items. For each name their is a corresponding 
+ *   item type (see ItemType). So if you add a name here
+ *   you have to add an item type to. In addition you
+ *   have to add an item attribute (see item_attributes) 
+ *   entry too.
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Get_Item_Type(), item_attributes, item_attr_names,
+ * SOURCE
+ */
+
+char *item_names[] =
+{
+  NULL,
+  "NAME",
+  /* Item name + short description */
+  "COPYRIGHT",
+  /* who own the copyright : "(c) <year>-<year> by <company/person>" */
+  "SYNOPSIS", "USAGE",
+  /* how to use it */
+  "FUNCTION", "DESCRIPTION", "PURPOSE",
+  /* what does it */
+  "AUTHOR",
+  /* who wrote it */
+  "CREATION DATE",
+  /* when did the work start */
+  "MODIFICATION HISTORY", "HISTORY",
+  /* who done what changes when */
+  "INPUTS", "ARGUMENTS", "OPTIONS", "PARAMETERS", "SWITCHES",
+  /* what can we feed into it */
+  "OUTPUT", "SIDE EFFECTS",
+  /* what output will be made */
+  "RESULT", "RETURN VALUE",
+  /* what do we get returned */
+  "EXAMPLE",
+  /* a clear example of the items use */
+  "NOTES",
+  /* any annotations */
+  "DIAGNOSTICS",
+  /* diagnostical output */
+  "WARNINGS", "ERRORS",
+  /* warning & error-messages */
+  "BUGS",
+  /* known bugs */
+  "TODO", "IDEAS",
+  /* what to implement next & ideas */
+  "PORTABILITY",
+  /* where does it come from, where will it work */
+  "SEE ALSO",
+  /* references */
+  "SOURCE",
+  /* source code inclusion */
+  "METHODS", "NEW METHODS",
+  /* oop methods */
+  "ATTRIBUTES", "NEW ATTRIBUTES",
+  /* oop attributes */
+  "TAGS",
+  /* tagitem description */
+  "COMMANDS",
+  /* command description */
+  "DERIVED FROM",
+  /* oop super class */
+  "DERIVED BY",
+  /* oop sub class */
+  "USES", "CHILDREN",
+  /* what modules are used by this one */
+  "USED BY", "PARENTS",
+  /* which modules do use this */
+  NULL,
+};
+
+/***********/
+
+
+/****v* ROBODoc/item_attributes [3.0h]
+ * NAME
+ *   item_attributes -- attributes of the various items
+ * FUNCTION
+ *   links each item type with a text attribute.
+ * SEE ALSO
+ *   RB_Get_Item_Type(), item_names, item_attr_names
+ * SOURCE
+ */
+
+long item_attributes[NUMBER_OF_ITEMS] =
+{
+  0,                           /* NO_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_SHINE,      /* NAME_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* COPYRIGHT_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_SHINE,      /* SYNOPSIS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* USAGE_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* FUNCTION_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_DEFAULT,            /* DESCRIPTION_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* PURPOSE_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_BOLD,       /* AUTHOR_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_BOLD,       /* CREATION_DATE_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* MODIFICATION_HISTORY_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* HISTORY_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* INPUT_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* ARGUMENT_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* OPTION_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* PARAMETER_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* SWITCH_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* OUTPUT_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* SIDE_EFFECTS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* RESULT_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* RETURN_VALUE_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_SHINE,              /* EXAMPLE_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_DEFAULT,    /* NOTE_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* DIAGNOSTICS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* WARNING_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* ERROR_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_SHINE,      /* BUGS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* TODO_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* IDEAS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* PORTABILITY_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* SEE_ALSO_ITEM */
+  ITEM_NAME_LARGE_FONT | TEXT_BODY_SHINE,              /* SOURCE_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* METHODS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* NEW_METHODS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* ATTRIBUTES_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* NEW_ATTRIBUTES_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* TAGS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* COMMANDS_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* DERIVED_FROM_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* DERIVED_BY_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* USES_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* CHILDREN */
+  ITEM_NAME_LARGE_FONT,                /* USED_BY_ITEM */
+  ITEM_NAME_LARGE_FONT,                /* PARENTS */
+  0                            /* OTHER_ITEM */
+};
+
+/**********/
+
+
+/****v* ROBODoc/item_attr_names [3.0j]
+ * NAME
+ *   item_attr_names
+ * SYNOPSIS
+ *   char *item_attr_names[]
+ * FUNCTION
+ *   used for strcmp() in RB_Get_Item_Attr()
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Get_Item_Attr(), item_attributes, item_names
+ * SOURCE
+ */
+
+char *item_attr_names[] =
+{
+/* "NORMAL", */
+  "LARGE", "ITALICS", "NONPROP", "SMALL", "BOLD",
+  "UNDERLINE", "SHINE", "HIGHLIGHT", "DEFAULT",
+};
+
+/*************/
+
+
+/* ASCII AMIGAGUIDE HTML LATEX RTF */
+
+char *att_start_command[SIZE_ATTRIBUTES][SIZE_MODES] =
+{
+  {"", "@{b}", "<FONT SIZE=\"+1\">", "{\\large ", "\\par\\fs28 "}, /* Large Font */
+  {"", "@{i}", "<I>", "{\\it ", "\\i1 "},      /* Italics. */
+  {"", "", "", "", ""},                /* NON-Proportional font. */
+  {"", "", "<SMALL>", "{\\small ", "\\fs16 "}, /* Small Font. */
+  {"", "@{b}", "<B>", "{\\bf ", "\\b1 "},      /* Bold. */
+  {"", "@{u}", "<U>", "\\underline{", "\\ul1 "},       /* Underline */
+  {"", "@{fg shine}", "<FONT FACE=\"courier\" size=\"3\">", "{\\em ", ""},/* Shine */
+  {"", "@{fg highlight}", "<EM>", "{\\em ", ""},               /* Highlight */
+  {"", "", "<FONT FACE=\"Helvetiva,Arial,Sans-serif\">", "", ""}               /* Default */
+};
+
+char *att_stop_command[SIZE_ATTRIBUTES][SIZE_MODES] =
+{
+  {"", "@{ub}", "</FONT>", "}", "\\fs20\\line "},      /* Large Font */
+  {"", "@{ui}", "</I>", "}", "\\i0 "}, /* Italics. */
+  {"", "", "", "", ""},                /* NON-Proportional font. */
+  {"", "", "</SMALL>", "}", "\\fs20 "},                /* Small Font. */
+  {"", "@{ub}", "</B>", "}", "\\b0 "}, /* Bold. */
+  {"", "@{uu}", "</U>", "}", "\\ul0 "},                /* Underline */
+  {"", "@{fg text}", "</FONT>", "}", ""},              /* Shine */
+  {"", "@{fg text}", "</EM>", "}", ""},        /* Highlight */
+  {"", "", "</FONT>", "", ""}  /* Normal */
+};
+
+
+
+/****f* ROBODoc/RB_Get_Item_Type [3.0b]
+ * NAME
+ *   RB_Get_Item_Type -- shortcut
+ * SYNOPSIS
+ *   int RB_Get_Item_Type( char *cmp_name )
+ * FUNCTION
+ *   return the item_type represented by the given string
+ * INPUTS
+ *   char *cmp_name          -- item_name to evaluate
+ * RESULT
+ *   int                     -- the right item_type or NO_ITEM
+ * NOTES
+ *   uses global char *item_names[]
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Analyse_Defaults_File(), RB_Get_Item_Attr()
+ * SOURCE
+ */
+
+int
+RB_Get_Item_Type (char *cmp_name)
+{
+  int item_type;
+
+  for (item_type = NAME_ITEM; item_type < OTHER_ITEM; ++item_type)
+    {
+      if (!strncmp (item_names[item_type], cmp_name,
+                   strlen (item_names[item_type])))
+       return (item_type);
+    }
+  return (NO_ITEM);
+}
+
+/*** RB_Get_Item_Type ***/
+
+
+
+/****f* ROBODoc/RB_Get_Item_Attr [3.0b]
+ *
+ * NAME
+ *   RB_Get_Item_Attr -- shortcut
+ * SYNOPSIS
+ *   int RB_Get_Item_Attr( char *cmp_name )
+ * FUNCTION
+ *   return the item_attr represented by the given string
+ * INPUTS
+ *   char *cmp_name  -- item_attr_name to evaluate
+ * RESULT
+ *   int             -- the right item_attr or NULL
+ * NOTES
+ *   uses global char *item_attr_names[]
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Analyse_Defaults_File(), RB_Get_Item_Type()
+ * SOURCE
+ */
+
+int
+RB_Get_Item_Attr (char *cmp_name)
+{
+  int item_attr;
+
+  for (item_attr = MAKE_LARGE; item_attr < SIZE_ATTRIBUTES; ++item_attr)
+    if (!strcmp (item_attr_names[item_attr], cmp_name))
+      return (item_attr);
+  if (strcmp ("NORMAL", cmp_name))
+    {
+      fprintf (stderr, "%s: Warning unknown attribute [%s] in defaults file.\n",
+              whoami, cmp_name);
+    }
+  return (MAKE_NORMAL);
+}
+
+/************/
diff --git a/util/robodoc/Source/items.h b/util/robodoc/Source/items.h
new file mode 100644 (file)
index 0000000..071c768
--- /dev/null
@@ -0,0 +1,83 @@
+
+
+
+#ifndef ROBODOC_ITEMS_H
+#define ROBODOC_ITEMS_H
+
+enum
+  {
+    MAKE_NORMAL = -1, MAKE_LARGE, MAKE_ITALICS, MAKE_NON_PROP, MAKE_SMALL,
+    MAKE_BOLD, MAKE_UNDERLINE, MAKE_SHINE, MAKE_HIGH, MAKE_DEFAULT,
+    SIZE_ATTRIBUTES
+  };
+
+#define ITEM_NAME_LARGE_FONT (1<<0)
+#define TEXT_BODY_LARGE_FONT (1<<(MAKE_LARGE     + 1))
+#define TEXT_BODY_ITALICS    (1<<(MAKE_ITALICS   + 1))
+#define TEXT_BODY_NON_PROP   (1<<(MAKE_NON_PROP  + 1))
+#define TEXT_BODY_SMALL_FONT (1<<(MAKE_SMALL     + 1))
+#define TEXT_BODY_BOLD       (1<<(MAKE_BOLD      + 1))
+#define TEXT_BODY_UNDERLINE  (1<<(MAKE_UNDERLINE + 1))
+#define TEXT_BODY_SHINE      (1<<(MAKE_SHINE     + 1))
+#define TEXT_BODY_HIGHLIGHT  (1<<(MAKE_HIGH      + 1))
+#define TEXT_BODY_DEFAULT    (1<<(MAKE_DEFAULT   + 1))
+
+
+/****** ROBODoc/ItemTypes *
+ * NAME 
+ *   ItemTypes -- enumeration of item types
+ * FUNCTION
+ *   Give an unique number to each item type. This defines all item types that
+ *   are recognized by ROBODoc. The corresponding names (string) of each item
+ *   are defined in item_names.  If you add an item here you also should
+ *   add an corresponding item name.  
+ * SOURCE
+ */
+
+enum
+  {
+    NO_ITEM = 0,
+    NAME_ITEM,
+    COPYRIGHT_ITEM,
+    SYNOPSIS_ITEM, USAGE_ITEM,
+    FUNCTION_ITEM, DESCRIPTION_ITEM, PURPOSE_ITEM,
+    AUTHOR_ITEM,
+    CREATION_DATE_ITEM,
+    MODIFICATION_HISTORY_ITEM, HISTORY_ITEM,
+    INPUT_ITEM, ARGUMENT_ITEM, OPTION_ITEM, PARAMETER_ITEM, SWITCH_ITEM,
+    OUTPUT_ITEM, SIDE_EFFECTS_ITEM,
+    RESULT_ITEM, RETURN_VALUE_ITEM,
+    EXAMPLE_ITEM,
+    NOTE_ITEM,
+    DIAGNOSTICS_ITEM,
+    WARNING_ITEM, ERROR_ITEM,
+    BUGS_ITEM,
+    TODO_ITEM, IDEAS_ITEM,
+    PORTABILITY_ITEM,
+    SEE_ALSO_ITEM,
+    SOURCE_ITEM,
+    METHODS_ITEM, NEW_METHODS_ITEM,
+    ATTRIBUTES_ITEM, NEW_ATTRIBUTES_ITEM,
+    TAGS_ITEM,
+    COMMANDS_ITEM,
+    DERIVED_FROM_ITEM,
+    DERIVED_BY_ITEM,
+    USES_ITEM, CHILDREN_ITEM,
+    USED_BY_ITEM, PARENTS_ITEM,
+    OTHER_ITEM,
+    NUMBER_OF_ITEMS
+  };
+
+/****/
+
+extern char *item_names[];
+extern long item_attributes[NUMBER_OF_ITEMS];
+extern char *item_attr_names[];
+extern char *att_start_command[SIZE_ATTRIBUTES][SIZE_MODES];
+extern char *att_stop_command[SIZE_ATTRIBUTES][SIZE_MODES];
+
+int RB_Get_Item_Type (char *);
+int RB_Get_Item_Attr (char *cmp_name);
+
+
+#endif /* ROBODOC_ITEMS_H */
diff --git a/util/robodoc/Source/links.c b/util/robodoc/Source/links.c
new file mode 100644 (file)
index 0000000..7609a6d
--- /dev/null
@@ -0,0 +1,442 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "robodoc.h"
+#include "headers.h"
+#include "util.h"
+#include "links.h"
+#include "folds.h"
+
+
+FILE *xreffiles_file = NULL;
+FILE *xref_file = NULL;
+int link_index_size = 0;
+struct RB_link **link_index = NULL;
+
+/****f* ROBODoc/RB_Analyse_Xrefs [3.0b]
+ * NAME
+ *   RB_Analyse_Xrefs -- scan the xref files.
+ * SYNOPSIS
+ *   RB_Analyse_Xrefs (xreffiles_file)
+ *   RB_Analyse_Xrefs (FILE *)
+ * FUNCTION
+ *   Scan the file xreffiles_file. This file contains the
+ *   names of one or more xref files. All the references in the
+ *   files are scaned and stored in a link list of the type
+ *   RB_link. These xref files can be generated with robodoc.
+ * INPUTS
+ *   xreffiles_file - a file pointer to the file with xref file
+ *   names.
+ * RESULT
+ *   none
+ * BUGS
+ *   Might fail if there are syntax errors in one of the xref
+ *   files.
+ *   Bad use of feof() and fgets().
+ * SEE ALSO
+ *   RB_Generate_xrefs, RB_Add_Link
+ * SOURCE
+ */
+
+void
+RB_Analyse_Xrefs (FILE * xreffiles_file)
+{
+  while (!feof (xreffiles_file))
+    {
+      fgets (line_buffer, MAX_LINE_LEN, xreffiles_file);
+      if (!feof (xreffiles_file))
+       {
+         char *cur_char;
+
+         cur_char = line_buffer;
+         find_eol;
+         if (*cur_char == '\n')
+           *cur_char = '\0';
+         if (strlen (line_buffer) > 1)
+           {
+             for (cur_char--;
+                  (cur_char != line_buffer) && isspace (*cur_char);
+                  cur_char--)
+               *cur_char = '\0';
+             if ((xref_file = fopen (line_buffer, "r")) != NULL)
+               {
+                 int xrefs_found = FALSE;
+                 int end_of_xrefs = FALSE;
+
+                 while (!feof (xref_file) && !xrefs_found)
+                   {
+                     fgets (line_buffer, MAX_LINE_LEN, xref_file);
+                     if (!feof (xref_file) && !strncmp ("XREF:",
+                                                        line_buffer, 5))
+                       xrefs_found = TRUE;
+                   }
+
+                 while (!feof (xref_file) && !end_of_xrefs)
+                   {
+                     fgets (line_buffer, MAX_LINE_LEN, xref_file);
+                     if (!feof (xref_file))
+                       {
+                         cur_char = line_buffer;
+                         find_quote;
+                         if (*cur_char == '\"')
+                           RB_Add_Link ();
+                         else
+                           end_of_xrefs = TRUE;
+                       }
+                   }
+                 fclose (xref_file);
+                 xref_file = NULL;
+               }
+             else
+               RB_Panic ("could not open xref file \"%s\"\n", line_buffer);
+           }
+       }
+    }
+}
+
+/*************/
+
+
+/****f* ROBODoc/RB_Slow_Sort_Links
+ * NAME
+ *   RB_Slow_Sort_Links -- sort all links according to label name.
+ ******
+ */
+
+void
+RB_Slow_Sort_Links (void)
+{
+  struct RB_link *cur_link, *unsorted_links, *bigger_link;
+
+  if ((unsorted_links = first_link) != NULL)
+    {                          /* additional check koessi */
+      for (first_link = NULL;
+          unsorted_links->next_link;)
+       {
+         for (bigger_link = unsorted_links,
+              cur_link = bigger_link->next_link;
+              cur_link;
+              cur_link = cur_link->next_link)
+           {
+             if (strcmp (cur_link->label_name, bigger_link->label_name) > 0)
+               bigger_link = cur_link;
+           }
+         RB_Remove_From_List ((struct RB_header **) &unsorted_links,
+                              (struct RB_header *) bigger_link);
+         RB_Insert_In_List ((struct RB_header **) &first_link,
+                            (struct RB_header *) bigger_link);
+       }
+      RB_Insert_In_List ((struct RB_header **) &first_link,
+                        (struct RB_header *) unsorted_links);
+    }
+}
+
+
+/****f* ROBODoc/RB_Add_Link [3.0b]
+ * NAME
+ *   RB_Add_Link -- add a reference link to the list
+ * SYNOPSIS
+ *   void RB_Add_Link ()
+ * FUNCTION
+ *   Adds a reference from a xref file to the linked list
+ *   with references.
+ * INPUTS
+ *   Uses the global variable line_buffer and first_link.
+ * NOTES
+ *   Makes sneaky use of the function RB_Insert_In_List.
+ * SEE ALSO
+ *   RB_Analyse_Xrefs, RB_link.
+ * SOURCE
+ */
+
+void
+RB_Add_Link ()
+{
+  char *label_name, *file_name;
+  struct RB_link *new_link;
+  char *cur_char = line_buffer;
+
+  find_quote;
+  label_name = ++cur_char;
+  find_quote;
+  *cur_char++ = '\0';
+  find_quote;
+  file_name = ++cur_char;
+  find_quote;
+  *cur_char = '\0';
+  ++cur_char;
+
+  RB_Say ("adding xref link \"%s\"->\"%s\"\n", label_name, file_name);
+
+  new_link = RB_Alloc_Link (label_name, file_name);
+  new_link->type = atoi (cur_char);
+  RB_Insert_In_List ((struct RB_header **) &first_link,
+                    (struct RB_header *) new_link);
+}
+
+/*** RB_Add_Link ***/
+
+
+
+/****f* ROBODoc/RB_Generate_xrefs [2.0]
+ * NAME
+ *   RB_Generate_xrefs
+ * SYNOPSIS
+ *   RB_Generate_xrefs (dest_doc, source_name, dest_name)
+ *
+ *   RB_Generate_xrefs (FILE *, char *, char *)
+ * FUNCTION
+ *   Generates a xref file for the document that has been
+ *   analysed by ROBODoc.
+ * INPUTS
+ *   dest_doc    - pointer to the file to which the xrefs will be
+ *                 written.
+ *   source_name - pointer to the name of the document that has
+ *                 been analysed by robodoc
+ *   dest_name   - pointer to the name of the document robodoc will
+ *                 write the documentation to.
+ *   first_header - global variable, the list with function
+ *                 headers.
+ * SOURCE
+ */
+
+void
+RB_Generate_xrefs (FILE * dest_doc, char *source_name, char *dest_name)
+{
+  struct RB_header *cur_header;
+
+  fprintf (dest_doc, "/* XREF-File generated by ROBODoc v" VERSION
+          " */\n");
+  fprintf (dest_doc, "\nXREF:\n");
+  fprintf (dest_doc, " \"%s\" \"%s\" 0\n", source_name, dest_name);
+  for (cur_header = first_header;
+       cur_header;
+       cur_header = cur_header->next_header
+    )
+    {
+      if (cur_header->function_name)
+       fprintf (dest_doc, " \"%s\" \"%s\" %d\n",
+                cur_header->function_name, dest_name, cur_header->type);
+    }
+  fprintf (dest_doc, "\n/* End of XREF-File */\n");
+}
+
+/*** RB_Generate_xrefs ***/
+
+
+
+/****f* ROBODoc/RB_Find_Link [3.0h]
+ * NAME
+ *   RB_Find_Link -- try to match word with a link
+ * SYNOPSIS
+ *   result = RB_Find_Link (word_begin, label_name, file_name)
+ *   int      RB_Find_Link (char *,     char **,    char **)
+ * FUNCTION
+ *   Searches for the given word in the list of links and
+ *   headers.  There are three passes (or four, when the C option
+ *   is selected). Each pass uses a different definition of "word".
+ *   In the first pass it is any thing that ends with a 'space', a '.' 
+ *   or a ','.
+ *   In the second pass it is any string that consists of alpha
+ *   numerics, '_', ':', '.', or '-'.  
+ *   In the third pass (for C) it is any string that consists 
+ *   of alpha numerics or '_'.
+ *   In the last pass it is any string that consists of alpha
+ *   numerics.
+ * INPUTS
+ *   word_begin  - pointer to a word (a string).
+ *   label_name  - pointer to a pointer to a string
+ *   file_name   - pointer to a pointer to a string
+ * SIDE EFFECTS
+ *   label_name & file_name are modified
+ * RESULT
+ *   label_name    -- points to the label if a match was found,
+ *                    NULL otherwise.
+ *   file_name     -- points to the file name if a match was found,
+ *                    NULL otherwise.
+ *   TRUE          -- a match was found.
+ *   FALSE         -- no match was found.
+ * NOTES
+ *   This is a rather sensitive algorithm.
+ * BUGS
+ ******
+ */
+
+int
+RB_Find_Link (char *word_begin, char **label_name, char **file_name)
+{
+  char *cur_char, old_char;
+  int low_index, high_index, cur_index, state, pass;
+
+
+  for (pass = 0; pass < 4; pass++)
+    {
+
+      switch (pass)
+       {
+       case 0:
+         {
+           for (cur_char = word_begin;
+                isalnum (*cur_char) || ispunct (*cur_char);
+                cur_char++);
+           if (((*(cur_char-1)) == ',') || ((*(cur_char-1)) == '.')) 
+             cur_char--;
+           break;
+         }
+       case 1:
+         {
+           for (cur_char = word_begin;
+                isalnum (*cur_char) || (*cur_char == '_') ||
+                (*cur_char == '-') || (*cur_char == '.') ||
+                (*cur_char == ':');
+                cur_char++);
+           break;
+         }
+       case 2:
+         {
+           if (extra_flags & C_MODE) {
+          for (cur_char = word_begin;
+                  isalnum(*cur_char) || (*cur_char  == '_');
+                  cur_char++);
+             break;
+               }
+           else continue;
+         }
+       case 3:
+         {
+           for (cur_char = word_begin;
+                isalnum (*cur_char);
+                cur_char++);
+           break;
+         }
+       }
+
+      old_char = *cur_char;
+      *cur_char = '\0';                /* End the word with a '\0' */
+/*      RB_Say ("Testing \"%s\"\n", word_begin); */
+
+      /* Search in header table */
+      for (cur_index = 0, low_index = 0, high_index = header_index_size - 1;
+          high_index >= low_index;)
+       {
+         cur_index = (high_index - low_index) / 2 + low_index;
+         state = strcmp (word_begin, header_index[cur_index]->function_name);
+         if (state < 0)
+           high_index = cur_index - 1;
+         else if (state > 0)
+           low_index = cur_index + 1;
+         else
+           {
+             *label_name = header_index[cur_index]->function_name;
+             *file_name = NULL;
+              RB_Say ("linking \"%s\"->\"%s\"\n", word_begin, *label_name);
+             *cur_char = old_char;
+             return (TRUE);
+           }
+       }
+
+      /* Search in the link table */
+      for (cur_index = 0, low_index = 0, high_index = link_index_size - 1;
+          high_index >= low_index;)
+       {
+         cur_index = (high_index - low_index) / 2 + low_index;
+         state = strcmp (word_begin, link_index[cur_index]->label_name);
+         if (state < 0)
+           {
+             high_index = cur_index - 1;
+           }
+         else if (state == 0)
+           {
+             *label_name = link_index[cur_index]->label_name;
+             *file_name = link_index[cur_index]->file_name;
+              RB_Say ("linking \"%s\"->\"%s\" form \"%s\"\n",
+                              word_begin, *label_name, *file_name);
+             *cur_char = old_char;
+             return (TRUE);
+           }
+         else if (state > 0)
+           {
+             low_index = cur_index + 1;
+           }
+       }
+      *cur_char = old_char;
+      *file_name = NULL;
+      *label_name = NULL;
+    }
+
+  return (FALSE);
+}
+
+
+
+
+
+/****f* ROBODoc/RB_Alloc_Link [2.01]
+ * NAME
+ *   RB_Alloc_Link              -- oop
+ * SYNOPSIS
+ *   struct RB_link *RB_Alloc_Link( char *label_name, char *file_name )
+ * FUNCTION
+ *   allocate struct + strings
+ * INPUTS
+ *   char *label_name -- strings to copy into the link
+ *   char *file_name
+ * RESULT
+ *   struct RB_link *  -- ready-to-use
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_StrDup(), RB_Free_Link()
+ *******
+ */
+
+struct RB_link *
+RB_Alloc_Link (char *label_name, char *file_name)
+{
+  struct RB_link *new_link;
+  if ((new_link = malloc (sizeof (struct RB_link))) != NULL)
+    {
+      memset (new_link, 0, sizeof (struct RB_link));
+
+      if (file_name)
+       new_link->file_name = RB_StrDup (file_name);
+      if (label_name)
+       new_link->label_name = RB_StrDup (label_name);
+    }
+  else
+    RB_Panic ("out of memory! [Alloc Link]\n");
+
+  return (new_link);
+}
+
+
+/****f* ROBODoc/RB_Free_Link [2.01]
+ * NAME
+ *   RB_Free_Link               -- oop
+ * SYNOPSIS
+ *   void RB_Free_Link( struct RB_link *link )
+ * FUNCTION
+ *   free struct + strings
+ * INPUTS
+ *   struct RB_link *link
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Alloc_Link(), RB_Close_The_Shop()
+ * SOURCE
+ ******
+ */
+
+void
+RB_Free_Link (struct RB_link *link)
+{
+  if (link)
+    {
+      if (link->label_name)
+       free (link->label_name);
+      if (link->file_name)
+       free (link->file_name);
+      free (link);
+    }
+}
diff --git a/util/robodoc/Source/links.h b/util/robodoc/Source/links.h
new file mode 100644 (file)
index 0000000..b727a28
--- /dev/null
@@ -0,0 +1,44 @@
+
+
+#ifndef ROBODOC_LINKS_H
+#define ROBODOC_LINKS_H
+
+/****s* ROBODoc/RB_link [2.0e]
+ *  NAME
+ *    RB_link -- link data structure
+ *  PURPOSE
+ *    Structure to store links to the documentation of an component. 
+ *  PROPERTIES
+ *    next_link
+ *    prev_link
+ *    label_name  -- the label under which the component can be found.
+ *    file_name   -- the file the component can be found in.
+ *    type        -- the type of component (the header type).
+ *  SOURCE
+ */
+
+struct RB_link
+  {
+    struct RB_link *next_link;
+    struct RB_link *prev_link;
+    char *label_name;
+    char *file_name;
+    int type;
+  };
+
+/*********/
+
+extern FILE *xreffiles_file;
+extern FILE *xref_file;
+extern int link_index_size;
+extern struct RB_link **link_index;
+
+void RB_Analyse_Xrefs (FILE * xreffiles_file);
+void RB_Add_Link ();
+void RB_Generate_xrefs (FILE * dest_doc, char *source_name, char *dest_name);
+int RB_Find_Link (char *word_begin, char **label_name, char **file_name);
+struct RB_link *RB_Alloc_Link (char *label_name, char *file_name);
+void RB_Free_Link (struct RB_link *link);
+void RB_Slow_Sort_Links (void);
+
+#endif /* ROBODOC_LINKS_H */
diff --git a/util/robodoc/Source/makefile.am b/util/robodoc/Source/makefile.am
new file mode 100644 (file)
index 0000000..4e13caa
--- /dev/null
@@ -0,0 +1,140 @@
+## Process this file with automake to produce Makefile.in
+#
+#
+
+bin_PROGRAMS = robodoc
+robodoc_SOURCES = analyser.c analyser.h config.h folds.c folds.h \
+     generator.c generator.h headers.c headers.h items.c items.h \
+     links.c links.h robodoc.c robodoc.h util.c util.h
+
+
+###############################################################
+
+ROBODOC=./robodoc
+ROBOOPTS=C SORT -v
+BROWSER=netscape
+
+# Your source files.
+#
+SOURCES=analyser.c generator.c items.c util.c \
+  folds.c headers.c links.c robodoc.c \
+  analyser.h generator.h items.h util.h \
+  folds.h headers.h links.h robodoc.h
+
+# The name of your Project
+#
+PROJECT=ROBODoc
+
+# The various documentation files, derived from the source files.
+# HTML
+#
+HTMLDOCS=$(SOURCES:=.html)
+HTMLXREFS=$(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE=$(PROJECT)_html.xrefs
+# LATEX
+#
+LATEXDOCS=$(SOURCES:=.tex)
+LATEXXREFS=$(LATEXDOCS:.tex=.tex.xref)
+LATEXXREFSFILE=$(PROJECT)_tex.xrefs
+# ASCII
+#
+ASCIIDOCS=$(SOURCES:=.txt)
+# RTF
+#
+RTFDOCS=$(SOURCES:=.rtf)
+RTFXREFS=$(RTFDOCS:.rtf=.rtf.xref)
+RTFXREFSFILE=$(PROJECT)_rtf.xrefs
+
+# Some common targets
+xrefall: xrefhtml xreftex xrefrtf
+docall: html tex ascii rtf
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+xtex: $(LATEXXREFSFILE) 
+xrtf: $(RTFXREFSFILE)
+
+# Create the documentation files for the various formats.
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+tex: $(LATEXDOCS) $(PROJECT)_mi.tex
+rtf: $(RTFDOCS)
+ascii: $(ASCIIDOCS)
+
+# master index file, currently works only for html and latex documentation.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $< $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+$(PROJECT)_mi.tex: $(LATEXXREFSFILE)
+       $(ROBODOC) $< $@ INDEX LATEX TITLE "$(PROJECT) API Reference"
+
+# create xrefs file (file with the names of all .xref files).
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) > $@
+$(LATEXXREFSFILE) : $(LATEXXREFS)
+       /bin/ls  $(LATEXXREFS) > $@
+$(RTFXREFSFILE) : $(RTFXREFS)
+       /bin/ls  $(RTFXREFS) > $@
+
+# Rule to create an .xref file from a source file for the various formats.
+%.html.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.tex.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.rtf.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $< $@ HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+
+# Rule to create latex documentation from a source file.
+# We do not include source items, and generate laxtex documents
+# than can be included in a master document.
+%.tex : %
+       $(ROBODOC) $< $@ LATEX $(ROBOOPTS) NOSOURCE SINGLEDOC XREF $(LATEXXREFSFILE)
+
+# Rule to create ascii documentation from a source file.
+%.txt : %
+       $(ROBODOC) $< $@ ASCII 
+
+# Rule to create rtf documentation from a source file.
+%.rtf : %
+       $(ROBODOC) $< $@ RTF $(ROBOOPTS) XREF $(RTFXREFSFILE)
+
+# Use netscape to view the master index file for our project.
+example: html
+       $(BROWSER) $(PROJECT)_mi.html
+
+# Use the latex programs to generate a .dvi from the master index file
+# for our prokect. View this .dvi file with xdvi
+texview:  tex
+       latex $(PROJECT)_mi
+       makeindex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       dvips $(PROJECT)_mi.dvi -o $(PROJECT)_mi.ps
+
+# Clean-up the mess we made
+#
+myclean:
+       rm -f $(HTMLXREFS) 
+       rm -f $(HTMLDOCS) 
+       rm -f $(LATEXXREFS)
+       rm -f $(LATEXDOCS) 
+       rm -f $(PROJECT)_mi.* *.aux
+       rm -f $(RTFXREFS)
+       rm -f $(RTFDOCS)
+       rm -f $(ASCIIDOCS)
+       rm -f $(HTMLXREFSFILE) 
+       rm -f $(LATEXXREFSFILE) 
+       rm -f $(RTFXREFSFILE)
+       rm -f robodoc makefile.in makefile
+       rm -f *~ stamp-h stamp-h.in
+       rm -f *.o *.tex *.toc *.dvi *.aux *.log *.ps
+       rm -f masterindex.html
+       rm -f testheaders.html
+
+tt:
+       cp robodoc $(HOME)/Test
+
+
diff --git a/util/robodoc/Source/makefile.in b/util/robodoc/Source/makefile.in
new file mode 100644 (file)
index 0000000..bce003f
--- /dev/null
@@ -0,0 +1,437 @@
+# makefile.in generated automatically by automake 1.4 from makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#
+#
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+CC = @CC@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+bin_PROGRAMS = robodoc
+robodoc_SOURCES = analyser.c analyser.h config.h folds.c folds.h      generator.c generator.h headers.c headers.h items.c items.h      links.c links.h robodoc.c robodoc.h util.c util.h
+
+
+###############################################################
+
+ROBODOC = ./robodoc
+ROBOOPTS = C SORT -v
+BROWSER = netscape
+
+# Your source files.
+#
+SOURCES = analyser.c generator.c items.c util.c   folds.c headers.c links.c robodoc.c   analyser.h generator.h items.h util.h   folds.h headers.h links.h robodoc.h
+
+
+# The name of your Project
+#
+PROJECT = ROBODoc
+
+# The various documentation files, derived from the source files.
+# HTML
+#
+HTMLDOCS = $(SOURCES:=.html)
+HTMLXREFS = $(HTMLDOCS:.html=.html.xref)
+HTMLXREFSFILE = $(PROJECT)_html.xrefs
+# LATEX
+#
+LATEXDOCS = $(SOURCES:=.tex)
+LATEXXREFS = $(LATEXDOCS:.tex=.tex.xref)
+LATEXXREFSFILE = $(PROJECT)_tex.xrefs
+# ASCII
+#
+ASCIIDOCS = $(SOURCES:=.txt)
+# RTF
+#
+RTFDOCS = $(SOURCES:=.rtf)
+RTFXREFS = $(RTFDOCS:.rtf=.rtf.xref)
+RTFXREFSFILE = $(PROJECT)_rtf.xrefs
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = 
+PROGRAMS =  $(bin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I.
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+robodoc_OBJECTS =  analyser.o folds.o generator.o headers.o items.o \
+links.o robodoc.o util.o
+robodoc_LDADD = $(LDADD)
+robodoc_DEPENDENCIES = 
+robodoc_LDFLAGS = 
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DIST_COMMON =  ./stamp-h.in config.h.in makefile.am makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+SOURCES = $(robodoc_SOURCES)
+OBJECTS = $(robodoc_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/makefile.in: makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+       cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps Source/makefile
+
+makefile: $(srcdir)/makefile.in  $(top_builddir)/config.status
+       cd $(top_builddir) \
+         && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+config.h: stamp-h
+       @if test ! -f $@; then \
+               rm -f stamp-h; \
+               $(MAKE) stamp-h; \
+       else :; fi
+stamp-h: $(srcdir)/config.h.in $(top_builddir)/config.status
+       cd $(top_builddir) \
+         && CONFIG_FILES= CONFIG_HEADERS=Source/config.h \
+            $(SHELL) ./config.status
+       @echo timestamp > stamp-h 2> /dev/null
+$(srcdir)/config.h.in: $(srcdir)/stamp-h.in
+       @if test ! -f $@; then \
+               rm -f $(srcdir)/stamp-h.in; \
+               $(MAKE) $(srcdir)/stamp-h.in; \
+       else :; fi
+$(srcdir)/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+       cd $(top_srcdir) && $(AUTOHEADER)
+       @echo timestamp > $(srcdir)/stamp-h.in 2> /dev/null
+
+mostlyclean-hdr:
+
+clean-hdr:
+
+distclean-hdr:
+       -rm -f config.h
+
+maintainer-clean-hdr:
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+       -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       $(mkinstalldirs) $(DESTDIR)$(bindir)
+       @list='$(bin_PROGRAMS)'; for p in $$list; do \
+         if test -f $$p; then \
+           echo "  $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+            $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+         else :; fi; \
+       done
+
+uninstall-binPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       list='$(bin_PROGRAMS)'; for p in $$list; do \
+         rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+       done
+
+.c.o:
+       $(COMPILE) -c $<
+
+.s.o:
+       $(COMPILE) -c $<
+
+.S.o:
+       $(COMPILE) -c $<
+
+mostlyclean-compile:
+       -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+       -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+robodoc: $(robodoc_OBJECTS) $(robodoc_DEPENDENCIES)
+       @rm -f robodoc
+       $(LINK) $(robodoc_LDFLAGS) $(robodoc_OBJECTS) $(robodoc_LDADD) $(LIBS)
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+       list='$(SOURCES) $(HEADERS)'; \
+       unique=`for i in $$list; do echo $$i; done | \
+         awk '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       here=`pwd` && cd $(srcdir) \
+         && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)'; \
+       unique=`for i in $$list; do echo $$i; done | \
+         awk '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       test -z "$(ETAGS_ARGS)config.h.in$$unique$(LISP)$$tags" \
+         || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags config.h.in $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+       -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = Source
+
+distdir: $(DISTFILES)
+       @for file in $(DISTFILES); do \
+         d=$(srcdir); \
+         if test -d $$d/$$file; then \
+           cp -pr $$d/$$file $(distdir)/$$file; \
+         else \
+           test -f $(distdir)/$$file \
+           || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+           || cp -p $$d/$$file $(distdir)/$$file || :; \
+         fi; \
+       done
+
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+all-recursive-am: config.h
+       $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+install-exec-am: install-binPROGRAMS
+install-exec: install-exec-am
+
+install-data-am:
+install-data: install-data-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-binPROGRAMS
+uninstall: uninstall-am
+all-am: makefile $(PROGRAMS) config.h
+all-redirect: all-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+       $(mkinstalldirs)  $(DESTDIR)$(bindir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -rm -f Makefile $(CONFIG_CLEAN_FILES)
+       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am:  mostlyclean-hdr mostlyclean-binPROGRAMS \
+               mostlyclean-compile mostlyclean-tags \
+               mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am:  clean-hdr clean-binPROGRAMS clean-compile clean-tags \
+               clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am:  distclean-hdr distclean-binPROGRAMS distclean-compile \
+               distclean-tags distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am:  maintainer-clean-hdr maintainer-clean-binPROGRAMS \
+               maintainer-clean-compile maintainer-clean-tags \
+               maintainer-clean-generic distclean-am
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
+mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
+maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
+mostlyclean-compile distclean-compile clean-compile \
+maintainer-clean-compile tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \
+check-am installcheck-am installcheck all-recursive-am install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Some common targets
+xrefall: xrefhtml xreftex xrefrtf
+docall: html tex ascii rtf
+
+# Create the xref files for the various formats.
+xhtml: $(HTMLXREFSFILE) 
+xtex: $(LATEXXREFSFILE) 
+xrtf: $(RTFXREFSFILE)
+
+# Create the documentation files for the various formats.
+html: $(HTMLDOCS) $(PROJECT)_mi.html 
+tex: $(LATEXDOCS) $(PROJECT)_mi.tex
+rtf: $(RTFDOCS)
+ascii: $(ASCIIDOCS)
+
+# master index file, currently works only for html and latex documentation.
+$(PROJECT)_mi.html: $(HTMLXREFSFILE) 
+       $(ROBODOC) $< $@ INDEX HTML TITLE "$(PROJECT) Master Index"
+
+$(PROJECT)_mi.tex: $(LATEXXREFSFILE)
+       $(ROBODOC) $< $@ INDEX LATEX TITLE "$(PROJECT) API Reference"
+
+# create xrefs file (file with the names of all .xref files).
+$(HTMLXREFSFILE) : $(HTMLXREFS)
+       /bin/ls $(HTMLXREFS) > $@
+$(LATEXXREFSFILE) : $(LATEXXREFS)
+       /bin/ls  $(LATEXXREFS) > $@
+$(RTFXREFSFILE) : $(RTFXREFS)
+       /bin/ls  $(RTFXREFS) > $@
+
+# Rule to create an .xref file from a source file for the various formats.
+%.html.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.tex.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+%.rtf.xref : %
+       $(ROBODOC) $< $(@:.xref=) $(ROBOOPTS) INTERNAL GENXREF $@
+
+# Rule to create html documentation from a source file.
+%.html : %
+       $(ROBODOC) $< $@ HTML $(ROBOOPTS) XREF $(HTMLXREFSFILE)
+
+# Rule to create latex documentation from a source file.
+# We do not include source items, and generate laxtex documents
+# than can be included in a master document.
+%.tex : %
+       $(ROBODOC) $< $@ LATEX $(ROBOOPTS) NOSOURCE SINGLEDOC XREF $(LATEXXREFSFILE)
+
+# Rule to create ascii documentation from a source file.
+%.txt : %
+       $(ROBODOC) $< $@ ASCII 
+
+# Rule to create rtf documentation from a source file.
+%.rtf : %
+       $(ROBODOC) $< $@ RTF $(ROBOOPTS) XREF $(RTFXREFSFILE)
+
+# Use netscape to view the master index file for our project.
+example: html
+       $(BROWSER) $(PROJECT)_mi.html
+
+# Use the latex programs to generate a .dvi from the master index file
+# for our prokect. View this .dvi file with xdvi
+texview:  tex
+       latex $(PROJECT)_mi
+       makeindex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       latex $(PROJECT)_mi
+       dvips $(PROJECT)_mi.dvi -o $(PROJECT)_mi.ps
+
+# Clean-up the mess we made
+#
+myclean:
+       rm -f $(HTMLXREFS) 
+       rm -f $(HTMLDOCS) 
+       rm -f $(LATEXXREFS)
+       rm -f $(LATEXDOCS) 
+       rm -f $(PROJECT)_mi.* *.aux
+       rm -f $(RTFXREFS)
+       rm -f $(RTFDOCS)
+       rm -f $(ASCIIDOCS)
+       rm -f $(HTMLXREFSFILE) 
+       rm -f $(LATEXXREFSFILE) 
+       rm -f $(RTFXREFSFILE)
+       rm -f robodoc makefile.in makefile
+       rm -f *~ stamp-h stamp-h.in
+       rm -f *.o *.tex *.toc *.dvi *.aux *.log *.ps
+       rm -f masterindex.html
+       rm -f testheaders.html
+
+tt:
+       cp robodoc $(HOME)/Test
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/util/robodoc/Source/makefile.plain b/util/robodoc/Source/makefile.plain
new file mode 100644 (file)
index 0000000..8739dcb
--- /dev/null
@@ -0,0 +1,215 @@
+#****h* ROBODoc/Makefile.plain
+# NAME
+#   Makefile.plain -- Plain makefile that does not need autoconf 
+# SYNOPSIS
+#   make robodoc
+#   make html   
+#   make example
+#   make count
+#   make test
+#   make clean
+# PURPOSE
+#   The makefile for SAS C compiler v6.x and Dice, and GCC.
+#   You can use it if you are on a non Unix system or a system
+#   that does not support autoconfiguration.
+#
+#
+#
+#   The following targets are the most useful for the user.
+#
+#   robodoc -  makes the robodc executable.
+#   example -  makes robodoc and shows you the autodocs
+#              generated from the ROBODoc source code
+#              using browser.         
+#   html    -  makes autodocs for robodoc in html format.
+#
+#   Developers might try:
+#   test    -
+#   count   -
+#   clean   -  
+# NOTES
+#   This documentation is not complete. It is just a test to see
+#   how to best use ROBODoc with make files.
+#
+#****
+#
+# $Id$
+#
+
+SHELL = /bin/sh
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+#--------------------------------------
+# use DICE C under AmigaOS
+#--------------------------------------
+#CC = dcc
+#CFLAGS =
+
+#--------------------------------------
+# use gcc (optimising for AmigaOS)
+#--------------------------------------
+#CC = gcc
+#CFLAGS = -O2 -fstrength-reduce -s -m68020-40 -noixemul
+
+#--------------------------------------
+# use gcc (generic)
+#--------------------------------------
+
+CC = gcc
+CFLAGS = -Wall -ansi -pedantic
+LIBS=
+
+#
+#
+#
+
+BROWSER=netscape
+ROBODOC=./robodoc
+ETAGS=/usr/bin/etags
+EGREP=/bin/egrep
+RM=/bin/rm
+
+all: robodoc
+
+#--------------------------------------
+# sources for the robodoc executable
+#--------------------------------------
+
+SOURCES=analyser.c generator.c items.c util.c folds.c headers.c \
+        links.c robodoc.c     
+
+OBJECTS=analyser.o generator.o items.o util.o folds.o headers.o \
+        links.o robodoc.o     
+
+#****** makefile.plain/robodoc
+# NAME
+#   robodoc --
+# NOTE
+#   This assumes that you version of make knows how to make an .o file
+#   out of an .c file.
+# SOURCE
+#
+
+robodoc : $(OBJECTS) 
+       $(CC) $(OBJECTS) -o robodoc $(LIBS)
+
+#****
+
+
+#****** makefile.plain/html
+# NAME
+#   html -- ROBODoc HTML autodocs for ROBODoc
+# FUNCTION
+#   
+#****
+
+DOCS=analyser.c.html generator.c.html items.c.html util.c.html \
+  folds.c.html headers.c.html links.c.html robodoc.c.html \
+  analyser.h.html generator.h.html items.h.html util.h.html \
+  folds.h.html headers.h.html links.h.html robodoc.h.html
+
+XREF=$(DOCS:.html=.xref)
+
+XREFSFILE=robodoc.html.xrefs 
+
+html : robodoc masterindex.html 
+
+#
+# create xrefs file (file with the names of all .xref files).
+#
+robodoc.html.xrefs : $(XREF)
+       /bin/ls *.xref > $@
+
+#
+# Rule to create an .xref file from a source file.
+#
+%.xref : %
+       $(ROBODOC) $< $(@:.xref=.html) INTERNAL GENXREF $@
+
+#
+# Rule to create an .html file from a source file.
+#
+%.html : % $(XREFSFILE)
+       $(ROBODOC) $< $@ HTML INTERNAL XREF $(XREFSFILE)
+
+
+masterindex.html : $(XREFSFILE) $(DOCS)
+       $(ROBODOC) $(XREFSFILE) $@ INDEX HTML TITLE "ROBODoc Master Index"
+
+
+#****** makefile.plain/example
+# NAME
+#   example -- create and show autodocs extracted from ROBODoc source.
+# FUNCTION
+#
+#****
+
+example : html 
+       $(BROWSER) masterindex.html
+
+#----------------------------
+# Development
+#----------------------------
+
+tags :
+       $(ETAGS) *.c *.h
+
+#****** makefile.plain/count
+# NAME
+#   count -- count the number of lines of the ROBODoc source.
+#****
+
+count :
+       $(EGREP) -v -G "^ \*" *.c *.h | egrep -v -G "/\*"  | wc
+
+
+#****** makefile.plain/test
+# NAME
+#   test -- run some tests
+# FUNCTION
+#   Runs robodoc on file with a number of different headers.
+#
+#****
+
+test : $(ROBODOC)
+       $(ROBODOC) testheaders.c testheaders.html HTML -v
+
+
+#****** makefile.plain/clean
+# NAME
+#   clean -- Clean up the mess we made.
+# FUNCTION
+#   Cleans up the mess we made.
+#*****
+
+clean :
+       $(RM) -f $(DOCS) $(XREF)
+       $(RM) -f robodoc
+       $(RM) -f *~
+       $(RM) -f *.o *.tex *.toc *.dvi *.aux *.log *.ps
+       $(RM) -f masterindex.html
+       $(RM) -f testheaders.html
+       $(RM) -f stamp-h* makefile.in config.h robodoc.html.xrefs
+
+#------------------------------
+# Construction of the makefile
+#------------------------------
+
+depend :
+       makedepend -Y"" -f makefile.plain *.c *.h
+
+# DO NOT DELETE
+
+analyser.o: config.h.in robodoc.h headers.h items.h util.h folds.h links.h
+analyser.o: analyser.h
+folds.o: config.h.in folds.h robodoc.h
+generator.o: config.h.in robodoc.h headers.h items.h folds.h util.h links.h
+generator.o: generator.h analyser.h
+headers.o: config.h.in robodoc.h headers.h
+items.o: config.h.in robodoc.h items.h
+links.o: config.h.in headers.h robodoc.h util.h links.h
+robodoc.o: config.h.in robodoc.h folds.h headers.h items.h util.h links.h
+robodoc.o: analyser.h generator.h
+util.o: config.h.in robodoc.h links.h headers.h folds.h items.h util.h
+
diff --git a/util/robodoc/Source/robodoc.c b/util/robodoc/Source/robodoc.c
new file mode 100644 (file)
index 0000000..3274d77
--- /dev/null
@@ -0,0 +1,862 @@
+/****h* Autodoc/ROBODoc [3.2]
+ * NAME
+ *   ROBODoc -- AutoDoc formatter
+ * COPYRIGHT
+ *  Copyright (C) 1994-2000  Frans Slothouber and Jacco van Weert.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * FUNCTION
+ *   ROBODoc is intended to be a replacement for the original AutoDocs
+ *   program.  ROBODoc will extract the procedure comment headers
+ *   from a source file, and put them into a separate documentation file.
+ *   There are five different file output formats:
+ *       ASCII
+ *       HTML (HyperText Markup Langauge) -- mainly used on the Internet;
+ *           thus the files can be viewed with a normal HTML viewer/browser
+ *       AmigaGuide -- this format can be viewed by AmigaGuide (Amiga only)
+ *       LaTeX - as input to LaTeX to produce .dvi files
+ *       RTF (Rich Text Format) -- as input to the Help compiler (Windows only)
+ *   For further information read the documentation in the archive.
+ * AUTHOR
+ *   Frans Slothouber:  <fslothouber@acm.org>.
+ *     Source code and additional extensions from version 2.0 and up.
+ *
+ *   Petteri Kettunen: <petterik@iki.fi>
+ *     Bug fixes, FOLD, C features.
+ *
+ *   Jacco van Weert: <weertj@xs4all.nl>
+ *     Original idea and program.
+ *
+ *   Bernd Koesling:  <KOESSI@CHESSY.aworld.de>
+ *     Bug fixes, functional improvements, code cleanup.
+ *
+ *   Anthon Pang:  <apang@mindlink.net>
+ *     RTF support, Bug fixes.
+ *
+ *   Thomas Aglassinger: <agi@sbox.tu-graz.ac.at>
+ *     Fixes and cleanup of HTML-output
+ *
+ * CREATION DATE
+ *   20-Dec-94
+ * MODIFICATION HISTORY
+ *   Modifications by Jacco van Weert.
+ *     19-Jan-95     -  v0.8:   First test beta-version
+ *     26-Jan-95     -  v0.92:  2nd test beta-version
+ *     2-Feb-95      -  v0.93:  Mungwall hit, solved.
+ *                              When item headers, are also available
+ *                              in body then parts are duplicated solved.
+ *     Mar-95        -  v1.0a:  Final version
+ *     2-Apr-95      -  v1.0b:  Bug fixes
+ *                              Procedure header search bug solved.
+ *                              Print 'created procedure' text
+ *     20-Apr-95     -  v1.1a:  INTERNALONLY option added.
+ *                              Sort problem solved.
+ *   Modifications by FNC Slothouber.
+ *     10-May-1995 -  v2.0a  * Program completely rewritten
+ *                           * added SOURCE item and LaTeX output.
+ *                           * added TAB converter.
+ *     11-May-1995 -  v2.0b  * Accepts headers that start with
+ *                             any sequence of non-spaces.
+ *                             RoboDoc should work with any
+ *                             type of programming language now.
+ *     12-May-1995 -  v2.0c  * Bug fixes.
+ *     15-May-1995 -  v2.0d  * New Defaults file.
+ *                           * Added Verbose option.
+ *     24-May-1995 -  v2.0e  * Fixed a bug that cause the
+ *                             CleanUp Routine to lock up.
+ *                           * Improved the HTML output,
+ *                             should work faster now.
+ *   Modifications by Koessi
+ *     01-Aug-1995  - v2.0?  * more robust parsing, less enforcer-hits
+ *                           * removed self-referencing links !
+ *                           * remarked most changes with *koessi*
+ *                           * added GoldEd-foldmarks
+ *                           * compiled successfully with SAS-C 6.3
+ *     07-Aug-1995   -       * automated foldmarks "\***"
+ *                           ! GoldEd's foldmarks == RoboDoc marker !
+ *                           * quoted source parsing enhanced
+ *     08-Aug-1995   -       * a lot of while instead of for
+ *                           * a lot of switch() instead of ifelse
+ *                           * version defined
+ *                           * RB_Say, RB_Panic now useable like printf()
+ *                             new formats for nearly all output-strings
+ *                           * char *whoami is global copy of argv[0]
+ *                           * BOLD <- MAKE_LARGE && AMIGAGUIDE
+ *                           * succesfully compiled&tested on HPUX
+ *                           (HP9000/800)
+ *                           * optimized listfunctions
+ *                           * encapsulated header- and link-
+ *                             allocating and freeing
+ *                           * RB_Find_Function_Name() replaced
+ *                             with RB_FilePart()
+ *  Modifications by FNC Slothouber.
+ *    18-Aug-1995   -  v3.0  
+ *      o New scanner that searches for a set default markers 
+ *        that define what is a comment or what is not and that 
+ *        define what or what is not a header/end marker.
+ *      o Added Beast Support
+ *    27-Aug-1995   - v3.0b  
+ *      o Fixed a bug with the defaults file
+ *      o Improved search algorithm RoboDoc is now 5.8 times faster.
+ *    06-Sep-1995   - v3.0c  
+ *      o Bug fixes
+ *    08-Oct-1995   - v3.0d  
+ *      o Bug fixes
+ *    04-Feb-1996   - v3.0e  
+ *      o fixed the problem with the TOC that included links to headers that
+ *                             were not selected. (i.e internal)
+ *  Modifications by apang
+ *    08-Mar-1996   - v3.0f  
+ *      o Cleaner build for Borland C++ 4.52
+ *      o Added more markers (C++, Pascal, Modula-2, COBOL)
+ *      o Added more item types/names
+ *      o Added #defines for the preamble (COMMENT_ROBODOC and 
+ *        COMMENT_COPYRIGHT)
+ *      o BLANK_HEADER for detection of asterisk'd lines
+ *      o RB_Say() the GENERIC header warning instead of using printf()
+ *      o Indents SOURCE body in output
+ *      o ASCII respects the TOC flag; removed extraneous newline after 
+ *        formfeed (so it's more like AutoDoc)
+ *      o HTML output fixed to handle '<', '>', and '&'
+ *      o LaTeX attributes and '%' handling added; fancied up the output a bit
+ *      o RTF support added
+ *      o Changed some fprintf()'s to fputc()'s for potentially lower overhead
+ *      o Fixed line eater bug
+ *      o More general fix to the TOC problem of including internal links 
+ *        when it wasn't selected
+ *  Modifications by FNC Slothouber.
+ *    01-April-1996  - v3.0h 
+ *      o Added ';' to &gt and &lt so lynx also recognizes them.
+ *      o Fancied up the HTML output.
+ *    10-July-1996   - v3.0i 
+ *      o Bug Fix, Both the options INTERNAL and INTERNALONLY did not 
+ *        work correctly.
+ *  Modifications by agi
+ *    15-Dec-1997    - v3.0j 
+ *      o cleaned the HTML-output, so it now conforms to the DTD for HTML-3.2
+ *      o TOC now is an ordered list (<OL> and <LI>)
+ *      o added "<!DOCTYPE..>"
+ *      o added quotes to values of some HTML-attributes
+ *      o more compatible implementation of the SGML-comment containing 
+ *        copyright-info replaced all occurrences of <B><PRE>.. by <PRE><B>
+ *      o replaced <H2/3> by <H1/2>
+ *      o fixed two minor warnings reported by gcc -Wall
+ *  Modifications by FNC Slothouber.
+ *    14-Aug-1998    - v3.0k * Tcl/Tk '#' handling added;
+ *       Feb-1999    - v3.0l * Added function to reverse the header list.
+ *  Modifications by Petteri Kettunen
+ *    Feb 1999      - v3.0m 
+ *      o Changed background color to white
+ *      o Changed size of Table of Contents title. (H3 instead of H1)
+ *      o The reverse function also reversed the sorted header list, 
+ *        fixed this.
+ *  Modifications by Petteri Kettunen
+ *   August 1999 - v3.0m+ 
+ *      o Support for folding in SOURCE items, HTML only.
+ *      o indent -kr 
+ *      o Added options FOLD and C
+ *  Modifications by FNC Slothouber. 
+ *   August 1999 - v3.1   
+ *      o More documentation and a more informative usage() function. 
+ *      o GPL-ed.
+ *      o robodoc -c prints licence
+ *      o removed a number of Source items from the documentation to reduce 
+ *        the size of the robodoc.c.html file...  no fun for people
+ *        to download a >100k file.
+ *      o removed the warning about not using a robodoc default file.
+ *      o indent -orig -i2 -nbc -ncdb -bad -bap
+ *      o Fixed the warnings. 
+ *      o Fixed some occurrences of (evil cast)malloc  (thou shalt not 
+ *        cast malloc :) 
+ *      o ROBODoc now returns EXIT_FAILURE or  EXIT_SUCCESS, as defined 
+ *        in <stdlib.h>
+ *      o Fixed a memory leak in RB_Analyse_Document()
+ *   Oct 1999 - v3.1b     
+ *      o <A NAME="source code file name"> is generated at the beginning of 
+ *        each document. A mention of the source code name in another 
+ *        document creates a link to this name (provided you use xrefs).
+ *      o Moved most #defines and enums to robodoc.h
+ *      o Made ROBODoc more forgiving in reading the xrefs file. Empty 
+ *        lines are allowed and also spaces at the end of a file name.
+ *   Nov 1999 - v3.1c  -- From patches that I received from Stefan Kost
+ *      o renamed BEAST METHODS -> METHODS
+ *      o renamed BEAST ATTRIBUTES -> ATTRIBUTES
+ *      o added new items useful for object oriented programming; some of 
+ *        these items are already used in os3.1 autodocs
+ *        TAGS, COMMANDS, DERIVED FROM, DERIVED BY, USES,
+ *        CHILDREN, USED BY, PARENTS, USAGE, PURPOSE
+ *      o commented the item names
+ *      o changed item-type enums to end all with _ITEM
+ *      o changed RB_Find_Link to accept names ending with '...'
+ *      o changed copyright comment to be a style-guide conform version string.
+ *      o changed RB_VER[] to be a style-guide conform version string
+ *      o changed AMIGA into _AMIGA, because the first one does not exists, 
+ *        when compiling with NOANSI on SAS C/C++
+ *   Dec 1999 - v3.1d
+ *      o added new header types for, classes, methods, variables, 
+ *        functions, strutures and constants. (Idea of Stefan Kost) 
+ *      o added a command to create a master index file that contains
+ *        sorted pointers to all classes, methods, variables, 
+ *        functions, strutures and constants.
+ *   Dec 1999 - v3.1e
+ *      o added markers for HTML.
+ *      o modified the RB_Find_Link() function to also words that include
+ *        "::". This is used for C++ methods.
+ *      o added a RB_Function_Name() function that correctly extracts the
+ *        function name (or the name of any other object that is documented)
+ *        from the header name.  The old code used RB_FilePart which failed
+ *        on C++ method names. 
+ *      o Fixed a core-dumping bug in RB_Set_Doc_Base()
+ *   Dec 1999 - v3.1f
+ *      o added RB_TimeStamp() to include time stamps in the documentation.
+ *      o Documentation is now generated in LaTeX2e format.
+ *      o added '|****' as begin marker, '|' as remark marker and '|***' as
+ *        end marker for GNU assembler support.
+ *      o ran ident on all source. Using the GNU standard now. 
+ *      o Added new fold markers provided by Petteri
+ *   May 2000 - v3.2
+ *      o Using automake and autoconf.
+ *      o Added fixes to folding code Petteri.
+ *      o Added markers for FORTAN 90
+ *   June 2000 - V3.2.1
+ *      o Added patch from Simo Muinonen: This solved the following
+ *        problem:
+ *          When e.g. a field of a structured C variable (with an
+ *          underscore in its name) is referred to using the
+ *          point notation (e.g. "g_Status.tpstat"), the variable
+ *          name is not recognized as a separate keyword.  This
+ *          can also happen when a keyword is in a comment at the
+ *          end of a sentence with an immediately following ".".
+ *      o Fixed the "stuctures" type in the master index file.
+ *      o Added mailto: support provided by Guillaume Etorre.
+ *    July 2000 - V3.2.2
+ *      o Added option SINGLEDOC
+ *        For LaTeX output this generates documentation without
+ *        the start and end headers.  This way the generated file
+ *        can be included in a master file.  
+ *      o Added master index file for LaTeX output.  The documentation
+ *        gathered from several source files can now be included into
+ *        one big file.
+ *      o Added the option NOSOURCE.  With this option the SOURCE item
+ *        is not included in the documentation.
+ *      o Added the TITLE option. This allows to set the title for
+ *        the master index file.
+ *      o Made the search for headermarkers case insensitve.
+ *        REM == Rem == rem  
+ *    July 2000 - V3.2.3
+ *      o Fixed a bug that caused links of the type
+ *        "someword/anotherword," to be ignored, while
+ *        "someword/anotherword" was recognized.
+ *    Sep 2000 
+ *      o Labels with identical names are now numbered.
+ *    Apr 2001
+ *      o The source file is opened "rb" this I hope will
+ *        make it possible to use Robodoc under windows.
+ *        (Thanks to Carlo Caminati) 
+ *
+ * NOTES
+ *   Has been succesfully compiled:
+ *     On an Amiga with SAS/C, DICE C and gcc (Amiga 1200)
+ *     On a Sun Sparc Station with gcc   (under SunOS 4.1)
+ *     On a Dec Alpha Station
+ *     Under HP/UX on a HP9000/800
+ *     Under IRIX
+ *     On a Linux box with gcc, Slackware, Redhat, and Debian 2.1.
+ * BUGS
+ *   - HTML output is not Lynx friendly -- attributes are applied
+ *     to leading white space =P ... solution: fix Lynx  >=)
+ *   - Can't get the escape character for @ to work in
+ *     AmigaGuide format.
+ *   - Horrible use of feof() and fgets() 
+ *   Other bugs?
+ *     Catch them in a jar and send them to fslothouber@acm.org
+ *     Latest version can be found on 
+ *       http://robodoc.sourceforge.net
+ *       http://www.xs4all.nl/~rfsber/Robo/
+ *       http://freshmeat.net/ 
+ *     
+ ****/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "robodoc.h"
+#include "folds.h"
+#include "headers.h"
+#include "items.h"
+#include "util.h"
+#include "links.h"
+#include "analyser.h"
+#include "generator.h"
+
+#ifdef _AMIGA
+char RB_VER[] = "\0$VER: robodoc " VERSION " " __AMIGADATE__ " (c) by Maverick Software Development 1994-2001";
+#else
+char RB_VER[] = "$VER: robodoc " VERSION " (" __DATE__ ") (c) by Maverick Software Development 1994-2001";
+#endif
+
+
+/****v* ROBODoc/source
+ * NAME
+ *   source -- source code file.
+ * PURPOSE
+ *   Pointer to the file with source code.   
+ * NOTES
+ *   This is a global. It is however only used by main() and
+ *   RB_Close_The_Shop(). All other functions are passed a copy.
+ *
+ *   It is a global so that the file can be closed by
+ *   RB_Close_The_Shop() when the program exits (normally or
+ *   abnormaly).
+ *****
+ */
+
+FILE *source = NULL;
+
+/****v* ROBODoc/documentation
+ * NAME
+ *   documentation -- documentation file.
+ * PURPOSE
+ *   Pointer to the file that will contain the documentation extracted
+ *   from the source code.   
+ * NOTES
+ *   This is a global. It is however only used by main() and
+ *   RB_Close_The_Shop(). All other functions are passed a copy.
+ *
+ *   It is a global so that the file can be closed by
+ *   RB_Close_The_Shop() when the program exits (normally or
+ *   abnormaly).
+ *****
+ */
+
+FILE *documentation = NULL;
+
+
+/****v* ROBODoc/document_title
+ * NAME
+ *   documentat_title -- title for the documentation.
+ * PURPOSE
+ *   Used as the title for master index files or for latex documentation.
+ *****
+ */
+
+char *document_title = NULL;
+
+/****v* ROBODoc/output_mode [2.0]
+ * NAME
+ *   output_mode -- the mode of output
+ * FUNCTION
+ *   Controls which type of output will be generated.
+ * SOURCE
+ */
+
+int output_mode = ASCII;
+
+/*******/
+
+
+/****v* ROBODoc/course_of_action [2.0]
+ * NAME
+ *   course_of_action
+ * FUNCTION
+ *   Global Variable that defines the course of action.
+ * SOURCE
+ */
+
+int course_of_action = DO_MAKE_DOCUMENT;
+
+/*******/
+
+
+/****v* ROBODoc/line_buffer [2.0]
+ * NAME
+ *   line_buffer -- global line buffer
+ * FUNCTION
+ *   Temporary storage area for lines
+ *   that are read from an input file.
+ * SOURCE
+ */
+
+char line_buffer[MAX_LINE_LEN];
+
+/*******/
+
+
+/****v* ROBODoc/line_number [2.0]
+ * NAME
+ *   line_number -- global line counter
+ * PURPOSE
+ *   Keeps track of the number of lines that are read from the source file.
+ * AUTHOR
+ *   Koessi
+ * SOURCE
+ */
+
+int line_number = 0;
+
+/*******/
+
+
+/****v* ROBODoc/use [3.0h]
+ * NAME
+ *   use -- usage string
+ * FUNCTION
+ *   Inform the user how to use ROBODoc.
+ * AUTHOR
+ *   Koessi
+ * SOURCE
+ */
+
+char use[] =
+"ROBODoc Version " VERSION ", autodocs formatter ($Revision$)\n"
+"(c) 1994-2001 Frans Slothouber and Jacco van Weert\n"
+"robodoc comes with ABSOLUTELY NO WARRANTY.\n"
+"This is free software, and you are welcome to redistribute it\n"
+"under certain conditions; type `robodoc -c' for details.\n"
+"\n"
+"Usage:\n"
+"  robodoc <source file> <documentation file> [options]\n"
+"    or\n"
+"  robodoc <xrefs file> <master index file> INDEX [options]\n"
+"\n"
+"You can use one or more of the following options:\n"
+"  GENXREF <xref file>  - to generate an xref file.\n"
+"  XREF    <xrefs file> - if you want to use xref files to create\n"
+"                         cross links.\n"
+"  INDEX           - create a master index file.\n"
+"  TABSIZE <nr_sp> - convert each TAB to nr_sp of spaces.\n"
+"  TOC             - a table of contents will be generated.\n"
+"  SORT            - the headers will be sorted.\n"
+"  -v              - tell robodoc to tell you all about it.\n"
+"  INTERNAL        - headers marked internal will also be included.\n"
+"  INTERNALONLY    - only headers marked internal will be included.\n"
+"  FOLD            - enable folding if HTML output is selected.\n"
+"  C               - Use ANSI C grammar in source items (test, HTML only).\n"
+"The type of output is selected with one of the following switches:\n"
+"  ASCII, GUIDE, HTML, LATEX, or RTF\n"
+"If no type is specified ASCII is used.\n"
+"The following abbreviations are also allowed:\n"
+"  TOC = -t  XREF = -x   SORT = -s  INTERNAL = -i \n"
+"  GENXREF = -g  INTERNALONLY = -io  TABSIZE = -ts\n"
+"Example:\n"
+"  robodoc simulator.c simulator.html HTML -v TOC SORT\n"
+"Authors/Contributors: Frans Slothouber <fslothouber@acm.org>,"
+" Jacco van Weert,\n"
+"  Petteri Kettunen, Bernd Koesling, Thomas Aglassinger, Anthon Pang, and\n"
+"  Stefan Kost\n"
+"For more information, and the lastest version:\n"
+"  http://www.xs4all.nl/~rfsber/Robo/index.html\n"
+"  Send bug reports to <fslothouber@acm.org>.\n";
+
+/********/
+
+
+/****v* ROBODoc/copying [3.1]
+ * NAME
+ *   copying -- licence information
+ * FUNCTION
+ *   inform the user how to copy me
+ * AUTHOR
+ *   Frans
+ *******
+ */
+
+char copying[] =
+"\n"
+" Distributed under the GNU GENERAL PUBLIC LICENSE\n"
+"   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
+" See the source archive for a copy of the complete licence\n"
+" If you do not have it you can get it from\n"
+" http://freshmeat.net/appindex/1999/08/30/936003795.html\n";
+
+
+/* Global variables */
+
+char *whoami = NULL;           /* me,myself&i */
+int tab_size = 8;
+char doc_base[1024];           /* PetteriK */
+
+
+/****f* ROBODoc/RB_Set_Doc_Base
+ * NAME
+ *   RB_Set_Doc_Base -- get file name without extension.
+ ******
+ */
+
+void
+RB_Set_Doc_Base (char *path)
+{
+  int ptr = 0, n = -1;
+
+  while (path[ptr] != '\0')
+    {
+      if (path[ptr] == '.')
+       {
+         n = ptr;
+       }
+      ptr++;
+    }
+  if (n != -1)
+    {
+      strncpy (doc_base, path, n);
+    }
+  else
+    {
+      strcpy (doc_base, path);
+    }
+  RB_Say ("doc_base is \"%s\"\n", doc_base);
+}
+
+
+
+
+/****f* ROBODoc/main [2.0d]
+ * NAME
+ *   main -- Entry point of ROBODoc
+ * SYNOPSIS
+ *   main (int argc, char **argv)
+ * FUNCTION
+ *   Get and parse the arguments.
+ *   Analyse document and generate the documentation.
+ * SOURCE
+ */
+
+int
+main (int argc, char **argv)
+{
+  char *file_with_xrefs, *output_file_for_xrefs;
+
+  whoami = argv[0];            /* global me,myself&i */
+  if (argc < 2)
+    {
+      printf ("%s", use);
+    }
+  else if (argc < 3)
+    {
+      if (strcmp (argv[1], "-c") == 0)
+       {
+         printf ("%s", copying);
+       }
+      else
+       {
+         printf ("%s", use);
+       }
+    }
+  else
+    {
+      RB_Analyse_Arguments (argc, argv, &file_with_xrefs,
+                           &output_file_for_xrefs);
+
+      RB_Say ("Analysing Defaults File\n");
+      RB_Analyse_Defaults_File ();
+
+      RB_Say ("trying to open source file \"%s\"\n", argv[1]);
+      if ((source = fopen (argv[1], "rb")) != NULL)
+       {
+         if (!(course_of_action & DO_INDEX))
+           {
+             RB_Say ("analysing source file \"%s\"\n", argv[1]);
+             RB_Analyse_Document (source);
+              RB_Number_Duplicate_Headers();
+
+             if (course_of_action & DO_SORT)
+               {
+                 RB_Say ("sorting headers\n");
+                 RB_Slow_Sort ();
+               }
+             else
+               {
+                 RB_Reverse_List ();
+               }
+             if ((course_of_action & DO_USE_XREFS) && file_with_xrefs)
+               {
+                 if ((xreffiles_file = fopen (file_with_xrefs, "r")) != NULL)
+                   {
+                     RB_Analyse_Xrefs (xreffiles_file);
+                   }
+                 else
+                   {
+                     RB_Panic ("can't open file with xref files \"%s\"\n",
+                               file_with_xrefs);
+                   }
+               }
+           }
+         else
+           {                   /* INDEX */
+             if ((xreffiles_file = fopen (argv[1], "r")) != NULL)
+               {
+                 RB_Analyse_Xrefs (xreffiles_file);
+               }
+             else
+               {
+                 RB_Panic ("can't open file with xref files \"%s\"\n",
+                           argv[1]);
+               }
+           }
+         if (course_of_action & DO_MAKE_DOCUMENT)
+           {
+             RB_Say ("trying to open destination file \"%s\"\n", argv[2]);
+             if ((documentation = fopen (argv[2], "w")) != NULL)
+               {
+                 RB_Say ("generating documentation\n");
+                 RB_Set_Doc_Base (argv[2]);
+                 RB_Generate_Documentation (documentation,
+                                            RB_FilePart (argv[1]),
+                                            RB_FilePart (argv[2]));
+                 fclose (documentation);
+                 documentation = NULL;
+               }
+             else
+               RB_Panic ("can't open destination file \"%s\"\n", argv[2]);
+           }
+         else if ((course_of_action & DO_MAKE_XREFS)
+                  && output_file_for_xrefs)
+           {
+             RB_Say ("trying to open xref destination file \"%s\"\n",
+                     output_file_for_xrefs);
+             if ((documentation = fopen (output_file_for_xrefs, "w")) != NULL)
+               {
+                 RB_Say ("generating xref destination file \"%s\"\n",
+                         output_file_for_xrefs);
+                 RB_Generate_xrefs (documentation, argv[1], argv[2]);
+                 fclose (documentation);
+                 documentation = NULL;
+               }
+             else
+               RB_Panic ("can't open xref destination file \"%s\"\n",
+                         output_file_for_xrefs);
+           }
+         else if (course_of_action & DO_INDEX)
+           {
+             if ((documentation = fopen (argv[2], "w")) != NULL)
+               {
+                 RB_Generate_Index (documentation, argv[1]);
+                 fclose (documentation);
+                 documentation = NULL;
+               }
+             else
+               {
+                 RB_Panic ("can't open destination file \"%s\"\n", argv[2]);
+               }
+           }
+       }
+      else
+       RB_Panic ("can't open source file \"%s\"\n", argv[1]);
+    }
+  RB_Say ("Ready\n");
+  RB_Close_The_Shop ();
+  return EXIT_SUCCESS;
+}
+
+/*****/
+
+
+
+
+/****f* ROBODoc/RB_Analyse_Arguments [3.0h]
+ * NAME
+ *   RB_Analyse_Arguments
+ * SYNOPSIS
+ *   RB_Analyse_Arguments (argc, argv, file_with_xrefs,
+ *                         output_file_for_xrefs)
+ *   RB_Analyse_Arguments (int, char **, char **, char **)
+ * FUNCTION
+ *   Get and parse the arguments. This is a quite complex function.
+ *   It assumes that the first and second parameter are the
+ *   name of the source file and name of the documentation file
+ *   respectively. They are therefore skipped.
+ *   May modifie: output_mode, course_of_action, file_with_xrefs, 
+ *   output_file_for_xrefs, document_title.
+ * SOURCE
+ */
+
+void
+RB_Analyse_Arguments (int argc, char **argv,
+                     char **file_with_xrefs,
+                     char **output_file_for_xrefs)
+{
+  char **parameter;
+  int parameter_nr;
+
+  for (parameter_nr = argc - 3, parameter = argv + 3;
+       parameter_nr > 0;
+       parameter++, parameter_nr--)
+    {
+
+      if (!RB_Str_Case_Cmp (*parameter, "HTML"))
+       output_mode = HTML;
+      else if (!RB_Str_Case_Cmp (*parameter, "GUIDE"))
+       output_mode = AMIGAGUIDE;
+      else if (!RB_Str_Case_Cmp (*parameter, "LATEX"))
+       output_mode = LATEX;
+      else if (!RB_Str_Case_Cmp (*parameter, "ASCII"))
+       output_mode = ASCII;
+      else if (!RB_Str_Case_Cmp (*parameter, "RTF"))
+       output_mode = RTF;
+      else if (!RB_Str_Case_Cmp (*parameter, "FOLD"))
+       extra_flags |= FOLD;    /* PetteriK */
+      else if (!RB_Str_Case_Cmp (*parameter, "C"))
+       extra_flags |= C_MODE;  /* PetteriK */
+      else if (!RB_Str_Case_Cmp (*parameter, "SORT") ||
+              !RB_Str_Case_Cmp (*parameter, "-S"))
+       course_of_action |= DO_SORT;
+      else if (!RB_Str_Case_Cmp (*parameter, "INDEX"))
+       {
+         course_of_action |= DO_INDEX;
+         course_of_action &= ~DO_MAKE_DOCUMENT;
+       }
+      else if (!RB_Str_Case_Cmp (*parameter, "INTERNAL") ||
+              !RB_Str_Case_Cmp (*parameter, "-I"))
+       course_of_action |= DO_INCLUDE_INTERNAL;
+      else if (!RB_Str_Case_Cmp (*parameter, "SINGLEDOC"))            
+       course_of_action |= DO_SINGLEDOC;
+      else if (!RB_Str_Case_Cmp (*parameter, "NOSOURCE"))             
+       course_of_action |= DO_NOSOURCE;
+      else if (!RB_Str_Case_Cmp (*parameter, "INTERNALONLY") ||
+              !RB_Str_Case_Cmp (*parameter, "-IO"))
+       course_of_action |= DO_INTERNAL_ONLY;
+      else if (!RB_Str_Case_Cmp (*parameter, "TOC") ||
+              !RB_Str_Case_Cmp (*parameter, "-T"))
+       course_of_action |= DO_TOC;
+      else if (!RB_Str_Case_Cmp (*parameter, "-V"))
+       course_of_action |= DO_TELL;
+      else if (!RB_Str_Case_Cmp (*parameter, "TITLE"))
+       {
+         if (--parameter_nr)
+           {
+             parameter++;
+             document_title = *parameter;
+             RB_Say ("TITLE=\"%s\"\n", *document_title);
+           }
+         else
+           RB_Panic ("you must specify a title with the TITLE option\n");
+       }
+      else if (!RB_Str_Case_Cmp (*parameter, "XREF") ||
+              !RB_Str_Case_Cmp (*parameter, "-X"))
+       {
+         if (--parameter_nr)
+           {
+             parameter++;
+             *file_with_xrefs = *parameter;
+             RB_Say ("XREF=\"%s\"\n", *file_with_xrefs);
+             course_of_action |= DO_USE_XREFS;
+           }
+         else
+           RB_Panic ("you must specify a xref file with the XREF option\n");
+       }
+      else if (!RB_Str_Case_Cmp (*parameter, "TABSIZE") ||
+              !RB_Str_Case_Cmp (*parameter, "-TS"))
+       {
+         if (--parameter_nr)
+           {
+             parameter++;
+             tab_size = atoi (*parameter);
+           }
+         else
+           {
+             RB_Panic ("you must specify the number of spaces with the"
+                       " TABSIZE option\n");
+           }
+       }
+      else if (!RB_Str_Case_Cmp (*parameter, "GENXREF") ||
+              !RB_Str_Case_Cmp (*parameter, "-G"))
+       {
+         if (--parameter_nr)
+           {
+             ++parameter;
+             *output_file_for_xrefs = *parameter;
+             RB_Say ("GENXREF=\"%s\"\n", *output_file_for_xrefs);
+             course_of_action |= DO_MAKE_XREFS;
+             course_of_action &= ~DO_MAKE_DOCUMENT;
+           }
+         else
+           RB_Panic ("you must specify a xref file with the GENXREF option\n");
+       }
+      else
+       {
+         RB_Panic ("unknown option %s\n", *parameter);
+       }
+    }
+  if ((course_of_action & DO_USE_XREFS) &&
+      (output_mode == ASCII) &&
+      !(course_of_action & DO_INDEX))
+    {
+      printf ("%s: WARNING, you can not use xrefs when you generate\n"
+             "\t\tdocumentation in ASCII [discarding switch]\n",
+             argv[0]);
+      course_of_action &= ~DO_USE_XREFS;
+    }
+  if (course_of_action & DO_INDEX)
+    {
+      if ((output_mode != LATEX) && (output_mode != HTML)) { 
+           RB_Panic ("you can only use the INDEX option in combination with LATEX or HTML\n");
+      }
+    }
+}
+
+/******/
+
+
+
+/****i* ROBODoc/RB_Close_The_Shop [3.0b]
+ * NAME
+ *   RB_Close_The_Shop -- free resources.
+ * SYNOPSIS
+ *   void RB_Close_The_Shop ()
+ * FUNCTION
+ *   Frees all resources used by robodoc.
+ * SEE ALSO
+ *   RB_Free_Header(), RB_Free_Link()
+ * SOURCE
+ */
+
+void
+RB_Close_The_Shop (void)
+{
+  struct RB_header *cur_header, *tmp_header;
+  struct RB_link *cur_link, *tmp_link;
+
+  if (source)
+    fclose (source);
+  if (documentation)
+    fclose (documentation);
+  if (xreffiles_file)
+    fclose (xreffiles_file);
+  if (xref_file)
+    fclose (xref_file);
+
+  for (cur_header = first_header; cur_header;)
+    {
+      tmp_header = cur_header->next_header;
+      RB_Free_Header (cur_header);
+      cur_header = tmp_header;
+    }
+
+  for (cur_link = first_link; cur_link;)
+    {
+      tmp_link = cur_link->next_link;
+      RB_Free_Link (cur_link);
+      cur_link = tmp_link;
+    }
+
+  if (header_index)
+    free (header_index);
+  if (link_index)
+    free (link_index);
+}
+
+/******/
diff --git a/util/robodoc/Source/robodoc.h b/util/robodoc/Source/robodoc.h
new file mode 100644 (file)
index 0000000..f806a3d
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *    ROBODoc - a documentation extraction program for several languages.
+ *
+ *    Copyright (C) 1994-1999  Frans Slothouber and Jacco van Weert.
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
+ *    MA  02111-1307  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef VERSION
+#define VERSION "unknown"
+#endif
+
+#define COMMENT_ROBODOC \
+    "Generated with ROBODoc Version " VERSION " (" __DATE__ ")\n"
+#define COMMENT_COPYRIGHT\
+    "ROBODoc (c) 1994-2001 by Frans Slothouber and Jacco van Weert.\n"
+
+#define DO_SORT             (1<<0)
+#define DO_MAKE_XREFS       (1<<1)
+#define DO_USE_XREFS        (1<<2)
+#define DO_TOC              (1<<3)
+#define DO_MAKE_DOCUMENT    (1<<4)
+#define DO_INCLUDE_INTERNAL (1<<5)
+#define DO_INTERNAL_ONLY    (1<<6)
+#define DO_TELL             (1<<7)
+#define DO_INDEX            (1<<8)
+#define DO_SINGLEDOC        (1<<9)
+#define DO_NOSOURCE         (1<<10)
+
+/* Output Modes */
+
+enum
+  {
+    ASCII = 0, AMIGAGUIDE, HTML, LATEX, RTF, SIZE_MODES
+  };
+
+/* Reserved for Future Use */
+
+enum
+  {
+    ANSI, GNUINFO, TROFF, XML
+  };
+
+/* Evil macros !! */
+
+#define skip_while(cond) { for (;*cur_char && (cond);cur_char++) ; }
+#define find_eol   { for (;*cur_char && *cur_char!='\n';cur_char++) ; }
+#define find_quote { for (;*cur_char && *cur_char!='\"';cur_char++) ; }
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE  1
+#endif
+
+/* Prototypes */
+
+void RB_Analyse_Arguments (int, char **, char **, char **);
+void RB_Set_Doc_Base (char *path);
+void RB_Close_The_Shop (void);
+
+
+#define MAX_LINE_LEN 512
+
+extern char *whoami;
+extern char *document_title;
+extern int output_mode;
+extern int course_of_action;
+extern int tab_size;
+extern char doc_base[1024];    /* PetteriK */
+extern int line_number;
+extern char line_buffer[MAX_LINE_LEN];
diff --git a/util/robodoc/Source/stamp-h.in b/util/robodoc/Source/stamp-h.in
new file mode 100644 (file)
index 0000000..9788f70
--- /dev/null
@@ -0,0 +1 @@
+timestamp
diff --git a/util/robodoc/Source/util.c b/util/robodoc/Source/util.c
new file mode 100644 (file)
index 0000000..18ff5f3
--- /dev/null
@@ -0,0 +1,657 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>            /* for RB_Say() */
+
+#include "robodoc.h"
+#include "links.h"
+#include "headers.h"
+#include "folds.h"
+#include "items.h"
+#include "util.h"
+#include "time.h"
+
+
+
+
+/****f* ROBODoc/RB_FilePart [2.0x]
+ * NAME
+ *   RB_FilePart
+ * SYNOPSIS
+ *   char *RB_FilePart(char *file_name)
+ * FUNCTION
+ *   return the basename (like Amiga/Dos/FilePart())
+ * NOTES
+ *   koessi
+ * SEE ALSO
+ * SOURCE
+ */
+
+char *
+RB_FilePart (char *file_name)
+{
+  char *cur_char;
+  char c;
+
+  if ((cur_char = file_name) != NULL)
+    {
+      for (; (c = *cur_char) != '\0'; ++cur_char)
+       {
+         if ((c == '/') || (c == ':'))
+           {
+             ++cur_char;
+             while ('/' == *cur_char)
+               ++cur_char;
+
+             if (*cur_char)
+               file_name = cur_char;
+           }
+       }
+    }
+  return (file_name);
+}
+
+/*** RB_File_Part ***/
+
+
+
+/****f* ROBODoc/RB_Analyse_Defaults_File [3.0b]
+ * NAME
+ *   RB_Analyse_Defaults_file -- read default from defaults file
+ * SYNOPSIS
+ *   RB_Analyse_Defaults_file
+ * FUNCTION
+ *   Read the default vaules from the default file.
+ * NOTES
+ *   FS: The use of while (!feof(defaults_file)) {
+ *       is wrong here. Should check return value of
+ *       fgets().
+ * SOURCE
+ */
+
+void
+RB_Analyse_Defaults_File ()
+{
+  FILE *defaults_file;
+
+  /* defaults file in working directory? */
+  defaults_file = fopen ("robodoc.defaults", "r");
+  if (defaults_file == NULL)
+    {
+      /* try again from the directory from 
+         which this application was started  */
+#ifdef _MSC_VER
+      /* windows */
+      char path[_MAX_PATH], *c;
+
+      strcpy (path, whoami);
+      if ((c = strrchr (path, '\\')) != NULL)
+       {
+         *c = '\0';
+         strcat (path, "\\");
+       }
+      strcat (path, "robodoc.defaults");
+      defaults_file = fopen (path, "r");
+#else
+      /* non-windows ... to be done */
+#endif /* _MSC_VER */
+    }
+  if (defaults_file != NULL)
+    {
+      while (!feof (defaults_file))
+       {
+         char *cur_char;
+
+         *line_buffer = '\0';
+
+         fgets (line_buffer, MAX_LINE_LEN, defaults_file);
+
+         if (*line_buffer != '\n')
+           {
+             int item_type;
+
+             item_type = RB_Get_Item_Type (line_buffer);
+             if (item_type != NO_ITEM)
+               {
+                 char *values;
+
+                 item_attributes[item_type] = ITEM_NAME_LARGE_FONT;
+
+                 cur_char = line_buffer + strlen (item_names[item_type]);
+                 for (; *cur_char && isspace (*cur_char); cur_char++);
+
+                 while (*cur_char)
+                   {
+                     for (values = cur_char;
+                          *cur_char && !isspace (*cur_char);
+                          cur_char++);
+                     if (*cur_char)
+                       {
+                         int item_attr;
+
+                         *cur_char = '\0';
+                         item_attr = RB_Get_Item_Attr (values);
+                         if (item_attr != MAKE_NORMAL)
+                           {
+                             RB_Say ("Default: %s = %s\n", 
+                                     item_names[item_type],
+                                     item_attr_names[item_attr]);
+                             item_attributes[item_type] |=
+                               (1 << (item_attr + 1));
+                           }
+                       }
+                     for (cur_char++; *cur_char && isspace (*cur_char);
+                          cur_char++);
+                   }
+               }
+           }
+       }
+      fclose (defaults_file);
+    }
+/* else { printf("%s: WARNING, robodoc.defaults file was not found.\n",
+ * whoami); printf("\t\tyou should really use one.\n"); } */
+}
+
+/**********/
+
+
+
+/****f* ROBODoc/RB_Skip_Remark_Marker [2.0e]
+ * NAME
+ *    RB_Skip_Remark_Marker
+ * SYNOPSIS
+ *     text  = RB_Skip_Remark_Marker (line_buffer)
+ *    char *                            char *
+ * FUNCTION
+ *    Scan and search for a recognized remark marker; skip past the
+ *    marker to the body of the text
+ * NOTE
+ *    This should be in generator.c
+ * SOURCE
+ */
+
+char *
+RB_Skip_Remark_Marker (char *line_buffer)
+{
+  int marker, found;
+  char *cur_char, *cur_mchar;
+
+  found = FALSE;
+  cur_char = NULL;
+  for (marker = 0;
+       ((cur_mchar = remark_markers[marker]) != NULL) && !found;
+       marker++)
+    {
+      for (found = TRUE, cur_char = line_buffer;
+          *cur_mchar && *cur_char && found;
+          cur_mchar++, cur_char++)
+       {
+         if (tolower(*cur_mchar) != tolower(*cur_char))
+           found = FALSE;
+       }
+    }
+  return (cur_char);
+}
+
+/**************/
+
+
+
+
+/****f* ROBODoc/RB_Slow_Sort [2.0]
+ * NAME
+ *   RB_Slow_Sort -- sort list of headers alphabetically
+ * SYNOPSIS
+ *   RB_Slow_Sort ()
+ * FUNCTION
+ *   Sorts the list of headers according to the header name
+ *   in alphabetically fashion.
+ * NOTES
+ *   This isn't a particularly speedy way of sorting.
+ * SOURCE
+ */
+
+void
+RB_Slow_Sort (void)
+{
+  struct RB_header *cur_header, *unsorted_headers, *bigger_header;
+
+  if ((unsorted_headers = first_header) != NULL)
+    {                          /* additional
+                                * check *koessi */
+      for (first_header = NULL;
+          unsorted_headers->next_header;)
+       {
+         for (bigger_header = unsorted_headers,
+              cur_header = bigger_header->next_header;
+              cur_header;
+              cur_header = cur_header->next_header)
+           {
+             if (strcmp (cur_header->name, bigger_header->name) > 0)
+               bigger_header = cur_header;
+           }
+         RB_Remove_From_List (&unsorted_headers, bigger_header);
+         RB_Insert_In_List (&first_header, bigger_header);
+       }
+      RB_Insert_In_List (&first_header, unsorted_headers);
+    }
+}
+
+/*********/
+
+
+/****f* ROBODoc/RB_Insert_In_List [2.0]
+ * NAME
+ *   RB_Insert_In_List -- Insert a header in a list.
+ * SYNOPSIS
+ *   RB_Insert_In_List (anchor,new_header)
+ *
+ *   RB_Insert_In_List (struct RB_header **, struct RB_header *)
+ * FUNCTION
+ *   Insert a node in a doubly linked list.
+ * INPUTS
+ *   anchor     - pointer to the first node in the list.
+ *   new_header - node to be inserted.
+ * MODIFICATION HISTORY
+ *   8. August 1995      --  optimized by koessi
+ * NOTES
+ *   
+ * SOURCE
+ */
+
+void
+RB_Insert_In_List (struct RB_header **anchor,
+                  struct RB_header *new_header)
+{
+  struct RB_header *old_header;
+
+  if ((old_header = *anchor) != NULL)
+    old_header->prev_header = new_header;
+  new_header->next_header = old_header;
+  new_header->prev_header = NULL;
+  *anchor = new_header;
+}
+
+/*** RB_Insert_In_List ***/
+
+/****f* ROBODoc/RB_Reverse_List [2.0]
+ * NAME
+ *   RB_Reverse_List -- Insert a header in a list.
+ * SYNOPSIS
+ *   RB_Reverse_List (void)
+ * FUNCTION
+ *
+ * INPUTS
+ *
+ * MODIFICATION HISTORY
+ *
+ * NOTES
+ *
+ * SOURCE
+ */
+
+void
+RB_Reverse_List (void)
+{
+  struct RB_header *cur_header;
+  struct RB_header *temp_header;
+
+  for (cur_header = first_header;
+       cur_header;
+    )
+    {
+      first_header = cur_header;
+      temp_header = cur_header->next_header;
+      cur_header->next_header = cur_header->prev_header;
+      cur_header->prev_header = temp_header;
+      cur_header = temp_header;
+    }
+}
+
+/*** ***/
+
+
+/****f* ROBODoc/RB_Remove_From_List [2.0]
+ * NAME
+ *   RB_Remove_From_List -- remove a header from a list.
+ * SYNOPSIS
+ *   RB_Remove_From_List (anchor, old_header)
+ *   RB_Remove_From_List (struct RB_header **, struct RB_header *)
+ * MODIFICATION HISTORY
+ *   8. August 1995      --  optimized by koessi
+ * SOURCE
+ */
+
+void
+RB_Remove_From_List (struct RB_header **anchor,
+                    struct RB_header *old_header)
+{
+  struct RB_header *next_header = old_header->next_header;
+  struct RB_header *prev_header = old_header->prev_header;
+
+  if (next_header)
+    next_header->prev_header = prev_header;
+  if (prev_header)
+    prev_header->next_header = next_header;
+  else
+    *anchor = next_header;
+}
+
+/********/
+
+
+/****f* ROBODoc/RB_Alloc_Header [2.01]
+ * NAME
+ *   RB_Alloc_Header            -- oop
+ * SYNOPSIS
+ *   struct RB_header *RB_Alloc_Header( void )
+ * FUNCTION
+ *   allocate the struct RB_header
+ * RESULT
+ *   struct RB_header *      -- all attributes/pointers set to zero
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Free_Header()
+ * SOURCE
+ */
+
+struct RB_header *
+RB_Alloc_Header (void)
+{
+  struct RB_header *new_header;
+
+  if ((new_header = malloc (sizeof (struct RB_header))) != NULL)
+      memset (new_header, 0, sizeof (struct RB_header));
+  else
+    RB_Panic ("out of memory! [Alloc Header]\n");
+  return (new_header);
+}
+
+/********/
+
+
+/****f* ROBODoc/RB_Free_Header [2.01]
+ * NAME
+ *   RB_Free_Header             -- oop
+ * SYNOPSIS
+ *   void RB_Free_Header( struct RB_header *header )
+ * FUNCTION
+ *   free struct RB_header and associated strings
+ * INPUTS
+ *   struct RB_header *header -- this one
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Alloc_Header(), RB_Close_The_Shop()
+ * SOURCE
+ */
+
+void
+RB_Free_Header (struct RB_header *header)
+{
+  if (header)
+    {
+      if (header->version)
+       free (header->version);
+      if (header->name)
+       free (header->name);
+      if (header->contents)
+       free (header->contents);
+      free (header);
+    }
+}
+
+/************/
+
+
+/****i* ROBODoc/RB_WordLen [2.01]
+ * NAME
+ *   RB_WordLen -- like strlen
+ * SYNOPSIS
+ *   int RB_WordLen( char *str )
+ * FUNCTION
+ *   get the amount of bytes until next space
+ * INPUTS
+ *   char *str -- the word
+ * RESULT
+ *   int -- length of the next word or 0
+ * AUTHOR
+ *   Koessi
+ * SEE ALSO
+ *   RB_Find_Header_Name()
+ * SOURCE
+ */
+
+int
+RB_WordLen (char *str)
+{
+  int len;
+  char c;
+
+  for (len = 0; ((c = *str) != '\0') && !isspace (c) && (c != '\n');
+       ++str, ++len);
+  return (len);
+}
+
+/*** RB_WordLen ***/
+
+
+/****i* ROBODoc/RB_StrDup [2.01]
+ * NAME
+ *   RB_StrDup
+ * SYNOPSIS
+ *   char *RB_StrDup( char *str )
+ * FUNCTION
+ *   duplicate the given string
+ * INPUTS
+ *   char *str               -- source
+ * RESULT
+ *   char *                  -- destination
+ * AUTHOR
+ *   Koessi
+ * SOURCE
+ */
+
+char *
+RB_StrDup (char *str)
+{
+  char *dupstr;
+  if ((dupstr = malloc ((strlen (str) + 1) * sizeof (char))) != NULL)
+      strcpy (dupstr, str);
+  else
+    RB_Panic ("out of memory! [StrDup]\n");
+  return (dupstr);
+}
+
+/*** RB_StrDup ***/
+
+
+/****f* ROBODoc/RB_CookStr [3.0h]
+ * NAME
+ *   RB_CookStr
+ * SYNOPSIS
+ *   char *RB_CookStr( char *str )
+ * FUNCTION
+ *   duplicate the given string, massaging it for the current output_mode
+ * INPUTS
+ *   char *str               -- source
+ * RESULT
+ *   char *                  -- destination
+ * AUTHOR
+ *   apang
+ * NOTES
+ *   Doesn't try/need to be as aggressive as RB_Generate_Item_Body()
+ * SOURCE
+ */
+
+char *
+RB_CookStr (char *str)
+{
+  static char work_buf[MAX_LINE_LEN];
+  char *cptr, c;
+  int i;
+
+  cptr = work_buf;
+  switch (output_mode)
+    {
+    case LATEX:
+      for (i = 0; ((c = *str++) != '\0') && (i < (MAX_LINE_LEN - 1));)
+       {
+         i++;
+         if (c == '_')
+           {
+             if (i < (MAX_LINE_LEN - 1))
+               {
+                 *cptr++ = '\\';
+                 *cptr++ = '_';
+                 i++;
+               }
+             else
+               {
+                 break;
+               }
+           }
+         else
+           {
+             *cptr++ = c;
+           }
+       }
+      break;
+
+    case RTF:
+      for (; (c = *str++) != '\0';)
+       {
+         if (isalnum (c) || c == '.' || c == '_')
+           {
+             *cptr++ = c;
+           }
+       }
+      break;
+
+    default:
+      return RB_StrDup (str);
+    }
+
+  *cptr = '\0';
+  return RB_StrDup (work_buf);
+}
+
+/*** RB_CookStr ***/
+
+
+/****f* ROBODoc/RB_Say [2.01]
+ * NAME
+ *   RB_Say                     -- varargs
+ * SYNOPSIS
+ *   void RB_Say( char *what, char *why, ... )
+ * FUNCTION
+ *   say what's going on
+ * INPUTS
+ *   char *format            -- formatstring
+ *    ...                    -- parameters
+ * AUTHOR
+ *   Koessi
+ * SOURCE
+ */
+
+void
+RB_Say (char *format,...)
+{
+  va_list ap;
+
+  if (course_of_action & DO_TELL)
+    {
+      va_start (ap, format);
+      printf ("%s: ", whoami);
+      vprintf (format, ap);
+      va_end (ap);
+    }
+}
+
+/*** RB_Say ***/
+
+
+/****f* ROBODoc/RB_Panic [2.01]
+ * NAME
+ *   RB_Panic -- free resources and shut down
+ * SYNOPSIS
+ *   void RB_Panic( char *format, char *why, ... )
+ * FUNCTION
+ *   Print error message.
+ *   Frees all resources used by robodoc.
+ *   Terminates program
+ * INPUTS
+ *   char *format            -- formatstring
+ *   ...                     -- parameters
+ * AUTHOR
+ *   Koessi
+ * SOURCE
+ */
+
+void
+RB_Panic (char *format,...)
+{
+  va_list ap;
+
+  va_start (ap, format);
+  printf ("%s: FATAL ERROR - [line %d]\n", whoami, line_number);
+  printf ("%s: %s\n%s: ", whoami, line_buffer, whoami);
+  vprintf (format, ap);
+  printf ("%s: closing down...\n", whoami);
+  va_end (ap);
+  RB_Close_The_Shop ();
+  exit (EXIT_FAILURE);
+}
+
+/*** RB_Panic ***/
+
+
+
+
+/****f* ROBODoc/RB_Str_Case_Cmp
+ * NAME
+ *   RB_Str_Case_Cmp
+ * SYNOPSIS
+ *   int      RB_Str_Case_Cmp(char *, char *)
+ *   result = RB_Str_Case_Cmp(s, t)
+ * FUNCTION
+ *   Compare two strings, regardless of the case of the characters.
+ * RESULT
+ *    0  s == t
+ *   -1  s < t
+ *    1  s > t
+ * SOURCE
+ */
+
+int
+RB_Str_Case_Cmp (char *s, char *t)
+{
+  for (; tolower (*s) == tolower (*t); s++, t++)
+    if (*s == '\0')
+      return 0;
+  return (int) (tolower (*s) - tolower (*t));
+}
+
+/*********/
+
+
+/****f* ROBODoc/RB_TimeStamp
+ * NAME
+ *   RB_TimeStamp -- print a time stamp
+ *****
+ */
+
+void 
+RB_TimeStamp (FILE * f)
+{
+  time_t ttp;
+  char timeBuffer[255];
+
+  time (&ttp);
+  strftime (timeBuffer, 255, "%a %b %d %H:%M:%S %Y\n", localtime (&ttp));
+  fprintf (f, "%s", timeBuffer);
+}
diff --git a/util/robodoc/Source/util.h b/util/robodoc/Source/util.h
new file mode 100644 (file)
index 0000000..7f78abe
--- /dev/null
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+#ifndef ROBODOC_UTIL_H
+#define ROBODOC_UTIL_H
+
+char *RB_FilePart (char *);
+void RB_Analyse_Defaults_File (void);
+char *RB_Skip_Remark_Marker (char *line_buffer);
+void RB_Slow_Sort (void);
+void RB_Reverse_List (void);
+void RB_Insert_In_List (struct RB_header **, struct RB_header *);
+void RB_Remove_From_List (struct RB_header **, struct RB_header *);
+struct RB_header *RB_Alloc_Header (void);
+void RB_Free_Header (struct RB_header *);
+int RB_WordLen (char *);
+char *RB_StrDup (char *);
+char *RB_CookStr (char *);
+void RB_Say (char *,...);
+void RB_Panic (char *,...);
+void RB_Close_The_Shop (void);
+int RB_Str_Case_Cmp (char *s, char *t);
+void RB_TimeStamp (FILE * f);
+
+
+#endif /* ROBODOC_UTIL_H */
diff --git a/util/robodoc/TODO b/util/robodoc/TODO
new file mode 100644 (file)
index 0000000..02bb304
--- /dev/null
@@ -0,0 +1,8 @@
+My wish list:
+
+o Add XML output
+o Improve the support for LaTeX. Instead of a lot of
+  individual documents robodoc should create one big
+  book with documentation. 
+o Fix the man page so that it also looks good with tkMan
+
diff --git a/util/robodoc/aclocal.m4 b/util/robodoc/aclocal.m4
new file mode 100644 (file)
index 0000000..bd4779c
--- /dev/null
@@ -0,0 +1,127 @@
+dnl aclocal.m4 generated automatically by aclocal 1.4
+
+dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+# Like AC_CONFIG_HEADER, but automatically create stamp file.
+
+AC_DEFUN(AM_CONFIG_HEADER,
+[AC_PREREQ([2.12])
+AC_CONFIG_HEADER([$1])
+dnl When config.status generates a header, we must update the stamp-h file.
+dnl This file resides in the same directory as the config header
+dnl that is generated.  We must strip everything past the first ":",
+dnl and everything past the last "/".
+AC_OUTPUT_COMMANDS(changequote(<<,>>)dnl
+ifelse(patsubst(<<$1>>, <<[^ ]>>, <<>>), <<>>,
+<<test -z "<<$>>CONFIG_HEADERS" || echo timestamp > patsubst(<<$1>>, <<^\([^:]*/\)?.*>>, <<\1>>)stamp-h<<>>dnl>>,
+<<am_indx=1
+for am_file in <<$1>>; do
+  case " <<$>>CONFIG_HEADERS " in
+  *" <<$>>am_file "*<<)>>
+    echo timestamp > `echo <<$>>am_file | sed -e 's%:.*%%' -e 's%[^/]*$%%'`stamp-h$am_indx
+    ;;
+  esac
+  am_indx=`expr "<<$>>am_indx" + 1`
+done<<>>dnl>>)
+changequote([,]))])
+
+# Do all the work for Automake.  This macro actually does too much --
+# some checks are only needed if your package does certain things.
+# But this isn't really a big deal.
+
+# serial 1
+
+dnl Usage:
+dnl AM_INIT_AUTOMAKE(package,version, [no-define])
+
+AC_DEFUN(AM_INIT_AUTOMAKE,
+[AC_REQUIRE([AC_PROG_INSTALL])
+PACKAGE=[$1]
+AC_SUBST(PACKAGE)
+VERSION=[$2]
+AC_SUBST(VERSION)
+dnl test to see if srcdir already configured
+if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
+  AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+fi
+ifelse([$3],,
+AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package]))
+AC_REQUIRE([AM_SANITY_CHECK])
+AC_REQUIRE([AC_ARG_PROGRAM])
+dnl FIXME This is truly gross.
+missing_dir=`cd $ac_aux_dir && pwd`
+AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
+AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
+AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
+AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
+AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
+AC_REQUIRE([AC_PROG_MAKE_SET])])
+
+#
+# Check to make sure that the build environment is sane.
+#
+
+AC_DEFUN(AM_SANITY_CHECK,
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftestfile
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null`
+   if test "[$]*" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftestfile`
+   fi
+   if test "[$]*" != "X $srcdir/configure conftestfile" \
+      && test "[$]*" != "X conftestfile $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "[$]2" = conftestfile
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+rm -f conftest*
+AC_MSG_RESULT(yes)])
+
+dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY)
+dnl The program must properly implement --version.
+AC_DEFUN(AM_MISSING_PROG,
+[AC_MSG_CHECKING(for working $2)
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if ($2 --version) < /dev/null > /dev/null 2>&1; then
+   $1=$2
+   AC_MSG_RESULT(found)
+else
+   $1="$3/missing $2"
+   AC_MSG_RESULT(missing)
+fi
+AC_SUBST($1)])
+
diff --git a/util/robodoc/configure b/util/robodoc/configure
new file mode 100755 (executable)
index 0000000..bc022a5
--- /dev/null
@@ -0,0 +1,1952 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=Source/robodoc.h
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:561: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6
+echo "configure:614: checking whether build environment is sane" >&5
+# Just in case
+sleep 1
+echo timestamp > conftestfile
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null`
+   if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftestfile`
+   fi
+   if test "$*" != "X $srcdir/configure conftestfile" \
+      && test "$*" != "X conftestfile $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      { echo "configure: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" 1>&2; exit 1; }
+   fi
+
+   test "$2" = conftestfile
+   )
+then
+   # Ok.
+   :
+else
+   { echo "configure: error: newly created file is older than distributed files!
+Check your system clock" 1>&2; exit 1; }
+fi
+rm -f conftest*
+echo "$ac_t""yes" 1>&6
+if test "$program_transform_name" = s,x,x,; then
+  program_transform_name=
+else
+  # Double any \ or $.  echo might interpret backslashes.
+  cat <<\EOF_SED > conftestsed
+s,\\,\\\\,g; s,\$,$$,g
+EOF_SED
+  program_transform_name="`echo $program_transform_name|sed -f conftestsed`"
+  rm -f conftestsed
+fi
+test "$program_prefix" != NONE &&
+  program_transform_name="s,^,${program_prefix},; $program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s,\$\$,${program_suffix},; $program_transform_name"
+
+# sed with no file args requires a program.
+test "$program_transform_name" = "" && program_transform_name="s,x,x,"
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:671: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+       @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+PACKAGE=robodoc
+
+VERSION=3.2.3
+
+if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
+  { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; }
+fi
+cat >> confdefs.h <<EOF
+#define PACKAGE "$PACKAGE"
+EOF
+
+cat >> confdefs.h <<EOF
+#define VERSION "$VERSION"
+EOF
+
+
+
+missing_dir=`cd $ac_aux_dir && pwd`
+echo $ac_n "checking for working aclocal""... $ac_c" 1>&6
+echo "configure:717: checking for working aclocal" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (aclocal --version) < /dev/null > /dev/null 2>&1; then
+   ACLOCAL=aclocal
+   echo "$ac_t""found" 1>&6
+else
+   ACLOCAL="$missing_dir/missing aclocal"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working autoconf""... $ac_c" 1>&6
+echo "configure:730: checking for working autoconf" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (autoconf --version) < /dev/null > /dev/null 2>&1; then
+   AUTOCONF=autoconf
+   echo "$ac_t""found" 1>&6
+else
+   AUTOCONF="$missing_dir/missing autoconf"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working automake""... $ac_c" 1>&6
+echo "configure:743: checking for working automake" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (automake --version) < /dev/null > /dev/null 2>&1; then
+   AUTOMAKE=automake
+   echo "$ac_t""found" 1>&6
+else
+   AUTOMAKE="$missing_dir/missing automake"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working autoheader""... $ac_c" 1>&6
+echo "configure:756: checking for working autoheader" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (autoheader --version) < /dev/null > /dev/null 2>&1; then
+   AUTOHEADER=autoheader
+   echo "$ac_t""found" 1>&6
+else
+   AUTOHEADER="$missing_dir/missing autoheader"
+   echo "$ac_t""missing" 1>&6
+fi
+
+echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6
+echo "configure:769: checking for working makeinfo" >&5
+# Run test in a subshell; some versions of sh will print an error if
+# an executable is not found, even if stderr is redirected.
+# Redirect stdin to placate older versions of autoconf.  Sigh.
+if (makeinfo --version) < /dev/null > /dev/null 2>&1; then
+   MAKEINFO=makeinfo
+   echo "$ac_t""found" 1>&6
+else
+   MAKEINFO="$missing_dir/missing makeinfo"
+   echo "$ac_t""missing" 1>&6
+fi
+
+
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:784: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+       @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:814: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:844: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:895: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:927: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 938 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:943: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:969: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:974: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:983: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:1002: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1036: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 1051 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1057: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1068 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1074: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 1085 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1091: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1116: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1121 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1129: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1146 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1164 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1185 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1196: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:1221: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1226 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1275: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1296: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1301 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_size_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+  cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+
+echo $ac_n "checking for strftime""... $ac_c" 1>&6
+echo "configure:1330: checking for strftime" >&5
+if eval "test \"`echo '$''{'ac_cv_func_strftime'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1335 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char strftime(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char strftime();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_strftime) || defined (__stub___strftime)
+choke me
+#else
+strftime();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1358: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_strftime=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_strftime=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'strftime`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_STRFTIME 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+# strftime is in -lintl on SCO UNIX.
+echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6
+echo "configure:1380: checking for strftime in -lintl" >&5
+ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lintl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1388 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char strftime();
+
+int main() {
+strftime()
+; return 0; }
+EOF
+if { (eval echo configure:1399: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_STRFTIME 1
+EOF
+
+LIBS="-lintl $LIBS"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:1426: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1431 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char vprintf(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1454: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:1478: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1483 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1506: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+for ac_func in strstr
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1533: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1538 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1561: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "makefile Docs/makefile Source/makefile Source/config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@PACKAGE@%$PACKAGE%g
+s%@VERSION@%$VERSION%g
+s%@ACLOCAL@%$ACLOCAL%g
+s%@AUTOCONF@%$AUTOCONF%g
+s%@AUTOMAKE@%$AUTOMAKE%g
+s%@AUTOHEADER@%$AUTOHEADER%g
+s%@MAKEINFO@%$MAKEINFO%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"makefile Docs/makefile Source/makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="Source/config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+test -z "$CONFIG_HEADERS" || echo timestamp > Source/stamp-h
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
+
diff --git a/util/robodoc/configure.in b/util/robodoc/configure.in
new file mode 100644 (file)
index 0000000..6c0f5af
--- /dev/null
@@ -0,0 +1,29 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(Source/robodoc.h)
+
+AM_CONFIG_HEADER(Source/config.h)
+
+AM_INIT_AUTOMAKE(robodoc, 3.2.3)
+
+AC_PROG_MAKE_SET
+
+dnl Checks for programs.
+AC_PROG_CC
+
+dnl Checks for libraries.
+
+dnl Checks for header files.
+AC_HEADER_STDC
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+dnl Checks for library functions.
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(strstr)
+
+AC_OUTPUT(makefile Docs/makefile Source/makefile)
+
diff --git a/util/robodoc/install-sh b/util/robodoc/install-sh
new file mode 100755 (executable)
index 0000000..e9de238
--- /dev/null
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/util/robodoc/makefile.am b/util/robodoc/makefile.am
new file mode 100644 (file)
index 0000000..4fd7cf2
--- /dev/null
@@ -0,0 +1,87 @@
+## Process this file with automake to produce Makefile.in
+
+#
+# Information for automake
+#
+
+AUTOMAKE_OPTIONS = dist-zip
+
+SUBDIRS = Docs Source
+
+exampledir = $(prefix)/examples/$(PACKAGE)-$(VERSION)
+
+docdir = $(prefix)/doc/$(PACKAGE)-$(VERSION)
+doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README
+
+EXTRA_DIST = \
+        Docs/robodoc.1 \
+        Docs/robodoc.m4 \
+        Docs/robodoc.html \
+       Docs/general.m4 \
+       Docs/example.c \
+       Docs/tocgen.m4 \
+       Docs/example_makefile \
+        Docs/main.css \
+        Examples/C/prog1.c \
+        Examples/C/prog2.c \
+        Examples/C/prog1.c.html \
+        Examples/C/prog2.c.html \
+       Examples/C/makefile \
+       Examples/CPP/muppets.h \
+       Examples/CPP/muppets.cpp \
+       Examples/CPP/masterindex.html \
+       Examples/CPP/muppets.h.html \
+       Examples/CPP/muppets.cpp.html \
+       Examples/CPP/makefile \
+       Headers/assembler.sample \
+       Headers/basic.sample \
+       Headers/c.sample \
+       Headers/cpp.sample \
+       Headers/fortan.sample \
+       Headers/html.sample \
+       Headers/tcl.sample \
+       Source/makefile.plain
+
+#
+# End of automake stuff
+#
+
+
+myclean:
+       rm -f *~
+       rm -f makefile.in
+       rm -f *.tar.gz *.zip
+       rm -f *.log aclocal.m4 config.cache
+       rm -f install-sh
+       rm -f mkinstalldirs
+       rm -f missing makefile
+       rm -f configure config.status
+       $(MAKE) -f makefile.am -C Docs     myclean
+       $(MAKE) -f makefile.am -C Source   myclean
+       $(MAKE)                -C Examples myclean
+
+Docs/robodoc.html :
+       $(MAKE) -C Docs robodoc.html
+
+Examples/C/prog1.c.html :
+       $(MAKE) -C Examples/C prog1.c.html 
+
+Examples/C/prog2.c.html :
+       $(MAKE) -C Examples/C prog2.c.html 
+
+Examples/CPP/masterindex.html :
+       $(MAKE) -C Examples/CPP masterindex.html 
+
+Examples/CPP/muppets.h.html :
+       $(MAKE) -C Examples/CPP muppets.h.html 
+
+Examples/CPP/muppets.cpp.html :
+       $(MAKE) -C Examples/CPP muppets.cpp.html 
+
+#
+#
+#
+
+docall:
+       $(MAKE) -C Source html
+
diff --git a/util/robodoc/makefile.in b/util/robodoc/makefile.in
new file mode 100644 (file)
index 0000000..91b3090
--- /dev/null
@@ -0,0 +1,430 @@
+# makefile.in generated automatically by automake 1.4 from makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#
+# Information for automake
+#
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = .
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+CC = @CC@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+AUTOMAKE_OPTIONS = dist-zip
+
+SUBDIRS = Docs Source
+
+exampledir = $(prefix)/examples/$(PACKAGE)-$(VERSION)
+
+docdir = $(prefix)/doc/$(PACKAGE)-$(VERSION)
+doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README
+
+EXTRA_DIST =          Docs/robodoc.1         Docs/robodoc.m4         Docs/robodoc.html         Docs/general.m4         Docs/example.c  Docs/tocgen.m4  Docs/example_makefile         Docs/main.css         Examples/C/prog1.c         Examples/C/prog2.c         Examples/C/prog1.c.html         Examples/C/prog2.c.html       Examples/C/makefile     Examples/CPP/muppets.h  Examples/CPP/muppets.cpp        Examples/CPP/masterindex.html   Examples/CPP/muppets.h.html     Examples/CPP/muppets.cpp.html   Examples/CPP/makefile   Headers/assembler.sample        Headers/basic.sample    Headers/c.sample        Headers/cpp.sample      Headers/fortan.sample   Headers/html.sample     Headers/tcl.sample      Source/makefile.plain
+
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ./Source/config.h
+CONFIG_CLEAN_FILES = 
+DATA =  $(doc_DATA)
+
+DIST_COMMON =  README AUTHORS COPYING ChangeLog INSTALL NEWS \
+Source/config.h.in Source/stamp-h.in TODO aclocal.m4 configure \
+configure.in install-sh makefile.am makefile.in missing mkinstalldirs
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+all: all-redirect
+.SUFFIXES:
+$(srcdir)/makefile.in: makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+       cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps makefile
+
+makefile: $(srcdir)/makefile.in  $(top_builddir)/config.status
+       cd $(top_builddir) \
+         && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+$(ACLOCAL_M4):  configure.in 
+       cd $(srcdir) && $(ACLOCAL)
+
+config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       $(SHELL) ./config.status --recheck
+$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES)
+       cd $(srcdir) && $(AUTOCONF)
+
+Source/config.h: Source/stamp-h
+       @if test ! -f $@; then \
+               rm -f Source/stamp-h; \
+               $(MAKE) Source/stamp-h; \
+       else :; fi
+Source/stamp-h: $(srcdir)/Source/config.h.in $(top_builddir)/config.status
+       cd $(top_builddir) \
+         && CONFIG_FILES= CONFIG_HEADERS=Source/config.h \
+            $(SHELL) ./config.status
+       @echo timestamp > Source/stamp-h 2> /dev/null
+$(srcdir)/Source/config.h.in: $(srcdir)/Source/stamp-h.in
+       @if test ! -f $@; then \
+               rm -f $(srcdir)/Source/stamp-h.in; \
+               $(MAKE) $(srcdir)/Source/stamp-h.in; \
+       else :; fi
+$(srcdir)/Source/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+       cd $(top_srcdir) && $(AUTOHEADER)
+       @echo timestamp > $(srcdir)/Source/stamp-h.in 2> /dev/null
+
+mostlyclean-hdr:
+
+clean-hdr:
+
+distclean-hdr:
+       -rm -f Source/config.h
+
+maintainer-clean-hdr:
+
+install-docDATA: $(doc_DATA)
+       @$(NORMAL_INSTALL)
+       $(mkinstalldirs) $(DESTDIR)$(docdir)
+       @list='$(doc_DATA)'; for p in $$list; do \
+         if test -f $(srcdir)/$$p; then \
+           echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(docdir)/$$p"; \
+           $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(docdir)/$$p; \
+         else if test -f $$p; then \
+           echo " $(INSTALL_DATA) $$p $(DESTDIR)$(docdir)/$$p"; \
+           $(INSTALL_DATA) $$p $(DESTDIR)$(docdir)/$$p; \
+         fi; fi; \
+       done
+
+uninstall-docDATA:
+       @$(NORMAL_UNINSTALL)
+       list='$(doc_DATA)'; for p in $$list; do \
+         rm -f $(DESTDIR)$(docdir)/$$p; \
+       done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+
+@SET_MAKE@
+
+all-recursive install-data-recursive install-exec-recursive \
+installdirs-recursive install-recursive uninstall-recursive  \
+check-recursive installcheck-recursive info-recursive dvi-recursive:
+       @set fnord $(MAKEFLAGS); amf=$$2; \
+       dot_seen=no; \
+       target=`echo $@ | sed s/-recursive//`; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         echo "Making $$target in $$subdir"; \
+         if test "$$subdir" = "."; then \
+           dot_seen=yes; \
+           local_target="$$target-am"; \
+         else \
+           local_target="$$target"; \
+         fi; \
+         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+          || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+       done; \
+       if test "$$dot_seen" = "no"; then \
+         $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+       fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+       @set fnord $(MAKEFLAGS); amf=$$2; \
+       dot_seen=no; \
+       rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
+         rev="$$subdir $$rev"; \
+         test "$$subdir" = "." && dot_seen=yes; \
+       done; \
+       test "$$dot_seen" = "no" && rev=". $$rev"; \
+       target=`echo $@ | sed s/-recursive//`; \
+       for subdir in $$rev; do \
+         echo "Making $$target in $$subdir"; \
+         if test "$$subdir" = "."; then \
+           local_target="$$target-am"; \
+         else \
+           local_target="$$target"; \
+         fi; \
+         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+          || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+       done && test -z "$$fail"
+tags-recursive:
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+       done
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+       list='$(SOURCES) $(HEADERS)'; \
+       unique=`for i in $$list; do echo $$i; done | \
+         awk '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       here=`pwd` && cd $(srcdir) \
+         && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+   if test "$$subdir" = .; then :; else \
+           test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \
+   fi; \
+       done; \
+       list='$(SOURCES) $(HEADERS)'; \
+       unique=`for i in $$list; do echo $$i; done | \
+         awk '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+         || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+       -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+       -rm -rf $(distdir)
+       GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz
+       mkdir $(distdir)/=build
+       mkdir $(distdir)/=inst
+       dc_install_base=`cd $(distdir)/=inst && pwd`; \
+       cd $(distdir)/=build \
+         && ../configure --srcdir=.. --prefix=$$dc_install_base \
+         && $(MAKE) $(AM_MAKEFLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) dvi \
+         && $(MAKE) $(AM_MAKEFLAGS) check \
+         && $(MAKE) $(AM_MAKEFLAGS) install \
+         && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+         && $(MAKE) $(AM_MAKEFLAGS) dist
+       -rm -rf $(distdir)
+       @banner="$(distdir).tar.gz is ready for distribution"; \
+       dashes=`echo "$$banner" | sed s/./=/g`; \
+       echo "$$dashes"; \
+       echo "$$banner"; \
+       echo "$$dashes"
+dist: distdir
+       -chmod -R a+r $(distdir)
+       GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
+       -rm -rf $(distdir)
+dist-zip: distdir
+       -chmod -R a+r $(distdir)
+       zip -rq $(distdir).zip $(distdir)
+       -rm -rf $(distdir)
+dist-all: distdir
+       -chmod -R a+r $(distdir)
+       GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
+       zip -rq $(distdir).zip $(distdir)
+       -rm -rf $(distdir)
+distdir: $(DISTFILES)
+       -rm -rf $(distdir)
+       mkdir $(distdir)
+       -chmod 777 $(distdir)
+       $(mkinstalldirs) $(distdir)/Docs $(distdir)/Examples/C \
+          $(distdir)/Examples/CPP $(distdir)/Headers $(distdir)/Source
+       @for file in $(DISTFILES); do \
+         d=$(srcdir); \
+         if test -d $$d/$$file; then \
+           cp -pr $$/$$file $(distdir)/$$file; \
+         else \
+           test -f $(distdir)/$$file \
+           || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+           || cp -p $$d/$$file $(distdir)/$$file || :; \
+         fi; \
+       done
+       for subdir in $(SUBDIRS); do \
+         if test "$$subdir" = .; then :; else \
+           test -d $(distdir)/$$subdir \
+           || mkdir $(distdir)/$$subdir \
+           || exit 1; \
+           chmod 777 $(distdir)/$$subdir; \
+           (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \
+             || exit 1; \
+         fi; \
+       done
+info-am:
+info: info-recursive
+dvi-am:
+dvi: dvi-recursive
+check-am: all-am
+check: check-recursive
+installcheck-am:
+installcheck: installcheck-recursive
+install-exec-am:
+install-exec: install-exec-recursive
+
+install-data-am: install-docDATA
+install-data: install-data-recursive
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-recursive
+uninstall-am: uninstall-docDATA
+uninstall: uninstall-recursive
+all-am: makefile $(DATA)
+all-redirect: all-recursive
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs: installdirs-recursive
+installdirs-am:
+       $(mkinstalldirs)  $(DESTDIR)$(docdir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -rm -f Makefile $(CONFIG_CLEAN_FILES)
+       -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am:  mostlyclean-hdr mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-recursive
+
+clean-am:  clean-hdr clean-tags clean-generic mostlyclean-am
+
+clean: clean-recursive
+
+distclean-am:  distclean-hdr distclean-tags distclean-generic clean-am
+
+distclean: distclean-recursive
+       -rm -f config.status
+
+maintainer-clean-am:  maintainer-clean-hdr maintainer-clean-tags \
+               maintainer-clean-generic distclean-am
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-recursive
+       -rm -f config.status
+
+.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
+uninstall-docDATA install-docDATA install-data-recursive \
+uninstall-data-recursive install-exec-recursive \
+uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \
+all-recursive check-recursive installcheck-recursive info-recursive \
+dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \
+maintainer-clean-recursive tags tags-recursive mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs-am \
+installdirs mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+#
+# End of automake stuff
+#
+
+myclean:
+       rm -f *~
+       rm -f makefile.in
+       rm -f *.tar.gz *.zip
+       rm -f *.log aclocal.m4 config.cache
+       rm -f install-sh
+       rm -f mkinstalldirs
+       rm -f missing makefile
+       rm -f configure config.status
+       $(MAKE) -f makefile.am -C Docs     myclean
+       $(MAKE) -f makefile.am -C Source   myclean
+       $(MAKE)                -C Examples myclean
+
+Docs/robodoc.html :
+       $(MAKE) -C Docs robodoc.html
+
+Examples/C/prog1.c.html :
+       $(MAKE) -C Examples/C prog1.c.html 
+
+Examples/C/prog2.c.html :
+       $(MAKE) -C Examples/C prog2.c.html 
+
+Examples/CPP/masterindex.html :
+       $(MAKE) -C Examples/CPP masterindex.html 
+
+Examples/CPP/muppets.h.html :
+       $(MAKE) -C Examples/CPP muppets.h.html 
+
+Examples/CPP/muppets.cpp.html :
+       $(MAKE) -C Examples/CPP muppets.cpp.html 
+
+#
+#
+#
+
+docall:
+       $(MAKE) -C Source html
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/util/robodoc/missing b/util/robodoc/missing
new file mode 100755 (executable)
index 0000000..7789652
--- /dev/null
@@ -0,0 +1,190 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+# Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+case "$1" in
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]"
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing - GNU libit 0.0"
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+  aclocal)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`acinclude.m4' or \`configure.in'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`configure.in'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`acconfig.h' or \`configure.in'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case "$f" in
+      *:*) touch_files="$touch_files "`echo "$f" |
+                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+          sed 's/\.am$/.in/' |
+          while read f; do touch "$f"; done
+    ;;
+
+  bison|yacc)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+       case "$LASTARG" in
+       *.y)
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" y.tab.c
+           fi
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" y.tab.h
+           fi
+         ;;
+       esac
+    fi
+    if [ ! -f y.tab.h ]; then
+       echo >y.tab.h
+    fi
+    if [ ! -f y.tab.c ]; then
+       echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex|flex)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+       case "$LASTARG" in
+       *.l)
+           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" lex.yy.c
+           fi
+         ;;
+       esac
+    fi
+    if [ ! -f lex.yy.c ]; then
+       echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  makeinfo)
+    echo 1>&2 "\
+WARNING: \`$1' is missing on your system.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+      file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+    fi
+    touch $file
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and you do not seem to have it handy on your
+         system.  You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequirements for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
diff --git a/util/robodoc/mkinstalldirs b/util/robodoc/mkinstalldirs
new file mode 100755 (executable)
index 0000000..6b3b5fc
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp"
+
+        mkdir "$pathcomp" || lasterr=$?
+
+        if test ! -d "$pathcomp"; then
+         errstatus=$lasterr
+        fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/win32/Makefile.am b/win32/Makefile.am
new file mode 100644 (file)
index 0000000..734c64f
--- /dev/null
@@ -0,0 +1,25 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+#
+#  Copyright (C) 2000 Pekka Riikonen
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+SUBDIRS = libsilc libsilcclient
+
+EXTRA_DIST = silcdefs.h silc.dsw copy_dll tests
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/win32/copy_dll b/win32/copy_dll
new file mode 100644 (file)
index 0000000..f397445
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# Copy compiled libraries
+
+mkdir _libsilc
+mkdir _libsilc/Debug
+mkdir _libsilc/Release
+
+mkdir _libsilcclient
+mkdir _libsilcclient/Debug
+mkdir _libsilcclient/Release
+
+cp libsilc/Debug/libsilc.dll _libsilc/Debug
+cp libsilc/Debug/libsilc.exp _libsilc/Debug
+cp libsilc/Debug/libsilc.lib _libsilc/Debug
+cp libsilc/Release/libsilc.dll _libsilc/Release
+cp libsilc/Release/libsilc.exp _libsilc/Release
+cp libsilc/Release/libsilc.lib _libsilc/Release
+
+cp libsilcclient/Debug/libsilcclient.dll _libsilcclient/Debug
+cp libsilcclient/Debug/libsilcclient.exp _libsilcclient/Debug
+cp libsilcclient/Debug/libsilcclient.lib _libsilcclient/Debug
+cp libsilcclient/Release/libsilcclient.dll _libsilcclient/Release
+cp libsilcclient/Release/libsilcclient.exp _libsilcclient/Release
+cp libsilcclient/Release/libsilcclient.lib _libsilcclient/Release
+
+rm -rf libsilc/Debug
+rm -rf libsilc/Release
+rm -rf libsilcclient/Debug
+rm -rf libsilcclient/Release
+
+mv _libsilc/Debug libsilc
+mv _libsilc/Release libsilc
+mv _libsilcclient/Debug libsilcclient
+mv _libsilcclient/Release libsilcclient
+
+rm -rf _libsilc
+rm -rf _libsilcclient
+
+# Cleanup for distribution
+rm -rf *.ncb
+rm -rf *.opt
+rm -rf libsilc/*.ncb
+rm -rf libsilc/*.opt
+rm -rf libsilc/*.plg
+rm -rf libsilcclient/*.ncb
+rm -rf libsilcclient/*.opt
+rm -rf libsilcclient/*.plg
+rm -rf Debug
+rm -rf Release
+
similarity index 90%
rename from Makefile.am
rename to win32/libsilc/Makefile.am
index f0436ab9d53af97e3b38401632722639f15c66f5..58a30e525726b706a8a6774b3f19bc8be7d98657 100644 (file)
@@ -18,4 +18,7 @@
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-SUBDIRS = lib silcd silc doc includes
+EXTRA_DIST = *.dsp *.def
+
+include $(top_srcdir)/Makefile.defines.in
+
diff --git a/win32/libsilc/libsilc.def b/win32/libsilc/libsilc.def
new file mode 100644 (file)
index 0000000..81a750d
--- /dev/null
@@ -0,0 +1,559 @@
+;
+; Exports file for SILC Core DLL.
+;
+; This file is generated from MinGW compiled object files using the
+; following command:
+;
+; dlltool --export-all --output-def libsilc.def libsilc.a
+;
+; ----------------------------------------------------------------------------
+;
+; If you edit this file by adding or removing any exports be sure to
+; preserve the ordinal values (the @ n in the exports) and add new exports
+; always at the end of the list with new ordinal value. Do not ever
+; add new export with old ordinal value if you need to preserve backwards
+; compatiblity. -Pekka
+;
+EXPORTS
+       silc_aes_context_len @ 235 ; 
+       silc_aes_decrypt_cbc @ 236 ; 
+       silc_aes_encrypt_cbc @ 237 ; 
+       silc_aes_set_key @ 238 ; 
+       silc_aes_set_key_with_string @ 239 ; 
+       silc_argument_get_arg_num @ 240 ; 
+       silc_argument_get_arg_type @ 241 ; 
+       silc_argument_get_first_arg @ 242 ; 
+       silc_argument_get_next_arg @ 243 ; 
+       silc_argument_payload_encode @ 244 ; 
+       silc_argument_payload_encode_payload @ 245 ; 
+       silc_argument_payload_free @ 246 ; 
+       silc_argument_payload_parse @ 247 ; 
+       silc_auth_get_data @ 248 ; 
+       silc_auth_get_method @ 249 ; 
+       silc_auth_payload_encode @ 250 ; 
+       silc_auth_payload_free @ 251 ; 
+       silc_auth_payload_parse @ 252 ; 
+       silc_auth_public_key_auth_generate @ 253 ; 
+       silc_auth_public_key_auth_verify @ 254 ; 
+       silc_auth_public_key_auth_verify_data @ 255 ; 
+       silc_auth_verify @ 256 ; 
+       silc_auth_verify_data @ 257 ; 
+       silc_blowfish_context_len @ 258 ; 
+       silc_blowfish_decrypt_cbc @ 259 ; 
+       silc_blowfish_encrypt_cbc @ 260 ; 
+       silc_blowfish_set_key @ 261 ; 
+       silc_blowfish_set_key_with_string @ 262 ; 
+       silc_buffer_format @ 263 ; 
+       silc_buffer_unformat @ 264 ; 
+       silc_calloc @ 265 ; 
+       silc_cast_context_len @ 266 ; 
+       silc_cast_decrypt_cbc @ 267 ; 
+       silc_cast_encrypt_cbc @ 268 ; 
+       silc_cast_set_key @ 269 ; 
+       silc_cast_set_key_with_string @ 270 ; 
+       silc_channel_get_id @ 271 ; 
+       silc_channel_get_id_parse @ 272 ; 
+       silc_channel_get_mode @ 273 ; 
+       silc_channel_get_name @ 274 ; 
+       silc_channel_key_get_cipher @ 275 ; 
+       silc_channel_key_get_id @ 276 ; 
+       silc_channel_key_get_key @ 277 ; 
+       silc_channel_key_payload_encode @ 278 ; 
+       silc_channel_key_payload_free @ 279 ; 
+       silc_channel_key_payload_parse @ 280 ; 
+       silc_channel_message_get_mac @ 281 ; 
+       silc_channel_message_get_data @ 282 ; 
+       silc_channel_message_get_flags @ 283 ; 
+       silc_channel_message_get_iv @ 284 ; 
+       silc_channel_message_payload_decrypt @ 285 ; 
+       silc_channel_message_payload_encode @ 286 ; 
+       silc_channel_message_payload_free @ 287 ; 
+       silc_channel_message_payload_parse @ 288 ; 
+       silc_channel_payload_encode @ 289 ; 
+       silc_channel_payload_free @ 290 ; 
+       silc_channel_payload_list_free @ 291 ; 
+       silc_channel_payload_parse @ 292 ; 
+       silc_channel_payload_parse_list @ 293 ; 
+       silc_check_line @ 294 ; 
+       silc_cipher_alloc @ 295 ; 
+       silc_cipher_decrypt @ 296 ; 
+       silc_cipher_encrypt @ 297 ; 
+       silc_cipher_free @ 298 ; 
+       silc_cipher_get_block_len @ 299 ; 
+       silc_cipher_get_iv @ 300 ; 
+       silc_cipher_get_key_len @ 301 ; 
+       silc_cipher_get_supported @ 302 ; 
+       silc_cipher_is_supported @ 303 ; 
+       silc_cipher_list @ 304 ; 
+       silc_cipher_register @ 305 ; 
+       silc_cipher_register_default @ 306 ; 
+       silc_cipher_set_iv @ 307 ; 
+       silc_cipher_set_key @ 308 ; 
+       silc_cipher_unregister @ 309 ; 
+       silc_client_chmode @ 310 ; 
+       silc_client_chumode @ 311 ; 
+       silc_client_chumode_char @ 312 ; 
+       silc_command_get @ 313 ; 
+       silc_command_get_args @ 314 ; 
+       silc_command_get_ident @ 315 ; 
+       silc_command_payload_encode @ 316 ; 
+       silc_command_payload_encode_payload @ 317 ; 
+       silc_command_payload_encode_va @ 318 ; 
+       silc_command_payload_encode_vap @ 319 ; 
+       silc_command_payload_free @ 320 ; 
+       silc_command_payload_parse @ 321 ; 
+       silc_command_reply_payload_encode_va @ 322 ; 
+       silc_command_set_command @ 323 ; 
+       silc_command_set_ident @ 324 ; 
+       silc_config_check_num_token @ 325 ; 
+       silc_config_get_token @ 326 ; 
+       silc_config_open @ 327 ; 
+       silc_debug @ 328 DATA ; 
+       silc_decode_pem @ 329 ; 
+       silc_default_ciphers @ 330 DATA ; 
+       silc_default_hash @ 331 DATA ; 
+       silc_default_hmacs @ 332 DATA ; 
+       silc_default_pkcs @ 333 DATA ; 
+       silc_encode_pem @ 334 ; 
+       silc_encode_pem_file @ 335 ; 
+       silc_file_readfile @ 336 ; 
+       silc_file_writefile @ 337 ; 
+       silc_file_writefile_mode @ 338 ; 
+       silc_format @ 339 ; 
+       silc_free @ 340 ; 
+       silc_get_time @ 341 ; 
+       silc_gets @ 342 ; 
+       silc_gettimeofday @ 343 ; 
+       silc_hash_alloc @ 344 ; 
+       silc_hash_client_id_compare @ 345 ; 
+       silc_hash_data @ 346 ; 
+       silc_hash_data_compare @ 347 ; 
+       silc_hash_fingerprint @ 348 ; 
+       silc_hash_free @ 349 ; 
+       silc_hash_get_supported @ 350 ; 
+       silc_hash_id @ 351 ; 
+       silc_hash_id_compare @ 352 ; 
+       silc_hash_is_supported @ 353 ; 
+       silc_hash_len @ 354 ; 
+       silc_hash_list @ 355 ; 
+       silc_hash_make @ 356 ; 
+       silc_hash_ptr @ 357 ; 
+       silc_hash_register @ 358 ; 
+       silc_hash_register_default @ 359 ; 
+       silc_hash_string @ 360 ; 
+       silc_hash_string_compare @ 361 ; 
+       silc_hash_table_add @ 362 ; 
+       silc_hash_table_add_ext @ 363 ; 
+       silc_hash_table_alloc @ 364 ; 
+       silc_hash_table_count @ 365 ; 
+       silc_hash_table_del @ 366 ; 
+       silc_hash_table_del_by_context @ 367 ; 
+       silc_hash_table_del_by_context_ext @ 368 ; 
+       silc_hash_table_del_ext @ 369 ; 
+       silc_hash_table_find @ 370 ; 
+       silc_hash_table_find_ext @ 371 ; 
+       silc_hash_table_find_foreach @ 372 ; 
+       silc_hash_table_find_foreach_ext @ 373 ; 
+       silc_hash_table_foreach @ 374 ; 
+       silc_hash_table_free @ 375 ; 
+       silc_hash_table_get @ 376 ; 
+       silc_hash_table_list @ 377 ; 
+       silc_hash_table_rehash @ 378 ; 
+       silc_hash_table_rehash_ext @ 379 ; 
+       silc_hash_table_replace @ 380 ; 
+       silc_hash_table_replace_ext @ 381 ; 
+       silc_hash_table_size @ 382 ; 
+       silc_hash_uint @ 383 ; 
+       silc_hash_unregister @ 384 ; 
+       silc_hmac_alloc @ 385 ; 
+       silc_hmac_free @ 386 ; 
+       silc_hmac_get_supported @ 387 ; 
+       silc_hmac_is_supported @ 388 ; 
+       silc_hmac_len @ 389 ; 
+       silc_hmac_list @ 390 ; 
+       silc_hmac_make @ 391 ; 
+       silc_hmac_make_truncated @ 393 ; 
+       silc_hmac_make_with_key @ 394 ; 
+       silc_hmac_register @ 395 ; 
+       silc_hmac_register_default @ 396 ; 
+       silc_hmac_set_key @ 397 ; 
+       silc_hmac_unregister @ 398 ; 
+       silc_id_dup @ 399 ; 
+       silc_id_get_len @ 400 ; 
+       silc_id_id2str @ 401 ; 
+       silc_id_payload_encode @ 402 ; 
+       silc_id_payload_free @ 403 ; 
+       silc_id_payload_get_data @ 404 ; 
+       silc_id_payload_get_id @ 405 ; 
+       silc_id_payload_get_len @ 406 ; 
+       silc_id_payload_get_type @ 407 ; 
+       silc_id_payload_parse @ 408 ; 
+       silc_id_payload_parse_id @ 410 ; 
+       silc_id_render @ 411 ; 
+       silc_id_str2id @ 412 ; 
+       silc_idcache_add @ 413 ; 
+       silc_idcache_alloc @ 414 ; 
+       silc_idcache_del @ 415 ; 
+       silc_idcache_del_all @ 416 ; 
+       silc_idcache_del_by_context @ 417 ; 
+       silc_idcache_del_by_id @ 418 ; 
+       silc_idcache_del_by_id_ext @ 419 ; 
+       silc_idcache_find_by_context @ 420 ; 
+       silc_idcache_find_by_id @ 421 ; 
+       silc_idcache_find_by_id_one @ 422 ; 
+       silc_idcache_find_by_id_one_ext @ 423 ; 
+       silc_idcache_find_by_name @ 424 ; 
+       silc_idcache_find_by_name_one @ 425 ; 
+       silc_idcache_free @ 426 ; 
+       silc_idcache_get_all @ 427 ; 
+       silc_idcache_list_count @ 428 ; 
+       silc_idcache_list_first @ 429 ; 
+       silc_idcache_list_free @ 430 ; 
+       silc_idcache_list_next @ 431 ; 
+       silc_idcache_purge @ 432 ; 
+       silc_idcache_purge_by_context @ 433 ; 
+       silc_key_agreement_get_hostname @ 434 ; 
+       silc_key_agreement_get_port @ 435 ; 
+       silc_key_agreement_payload_encode @ 436 ; 
+       silc_key_agreement_payload_free @ 437 ; 
+       silc_key_agreement_payload_parse @ 438 ; 
+       silc_log_output @ 439 ; 
+       silc_log_output_debug @ 440 ; 
+       silc_log_output_hexdump @ 441 ; 
+       silc_log_reset_callbacks @ 442 ; 
+       silc_log_reset_debug_callbacks @ 443 ; 
+       silc_log_set_callback @ 444 ; 
+       silc_log_set_debug_callbacks @ 445 ; 
+       silc_log_set_file @ 446 ; 
+       silc_malloc @ 448 ; 
+       silc_mars_context_len @ 449 ; 
+       silc_mars_decrypt_cbc @ 450 ; 
+       silc_mars_encrypt_cbc @ 451 ; 
+       silc_mars_set_key @ 452 ; 
+       silc_mars_set_key_with_string @ 453 ; 
+       silc_math_gen_prime @ 454 ; 
+       silc_math_prime_test @ 455 ; 
+       silc_md5_context_len @ 456 ; 
+       silc_md5_final @ 457 ; 
+       silc_md5_init @ 458 ; 
+       silc_md5_transform @ 459 ; 
+       silc_md5_update @ 460 ; 
+       silc_mp_abs @ 461 ; 
+       silc_mp_add @ 462 ; 
+       silc_mp_add_ui @ 463 ; 
+       silc_mp_and @ 464 ; 
+       silc_mp_bin2mp @ 465 ; 
+       silc_mp_cmp @ 466 ; 
+       silc_mp_cmp_si @ 467 ; 
+       silc_mp_cmp_ui @ 468 ; 
+       silc_mp_div @ 469 ; 
+       silc_mp_div_2exp @ 470 ; 
+       silc_mp_div_2exp_qr @ 471 ; 
+       silc_mp_div_qr @ 472 ; 
+       silc_mp_div_ui @ 473 ; 
+       silc_mp_gcd @ 474 ; 
+       silc_mp_gcdext @ 475 ; 
+       silc_mp_get_str @ 476 ; 
+       silc_mp_get_ui @ 477 ; 
+       silc_mp_init @ 478 ; 
+       silc_mp_mod @ 479 ; 
+       silc_mp_mod_2exp @ 480 ; 
+       silc_mp_mod_ui @ 481 ; 
+       silc_mp_modinv @ 482 ; 
+       silc_mp_mp2bin @ 483 ; 
+       silc_mp_mp2bin_noalloc @ 484 ; 
+       silc_mp_mul @ 485 ; 
+       silc_mp_mul_2exp @ 486 ; 
+       silc_mp_mul_ui @ 487 ; 
+       silc_mp_neg @ 488 ; 
+       silc_mp_or @ 489 ; 
+       silc_mp_pow @ 490 ; 
+       silc_mp_pow_mod @ 491 ; 
+       silc_mp_pow_mod_ui @ 492 ; 
+       silc_mp_pow_ui @ 493 ; 
+       silc_mp_set @ 494 ; 
+       silc_mp_set_si @ 495 ; 
+       silc_mp_set_str @ 496 ; 
+       silc_mp_set_ui @ 497 ; 
+       silc_mp_size @ 498 ; 
+       silc_mp_sizeinbase @ 499 ; 
+       silc_mp_sqrt @ 500 ; 
+       silc_mp_sub @ 501 ; 
+       silc_mp_sub_ui @ 502 ; 
+       silc_mp_uninit @ 503 ; 
+       silc_mp_xor @ 504 ; 
+       silc_mutex_alloc @ 505 ; 
+       silc_mutex_free @ 506 ; 
+       silc_mutex_lock @ 507 ; 
+       silc_mutex_unlock @ 508 ; 
+       silc_net_accept_connection @ 509 ; 
+       silc_net_addr2bin @ 510 ; 
+       silc_net_check_host_by_sock @ 511 ; 
+       silc_net_check_local_by_sock @ 512 ; 
+       silc_net_close_connection @ 513 ; 
+       silc_net_close_server @ 514 ; 
+       silc_net_create_connection @ 515 ; 
+       silc_net_create_connection_async @ 516 ; 
+       silc_net_create_server @ 517 ; 
+       silc_net_get_local_port @ 518 ; 
+       silc_net_get_remote_port @ 519 ; 
+       silc_net_get_socket_opt @ 520 ; 
+       silc_net_is_ip @ 521 ; 
+       silc_net_localhost @ 522 ; 
+       silc_net_set_socket_nonblock @ 523 ; 
+       silc_net_set_socket_opt @ 524 ; 
+       silc_net_win32_init @ 525 ; 
+       silc_net_win32_uninit @ 526 ; 
+       silc_none_context_len @ 527 ; 
+       silc_none_decrypt_cbc @ 528 ; 
+       silc_none_encrypt_cbc @ 529 ; 
+       silc_none_set_key @ 530 ; 
+       silc_none_set_key_with_string @ 531 ; 
+       silc_notify_get_arg_num @ 532 ; 
+       silc_notify_get_args @ 533 ; 
+       silc_notify_get_type @ 534 ; 
+       silc_notify_payload_encode @ 535 ; 
+       silc_notify_payload_encode_args @ 536 ; 
+       silc_notify_payload_free @ 537 ; 
+       silc_notify_payload_parse @ 538 ; 
+       silc_packet_assemble @ 539 ; 
+       silc_packet_context_alloc @ 540 ; 
+       silc_packet_context_dup @ 541 ; 
+       silc_packet_context_free @ 542 ; 
+       silc_packet_encrypt @ 544 ; 
+       silc_packet_parse @ 545 ; 
+       silc_packet_parse_special @ 546 ; 
+       silc_packet_receive @ 547 ; 
+       silc_packet_receive_process @ 548 ; 
+       silc_packet_send @ 549 ; 
+       silc_packet_send_prepare @ 550 ; 
+       silc_parse_command_line @ 551 ; 
+       silc_parse_userfqdn @ 552 ; 
+       silc_pkcs1_decrypt @ 553 ; 
+       silc_pkcs1_encrypt @ 554 ; 
+       silc_pkcs1_sign @ 555 ; 
+       silc_pkcs1_verify @ 556 ; 
+       silc_pkcs_alloc @ 557 ; 
+       silc_pkcs_decode_identifier @ 558 ; 
+       silc_pkcs_decrypt @ 559 ; 
+       silc_pkcs_encode_identifier @ 560 ; 
+       silc_pkcs_encrypt @ 561 ; 
+       silc_pkcs_free @ 562 ; 
+       silc_pkcs_free_identifier @ 563 ; 
+       silc_pkcs_get_key_len @ 564 ; 
+       silc_pkcs_get_private_key @ 565 ; 
+       silc_pkcs_get_public_key @ 566 ; 
+       silc_pkcs_get_supported @ 567 ; 
+       silc_pkcs_is_supported @ 568 ; 
+       silc_pkcs_list @ 569 ; 
+       silc_pkcs_load_private_key @ 570 ; 
+       silc_pkcs_load_public_key @ 571 ; 
+       silc_pkcs_private_key_alloc @ 572 ; 
+       silc_pkcs_private_key_data_encode @ 573 ; 
+       silc_pkcs_private_key_data_set @ 574 ; 
+       silc_pkcs_private_key_decode @ 575 ; 
+       silc_pkcs_private_key_encode @ 576 ; 
+       silc_pkcs_private_key_free @ 577 ; 
+       silc_pkcs_private_key_set @ 578 ; 
+       silc_pkcs_public_key_alloc @ 579 ; 
+       silc_pkcs_public_key_data_encode @ 580 ; 
+       silc_pkcs_public_key_data_set @ 581 ; 
+       silc_pkcs_public_key_decode @ 582 ; 
+       silc_pkcs_public_key_encode @ 583 ; 
+       silc_pkcs_public_key_free @ 584 ; 
+       silc_pkcs_public_key_set @ 585 ; 
+       silc_pkcs_register @ 586 ; 
+       silc_pkcs_register_default @ 587 ; 
+       silc_pkcs_save_private_key @ 588 ; 
+       silc_pkcs_save_private_key_data @ 589 ; 
+       silc_pkcs_save_public_key @ 590 ; 
+       silc_pkcs_save_public_key_data @ 591 ; 
+       silc_pkcs_sign @ 592 ; 
+       silc_pkcs_sign_with_hash @ 593 ; 
+       silc_pkcs_unregister @ 594 ; 
+       silc_pkcs_verify @ 595 ; 
+       silc_pkcs_verify_with_hash @ 596 ; 
+       silc_private_message_get_flags @ 597 ; 
+       silc_private_message_get_message @ 598 ; 
+       silc_private_message_payload_encode @ 599 ; 
+       silc_private_message_payload_free @ 600 ; 
+       silc_private_message_payload_parse @ 601 ; 
+       silc_protocol_alloc @ 602 ; 
+       silc_protocol_cancel @ 603 ; 
+       silc_protocol_execute @ 604 ; 
+       silc_protocol_execute_final @ 605 ; 
+       silc_protocol_free @ 606 ; 
+       silc_protocol_list @ 607 ; 
+       silc_protocol_register @ 608 ; 
+       silc_protocol_unregister @ 609 ; 
+       silc_rc5_context_len @ 610 ; 
+       silc_rc5_decrypt_cbc @ 611 ; 
+       silc_rc5_encrypt_cbc @ 612 ; 
+       silc_rc5_set_key @ 613 ; 
+       silc_rc5_set_key_with_string @ 614 ; 
+       silc_rc6_context_len @ 615 ; 
+       silc_rc6_decrypt_cbc @ 616 ; 
+       silc_rc6_encrypt_cbc @ 617 ; 
+       silc_rc6_set_key @ 618 ; 
+       silc_rc6_set_key_with_string @ 619 ; 
+       silc_realloc @ 620 ; 
+       silc_rng_add_noise @ 621 ; 
+       silc_rng_alloc @ 622 ; 
+       silc_rng_free @ 623 ; 
+       silc_rng_get_byte @ 624 ; 
+       silc_rng_get_rn16 @ 625 ; 
+       silc_rng_get_rn32 @ 626 ; 
+       silc_rng_get_rn_data @ 627 ; 
+       silc_rng_get_rn_string @ 628 ; 
+       silc_rng_global_add_noise @ 629 ; 
+       silc_rng_global_get_byte @ 630 ; 
+       silc_rng_global_get_rn16 @ 631 ; 
+       silc_rng_global_get_rn32 @ 632 ; 
+       silc_rng_global_get_rn_data @ 633 ; 
+       silc_rng_global_get_rn_string @ 634 ; 
+       silc_rng_global_init @ 635 ; 
+       silc_rng_global_uninit @ 636 ; 
+       silc_rng_init @ 637 ; 
+       silc_rsa_clear_keys @ 638 ; 
+       silc_rsa_context_len @ 639 ; 
+       silc_rsa_decrypt @ 640 ; 
+       silc_rsa_encrypt @ 641 ; 
+       silc_rsa_get_private_key @ 642 ; 
+       silc_rsa_get_public_key @ 643 ; 
+       silc_rsa_init @ 644 ; 
+       silc_rsa_set_private_key @ 645 ; 
+       silc_rsa_set_public_key @ 646 ; 
+       silc_rsa_sign @ 647 ; 
+       silc_rsa_verify @ 648 ; 
+       silc_schedule @ 649 ; 
+       silc_schedule_init @ 650 ; 
+       silc_schedule_one @ 651 ; 
+       silc_schedule_set_listen_fd @ 652 ; 
+       silc_schedule_stop @ 653 ; 
+       silc_schedule_task_add @ 654 ; 
+       silc_schedule_task_del @ 655 ; 
+       silc_schedule_task_del_by_callback @ 656 ; 
+       silc_schedule_task_del_by_context @ 657 ; 
+       silc_schedule_task_del_by_fd @ 658 ; 
+       silc_schedule_uninit @ 659 ; 
+       silc_schedule_unset_listen_fd @ 660 ; 
+       silc_schedule_wakeup @ 661 ; 
+       silc_schedule_wakeup_init @ 662 ; 
+       silc_schedule_wakeup_internal @ 663 ; 
+       silc_schedule_wakeup_uninit @ 664 ; 
+       silc_select @ 665 ; 
+       silc_sha1_context_len @ 666 ; 
+       silc_sha1_final @ 667 ; 
+       silc_sha1_init @ 668 ; 
+       silc_sha1_transform @ 669 ; 
+       silc_sha1_update @ 670 ; 
+       silc_ske_abort @ 671 ; 
+       silc_ske_alloc @ 672 ; 
+       silc_ske_assemble_security_properties @ 673 ; 
+       silc_ske_create_rnd @ 674 ; 
+       silc_ske_end @ 675 ; 
+       silc_ske_free @ 676 ; 
+       silc_ske_free_key_material @ 677 ; 
+       silc_ske_group_get_by_name @ 678 ; 
+       silc_ske_group_get_by_number @ 679 ; 
+       silc_ske_get_supported_groups @ 680 ; 
+       silc_ske_group_get_number @ 681 ; 
+       silc_ske_groups @ 682 ; 
+       silc_ske_initiator_finish @ 683 ; 
+       silc_ske_initiator_phase_1 @ 684 ; 
+       silc_ske_initiator_phase_2 @ 685 ; 
+       silc_ske_initiator_start @ 686 ; 
+       silc_ske_make_hash @ 687 ; 
+       silc_ske_payload_ke_decode @ 688 ; 
+       silc_ske_payload_ke_encode @ 689 ; 
+       silc_ske_payload_ke_free @ 690 ; 
+       silc_ske_payload_start_decode @ 691 ; 
+       silc_ske_payload_start_encode @ 692 ; 
+       silc_ske_payload_start_free @ 693 ; 
+       silc_ske_process_key_material @ 694 ; 
+       silc_ske_process_key_material_data @ 695 ; 
+       silc_ske_responder_finish @ 696 ; 
+       silc_ske_responder_phase_1 @ 697 ; 
+       silc_ske_responder_phase_2 @ 698 ; 
+       silc_ske_responder_start @ 699 ; 
+       silc_ske_select_security_properties @ 700 ; 
+       silc_ske_set_callbacks @ 701 ; 
+       silc_socket_alloc @ 702 ; 
+       silc_socket_dup @ 703 ; 
+       silc_socket_free @ 704 ; 
+       silc_socket_host_lookup @ 705 ; 
+       silc_socket_read @ 706 ; 
+       silc_socket_set_heartbeat @ 707 ; 
+       silc_socket_write @ 708 ; 
+       silc_string_compare @ 709 ; 
+       silc_thread_create @ 710 ; 
+       silc_thread_exit @ 711 ; 
+       silc_thread_self @ 712 ; 
+       silc_thread_wait @ 713 ; 
+       silc_to_upper @ 715 ; 
+       silc_twofish_context_len @ 716 ; 
+       silc_twofish_decrypt_cbc @ 717 ; 
+       silc_twofish_encrypt_cbc @ 718 ; 
+       silc_twofish_set_key @ 719 ; 
+       silc_twofish_set_key_with_string @ 720 ; 
+       silc_buffer_format_vp @ 789 ; 
+       silc_buffer_unformat_vp @ 790 ; 
+       silc_sftp_client_start @ 791 ;
+       silc_sftp_client_shutdown @ 792 ;
+       silc_sftp_client_receive_process @ 793 ;
+       silc_sftp_open @ 794 ;
+       silc_sftp_close @ 795 ;
+       silc_sftp_read @ 796 ;
+       silc_sftp_write @ 797 ;
+       silc_sftp_remove @ 798 ;
+       silc_sftp_rename @ 799 ;
+       silc_sftp_mkdir @ 800 ;
+       silc_sftp_rmdir @ 801 ;
+       silc_sftp_opendir @ 802 ;
+       silc_sftp_readdir @ 803 ;
+       silc_sftp_stat @ 804 ;
+       silc_sftp_lstat @ 805 ;
+       silc_sftp_fstat @ 806 ;
+       silc_sftp_setstat @ 807 ;
+       silc_sftp_fsetstat @ 808 ;
+       silc_sftp_readlink @ 809 ;
+       silc_sftp_symlink @ 810 ;
+       silc_sftp_realpath @ 811 ;
+       silc_sftp_extended @ 812 ;
+       silc_sftp_server_start @ 813 ;
+       silc_sftp_server_shutdown @ 814 ;
+       silc_sftp_server_receive_process @ 815 ;
+       silc_sftp_fs_memory @ 816 ;
+       silc_sftp_fs_memory_alloc @ 817 ;
+       silc_sftp_fs_memory_free @ 818 ;
+       silc_sftp_fs_memory_add_dir @ 819 ;
+       silc_sftp_fs_memory_del_dir @ 820 ;
+       silc_sftp_fs_memory_add_file @ 821 ;
+       silc_sftp_fs_memory_del_file @ 822 ;
+       silc_file_open @ 824 ;
+       silc_file_close @ 825 ;
+       silc_file_read @ 826 ;
+       silc_file_write @ 827 ;
+       silc_file_size @ 828 ;
+       silc_hmac_init @ 829 ;
+       silc_hmac_update @ 830 ;
+       silc_hmac_final @ 831 ;
+       silc_hmac_init_with_key @ 832 ;
+       silc_hmac_get_name @ 833 ;
+       silc_hmac_get_hash @ 834 ;
+       silc_net_localip @ 835 ;
+       silc_sftp_server_set_monitor @ 836 ;
+       silc_net_gethostbyname @ 837 ;
+       silc_net_gethostbyaddr @ 838 ;
+       silc_net_gethostbyname_async @ 839 ;
+       silc_net_gethostbyaddr_async @ 840 ;
+       silc_net_is_ip4 @ 841 ;
+       silc_net_is_ip6 @ 842 ;
+       silc_log_set_debug_string @ 843 ;
+       silc_log_reset_all @ 844 ;
+       silc_log_flush_all @ 845 ;
+       silc_log_get_file @ 846 ;
+       silc_log_quick @ 847 DATA ;
+       silc_log_flushdelay @ 848 DATA ;
+       silc_hash_table_list_reset @ 849 ;
+       silc_debug_hexdump @ 850 DATA ;
diff --git a/win32/libsilc/libsilc.dsp b/win32/libsilc/libsilc.dsp
new file mode 100644 (file)
index 0000000..5c35110
--- /dev/null
@@ -0,0 +1,695 @@
+# Microsoft Developer Studio Project File - Name="libsilc" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102\r
+\r
+CFG=libsilc - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "libsilc.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "libsilc.mak" CFG="libsilc - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "libsilc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE "libsilc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "libsilc - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILC_EXPORTS" /YX /FD /c\r
+# ADD CPP /nologo /MT /W2 /GX /O2 /I ".\\" /I "..\\" /I "..\..\\" /I "..\..\includes" /I "..\..\lib\silccore" /I "..\..\lib\silcske" /I "..\..\lib\silcmath" /I "..\..\lib\silcmath\mpi" /I "..\..\lib\silcutil" /I "..\..\lib\silccrypt" /I "..\..\lib\silcsim" /I "..\..\lib\trq" /I "..\..\lib\silcsftp" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILC_EXPORTS" /D "MP_API_COMPATIBLE" /D "DLL" /FD /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /dll /machine:I386 /def:"libsilc.def"\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "libsilc - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILC_EXPORTS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MTd /W2 /Gm /GX /ZI /Od /I ".\\" /I "..\\" /I "..\..\\" /I "..\..\includes" /I "..\..\lib\silccore" /I "..\..\lib\silcske" /I "..\..\lib\silcmath" /I "..\..\lib\silcmath\mpi" /I "..\..\lib\silcutil" /I "..\..\lib\silccrypt" /I "..\..\lib\silcsim" /I "..\..\lib\trq" /I "..\..\lib\silcsftp" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILC_EXPORTS" /D "MP_API_COMPATIBLE" /D "SILC_DEBUG" /D "DLL" /FD /GZ /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /dll /debug /machine:I386 /def:"libsilc.def" /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "libsilc - Win32 Release"\r
+# Name "libsilc - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Group "silccore"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcargument.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcauth.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcchannel.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silccommand.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcid.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcidcache.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcnotify.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcpacket.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcprivate.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcske"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\groups.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\payload.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\silcske.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcutil"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcbuffmt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcconfig.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silchashtable.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silclog.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcmemory.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcnet.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcprotocol.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcschedule.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcsockconn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcutil.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\win32\silcwin32mutex.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\win32\silcwin32net.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\win32\silcwin32schedule.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\win32\silcwin32sockconn.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\win32\silcwin32thread.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\win32\silcwin32util.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcmath"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Group "mpi"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mpi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mplogic.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mpmontg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mpprime.c\r
+# End Source File\r
+# End Group\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\modinv.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mp_mpi.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpbin.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\silcprimegen.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "silccrypt"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\aes.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\blowfish.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\cast.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\mars.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\md5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\none.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\pkcs1.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rc5.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rc6.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rsa.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\sha1.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silccipher.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silchash.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silchmac.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silcpkcs.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silcrng.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\twofish.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcsftp No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\sftp_client.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\sftp_fs_memory.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\sftp_server.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\sftp_util.c\r
+# End Source File\r
+# End Group\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Group "silccore No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcargument.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcauth.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcchannel.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silccommand.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcid.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcidcache.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcmode.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcnotify.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcpacket.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccore\silcprivate.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcske No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\groups.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\groups_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\payload.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\silcske.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcske\silcske_status.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcutil No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcbuffer.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcbuffmt.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcbufutil.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcconfig.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcdlist.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silchashtable.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silclist.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silclog.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcmemory.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcmutex.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcnet.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcprotocol.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcschedule.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcschedule_i.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcsockconn.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcthread.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcutil\silcutil.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcmath No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Group "mpi No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\logtab.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\montmulf.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE="..\..\lib\silcmath\mpi\mpi-config.h"\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE="..\..\lib\silcmath\mpi\mpi-priv.h"\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mpi.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mplogic.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mpi\mpprime.h\r
+# End Source File\r
+# End Group\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\mp_mpi.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\silcmath.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcmath\silcmp.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "silccrypt No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\aes.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\blowfish.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\blowfish_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\cast.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\cast_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\ciphers.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\ciphers_def.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\mars.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\mars_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\md5.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\md5_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\none.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\pkcs1.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rc5.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rc5_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rc6.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rc6_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rijndael_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rsa.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\rsa_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\sha1.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\sha1_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silccipher.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silcdh.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silchash.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silchmac.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silcpkcs.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\silcrng.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\twofish.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silccrypt\twofish_internal.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "silcsftp"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\sftp_util.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\silcsftp.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcsftp\silcsftp_fs.h\r
+# End Source File\r
+# End Group\r
+# Begin Source File\r
+\r
+SOURCE=..\silcdefs.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# End Target\r
+# End Project\r
similarity index 90%
rename from lib/silcsim/modules/Makefile.am
rename to win32/libsilcclient/Makefile.am
index f287498d90ee23f03b38bc4c345011f2deb534d3..58a30e525726b706a8a6774b3f19bc8be7d98657 100644 (file)
@@ -18,5 +18,7 @@
 
 AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
 
-all:
-       -cd ..
+EXTRA_DIST = *.dsp *.def
+
+include $(top_srcdir)/Makefile.defines.in
+
diff --git a/win32/libsilcclient/libsilcclient.def b/win32/libsilcclient/libsilcclient.def
new file mode 100644 (file)
index 0000000..8cea6bb
--- /dev/null
@@ -0,0 +1,165 @@
+;
+; Exports file for SILC Client DLL.
+;
+; This file is generated from MinGW compiled object files using the
+; following command:
+;
+; dlltool --export-all --output-def libsilc.def libsilc.a
+;
+; ----------------------------------------------------------------------------
+;
+; If you edit this file by adding or removing any exports be sure to
+; preserve the ordinal values (the @ n in the exports) and add new exports
+; always at the end of the list with new ordinal value. Do not ever
+; add new export with old ordinal value if you need to preserve backwards
+; compatiblity. -Pekka
+;
+EXPORTS
+       silc_ske_check_version @ 1 ; 
+       silc_client_add_channel_private_key @ 2 ; 
+       silc_client_add_connection @ 3 ; 
+       silc_client_add_private_message_key @ 4 ; 
+       silc_client_add_private_message_key_ske @ 5 ; 
+       silc_client_add_socket @ 6 ; 
+       silc_client_alloc @ 7 ; 
+       silc_client_channel_message @ 8 ; 
+       silc_client_close_connection @ 9 ; 
+       silc_client_command_alloc @ 10 ; 
+       silc_client_command_ban @ 11 ; 
+       silc_client_command_close @ 12 ; 
+       silc_client_command_cmode @ 13 ; 
+       silc_client_command_connect @ 14 ; 
+       silc_client_command_cumode @ 15 ; 
+       silc_client_command_dup @ 16 ; 
+       silc_client_command_find @ 17 ; 
+       silc_client_command_free @ 18 ; 
+       silc_client_command_get_channel_by_id_callback @ 19 ; 
+       silc_client_command_get_client_by_id_callback @ 20 ; 
+       silc_client_command_get_client_callback @ 21 ; 
+       silc_client_command_get_clients_list_callback @ 22 ; 
+       silc_client_command_getkey @ 23 ; 
+       silc_client_command_identify @ 24 ; 
+       silc_client_command_info @ 25 ; 
+       silc_client_command_invite @ 26 ; 
+       silc_client_command_join @ 27 ; 
+       silc_client_command_kick @ 28 ; 
+       silc_client_command_kill @ 29 ; 
+       silc_client_command_leave @ 30 ; 
+       silc_client_command_list @ 31 ; 
+       silc_client_command_motd @ 32 ; 
+       silc_client_command_nick @ 33 ; 
+       silc_client_command_oper @ 34 ; 
+       silc_client_command_pending @ 35 ; 
+       silc_client_command_pending_check @ 36 ; 
+       silc_client_command_pending_del @ 37 ; 
+       silc_client_command_ping @ 38 ; 
+       silc_client_command_quit @ 39 ; 
+       silc_client_command_reply_ban @ 40 ; 
+       silc_client_command_reply_close @ 41 ; 
+       silc_client_command_reply_cmode @ 42 ; 
+       silc_client_command_reply_connect @ 43 ; 
+       silc_client_command_reply_cumode @ 44 ; 
+       silc_client_command_reply_free @ 45 ; 
+       silc_client_command_reply_getkey @ 46 ; 
+       silc_client_command_reply_identify @ 47 ; 
+       silc_client_command_reply_info @ 48 ; 
+       silc_client_command_reply_invite @ 49 ; 
+       silc_client_command_reply_join @ 50 ; 
+       silc_client_command_reply_kick @ 51 ; 
+       silc_client_command_reply_kill @ 52 ; 
+       silc_client_command_reply_leave @ 53 ; 
+       silc_client_command_reply_list @ 54 ; 
+       silc_client_command_reply_motd @ 55 ; 
+       silc_client_command_reply_nick @ 56 ; 
+       silc_client_command_reply_oper @ 57 ; 
+       silc_client_command_reply_ping @ 58 ; 
+       silc_client_command_reply_process @ 59 ; 
+       silc_client_command_reply_shutdown @ 60 ; 
+       silc_client_command_reply_silcoper @ 61 ; 
+       silc_client_command_reply_topic @ 62 ; 
+       silc_client_command_reply_umode @ 63 ; 
+       silc_client_command_reply_users @ 64 ; 
+       silc_client_command_reply_whois @ 65 ; 
+       silc_client_command_reply_whowas @ 66 ; 
+       silc_client_command_shutdown @ 67 ; 
+       silc_client_command_silcoper @ 68 ; 
+       silc_client_command_status_message @ 69 ; 
+       silc_client_command_topic @ 70 ; 
+       silc_client_command_umode @ 71 ; 
+       silc_client_command_users @ 72 ; 
+       silc_client_command_whois @ 73 ; 
+       silc_client_command_whowas @ 74 ; 
+       silc_client_connect_to_server @ 75 ; 
+       silc_client_del_channel @ 76 ; 
+       silc_client_del_channel_private_key @ 77 ; 
+       silc_client_del_channel_private_keys @ 78 ; 
+       silc_client_del_client @ 79 ; 
+       silc_client_del_client_entry @ 80 ; 
+       silc_client_del_connection @ 81 ; 
+       silc_client_del_private_message_key @ 82 ; 
+       silc_client_del_server @ 83 ; 
+       silc_client_del_socket @ 84 ; 
+       silc_client_disconnected_by_server @ 85 ; 
+       silc_client_error_by_server @ 86 ; 
+       silc_client_free @ 87 ; 
+       silc_client_free_channel_private_keys @ 88 ; 
+       silc_client_free_private_message_keys @ 89 ; 
+       silc_client_get_channel @ 90 ; 
+       silc_client_get_channel_by_id @ 91 ; 
+       silc_client_get_channel_by_id_resolve @ 92 ; 
+       silc_client_get_client_by_id @ 93 ; 
+       silc_client_get_client_by_id_resolve @ 94 ; 
+       silc_client_get_clients @ 95 ; 
+       silc_client_get_clients_by_list @ 96 ; 
+       silc_client_get_clients_local @ 97 ; 
+       silc_client_get_server @ 98 ; 
+       silc_client_get_server_by_id @ 99 ; 
+       silc_client_init @ 100 ; 
+       silc_client_key_agreement @ 101 ; 
+       silc_client_list_channel_private_keys @ 102 ; 
+       silc_client_list_private_message_keys @ 103 ; 
+       silc_client_new_channel_id @ 104 ; 
+       silc_client_notify_by_server @ 105 ; 
+       silc_client_packet_process @ 106 ; 
+       silc_client_packet_send @ 107 ; 
+       silc_client_packet_send_real @ 108 ; 
+       silc_client_perform_key_agreement @ 109 ; 
+       silc_client_perform_key_agreement_fd @ 110 ; 
+       silc_client_private_message @ 111 ; 
+       silc_client_private_message_key @ 112 ; 
+       silc_client_process_failure @ 113 ; 
+       silc_client_protocol_ke_send_packet @ 114 ; 
+       silc_client_protocol_ke_set_keys @ 115 ; 
+       silc_client_protocol_ke_verify_key @ 116 ; 
+       silc_client_protocols_register @ 117 ; 
+       silc_client_protocols_unregister @ 118 ; 
+       silc_client_receive_channel_key @ 119 ; 
+       silc_client_receive_new_id @ 120 ; 
+       silc_client_remove_from_channels @ 121 ; 
+       silc_client_replace_from_channels @ 122 ; 
+       silc_client_run @ 123 ; 
+       silc_client_save_channel_key @ 124 ; 
+       silc_client_send_channel_message @ 125 ; 
+       silc_client_command_send @ 126 ; 
+       silc_client_send_key_agreement @ 127 ; 
+       silc_client_send_private_message @ 128 ; 
+       silc_client_send_private_message_key @ 129 ; 
+       silc_client_start_key_exchange @ 131 ; 
+       silc_client_stop @ 132 ; 
+       silc_command_status_messages @ 135 DATA ; 
+       silc_idlist_get_channel_by_id @ 136 ; 
+       silc_idlist_get_client @ 137 ; 
+       silc_client_abort_key_agreement @ 138 ; 
+       silc_client_set_away_message @ 139 ; 
+       silc_client_request_authentication_method @ 140 ; 
+       silc_client_file_send @ 141 ;
+       silc_client_file_receive @ 142 ;
+       silc_client_file_close @ 143 ;
+       silc_client_command_register @ 144 ;
+       silc_client_command_unregister @ 145 ;
+       silc_client_commands_register @ 146 ;
+       silc_client_commands_unregister @ 147 ;
+       silc_client_command_call @ 148 ;
+       silc_client_command_reply_quit @ 149 ;
+       silc_client_run_one @ 150 ;
+       silc_client_on_channel @ 151 ;
diff --git a/win32/libsilcclient/libsilcclient.dsp b/win32/libsilcclient/libsilcclient.dsp
new file mode 100644 (file)
index 0000000..30165e8
--- /dev/null
@@ -0,0 +1,180 @@
+# Microsoft Developer Studio Project File - Name="libsilcclient" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102\r
+\r
+CFG=libsilcclient - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "libsilcclient.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "libsilcclient.mak" CFG="libsilcclient - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "libsilcclient - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE "libsilcclient - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "libsilcclient - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILCCLIENT_EXPORTS" /YX /FD /c\r
+# ADD CPP /nologo /MT /W2 /GX /O2 /I ".\\" /I "..\\" /I "..\..\\" /I "..\..\includes" /I "..\..\lib\silccore" /I "..\..\lib\silcske" /I "..\..\lib\silcmath" /I "..\..\lib\silcmath\mpi" /I "..\..\lib\silcutil" /I "..\..\lib\silccrypt" /I "..\..\lib\silcsim" /I "..\..\lib\trq" /I "..\..\lib\silcclient" /I "..\..\lib\silcsftp" /D "NDEBUG" /D "MP_API_COMPATIBLE" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILCCLIENT_EXPORTS" /D "DLL" /YX /FD /c\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib ..\libsilc\Release\libsilc.lib /nologo /dll /machine:I386 /def:"libsilcclient.def"\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "libsilcclient - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILCCLIENT_EXPORTS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MTd /W2 /Gm /GX /ZI /Od /I ".\\" /I "..\\" /I "..\..\\" /I "..\..\includes" /I "..\..\lib\silccore" /I "..\..\lib\silcske" /I "..\..\lib\silcmath" /I "..\..\lib\silcmath\mpi" /I "..\..\lib\silcutil" /I "..\..\lib\silccrypt" /I "..\..\lib\silcsim" /I "..\..\lib\trq" /I "..\..\lib\silcclient" /I "..\..\lib\silcsftp" /D "_DEBUG" /D "MP_API_COMPATIBLE" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBSILCCLIENT_EXPORTS" /D "SILC_DEBUG" /D "DLL" /YX /FD /GZ /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib ..\libsilc\Debug\libsilc.lib /nologo /dll /debug /machine:I386 /def:"libsilcclient.def" /pdbtype:sept\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "libsilcclient - Win32 Release"\r
+# Name "libsilcclient - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Group "silcclient"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client_channel.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client_ftp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client_keyagr.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client_notify.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client_prvmsg.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\command.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\command_reply.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\idlist.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\protocol.c\r
+# End Source File\r
+# End Group\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Group "silcclient No. 1"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\client_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\command.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\command_reply.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\idlist.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\protocol.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\lib\silcclient\silcapi.h\r
+# End Source File\r
+# End Group\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/win32/silc.dsw b/win32/silc.dsw
new file mode 100644 (file)
index 0000000..a8cbf4a
--- /dev/null
@@ -0,0 +1,41 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r
+\r
+###############################################################################\r
+\r
+Project: "libsilc"=".\libsilc\libsilc.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Project: "libsilcclient"=".\libsilcclient\libsilcclient.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
diff --git a/win32/silcdefs.h b/win32/silcdefs.h
new file mode 100644 (file)
index 0000000..fd58aef
--- /dev/null
@@ -0,0 +1,319 @@
+/* includes/silcdefs.h.  Generated automatically by configure.  */
+/* includes/silcdefs.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define to empty if the keyword does not work.  */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+/* #undef gid_t */
+
+/* Define as __inline if that's what the C compiler calls it.  */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+/* #undef mode_t */
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+/* #undef pid_t */
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+/* #undef size_t */
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly.  */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+/* #undef uid_t */
+
+/* Debugging */
+/* #undef SILC_DEBUG */
+
+/* Default configuration file */
+/* #undef SILC_SERVER_CONFIG_FILE */
+
+/* Multi-thread support */
+#define SILC_THREADS 1
+
+/* Default paths */
+#define SILC_ETCDIR "/etc/silc"
+#define SILC_HELPDIR "help"
+#define SILC_DOCDIR "doc"
+#define SILC_MODULESDIR "modules"
+#define SILC_LOGSDIR "logs"
+
+/* SIM (SILC Module) support */
+/* #undef SILC_SIM */
+/* #undef HAVE_RTLD_NOW */
+/* #undef HAVE_RTLD_LAZY */
+
+/* Types */
+#define SILC_SIZEOF_LONG_LONG 8
+#define SILC_SIZEOF_LONG 4
+#define SILC_SIZEOF_INT 4
+#define SILC_SIZEOF_SHORT 2
+#define SILC_SIZEOF_CHAR 1
+#define SILC_SIZEOF_VOID_P 4
+
+/* MP library */
+/* #undef SILC_MP_GMP */
+#define SILC_MP_NSS_MPI 1
+
+/* Redefs for SOCKS5 library */
+/* macros/curses checks */
+/* #undef HAS_CURSES */
+/* #undef USE_SUNOS_CURSES */
+/* #undef USE_BSD_CURSES */
+/* #undef USE_SYSV_CURSES */
+/* #undef USE_NCURSES */
+/* #undef NO_COLOR_CURSES */
+/* #undef SCO_FLAVOR */
+
+/* #undef SOCKS */
+/* #undef SOCKS5 */
+/* #undef Rconnect */
+/* #undef Rgetsockname */
+/* #undef Rgetpeername */
+/* #undef Rbind */
+/* #undef Raccept */  
+/* #undef Rlisten */
+/* #undef Rselect */
+/* #undef Rrecvfrom */
+/* #undef Rsendto */
+/* #undef Rrecv */
+/* #undef Rsend */
+/* #undef Rread */
+/* #undef Rwrite */
+/* #undef Rrresvport */
+/* #undef Rshutdown */
+/* #undef Rlisten */
+/* #undef Rclose */
+/* #undef Rdup */
+/* #undef Rdup2 */
+/* #undef Rfclose */
+/* #undef Rgethostbyname */
+
+/* Native WIN32 compilation (-mno-cygwin GCC option) under cygwin, though
+   the code compiles with any native WIN32 compiler. */
+#define SILC_WIN32 1
+
+/* SILC distribution definitions (leave this at the end of file) */
+#define SILC_DIST_TOOLKIT 1
+/* #undef SILC_DIST_CLIENT */
+/* #undef SILC_DIST_SERVER */
+/* #undef SILC_DIST_WIN32DLL */
+
+/* The number of bytes in a char.  */
+#define SIZEOF_CHAR 1
+
+/* The number of bytes in a int.  */
+#define SIZEOF_INT 4
+
+/* The number of bytes in a long.  */
+#define SIZEOF_LONG 4
+
+/* The number of bytes in a long long.  */
+#define SIZEOF_LONG_LONG 8
+
+/* The number of bytes in a short.  */
+#define SIZEOF_SHORT 2
+
+/* The number of bytes in a void *.  */
+#define SIZEOF_VOID_P 4
+
+/* Define if you have the bind function.  */
+#define HAVE_BIND 1
+
+/* Define if you have the chmod function.  */
+#define HAVE_CHMOD 1
+
+/* Define if you have the close function.  */
+#define HAVE_CLOSE 1
+
+/* Define if you have the connect function.  */
+#define HAVE_CONNECT 1
+
+/* Define if you have the ctime function.  */
+#define HAVE_CTIME 1
+
+/* Define if you have the fcntl function.  */
+#define HAVE_FCNTL 1
+
+/* Define if you have the fstat function.  */
+#define HAVE_FSTAT 1
+
+/* Define if you have the getenv function.  */
+#define HAVE_GETENV 1
+
+/* Define if you have the getgid function.  */
+#define HAVE_GETGID 1
+
+/* Define if you have the gethostbyaddr function.  */
+#define HAVE_GETHOSTBYADDR 1
+
+/* Define if you have the gethostname function.  */
+#define HAVE_GETHOSTNAME 1
+
+/* Define if you have the getopt_long function.  */
+#define HAVE_GETOPT_LONG 1
+
+/* Define if you have the getpgid function.  */
+#define HAVE_GETPGID 1
+
+/* Define if you have the getpgrp function.  */
+#define HAVE_GETPGRP 1
+
+/* Define if you have the getpid function.  */
+#define HAVE_GETPID 1
+
+/* Define if you have the getservbyname function.  */
+#define HAVE_GETSERVBYNAME 1
+
+/* Define if you have the getservbyport function.  */
+#define HAVE_GETSERVBYPORT 1
+
+/* Define if you have the getsid function.  */
+/* #undef HAVE_GETSID */
+
+/* Define if you have the gettimeofday function.  */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the getuid function.  */
+#define HAVE_GETUID 1
+
+/* Define if you have the listen function.  */
+#define HAVE_LISTEN 1
+
+/* Define if you have the memcpy function.  */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the memmove function.  */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the memset function.  */
+#define HAVE_MEMSET 1
+
+/* Define if you have the mlock function.  */
+/* #undef HAVE_MLOCK */
+
+/* Define if you have the munlock function.  */
+/* #undef HAVE_MUNLOCK */
+
+/* Define if you have the pthread_create function.  */
+#define HAVE_PTHREAD_CREATE 1
+
+/* Define if you have the putenv function.  */
+#define HAVE_PUTENV 1
+
+/* Define if you have the select function.  */
+#define HAVE_SELECT 1
+
+/* Define if you have the setsockopt function.  */
+#define HAVE_SETSOCKOPT 1
+
+/* Define if you have the shutdown function.  */
+#define HAVE_SHUTDOWN 1
+
+/* Define if you have the stat function.  */
+#define HAVE_STAT 1
+
+/* Define if you have the strchr function.  */
+#define HAVE_STRCHR 1
+
+/* Define if you have the strcpy function.  */
+#define HAVE_STRCPY 1
+
+/* Define if you have the strerror function.  */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strncpy function.  */
+#define HAVE_STRNCPY 1
+
+/* Define if you have the strstr function.  */
+#define HAVE_STRSTR 1
+
+/* Define if you have the time function.  */
+#define HAVE_TIME 1
+
+/* Define if you have the <arpa/inet.h> header file.  */
+#define HAVE_ARPA_INET_H 1
+
+/* Define if you have the <assert.h> header file.  */
+#define HAVE_ASSERT_H 1
+
+/* Define if you have the <ctype.h> header file.  */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <dlfcn.h> header file.  */
+#define HAVE_DLFCN_H 1
+
+/* Define if you have the <errno.h> header file.  */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <fcntl.h> header file.  */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <getopt.h> header file.  */
+#define HAVE_GETOPT_H 1
+
+/* Define if you have the <grp.h> header file.  */
+#define HAVE_GRP_H 1
+
+/* Define if you have the <limits.h> header file.  */
+#define HAVE_LIMITS_H 1
+
+/* Define if you have the <ncurses.h> header file.  */
+#define HAVE_NCURSES_H 1
+
+/* Define if you have the <netdb.h> header file.  */
+#define HAVE_NETDB_H 1
+
+/* Define if you have the <netinet/in.h> header file.  */
+#define HAVE_NETINET_IN_H 1
+
+/* Define if you have the <netinet/tcp.h> header file.  */
+#define HAVE_NETINET_TCP_H 1
+
+/* Define if you have the <paths.h> header file.  */
+#define HAVE_PATHS_H 1
+
+/* Define if you have the <pthread.h> header file.  */
+#define HAVE_PTHREAD_H 1
+
+/* Define if you have the <pwd.h> header file.  */
+#define HAVE_PWD_H 1
+
+/* Define if you have the <regex.h> header file.  */
+#define HAVE_REGEX_H 1
+
+/* Define if you have the <signal.h> header file.  */
+#define HAVE_SIGNAL_H 1
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <sys/mman.h> header file.  */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define if you have the <sys/stat.h> header file.  */
+#define HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/time.h> header file.  */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <sys/types.h> header file.  */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if you have the <termcap.h> header file.  */
+#define HAVE_TERMCAP_H 1
+
+/* Define if you have the <unistd.h> header file.  */
+#define HAVE_UNISTD_H 1
diff --git a/win32/tests/testi2.cpp b/win32/tests/testi2.cpp
new file mode 100644 (file)
index 0000000..b2a9ec5
--- /dev/null
@@ -0,0 +1,468 @@
+// testi2.cpp : Defines the entry point for the application.\r
+//\r
+\r
+#define FD_SETSIZE 5000\r
+#include "stdafx.h"\r
+#include "resource.h"\r
+#include <winsock2.h>\r
+#include <mswsock.h>\r
+extern "C"\r
+{\r
+#define FD_SETSIZE 5000\r
+#include "silcincludes.h"\r
+#include "clientlibincludes.h"\r
+}\r
+\r
+#define MAX_LOADSTRING 100\r
+\r
+// Global Variables:\r
+HINSTANCE hInst;                                                               // current instance\r
+TCHAR szTitle[MAX_LOADSTRING];                                                         // The title bar text\r
+TCHAR szWindowClass[MAX_LOADSTRING];                                                           // The title bar text\r
+\r
+// Foward declarations of functions included in this code module:\r
+ATOM                           MyRegisterClass(HINSTANCE hInstance);\r
+BOOL                           InitInstance(HINSTANCE, int);\r
+LRESULT CALLBACK       WndProc(HWND, UINT, WPARAM, LPARAM);\r
+LRESULT CALLBACK       About(HWND, UINT, WPARAM, LPARAM);\r
+\r
+void silc_op_say(SilcClient client, SilcClientConnection conn, \r
+                 SilcClientMessageType type, char *msg, ...)\r
+{\r
+       va_list vp;\r
+       char message[2048];\r
+\r
+       memset(message, 0, sizeof(message));\r
+       strncat(message, "\n***  ", 5);\r
+\r
+       va_start(vp, msg);\r
+       vsprintf(message + 5, msg, vp);\r
+       va_end(vp);\r
+\r
+       MessageBox( NULL, (char *)message, "say", MB_OK | MB_ICONINFORMATION );\r
+}\r
+\r
+void silc_notify(SilcClient client, SilcClientConnection conn, \r
+                SilcNotifyType type, ...)\r
+{\r
+\r
+}\r
+\r
+void silc_connect(SilcClient client, SilcClientConnection conn, int success)\r
+{\r
+\r
+}\r
+\r
+int silc_auth_meth(SilcClient client, SilcClientConnection conn,\r
+                        char *hostname, uint16 port,\r
+                        SilcProtocolAuthMeth *auth_meth,\r
+                        unsigned char **auth_data,\r
+                        uint32 *auth_data_len)\r
+{\r
+    *auth_meth = SILC_AUTH_NONE;\r
+       return TRUE;\r
+}\r
+void silc_verify_public_key(SilcClient client, SilcClientConnection conn,\r
+                           SilcSocketType conn_type, unsigned char *pk, \r
+                           uint32 pk_len, SilcSKEPKType pk_type,\r
+                           SilcVerifyPublicKey completion, void *context)\r
+{\r
+  completion(TRUE, context);\r
+}\r
+\r
+void silc_command_reply(SilcClient client, SilcClientConnection conn,\r
+                       SilcCommandPayload cmd_payload, int success,\r
+                       SilcCommand command, SilcCommandStatus status, ...)\r
+{\r
+\r
+}\r
+\r
+/* SILC client operations */\r
+SilcClientOperations ops = {\r
+  silc_op_say,\r
+       NULL,\r
+       NULL,\r
+       silc_notify,\r
+       NULL,\r
+       silc_command_reply,\r
+       silc_connect,\r
+       NULL,\r
+       silc_auth_meth,\r
+       silc_verify_public_key,\r
+};\r
+\r
+SILC_TASK_CALLBACK(connect_client)\r
+{\r
+  SilcClient client = (SilcClient)context;\r
+       silc_client_connect_to_server(client, 1334, "leevi.kuo.fi.ssh.com", NULL);\r
+}\r
+\r
+void silc_log(char *message)\r
+{\r
+}\r
+\r
+void silc_debugl(char *file, char *function, \r
+                                                               int line, char *message)\r
+{\r
+       char m[5000];\r
+       memcpy(m, message, strlen(message));\r
+       m[strlen(message)] = '\n';\r
+       m[strlen(message) + 1] = 0;\r
+       OutputDebugString(m);\r
+}\r
+\r
+void silc_hexdumpl(char *file, char *function, \r
+                                                          int line, unsigned char *data_in,\r
+                                                          uint32 data_len, char *message)\r
+{\r
+  int i, k;\r
+  int off, pos, count;\r
+  unsigned char *data = (unsigned char *)data_in;\r
+       char m[10000], *cp;\r
+       int len = data_len;\r
+       \r
+//     memset(m, 0, sizeof(m));\r
+\r
+       cp = m;\r
+  snprintf(cp, 10000, "%s:%d: %s\n", function, line, message);\r
+       cp += strlen(cp);\r
+\r
+  k = 0;\r
+  off = len % 16;\r
+  pos = 0;\r
+  count = 16;\r
+  while (1) {\r
+\r
+    if (off) {\r
+      if ((len - pos) < 16 && (len - pos <= len - off))\r
+                               count = off;\r
+    } else {\r
+      if (pos == len)\r
+                               count = 0;\r
+    }\r
+    if (off == len)\r
+      count = len;\r
+\r
+    if (count) {\r
+      snprintf(cp, sizeof(m), "%08X  ", k++ * 16);\r
+                       cp += strlen(cp);\r
+               }\r
+\r
+    for (i = 0; i < count; i++) {\r
+      snprintf(cp, sizeof(m), "%02X ", data[pos + i]);\r
+                       cp += strlen(cp);\r
+      \r
+      if ((i + 1) % 4 == 0) {\r
+                               snprintf(cp, sizeof(m), " ");\r
+                               cp += strlen(cp);\r
+                       }\r
+               }\r
+\r
+    if (count && count < 16) {\r
+      int j;\r
+      \r
+      for (j = 0; j < 16 - count; j++) {\r
+                               snprintf(cp, sizeof(m), "   ");\r
+                               cp += strlen(cp);\r
+               \r
+                               if ((j + count + 1) % 4 == 0) {\r
+                                       snprintf(cp, sizeof(m), " ");\r
+                                       cp += strlen(cp);\r
+                               }\r
+                       }\r
+    }\r
+         \r
+    for (i = 0; i < count; i++) {\r
+      char ch;\r
+      \r
+      if (data[pos] < 32 || data[pos] >= 127)\r
+                               ch = '.';\r
+      else\r
+                               ch = data[pos];\r
+\r
+      snprintf(cp, sizeof(m), "%c", ch);\r
+                       cp += strlen(cp);\r
+      pos++;\r
+    }\r
+\r
+    if (count) {\r
+      snprintf(cp, sizeof(m), "\n");\r
+                       cp += strlen(cp);\r
+               }\r
+\r
+    if (count < 16)\r
+      break;\r
+  }\r
+       \r
+       OutputDebugString(m);\r
+       MessageBox( NULL, (char *)m, "hexdump", MB_OK | MB_ICONINFORMATION );\r
+}\r
+\r
+static int \r
+silc_create_key_pair(char *pkcs_name, int bits, char *path,\r
+                            char *identifier, \r
+                            SilcPublicKey *ret_pub_key,\r
+                            SilcPrivateKey *ret_prv_key)\r
+{\r
+  SilcPKCS pkcs;\r
+  SilcPublicKey pub_key;\r
+  SilcPrivateKey prv_key;\r
+  SilcRng rng;\r
+  unsigned char *key;\r
+  uint32 key_len;\r
+  char pkfile[256], prvfile[256];\r
+\r
+  if (!pkcs_name || !path)\r
+    return FALSE;\r
+\r
+  if (!bits)\r
+    bits = 1024;\r
+\r
+  rng = silc_rng_alloc();\r
+  silc_rng_init(rng);\r
+  silc_rng_global_init(rng);\r
+\r
+  /* Generate keys */\r
+  silc_pkcs_alloc((const unsigned char *)pkcs_name, &pkcs);\r
+  pkcs->pkcs->init(pkcs->context, bits, rng);\r
+\r
+  /* Save public key into file */\r
+  key = silc_pkcs_get_public_key(pkcs, &key_len);\r
+  pub_key = silc_pkcs_public_key_alloc(pkcs->pkcs->name, identifier,\r
+                                       key, key_len);\r
+  *ret_pub_key = pub_key;\r
+\r
+  memset(key, 0, sizeof(key_len));\r
+  silc_free(key);\r
+\r
+  /* Save private key into file */\r
+  key = silc_pkcs_get_private_key(pkcs, &key_len);\r
+  prv_key = silc_pkcs_private_key_alloc(pkcs->pkcs->name, key, key_len);\r
+  *ret_prv_key = prv_key;\r
+\r
+  memset(key, 0, sizeof(key_len));\r
+  silc_free(key);\r
+\r
+  silc_rng_free(rng);\r
+  silc_pkcs_free(pkcs);\r
+\r
+  return TRUE;\r
+}\r
+\r
+\r
+int APIENTRY WinMain(HINSTANCE hInstance,\r
+                     HINSTANCE hPrevInstance,\r
+                     LPSTR     lpCmdLine,\r
+                     int       nCmdShow)\r
+{\r
+       // TODO: Place code here.\r
+       MSG msg;\r
+       HACCEL hAccelTable;\r
+       HANDLE h;\r
+       HANDLE handles[100];\r
+       SOCKET s;\r
+       unsigned int k;\r
+       WSAEVENT e, e2, e3;\r
+       int ret;\r
+       DWORD ready;\r
+       HMODULE mod;\r
+\r
+       // Initialize global strings\r
+       LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);\r
+       LoadString(hInstance, IDC_TESTI2, szWindowClass, MAX_LOADSTRING);\r
+       MyRegisterClass(hInstance);\r
+\r
+       // Perform application initialization:\r
+       if (!InitInstance (hInstance, nCmdShow)) \r
+       {\r
+               return FALSE;\r
+       }\r
+\r
+       hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_TESTI2);\r
+\r
+       {\r
+               SilcSchedule sched;     \r
+               SilcClient client;\r
+\r
+               silc_net_win32_init();\r
+               client = silc_client_alloc(&ops, NULL, NULL, "SILC-1.0-0.5.1");\r
+               client->realname = "pekka riikonen";\r
+               client->username = "priikone";\r
+               client->hostname = "leevi.kuo.fi.ssh.com";\r
+\r
+               silc_cipher_register_default();\r
+               silc_pkcs_register_default();\r
+               silc_hash_register_default();\r
+               silc_hmac_register_default();\r
+\r
+               silc_debug = TRUE;\r
+               silc_log_set_debug_callbacks(silc_debugl, silc_hexdumpl);\r
+\r
+               silc_create_key_pair("rsa", 1024, "kk", "UN=priikone, HN=pelle.kuo.fi.ssh.com", \r
+                                                                                               &client->public_key, &client->private_key);\r
+\r
+               silc_client_init(client);\r
+\r
+               silc_schedule_task_add(client->schedule, 0, connect_client, \r
+                                                               client, 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); \r
+\r
+               silc_client_run(client);                \r
+       }\r
+       \r
+       return msg.wParam;\r
+}\r
+\r
+//\r
+//  FUNCTION: MyRegisterClass()\r
+//\r
+//  PURPOSE: Registers the window class.\r
+//\r
+//  COMMENTS:\r
+//\r
+//    This function and its usage is only necessary if you want this code\r
+//    to be compatible with Win32 systems prior to the 'RegisterClassEx'\r
+//    function that was added to Windows 95. It is important to call this function\r
+//    so that the application will get 'well formed' small icons associated\r
+//    with it.\r
+//\r
+ATOM MyRegisterClass(HINSTANCE hInstance)\r
+{\r
+       WNDCLASSEX wcex;\r
+\r
+       wcex.cbSize = sizeof(WNDCLASSEX); \r
+\r
+       wcex.style                      = CS_HREDRAW | CS_VREDRAW;\r
+       wcex.lpfnWndProc        = (WNDPROC)WndProc;\r
+       wcex.cbClsExtra         = 0;\r
+       wcex.cbWndExtra         = 0;\r
+       wcex.hInstance          = hInstance;\r
+       wcex.hIcon                      = LoadIcon(hInstance, (LPCTSTR)IDI_TESTI2);\r
+       wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);\r
+       wcex.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);\r
+       wcex.lpszMenuName       = (LPCSTR)IDC_TESTI2;\r
+       wcex.lpszClassName      = szWindowClass;\r
+       wcex.hIconSm            = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);\r
+\r
+       return RegisterClassEx(&wcex);\r
+}\r
+\r
+//\r
+//   FUNCTION: InitInstance(HANDLE, int)\r
+//\r
+//   PURPOSE: Saves instance handle and creates main window\r
+//\r
+//   COMMENTS:\r
+//\r
+//        In this function, we save the instance handle in a global variable and\r
+//        create and display the main program window.\r
+//\r
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)\r
+{\r
+   HWND hWnd;\r
+\r
+   hInst = hInstance; // Store instance handle in our global variable\r
+\r
+   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,\r
+      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);\r
+\r
+   if (!hWnd)\r
+   {\r
+                       LPVOID lpMsgBuf;\r
+                       FormatMessage( \r
+                         FORMAT_MESSAGE_ALLOCATE_BUFFER | \r
+                         FORMAT_MESSAGE_FROM_SYSTEM | \r
+                         FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                         NULL,\r
+                         GetLastError(),\r
+                         0, // Default language\r
+                         (LPTSTR) &lpMsgBuf,\r
+                         0,\r
+                         NULL \r
+                       );\r
+                       // Process any inserts in lpMsgBuf.\r
+                       // ...\r
+                       // Display the string.\r
+                       MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );\r
+                       // Free the buffer.\r
+                       LocalFree( lpMsgBuf );\r
+\r
+      return FALSE;\r
+   }\r
+\r
+   ShowWindow(hWnd, nCmdShow);\r
+   UpdateWindow(hWnd);\r
+\r
+   return TRUE;\r
+}\r
+\r
+//\r
+//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)\r
+//\r
+//  PURPOSE:  Processes messages for the main window.\r
+//\r
+//  WM_COMMAND - process the application menu\r
+//  WM_PAINT   - Paint the main window\r
+//  WM_DESTROY - post a quit message and return\r
+//\r
+//\r
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
+{\r
+       int wmId, wmEvent;\r
+       PAINTSTRUCT ps;\r
+       HDC hdc;\r
+       TCHAR szHello[MAX_LOADSTRING];\r
+       LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);\r
+\r
+       switch (message) \r
+       {\r
+               case WM_COMMAND:\r
+                       wmId    = LOWORD(wParam); \r
+                       wmEvent = HIWORD(wParam); \r
+                       // Parse the menu selections:\r
+                       switch (wmId)\r
+                       {\r
+                               case IDM_ABOUT:\r
+                                  DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);\r
+                                  break;\r
+                               case IDM_EXIT:\r
+                                  DestroyWindow(hWnd);\r
+                                  break;\r
+                               default:\r
+                                  return DefWindowProc(hWnd, message, wParam, lParam);\r
+                       }\r
+                       break;\r
+               case WM_PAINT:\r
+                       hdc = BeginPaint(hWnd, &ps);\r
+                       // TODO: Add any drawing code here...\r
+                       RECT rt;\r
+                       GetClientRect(hWnd, &rt);\r
+                       DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);\r
+                       EndPaint(hWnd, &ps);\r
+                       break;\r
+               case WM_DESTROY:\r
+                       PostQuitMessage(0);\r
+                       break;\r
+               default:\r
+                       return DefWindowProc(hWnd, message, wParam, lParam);\r
+   }\r
+   return 0;\r
+}\r
+\r
+// Mesage handler for about box.\r
+LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
+{\r
+       switch (message)\r
+       {\r
+               case WM_INITDIALOG:\r
+                               return TRUE;\r
+\r
+               case WM_COMMAND:\r
+                       if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) \r
+                       {\r
+                               EndDialog(hDlg, LOWORD(wParam));\r
+                               return TRUE;\r
+                       }\r
+                       break;\r
+       }\r
+    return FALSE;\r
+}\r
diff --git a/win32/tests/testi2.h b/win32/tests/testi2.h
new file mode 100644 (file)
index 0000000..1cac1a7
--- /dev/null
@@ -0,0 +1,12 @@
+\r
+#if !defined(AFX_TESTI2_H__01E3C6D6_6C6A_11D5_A2B8_000102F0ABE4__INCLUDED_)\r
+#define AFX_TESTI2_H__01E3C6D6_6C6A_11D5_A2B8_000102F0ABE4__INCLUDED_\r
+\r
+#if _MSC_VER > 1000\r
+#pragma once\r
+#endif // _MSC_VER > 1000\r
+\r
+#include "resource.h"\r
+\r
+\r
+#endif // !defined(AFX_TESTI2_H__01E3C6D6_6C6A_11D5_A2B8_000102F0ABE4__INCLUDED_)\r